Compare commits
798 commits
powershell
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c12519b9d | ||
|
|
2bb8561eb6 |
||
|
|
a868102b5e |
||
|
|
dc862fe565 | ||
|
|
b4bcfca932 |
||
|
|
3df0602a69 |
||
|
|
1c2dda4a6a |
||
|
|
db5ed8e802 | ||
|
|
17ca1522f8 | ||
|
|
561a5b9aa2 | ||
|
|
46cfdc678d | ||
|
|
51d78fce5f |
||
|
|
14f34ea6d1 |
||
|
|
a720d0e1d5 | ||
|
|
7c7e3f9c3f | ||
|
|
57ce099280 | ||
|
|
27eb464b8e |
||
|
|
f1e2c12e5b | ||
|
|
26cd7c5c7e |
||
|
|
59c9cd10ab | ||
|
|
9c806d2a01 |
||
|
|
3af97f1928 |
||
|
|
da39c4955c |
||
|
|
21ebb22fd4 | ||
|
|
aa0cddc0da |
||
|
|
5075f3a11a | ||
|
|
bab81953d7 |
||
|
|
0500e41429 |
||
|
|
1cbb400d42 |
||
|
|
5ea1f0d9d4 | ||
|
|
a51b8b23c9 | ||
|
|
99c0c4f763 |
||
|
|
daa8865fea |
||
|
|
f4d756fe86 |
||
|
|
d43904eb57 | ||
|
|
aacd62f09f | ||
|
|
0e9497ce8f | ||
|
|
2708e8e6ec |
||
|
|
adf6163653 |
||
|
|
0af9d70b0d |
||
|
|
9113dbb698 | ||
|
|
08679d1c3e | ||
|
|
4e04a862cb | ||
|
|
a7e8e93920 | ||
|
|
2b84439413 | ||
|
|
0c88cc8bad | ||
|
|
a47d0b921c |
||
|
|
026ac22280 | ||
|
|
297c0bea56 | ||
|
|
a9a26a5a60 |
||
|
|
1e25313cb5 | ||
|
|
bc15c94513 | ||
|
|
f389d65a24 | ||
|
|
b4195eee93 | ||
|
|
6ad9101cf2 |
||
|
|
a41db89523 | ||
|
|
dd88e051a4 | ||
|
|
fa92c2a8d5 | ||
|
|
6709b45c77 |
||
|
|
278fc3cd8c | ||
|
|
13a5a1b947 | ||
|
|
7d83419a4f | ||
|
|
7b3016aa90 |
||
|
|
d70d51a614 | ||
|
|
caeb6610ed |
||
|
|
0d9e7835a8 | ||
|
|
054ea35428 | ||
|
|
244d7d8fdf | ||
|
|
35e5b3d56e | ||
|
|
842d9fbc2d | ||
|
|
6a24a17f2e |
||
|
|
91e0426e88 |
||
|
|
bf24b37190 |
||
|
|
2d589475eb |
||
|
|
1414f261a1 | ||
|
|
a39017dd06 | ||
|
|
c05335c799 | ||
|
|
efc5c76866 | ||
|
|
154e727b96 | ||
|
|
6b74e584d5 | ||
|
|
85bb8594ab | ||
|
|
63fd3165fb | ||
|
|
afb912dd08 | ||
|
|
b92a89f0d4 | ||
|
|
6f88b400e1 | ||
|
|
af2670ef9a |
||
|
|
2b24611036 |
||
|
|
d84ebdd0ee |
||
|
|
6b67d165b9 | ||
|
|
a53d68f043 | ||
|
|
6d3f3e207b | ||
|
|
5716bbefa9 |
||
|
|
2615cff97a | ||
|
|
01ea50cb70 | ||
|
|
7d2770f3c4 | ||
|
|
2a635294d0 | ||
|
|
38c843219e | ||
|
|
c012f9520e | ||
|
|
943ae6c7c9 | ||
|
|
82db32780b | ||
|
|
4cb4b814a1 |
||
|
|
d2b92b7ce5 | ||
|
|
5bd83d3e37 | ||
|
|
f6517892c1 |
||
|
|
edcb057ead | ||
|
|
06f9bfc057 | ||
|
|
804b499286 | ||
|
|
49a9a4b367 | ||
|
|
f2d407256e | ||
|
|
94242fa532 | ||
|
|
ba83a59e88 | ||
|
|
675a68c601 | ||
|
|
e1078375fe | ||
|
|
26452289a8 | ||
|
|
ec9122284e | ||
|
|
131cfcdd33 | ||
|
|
c2082cffae | ||
|
|
9e1a1ab4b5 | ||
|
|
32f9a6ec43 | ||
|
|
d8d6eb2286 | ||
|
|
1d38b8198f |
||
|
|
f40ac5db23 | ||
|
|
11edcddd9c |
||
|
|
f9c5a33301 |
||
|
|
fdfa8b265b |
||
|
|
a8651e257b |
||
|
|
d8eb6a0463 | ||
|
|
09efcf5e50 | ||
|
|
5201995279 | ||
|
|
fd03e074f3 |
||
|
|
fd0c6e7675 | ||
|
|
6fac220ee5 | ||
|
|
0810d7154c | ||
|
|
476300f815 | ||
|
|
95b900a0a7 |
||
|
|
0e0cc6d4ae | ||
|
|
c33fddd150 |
||
|
|
448ee33a6a | ||
|
|
ae289a88c7 | ||
|
|
3c7311e33a | ||
|
|
d561c4aea5 | ||
|
|
3c54cd268f | ||
|
|
bcf4120ba4 | ||
|
|
50dc55e0e8 | ||
|
|
716181e056 | ||
|
|
840ee09242 | ||
|
|
0bb8416e11 | ||
|
|
d81bdf30ef | ||
|
|
e70b8f37a3 |
||
|
|
5754e96067 | ||
|
|
4958de92d3 | ||
|
|
6b546cd621 | ||
|
|
323e22b8a9 | ||
|
|
5a1eb9250a | ||
|
|
cc84e15932 | ||
|
|
7dcb15f11c | ||
|
|
44711899cb | ||
|
|
0313efa06f | ||
|
|
51d551fe52 | ||
|
|
ae137ecdd0 | ||
|
|
d6c68d691c | ||
|
|
47680565c4 | ||
|
|
8c39a861bd | ||
|
|
639e89f5db | ||
|
|
5a23ec5beb | ||
|
|
e0b0a7f3d2 | ||
|
|
0ff9dc5f9e | ||
|
|
0c79384529 | ||
|
|
789377eab4 | ||
|
|
4466fce20b | ||
|
|
13102dc711 | ||
|
|
91bebc1826 | ||
|
|
3239963893 | ||
|
|
31e44548d3 | ||
|
|
e5e13ffcdd | ||
|
|
894ca522d3 | ||
|
|
322b7e0a38 | ||
|
|
bc1146df3b | ||
|
|
d2ed8a828c | ||
|
|
fccafd6739 | ||
|
|
672ac78fef | ||
|
|
64f2c8eb01 | ||
|
|
cb174c176d | ||
|
|
2d082cc923 | ||
|
|
0d703779dc | ||
|
|
f3c39e12ab | ||
|
|
cfae062da1 | ||
|
|
0b4da4c82b | ||
|
|
64e38b57ab | ||
|
|
0895c5e829 | ||
|
|
052d63dee5 | ||
|
|
93568ba05d | ||
|
|
e0b1d6ed81 | ||
|
|
3c857cebf4 | ||
|
|
2f1c93a2ac | ||
|
|
cb441be7bc | ||
|
|
b9ed7f34b4 | ||
|
|
04a5e889f9 | ||
|
|
f60b259dbc | ||
|
|
b95ae00054 | ||
|
|
4617250f41 | ||
|
|
0ced5eb1d4 | ||
|
|
747ad9b804 | ||
|
|
d2990d7bae | ||
|
|
819d6366ac | ||
|
|
4e994968ff | ||
|
|
7432be9532 | ||
|
|
07858cc250 | ||
|
|
4c0ba92ac6 | ||
|
|
7c4eef9096 | ||
|
|
85ca867cc2 |
||
|
|
cb922bdc83 |
||
|
|
e99ac0d658 |
||
|
|
0cc4322b18 | ||
|
|
7f77842ab8 | ||
|
|
4454dead0f |
||
|
|
30eec0d93c | ||
|
|
52eff32651 |
||
|
|
5303de8195 |
||
|
|
75e8450cf3 | ||
|
|
35f7e08fbb | ||
|
|
d38da376c2 | ||
|
|
1f20115960 | ||
|
|
442a18f8f2 |
||
|
|
e010d3217c | ||
|
|
1556e63ef0 |
||
|
|
205eb7cdb3 |
||
|
|
44a0f819e2 |
||
|
|
4206d0e57c | ||
|
|
b180b14ed4 |
||
|
|
d52eb3a6e9 | ||
|
|
b72aa18dec | ||
|
|
07ea3880ac |
||
|
|
38ed75dda7 |
||
|
|
0ad826704b |
||
|
|
65708f55e0 |
||
|
|
7bd22d2bbd | ||
|
|
ce8dd9113c | ||
|
|
ec68811c8a | ||
|
|
47c404f823 | ||
|
|
41a98026fa |
||
|
|
523ea29faa |
||
|
|
d0fc7815b3 |
||
|
|
0942aa4523 | ||
|
|
f59ba048cb |
||
|
|
f20c4c9725 | ||
|
|
f8d1e484f9 |
||
|
|
66130389c5 | ||
|
|
61179b7670 | ||
|
|
f161ce1e8c | ||
|
|
d04a8400f9 |
||
|
|
b65d9536ad | ||
|
|
28c5534704 | ||
|
|
2eb32f3153 |
||
|
|
8261cde3c9 | ||
|
|
573121ee08 |
||
|
|
632e6696ef | ||
|
|
c531686d39 | ||
|
|
2eac9ddff8 | ||
|
|
8e2d352eb8 | ||
|
|
4ac9785217 |
||
|
|
4a45753a4c | ||
|
|
e15c50a4f4 | ||
|
|
17a9494dbc |
||
|
|
4db4880b6d |
||
|
|
4c2b3bd886 |
||
|
|
e6500d39d8 |
||
|
|
ff4acb17d1 |
||
|
|
4f03e4f65a | ||
|
|
23130d74ad |
||
|
|
5ab92a7e67 | ||
|
|
979c1e8779 | ||
|
|
a9d0ebde0b | ||
|
|
82307ff1ba | ||
|
|
7d5ad3ffa1 | ||
|
|
8801c2dac4 | ||
|
|
e1c1347bdd | ||
|
|
c769e8a479 | ||
|
|
53b1d12447 | ||
|
|
8e3a734141 | ||
|
|
0f0d684e92 | ||
|
|
42cdff043a | ||
|
|
f0785c11f2 | ||
|
|
a5d66a7477 | ||
|
|
2399a79cae | ||
|
|
e9e0e9e5b9 | ||
|
|
e2ca9b5318 | ||
|
|
e5a765409a |
||
|
|
f87aa4aa19 | ||
|
|
07ec08e664 | ||
|
|
3330c704d7 | ||
|
|
87ce1734ea | ||
|
|
48e075624a |
||
|
|
b36f9e893a | ||
|
|
b7de25e3d1 |
||
|
|
47c2cef2a1 | ||
|
|
cd3b5f5baa | ||
|
|
5b88837919 | ||
|
|
888c558aa4 |
||
|
|
85865e0012 | ||
|
|
7be6d852c6 |
||
|
|
96a594e4cf |
||
|
|
69bb2737d1 | ||
|
|
1eb2bc2199 | ||
|
|
2819e224e7 | ||
|
|
2bdb30a45e | ||
|
|
3f3a001283 |
||
|
|
5ae3d9f336 |
||
|
|
30741f8813 |
||
|
|
2225735b80 | ||
|
|
fb86ef924b | ||
|
|
97bef33660 |
||
|
|
d5b9411256 | ||
|
|
7b048367f7 |
||
|
|
80985148e7 | ||
|
|
0d112d70a0 | ||
|
|
78bec87f4e |
||
|
|
c76e20cd9b |
||
|
|
ea36b60a07 |
||
|
|
5057906317 |
||
|
|
8d17572538 | ||
|
|
f3c5944dd2 | ||
|
|
1eb9933e2a |
||
|
|
1b9763a4fc | ||
|
|
16f22b396f | ||
|
|
f6726de058 |
||
|
|
3d442c978f |
||
|
|
692a0d8d39 |
||
|
|
bd09206caf | ||
|
|
d1bfe18e18 |
||
|
|
b2f41643f4 | ||
|
|
42b79d8cbd |
||
|
|
b34ccd679d | ||
|
|
069224e28d | ||
|
|
6fd4724189 | ||
|
|
6a5c0cf63e |
||
|
|
2ad1a3e502 | ||
|
|
26277cefbd | ||
|
|
ce4f341feb | ||
|
|
4ed9158282 | ||
|
|
9e2526c4a4 | ||
|
|
75e1c6b1c1 |
||
|
|
4424969028 | ||
|
|
173f2d6f37 | ||
|
|
e76d6a5c97 | ||
|
|
1db1bd6d5b | ||
|
|
a037e4d31a | ||
|
|
2e3bbef6df | ||
|
|
277994e27c | ||
|
|
853af58124 | ||
|
|
778745b5c2 | ||
|
|
db4e60aca5 | ||
|
|
522d8fbc99 |
||
|
|
99d20bb277 | ||
|
|
8b8be9f5b5 | ||
|
|
3e452b73d1 | ||
|
|
f1cc01a399 | ||
|
|
2c401a859c |
||
|
|
ca804bda99 | ||
|
|
4e5011fe1b | ||
|
|
45f0b68d87 | ||
|
|
1ace7b648e | ||
|
|
710cffe2da | ||
|
|
0867edd81c | ||
|
|
3a79ce9ab7 | ||
|
|
2710ee2bfa | ||
|
|
3aa949431e | ||
|
|
97bdb0d0cc | ||
|
|
733843a6d4 | ||
|
|
e81be848a1 |
||
|
|
b22d8e570e | ||
|
|
dffd65f241 | ||
|
|
b2456b587f | ||
|
|
7a6fea120b |
||
|
|
f538102d33 | ||
|
|
18fd56484e |
||
|
|
c1b544fb3c | ||
|
|
e634982d78 |
||
|
|
8b488d479d | ||
|
|
9b7540fbf6 | ||
|
|
95fa0c5105 | ||
|
|
7a8bdef088 |
||
|
|
51cc6c4d3a | ||
|
|
3374d32891 |
||
|
|
ef94b1bc49 | ||
|
|
24193a17ff |
||
|
|
3b03cedc2b |
||
|
|
dae5760900 | ||
|
|
3c1ac36e6e | ||
|
|
961557975c |
||
|
|
9de4d07955 | ||
|
|
e90093870e |
||
|
|
d263568ef7 |
||
|
|
dc57ed7a67 |
||
|
|
3573439a5d |
||
|
|
044e27144e |
||
|
|
cc06605b40 |
||
|
|
67a380c9fa |
||
|
|
213a02dcbe | ||
|
|
51bbadc4f5 | ||
|
|
57f1c128c5 | ||
|
|
9a9d43cb82 |
||
|
|
f0d4716783 |
||
|
|
ca63c08d6a | ||
|
|
be44a22903 | ||
|
|
7824bb29b2 |
||
|
|
750e68bdf5 |
||
|
|
1f270ed423 |
||
|
|
3a160aa77a | ||
|
|
d86b42bf5b | ||
|
|
10e9a75fc7 | ||
|
|
6593f383cf | ||
|
|
f6158d0ffb | ||
|
|
aa26d4bc1a | ||
|
|
9f57cb0042 |
||
|
|
b02b1835d4 |
||
|
|
cb0eee180f | ||
|
|
917b737486 | ||
|
|
effc49eed9 |
||
|
|
1e43cd4870 | ||
|
|
db2288c6ed |
||
|
|
f9d20b9537 | ||
|
|
56418e3233 |
||
|
|
8f5b928e4b | ||
|
|
5fbc70165d |
||
|
|
8438dd40a1 |
||
|
|
7acf6ab45c |
||
|
|
29cb5c914b | ||
|
|
357e112cdf |
||
|
|
26d7e95adc | ||
|
|
89ffb1798c |
||
|
|
c50c99ef34 | ||
|
|
2ea112ded9 | ||
|
|
8d1c723b28 | ||
|
|
e603520860 | ||
|
|
8a6d56d3e1 | ||
|
|
2d6cada5a9 | ||
|
|
d7eff46e0b | ||
|
|
625da3fcbe | ||
|
|
000f7a9232 | ||
|
|
4144631d03 | ||
|
|
8275d2fafb | ||
|
|
98bef3db03 | ||
|
|
375ff4aa27 | ||
|
|
cd5ca37ce1 | ||
|
|
05bbafd60c | ||
|
|
ca4ab52f8d | ||
|
|
0140a607b1 | ||
|
|
23e5f6bbf4 | ||
|
|
025d193493 | ||
|
|
f945dbcfdd | ||
|
|
8f3de079bc | ||
|
|
6b89df173f | ||
|
|
d3fd0a38f0 | ||
|
|
43d438dfc9 |
||
|
|
daf854e3e1 |
||
|
|
0c6715e95e |
||
|
|
db5073aec1 |
||
|
|
e885c95daa | ||
|
|
c1e29be9b8 |
||
|
|
99b582378a | ||
|
|
47ace82364 | ||
|
|
3726766694 | ||
|
|
80afb153b9 | ||
|
|
f8cbb7c5b6 | ||
|
|
7a9f75a06e | ||
|
|
b1fd15c56a | ||
|
|
9faa8aa6f7 | ||
|
|
e884a5f71c | ||
|
|
4f0847bcf8 | ||
|
|
6d020a50de | ||
|
|
231720b5b5 | ||
|
|
79a8ad40a4 | ||
|
|
82ec19fff9 | ||
|
|
97f6dd29a6 | ||
|
|
9df680089b | ||
|
|
db344148ea | ||
|
|
3beb25f949 | ||
|
|
cd796d1500 | ||
|
|
727214c599 | ||
|
|
ebc0b3607a | ||
|
|
2c5937c2c1 | ||
|
|
de2a924c38 | ||
|
|
05b855e605 |
||
|
|
c6e8e8038f | ||
|
|
a4f3fd9c5e | ||
|
|
2440a987b2 | ||
|
|
434a6f41d4 | ||
|
|
fc7725fefb | ||
|
|
0938d72a8c | ||
|
|
8d2ea44cb9 | ||
|
|
97a5d737ee | ||
|
|
b3c86bc757 | ||
|
|
1ae5c6fdcc |
||
|
|
81fffbe80e | ||
|
|
f81cc126c3 | ||
|
|
4d88dd6eff |
||
|
|
f9f0d5a83e | ||
|
|
f383c62542 |
||
|
|
99518e7eba | ||
|
|
fe58b94bb0 |
||
|
|
0de023e3c4 | ||
|
|
7c943c2ed7 | ||
|
|
036c9b9605 | ||
|
|
193196cc03 | ||
|
|
43dd282702 | ||
|
|
bdca96f663 | ||
|
|
b0d41eb347 | ||
|
|
cb39e2b511 | ||
|
|
3643c2effd | ||
|
|
5ce4147361 | ||
|
|
b049ba6d8d |
||
|
|
6cfc313234 | ||
|
|
8e286be6ee | ||
|
|
915fb29a9f |
||
|
|
9f6caadc40 | ||
|
|
a4abe511c7 | ||
|
|
9baba5afab | ||
|
|
fe8d7251a4 | ||
|
|
a647b65983 | ||
|
|
81712b124f | ||
|
|
044804ca20 | ||
|
|
85667a5bac |
||
|
|
50b8b0103c | ||
|
|
402071065f | ||
|
|
e24d0a4659 | ||
|
|
8143992345 | ||
|
|
c43f16e288 | ||
|
|
4b7fc83ce3 | ||
|
|
39212f4ffc | ||
|
|
2117a0515d | ||
|
|
669b019e25 |
||
|
|
b486839269 | ||
|
|
a2ae1c288e | ||
|
|
0e56146726 | ||
|
|
32c7960b5f | ||
|
|
99faf76dcc | ||
|
|
7848629a1c | ||
|
|
b322a2e89c | ||
|
|
596ea65ca6 |
||
|
|
ec009a1332 | ||
|
|
8e2313bf85 |
||
|
|
df8389380a |
||
|
|
6f3884253c | ||
|
|
d1f2df36cc |
||
|
|
de63700bf4 |
||
|
|
c0c3bd6645 |
||
|
|
7f625fee9f | ||
|
|
f217a1d9ef | ||
|
|
3595c8c638 |
||
|
|
d6a6184421 | ||
|
|
46e8e4c56b |
||
|
|
a90da6219f |
||
|
|
f2994dfad7 |
||
|
|
5faf56de9d |
||
|
|
7705d6020c | ||
|
|
2b22d3d29f |
||
|
|
435e33e23e |
||
|
|
71ad4c2022 |
||
|
|
ae2ba01c5b | ||
|
|
3d113eaec4 | ||
|
|
cd1b304d30 | ||
|
|
5e64b07e8d | ||
|
|
0bf511debc | ||
|
|
62b3070c50 | ||
|
|
c9b1456759 | ||
|
|
2437aace41 | ||
|
|
b80c483284 |
||
|
|
5f11fe4e6e | ||
|
|
58a95402c4 | ||
|
|
8c4112cd1f | ||
|
|
6a1c9a6b52 | ||
|
|
060a9f4597 |
||
|
|
90bbbe90f5 | ||
|
|
c6d6c7f876 | ||
|
|
36253e58ce | ||
|
|
dc3deadb26 | ||
|
|
dd13c7d543 | ||
|
|
4946e95d4c |
||
|
|
62c1fe79da |
||
|
|
0452329203 | ||
|
|
4c96655526 |
||
|
|
ae0ba29d5e |
||
|
|
dd101aedca |
||
|
|
fd6b353782 | ||
|
|
5837135fee | ||
|
|
c103e2adf5 | ||
|
|
fa0171f464 |
||
|
|
ce6ad5d1ca |
||
|
|
2451d56de4 | ||
|
|
abd8ba7d38 | ||
|
|
bdfa0f92ab | ||
|
|
3354db275b | ||
|
|
ccf427a7c8 | ||
|
|
3bf461c621 |
||
|
|
56c0c721db | ||
|
|
16e1ae143b |
||
|
|
a87c9e8715 | ||
|
|
4586aa36ff | ||
|
|
80ba644732 | ||
|
|
f4d855e1df |
||
|
|
77522cbd44 | ||
|
|
77dc400077 | ||
|
|
e153b9536e | ||
|
|
c25e9256aa | ||
|
|
1b71d84f58 |
||
|
|
b20f61bcff | ||
|
|
f6d12eff72 | ||
|
|
7a70519b03 | ||
|
|
aed4144fe5 | ||
|
|
f0ee397f5d | ||
|
|
31c13bd8eb |
||
|
|
d8d7a3f7c6 |
||
|
|
b3760f43f9 |
||
|
|
980b80ebbb | ||
|
|
a5a2966ba7 | ||
|
|
c3105cf912 |
||
|
|
be26098315 | ||
|
|
0918a72dd9 |
||
|
|
db6d9efb75 |
||
|
|
7e29143a6f |
||
|
|
93edbe468b | ||
|
|
1b7d0caf26 | ||
|
|
0e4cad302d | ||
|
|
d0652187a3 |
||
|
|
ae7e6d0b0c | ||
|
|
5cb9c62e7f | ||
|
|
0a0cd88545 |
||
|
|
59f23c7545 |
||
|
|
87e80db94d |
||
|
|
1003cdc0b2 | ||
|
|
8664c0ad78 | ||
|
|
03d97dc58f |
||
|
|
5d404708cf | ||
|
|
d42fd51f35 | ||
|
|
9393c1d80e | ||
|
|
36fbe5bba5 |
||
|
|
a5765b153c | ||
|
|
ec36099f97 | ||
|
|
de04598bc6 | ||
|
|
6c8657c9a7 | ||
|
|
c1283a292c | ||
|
|
1b7402915c | ||
|
|
cd26e81fd9 | ||
|
|
b7dee6410a |
||
|
|
27e11d5265 |
||
|
|
7da627f95e |
||
|
|
2c1060c49e | ||
|
|
e61e7fefe6 |
||
|
|
c4bedcee89 | ||
|
|
5e8d4f97c4 |
||
|
|
926029e343 | ||
|
|
c8dcc47b1c |
||
|
|
a74783513c | ||
|
|
0aba8e0179 | ||
|
|
3366e1c784 | ||
|
|
4a7e3b9229 | ||
|
|
37fefafe35 | ||
|
|
866e6fe9b1 |
||
|
|
68a45b5771 | ||
|
|
267f202dad | ||
|
|
e37ef18c28 | ||
|
|
be32a0a1a8 | ||
|
|
583fb95ea0 | ||
|
|
86afe89d63 |
||
|
|
6b735ce4d0 | ||
|
|
79df51e83c | ||
|
|
dcabc52937 | ||
|
|
8052484cc7 | ||
|
|
a56bee7b0a | ||
|
|
933f946801 | ||
|
|
6fd8d16487 |
||
|
|
a90a529ced |
||
|
|
ac56e3adef |
||
|
|
19cc58f09e | ||
|
|
db95fe0c1d | ||
|
|
8c01ea7bfd |
||
|
|
6878c80cfb | ||
|
|
896b20f490 | ||
|
|
fb4411676c |
||
|
|
3f3ede48ba | ||
|
|
44efc0a1b3 | ||
|
|
6bcc58a39b | ||
|
|
9485a9f46c |
||
|
|
5eb042be14 |
||
|
|
7f3d7a46e4 | ||
|
|
9c18f3e802 |
||
|
|
018ae05de8 | ||
|
|
74eb94fe53 | ||
|
|
8d4ec3f53a | ||
|
|
309c292413 | ||
|
|
c1851a38e5 |
||
|
|
96a7083f29 |
||
|
|
bd794526b7 | ||
|
|
4e1f14261b |
||
|
|
ab56d2cef4 | ||
|
|
b915ada0c8 | ||
|
|
e26fc6bd8c |
||
|
|
3d4ce6d7d4 |
||
|
|
d6994c2ba6 |
||
|
|
23b3bb719b | ||
|
|
8253c1687b | ||
|
|
40bec48e97 | ||
|
|
c6c0144f85 |
||
|
|
ea1a56ed0d | ||
|
|
1da905a955 | ||
|
|
04aaafd69a |
||
|
|
da0fc65333 | ||
|
|
eda6b890de | ||
|
|
c6e192d5f5 | ||
|
|
7903e0e0fe |
||
|
|
41c5093568 |
||
|
|
b8d2b548d8 | ||
|
|
9b2710dd47 | ||
|
|
239b538bfc |
||
|
|
bd820b2a8b | ||
|
|
a524762f98 | ||
|
|
8a4a8b39da | ||
|
|
9c3813f11b | ||
|
|
ffe5209ea6 | ||
|
|
94f19459b9 | ||
|
|
d69e63719b | ||
|
|
9b5960f81e | ||
|
|
287286cadb | ||
|
|
42d4b80200 |
||
|
|
9e3d7237a5 | ||
|
|
4318e567f2 |
||
|
|
5ee8ffc3f6 | ||
|
|
6cf4f9f9ab | ||
|
|
ef4a6fc10c | ||
|
|
7ca51f8f08 | ||
|
|
098cd0929b |
||
|
|
b2b08530db |
||
|
|
df759ec9dc | ||
|
|
355f0f0e0c | ||
|
|
1e8bd384b9 | ||
|
|
099ffc2af9 | ||
|
|
c2b0cc8f18 | ||
|
|
2252a2bf02 | ||
|
|
3b97500289 | ||
|
|
f6eccf1314 | ||
|
|
d711d655aa | ||
|
|
6ee8f7875d | ||
|
|
149d48b688 | ||
|
|
c898eb47b2 | ||
|
|
a00e6a19d6 | ||
|
|
7456c26c2a | ||
|
|
04bb03da0a | ||
|
|
29c26996fb | ||
|
|
18627b9244 | ||
|
|
5aa33c19f7 | ||
|
|
ee1bb009ea | ||
|
|
d1e3b648d3 | ||
|
|
dcdab63516 | ||
|
|
cb2e08b850 |
||
|
|
9be0f43a5f | ||
|
|
1277ec6347 |
||
|
|
9f0becab7b | ||
|
|
8a57104a0e | ||
|
|
bbaab0ebc1 | ||
|
|
55bb50d296 | ||
|
|
8bb7017a49 | ||
|
|
d950352545 | ||
|
|
2c347c7920 | ||
|
|
b95fe20845 | ||
|
|
8d7de7172a | ||
|
|
bda1bb05fd |
||
|
|
1e153910fa | ||
|
|
12b6c925dc |
||
|
|
05d377217d | ||
|
|
68c47edabb |
||
|
|
fec355cab4 |
||
|
|
abb3c6b387 |
||
|
|
c8334dc30c | ||
|
|
0ecc99a75c | ||
|
|
a74b9f2c65 |
||
|
|
c8a99831d6 | ||
|
|
144efab83d | ||
|
|
e2738de64e | ||
|
|
6944cb7c1f | ||
|
|
415a601963 |
||
|
|
ed6beff03b | ||
|
|
94b8f0a81d | ||
|
|
c02e308d4f |
||
|
|
74ef72b150 | ||
|
|
7d046574cb | ||
|
|
16c3b65cae | ||
|
|
8d025c475d | ||
|
|
bed68aa691 | ||
|
|
ccc966f985 | ||
|
|
6791b4bb58 |
||
|
|
28a02617ee | ||
|
|
105e6d4460 |
||
|
|
7297e12605 | ||
|
|
25b22d2a9e | ||
|
|
0057094179 | ||
|
|
5586d9e694 | ||
|
|
f173a043be |
168 changed files with 21489 additions and 3633 deletions
94
.github/ISSUE_TEMPLATE/bug_report.md
vendored
94
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -7,52 +7,108 @@ assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Describe the bug**
|
> DO NOT DELETE THIS TEMPLATE. IF YOU DELETE THIS TEMPLATE AND DO NOT COMPLETE IT, YOUR ISSUE WILL BE CLOSED.
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
### Describe the bug
|
||||||
List of steps to reproduce
|
|
||||||
|
|
||||||
Vimspector config file:
|
> Provide A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
### Minimal reproduciton
|
||||||
|
|
||||||
|
> Please answer the following questions
|
||||||
|
|
||||||
|
* Does your issue reproduce using `vim --clean -Nu /path/to/vimspector/support/minimal_vimrc` ? \[Yes/No]
|
||||||
|
* If you are using Neovim, does your issue reproduce using Vim? \[Yes/No]
|
||||||
|
|
||||||
|
> List of steps to reproduce:
|
||||||
|
|
||||||
|
> 1. Run `vim ---clean Nu /path/to/vimspector/support/minimal_vimrc`
|
||||||
|
> 2. Open _this project_...
|
||||||
|
> 3. Press _this sequence of keys_
|
||||||
|
|
||||||
|
> Use the following Vimspector config file:
|
||||||
|
|
||||||
```
|
```
|
||||||
paste .vimspector.json here
|
paste .vimspector.json here
|
||||||
```
|
```
|
||||||
|
|
||||||
**Expected behavior**
|
### Expected behaviour
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Actual behaviour**
|
> Provide A clear and concise description of what you expected to happen.
|
||||||
What actually happened, including output, log files etc.
|
|
||||||
|
|
||||||
Please include:
|
### Actual behaviour
|
||||||
* Vimspector log (~/.vimspector.log)
|
|
||||||
* Output from any or all UI diagnostic tabs (Server, etc.)
|
|
||||||
|
|
||||||
**Environemnt**
|
> What actually happened, including output, log files etc.
|
||||||
|
|
||||||
NOTE: NeoVim is not supported.
|
> Please include:
|
||||||
NOTE: Windows is not supported.
|
> * Vimspector log (~/.vimspector.log)
|
||||||
|
> * Output from any or all UI diagnostic tabs (Server, etc.)
|
||||||
|
|
||||||
* Output of `vim --version`
|
### Environemnt
|
||||||
|
|
||||||
|
***NOTE***: NeoVim is supported only on a best-effort basis. Please check the README
|
||||||
|
for limitations of neovim. Don't be offended if I ask you to reproduce issues in
|
||||||
|
Vim.
|
||||||
|
|
||||||
|
***NOTE***: Windows support is experimental and best-efrort only. If you find an
|
||||||
|
issue related to Windows or windows-isms, consider sending a PR or
|
||||||
|
discussing on Gitter rather than raising an issue.
|
||||||
|
|
||||||
|
* Version of Vimspector: (e.g. output of `git rev-parse HEAD` if cloned or the
|
||||||
|
name of the tarball used to install otherwise)
|
||||||
|
|
||||||
|
* Output of `:VimspectorDebugInfo`
|
||||||
|
|
||||||
```
|
```
|
||||||
paste here
|
paste here
|
||||||
```
|
```
|
||||||
|
|
||||||
* Output of `which vim`:
|
* Output of `vim --version` or `nvim --version`
|
||||||
|
|
||||||
```
|
```
|
||||||
paste here
|
paste here
|
||||||
```
|
```
|
||||||
|
|
||||||
* Output of `:py3 pass`:
|
* Output of `which vim` or `which nvim`:
|
||||||
|
|
||||||
|
```
|
||||||
|
paste here
|
||||||
|
```
|
||||||
|
|
||||||
|
* Output of `:py3 print( __import__( 'sys' ).version )`:
|
||||||
|
|
||||||
|
```
|
||||||
|
paste here
|
||||||
|
```
|
||||||
|
|
||||||
|
* Output of `:py3 import vim`:
|
||||||
|
|
||||||
|
```
|
||||||
|
paste here
|
||||||
|
```
|
||||||
|
|
||||||
|
* Output of `:py3 import vimspector`:
|
||||||
|
|
||||||
|
```
|
||||||
|
paste here
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
* For neovim: output of `:checkhealth`
|
||||||
|
|
||||||
```
|
```
|
||||||
paste here
|
paste here
|
||||||
```
|
```
|
||||||
|
|
||||||
* Operating system: <linux or macOS> and version
|
* Operating system: <linux or macOS> and version
|
||||||
|
|
||||||
|
### Declaration
|
||||||
|
|
||||||
|
> Please complete the following declaration. If this declaration is not completed, your issue may be closed without comment.
|
||||||
|
|
||||||
|
* I have read and understood [CONTRIBUTING.md](https://github.com/puremourning/vimspector/blob/master/CONTRIBUTING.md) \[Yes/No]
|
||||||
|
|
||||||
|
|
||||||
|
### Additional information
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
Add any other context about the problem here.
|
||||||
|
|
|
||||||
12
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
12
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Questions and support
|
||||||
|
url: http://gitter.im/vimspector/Lobby
|
||||||
|
about: Please ask and answer questions here.
|
||||||
|
- name: Discussions
|
||||||
|
url: https://github.com/puremourning/vimspector/discussions
|
||||||
|
about: Please post questions and useful hints here
|
||||||
|
- name: Support for additional languages
|
||||||
|
url: https://github.com/puremourning/vimspector/wiki/languages
|
||||||
|
about: Please see here for information on support for additional languages
|
||||||
|
|
||||||
200
.github/workflows/build.yaml
vendored
Normal file
200
.github/workflows/build.yaml
vendored
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
name: Build
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
PythonLint:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
container: 'puremourning/vimspector:test'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: 'Insatll requirements'
|
||||||
|
run: pip3 install --user -r dev_requirements.txt
|
||||||
|
- name: 'Run flake8'
|
||||||
|
run: '$HOME/.local/bin/flake8 python3/ *.py'
|
||||||
|
VimscriptLint:
|
||||||
|
runs-on: 'ubuntu-18.04'
|
||||||
|
container: 'puremourning/vimspector:test'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: 'Install requirements'
|
||||||
|
run: pip3 install --user -r dev_requirements.txt
|
||||||
|
- name: 'Run vint'
|
||||||
|
run: $HOME/.local/bin/vint autoload/ compiler/ plugin/ tests/ syntax/
|
||||||
|
|
||||||
|
Linux:
|
||||||
|
runs-on: 'ubuntu-18.04'
|
||||||
|
container:
|
||||||
|
image: 'puremourning/vimspector:test'
|
||||||
|
options: --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
|
||||||
|
go get -u github.com/go-delve/delve/cmd/dlv
|
||||||
|
name: 'Install Delve for Go'
|
||||||
|
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
key: v1-gadgets-${{ runner.os }}-${{ hashFiles( 'python3/vimspector/gadgets.py' ) }}
|
||||||
|
path: gadgets/linux/download
|
||||||
|
name: Cache gadgets
|
||||||
|
|
||||||
|
- run: vim --version
|
||||||
|
name: 'Print vim version information'
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
|
||||||
|
export GOPATH=$HOME/go
|
||||||
|
./run_tests --install --update --report messages --quiet
|
||||||
|
name: 'Run the tests'
|
||||||
|
id: run_tests
|
||||||
|
env:
|
||||||
|
VIMSPECTOR_MIMODE: gdb
|
||||||
|
|
||||||
|
- name: "Upload test logs"
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: 'test-logs-${{ runner.os }}'
|
||||||
|
path: 'tests/logs/**/*'
|
||||||
|
|
||||||
|
- run: ./make_package linux ${{ github.run_id }}
|
||||||
|
name: 'Package'
|
||||||
|
|
||||||
|
# TODO: test the tarball
|
||||||
|
|
||||||
|
- name: "Upload package"
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: 'package-linux'
|
||||||
|
path: 'package/linux-${{ github.run_id }}.tar.gz'
|
||||||
|
|
||||||
|
# - name: Start SSH session if failed
|
||||||
|
# uses: luchihoratiu/debug-via-ssh@main
|
||||||
|
# if: failure()
|
||||||
|
# with:
|
||||||
|
# NGROK_AUTH_TOKEN: ${{ secrets.NGROK_AUTH_TOKEN }}
|
||||||
|
# SSH_PASS: ${{ secrets.SSH_PASS }}
|
||||||
|
|
||||||
|
MacOS:
|
||||||
|
runs-on: 'macos-10.15'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
brew update-reset
|
||||||
|
brew doctor || true
|
||||||
|
for p in python@3.8 tcl-tk llvm lua luajit love; do
|
||||||
|
brew install $p || brew outdated $p || brew upgrade $p
|
||||||
|
done
|
||||||
|
brew install --cask macvim
|
||||||
|
brew link --overwrite python@3.8
|
||||||
|
name: 'Install vim and deps'
|
||||||
|
|
||||||
|
- run: go get -u github.com/go-delve/delve/cmd/dlv
|
||||||
|
name: 'Install Delve for Go'
|
||||||
|
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
key: v1-gadgets-${{ runner.os }}-${{ hashFiles( 'python3/vimspector/gadgets.py' ) }}
|
||||||
|
path: gadgets/macos/download
|
||||||
|
name: Cache gadgets
|
||||||
|
|
||||||
|
- name: 'Install .NET Core SDK 3.1'
|
||||||
|
uses: actions/setup-dotnet@v1.7.2
|
||||||
|
with:
|
||||||
|
dotnet-version: 3.1
|
||||||
|
|
||||||
|
- uses: maxim-lobanov/setup-xcode@v1
|
||||||
|
with:
|
||||||
|
xcode-version: ^11
|
||||||
|
name: "Switch to xcode 11 because of .NET debugging bug"
|
||||||
|
|
||||||
|
- run: vim --version
|
||||||
|
name: 'Print vim version information'
|
||||||
|
|
||||||
|
- run: ./run_tests --install --update --report messages --quiet
|
||||||
|
name: 'Run the tests'
|
||||||
|
id: run_tests
|
||||||
|
env:
|
||||||
|
VIMSPECTOR_MIMODE: lldb
|
||||||
|
|
||||||
|
- name: "Upload test logs"
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: 'test-logs-${{ runner.os }}'
|
||||||
|
path: 'tests/logs'
|
||||||
|
|
||||||
|
- run: ./make_package macos ${{ github.run_id }}
|
||||||
|
name: 'Package'
|
||||||
|
|
||||||
|
# TODO: test the tarball
|
||||||
|
|
||||||
|
- name: "Upload package"
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: 'package-macos'
|
||||||
|
path: 'package/macos-${{ github.run_id }}.tar.gz'
|
||||||
|
|
||||||
|
# - name: Start SSH session if failed
|
||||||
|
# uses: luchihoratiu/debug-via-ssh@main
|
||||||
|
# if: failure()
|
||||||
|
# with:
|
||||||
|
# NGROK_AUTH_TOKEN: ${{ secrets.NGROK_AUTH_TOKEN }}
|
||||||
|
# SSH_PASS: ${{ secrets.SSH_PASS }} # [V]imspector
|
||||||
|
|
||||||
|
PublishRelease:
|
||||||
|
runs-on: 'ubuntu-18.04'
|
||||||
|
needs:
|
||||||
|
- Linux
|
||||||
|
- MacOS
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
steps:
|
||||||
|
- name: 'Download artifacts'
|
||||||
|
id: download_artifacts
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
|
||||||
|
- name: 'Create Release'
|
||||||
|
id: create_release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.run_id }}
|
||||||
|
release_name: Build ${{ github.run_id }}
|
||||||
|
draft: false
|
||||||
|
prerelease: true
|
||||||
|
|
||||||
|
- name: 'Upload Linux Package'
|
||||||
|
id: upload-release-asset-linux
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ${{ steps.download_artifacts.outputs.download-path }}/package-linux/linux-${{ github.run_id }}.tar.gz
|
||||||
|
asset_name: vimspector-linux-${{ github.run_id }}.tar.gz
|
||||||
|
asset_content_type: application/gzip
|
||||||
|
|
||||||
|
- name: 'Upload MacOS Package'
|
||||||
|
id: upload-release-asset-macos
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ${{ steps.download_artifacts.outputs.download-path }}/package-macos/macos-${{ github.run_id }}.tar.gz
|
||||||
|
asset_name: vimspector-macos-${{ github.run_id }}.tar.gz
|
||||||
|
asset_content_type: application/gzip
|
||||||
27
.github/workflows/lock_old_issues.yaml
vendored
Normal file
27
.github/workflows/lock_old_issues.yaml
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
name: "Lock Old Issues"
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lock:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: dessant/lock-threads@v2
|
||||||
|
with:
|
||||||
|
github-token: ${{ github.token }}
|
||||||
|
issue-lock-inactive-days: '60'
|
||||||
|
# issue-exclude-created-before: ''
|
||||||
|
# issue-exclude-labels: ''
|
||||||
|
# issue-lock-labels: ''
|
||||||
|
# issue-lock-comment: ''
|
||||||
|
# issue-lock-reason: 'resolved'
|
||||||
|
# pr-lock-inactive-days: '365'
|
||||||
|
# pr-exclude-created-before: ''
|
||||||
|
# pr-exclude-labels: ''
|
||||||
|
# pr-lock-labels: ''
|
||||||
|
# pr-lock-comment: ''
|
||||||
|
# pr-lock-reason: 'resolved'
|
||||||
|
process-only: 'issues'
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -6,6 +6,7 @@ tests/*.res
|
||||||
tests/messages
|
tests/messages
|
||||||
tests/debuglog
|
tests/debuglog
|
||||||
test.log
|
test.log
|
||||||
|
*.testlog
|
||||||
gadgets/
|
gadgets/
|
||||||
package/
|
package/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
@ -15,3 +16,8 @@ README.md.toc.*
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.vimspector.log
|
*.vimspector.log
|
||||||
support/test/csharp/*.exe*
|
support/test/csharp/*.exe*
|
||||||
|
.neomake.log
|
||||||
|
configurations/
|
||||||
|
venv/
|
||||||
|
test-base/
|
||||||
|
tags
|
||||||
|
|
|
||||||
19
.mergify.yml
19
.mergify.yml
|
|
@ -3,11 +3,14 @@ pull_request_rules:
|
||||||
conditions:
|
conditions:
|
||||||
- author=puremourning
|
- author=puremourning
|
||||||
- base=master
|
- base=master
|
||||||
|
# Review
|
||||||
- status-success=code-review/reviewable
|
- status-success=code-review/reviewable
|
||||||
- status-success=puremourning.vimspector # Azure pipeline
|
|
||||||
|
|
||||||
- "#changes-requested-reviews-by=0"
|
- "#changes-requested-reviews-by=0"
|
||||||
|
# CI https://doc.mergify.io/conditions.html#github-actions
|
||||||
|
- status-success=PythonLint
|
||||||
|
- status-success=VimscriptLint
|
||||||
|
- status-success=Linux
|
||||||
|
- status-success=MacOS
|
||||||
actions: &merge-actions
|
actions: &merge-actions
|
||||||
merge:
|
merge:
|
||||||
method: merge
|
method: merge
|
||||||
|
|
@ -18,12 +21,16 @@ pull_request_rules:
|
||||||
conditions:
|
conditions:
|
||||||
- author!=puremourning
|
- author!=puremourning
|
||||||
- base=master
|
- base=master
|
||||||
|
# Review
|
||||||
- status-success=code-review/reviewable
|
- status-success=code-review/reviewable
|
||||||
- status-success=puremourning.vimspector # Azure pipeline
|
|
||||||
- approved-reviews-by=puremourning
|
|
||||||
|
|
||||||
- "#approved-reviews-by>=1"
|
- "#approved-reviews-by>=1"
|
||||||
- "#changes-requested-reviews-by=0"
|
- "#changes-requested-reviews-by=0"
|
||||||
|
- approved-reviews-by=puremourning
|
||||||
|
# CI https://doc.mergify.io/conditions.html#github-actions
|
||||||
|
- status-success=PythonLint
|
||||||
|
- status-success=VimscriptLint
|
||||||
|
- status-success=Linux
|
||||||
|
- status-success=MacOS
|
||||||
actions:
|
actions:
|
||||||
<<: *merge-actions
|
<<: *merge-actions
|
||||||
comment:
|
comment:
|
||||||
|
|
|
||||||
140
.vimspector.json
140
.vimspector.json
|
|
@ -1,138 +1,22 @@
|
||||||
{
|
{
|
||||||
"adapters": {
|
|
||||||
"lldb-mi": {
|
|
||||||
"name": "lldb-mi",
|
|
||||||
"command": [
|
|
||||||
"node",
|
|
||||||
"$HOME/.vscode/extensions/webfreak.debug-0.22.0/out/src/lldb.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"cppdbg": {
|
|
||||||
"name": "cppdbg",
|
|
||||||
"command": [ "$HOME/.vscode/extensions/ms-vscode.cpptools-0.20.1/debugAdapters/OpenDebugAD7" ],
|
|
||||||
"attach": {
|
|
||||||
"pidProperty": "processId",
|
|
||||||
"pidSelect": "ask"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"python": {
|
|
||||||
"name": "python",
|
|
||||||
"command": [
|
|
||||||
"node",
|
|
||||||
"$HOME/.vscode/extensions/ms-python.python-2018.4.0/out/client/debugger/Main.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"bashdb": {
|
|
||||||
"name": "bashdb",
|
|
||||||
"command": [
|
|
||||||
"node",
|
|
||||||
"$HOME/.vscode/extensions/rogalmic.bash-debug-0.2.0/out/bashDebug.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"lldb": {
|
|
||||||
"name": "lldb",
|
|
||||||
"command": [
|
|
||||||
"lldb",
|
|
||||||
"-b",
|
|
||||||
"-O",
|
|
||||||
"command script import '$HOME/.vscode/extensions/vadimcn.vscode-lldb-0.8.7/adapter'",
|
|
||||||
"-O",
|
|
||||||
"script adapter.main.run_stdio_session()"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"simple_c_program - lldb-mi Launch": {
|
"Python: Attach To Vim": {
|
||||||
"adapter": "lldb-mi",
|
"variables": {
|
||||||
|
"port": "5678",
|
||||||
|
"host": "localhost"
|
||||||
|
},
|
||||||
|
"adapter": "multi-session",
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"request": "launch",
|
"request": "attach"
|
||||||
"target": "support/test/cpp/simple_c_program/test",
|
|
||||||
"args": [],
|
|
||||||
"cwd": ".",
|
|
||||||
"lldbmipath": "$HOME/.vscode/extensions/ms-vscode.cpptools-0.20.1/debugAdapters/lldb/bin/lldb-mi",
|
|
||||||
"trace": true,
|
|
||||||
"logFilePath": "$HOME/.vimspector.protocol.log"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"simple_c_progra - ms Launch": {
|
"Python: Run current script": {
|
||||||
"adapter": "cppdbg",
|
"adapter": "debugpy",
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"name": "ms Launch",
|
|
||||||
"type": "cppdbg",
|
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceRoot}/support/test/cpp/simple_c_program/test",
|
"program": "${file}",
|
||||||
"args": [],
|
"args": [ "*${args:--update-gadget-config}" ],
|
||||||
"cwd": "$HOME",
|
"justMyCode#json": "${justMyCode:true}"
|
||||||
"environment": [],
|
|
||||||
"externalConsole": true,
|
|
||||||
"MIMode": "lldb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"simple_python - launch": {
|
|
||||||
"adapter": "python",
|
|
||||||
"configuration": {
|
|
||||||
"name": "Python: Current File",
|
|
||||||
"type": "python",
|
|
||||||
"request": "launch",
|
|
||||||
"cwd": "${workspaceRoot}/support/test/python/simple_python",
|
|
||||||
"stopOnEntry": true,
|
|
||||||
"console": "externalTerminal",
|
|
||||||
"debugOptions": [],
|
|
||||||
"program": "${workspaceRoot}/support/test/python/simple_python/main.py"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"simple_c_program - MS Attach": {
|
|
||||||
"adapter": "cppdbg",
|
|
||||||
"configuration": {
|
|
||||||
"name": "(lldb) Attach",
|
|
||||||
"type": "cppdbg",
|
|
||||||
"request": "attach",
|
|
||||||
"program": "${workspaceRoot}/support/test/cpp/simple_c_program/test",
|
|
||||||
"MIMode": "lldb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bashdb": {
|
|
||||||
"adapter": "bashdb",
|
|
||||||
"configuration": {
|
|
||||||
"type": "bashdb",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Bash-Debug (simplest configuration)",
|
|
||||||
"program": "$HOME/.vim/bundle/YouCompleteMe/install.sh",
|
|
||||||
"args": [],
|
|
||||||
"cwd": "$HOME/.vim/bundle/YouCompleteMe",
|
|
||||||
"pathBash": "bash",
|
|
||||||
"pathBashdb": "bashdb",
|
|
||||||
"pathCat": "cat",
|
|
||||||
"pathMkfifo": "mkfifo",
|
|
||||||
"pathPkill": "pkill",
|
|
||||||
"showDebugOutput": true,
|
|
||||||
"trace": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lldb launch": {
|
|
||||||
"adapter": "lldb",
|
|
||||||
"configuration": {
|
|
||||||
"type": "lldb",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "LLDB: Launch",
|
|
||||||
"program": "$HOME/Development/vim/src/vim",
|
|
||||||
"args": [],
|
|
||||||
"cwd": "$HOME/Development/vim"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"racerd": {
|
|
||||||
"adapter": "lldb",
|
|
||||||
"configuration": {
|
|
||||||
"type": "lldb",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "LLDB: Launch",
|
|
||||||
"program": "$HOME/.vim/bundle/YouCompleteMe/third_party/ycmd/third_party/racerd/target/debug/racerd",
|
|
||||||
"args": [
|
|
||||||
"serve",
|
|
||||||
"--port=12345",
|
|
||||||
"--secret-file=secretfile"
|
|
||||||
],
|
|
||||||
"cwd": "$HOME/.vim/bundle/YouCompleteMe/third_party/ycmd"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
.vintrc.yml
Normal file
3
.vintrc.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
cmdargs:
|
||||||
|
color: true
|
||||||
|
severity: style_problem
|
||||||
61
.ycm_extra_conf.py
Normal file
61
.ycm_extra_conf.py
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
try:
|
||||||
|
from ycmd.extra_conf_support import IgnoreExtraConf
|
||||||
|
except ImportError:
|
||||||
|
IgnoreExtraConf = None
|
||||||
|
|
||||||
|
import os.path as p
|
||||||
|
|
||||||
|
PATH_TO_THIS_DIR = p.dirname( p.abspath( __file__ ) )
|
||||||
|
|
||||||
|
|
||||||
|
def Settings( **kwargs ):
|
||||||
|
if kwargs[ 'language' ] == 'json':
|
||||||
|
return {
|
||||||
|
'ls': {
|
||||||
|
'json': {
|
||||||
|
'schemas': [
|
||||||
|
{
|
||||||
|
'fileMatch': [ '.vimspector.json' ],
|
||||||
|
'url':
|
||||||
|
f'file://{PATH_TO_THIS_DIR}/docs/schema/vimspector.schema.json'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'fileMatch': [ '.gadgets.json', '.gadgets.d/*.json' ],
|
||||||
|
'url':
|
||||||
|
f'file://{PATH_TO_THIS_DIR}/docs/schema/gadgets.schema.json'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'capabilities': {
|
||||||
|
'textDocument': {
|
||||||
|
'completion': {
|
||||||
|
'completionItem': {
|
||||||
|
'snippetSupport': True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if kwargs[ 'language' ] == 'python':
|
||||||
|
return {
|
||||||
|
'sys_path': [
|
||||||
|
p.join( PATH_TO_THIS_DIR, 'python3' )
|
||||||
|
],
|
||||||
|
'ls': {
|
||||||
|
'python': {
|
||||||
|
'analysis': {
|
||||||
|
'extraPaths': [
|
||||||
|
p.join( PATH_TO_THIS_DIR, 'python3' ),
|
||||||
|
],
|
||||||
|
'useLibraryCodeForTypes': True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if IgnoreExtraConf:
|
||||||
|
raise IgnoreExtraConf()
|
||||||
|
|
||||||
|
return None
|
||||||
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity and
|
||||||
|
orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the overall
|
||||||
|
community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||||
|
any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email address,
|
||||||
|
without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
`benjacksonbkg@gmail.com`. All complaints will be reviewed and investigated
|
||||||
|
promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series of
|
||||||
|
actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or permanent
|
||||||
|
ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||||
|
community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
||||||
235
CONTRIBUTING.md
Normal file
235
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,235 @@
|
||||||
|
# Contributing to Vimspector
|
||||||
|
|
||||||
|
Contributions to Vimspector are always welcome. Contributions can take many
|
||||||
|
forms, such as:
|
||||||
|
|
||||||
|
* Raising, responding to, or reacting to Issues or Pull Requests
|
||||||
|
* Testing new in-progress changes and providing feedback
|
||||||
|
* Discussing in the Gitter channel
|
||||||
|
* etc.
|
||||||
|
|
||||||
|
At all times the [code of conduct](#code-of-conduct) applies.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
It's not completely trivial to configure Vimspector and there is a fairly large
|
||||||
|
amount of documentation. I know full well that documentation isn't everything,
|
||||||
|
so the first step in troubleshooting is to try a sample project that's known to
|
||||||
|
work, to check if the problem is your project configuration rather than an
|
||||||
|
actual bug.
|
||||||
|
|
||||||
|
Therefore before raising an issue for a supported language, please check with
|
||||||
|
the sample projects in `support/test/<language>` and `tests/testdata/` to see if
|
||||||
|
the problem is with your project settings, rather than with vimspector.
|
||||||
|
|
||||||
|
Information on these is in [the README](README.md#trying-it-out).
|
||||||
|
|
||||||
|
If in doubt, ask on Gitter.
|
||||||
|
|
||||||
|
## Diagnostics
|
||||||
|
|
||||||
|
Whenever reporting any type of fault, or difficulty in making the plugin
|
||||||
|
work, please always include _all_ of the diagnostics requested in the
|
||||||
|
[issue template][issue-template]. Please do not be offended if your request
|
||||||
|
is ignored if it does not include the requested diagnostics.
|
||||||
|
|
||||||
|
The Vimspector log file contains a full trace of the communication between
|
||||||
|
Vimspector and the debug adapter. This is the primary source of diagnostic
|
||||||
|
information when something goes wrong that's not a clear Vim traceback.
|
||||||
|
|
||||||
|
If you just want to see the Vimspector log file, use `:VimspectorToggleLog`,
|
||||||
|
which will tail it in a little window (doesn't work on Windows).
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
The GitHub issue tracker is for *bug reports* and *features requests* for the
|
||||||
|
Vimspector project, and on-topic comments and follow-ups to them. It is not for
|
||||||
|
general discussion, general support or for any other purpose.
|
||||||
|
|
||||||
|
Please **search the issue tracker for similar issues** before creating a new
|
||||||
|
one. There's no point in duplication; if an existing open issue addresses your
|
||||||
|
problem, please comment there instead of creating a duplicate. However, if the
|
||||||
|
issue you found is **closed as resolved** (e.g. with a PR or the original user's
|
||||||
|
problem was resolved), raise a **new issue**, because you've found a new
|
||||||
|
problem. Reference the original issue if you think that's useful information.
|
||||||
|
|
||||||
|
Closed issues which have been inactive for 60 days will be locked, this helps to
|
||||||
|
keep discussions focussed. If you believe you are still experiencing an issue
|
||||||
|
which has been closed, please raise a new issue, completing the issue template.
|
||||||
|
|
||||||
|
If you do find a similar _open_ issue, **don't just post 'me too' or similar**
|
||||||
|
responses. This almost never helps resolve the issue, and just causes noise for
|
||||||
|
the maintainers. Only post if it will aid the maintainers in solving the issue;
|
||||||
|
if there are existing diagnostics requested in the thread, perform
|
||||||
|
them and post the results.
|
||||||
|
|
||||||
|
Please do not be offended if your Issue or comment is closed or hidden, for any
|
||||||
|
of the following reasons:
|
||||||
|
|
||||||
|
* The [issue template][issue-template] was not completed
|
||||||
|
* The issue or comment is off-topic
|
||||||
|
* The issue does not represent a Vimspector bug or feature request
|
||||||
|
* The issue cannot be reasonably reproduced using the minimal vimrc
|
||||||
|
* The issue is a duplicate of an existing issue
|
||||||
|
* etc.
|
||||||
|
|
||||||
|
Issue titles are important. It's not usually helpful to write a title like
|
||||||
|
`Issue with Vimspector` or `Issue configuring` or even pasting an error message.
|
||||||
|
Spend a minute to come up with a consise summary of the problem. This helps with
|
||||||
|
management of issues, with triage, and above all with searching.
|
||||||
|
|
||||||
|
But above all else, please *please* complete the issue template. I know it is a
|
||||||
|
little tedious to get all the various diagnostics, but you *must* provide them,
|
||||||
|
*even if you think they are irrelevant*. This is important, because the
|
||||||
|
maintainer(s) can quickly cross-check theories by inspecting the provided
|
||||||
|
diagnostics without having to spend time asking for them, and waiting for the
|
||||||
|
response. This means *you get a better answer, faster*. So it's worth it,
|
||||||
|
honestly.
|
||||||
|
|
||||||
|
### Reproduce your issue with the minimal vimrc
|
||||||
|
|
||||||
|
Many problems can be caused by unexpected configuration or other plugins.
|
||||||
|
Therefore when raising an issue, you must attempt to reproduce your issue
|
||||||
|
with the minimal vimrc provided, and to provide any additional changes required
|
||||||
|
to that file in order to reproduce it. The purpose of this is to ensure that
|
||||||
|
the issue is not a conflict with another plugin, or a problem unique to your
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
If your issue does _not_ reproduce with the minimal vimrc, then you must say so
|
||||||
|
in the issue report.
|
||||||
|
|
||||||
|
The minimal vimrc is in `support/test/minimal_vimrc` and can be used as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
vim --clean -Nu /path/to/vimspector/support/minimal_vimrc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pull Requests
|
||||||
|
|
||||||
|
Vimspector is open to all contributors with ideas great and small! However,
|
||||||
|
there is a limit to the intended scope of the plugin and the amount of time the
|
||||||
|
maintainer has to support and... well... maintain features. It's probably well
|
||||||
|
understood that the contributor's input typically ends when a PR is megred, but
|
||||||
|
the maintainers have to keep it working forever.
|
||||||
|
|
||||||
|
### Small changes
|
||||||
|
|
||||||
|
For bug fixes, documentation changes, gadget versin updates, etc. please just
|
||||||
|
send a PR, I'm super happy to merge these!
|
||||||
|
|
||||||
|
If you are unsure, or looking for some pointers, feel free to ask in Gitter, or
|
||||||
|
mention is in the PR.
|
||||||
|
|
||||||
|
### Larger changes
|
||||||
|
|
||||||
|
For larger features that might be in any way controvertial, or increase the
|
||||||
|
complexity of the overall plugin, please come to Gitter and talk to the
|
||||||
|
maintainer(s) first. This saves a lot of potential back-and-forth and makes sure
|
||||||
|
that we're "on the same page" about the idea and the ongoing maintenance.
|
||||||
|
|
||||||
|
In addition, if you like hacking, feel free to raise a PR tagged with `[RFC]` in
|
||||||
|
the title and we can discuss the idea. I still prefer to discuss these things on
|
||||||
|
Gitter rather than back-and-forth on GitHub, though.
|
||||||
|
|
||||||
|
Please don't be offended if the maintainer(s) request significant rework for (or
|
||||||
|
perhaps even dismiss) a PR that's not gone through this process.
|
||||||
|
|
||||||
|
Please also don't be offended if the maintainer(s) ask if you're willing to
|
||||||
|
provide ongoing support for the feature. As an OSS project manned entirely in
|
||||||
|
what little spare time the maintainer(s) have, we're always looking for
|
||||||
|
contributions and contributors who will help with support and maintenance of
|
||||||
|
larger new features.
|
||||||
|
|
||||||
|
### PR Guidelines
|
||||||
|
|
||||||
|
When contributing pull requests, I ask that:
|
||||||
|
|
||||||
|
* You provide a clear and complete summary of the change, the use case and how
|
||||||
|
the change was tested.
|
||||||
|
* You avoid using APIs that are not available in the versions listed in the
|
||||||
|
dependencies on README.md
|
||||||
|
* You add tests for your PR.
|
||||||
|
* You test your changes in both Vim and Neovim at the supported versions (and
|
||||||
|
state that in the PR).
|
||||||
|
* You follow the style of the code as-is; the python code is YCM-stye, it is
|
||||||
|
*not* PEP8, nor should it be.
|
||||||
|
|
||||||
|
### Running the tests locally
|
||||||
|
|
||||||
|
There are 2 ways:
|
||||||
|
|
||||||
|
1. In the docker container. The CI tests for linux run in a container, so as to
|
||||||
|
ensure a consistent test environment. The container is defined in
|
||||||
|
`./tests/ci/`. There is also a container in `./tests/manual` which can be
|
||||||
|
used to run the tests interractively. To do this install and start docker,
|
||||||
|
then run `./tests/manual/run`. This will drop you into a bash shell inside
|
||||||
|
the linux container with your local vimspector code mounted. You can then
|
||||||
|
follow the instructions for running tets directly.
|
||||||
|
1. Directly: Run `./install_gadget.py --all` and then `./run_tests`. Note that
|
||||||
|
this depends on your runtime environment and might not match CI. I recommend
|
||||||
|
running the tests in the docker container. If you have your own custom
|
||||||
|
gadgets and/or custom configurations (in `vimspector/configurations` and/or
|
||||||
|
`vimspector/gadget`, then consider using `./run_tests --install --basedir
|
||||||
|
/tmp/vimspector_test` (then delete `/tmp/vimspector_test`). This will install
|
||||||
|
the gadgets to that dir and use it for the gadget dir/config dir so that your
|
||||||
|
custom configuration won't interfere with the tess.
|
||||||
|
|
||||||
|
When tests fail, they dump a load of logs to a directory for each failed tests.
|
||||||
|
Usually the most useful output is `messages`, which tells you what actually
|
||||||
|
failed.
|
||||||
|
|
||||||
|
For more infomration on the test framework, see
|
||||||
|
[this article](https://vimways.org/2019/a-test-to-attest-to/), authored by the
|
||||||
|
Vimspector creator.
|
||||||
|
|
||||||
|
### Code Style
|
||||||
|
|
||||||
|
The code style of the Python code is "YCM" style, because that's how I like it.
|
||||||
|
`flake8` is used to check for certain errors and code style.
|
||||||
|
|
||||||
|
The code style of the Vimscript is largely the same, and it is linted by
|
||||||
|
`vint`.
|
||||||
|
|
||||||
|
To run them:
|
||||||
|
|
||||||
|
* (optional) Create and activate a virtual env:
|
||||||
|
`python3 -m venv venv ; source venv/bin/activate`
|
||||||
|
* Install the development dependencies: `pip install -r dev_requirements.txt`
|
||||||
|
* Run `flake8`: `flake8 python3/ *.py`
|
||||||
|
* Run `vint`: `vint autoload/ plugin/ tests/`
|
||||||
|
|
||||||
|
They're also run by CI, so please check for lint failures. The canonical
|
||||||
|
definition of the command to run is the command run in CI, i.e. in
|
||||||
|
`.git/workflows/build.yml`.
|
||||||
|
|
||||||
|
### Debugging Vimspector
|
||||||
|
|
||||||
|
You can debug vimspector's python code using vimspector! We can use debugpy,
|
||||||
|
from within Vim's embedded python and connect to it. Here's how:
|
||||||
|
|
||||||
|
1. In one instance of vim, run the following to get debugpy to start listening
|
||||||
|
for us to connect: `:py3 __import__( 'vimspector', fromlist=[ 'developer' ]
|
||||||
|
).developer.SetUpDebugpy()`
|
||||||
|
|
||||||
|
2. In another instance of Vim, set a breakpoint in the vimspector python code
|
||||||
|
you want to debug and launch vimspector (e.g. `<F5>`). Select the `Python:
|
||||||
|
attach to vim` profile. This will attach to the debugpy running in the other
|
||||||
|
vim.
|
||||||
|
|
||||||
|
3. Back in the first vim (the debuggee), trigger the vimspector code in
|
||||||
|
question, e.g. by starting to debug something else.
|
||||||
|
|
||||||
|
4. You'll see it pause, and the 2nd vim (the debugger), you should be able to
|
||||||
|
step through and inspect as with any other python remote debugging.
|
||||||
|
|
||||||
|
NB. It's also possible to debug the vimscript code using vimspector, but this
|
||||||
|
requires unreleased vim patches and a fair amount of faff. You can always use
|
||||||
|
`:debug` (see the help) for this though.
|
||||||
|
|
||||||
|
# Code of conduct
|
||||||
|
|
||||||
|
Please see [code of conduct](CODE_OF_CONDUCT.md).
|
||||||
|
|
||||||
|
[vint]: https://github.com/Vimjas/vint
|
||||||
|
[flake8]: https://flake8.pycqa.org/en/latest/
|
||||||
|
[issue-template]: https://github.com/puremourning/vimspector/blob/master/.github/ISSUE_TEMPLATE/bug_report.md
|
||||||
|
|
@ -13,103 +13,559 @@
|
||||||
" See the License for the specific language governing permissions and
|
" See the License for the specific language governing permissions and
|
||||||
" limitations under the License.
|
" limitations under the License.
|
||||||
|
|
||||||
|
if !has( 'python3' )
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
" Boilerplate {{{
|
" Boilerplate {{{
|
||||||
let s:save_cpo = &cpoptions
|
let s:save_cpo = &cpoptions
|
||||||
set cpoptions&vim
|
set cpoptions&vim
|
||||||
" }}}
|
" }}}
|
||||||
|
|
||||||
|
function! s:Debug( ... ) abort
|
||||||
|
py3 <<EOF
|
||||||
|
if _vimspector_session is not None:
|
||||||
|
_vimspector_session._logger.debug( *vim.eval( 'a:000' ) )
|
||||||
|
EOF
|
||||||
|
endfunction
|
||||||
|
|
||||||
call vimspector#internal#state#Reset()
|
|
||||||
|
|
||||||
function! vimspector#Launch() abort
|
let s:enabled = v:null
|
||||||
py3 _vimspector_session.Start()
|
|
||||||
|
function! s:Initialised() abort
|
||||||
|
return s:enabled != v:null
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:Enabled() abort
|
||||||
|
if !s:Initialised()
|
||||||
|
let s:enabled = vimspector#internal#state#Reset()
|
||||||
|
endif
|
||||||
|
|
||||||
|
return s:enabled
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#Launch( ... ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.Start( *vim.eval( 'a:000' ) )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#LaunchWithSettings( settings ) abort
|
function! vimspector#LaunchWithSettings( settings ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
py3 _vimspector_session.Start( launch_variables = vim.eval( 'a:settings' ) )
|
py3 _vimspector_session.Start( launch_variables = vim.eval( 'a:settings' ) )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#Reset() abort
|
function! vimspector#Reset( ... ) abort
|
||||||
py3 _vimspector_session.Reset()
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
if a:0 == 0
|
||||||
|
let options = {}
|
||||||
|
else
|
||||||
|
let options = a:1
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.Reset( **vim.eval( 'options' ) )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#Restart() abort
|
function! vimspector#Restart() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
py3 _vimspector_session.Restart()
|
py3 _vimspector_session.Restart()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#ClearBreakpoints() abort
|
function! vimspector#ClearBreakpoints() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
py3 _vimspector_session.ClearBreakpoints()
|
py3 _vimspector_session.ClearBreakpoints()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#ToggleBreakpoint() abort
|
function! vimspector#ToggleBreakpoint( ... ) abort
|
||||||
py3 _vimspector_session.ToggleBreakpoint()
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
if a:0 == 0
|
||||||
|
let options = {}
|
||||||
|
else
|
||||||
|
let options = a:1
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.ToggleBreakpoint( vim.eval( 'options' ) )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#AddFunctionBreakpoint( function ) abort
|
function! vimspector#SetLineBreakpoint( file_name, line_num, ... ) abort
|
||||||
py3 _vimspector_session.AddFunctionBreakpoint( vim.eval( 'a:function' ) )
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
if a:0 == 0
|
||||||
|
let options = {}
|
||||||
|
else
|
||||||
|
let options = a:1
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.SetLineBreakpoint(
|
||||||
|
\ vim.eval( 'a:file_name' ),
|
||||||
|
\ int( vim.eval( 'a:line_num' ) ),
|
||||||
|
\ vim.eval( 'options' ) )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#ClearLineBreakpoint( file_name, line_num ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.ClearLineBreakpoint(
|
||||||
|
\ vim.eval( 'a:file_name' ),
|
||||||
|
\ int( vim.eval( 'a:line_num' ) ) )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! vimspector#RunToCursor() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.RunTo(
|
||||||
|
\ vim.eval( "expand( '%' )" ),
|
||||||
|
\ int( vim.eval( "line( '.' )" ) ) )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! vimspector#AddFunctionBreakpoint( function, ... ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
if a:0 == 0
|
||||||
|
let options = {}
|
||||||
|
else
|
||||||
|
let options = a:1
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.AddFunctionBreakpoint( vim.eval( 'a:function' ),
|
||||||
|
\ vim.eval( 'options' ) )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#StepOver() abort
|
function! vimspector#StepOver() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
py3 _vimspector_session.StepOver()
|
py3 _vimspector_session.StepOver()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#StepInto() abort
|
function! vimspector#StepInto() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
py3 _vimspector_session.StepInto()
|
py3 _vimspector_session.StepInto()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#StepOut() abort
|
function! vimspector#StepOut() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
py3 _vimspector_session.StepOut()
|
py3 _vimspector_session.StepOut()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#Continue() abort
|
function! vimspector#Continue() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
py3 _vimspector_session.Continue()
|
py3 _vimspector_session.Continue()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#Pause() abort
|
function! vimspector#Pause() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
py3 _vimspector_session.Pause()
|
py3 _vimspector_session.Pause()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#Stop() abort
|
function! vimspector#PauseContinueThread() abort
|
||||||
py3 _vimspector_session.Stop()
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.PauseContinueThread()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#SetCurrentThread() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.SetCurrentThread()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#Stop( ... ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
if a:0 == 0
|
||||||
|
let options = {}
|
||||||
|
else
|
||||||
|
let options = a:1
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.Stop( **vim.eval( 'options' ) )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#ExpandVariable() abort
|
function! vimspector#ExpandVariable() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
py3 _vimspector_session.ExpandVariable()
|
py3 _vimspector_session.ExpandVariable()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#SetVariableValue( ... ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
if a:0 == 0
|
||||||
|
py3 _vimspector_session.SetVariableValue()
|
||||||
|
else
|
||||||
|
py3 _vimspector_session.SetVariableValue( new_value = vim.eval( 'a:1' ) )
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
function! vimspector#DeleteWatch() abort
|
function! vimspector#DeleteWatch() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
py3 _vimspector_session.DeleteWatch()
|
py3 _vimspector_session.DeleteWatch()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#GoToFrame() abort
|
function! vimspector#GoToFrame() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
py3 _vimspector_session.ExpandFrameOrThread()
|
py3 _vimspector_session.ExpandFrameOrThread()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#AddWatch( expr ) abort
|
function! vimspector#UpFrame() abort
|
||||||
py3 _vimspector_session.AddWatch( vim.eval( 'a:expr' ) )
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.UpFrame()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#DownFrame() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.DownFrame()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#AddWatch( ... ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
if a:0 == 0
|
||||||
|
let expr = input( 'Enter watch expression: ',
|
||||||
|
\ '',
|
||||||
|
\ 'custom,vimspector#CompleteExpr' )
|
||||||
|
else
|
||||||
|
let expr = a:1
|
||||||
|
endif
|
||||||
|
|
||||||
|
if expr ==# ''
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
py3 _vimspector_session.AddWatch( vim.eval( 'expr' ) )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#AddWatchPrompt( expr ) abort
|
function! vimspector#AddWatchPrompt( expr ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
stopinsert
|
stopinsert
|
||||||
setlocal nomodified
|
setlocal nomodified
|
||||||
call vimspector#AddWatch( a:expr )
|
call vimspector#AddWatch( a:expr )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#EvaluateConsole( expr ) abort
|
function! vimspector#Evaluate( expr ) abort
|
||||||
stopinsert
|
if !s:Enabled()
|
||||||
setlocal nomodified
|
return
|
||||||
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) )
|
endif
|
||||||
|
py3 _vimspector_session.ShowOutput( 'Console' )
|
||||||
|
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ), True )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#ShowOutput( category ) abort
|
function! vimspector#EvaluateConsole( expr ) abort
|
||||||
py3 _vimspector_session.ShowOutput( vim.eval( 'a:category' ) )
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
stopinsert
|
||||||
|
setlocal nomodified
|
||||||
|
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ), False )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#ShowOutput( ... ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
if a:0 == 1
|
||||||
|
py3 _vimspector_session.ShowOutput( vim.eval( 'a:1' ) )
|
||||||
|
else
|
||||||
|
py3 _vimspector_session.ShowOutput( 'Console' )
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#ShowOutputInWindow( win_id, category ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
py3 __import__( 'vimspector',
|
||||||
|
\ fromlist = [ 'output' ] ).output.ShowOutputInWindow(
|
||||||
|
\ int( vim.eval( 'a:win_id' ) ),
|
||||||
|
\ vim.eval( 'a:category' ) )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#ToggleLog() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.ToggleLog()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#ListBreakpoints() abort
|
function! vimspector#ListBreakpoints() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
py3 _vimspector_session.ListBreakpoints()
|
py3 _vimspector_session.ListBreakpoints()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#GetConfigurations() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
let configurations = py3eval(
|
||||||
|
\ 'list( _vimspector_session.GetConfigurations( {} )[ 1 ].keys() )'
|
||||||
|
\ . ' if _vimspector_session else []' )
|
||||||
|
return configurations
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#CompleteOutput( ArgLead, CmdLine, CursorPos ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
let buffers = py3eval( '_vimspector_session.GetOutputBuffers() '
|
||||||
|
\ . ' if _vimspector_session else []' )
|
||||||
|
return join( buffers, "\n" )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#CompleteExpr( ArgLead, CmdLine, CursorPos ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let col = len( a:ArgLead )
|
||||||
|
let prev_non_keyword_char = match( a:ArgLead[ 0 : col - 1 ], '\k*$' ) + 1
|
||||||
|
|
||||||
|
return join( py3eval( '_vimspector_session.GetCommandLineCompletions( '
|
||||||
|
\ . 'vim.eval( "a:ArgLead" ), '
|
||||||
|
\ . 'int( vim.eval( "prev_non_keyword_char" ) ) )' ),
|
||||||
|
\ "\n" )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let s:latest_completion_request = {}
|
||||||
|
|
||||||
|
function! vimspector#CompleteFuncSync( prompt, find_start, query ) abort
|
||||||
|
if py3eval( 'not _vimspector_session' )
|
||||||
|
if a:find_start
|
||||||
|
return -3
|
||||||
|
endif
|
||||||
|
return v:none
|
||||||
|
endif
|
||||||
|
|
||||||
|
if a:find_start
|
||||||
|
|
||||||
|
" We're busy
|
||||||
|
if !empty( s:latest_completion_request )
|
||||||
|
return -3
|
||||||
|
endif
|
||||||
|
|
||||||
|
let line = getline( line( '.' ) )[ len( a:prompt ) : ]
|
||||||
|
let col = col( '.' ) - len( a:prompt )
|
||||||
|
|
||||||
|
" It seems that most servers don't implement the 'start' parameter, which is
|
||||||
|
" clearly necessary, as they all seem to assume a specific behaviour, which
|
||||||
|
" is undocumented.
|
||||||
|
|
||||||
|
let s:latest_completion_request.items =
|
||||||
|
\ py3eval( '_vimspector_session.GetCompletionsSync( '
|
||||||
|
\.' vim.eval( "line" ), '
|
||||||
|
\.' int( vim.eval( "col" ) ) )' )
|
||||||
|
|
||||||
|
let s:latest_completion_request.line = line
|
||||||
|
let s:latest_completion_request.col = col
|
||||||
|
|
||||||
|
let prev_non_keyword_char = match( line[ 0 : col - 1 ], '\k*$' ) + 1
|
||||||
|
let query_len = col - prev_non_keyword_char
|
||||||
|
|
||||||
|
let start_pos = col
|
||||||
|
for item in s:latest_completion_request.items
|
||||||
|
if !has_key( item, 'start' ) || !has_key( item, 'length' )
|
||||||
|
" The specification states that if start is not supplied, isertion
|
||||||
|
" should be at the requested column. But about 0 of the servers actually
|
||||||
|
" implement that
|
||||||
|
" (https://github.com/microsoft/debug-adapter-protocol/issues/138)
|
||||||
|
let item.start = prev_non_keyword_char
|
||||||
|
let item.length = query_len
|
||||||
|
else
|
||||||
|
" For some reason, the returned start value is 0-indexed even though we
|
||||||
|
" use columnsStartAt1
|
||||||
|
let item.start += 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !has_key( item, 'text' )
|
||||||
|
let item.text = item.label
|
||||||
|
endif
|
||||||
|
|
||||||
|
if item.start < start_pos
|
||||||
|
let start_pos = item.start
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
|
||||||
|
let s:latest_completion_request.start_pos = start_pos
|
||||||
|
let s:latest_completion_request.prompt = a:prompt
|
||||||
|
|
||||||
|
" call s:Debug( 'FindStart: %s', {
|
||||||
|
" \ 'line': line,
|
||||||
|
" \ 'col': col,
|
||||||
|
" \ 'prompt': len( a:prompt ),
|
||||||
|
" \ 'start_pos': start_pos,
|
||||||
|
" \ 'returning': ( start_pos + len( a:prompt ) ) - 1,
|
||||||
|
" \ } )
|
||||||
|
|
||||||
|
" start_pos is 1-based and the return of findstart is 0-based
|
||||||
|
return ( start_pos + len( a:prompt ) ) - 1
|
||||||
|
else
|
||||||
|
let items = []
|
||||||
|
let pfxlen = len( s:latest_completion_request.prompt )
|
||||||
|
for item in s:latest_completion_request.items
|
||||||
|
if item.start > s:latest_completion_request.start_pos
|
||||||
|
" fix up the text (insert anything that is already present in the line
|
||||||
|
" that would be erased by the fixed-up earlier start position)
|
||||||
|
"
|
||||||
|
" both start_pos and item.start are 1-based
|
||||||
|
let item.text = s:latest_completion_request.line[
|
||||||
|
\ s:latest_completion_request.start_pos + pfxlen - 1 :
|
||||||
|
\ item.start + pfxlen - 1 ] . item.text
|
||||||
|
endif
|
||||||
|
|
||||||
|
if item.length > len( a:query )
|
||||||
|
" call s:Debug( 'Rejecting %s, length is greater than %s',
|
||||||
|
" \ item,
|
||||||
|
" \ len( a:query ) )
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
call add( items, { 'word': item.text,
|
||||||
|
\ 'abbr': item.label,
|
||||||
|
\ 'menu': get( item, 'type', '' ),
|
||||||
|
\ 'icase': 1,
|
||||||
|
\ } )
|
||||||
|
endfor
|
||||||
|
let s:latest_completion_request = {}
|
||||||
|
|
||||||
|
" call s:Debug( 'Items: %s', items )
|
||||||
|
return { 'words': items, 'refresh': 'always' }
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#OmniFuncWatch( find_start, query ) abort
|
||||||
|
return vimspector#CompleteFuncSync( 'Expression: ', a:find_start, a:query )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#OmniFuncConsole( find_start, query ) abort
|
||||||
|
return vimspector#CompleteFuncSync( '> ', a:find_start, a:query )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#Install( bang, ... ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
let prefix = vimspector#internal#state#GetAPIPrefix()
|
||||||
|
py3 __import__( 'vimspector',
|
||||||
|
\ fromlist = [ 'installer' ] ).installer.RunInstaller(
|
||||||
|
\ vim.eval( 'prefix' ),
|
||||||
|
\ vim.eval( 'a:bang' ) == '!',
|
||||||
|
\ *vim.eval( 'a:000' ) )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#CompleteInstall( ArgLead, CmdLine, CursorPos ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
return py3eval( '"\n".join('
|
||||||
|
\ . '__import__( "vimspector", fromlist = [ "gadgets" ] )'
|
||||||
|
\ . '.gadgets.GADGETS.keys() '
|
||||||
|
\ . ')' )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#Update( bang, ... ) abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let prefix = vimspector#internal#state#GetAPIPrefix()
|
||||||
|
py3 __import__( 'vimspector',
|
||||||
|
\ fromlist = [ 'installer' ] ).installer.RunUpdate(
|
||||||
|
\ vim.eval( 'prefix' ),
|
||||||
|
\ vim.eval( 'a:bang' ) == '!',
|
||||||
|
\ *vim.eval( 'a:000' ) )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#AbortInstall() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let prefix = vimspector#internal#state#GetAPIPrefix()
|
||||||
|
py3 __import__( 'vimspector', fromlist = [ 'installer' ] ).installer.Abort()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! vimspector#OnBufferCreated( file_name ) abort
|
||||||
|
if len( a:file_name ) == 0
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Don't actually load up vimsepctor python in autocommands that trigger
|
||||||
|
" regularly. We'll only create the session obkect in s:Enabled()
|
||||||
|
if !s:Initialised()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
py3 _vimspector_session.RefreshSigns( vim.eval( 'a:file_name' ) )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#ShowEvalBalloon( is_visual ) abort
|
||||||
|
if a:is_visual
|
||||||
|
let expr = py3eval( '__import__( "vimspector", fromlist = [ "utils" ] )'
|
||||||
|
\ . '.utils.GetVisualSelection('
|
||||||
|
\ . ' int( vim.eval( "winbufnr( winnr() )" ) ) )' )
|
||||||
|
let expr = join( expr, '\n' )
|
||||||
|
else
|
||||||
|
let expr = expand( '<cexpr>' )
|
||||||
|
endif
|
||||||
|
|
||||||
|
return py3eval( '_vimspector_session.ShowEvalBalloon('
|
||||||
|
\ . ' int( vim.eval( "winnr()" ) ), "'
|
||||||
|
\ . expr
|
||||||
|
\ . '", 0 )' )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#PrintDebugInfo() abort
|
||||||
|
if !s:Enabled()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
py3 _vimspector_session.PrintDebugInfo()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
" Boilerplate {{{
|
" Boilerplate {{{
|
||||||
let &cpoptions=s:save_cpo
|
let &cpoptions=s:save_cpo
|
||||||
unlet s:save_cpo
|
unlet s:save_cpo
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,323 @@ let s:save_cpo = &cpoptions
|
||||||
set cpoptions&vim
|
set cpoptions&vim
|
||||||
" }}}
|
" }}}
|
||||||
|
|
||||||
function! vimspector#internal#balloon#BalloonExpr() abort
|
scriptencoding utf-8
|
||||||
" winnr + 1 because for *no good reason* winnr is 0 based here unlike
|
|
||||||
" everywhere else
|
let s:popup_win_id = 0
|
||||||
" int() because for *no good reason* winnr is a string.
|
let s:nvim_border_win_id = 0
|
||||||
py3 _vimspector_session.ShowBalloon( int( vim.eval( 'v:beval_winnr' ) ) + 1,
|
"
|
||||||
\ vim.eval( 'v:beval_text' ) )
|
" tooltip dimensions
|
||||||
return '...'
|
let s:min_width = 1
|
||||||
|
let s:min_height = 1
|
||||||
|
let s:max_width = 80
|
||||||
|
let s:max_height = 20
|
||||||
|
|
||||||
|
let s:is_neovim = has( 'nvim' )
|
||||||
|
|
||||||
|
|
||||||
|
" This is used as the balloonexpr in vim to show the Tooltip at the hover
|
||||||
|
" position
|
||||||
|
function! vimspector#internal#balloon#HoverTooltip() abort
|
||||||
|
return py3eval( '_vimspector_session.ShowEvalBalloon('
|
||||||
|
\ . ' int( vim.eval( "v:beval_winnr" ) ) + 1,'
|
||||||
|
\ . ' vim.eval( "v:beval_text"),'
|
||||||
|
\ . ' 1 )' )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#balloon#CreateTooltip( is_hover, ... ) abort
|
||||||
|
let body = []
|
||||||
|
if a:0 > 0
|
||||||
|
let body = a:1
|
||||||
|
endif
|
||||||
|
|
||||||
|
if s:popup_win_id != 0
|
||||||
|
call vimspector#internal#balloon#Close()
|
||||||
|
endif
|
||||||
|
|
||||||
|
if s:is_neovim
|
||||||
|
call s:CreateNeovimTooltip( body )
|
||||||
|
else
|
||||||
|
let config = {
|
||||||
|
\ 'wrap': 0,
|
||||||
|
\ 'filtermode': 'n',
|
||||||
|
\ 'maxwidth': s:max_width,
|
||||||
|
\ 'maxheight': s:max_height,
|
||||||
|
\ 'minwidth': s:min_width,
|
||||||
|
\ 'minheight': s:min_height,
|
||||||
|
\ 'scrollbar': 1,
|
||||||
|
\ 'border': [],
|
||||||
|
\ 'padding': [ 0, 1, 0, 1],
|
||||||
|
\ 'drag': 1,
|
||||||
|
\ 'resize': 1,
|
||||||
|
\ 'close': 'button',
|
||||||
|
\ 'callback': 'vimspector#internal#balloon#CloseCallback',
|
||||||
|
\ }
|
||||||
|
|
||||||
|
let config = vimspector#internal#popup#SetBorderChars( config )
|
||||||
|
|
||||||
|
if a:is_hover
|
||||||
|
let config[ 'filter' ] = 'vimspector#internal#balloon#MouseFilter'
|
||||||
|
let config[ 'mousemoved' ] = [ 0, 0, 0 ]
|
||||||
|
let s:popup_win_id = popup_beval( body, config )
|
||||||
|
else
|
||||||
|
let config[ 'filter' ] = 'vimspector#internal#balloon#CursorFilter'
|
||||||
|
let config[ 'moved' ] = 'any'
|
||||||
|
let config[ 'cursorline' ] = 1
|
||||||
|
let config[ 'mapping' ] = 0
|
||||||
|
let s:popup_win_id = popup_atcursor( body, config )
|
||||||
|
endif
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
return s:popup_win_id
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Filters for vim {{{
|
||||||
|
function! vimspector#internal#balloon#MouseFilter( winid, key ) abort
|
||||||
|
if a:key ==# "\<Esc>"
|
||||||
|
call vimspector#internal#balloon#Close()
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
|
||||||
|
if index( [ "\<leftmouse>", "\<2-leftmouse>" ], a:key ) < 0
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
|
||||||
|
let handled = 0
|
||||||
|
let mouse_coords = getmousepos()
|
||||||
|
|
||||||
|
" close the popup if mouse is clicked outside the window
|
||||||
|
if mouse_coords[ 'winid' ] != a:winid
|
||||||
|
call vimspector#internal#balloon#Close()
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
|
||||||
|
" place the cursor according to the click
|
||||||
|
call win_execute( a:winid,
|
||||||
|
\ ':call cursor( '
|
||||||
|
\ . mouse_coords[ 'line' ]
|
||||||
|
\ . ', '
|
||||||
|
\ . mouse_coords[ 'column' ]
|
||||||
|
\ . ' )' )
|
||||||
|
|
||||||
|
" expand the variable if we got double click
|
||||||
|
if a:key ==? "\<2-leftmouse>"
|
||||||
|
call py3eval( '_vimspector_session.ExpandVariable('
|
||||||
|
\ . 'buf = vim.buffers[ ' . winbufnr( a:winid ) . ' ],'
|
||||||
|
\ . 'line_num = ' . line( '.', a:winid )
|
||||||
|
\ . ')' )
|
||||||
|
let handled = 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
return handled
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:MatchKey( key, candidates ) abort
|
||||||
|
for candidate in a:candidates
|
||||||
|
" If the mapping string looks like a special character, then try and
|
||||||
|
" expand it. This is... a hack. The whole thing only works if the mapping
|
||||||
|
" is a single key (anyway), and so we assume any string starting with < is a
|
||||||
|
" special key (which will be the common case) and try and map it. If it
|
||||||
|
" fails... it fails.
|
||||||
|
if candidate[ 0 ] == '<'
|
||||||
|
try
|
||||||
|
execute 'let candidate = "\' . candidate . '"'
|
||||||
|
endtry
|
||||||
|
endif
|
||||||
|
|
||||||
|
if candidate ==# a:key
|
||||||
|
return v:true
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
|
||||||
|
return v:false
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#balloon#CursorFilter( winid, key ) abort
|
||||||
|
let mappings = py3eval(
|
||||||
|
\ "__import__( 'vimspector',"
|
||||||
|
\." fromlist = [ 'settings' ] ).settings.Dict("
|
||||||
|
\." 'mappings' )[ 'variables' ]" )
|
||||||
|
|
||||||
|
if index( [ "\<LeftMouse>", "\<2-LeftMouse>" ], a:key ) >= 0
|
||||||
|
return vimspector#internal#balloon#MouseFilter( a:winid, a:key )
|
||||||
|
endif
|
||||||
|
|
||||||
|
if s:MatchKey( a:key, mappings.expand_collapse )
|
||||||
|
call py3eval( '_vimspector_session.ExpandVariable('
|
||||||
|
\ . 'buf = vim.buffers[ ' . winbufnr( a:winid ) . ' ],'
|
||||||
|
\ . 'line_num = ' . line( '.', a:winid )
|
||||||
|
\ . ')' )
|
||||||
|
return 1
|
||||||
|
elseif s:MatchKey( a:key, mappings.set_value )
|
||||||
|
call py3eval( '_vimspector_session.SetVariableValue('
|
||||||
|
\ . 'buf = vim.buffers[ ' . winbufnr( a:winid ) . ' ],'
|
||||||
|
\ . 'line_num = ' . line( '.', a:winid )
|
||||||
|
\ . ')' )
|
||||||
|
return 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
return popup_filter_menu( a:winid, a:key )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
" Closing {{{
|
||||||
|
|
||||||
|
function! vimspector#internal#balloon#CloseCallback( ... ) abort
|
||||||
|
let s:popup_win_id = 0
|
||||||
|
let s:nvim_border_win_id = 0
|
||||||
|
return py3eval( '_vimspector_session.CleanUpTooltip()' )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#balloon#Close() abort
|
||||||
|
if s:popup_win_id == 0
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if s:is_neovim
|
||||||
|
call nvim_win_close( s:popup_win_id, v:true )
|
||||||
|
call nvim_win_close( s:nvim_border_win_id, v:true )
|
||||||
|
|
||||||
|
call vimspector#internal#balloon#CloseCallback()
|
||||||
|
else
|
||||||
|
call popup_close(s:popup_win_id)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
" Neovim pollyfill {{{
|
||||||
|
|
||||||
|
function! vimspector#internal#balloon#ResizeTooltip() abort
|
||||||
|
if !s:is_neovim
|
||||||
|
" Vim does this for us
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if s:popup_win_id <= 0 || s:nvim_border_win_id <= 0
|
||||||
|
" nothing to resize
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
noautocmd call win_gotoid( s:popup_win_id )
|
||||||
|
let buf_lines = getline( 1, '$' )
|
||||||
|
|
||||||
|
let width = s:min_width
|
||||||
|
let height = min( [ max( [ s:min_height, len( buf_lines ) ] ),
|
||||||
|
\ s:max_height ] )
|
||||||
|
|
||||||
|
" calculate the longest line
|
||||||
|
for l in buf_lines
|
||||||
|
let width = max( [ width, len( l ) ] )
|
||||||
|
endfor
|
||||||
|
|
||||||
|
let width = min( [ width, s:max_width ] )
|
||||||
|
|
||||||
|
let opts = {
|
||||||
|
\ 'width': width,
|
||||||
|
\ 'height': height,
|
||||||
|
\ }
|
||||||
|
|
||||||
|
" resize the content window
|
||||||
|
call nvim_win_set_config( s:popup_win_id, opts )
|
||||||
|
|
||||||
|
" resize the border window
|
||||||
|
let opts[ 'width' ] = width + 4
|
||||||
|
let opts[ 'height' ] = height + 2
|
||||||
|
|
||||||
|
call nvim_win_set_config( s:nvim_border_win_id, opts )
|
||||||
|
call nvim_buf_set_lines( nvim_win_get_buf( s:nvim_border_win_id ),
|
||||||
|
\ 0,
|
||||||
|
\ -1,
|
||||||
|
\ v:true,
|
||||||
|
\ s:GenerateBorder( width, height ) )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" neovim doesn't have the border support, so we have to make our own.
|
||||||
|
" FIXME: This will likely break if the user has `ambiwidth=2`
|
||||||
|
function! s:GenerateBorder( width, height ) abort
|
||||||
|
|
||||||
|
let top = '╭' . repeat('─',a:width + 2) . '╮'
|
||||||
|
let mid = '│' . repeat(' ',a:width + 2) . '│'
|
||||||
|
let bot = '╰' . repeat('─',a:width + 2) . '╯'
|
||||||
|
let lines = [ top ] + repeat( [ mid ], a:height ) + [ bot ]
|
||||||
|
|
||||||
|
return lines
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:CreateNeovimTooltip( body ) abort
|
||||||
|
" generate border for the float window by creating a background buffer and
|
||||||
|
" overlaying the content buffer
|
||||||
|
" see https://github.com/neovim/neovim/issues/9718#issuecomment-546603628
|
||||||
|
let buf_id = nvim_create_buf( v:false, v:true )
|
||||||
|
call nvim_buf_set_lines( buf_id,
|
||||||
|
\ 0,
|
||||||
|
\ -1,
|
||||||
|
\ v:true,
|
||||||
|
\ s:GenerateBorder( s:max_width, s:max_height ) )
|
||||||
|
|
||||||
|
" default the dimensions initially, then we'll calculate the real size and
|
||||||
|
" resize it.
|
||||||
|
let opts = {
|
||||||
|
\ 'relative': 'cursor',
|
||||||
|
\ 'width': s:max_width + 2,
|
||||||
|
\ 'height': s:max_height + 2,
|
||||||
|
\ 'col': 0,
|
||||||
|
\ 'row': 1,
|
||||||
|
\ 'anchor': 'NW',
|
||||||
|
\ 'style': 'minimal'
|
||||||
|
\ }
|
||||||
|
|
||||||
|
" this is the border window
|
||||||
|
let s:nvim_border_win_id = nvim_open_win( buf_id, 0, opts )
|
||||||
|
call nvim_win_set_option( s:nvim_border_win_id, 'signcolumn', 'no' )
|
||||||
|
call nvim_win_set_option( s:nvim_border_win_id, 'relativenumber', v:false )
|
||||||
|
call nvim_win_set_option( s:nvim_border_win_id, 'number', v:false )
|
||||||
|
|
||||||
|
" when calculating where to display the content window, we need to account
|
||||||
|
" for the border
|
||||||
|
let opts.row += 1
|
||||||
|
let opts.height -= 2
|
||||||
|
let opts.col += 2
|
||||||
|
let opts.width -= 4
|
||||||
|
|
||||||
|
" create the content window
|
||||||
|
let buf_id = nvim_create_buf( v:false, v:true )
|
||||||
|
call nvim_buf_set_lines( buf_id, 0, -1, v:true, a:body )
|
||||||
|
call nvim_buf_set_option( buf_id, 'modifiable', v:false )
|
||||||
|
let s:popup_win_id = nvim_open_win( buf_id, v:false, opts )
|
||||||
|
|
||||||
|
" Apparently none of these work, when 'style' is 'minimal'
|
||||||
|
call nvim_win_set_option( s:popup_win_id, 'wrap', v:false )
|
||||||
|
call nvim_win_set_option( s:popup_win_id, 'cursorline', v:true )
|
||||||
|
call nvim_win_set_option( s:popup_win_id, 'signcolumn', 'no' )
|
||||||
|
call nvim_win_set_option( s:popup_win_id, 'relativenumber', v:false )
|
||||||
|
call nvim_win_set_option( s:popup_win_id, 'number', v:false )
|
||||||
|
|
||||||
|
" Move the cursor into the popup window, as this is the only way we can
|
||||||
|
" interract with the popup in neovim
|
||||||
|
noautocmd call win_gotoid( s:popup_win_id )
|
||||||
|
|
||||||
|
nnoremap <silent> <buffer> <Esc> <cmd>quit<CR>
|
||||||
|
call py3eval( "__import__( 'vimspector', "
|
||||||
|
\." fromlist = [ 'variables' ] )."
|
||||||
|
\.' variables.AddExpandMappings()' )
|
||||||
|
|
||||||
|
" Close the popup whenever we leave this window
|
||||||
|
augroup vimspector#internal#balloon#nvim_float
|
||||||
|
autocmd!
|
||||||
|
autocmd WinLeave <buffer>
|
||||||
|
\ :call vimspector#internal#balloon#Close()
|
||||||
|
\ | autocmd! vimspector#internal#balloon#nvim_float
|
||||||
|
augroup END
|
||||||
|
|
||||||
|
call vimspector#internal#balloon#ResizeTooltip()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
|
||||||
" Boilerplate {{{
|
" Boilerplate {{{
|
||||||
let &cpoptions=s:save_cpo
|
let &cpoptions=s:save_cpo
|
||||||
unlet s:save_cpo
|
unlet s:save_cpo
|
||||||
|
|
|
||||||
|
|
@ -20,42 +20,49 @@ set cpoptions&vim
|
||||||
" }}}
|
" }}}
|
||||||
|
|
||||||
function! s:_OnServerData( channel, data ) abort
|
function! s:_OnServerData( channel, data ) abort
|
||||||
|
if !exists( 's:ch' ) || s:ch isnot a:channel
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
py3 << EOF
|
py3 << EOF
|
||||||
_vimspector_session.OnChannelData( vim.eval( 'a:data' ) )
|
_vimspector_session.OnChannelData( vim.eval( 'a:data' ) )
|
||||||
EOF
|
EOF
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:_OnServerError( channel, data ) abort
|
|
||||||
echom 'Channel received error: ' . a:data
|
|
||||||
redraw
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:_OnClose( channel ) abort
|
function! s:_OnClose( channel ) abort
|
||||||
|
if !exists( 's:ch' ) || s:ch isnot a:channel
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
echom 'Channel closed'
|
echom 'Channel closed'
|
||||||
redraw
|
redraw
|
||||||
unlet s:ch
|
unlet s:ch
|
||||||
py3 _vimspector_session.OnServerExit( 0 )
|
py3 _vimspector_session.OnServerExit( 0 )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:_Send( msg ) abort
|
|
||||||
call ch_sendraw( s:ch, a:msg )
|
|
||||||
return 1
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! vimspector#internal#channel#Timeout( id ) abort
|
|
||||||
py3 << EOF
|
|
||||||
_vimspector_session.OnRequestTimeout( vim.eval( 'a:id' ) )
|
|
||||||
EOF
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! vimspector#internal#channel#StartDebugSession( config ) abort
|
function! vimspector#internal#channel#StartDebugSession( config ) abort
|
||||||
|
|
||||||
if exists( 's:ch' )
|
if exists( 's:ch' )
|
||||||
echo 'Channel is already running'
|
echo 'Channel is already running'
|
||||||
return v:none
|
return v:false
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let l:addr = 'localhost:' . a:config[ 'port' ]
|
" If we _also_ have a command line, then start the actual job. This allows for
|
||||||
|
" servers which start up and listen on some port
|
||||||
|
if has_key( a:config, 'command' )
|
||||||
|
let s:job = job_start( a:config[ 'command' ],
|
||||||
|
\ {
|
||||||
|
\ 'in_mode': 'raw',
|
||||||
|
\ 'out_mode': 'raw',
|
||||||
|
\ 'err_mode': 'raw',
|
||||||
|
\ 'stoponexit': 'term',
|
||||||
|
\ 'env': a:config[ 'env' ],
|
||||||
|
\ 'cwd': a:config[ 'cwd' ],
|
||||||
|
\ }
|
||||||
|
\ )
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:addr = get( a:config, 'host', '127.0.0.1' ) . ':' . a:config[ 'port' ]
|
||||||
|
|
||||||
echo 'Connecting to ' . l:addr . '... (waiting fo up to 10 seconds)'
|
echo 'Connecting to ' . l:addr . '... (waiting fo up to 10 seconds)'
|
||||||
let s:ch = ch_open( l:addr,
|
let s:ch = ch_open( l:addr,
|
||||||
|
|
@ -68,44 +75,72 @@ function! vimspector#internal#channel#StartDebugSession( config ) abort
|
||||||
\ )
|
\ )
|
||||||
|
|
||||||
if ch_status( s:ch ) !=# 'open'
|
if ch_status( s:ch ) !=# 'open'
|
||||||
echom 'Unable to connect to debug adapter'
|
echom 'Unable to connect to' l:addr
|
||||||
redraw
|
redraw
|
||||||
return v:none
|
return v:false
|
||||||
endif
|
endif
|
||||||
|
|
||||||
return funcref( 's:_Send' )
|
return v:true
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#channel#Send( msg ) abort
|
||||||
|
call ch_sendraw( s:ch, a:msg )
|
||||||
|
return 1
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#channel#Timeout( id ) abort
|
||||||
|
py3 << EOF
|
||||||
|
_vimspector_session.OnRequestTimeout( vim.eval( 'a:id' ) )
|
||||||
|
EOF
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#internal#channel#StopDebugSession() abort
|
function! vimspector#internal#channel#StopDebugSession() abort
|
||||||
if !exists( 's:ch' )
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
if ch_status( s:ch ) ==# 'open'
|
if exists( 's:job' )
|
||||||
|
" We started the job, so we need to kill it and wait to read all the data
|
||||||
|
" from the socket
|
||||||
|
|
||||||
|
if job_status( s:job ) ==# 'run'
|
||||||
|
call job_stop( s:job, 'term' )
|
||||||
|
endif
|
||||||
|
|
||||||
|
while job_status( s:job ) ==# 'run'
|
||||||
|
call job_stop( s:job, 'kill' )
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
unlet s:job
|
||||||
|
|
||||||
|
if exists( 's:ch' ) && count( [ 'closed', 'fail' ], ch_status( s:ch ) ) == 0
|
||||||
|
" We're going to block on this channel reading, then manually call the
|
||||||
|
" close callback, so remove the automatic close callback to avoid tricky
|
||||||
|
" re-entrancy
|
||||||
|
call ch_setoptions( s:ch, { 'close_cb': '' } )
|
||||||
|
endif
|
||||||
|
|
||||||
|
elseif exists( 's:ch' ) &&
|
||||||
|
\ count( [ 'closed', 'fail' ], ch_status( s:ch ) ) == 0
|
||||||
|
|
||||||
" channel is open, close it and trigger the callback. The callback is _not_
|
" channel is open, close it and trigger the callback. The callback is _not_
|
||||||
" triggered when manually calling ch_close. if we get here and the channel
|
" triggered when manually calling ch_close. if we get here and the channel
|
||||||
" is not open, then we there is a _OnClose callback waiting for us, so do
|
" is not open, then we there is a _OnClose callback waiting for us, so do
|
||||||
" nothing.
|
" nothing.
|
||||||
call ch_close( s:ch )
|
call ch_close( s:ch )
|
||||||
call s:_OnClose( s:ch )
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
" block until we've read all data from the socket and handled it.
|
||||||
|
while count( [ 'open', 'buffered' ], ch_status( s:ch ) ) == 1
|
||||||
|
let data = ch_read( s:ch, { 'timeout': 10 } )
|
||||||
|
call s:_OnServerData( s:ch, data )
|
||||||
|
endwhile
|
||||||
|
call s:_OnClose( s:ch )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#internal#channel#Reset() abort
|
function! vimspector#internal#channel#Reset() abort
|
||||||
if exists( 's:ch' )
|
if exists( 's:ch' ) || exists( 's:job' )
|
||||||
call vimspector#internal#channel#StopDebugSession()
|
call vimspector#internal#channel#StopDebugSession()
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#internal#channel#ForceRead() abort
|
|
||||||
if exists( 's:ch' )
|
|
||||||
let data = ch_readraw( s:ch, { 'timeout': 1000 } )
|
|
||||||
if data !=# ''
|
|
||||||
call s:_OnServerData( s:ch, data )
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" Boilerplate {{{
|
" Boilerplate {{{
|
||||||
let &cpoptions=s:save_cpo
|
let &cpoptions=s:save_cpo
|
||||||
unlet s:save_cpo
|
unlet s:save_cpo
|
||||||
|
|
|
||||||
|
|
@ -20,26 +20,91 @@ set cpoptions&vim
|
||||||
" }}}
|
" }}}
|
||||||
|
|
||||||
function! s:_OnServerData( channel, data ) abort
|
function! s:_OnServerData( channel, data ) abort
|
||||||
|
if !exists( 's:job' ) || ch_getjob( a:channel ) isnot s:job
|
||||||
|
call ch_log( 'Get data after process exit' )
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
py3 _vimspector_session.OnChannelData( vim.eval( 'a:data' ) )
|
py3 _vimspector_session.OnChannelData( vim.eval( 'a:data' ) )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:_OnServerError( channel, data ) abort
|
function! s:_OnServerError( channel, data ) abort
|
||||||
|
if !exists( 's:job' ) || ch_getjob( a:channel ) isnot s:job
|
||||||
|
call ch_log( 'Get data after process exit' )
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
py3 _vimspector_session.OnServerStderr( vim.eval( 'a:data' ) )
|
py3 _vimspector_session.OnServerStderr( vim.eval( 'a:data' ) )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
" FIXME: We should wait until both the exit_cb _and_ the channel closed callback
|
||||||
|
" have been received before OnServerExit?
|
||||||
|
|
||||||
function! s:_OnExit( channel, status ) abort
|
function! s:_OnExit( channel, status ) abort
|
||||||
|
if !exists( 's:job' ) || ch_getjob( a:channel ) isnot s:job
|
||||||
|
call ch_log( 'Unexpected exit callback' )
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
echom 'Channel exit with status ' . a:status
|
echom 'Channel exit with status ' . a:status
|
||||||
redraw
|
redraw
|
||||||
unlet s:job
|
if exists( 's:job' )
|
||||||
|
unlet s:job
|
||||||
|
endif
|
||||||
py3 _vimspector_session.OnServerExit( vim.eval( 'a:status' ) )
|
py3 _vimspector_session.OnServerExit( vim.eval( 'a:status' ) )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:_OnClose( channel ) abort
|
function! s:_OnClose( channel ) abort
|
||||||
|
if !exists( 's:job' ) || job_getchannel( s:job ) != a:channel
|
||||||
|
call ch_log( 'Channel closed after exit' )
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
echom 'Channel closed'
|
echom 'Channel closed'
|
||||||
redraw
|
redraw
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:_Send( msg ) abort
|
function! vimspector#internal#job#StartDebugSession( config ) abort
|
||||||
|
if exists( 's:job' )
|
||||||
|
echom 'Not starting: Job is already running'
|
||||||
|
redraw
|
||||||
|
return v:false
|
||||||
|
endif
|
||||||
|
|
||||||
|
let s:job = job_start( a:config[ 'command' ],
|
||||||
|
\ {
|
||||||
|
\ 'in_mode': 'raw',
|
||||||
|
\ 'out_mode': 'raw',
|
||||||
|
\ 'err_mode': 'raw',
|
||||||
|
\ 'exit_cb': funcref( 's:_OnExit' ),
|
||||||
|
\ 'close_cb': funcref( 's:_OnClose' ),
|
||||||
|
\ 'out_cb': funcref( 's:_OnServerData' ),
|
||||||
|
\ 'err_cb': funcref( 's:_OnServerError' ),
|
||||||
|
\ 'stoponexit': 'term',
|
||||||
|
\ 'env': a:config[ 'env' ],
|
||||||
|
\ 'cwd': a:config[ 'cwd' ],
|
||||||
|
\ }
|
||||||
|
\ )
|
||||||
|
|
||||||
|
if !exists( 's:job' )
|
||||||
|
" The job died immediately after starting and we cleaned up
|
||||||
|
return v:false
|
||||||
|
endif
|
||||||
|
|
||||||
|
let status = job_status( s:job )
|
||||||
|
|
||||||
|
echom 'Started job, status is: ' . status
|
||||||
|
redraw
|
||||||
|
|
||||||
|
if status !=# 'run'
|
||||||
|
return v:false
|
||||||
|
endif
|
||||||
|
|
||||||
|
return v:true
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#job#Send( msg ) abort
|
||||||
if ! exists( 's:job' )
|
if ! exists( 's:job' )
|
||||||
echom "Can't send message: Job was not initialised correctly"
|
echom "Can't send message: Job was not initialised correctly"
|
||||||
redraw
|
redraw
|
||||||
|
|
@ -63,40 +128,6 @@ function! s:_Send( msg ) abort
|
||||||
return 1
|
return 1
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#internal#job#StartDebugSession( config ) abort
|
|
||||||
if exists( 's:job' )
|
|
||||||
echom 'Not starging: Job is already running'
|
|
||||||
redraw
|
|
||||||
return v:none
|
|
||||||
endif
|
|
||||||
|
|
||||||
let s:job = job_start( a:config[ 'command' ],
|
|
||||||
\ {
|
|
||||||
\ 'in_mode': 'raw',
|
|
||||||
\ 'out_mode': 'raw',
|
|
||||||
\ 'err_mode': 'raw',
|
|
||||||
\ 'exit_cb': funcref( 's:_OnExit' ),
|
|
||||||
\ 'close_cb': funcref( 's:_OnClose' ),
|
|
||||||
\ 'out_cb': funcref( 's:_OnServerData' ),
|
|
||||||
\ 'err_cb': funcref( 's:_OnServerError' ),
|
|
||||||
\ 'stoponexit': 'term',
|
|
||||||
\ 'env': a:config[ 'env' ],
|
|
||||||
\ 'cwd': a:config[ 'cwd' ],
|
|
||||||
\ }
|
|
||||||
\ )
|
|
||||||
|
|
||||||
echom 'Started job, status is: ' . job_status( s:job )
|
|
||||||
redraw
|
|
||||||
|
|
||||||
if job_status( s:job ) !=# 'run'
|
|
||||||
echom 'Unable to start job, status is: ' . job_status( s:job )
|
|
||||||
redraw
|
|
||||||
return v:none
|
|
||||||
endif
|
|
||||||
|
|
||||||
return funcref( 's:_Send' )
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! vimspector#internal#job#StopDebugSession() abort
|
function! vimspector#internal#job#StopDebugSession() abort
|
||||||
if !exists( 's:job' )
|
if !exists( 's:job' )
|
||||||
echom "Not stopping session: Job doesn't exist"
|
echom "Not stopping session: Job doesn't exist"
|
||||||
|
|
@ -105,8 +136,8 @@ function! vimspector#internal#job#StopDebugSession() abort
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if job_status( s:job ) ==# 'run'
|
if job_status( s:job ) ==# 'run'
|
||||||
echom 'Terminating job'
|
echom 'Terminating job'
|
||||||
redraw
|
redraw
|
||||||
call job_stop( s:job, 'kill' )
|
call job_stop( s:job, 'kill' )
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
@ -115,13 +146,11 @@ function! vimspector#internal#job#Reset() abort
|
||||||
call vimspector#internal#job#StopDebugSession()
|
call vimspector#internal#job#StopDebugSession()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#internal#job#ForceRead() abort
|
function! s:_OnCommandExit( category, ch, code ) abort
|
||||||
if exists( 's:job' )
|
py3 __import__( "vimspector",
|
||||||
let data = ch_readraw( job_getchannel( s:job ), { 'timeout': 1000 } )
|
\ fromlist = [ "utils" ] ).utils.OnCommandWithLogComplete(
|
||||||
if data !=# ''
|
\ vim.eval( 'a:category' ),
|
||||||
call s:_OnServerData( job_getchannel( s:job ), data )
|
\ int( vim.eval( 'a:code' ) ) )
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort
|
function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort
|
||||||
|
|
@ -135,31 +164,30 @@ function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort
|
||||||
|
|
||||||
let l:index = len( s:commands[ a:category ] )
|
let l:index = len( s:commands[ a:category ] )
|
||||||
|
|
||||||
|
let buf = '_vimspector_log_' . a:category
|
||||||
|
|
||||||
call add( s:commands[ a:category ], job_start(
|
call add( s:commands[ a:category ], job_start(
|
||||||
\ a:cmd,
|
\ a:cmd,
|
||||||
\ {
|
\ {
|
||||||
\ 'out_io': 'buffer',
|
\ 'out_io': 'buffer',
|
||||||
\ 'in_io': 'null',
|
|
||||||
\ 'err_io': 'buffer',
|
\ 'err_io': 'buffer',
|
||||||
\ 'out_name': '_vimspector_log_' . a:category . '_out',
|
\ 'out_msg': 0,
|
||||||
\ 'err_name': '_vimspector_log_' . a:category . '_err',
|
\ 'err_msg': 0,
|
||||||
|
\ 'out_name': buf,
|
||||||
|
\ 'err_name': buf,
|
||||||
|
\ 'exit_cb': funcref( 's:_OnCommandExit', [ a:category ] ),
|
||||||
\ 'out_modifiable': 0,
|
\ 'out_modifiable': 0,
|
||||||
\ 'err_modifiable': 0,
|
\ 'err_modifiable': 0,
|
||||||
\ 'stoponexit': 'kill'
|
\ 'stoponexit': 'kill'
|
||||||
\ } ) )
|
\ } ) )
|
||||||
|
|
||||||
if job_status( s:commands[ a:category ][ index ] ) !=# 'run'
|
if job_status( s:commands[ a:category ][ index ] ) !=# 'run'
|
||||||
echom 'Unable to start job for ' . a:cmd
|
echom 'Unable to start job for ' . string( a:cmd )
|
||||||
redraw
|
redraw
|
||||||
return v:none
|
return v:none
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let l:stdout = ch_getbufnr(
|
return bufnr( buf )
|
||||||
\ job_getchannel( s:commands[ a:category ][ index ] ), 'out' )
|
|
||||||
let l:stderr = ch_getbufnr(
|
|
||||||
\ job_getchannel( s:commands[ a:category ][ index ] ), 'err' )
|
|
||||||
|
|
||||||
return [ l:stdout, l:stderr ]
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
126
autoload/vimspector/internal/neochannel.vim
Normal file
126
autoload/vimspector/internal/neochannel.vim
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
" vimspector - A multi-language debugging system for Vim
|
||||||
|
" Copyright 2020 Ben Jackson
|
||||||
|
"
|
||||||
|
" Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
" you may not use this file except in compliance with the License.
|
||||||
|
" You may obtain a copy of the License at
|
||||||
|
"
|
||||||
|
" http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
"
|
||||||
|
" Unless required by applicable law or agreed to in writing, software
|
||||||
|
" distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
" See the License for the specific language governing permissions and
|
||||||
|
" limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
" Boilerplate {{{
|
||||||
|
let s:save_cpo = &cpoptions
|
||||||
|
set cpoptions&vim
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function! s:_OnEvent( chan_id, data, event ) abort
|
||||||
|
if v:exiting isnot# v:null
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !exists( 's:ch' ) || a:chan_id != s:ch
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if a:data == ['']
|
||||||
|
echom 'Channel closed'
|
||||||
|
redraw
|
||||||
|
unlet s:ch
|
||||||
|
py3 _vimspector_session.OnServerExit( 0 )
|
||||||
|
else
|
||||||
|
py3 _vimspector_session.OnChannelData( '\n'.join( vim.eval( 'a:data' ) ) )
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neochannel#StartDebugSession( config ) abort
|
||||||
|
if exists( 's:ch' )
|
||||||
|
echom 'Not starting: Channel is already running'
|
||||||
|
redraw
|
||||||
|
return v:false
|
||||||
|
endif
|
||||||
|
|
||||||
|
" If we _also_ have a command line, then start the actual job. This allows for
|
||||||
|
" servers which start up and listen on some port
|
||||||
|
if has_key( a:config, 'command' )
|
||||||
|
let old_env={}
|
||||||
|
try
|
||||||
|
let old_env = vimspector#internal#neoterm#PrepareEnvironment(
|
||||||
|
\ a:config[ 'env' ] )
|
||||||
|
let s:job = jobstart( a:config[ 'command' ],
|
||||||
|
\ {
|
||||||
|
\ 'cwd': a:config[ 'cwd' ],
|
||||||
|
\ 'env': a:config[ 'env' ],
|
||||||
|
\ }
|
||||||
|
\ )
|
||||||
|
finally
|
||||||
|
call vimspector#internal#neoterm#ResetEnvironment( a:config[ 'env' ],
|
||||||
|
\ old_env )
|
||||||
|
endtry
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:addr = get( a:config, 'host', '127.0.0.1' ) . ':' . a:config[ 'port' ]
|
||||||
|
|
||||||
|
let attempt = 1
|
||||||
|
while attempt <= 10
|
||||||
|
echo 'Connecting to ' . l:addr . '... (attempt' attempt 'of 10)'
|
||||||
|
try
|
||||||
|
let s:ch = sockconnect( 'tcp',
|
||||||
|
\ addr,
|
||||||
|
\ { 'on_data': funcref( 's:_OnEvent' ) } )
|
||||||
|
redraw
|
||||||
|
return v:true
|
||||||
|
catch /connection refused/
|
||||||
|
sleep 1
|
||||||
|
endtry
|
||||||
|
let attempt += 1
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
echom 'Unable to connect to' l:addr 'after 10 attempts'
|
||||||
|
redraw
|
||||||
|
return v:false
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neochannel#Send( msg ) abort
|
||||||
|
if ! exists( 's:ch' )
|
||||||
|
echom "Can't send message: Channel was not initialised correctly"
|
||||||
|
redraw
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
|
||||||
|
call chansend( s:ch, a:msg )
|
||||||
|
return 1
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neochannel#StopDebugSession() abort
|
||||||
|
if exists( 's:ch' )
|
||||||
|
call chanclose( s:ch )
|
||||||
|
" It doesn't look like we get a callback after chanclos. Who knows if we
|
||||||
|
" will subsequently receive data callbacks.
|
||||||
|
call s:_OnEvent( s:ch, [ '' ], 'data' )
|
||||||
|
endif
|
||||||
|
|
||||||
|
if exists( 's:job' )
|
||||||
|
if vimspector#internal#neojob#JobIsRunning( s:job )
|
||||||
|
call jobstop( s:job )
|
||||||
|
endif
|
||||||
|
unlet s:job
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neochannel#Reset() abort
|
||||||
|
call vimspector#internal#neochannel#StopDebugSession()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Boilerplate {{{
|
||||||
|
let &cpoptions=s:save_cpo
|
||||||
|
unlet s:save_cpo
|
||||||
|
" }}}
|
||||||
|
|
||||||
252
autoload/vimspector/internal/neojob.vim
Normal file
252
autoload/vimspector/internal/neojob.vim
Normal file
|
|
@ -0,0 +1,252 @@
|
||||||
|
" vimspector - A multi-language debugging system for Vim
|
||||||
|
" Copyright 2020 Ben Jackson
|
||||||
|
"
|
||||||
|
" Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
" you may not use this file except in compliance with the License.
|
||||||
|
" You may obtain a copy of the License at
|
||||||
|
"
|
||||||
|
" http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
"
|
||||||
|
" Unless required by applicable law or agreed to in writing, software
|
||||||
|
" distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
" See the License for the specific language governing permissions and
|
||||||
|
" limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
" Boilerplate {{{
|
||||||
|
let s:save_cpo = &cpoptions
|
||||||
|
set cpoptions&vim
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function! s:_OnEvent( chan_id, data, event ) abort
|
||||||
|
if v:exiting isnot# v:null
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !exists( 's:job' ) || a:chan_id != s:job
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" In neovim, the data argument is a list.
|
||||||
|
if a:event ==# 'stdout'
|
||||||
|
py3 _vimspector_session.OnChannelData( '\n'.join( vim.eval( 'a:data' ) ) )
|
||||||
|
elseif a:event ==# 'stderr'
|
||||||
|
py3 _vimspector_session.OnServerStderr( '\n'.join( vim.eval( 'a:data' ) ) )
|
||||||
|
elseif a:event ==# 'exit'
|
||||||
|
echom 'Channel exit with status ' . a:data
|
||||||
|
redraw
|
||||||
|
unlet s:job
|
||||||
|
py3 _vimspector_session.OnServerExit( vim.eval( 'a:data' ) )
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neojob#StartDebugSession( config ) abort
|
||||||
|
if exists( 's:job' )
|
||||||
|
echom 'Not starging: Job is already running'
|
||||||
|
redraw
|
||||||
|
return v:false
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
" HACK: Workaround for 'env' not being supported.
|
||||||
|
|
||||||
|
let old_env={}
|
||||||
|
try
|
||||||
|
let old_env = vimspector#internal#neoterm#PrepareEnvironment(
|
||||||
|
\ a:config[ 'env' ] )
|
||||||
|
let s:job = jobstart( a:config[ 'command' ],
|
||||||
|
\ {
|
||||||
|
\ 'on_stdout': funcref( 's:_OnEvent' ),
|
||||||
|
\ 'on_stderr': funcref( 's:_OnEvent' ),
|
||||||
|
\ 'on_exit': funcref( 's:_OnEvent' ),
|
||||||
|
\ 'cwd': a:config[ 'cwd' ],
|
||||||
|
\ 'env': a:config[ 'env' ],
|
||||||
|
\ }
|
||||||
|
\ )
|
||||||
|
finally
|
||||||
|
call vimspector#internal#neoterm#ResetEnvironment( a:config[ 'env' ],
|
||||||
|
\ old_env )
|
||||||
|
endtry
|
||||||
|
|
||||||
|
return v:true
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neojob#JobIsRunning( job ) abort
|
||||||
|
return jobwait( [ a:job ], 0 )[ 0 ] == -1
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neojob#Send( msg ) abort
|
||||||
|
if ! exists( 's:job' )
|
||||||
|
echom "Can't send message: Job was not initialised correctly"
|
||||||
|
redraw
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !vimspector#internal#neojob#JobIsRunning( s:job )
|
||||||
|
echom "Can't send message: Job is not running"
|
||||||
|
redraw
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
|
||||||
|
call chansend( s:job, a:msg )
|
||||||
|
return 1
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neojob#StopDebugSession() abort
|
||||||
|
if !exists( 's:job' )
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if vimspector#internal#neojob#JobIsRunning( s:job )
|
||||||
|
echom 'Terminating job'
|
||||||
|
redraw
|
||||||
|
call jobstop( s:job )
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neojob#Reset() abort
|
||||||
|
call vimspector#internal#neojob#StopDebugSession()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:_OnCommandEvent( category, id, data, event ) abort
|
||||||
|
if v:exiting isnot# v:null
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if a:event ==# 'stdout' || a:event ==# 'stderr'
|
||||||
|
if a:data == ['']
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !has_key( s:commands, a:category )
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !has_key( s:commands[ a:category ], a:id )
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if a:event ==# 'stdout'
|
||||||
|
let buffer = s:commands[ a:category ][ a:id ].stdout
|
||||||
|
elseif a:event ==# 'stderr'
|
||||||
|
let buffer = s:commands[ a:category ][ a:id ].stderr
|
||||||
|
endif
|
||||||
|
|
||||||
|
try
|
||||||
|
call bufload( buffer )
|
||||||
|
catch /E325/
|
||||||
|
" Ignore E325/ATTENTION
|
||||||
|
endtry
|
||||||
|
|
||||||
|
|
||||||
|
let numlines = py3eval( "len( vim.buffers[ int( vim.eval( 'buffer' ) ) ] )" )
|
||||||
|
let last_line = getbufline( buffer, '$' )[ 0 ]
|
||||||
|
|
||||||
|
call s:MakeBufferWritable( buffer )
|
||||||
|
try
|
||||||
|
if numlines == 1 && last_line ==# ''
|
||||||
|
call setbufline( buffer, 1, a:data[ 0 ] )
|
||||||
|
else
|
||||||
|
call setbufline( buffer, '$', last_line . a:data[ 0 ] )
|
||||||
|
endif
|
||||||
|
|
||||||
|
call appendbufline( buffer, '$', a:data[ 1: ] )
|
||||||
|
finally
|
||||||
|
call s:MakeBufferReadOnly( buffer )
|
||||||
|
call setbufvar( buffer, '&modified', 0 )
|
||||||
|
endtry
|
||||||
|
|
||||||
|
" if the buffer is visible, scroll it, but don't allow autocommands to fire,
|
||||||
|
" as this may close the current window!
|
||||||
|
let w = bufwinnr( buffer )
|
||||||
|
if w > 0
|
||||||
|
let cw = winnr()
|
||||||
|
try
|
||||||
|
noautocmd execute w . 'wincmd w'
|
||||||
|
noautocmd normal! Gz-
|
||||||
|
finally
|
||||||
|
noautocmd execute cw . 'wincmd w'
|
||||||
|
endtry
|
||||||
|
endif
|
||||||
|
elseif a:event ==# 'exit'
|
||||||
|
py3 __import__( "vimspector",
|
||||||
|
\ fromlist = [ "utils" ] ).utils.OnCommandWithLogComplete(
|
||||||
|
\ vim.eval( 'a:category' ),
|
||||||
|
\ int( vim.eval( 'a:data' ) ) )
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:SetUpHiddenBuffer( buffer ) abort
|
||||||
|
call setbufvar( a:buffer, '&hidden', 1 )
|
||||||
|
call setbufvar( a:buffer, '&bufhidden', 'hide' )
|
||||||
|
call setbufvar( a:buffer, '&wrap', 0 )
|
||||||
|
call setbufvar( a:buffer, '&swapfile', 0 )
|
||||||
|
call setbufvar( a:buffer, '&textwidth', 0 )
|
||||||
|
call s:MakeBufferReadOnly( a:buffer )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:MakeBufferReadOnly( buffer ) abort
|
||||||
|
call setbufvar( a:buffer, '&modifiable', 0 )
|
||||||
|
call setbufvar( a:buffer, '&readonly', 1 )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:MakeBufferWritable( buffer ) abort
|
||||||
|
call setbufvar( a:buffer, '&readonly', 0 )
|
||||||
|
call setbufvar( a:buffer, '&modifiable', 1 )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
let s:commands = {}
|
||||||
|
|
||||||
|
function! vimspector#internal#neojob#StartCommandWithLog( cmd, category ) abort
|
||||||
|
if ! has_key( s:commands, a:category )
|
||||||
|
let s:commands[ a:category ] = {}
|
||||||
|
endif
|
||||||
|
|
||||||
|
let buf = bufnr( '_vimspector_log_' . a:category, v:true )
|
||||||
|
|
||||||
|
" FIXME: This largely duplicates the same stuff in the python layer, but we
|
||||||
|
" don't want to potentially mess up Vim behaviour where the job output is
|
||||||
|
" attached to a buffer set up by Vim. So we sort o mimic that here.
|
||||||
|
call s:SetUpHiddenBuffer( buf )
|
||||||
|
|
||||||
|
let id = jobstart(a:cmd,
|
||||||
|
\ {
|
||||||
|
\ 'on_stdout': funcref( 's:_OnCommandEvent',
|
||||||
|
\ [ a:category ] ),
|
||||||
|
\ 'on_stderr': funcref( 's:_OnCommandEvent',
|
||||||
|
\ [ a:category ] ),
|
||||||
|
\ 'on_exit': funcref( 's:_OnCommandEvent',
|
||||||
|
\ [ a:category ] ),
|
||||||
|
\ } )
|
||||||
|
|
||||||
|
let s:commands[ a:category ][ id ] = {
|
||||||
|
\ 'stdout': buf,
|
||||||
|
\ 'stderr': buf
|
||||||
|
\ }
|
||||||
|
|
||||||
|
return buf
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neojob#CleanUpCommand( category ) abort
|
||||||
|
if ! has_key( s:commands, a:category )
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
for id in keys( s:commands[ a:category ] )
|
||||||
|
let id = str2nr( id )
|
||||||
|
if jobwait( [ id ], 0 )[ 0 ] == -1
|
||||||
|
call jobstop( id )
|
||||||
|
endif
|
||||||
|
call jobwait( [ id ], -1 )
|
||||||
|
endfor
|
||||||
|
unlet! s:commands[ a:category ]
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Boilerplate {{{
|
||||||
|
let &cpoptions=s:save_cpo
|
||||||
|
unlet s:save_cpo
|
||||||
|
" }}}
|
||||||
137
autoload/vimspector/internal/neopopup.vim
Normal file
137
autoload/vimspector/internal/neopopup.vim
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
" vimspector - A multi-language debugging system for Vim
|
||||||
|
" Copyright 2018 Ben Jackson
|
||||||
|
"
|
||||||
|
" Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
" you may not use this file except in compliance with the License.
|
||||||
|
" You may obtain a copy of the License at
|
||||||
|
"
|
||||||
|
" http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
"
|
||||||
|
" Unless required by applicable law or agreed to in writing, software
|
||||||
|
" distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
" See the License for the specific language governing permissions and
|
||||||
|
" limitations under the License.
|
||||||
|
|
||||||
|
" Boilerplate {{{
|
||||||
|
let s:save_cpo = &cpoptions
|
||||||
|
set cpoptions&vim
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
" Neovim's float window API, like its job/channel API is painful to use
|
||||||
|
" compared to Vim's so we have to employ more hacks
|
||||||
|
|
||||||
|
" We can't seem to pass a Window handle back to the python, so we have to
|
||||||
|
" maintain yet another cached here
|
||||||
|
let s:db = {}
|
||||||
|
let s:next_id = 0
|
||||||
|
|
||||||
|
|
||||||
|
function! s:MessageToList( message ) abort
|
||||||
|
if type( a:message ) == type( [] )
|
||||||
|
let message = a:message
|
||||||
|
else
|
||||||
|
let message = [ a:message ]
|
||||||
|
endif
|
||||||
|
return message
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:GetSplashConfig( message ) abort
|
||||||
|
let l = max( map( a:message, 'len( v:val )' ) )
|
||||||
|
let h = len( a:message )
|
||||||
|
|
||||||
|
return { 'relative': 'editor',
|
||||||
|
\ 'width': l,
|
||||||
|
\ 'height': h,
|
||||||
|
\ 'col': ( &columns / 2 ) - ( l / 2 ),
|
||||||
|
\ 'row': ( &lines / 2 ) - h / 2,
|
||||||
|
\ 'anchor': 'NW',
|
||||||
|
\ 'style': 'minimal',
|
||||||
|
\ 'focusable': v:false,
|
||||||
|
\ }
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neopopup#DisplaySplash( message ) abort
|
||||||
|
let message = s:MessageToList( a:message )
|
||||||
|
let buf = nvim_create_buf(v:false, v:true)
|
||||||
|
call nvim_buf_set_lines(buf, 0, -1, v:true, message )
|
||||||
|
|
||||||
|
let win = nvim_open_win(buf, 0, s:GetSplashConfig( message ) )
|
||||||
|
call nvim_win_set_option(win, 'wrap', v:false)
|
||||||
|
call nvim_win_set_option(win, 'colorcolumn', '')
|
||||||
|
|
||||||
|
let id = s:next_id
|
||||||
|
let s:next_id += 1
|
||||||
|
let s:db[ id ] = { 'win': win, 'buf': buf }
|
||||||
|
return id
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neopopup#UpdateSplash( id, message ) abort
|
||||||
|
let splash = s:db[ a:id ]
|
||||||
|
let message = s:MessageToList( a:message )
|
||||||
|
call nvim_buf_set_lines( splash.buf, 0, -1, v:true, message )
|
||||||
|
call nvim_win_set_config( splash.win, s:GetSplashConfig( message ) )
|
||||||
|
return a:id
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neopopup#HideSplash( id ) abort
|
||||||
|
let splash = s:db[ a:id ]
|
||||||
|
call nvim_win_close( splash.win, v:true )
|
||||||
|
unlet s:db[ a:id ]
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neopopup#Confirm( confirm_id,
|
||||||
|
\ text,
|
||||||
|
\ options,
|
||||||
|
\ default_value,
|
||||||
|
\ keys ) abort
|
||||||
|
|
||||||
|
" Neovim doesn't have an equivalent of popup_dialog, and it's way too much
|
||||||
|
" effort to write one, so we just use confirm()...
|
||||||
|
" Annoyingly we can't use confirm() here because for some reason it doesn't
|
||||||
|
" render properly in a channel callback. So we use input() and mimic dialog
|
||||||
|
" behaviour.
|
||||||
|
let prompt = a:text
|
||||||
|
for opt in a:options
|
||||||
|
let prompt .= ' ' . opt
|
||||||
|
endfor
|
||||||
|
let prompt .= ': '
|
||||||
|
|
||||||
|
try
|
||||||
|
let result = input( prompt, a:keys[ a:default_value - 1 ] )
|
||||||
|
catch /.*/
|
||||||
|
let result = -1
|
||||||
|
endtry
|
||||||
|
|
||||||
|
" Map the results to what the vim popup stuff would return (s:ConfirmCallback
|
||||||
|
" in popup.vim), i.e.:
|
||||||
|
" - 1-based index of selected item, or
|
||||||
|
" - -1 or 0 for cancellation
|
||||||
|
if result == ''
|
||||||
|
" User pressed ESC/ctrl-c
|
||||||
|
let result = -1
|
||||||
|
else
|
||||||
|
let index = 1
|
||||||
|
for k in a:keys
|
||||||
|
if k ==? result
|
||||||
|
let result = index
|
||||||
|
break
|
||||||
|
endif
|
||||||
|
let index += 1
|
||||||
|
endfor
|
||||||
|
|
||||||
|
if index > len( a:keys )
|
||||||
|
let result = -1
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
py3 __import__( 'vimspector', fromlist = [ 'utils' ] ).utils.ConfirmCallback(
|
||||||
|
\ int( vim.eval( 'a:confirm_id' ) ),
|
||||||
|
\ int( vim.eval( 'result' ) ) )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Boilerplate {{{
|
||||||
|
let &cpoptions=s:save_cpo
|
||||||
|
unlet s:save_cpo
|
||||||
|
" }}}
|
||||||
|
|
||||||
106
autoload/vimspector/internal/neoterm.vim
Normal file
106
autoload/vimspector/internal/neoterm.vim
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
" vimspector - A multi-language debugging system for Vim
|
||||||
|
" Copyright 2018 Ben Jackson
|
||||||
|
"
|
||||||
|
" Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
" you may not use this file except in compliance with the License.
|
||||||
|
" You may obtain a copy of the License at
|
||||||
|
"
|
||||||
|
" http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
"
|
||||||
|
" Unless required by applicable law or agreed to in writing, software
|
||||||
|
" distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
" See the License for the specific language governing permissions and
|
||||||
|
" limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
" Boilerplate {{{
|
||||||
|
let s:save_cpo = &cpoptions
|
||||||
|
set cpoptions&vim
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
" Ids are unique throughtout the life of neovim, but obviously buffer numbers
|
||||||
|
" aren't
|
||||||
|
"
|
||||||
|
" FIXME: Tidy this map when buffers are closed ?
|
||||||
|
let s:buffer_to_id = {}
|
||||||
|
|
||||||
|
function! vimspector#internal#neoterm#PrepareEnvironment( env ) abort
|
||||||
|
let old_env = {}
|
||||||
|
|
||||||
|
for key in keys( a:env )
|
||||||
|
if exists( '$' . key )
|
||||||
|
let old_env[ key ] = getenv( key )
|
||||||
|
endif
|
||||||
|
call setenv( key, a:env[ key ] )
|
||||||
|
endfor
|
||||||
|
|
||||||
|
return old_env
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neoterm#ResetEnvironment( env, old_env ) abort
|
||||||
|
for key in keys( a:env )
|
||||||
|
let value = get( a:old_env, key, v:null )
|
||||||
|
call setenv( key, value )
|
||||||
|
endfor
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neoterm#Start( cmd, opts ) abort
|
||||||
|
" Prepare current buffer to be turned into a term if curwin is not set
|
||||||
|
if ! get( a:opts, 'curwin', 0 )
|
||||||
|
let mods = 'rightbelow '
|
||||||
|
if get( a:opts, 'vertical', 0 )
|
||||||
|
let mods .= 'vertical '
|
||||||
|
let mods .= get( a:opts, 'term_cols', '' )
|
||||||
|
else
|
||||||
|
let mods .= get( a:opts, 'term_rows', '' )
|
||||||
|
endif
|
||||||
|
|
||||||
|
execute mods . 'new'
|
||||||
|
endif
|
||||||
|
|
||||||
|
" HACK: Neovim's termopen doesn't support env
|
||||||
|
|
||||||
|
let old_env={}
|
||||||
|
try
|
||||||
|
let old_env = vimspector#internal#neoterm#PrepareEnvironment(
|
||||||
|
\ a:opts[ 'env' ] )
|
||||||
|
setlocal nomodified
|
||||||
|
let id = termopen( a:cmd, {
|
||||||
|
\ 'cwd': a:opts[ 'cwd' ],
|
||||||
|
\ 'env': a:opts[ 'env' ],
|
||||||
|
\ } )
|
||||||
|
finally
|
||||||
|
call vimspector#internal#neoterm#ResetEnvironment( a:opts[ 'env' ],
|
||||||
|
\ old_env )
|
||||||
|
endtry
|
||||||
|
|
||||||
|
let bufnr = bufnr()
|
||||||
|
let s:buffer_to_id[ bufnr ] = id
|
||||||
|
return bufnr
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:JobIsRunning( job ) abort
|
||||||
|
return jobwait( [ a:job ], 0 )[ 0 ] == -1
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neoterm#IsFinished( bufno ) abort
|
||||||
|
if !has_key( s:buffer_to_id, a:bufno )
|
||||||
|
return v:true
|
||||||
|
endif
|
||||||
|
|
||||||
|
return !s:JobIsRunning( s:buffer_to_id[ a:bufno ] )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#neoterm#GetPID( bufno ) abort
|
||||||
|
if !has_key( s:buffer_to_id, a:bufno )
|
||||||
|
return -1
|
||||||
|
endif
|
||||||
|
|
||||||
|
return jobpid( s:buffer_to_id[ a:bufno ] )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Boilerplate {{{
|
||||||
|
let &cpoptions=s:save_cpo
|
||||||
|
unlet s:save_cpo
|
||||||
|
" }}}
|
||||||
146
autoload/vimspector/internal/popup.vim
Normal file
146
autoload/vimspector/internal/popup.vim
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
" vimspector - A multi-language debugging system for Vim
|
||||||
|
" Copyright 2018 Ben Jackson
|
||||||
|
"
|
||||||
|
" Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
" you may not use this file except in compliance with the License.
|
||||||
|
" You may obtain a copy of the License at
|
||||||
|
"
|
||||||
|
" http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
"
|
||||||
|
" Unless required by applicable law or agreed to in writing, software
|
||||||
|
" distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
" See the License for the specific language governing permissions and
|
||||||
|
" limitations under the License.
|
||||||
|
scriptencoding utf-8
|
||||||
|
|
||||||
|
|
||||||
|
" Boilerplate {{{
|
||||||
|
let s:save_cpo = &cpoptions
|
||||||
|
set cpoptions&vim
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
function! vimspector#internal#popup#DisplaySplash( message ) abort
|
||||||
|
return popup_dialog( a:message, {} )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#popup#UpdateSplash( id, message ) abort
|
||||||
|
call popup_settext( a:id, a:message )
|
||||||
|
return a:id
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#popup#HideSplash( id ) abort
|
||||||
|
call popup_hide( a:id )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let s:current_selection = 0
|
||||||
|
let s:selections = []
|
||||||
|
let s:text = []
|
||||||
|
|
||||||
|
function! s:UpdatePopup( id ) abort
|
||||||
|
let buf = copy( s:text )
|
||||||
|
call extend( buf, s:DrawButtons() )
|
||||||
|
call popup_settext( a:id, buf )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:ConfirmKeyFilter( keys, id, key ) abort
|
||||||
|
if a:key ==# "\<CR>"
|
||||||
|
call popup_close( a:id, s:current_selection + 1 )
|
||||||
|
return 1
|
||||||
|
elseif index( [ "\<Tab>", "\<Right>" ], a:key ) >= 0
|
||||||
|
let s:current_selection = ( s:current_selection + 1 ) % len( s:selections )
|
||||||
|
call s:UpdatePopup( a:id )
|
||||||
|
return 1
|
||||||
|
elseif index( [ "\<S-Tab>", "\<Left>" ], a:key ) >= 0
|
||||||
|
let s:current_selection = s:current_selection == 0
|
||||||
|
\ ? len( s:selections ) - 1: s:current_selection - 1
|
||||||
|
call s:UpdatePopup( a:id )
|
||||||
|
return 1
|
||||||
|
elseif a:key ==# "\<Esc>" || a:key ==# "\<C-c>"
|
||||||
|
call popup_close( a:id, -1 )
|
||||||
|
return 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
let index = 1
|
||||||
|
for key in a:keys
|
||||||
|
if a:key ==? key
|
||||||
|
call popup_close( a:id, index )
|
||||||
|
return 1
|
||||||
|
endif
|
||||||
|
let index += 1
|
||||||
|
endfor
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:ConfirmCallback( confirm_id, id, result ) abort
|
||||||
|
py3 __import__( 'vimspector', fromlist = [ 'utils' ] ).utils.ConfirmCallback(
|
||||||
|
\ int( vim.eval( 'a:confirm_id' ) ),
|
||||||
|
\ int( vim.eval( 'a:result' ) ) )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:SelectionPosition( idx ) abort
|
||||||
|
return a:idx == 0 ? 0 : len( join( s:selections[ : a:idx - 1 ], ' ' ) ) + 1
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:DrawButtons() abort
|
||||||
|
return [ {
|
||||||
|
\ 'text': join( s:selections, ' ' ),
|
||||||
|
\ 'props': [
|
||||||
|
\ {
|
||||||
|
\ 'col': s:SelectionPosition( s:current_selection ) + 1,
|
||||||
|
\ 'length': len( s:selections[ s:current_selection ] ),
|
||||||
|
\ 'type': 'VimspectorSelectedItem'
|
||||||
|
\ },
|
||||||
|
\ ]
|
||||||
|
\ } ]
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#popup#Confirm(
|
||||||
|
\ confirm_id,
|
||||||
|
\ text,
|
||||||
|
\ options,
|
||||||
|
\ default_value,
|
||||||
|
\ keys ) abort
|
||||||
|
|
||||||
|
silent! call prop_type_add( 'VimspectorSelectedItem', {
|
||||||
|
\ 'highlight': 'PMenuSel'
|
||||||
|
\ } )
|
||||||
|
|
||||||
|
let lines = split( a:text, "\n", v:true )
|
||||||
|
let buf = []
|
||||||
|
for line in lines
|
||||||
|
call add( buf, { 'text': line, 'props': [] } )
|
||||||
|
endfor
|
||||||
|
|
||||||
|
call add( buf, { 'text': '', 'props': [] } )
|
||||||
|
|
||||||
|
let s:selections = a:options
|
||||||
|
let s:current_selection = ( a:default_value - 1 )
|
||||||
|
|
||||||
|
let s:text = copy( buf )
|
||||||
|
call extend( buf, s:DrawButtons() )
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
\ 'callback': function( 's:ConfirmCallback', [ a:confirm_id ] ),
|
||||||
|
\ 'filter': function( 's:ConfirmKeyFilter', [ a:keys ] ),
|
||||||
|
\ 'mapping': v:false,
|
||||||
|
\ }
|
||||||
|
let config = vimspector#internal#popup#SetBorderChars( config )
|
||||||
|
|
||||||
|
return popup_dialog( buf, config )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#popup#SetBorderChars( config ) abort
|
||||||
|
" When ambiwidth is single, use prettier characters for the border. This
|
||||||
|
" would look silly when ambiwidth is double.
|
||||||
|
if &ambiwidth ==# 'single' && &encoding ==? 'utf-8'
|
||||||
|
let a:config[ 'borderchars' ] = [ '─', '│', '─', '│', '╭', '╮', '┛', '╰' ]
|
||||||
|
endif
|
||||||
|
|
||||||
|
return a:config
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
" Boilerplate {{{
|
||||||
|
let &cpoptions=s:save_cpo
|
||||||
|
unlet s:save_cpo
|
||||||
|
" }}}
|
||||||
|
|
@ -19,11 +19,32 @@ let s:save_cpo = &cpoptions
|
||||||
set cpoptions&vim
|
set cpoptions&vim
|
||||||
" }}}
|
" }}}
|
||||||
|
|
||||||
|
let s:prefix = ''
|
||||||
|
if has( 'nvim' )
|
||||||
|
let s:prefix='neo'
|
||||||
|
endif
|
||||||
|
|
||||||
function! vimspector#internal#state#Reset() abort
|
function! vimspector#internal#state#Reset() abort
|
||||||
py3 << EOF
|
try
|
||||||
from vimspector import debug_session
|
py3 import vim
|
||||||
_vimspector_session = debug_session.DebugSession()
|
py3 _vimspector_session = __import__(
|
||||||
EOF
|
\ "vimspector",
|
||||||
|
\ fromlist=[ "debug_session" ] ).debug_session.DebugSession(
|
||||||
|
\ vim.eval( 's:prefix' ) )
|
||||||
|
catch /.*/
|
||||||
|
echohl WarningMsg
|
||||||
|
echom 'Exception while loading vimspector:' v:exception
|
||||||
|
echom 'From:' v:throwpoint
|
||||||
|
echom 'Vimspector unavailable: Requires Vim compiled with Python 3.6'
|
||||||
|
echohl None
|
||||||
|
return v:false
|
||||||
|
endtry
|
||||||
|
|
||||||
|
return v:true
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#state#GetAPIPrefix() abort
|
||||||
|
return s:prefix
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Boilerplate {{{
|
" Boilerplate {{{
|
||||||
|
|
|
||||||
37
autoload/vimspector/internal/term.vim
Normal file
37
autoload/vimspector/internal/term.vim
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
" vimspector - A multi-language debugging system for Vim
|
||||||
|
" Copyright 2018 Ben Jackson
|
||||||
|
"
|
||||||
|
" Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
" you may not use this file except in compliance with the License.
|
||||||
|
" You may obtain a copy of the License at
|
||||||
|
"
|
||||||
|
" http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
"
|
||||||
|
" Unless required by applicable law or agreed to in writing, software
|
||||||
|
" distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
" See the License for the specific language governing permissions and
|
||||||
|
" limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
" Boilerplate {{{
|
||||||
|
let s:save_cpo = &cpoptions
|
||||||
|
set cpoptions&vim
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
function! vimspector#internal#term#Start( cmd, opts ) abort
|
||||||
|
rightbelow return term_start( a:cmd, a:opts )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#term#IsFinished( bufno ) abort
|
||||||
|
return index( split( term_getstatus( a:bufno ), ',' ), 'finished' ) >= 0
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! vimspector#internal#term#GetPID( bufno ) abort
|
||||||
|
return job_info( term_getjob( a:bufno ) ).process
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Boilerplate {{{
|
||||||
|
let &cpoptions=s:save_cpo
|
||||||
|
unlet s:save_cpo
|
||||||
|
" }}}
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
# Starter pipeline
|
|
||||||
# Start with a minimal pipeline that you can customize to build and deploy your code.
|
|
||||||
# Add steps that build, run tests, deploy, and more:
|
|
||||||
# https://aka.ms/yaml
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- stage: Build
|
|
||||||
jobs:
|
|
||||||
- job: 'PythonLint'
|
|
||||||
displayName: "Python Lint"
|
|
||||||
pool:
|
|
||||||
vmImage: 'ubuntu-16.04'
|
|
||||||
container: 'puremourning/vimspector:test'
|
|
||||||
steps:
|
|
||||||
- bash: pip3 install -r dev_requirements.txt
|
|
||||||
displayName: "Install requirements"
|
|
||||||
|
|
||||||
- bash: $HOME/.local/bin/flake8 python3/
|
|
||||||
displayName: "Run flake8"
|
|
||||||
|
|
||||||
- job: 'Vimscript'
|
|
||||||
displayName: "Vimscript Lint"
|
|
||||||
pool:
|
|
||||||
vmImage: 'ubuntu-16.04'
|
|
||||||
container: 'puremourning/vimspector:test'
|
|
||||||
steps:
|
|
||||||
- bash: pip3 install -r dev_requirements.txt
|
|
||||||
displayName: "Install requirements"
|
|
||||||
|
|
||||||
- bash: $HOME/.local/bin/vint autoload/ plugin/
|
|
||||||
displayName: "Run vint"
|
|
||||||
|
|
||||||
- job: 'linux'
|
|
||||||
pool:
|
|
||||||
vmImage: 'ubuntu-16.04'
|
|
||||||
container:
|
|
||||||
image: 'puremourning/vimspector:test'
|
|
||||||
options: --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
|
|
||||||
steps:
|
|
||||||
- bash: |
|
|
||||||
eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
|
|
||||||
go get -u github.com/go-delve/delve/cmd/dlv
|
|
||||||
displayName: 'Install Delve for Go'
|
|
||||||
|
|
||||||
- task: CacheBeta@0
|
|
||||||
inputs:
|
|
||||||
key: v1 | gadgets | $(Agent.OS) | install_gadget.py
|
|
||||||
path: gadgets/linux/download
|
|
||||||
displayName: Cache gadgets
|
|
||||||
|
|
||||||
- bash: python3 install_gadget.py --all
|
|
||||||
displayName: 'Install gadgets - python3'
|
|
||||||
|
|
||||||
- bash: vim --version
|
|
||||||
displayName: 'Print vim version information'
|
|
||||||
|
|
||||||
- bash: |
|
|
||||||
eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
|
|
||||||
export GOPATH=$HOME/go
|
|
||||||
./run_tests
|
|
||||||
displayName: 'Run the tests'
|
|
||||||
env:
|
|
||||||
VIMSPECTOR_MIMODE: gdb
|
|
||||||
|
|
||||||
- bash: ./make_package linux $(Build.SourceVersion)
|
|
||||||
displayName: 'Package'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'package-linux'
|
|
||||||
targetPath: 'package/linux-$(Build.SourceVersion).tar.gz'
|
|
||||||
|
|
||||||
- job: 'macos'
|
|
||||||
pool:
|
|
||||||
vmImage: 'macOS-10.13'
|
|
||||||
steps:
|
|
||||||
- bash: |
|
|
||||||
brew unlink node@6
|
|
||||||
brew install macvim node@10
|
|
||||||
brew link --force --overwrite node@10
|
|
||||||
displayName: 'Install vim and node'
|
|
||||||
|
|
||||||
- bash: go get -u github.com/go-delve/delve/cmd/dlv
|
|
||||||
displayName: 'Install Delve for Go'
|
|
||||||
|
|
||||||
- task: CacheBeta@0
|
|
||||||
inputs:
|
|
||||||
key: v1 | gadgets | $(Agent.OS) | install_gadget.py
|
|
||||||
path: gadgets/macos/download
|
|
||||||
displayName: Cache gadgets
|
|
||||||
|
|
||||||
- bash: python3 install_gadget.py --all
|
|
||||||
displayName: 'Install gadgets - python3'
|
|
||||||
|
|
||||||
- bash: vim --version
|
|
||||||
displayName: 'Print vim version information'
|
|
||||||
|
|
||||||
- bash: ./run_tests
|
|
||||||
displayName: 'Run the tests'
|
|
||||||
env:
|
|
||||||
VIMSPECTOR_MIMODE: lldb
|
|
||||||
|
|
||||||
- bash: ./make_package macos $(Build.SourceVersion)
|
|
||||||
displayName: 'Package'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'package-macos'
|
|
||||||
targetPath: 'package/macos-$(Build.SourceVersion).tar.gz'
|
|
||||||
|
|
||||||
- stage: "Publish"
|
|
||||||
dependsOn:
|
|
||||||
- "Build"
|
|
||||||
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
|
|
||||||
jobs:
|
|
||||||
- job: 'Publish'
|
|
||||||
pool:
|
|
||||||
vmImage: 'ubuntu-16.04'
|
|
||||||
steps:
|
|
||||||
- task: DownloadPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'package-linux'
|
|
||||||
targetPath: $(Build.ArtifactStagingDirectory)
|
|
||||||
- task: DownloadPipelineArtifact@0
|
|
||||||
inputs:
|
|
||||||
artifactName: 'package-macos'
|
|
||||||
targetPath: $(Build.ArtifactStagingDirectory)
|
|
||||||
- task: GitHubRelease@0
|
|
||||||
inputs:
|
|
||||||
gitHubConnection: puremourning
|
|
||||||
repositoryName: '$(Build.Repository.Name)'
|
|
||||||
action: 'create' # Options: create, edit, delete
|
|
||||||
target: '$(Build.SourceVersion)' # Required when action == Create || Action == Edit
|
|
||||||
tagSource: 'manual' # Required when action == Create# Options: auto, manual
|
|
||||||
tag: "$(Build.BuildId)"
|
|
||||||
#title: # Optional
|
|
||||||
#releaseNotesSource: 'file' # Optional. Options: file, input
|
|
||||||
#releaseNotesFile: # Optional
|
|
||||||
#releaseNotes: # Optional
|
|
||||||
#assets: '$(Build.ArtifactStagingDirectory)/*' # Optional
|
|
||||||
#assetUploadMode: 'delete' # Optional. Options: delete, replace
|
|
||||||
#isDraft: false # Optional
|
|
||||||
isPreRelease: true # Optional
|
|
||||||
#addChangeLog: true # Optional
|
|
||||||
|
|
@ -13,12 +13,14 @@
|
||||||
" See the License for the specific language governing permissions and
|
" See the License for the specific language governing permissions and
|
||||||
" limitations under the License.
|
" limitations under the License.
|
||||||
|
|
||||||
|
scriptencoding utf-8
|
||||||
|
|
||||||
" Compiler plugin to help running vimspector tests
|
" Compiler plugin to help running vimspector tests
|
||||||
|
|
||||||
if exists("current_compiler")
|
if exists('current_compiler')
|
||||||
finish
|
finish
|
||||||
endif
|
endif
|
||||||
let current_compiler = "vimspector_test"
|
let current_compiler = 'vimspector_test'
|
||||||
|
|
||||||
setlocal errorformat=
|
setlocal errorformat=
|
||||||
\Found\ errors\ in\ %f:%.%#:
|
\Found\ errors\ in\ %f:%.%#:
|
||||||
|
|
@ -35,52 +37,71 @@ if ! exists( ':' . s:make_cmd )
|
||||||
endif
|
endif
|
||||||
|
|
||||||
function! VimGetCurrentFunction()
|
function! VimGetCurrentFunction()
|
||||||
echom s:GetCurrentFunction()
|
echom s:GetCurrentFunction()[ 0 ]
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:GetCurrentFunction()
|
function! s:GetCurrentFunction()
|
||||||
" Store the cursor position; we'll need to reset it
|
" Store the cursor position; we'll need to reset it
|
||||||
let [ l:buf, l:row, l:col, l:offset ] = getpos( '.' )
|
let [ buf, row, col, offset ] = getpos( '.' )
|
||||||
|
|
||||||
let l:test_function = ''
|
let [ test_function, test_function_line ] = [ v:null, -1 ]
|
||||||
|
|
||||||
let l:pattern = '\V\C\s\*function!\?\s\+\(\<\w\+\>\)\.\*\$'
|
let pattern = '\V\C\s\*func\%\(tion\)\?!\?\s\+\(\<\w\+\>\)\.\*\$'
|
||||||
|
|
||||||
let l:lnum = prevnonblank( '.' )
|
let lnum = prevnonblank( '.' )
|
||||||
|
|
||||||
" Find the top-level method and class
|
" Find the top-level method and class
|
||||||
while l:lnum > 0
|
while lnum > 0
|
||||||
call cursor( l:lnum, 1 )
|
call cursor( lnum, 1 )
|
||||||
let l:lnum = search( l:pattern, 'bcnWz' )
|
let lnum = search( pattern, 'bcnWz' )
|
||||||
|
|
||||||
if l:lnum <= 0
|
if lnum <= 0
|
||||||
call cursor( l:row, l:col )
|
call cursor( row, col )
|
||||||
return l:test_function
|
return [ test_function, test_function_line ]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let l:this_decl = substitute( getline( l:lnum ), l:pattern, '\1', '' )
|
let this_decl = substitute( getline( lnum ), pattern, '\1', '' )
|
||||||
let l:this_decl_is_test = match( l:this_decl, '\V\C\^Test_' ) >= 0
|
let this_decl_is_test = match( this_decl, '\V\C\^Test_' ) >= 0
|
||||||
|
|
||||||
if l:this_decl_is_test
|
if this_decl_is_test
|
||||||
let l:test_function = l:this_decl
|
let [ test_function, test_function_line ] = [ this_decl, lnum ]
|
||||||
|
|
||||||
if indent( l:lnum ) == 0
|
if indent( lnum ) == 0
|
||||||
call cursor( l:row, l:col )
|
call cursor( row, col )
|
||||||
return l:test_function
|
return [ test_function, test_function_line ]
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let l:lnum = prevnonblank( l:lnum - 1 )
|
let lnum = prevnonblank( lnum - 1 )
|
||||||
endwhile
|
endwhile
|
||||||
|
|
||||||
|
return [ v:null, -1 ]
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! s:RunTestUnderCursorInVimspector()
|
||||||
|
update
|
||||||
|
let l:test_func_name = s:GetCurrentFunction()[ 0 ]
|
||||||
|
|
||||||
|
if l:test_func_name ==# ''
|
||||||
|
echo 'No test method found'
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
echo "Running test '" . l:test_func_name . "'"
|
||||||
|
|
||||||
|
call vimspector#LaunchWithSettings( {
|
||||||
|
\ 'configuration': 'Run test',
|
||||||
|
\ 'TestFunction': l:test_func_name
|
||||||
|
\ } )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
function! s:RunTestUnderCursor()
|
function! s:RunTestUnderCursor()
|
||||||
update
|
update
|
||||||
let l:test_func_name = s:GetCurrentFunction()
|
let l:test_func_name = s:GetCurrentFunction()[ 0 ]
|
||||||
|
|
||||||
if l:test_func_name ==# ''
|
if l:test_func_name ==# ''
|
||||||
echo "No test method found"
|
echo 'No test method found'
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
@ -90,7 +111,9 @@ function! s:RunTestUnderCursor()
|
||||||
let l:cwd = getcwd()
|
let l:cwd = getcwd()
|
||||||
execute 'lcd ' . s:root_dir
|
execute 'lcd ' . s:root_dir
|
||||||
try
|
try
|
||||||
execute s:make_cmd . ' ' . l:test_arg
|
execute s:make_cmd . ' --report messages '
|
||||||
|
\ . get( g:, 'vimspector_test_args', '' ) . ' '
|
||||||
|
\ . l:test_arg
|
||||||
finally
|
finally
|
||||||
execute 'lcd ' . l:cwd
|
execute 'lcd ' . l:cwd
|
||||||
endtry
|
endtry
|
||||||
|
|
@ -101,7 +124,9 @@ function! s:RunTest()
|
||||||
let l:cwd = getcwd()
|
let l:cwd = getcwd()
|
||||||
execute 'lcd ' . s:root_dir
|
execute 'lcd ' . s:root_dir
|
||||||
try
|
try
|
||||||
execute s:make_cmd . ' %:p:t'
|
execute s:make_cmd . ' --report messages '
|
||||||
|
\ . get( g:, 'vimspector_test_args', '' )
|
||||||
|
\ . ' %:p:t'
|
||||||
finally
|
finally
|
||||||
execute 'lcd ' . l:cwd
|
execute 'lcd ' . l:cwd
|
||||||
endtry
|
endtry
|
||||||
|
|
@ -112,7 +137,8 @@ function! s:RunAllTests()
|
||||||
let l:cwd = getcwd()
|
let l:cwd = getcwd()
|
||||||
execute 'lcd ' . s:root_dir
|
execute 'lcd ' . s:root_dir
|
||||||
try
|
try
|
||||||
execute s:make_cmd
|
execute s:make_cmd . ' --report messages '
|
||||||
|
\ . get( g:, 'vimspector_test_args', '' )
|
||||||
finally
|
finally
|
||||||
execute 'lcd ' . l:cwd
|
execute 'lcd ' . l:cwd
|
||||||
endtry
|
endtry
|
||||||
|
|
@ -125,10 +151,36 @@ if ! has( 'gui_running' )
|
||||||
nnoremap <buffer> Â :call <SID>RunAllTests()<CR>
|
nnoremap <buffer> Â :call <SID>RunAllTests()<CR>
|
||||||
" † is right-option+t
|
" † is right-option+t
|
||||||
nnoremap <buffer> † :call <SID>RunTestUnderCursor()<CR>
|
nnoremap <buffer> † :call <SID>RunTestUnderCursor()<CR>
|
||||||
|
nnoremap <buffer> <leader>† :call <SID>RunTestUnderCursorInVimspector()<CR>
|
||||||
" å is the right-option+q
|
" å is the right-option+q
|
||||||
nnoremap <buffer> å :cfirst<CR>
|
nnoremap <buffer> å :cfirst<CR>
|
||||||
" å is the right-option+a
|
" å is the right-option+a
|
||||||
nnoremap <buffer> œ :cnext<CR>
|
nnoremap <buffer> œ :FuncLine<CR>
|
||||||
" Ω is the right-option+z
|
" Ω is the right-option+z
|
||||||
nnoremap <buffer> Ω :cprevious<CR>
|
nnoremap <buffer> Ω :cprevious<CR>
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
function! s:GoToCurrentFunctionLine( ... )
|
||||||
|
if a:0 < 1
|
||||||
|
call inputsave()
|
||||||
|
let lnum = str2nr( input( 'Enter line num: ' ) )
|
||||||
|
call inputrestore()
|
||||||
|
else
|
||||||
|
let lnum = a:1
|
||||||
|
endif
|
||||||
|
|
||||||
|
let [ f, l ] = s:GetCurrentFunction()
|
||||||
|
if f is v:null
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let lnum += l
|
||||||
|
|
||||||
|
echo 'Function' f 'at line' l '(jump to line ' lnum . ')'
|
||||||
|
|
||||||
|
call cursor( [ lnum, indent( lnum ) ] )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
command! -buffer -nargs=? -bar
|
||||||
|
\ FuncLine
|
||||||
|
\ :call s:GoToCurrentFunctionLine( <f-args> )
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,6 @@
|
||||||
flake8==3.7.7
|
flake8==3.8.3
|
||||||
vim-vint==0.3.21
|
flake8-comprehensions==3.2.3
|
||||||
|
flake8-ycm>= 0.1.0
|
||||||
|
|
||||||
|
# Use fork of vint which is up to date
|
||||||
|
git+https://github.com/puremourning/vint
|
||||||
|
|
|
||||||
1085
doc/vimspector-ref.txt
Normal file
1085
doc/vimspector-ref.txt
Normal file
File diff suppressed because it is too large
Load diff
2394
doc/vimspector.txt
Normal file
2394
doc/vimspector.txt
Normal file
File diff suppressed because it is too large
Load diff
5
docs/.gitignore
vendored
Normal file
5
docs/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
_site
|
||||||
|
.sass-cache
|
||||||
|
.jekyll-metadata
|
||||||
|
vendor/
|
||||||
|
.bundle/
|
||||||
24
docs/404.html
Normal file
24
docs/404.html
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
---
|
||||||
|
layout: default
|
||||||
|
---
|
||||||
|
|
||||||
|
<style type="text/css" media="screen">
|
||||||
|
.container {
|
||||||
|
margin: 10px auto;
|
||||||
|
max-width: 600px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin: 30px 0;
|
||||||
|
font-size: 4em;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: -1px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h1>404</h1>
|
||||||
|
|
||||||
|
<p><strong>Page not found :(</strong></p>
|
||||||
|
<p>The requested page could not be found.</p>
|
||||||
|
</div>
|
||||||
32
docs/Gemfile
Normal file
32
docs/Gemfile
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
# Hello! This is where you manage which Jekyll version is used to run.
|
||||||
|
# When you want to use a different version, change it below, save the
|
||||||
|
# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
|
||||||
|
#
|
||||||
|
# bundle exec jekyll serve
|
||||||
|
#
|
||||||
|
# This will help ensure the proper Jekyll version is running.
|
||||||
|
# Happy Jekylling!
|
||||||
|
#gem "jekyll", "~> 3.8.5"
|
||||||
|
|
||||||
|
# This is the default theme for new Jekyll sites. You may change this to anything you like.
|
||||||
|
gem "minima", "~> 2.0"
|
||||||
|
|
||||||
|
# If you want to use GitHub Pages, remove the "gem "jekyll"" above and
|
||||||
|
# uncomment the line below. To upgrade, run `bundle update github-pages`.
|
||||||
|
gem "github-pages", group: :jekyll_plugins
|
||||||
|
|
||||||
|
# If you have any plugins, put them here!
|
||||||
|
group :jekyll_plugins do
|
||||||
|
gem "jekyll-feed", "~> 0.6"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
||||||
|
gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
|
||||||
|
|
||||||
|
# Performance-booster for watching directories on Windows
|
||||||
|
gem "wdm", "~> 0.1.0" if Gem.win_platform?
|
||||||
|
|
||||||
|
|
||||||
|
gem "webrick", "~> 1.7"
|
||||||
270
docs/Gemfile.lock
Normal file
270
docs/Gemfile.lock
Normal file
|
|
@ -0,0 +1,270 @@
|
||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
activesupport (6.0.3.6)
|
||||||
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
|
i18n (>= 0.7, < 2)
|
||||||
|
minitest (~> 5.1)
|
||||||
|
tzinfo (~> 1.1)
|
||||||
|
zeitwerk (~> 2.2, >= 2.2.2)
|
||||||
|
addressable (2.8.0)
|
||||||
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
|
coffee-script (2.4.1)
|
||||||
|
coffee-script-source
|
||||||
|
execjs
|
||||||
|
coffee-script-source (1.11.1)
|
||||||
|
colorator (1.1.0)
|
||||||
|
commonmarker (0.17.13)
|
||||||
|
ruby-enum (~> 0.5)
|
||||||
|
concurrent-ruby (1.1.8)
|
||||||
|
dnsruby (1.61.5)
|
||||||
|
simpleidn (~> 0.1)
|
||||||
|
em-websocket (0.5.2)
|
||||||
|
eventmachine (>= 0.12.9)
|
||||||
|
http_parser.rb (~> 0.6.0)
|
||||||
|
ethon (0.12.0)
|
||||||
|
ffi (>= 1.3.0)
|
||||||
|
eventmachine (1.2.7)
|
||||||
|
execjs (2.7.0)
|
||||||
|
faraday (1.3.0)
|
||||||
|
faraday-net_http (~> 1.0)
|
||||||
|
multipart-post (>= 1.2, < 3)
|
||||||
|
ruby2_keywords
|
||||||
|
faraday-net_http (1.0.1)
|
||||||
|
ffi (1.15.0)
|
||||||
|
forwardable-extended (2.6.0)
|
||||||
|
gemoji (3.0.1)
|
||||||
|
github-pages (214)
|
||||||
|
github-pages-health-check (= 1.17.0)
|
||||||
|
jekyll (= 3.9.0)
|
||||||
|
jekyll-avatar (= 0.7.0)
|
||||||
|
jekyll-coffeescript (= 1.1.1)
|
||||||
|
jekyll-commonmark-ghpages (= 0.1.6)
|
||||||
|
jekyll-default-layout (= 0.1.4)
|
||||||
|
jekyll-feed (= 0.15.1)
|
||||||
|
jekyll-gist (= 1.5.0)
|
||||||
|
jekyll-github-metadata (= 2.13.0)
|
||||||
|
jekyll-mentions (= 1.6.0)
|
||||||
|
jekyll-optional-front-matter (= 0.3.2)
|
||||||
|
jekyll-paginate (= 1.1.0)
|
||||||
|
jekyll-readme-index (= 0.3.0)
|
||||||
|
jekyll-redirect-from (= 0.16.0)
|
||||||
|
jekyll-relative-links (= 0.6.1)
|
||||||
|
jekyll-remote-theme (= 0.4.3)
|
||||||
|
jekyll-sass-converter (= 1.5.2)
|
||||||
|
jekyll-seo-tag (= 2.7.1)
|
||||||
|
jekyll-sitemap (= 1.4.0)
|
||||||
|
jekyll-swiss (= 1.0.0)
|
||||||
|
jekyll-theme-architect (= 0.1.1)
|
||||||
|
jekyll-theme-cayman (= 0.1.1)
|
||||||
|
jekyll-theme-dinky (= 0.1.1)
|
||||||
|
jekyll-theme-hacker (= 0.1.2)
|
||||||
|
jekyll-theme-leap-day (= 0.1.1)
|
||||||
|
jekyll-theme-merlot (= 0.1.1)
|
||||||
|
jekyll-theme-midnight (= 0.1.1)
|
||||||
|
jekyll-theme-minimal (= 0.1.1)
|
||||||
|
jekyll-theme-modernist (= 0.1.1)
|
||||||
|
jekyll-theme-primer (= 0.5.4)
|
||||||
|
jekyll-theme-slate (= 0.1.1)
|
||||||
|
jekyll-theme-tactile (= 0.1.1)
|
||||||
|
jekyll-theme-time-machine (= 0.1.1)
|
||||||
|
jekyll-titles-from-headings (= 0.5.3)
|
||||||
|
jemoji (= 0.12.0)
|
||||||
|
kramdown (= 2.3.1)
|
||||||
|
kramdown-parser-gfm (= 1.1.0)
|
||||||
|
liquid (= 4.0.3)
|
||||||
|
mercenary (~> 0.3)
|
||||||
|
minima (= 2.5.1)
|
||||||
|
nokogiri (>= 1.10.4, < 2.0)
|
||||||
|
rouge (= 3.26.0)
|
||||||
|
terminal-table (~> 1.4)
|
||||||
|
github-pages-health-check (1.17.0)
|
||||||
|
addressable (~> 2.3)
|
||||||
|
dnsruby (~> 1.60)
|
||||||
|
octokit (~> 4.0)
|
||||||
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
|
typhoeus (~> 1.3)
|
||||||
|
html-pipeline (2.14.0)
|
||||||
|
activesupport (>= 2)
|
||||||
|
nokogiri (>= 1.4)
|
||||||
|
http_parser.rb (0.6.0)
|
||||||
|
i18n (0.9.5)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
jekyll (3.9.0)
|
||||||
|
addressable (~> 2.4)
|
||||||
|
colorator (~> 1.0)
|
||||||
|
em-websocket (~> 0.5)
|
||||||
|
i18n (~> 0.7)
|
||||||
|
jekyll-sass-converter (~> 1.0)
|
||||||
|
jekyll-watch (~> 2.0)
|
||||||
|
kramdown (>= 1.17, < 3)
|
||||||
|
liquid (~> 4.0)
|
||||||
|
mercenary (~> 0.3.3)
|
||||||
|
pathutil (~> 0.9)
|
||||||
|
rouge (>= 1.7, < 4)
|
||||||
|
safe_yaml (~> 1.0)
|
||||||
|
jekyll-avatar (0.7.0)
|
||||||
|
jekyll (>= 3.0, < 5.0)
|
||||||
|
jekyll-coffeescript (1.1.1)
|
||||||
|
coffee-script (~> 2.2)
|
||||||
|
coffee-script-source (~> 1.11.1)
|
||||||
|
jekyll-commonmark (1.3.1)
|
||||||
|
commonmarker (~> 0.14)
|
||||||
|
jekyll (>= 3.7, < 5.0)
|
||||||
|
jekyll-commonmark-ghpages (0.1.6)
|
||||||
|
commonmarker (~> 0.17.6)
|
||||||
|
jekyll-commonmark (~> 1.2)
|
||||||
|
rouge (>= 2.0, < 4.0)
|
||||||
|
jekyll-default-layout (0.1.4)
|
||||||
|
jekyll (~> 3.0)
|
||||||
|
jekyll-feed (0.15.1)
|
||||||
|
jekyll (>= 3.7, < 5.0)
|
||||||
|
jekyll-gist (1.5.0)
|
||||||
|
octokit (~> 4.2)
|
||||||
|
jekyll-github-metadata (2.13.0)
|
||||||
|
jekyll (>= 3.4, < 5.0)
|
||||||
|
octokit (~> 4.0, != 4.4.0)
|
||||||
|
jekyll-mentions (1.6.0)
|
||||||
|
html-pipeline (~> 2.3)
|
||||||
|
jekyll (>= 3.7, < 5.0)
|
||||||
|
jekyll-optional-front-matter (0.3.2)
|
||||||
|
jekyll (>= 3.0, < 5.0)
|
||||||
|
jekyll-paginate (1.1.0)
|
||||||
|
jekyll-readme-index (0.3.0)
|
||||||
|
jekyll (>= 3.0, < 5.0)
|
||||||
|
jekyll-redirect-from (0.16.0)
|
||||||
|
jekyll (>= 3.3, < 5.0)
|
||||||
|
jekyll-relative-links (0.6.1)
|
||||||
|
jekyll (>= 3.3, < 5.0)
|
||||||
|
jekyll-remote-theme (0.4.3)
|
||||||
|
addressable (~> 2.0)
|
||||||
|
jekyll (>= 3.5, < 5.0)
|
||||||
|
jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0)
|
||||||
|
rubyzip (>= 1.3.0, < 3.0)
|
||||||
|
jekyll-sass-converter (1.5.2)
|
||||||
|
sass (~> 3.4)
|
||||||
|
jekyll-seo-tag (2.7.1)
|
||||||
|
jekyll (>= 3.8, < 5.0)
|
||||||
|
jekyll-sitemap (1.4.0)
|
||||||
|
jekyll (>= 3.7, < 5.0)
|
||||||
|
jekyll-swiss (1.0.0)
|
||||||
|
jekyll-theme-architect (0.1.1)
|
||||||
|
jekyll (~> 3.5)
|
||||||
|
jekyll-seo-tag (~> 2.0)
|
||||||
|
jekyll-theme-cayman (0.1.1)
|
||||||
|
jekyll (~> 3.5)
|
||||||
|
jekyll-seo-tag (~> 2.0)
|
||||||
|
jekyll-theme-dinky (0.1.1)
|
||||||
|
jekyll (~> 3.5)
|
||||||
|
jekyll-seo-tag (~> 2.0)
|
||||||
|
jekyll-theme-hacker (0.1.2)
|
||||||
|
jekyll (> 3.5, < 5.0)
|
||||||
|
jekyll-seo-tag (~> 2.0)
|
||||||
|
jekyll-theme-leap-day (0.1.1)
|
||||||
|
jekyll (~> 3.5)
|
||||||
|
jekyll-seo-tag (~> 2.0)
|
||||||
|
jekyll-theme-merlot (0.1.1)
|
||||||
|
jekyll (~> 3.5)
|
||||||
|
jekyll-seo-tag (~> 2.0)
|
||||||
|
jekyll-theme-midnight (0.1.1)
|
||||||
|
jekyll (~> 3.5)
|
||||||
|
jekyll-seo-tag (~> 2.0)
|
||||||
|
jekyll-theme-minimal (0.1.1)
|
||||||
|
jekyll (~> 3.5)
|
||||||
|
jekyll-seo-tag (~> 2.0)
|
||||||
|
jekyll-theme-modernist (0.1.1)
|
||||||
|
jekyll (~> 3.5)
|
||||||
|
jekyll-seo-tag (~> 2.0)
|
||||||
|
jekyll-theme-primer (0.5.4)
|
||||||
|
jekyll (> 3.5, < 5.0)
|
||||||
|
jekyll-github-metadata (~> 2.9)
|
||||||
|
jekyll-seo-tag (~> 2.0)
|
||||||
|
jekyll-theme-slate (0.1.1)
|
||||||
|
jekyll (~> 3.5)
|
||||||
|
jekyll-seo-tag (~> 2.0)
|
||||||
|
jekyll-theme-tactile (0.1.1)
|
||||||
|
jekyll (~> 3.5)
|
||||||
|
jekyll-seo-tag (~> 2.0)
|
||||||
|
jekyll-theme-time-machine (0.1.1)
|
||||||
|
jekyll (~> 3.5)
|
||||||
|
jekyll-seo-tag (~> 2.0)
|
||||||
|
jekyll-titles-from-headings (0.5.3)
|
||||||
|
jekyll (>= 3.3, < 5.0)
|
||||||
|
jekyll-watch (2.2.1)
|
||||||
|
listen (~> 3.0)
|
||||||
|
jemoji (0.12.0)
|
||||||
|
gemoji (~> 3.0)
|
||||||
|
html-pipeline (~> 2.2)
|
||||||
|
jekyll (>= 3.0, < 5.0)
|
||||||
|
kramdown (2.3.1)
|
||||||
|
rexml
|
||||||
|
kramdown-parser-gfm (1.1.0)
|
||||||
|
kramdown (~> 2.0)
|
||||||
|
liquid (4.0.3)
|
||||||
|
listen (3.5.1)
|
||||||
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||||
|
rb-inotify (~> 0.9, >= 0.9.10)
|
||||||
|
mercenary (0.3.6)
|
||||||
|
mini_portile2 (2.5.1)
|
||||||
|
minima (2.5.1)
|
||||||
|
jekyll (>= 3.5, < 5.0)
|
||||||
|
jekyll-feed (~> 0.9)
|
||||||
|
jekyll-seo-tag (~> 2.1)
|
||||||
|
minitest (5.14.4)
|
||||||
|
multipart-post (2.1.1)
|
||||||
|
nokogiri (1.11.5)
|
||||||
|
mini_portile2 (~> 2.5.0)
|
||||||
|
racc (~> 1.4)
|
||||||
|
octokit (4.20.0)
|
||||||
|
faraday (>= 0.9)
|
||||||
|
sawyer (~> 0.8.0, >= 0.5.3)
|
||||||
|
pathutil (0.16.2)
|
||||||
|
forwardable-extended (~> 2.6)
|
||||||
|
public_suffix (4.0.6)
|
||||||
|
racc (1.5.2)
|
||||||
|
rb-fsevent (0.10.4)
|
||||||
|
rb-inotify (0.10.1)
|
||||||
|
ffi (~> 1.0)
|
||||||
|
rexml (3.2.5)
|
||||||
|
rouge (3.26.0)
|
||||||
|
ruby-enum (0.9.0)
|
||||||
|
i18n
|
||||||
|
ruby2_keywords (0.0.4)
|
||||||
|
rubyzip (2.3.0)
|
||||||
|
safe_yaml (1.0.5)
|
||||||
|
sass (3.7.4)
|
||||||
|
sass-listen (~> 4.0.0)
|
||||||
|
sass-listen (4.0.0)
|
||||||
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||||
|
rb-inotify (~> 0.9, >= 0.9.7)
|
||||||
|
sawyer (0.8.2)
|
||||||
|
addressable (>= 2.3.5)
|
||||||
|
faraday (> 0.8, < 2.0)
|
||||||
|
simpleidn (0.2.1)
|
||||||
|
unf (~> 0.1.4)
|
||||||
|
terminal-table (1.8.0)
|
||||||
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||||
|
thread_safe (0.3.6)
|
||||||
|
typhoeus (1.4.0)
|
||||||
|
ethon (>= 0.9.0)
|
||||||
|
tzinfo (1.2.9)
|
||||||
|
thread_safe (~> 0.1)
|
||||||
|
unf (0.1.4)
|
||||||
|
unf_ext
|
||||||
|
unf_ext (0.0.7.7)
|
||||||
|
unicode-display_width (1.7.0)
|
||||||
|
webrick (1.7.0)
|
||||||
|
zeitwerk (2.4.2)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
github-pages
|
||||||
|
jekyll-feed (~> 0.6)
|
||||||
|
minima (~> 2.0)
|
||||||
|
tzinfo-data
|
||||||
|
webrick (~> 1.7)
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.2.3
|
||||||
13
docs/README.local
Normal file
13
docs/README.local
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
To update/install:
|
||||||
|
|
||||||
|
gem install bundler
|
||||||
|
bundle install --path vendor/bundle
|
||||||
|
|
||||||
|
To run a local server/test the build
|
||||||
|
|
||||||
|
bundle exec jekyll serve
|
||||||
|
|
||||||
|
To update deps
|
||||||
|
|
||||||
|
bundle update
|
||||||
|
or bundle update github-pages
|
||||||
42
docs/_config.yml
Normal file
42
docs/_config.yml
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# Welcome to Jekyll!
|
||||||
|
#
|
||||||
|
# This config file is meant for settings that affect your whole blog, values
|
||||||
|
# which you are expected to set up once and rarely edit after that. If you find
|
||||||
|
# yourself editing this file very often, consider using Jekyll's data files
|
||||||
|
# feature for the data you need to update frequently.
|
||||||
|
#
|
||||||
|
# For technical reasons, this file is *NOT* reloaded automatically when you use
|
||||||
|
# 'bundle exec jekyll serve'. If you change this file, please restart the server process.
|
||||||
|
|
||||||
|
# Site settings
|
||||||
|
# These are used to personalize your new site. If you look in the HTML files,
|
||||||
|
# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on.
|
||||||
|
# You can create any custom variable you would like, and they will be accessible
|
||||||
|
# in the templates via {{ site.myvariable }}.
|
||||||
|
title: Vimspector Documentation
|
||||||
|
description: |
|
||||||
|
Reference Documentation for Vimspector: A multi-language debugging front
|
||||||
|
end for Vim
|
||||||
|
|
||||||
|
# Build settings
|
||||||
|
markdown: kramdown
|
||||||
|
theme: minima
|
||||||
|
plugins:
|
||||||
|
- jekyll-feed
|
||||||
|
|
||||||
|
header_pages:
|
||||||
|
- index.md
|
||||||
|
- configuration.md
|
||||||
|
- schema/index.md
|
||||||
|
|
||||||
|
# Exclude from processing.
|
||||||
|
# The following items will not be processed, by default. Create a custom list
|
||||||
|
# to override the default setting.
|
||||||
|
# exclude:
|
||||||
|
# - Gemfile
|
||||||
|
# - Gemfile.lock
|
||||||
|
# - node_modules
|
||||||
|
# - vendor/bundle/
|
||||||
|
# - vendor/cache/
|
||||||
|
# - vendor/gems/
|
||||||
|
# - vendor/ruby/
|
||||||
1043
docs/configuration.md
Normal file
1043
docs/configuration.md
Normal file
File diff suppressed because it is too large
Load diff
25
docs/custom_gadget_file.md
Normal file
25
docs/custom_gadget_file.md
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
title: Configuration
|
||||||
|
---
|
||||||
|
|
||||||
|
This document describes how to use vimspector's `install_gadget.py` to install
|
||||||
|
custom debug adapters. This can be useful as a way to get an adapter working
|
||||||
|
that isn't officially supported by Vimspector, but otherwise can be made to work
|
||||||
|
by simply downloading the VScode extension into the gadget directory.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
./install_gadget.py --enable-custom=/path/to/a.json \
|
||||||
|
--enable-custom=/path/to/b.json`
|
||||||
|
```
|
||||||
|
|
||||||
|
This tells `install_gadget.py` to read `a.json` and `b.json` as _gadget
|
||||||
|
definitions_ and download/unpack the specified gadgets into the gadget dir, just
|
||||||
|
like the supported adapters.
|
||||||
|
|
||||||
|
## Gadget Definitions
|
||||||
|
|
||||||
|
A _gadget definition_ is a file containing a single JSON object definition,
|
||||||
|
describing the debug adapter and how to download and install it. This mechanism
|
||||||
|
is crude but can be effective.
|
||||||
23
docs/index.md
Normal file
23
docs/index.md
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
title: Vimspector Reference Guide
|
||||||
|
---
|
||||||
|
|
||||||
|
This section contains reference material for configuring and using
|
||||||
|
[Vimspector][vimspector]. It is intentionally technical in nature and should
|
||||||
|
serve as a reference guide, rather than a user guide or tutorial.
|
||||||
|
|
||||||
|
It complements the following:
|
||||||
|
|
||||||
|
* The [Vimspector README][readme], which contains a general overview and is the
|
||||||
|
main go-to for getting started, installation, etc.
|
||||||
|
* The [Vimspector Website][website], which contains a step-by-step tutorial and
|
||||||
|
some detail about the user interface.
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
* [Configuring Vimspector for your projects](configuration.html)
|
||||||
|
* [Full JSON Schema reference](schema/)
|
||||||
|
|
||||||
|
[vimspector]: https://github.com/puremourning/vimspector
|
||||||
|
[readme]: https://github.com/puremourning/vimspector/blob/master/README.md
|
||||||
|
[website]: https://puremourning.github.io/vimspector-web
|
||||||
11
docs/schema/gadgets.schema.json
Normal file
11
docs/schema/gadgets.schema.json
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"$id": "https://puremourning.github.io/vimspector/schema/gadgets.schema.json",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"adapters": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": { "$ref": "vimspector.schema.json#/definitions/adapter" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
docs/schema/index.md
Normal file
21
docs/schema/index.md
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
---
|
||||||
|
title: Configuration Schema
|
||||||
|
---
|
||||||
|
|
||||||
|
This area contains the [JSON Schema][json-schema] representation of the
|
||||||
|
configuration objects used for configuring Vimspector. For more information on
|
||||||
|
JSON Schema, check out [Understanding JSON
|
||||||
|
Schema](http://json-schema.org/understanding-json-schema)
|
||||||
|
|
||||||
|
Vimsepctor specification is based on [Draft 7][draft-7] of JSON Schema
|
||||||
|
standard.
|
||||||
|
|
||||||
|
## The schemas
|
||||||
|
|
||||||
|
* [`vimspector.schema.json`](vimspector.schema.json) - contains the full
|
||||||
|
configuration defnition (`.vimspector.json`).
|
||||||
|
* [`gadgets.schema.json`](gadgets.schema.json) - contains the specification for
|
||||||
|
gadget-only objects (`.gadgets.json`).
|
||||||
|
|
||||||
|
[json-schema]: http://json-schema.org
|
||||||
|
[draft-7]: https://json-schema.org/specification-links.html#draft-7
|
||||||
298
docs/schema/vimspector.schema.json
Normal file
298
docs/schema/vimspector.schema.json
Normal file
|
|
@ -0,0 +1,298 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"$id": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json",
|
||||||
|
"definitions": {
|
||||||
|
"variables": {
|
||||||
|
"description": "A mappoing of name/value pairs to set variables to be used elsewhere in the definition, or a list of such mappings.",
|
||||||
|
"properties": {
|
||||||
|
"variables": {
|
||||||
|
"oneOf": [
|
||||||
|
{ "$ref": "#/definitions/variables-mapping" },
|
||||||
|
{ "$ref": "#/definitions/variables-list" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"variables-list": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "A list of variable mappings. This can be useful where variable definitions rely on each other. By using a list, you can control the sequence in which variables are defined.",
|
||||||
|
"items": { "$ref": "#/definitions/variables-mapping" }
|
||||||
|
},
|
||||||
|
"variables-mapping": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"oneOf": [
|
||||||
|
{ "type": "string" },
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [ "shell" ],
|
||||||
|
"properties": {
|
||||||
|
"shell": {
|
||||||
|
"type": [ "array", "string" ],
|
||||||
|
"description": "Command to run. If it's a string, it's split using Python's shelex splitting. Can contain other variable references."
|
||||||
|
},
|
||||||
|
"cwd": { "type": "string" },
|
||||||
|
"env": { "type": "object" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"adapter-launchattach": {
|
||||||
|
"properties": {
|
||||||
|
"launch": {
|
||||||
|
"allOf": [
|
||||||
|
{ "$ref": "#/definitions/adapter-remote" },
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"delay": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A time in the format understood by :help :sleep to wait after running the attachCommand(s)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"attach": {
|
||||||
|
"allOf": [
|
||||||
|
{ "$ref": "#/definitions/adapter-remote" },
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [ "pidSelect" ],
|
||||||
|
"properties": {
|
||||||
|
"pidSelect": {
|
||||||
|
"enum": [ "ask", "none" ]
|
||||||
|
},
|
||||||
|
"pidProperty": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The launch config property which the PID should be injected into. Required when 'pidSelect' is 'ask'."
|
||||||
|
},
|
||||||
|
"delay": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A time in the format understood by :help :sleep to wait after running the attachCommand(s)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"adapter-remote": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"remote": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Configures how Vimspector will marshal remote debugging requests. When remote debugging, Vimspector will either ssh to 'account'@'host' or docker exec -it to 'container' and run 'pidCommand', 'attachCommands', 'runCommands', etc. based on the 'remote-command' option in the debug configuration. If 'remote-command' is 'launch', it runs 'runCommand(s)', otherwise (it's 'attach') vimspector runs 'pidCommand', followed by 'attachCommand(s)'.Then it starts up the debug adapter with the debug configuration as normal. Usually this is configured with an 'attach' request (whether we remotely 'launched' or not). Once the initialization exchange is complete, Vimspector runs the optional 'initCompleteCommand' which can be used to force the application to break, e.g. by sending it SIGINT. This is required on some platforms which have buggy gdbservers (for example)",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"oneOf": [
|
||||||
|
{ "required": [ "host" ] },
|
||||||
|
{ "required": [ "container" ] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"account": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Remote account name used when ssh'ing. Defaults to the current user account."
|
||||||
|
},
|
||||||
|
"host": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Name of the remote host to connect to (via passwordless SSH)."
|
||||||
|
},
|
||||||
|
"container": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Name or container id of the docker run container to connect to (via docker exec). Note the container must already be running (Vimspector will not start it) and it must have the port forwarded to the host if subsequently connecting via a port (for example <tt>docker run -p 8765:8765 -it simple_python</tt>)."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"oneOf": [
|
||||||
|
{ "required": [ "attachCommand" ] },
|
||||||
|
{ "required": [ "attachCommands" ] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"initCompleteCommand": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" },
|
||||||
|
"description": "For remote-attach. Remote command to execute after initialization of the debug adapter. Can be used to work around buggy attach behaviour on certain platforms (advanced usage). Can contain the special token %PID% which is replaced with the PID returned by 'pidCommand'"
|
||||||
|
},
|
||||||
|
"pidCommand": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" },
|
||||||
|
"description": "Required for remote-attach. Remote command to execute to return the PID to attach to."
|
||||||
|
},
|
||||||
|
"attachCommands": {
|
||||||
|
"type": [ "array" ],
|
||||||
|
"items": { "type": "array", "items": { "type": "string" } },
|
||||||
|
"description": "For remote-attach. List of commands to execute remotely to set up the attach. Can contain the special token %PID% which is replaced with the PID returned by the remote 'pidCommand'."
|
||||||
|
},
|
||||||
|
"attachCommand": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" },
|
||||||
|
"description": "A single command to execute for remote-attach. Like attachCommands but for a single command. If attachCommands is supplied, this is not used."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"oneOf": [
|
||||||
|
{ "required": [ "runCommand" ] },
|
||||||
|
{ "required": [ "runCommands" ] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"runCommands": {
|
||||||
|
"type": [ "array" ],
|
||||||
|
"items": { "type": "array", "items": { "type": "string" } },
|
||||||
|
"description": "For remote-launch. List of commands to execute remotely to set up the launch. An entry in the array can be the special token '%CMD%' which is replaced with the evaluated 'remote-cmdLine' value in the debug configuration. This is useful to parameterize launcging remotely under something like gdbserver."
|
||||||
|
},
|
||||||
|
"runCommand": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" },
|
||||||
|
"description": "A single command to execute for remote-launch. Like runCommands but for a single command."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"adapter": {
|
||||||
|
"allOf": [
|
||||||
|
{ "type": "object" },
|
||||||
|
{ "$ref": "#/definitions/variables" },
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Passed to the adapter in the initialization request. Some adapters are particularly picky about what value goes here. Usually it can be omitted and Vimspector will send a generic value"
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Base debug configuration. Can be used to set default values for all debug configurations. When reading individual debug configurations from 'configurations', those configurations are merged with this object. Definitions in the debug configuration override anything in this object. Typical usage for this is to set the 'type' parameter, which some debug adapters are very picky about, or to set e.g. the path to an underlying debugger."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ "$ref": "#/definitions/adapter-launchattach" },
|
||||||
|
{
|
||||||
|
"anyOf": [
|
||||||
|
{ "required": [ "command" ] },
|
||||||
|
{ "required": [ "port" ] },
|
||||||
|
{ "required": [ "command", "port" ] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"host": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "127.0.0.1",
|
||||||
|
"description": "Connect to this host in multi-session mode"
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"oneOf": [
|
||||||
|
{ "type": "string" },
|
||||||
|
{ "type": "integer" }
|
||||||
|
],
|
||||||
|
"description": "If supplied, indicates that a socket connection should be made to this port on 'host'. If the value is 'ask', then the user is asked to enter the port number to connect to."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"required": [ "configurations" ],
|
||||||
|
"properties": {
|
||||||
|
"adapters": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": { "$ref": "#/definitions/adapter" }
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"allOf": [
|
||||||
|
{ "$ref": "#/definitions/variables" },
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"adapter": {
|
||||||
|
"description": "Adapter configuration to use for this debug session",
|
||||||
|
"oneOf": [
|
||||||
|
{ "$ref": "#/definitions/adapter" },
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Name of an adapter in the 'adapters' mapping"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"remote-request": {
|
||||||
|
"enum": [ "launch", "attach" ],
|
||||||
|
"description": "When the 'remote' block is defined in the adapter configuration, this can be used to override the actual action taken (remotely). Usually the actual 'configuration' will contain 'request' of 'attach', but in order to remotely 'launch' the process (e.g. under gdbserver or equivalent), use remote-attach set to 'launch'"
|
||||||
|
},
|
||||||
|
"remote-cmdLine": {
|
||||||
|
"type": [ "string", "array" ],
|
||||||
|
"description": "Defines the value of the special token %CMD% in remote-launch 'runCommand(s)'. The value is inserted into the command line where an entry matching '%CMD%' is found in 'runCommand(s)' command array."
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [ "request" ],
|
||||||
|
"properties": {
|
||||||
|
"request": {
|
||||||
|
"enum": [ "launch", "attach" ],
|
||||||
|
"description": "Type of session - launch process or attach to process"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": {
|
||||||
|
"description": "Additional properties are passed to the debug adapter in the 'launch' or 'attach' request and are specific to the debug adapter."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"breakpoints": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"line": {
|
||||||
|
"$comment": "TBA: Not supported yet"
|
||||||
|
},
|
||||||
|
"function": {
|
||||||
|
"$comment": "TBA: Not supported yet"
|
||||||
|
},
|
||||||
|
"exception": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Exception breakpoints configuration, mapping the server's exception filter to enabled/disable/default flag",
|
||||||
|
"additionalProperties": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "true = enable, false = disable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": [ "Y", "N", "" ],
|
||||||
|
"description": "Y = enable, N = disable, '' = default"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# vimspector - A multi-language debugging system for Vim
|
# vimspector - A multi-language debugging system for Vim
|
||||||
# Copyright 2019 Ben Jackson
|
# Copyright 2019 Ben Jackson
|
||||||
|
|
@ -15,506 +15,52 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
try:
|
import sys
|
||||||
import urllib.request as urllib2
|
|
||||||
except ImportError:
|
if sys.version_info.major < 3:
|
||||||
import urllib2
|
sys.exit( "You need to run this with python 3. Your version is " +
|
||||||
|
'.'.join( map( str, sys.version_info[ :3 ] ) ) )
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import contextlib
|
|
||||||
import os
|
import os
|
||||||
import string
|
|
||||||
import zipfile
|
|
||||||
import gzip
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import traceback
|
|
||||||
import tarfile
|
|
||||||
import hashlib
|
|
||||||
import sys
|
|
||||||
import json
|
import json
|
||||||
import functools
|
import functools
|
||||||
import time
|
import operator
|
||||||
|
import glob
|
||||||
try:
|
|
||||||
from io import BytesIO ## for Python 3
|
|
||||||
except ImportError:
|
|
||||||
from BytesIO import BytesIO
|
|
||||||
|
|
||||||
# Include vimspector source, for utils
|
# Include vimspector source, for utils
|
||||||
sys.path.insert( 1, os.path.join( os.path.dirname( __file__ ),
|
sys.path.insert( 1, os.path.join( os.path.dirname( __file__ ),
|
||||||
'python3' ) )
|
'python3' ) )
|
||||||
|
|
||||||
from vimspector import install
|
from vimspector import install, installer, gadgets
|
||||||
|
from vimspector.vendor.json_minify import minify
|
||||||
|
|
||||||
GADGETS = {
|
# ------------------------------------------------------------------------------
|
||||||
'vscode-cpptools': {
|
# Entry point
|
||||||
'language': 'c',
|
# ------------------------------------------------------------------------------
|
||||||
'download': {
|
|
||||||
'url': 'https://github.com/Microsoft/vscode-cpptools/releases/download/'
|
|
||||||
'${version}/${file_name}',
|
|
||||||
},
|
|
||||||
'do': lambda name, root: InstallCppTools( name, root ),
|
|
||||||
'all': {
|
|
||||||
'version': '0.23.1',
|
|
||||||
},
|
|
||||||
'linux': {
|
|
||||||
'file_name': 'cpptools-linux.vsix',
|
|
||||||
'checksum':
|
|
||||||
'c0f424bd6d5e016d70126587c80b92d981729c708ce524f2cce4c3f524b41d71'
|
|
||||||
},
|
|
||||||
'macos': {
|
|
||||||
'file_name': 'cpptools-osx.vsix',
|
|
||||||
'checksum':
|
|
||||||
'431692395ba243ea20428e083d5df3201a0dbda31a66eab7729da0f377def5fd',
|
|
||||||
},
|
|
||||||
'windows': {
|
|
||||||
'file_name': 'cpptools-win32.vsix',
|
|
||||||
'checksum': None,
|
|
||||||
},
|
|
||||||
"adapters": {
|
|
||||||
"vscode-cpptools": {
|
|
||||||
"name": "cppdbg",
|
|
||||||
"command": [
|
|
||||||
"${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7"
|
|
||||||
],
|
|
||||||
"attach": {
|
|
||||||
"pidProperty": "processId",
|
|
||||||
"pidSelect": "ask"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'vscode-python': {
|
|
||||||
'language': 'python',
|
|
||||||
'download': {
|
|
||||||
'url': 'https://github.com/Microsoft/vscode-python/releases/download/'
|
|
||||||
'${version}/${file_name}',
|
|
||||||
},
|
|
||||||
'all': {
|
|
||||||
'version': '2019.10.41019',
|
|
||||||
'file_name': 'ms-python-release.vsix',
|
|
||||||
'checksum':
|
|
||||||
'38e8bf782fc6d2dc904868add2e1e5dc66197a06a902f6d17e15f96d4e9bf16b',
|
|
||||||
},
|
|
||||||
'adapters': {
|
|
||||||
"vscode-python": {
|
|
||||||
"name": "vscode-python",
|
|
||||||
"command": [
|
|
||||||
"node",
|
|
||||||
"${gadgetDir}/vscode-python/out/client/debugger/debugAdapter/main.js",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'tclpro': {
|
|
||||||
'language': 'tcl',
|
|
||||||
'repo': {
|
|
||||||
'url': 'https://github.com/puremourning/TclProDebug',
|
|
||||||
'ref': 'f5c56b7067661ce84e205765060224076569ae0e', # master 26/10/2019
|
|
||||||
},
|
|
||||||
'do': lambda name, root: InstallTclProDebug( name, root )
|
|
||||||
},
|
|
||||||
'netcoredbg': {
|
|
||||||
'language': 'csharp',
|
|
||||||
'enabled': False,
|
|
||||||
'download': {
|
|
||||||
'url': 'https://github.com/Samsung/netcoredbg/releases/download/latest/'
|
|
||||||
'${file_name}',
|
|
||||||
'format': 'tar',
|
|
||||||
},
|
|
||||||
'all': {
|
|
||||||
'version': 'master'
|
|
||||||
},
|
|
||||||
'macos': {
|
|
||||||
'file_name': 'netcoredbg-osx-master.tar.gz',
|
|
||||||
'checksum': '',
|
|
||||||
},
|
|
||||||
'linux': {
|
|
||||||
'file_name': 'netcoredbg-linux-master.tar.gz',
|
|
||||||
'checksum': '',
|
|
||||||
},
|
|
||||||
'do': lambda name, root: MakeSymlink( gadget_dir,
|
|
||||||
name,
|
|
||||||
os.path.join( root, 'netcoredbg' ) ),
|
|
||||||
'adapters': {
|
|
||||||
'netcoredbg': {
|
|
||||||
"name": "netcoredbg",
|
|
||||||
"command": [
|
|
||||||
"${gadgetDir}/netcoredbg/netcoredbg",
|
|
||||||
"--interpreter=vscode"
|
|
||||||
],
|
|
||||||
"attach": {
|
|
||||||
"pidProperty": "processId",
|
|
||||||
"pidSelect": "ask"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'vscode-mono-debug': {
|
|
||||||
'language': 'csharp',
|
|
||||||
'enabled': False,
|
|
||||||
'download': {
|
|
||||||
'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/'
|
|
||||||
'publishers/ms-vscode/vsextensions/mono-debug/${version}/'
|
|
||||||
'vspackage',
|
|
||||||
'target': 'vscode-mono-debug.vsix.gz',
|
|
||||||
'format': 'zip.gz',
|
|
||||||
},
|
|
||||||
'all': {
|
|
||||||
'file_name': 'vscode-mono-debug.vsix',
|
|
||||||
'version': '0.15.8',
|
|
||||||
'checksum':
|
|
||||||
'723eb2b621b99d65a24f215cb64b45f5fe694105613a900a03c859a62a810470',
|
|
||||||
},
|
|
||||||
'adapters': {
|
|
||||||
'vscode-mono-debug': {
|
|
||||||
"name": "mono-debug",
|
|
||||||
"command": [
|
|
||||||
"mono",
|
|
||||||
"${gadgetDir}/vscode-mono-debug/bin/Release/mono-debug.exe"
|
|
||||||
],
|
|
||||||
"attach": {
|
|
||||||
"pidSelect": "none"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'vscode-bash-debug': {
|
|
||||||
'language': 'bash',
|
|
||||||
'download': {
|
|
||||||
'url': 'https://github.com/rogalmic/vscode-bash-debug/releases/'
|
|
||||||
'download/${version}/${file_name}',
|
|
||||||
},
|
|
||||||
'all': {
|
|
||||||
'file_name': 'bash-debug-0.3.5.vsix',
|
|
||||||
'version': 'v0.3.5',
|
|
||||||
'checksum': '',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'vscode-go': {
|
|
||||||
'language': 'go',
|
|
||||||
'download': {
|
|
||||||
'url': 'https://github.com/microsoft/vscode-go/releases/download/'
|
|
||||||
'${version}/${file_name}'
|
|
||||||
},
|
|
||||||
'all': {
|
|
||||||
'version': '0.11.4',
|
|
||||||
'file_name': 'Go-0.11.4.vsix',
|
|
||||||
'checksum':
|
|
||||||
'ff7d7b944da5448974cb3a0086f4a2fd48e2086742d9c013d6964283d416027e'
|
|
||||||
},
|
|
||||||
'adapters': {
|
|
||||||
'vscode-go': {
|
|
||||||
'name': 'delve',
|
|
||||||
'command': [
|
|
||||||
'node',
|
|
||||||
'${gadgetDir}/vscode-go/out/src/debugAdapter/goDebug.js'
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'vscode-node-debug2': {
|
|
||||||
'language': 'node',
|
|
||||||
'enabled': False,
|
|
||||||
'repo': {
|
|
||||||
'url': 'https://github.com/microsoft/vscode-node-debug2',
|
|
||||||
'ref': 'v1.39.1',
|
|
||||||
},
|
|
||||||
'do': lambda name, root: InstallNodeDebug( name, root ),
|
|
||||||
'adapters': {
|
|
||||||
'vscode-node': {
|
|
||||||
'name': 'node2',
|
|
||||||
'type': 'node2',
|
|
||||||
'command': [
|
|
||||||
'node',
|
|
||||||
'${gadgetDir}/vscode-node-debug2/out/src/nodeDebug.js'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'debugger-for-chrome': {
|
|
||||||
'language': 'chrome',
|
|
||||||
'enabled': False,
|
|
||||||
'download': {
|
|
||||||
'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/'
|
|
||||||
'publishers/msjsdiag/vsextensions/'
|
|
||||||
'debugger-for-chrome/${version}/vspackage',
|
|
||||||
'target': 'msjsdiag.debugger-for-chrome-4.12.0.vsix.gz',
|
|
||||||
'format': 'zip.gz',
|
|
||||||
},
|
|
||||||
'all': {
|
|
||||||
'version': '4.12.0',
|
|
||||||
'file_name': 'msjsdiag.debugger-for-chrome-4.12.0.vsix',
|
|
||||||
'checksum':
|
|
||||||
'0df2fe96d059a002ebb0936b0003e6569e5a5c35260dc3791e1657d27d82ccf5'
|
|
||||||
},
|
|
||||||
'adapters': {
|
|
||||||
'chrome': {
|
|
||||||
'name': 'debugger-for-chrome',
|
|
||||||
'type': 'chrome',
|
|
||||||
'command': [
|
|
||||||
'node',
|
|
||||||
'${gadgetDir}/debugger-for-chrome/out/src/chromeDebug.js'
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
parser = argparse.ArgumentParser(
|
||||||
def CurrentWorkingDir( d ):
|
formatter_class = argparse.RawDescriptionHelpFormatter,
|
||||||
cur_d = os.getcwd()
|
description = 'Install DAP Servers for use with Vimspector.',
|
||||||
try:
|
epilog =
|
||||||
os.chdir( d )
|
"""
|
||||||
yield
|
If you're not sure, normally --all is enough to get started.
|
||||||
finally:
|
|
||||||
os.chdir( cur_d )
|
|
||||||
|
|
||||||
|
Custom server definitions can be defined in JSON files, allowing
|
||||||
|
installation of arbitrary servers packaged in one of the ways that this
|
||||||
|
installer understands.
|
||||||
|
|
||||||
def MakeExecutable( file_path ):
|
The format of the file can be found on the Vimspector reference guide:
|
||||||
# TODO: import stat and use them by _just_ adding the X bit.
|
https://puremourning.github.io/vimspector
|
||||||
print( 'Making executable: {}'.format( file_path ) )
|
|
||||||
os.chmod( file_path, 0o755 )
|
|
||||||
|
|
||||||
|
NOTE: This script should usually _not_ be run under `sudo` or as root. It
|
||||||
def InstallCppTools( name, root ):
|
downloads and extracts things only to directories under its own path. No
|
||||||
extension = os.path.join( root, 'extension' )
|
system files or folders are chnaged by this script. If you really want to
|
||||||
|
run under sudo, pass --sudo, but this is _almost certainly_ the wrong thing
|
||||||
# It's hilarious, but the execute bits aren't set in the vsix. So they
|
to do.
|
||||||
# actually have javascript code which does this. It's just a horrible horrible
|
"""
|
||||||
# hack that really is not funny.
|
)
|
||||||
MakeExecutable( os.path.join( extension, 'debugAdapters', 'OpenDebugAD7' ) )
|
|
||||||
with open( os.path.join( extension, 'package.json' ) ) as f:
|
|
||||||
package = json.load( f )
|
|
||||||
runtime_dependencies = package[ 'runtimeDependencies' ]
|
|
||||||
for dependency in runtime_dependencies:
|
|
||||||
for binary in dependency.get( 'binaries' ):
|
|
||||||
file_path = os.path.abspath( os.path.join( extension, binary ) )
|
|
||||||
if os.path.exists( file_path ):
|
|
||||||
MakeExecutable( os.path.join( extension, binary ) )
|
|
||||||
|
|
||||||
MakeExtensionSymlink( name, root )
|
|
||||||
|
|
||||||
|
|
||||||
def InstallTclProDebug( name, root ):
|
|
||||||
configure = [ './configure' ]
|
|
||||||
|
|
||||||
if OS == 'macos':
|
|
||||||
# Apple removed the headers from system frameworks because they are
|
|
||||||
# determined to make life difficult. And the TCL configure scripts are super
|
|
||||||
# old so don't know about this. So we do their job for them and try and find
|
|
||||||
# a tclConfig.sh.
|
|
||||||
#
|
|
||||||
# NOTE however that in Apple's infinite wisdom, installing the "headers" in
|
|
||||||
# the other location is actually broken because the paths in the
|
|
||||||
# tclConfig.sh are pointing at the _old_ location. You actually do have to
|
|
||||||
# run the package installation which puts the headers back in order to work.
|
|
||||||
# This is why the below list is does not contain stuff from
|
|
||||||
# /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform
|
|
||||||
# '/Applications/Xcode.app/Contents/Developer/Platforms'
|
|
||||||
# '/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System'
|
|
||||||
# '/Library/Frameworks/Tcl.framework',
|
|
||||||
# '/Applications/Xcode.app/Contents/Developer/Platforms'
|
|
||||||
# '/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System'
|
|
||||||
# '/Library/Frameworks/Tcl.framework/Versions'
|
|
||||||
# '/Current',
|
|
||||||
for p in [ '/usr/local/opt/tcl-tk/lib' ]:
|
|
||||||
if os.path.exists( os.path.join( p, 'tclConfig.sh' ) ):
|
|
||||||
configure.append( '--with-tcl=' + p )
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
with CurrentWorkingDir( os.path.join( root, 'lib', 'tclparser' ) ):
|
|
||||||
subprocess.check_call( configure )
|
|
||||||
subprocess.check_call( [ 'make' ] )
|
|
||||||
|
|
||||||
MakeSymlink( gadget_dir, name, root )
|
|
||||||
|
|
||||||
|
|
||||||
def InstallNodeDebug( name, root ):
|
|
||||||
node_version = subprocess.check_output( [ 'node', '--version' ],
|
|
||||||
universal_newlines=True ).strip()
|
|
||||||
print( "Node.js version: {}".format( node_version ) )
|
|
||||||
if list( map( int, node_version[ 1: ].split( '.' ) ) ) >= [ 12, 0, 0 ]:
|
|
||||||
print( "Can't install vscode-debug-node2:" )
|
|
||||||
print( "Sorry, you appear to be running node 12 or later. That's not "
|
|
||||||
"compatible with the build system for this extension, and as far as "
|
|
||||||
"we know, there isn't a pre-built independent package." )
|
|
||||||
print( "My advice is to install nvm, then do:" )
|
|
||||||
print( " $ nvm install --lts 10" )
|
|
||||||
print( " $ nvm use --lts 10" )
|
|
||||||
print( " $ ./install_gadget.py --enable-node ..." )
|
|
||||||
raise RuntimeError( 'Invalid node environent for node debugger' )
|
|
||||||
|
|
||||||
with CurrentWorkingDir( root ):
|
|
||||||
subprocess.check_call( [ 'npm', 'install' ] )
|
|
||||||
subprocess.check_call( [ 'npm', 'run', 'build' ] )
|
|
||||||
MakeSymlink( gadget_dir, name, root )
|
|
||||||
|
|
||||||
|
|
||||||
def WithRetry( f ):
|
|
||||||
retries = 5
|
|
||||||
timeout = 1 # seconds
|
|
||||||
|
|
||||||
@functools.wraps( f )
|
|
||||||
def wrapper( *args, **kwargs ):
|
|
||||||
thrown = None
|
|
||||||
for _ in range( retries ):
|
|
||||||
try:
|
|
||||||
return f( *args, **kwargs )
|
|
||||||
except Exception as e:
|
|
||||||
thrown = e
|
|
||||||
print( "Failed - {}, will retry in {} seconds".format( e, timeout ) )
|
|
||||||
time.sleep( timeout )
|
|
||||||
raise thrown
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
@WithRetry
|
|
||||||
def UrlOpen( *args, **kwargs ):
|
|
||||||
return urllib2.urlopen( *args, **kwargs )
|
|
||||||
|
|
||||||
|
|
||||||
def DownloadFileTo( url, destination, file_name = None, checksum = None ):
|
|
||||||
if not file_name:
|
|
||||||
file_name = url.split( '/' )[ -1 ]
|
|
||||||
|
|
||||||
file_path = os.path.abspath( os.path.join( destination, file_name ) )
|
|
||||||
|
|
||||||
if not os.path.isdir( destination ):
|
|
||||||
os.makedirs( destination )
|
|
||||||
|
|
||||||
if os.path.exists( file_path ):
|
|
||||||
if checksum:
|
|
||||||
if ValidateCheckSumSHA256( file_path, checksum ):
|
|
||||||
print( "Checksum matches for {}, using it".format( file_path ) )
|
|
||||||
return file_path
|
|
||||||
else:
|
|
||||||
print( "Checksum doesn't match for {}, removing it".format(
|
|
||||||
file_path ) )
|
|
||||||
|
|
||||||
print( "Removing existing {}".format( file_path ) )
|
|
||||||
os.remove( file_path )
|
|
||||||
|
|
||||||
r = urllib2.Request( url, headers = { 'User-Agent': 'Vimspector' } )
|
|
||||||
|
|
||||||
print( "Downloading {} to {}/{}".format( url, destination, file_name ) )
|
|
||||||
|
|
||||||
with contextlib.closing( UrlOpen( r ) ) as u:
|
|
||||||
with open( file_path, 'wb' ) as f:
|
|
||||||
f.write( u.read() )
|
|
||||||
|
|
||||||
if checksum:
|
|
||||||
if not ValidateCheckSumSHA256( file_path, checksum ):
|
|
||||||
raise RuntimeError(
|
|
||||||
'Checksum for {} ({}) does not match expected {}'.format(
|
|
||||||
file_path,
|
|
||||||
GetChecksumSHA254( file_path ),
|
|
||||||
checksum ) )
|
|
||||||
else:
|
|
||||||
print( "Checksum for {}: {}".format( file_path,
|
|
||||||
GetChecksumSHA254( file_path ) ) )
|
|
||||||
|
|
||||||
return file_path
|
|
||||||
|
|
||||||
|
|
||||||
def GetChecksumSHA254( file_path ):
|
|
||||||
with open( file_path, 'rb' ) as existing_file:
|
|
||||||
return hashlib.sha256( existing_file.read() ).hexdigest()
|
|
||||||
|
|
||||||
|
|
||||||
def ValidateCheckSumSHA256( file_path, checksum ):
|
|
||||||
existing_sha256 = GetChecksumSHA254( file_path )
|
|
||||||
return existing_sha256 == checksum
|
|
||||||
|
|
||||||
|
|
||||||
def RemoveIfExists( destination ):
|
|
||||||
if os.path.exists( destination ) or os.path.islink( destination ):
|
|
||||||
if os.path.islink( destination ):
|
|
||||||
print( "Removing file {}".format( destination ) )
|
|
||||||
os.remove( destination )
|
|
||||||
else:
|
|
||||||
print( "Removing dir {}".format( destination ) )
|
|
||||||
shutil.rmtree( destination )
|
|
||||||
|
|
||||||
|
|
||||||
# Python's ZipFile module strips execute bits from files, for no good reason
|
|
||||||
# other than crappy code. Let's do it's job for it.
|
|
||||||
class ModePreservingZipFile( zipfile.ZipFile ):
|
|
||||||
def extract( self, member, path = None, pwd = None ):
|
|
||||||
if not isinstance( member, zipfile.ZipInfo ):
|
|
||||||
member = self.getinfo( member )
|
|
||||||
|
|
||||||
if path is None:
|
|
||||||
path = os.getcwd()
|
|
||||||
|
|
||||||
ret_val = self._extract_member( member, path, pwd )
|
|
||||||
attr = member.external_attr >> 16
|
|
||||||
os.chmod( ret_val, attr )
|
|
||||||
return ret_val
|
|
||||||
|
|
||||||
|
|
||||||
def ExtractZipTo( file_path, destination, format ):
|
|
||||||
print( "Extracting {} to {}".format( file_path, destination ) )
|
|
||||||
RemoveIfExists( destination )
|
|
||||||
|
|
||||||
if format == 'zip':
|
|
||||||
with ModePreservingZipFile( file_path ) as f:
|
|
||||||
f.extractall( path = destination )
|
|
||||||
elif format == 'zip.gz':
|
|
||||||
with gzip.open( file_path, 'rb' ) as f:
|
|
||||||
file_contents = f.read()
|
|
||||||
|
|
||||||
with ModePreservingZipFile( BytesIO( file_contents ) ) as f:
|
|
||||||
f.extractall( path = destination )
|
|
||||||
|
|
||||||
elif format == 'tar':
|
|
||||||
try:
|
|
||||||
with tarfile.open( file_path ) as f:
|
|
||||||
f.extractall( path = destination )
|
|
||||||
except Exception:
|
|
||||||
# There seems to a bug in python's tarfile that means it can't read some
|
|
||||||
# windows-generated tar files
|
|
||||||
os.makedirs( destination )
|
|
||||||
with CurrentWorkingDir( destination ):
|
|
||||||
subprocess.check_call( [ 'tar', 'zxvf', file_path ] )
|
|
||||||
|
|
||||||
|
|
||||||
def MakeExtensionSymlink( name, root ):
|
|
||||||
MakeSymlink( gadget_dir, name, os.path.join( root, 'extension' ) ),
|
|
||||||
|
|
||||||
|
|
||||||
def MakeSymlink( in_folder, link, pointing_to ):
|
|
||||||
RemoveIfExists( os.path.join( in_folder, link ) )
|
|
||||||
|
|
||||||
in_folder = os.path.abspath( in_folder )
|
|
||||||
pointing_to = os.path.relpath( os.path.abspath( pointing_to ),
|
|
||||||
in_folder )
|
|
||||||
os.symlink( pointing_to, os.path.join( in_folder, link ) )
|
|
||||||
|
|
||||||
|
|
||||||
def CloneRepoTo( url, ref, destination ):
|
|
||||||
RemoveIfExists( destination )
|
|
||||||
subprocess.check_call( [ 'git', 'clone', url, destination ] )
|
|
||||||
subprocess.check_call( [ 'git', '-C', destination, 'checkout', ref ] )
|
|
||||||
subprocess.check_call( [ 'git', 'submodule', 'sync', '--recursive' ] )
|
|
||||||
subprocess.check_call( [ 'git',
|
|
||||||
'submodule',
|
|
||||||
'update',
|
|
||||||
'--init',
|
|
||||||
'--recursive' ] )
|
|
||||||
|
|
||||||
|
|
||||||
OS = install.GetOS()
|
|
||||||
gadget_dir = install.GetGadgetDir( os.path.dirname( __file__ ), OS )
|
|
||||||
|
|
||||||
print( 'OS = ' + OS )
|
|
||||||
print( 'gadget_dir = ' + gadget_dir )
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument( '--all',
|
parser.add_argument( '--all',
|
||||||
action = 'store_true',
|
action = 'store_true',
|
||||||
help = 'Enable all supported completers' )
|
help = 'Enable all supported completers' )
|
||||||
|
|
@ -523,103 +69,186 @@ parser.add_argument( '--force-all',
|
||||||
action = 'store_true',
|
action = 'store_true',
|
||||||
help = 'Enable all unsupported completers' )
|
help = 'Enable all unsupported completers' )
|
||||||
|
|
||||||
done_languages = set()
|
parser.add_argument( '--upgrade',
|
||||||
for name, gadget in GADGETS.items():
|
action = 'store_true',
|
||||||
lang = gadget[ 'language' ]
|
help = 'Only update adapters changed from the manifest' )
|
||||||
if lang in done_languages:
|
|
||||||
continue
|
parser.add_argument( '--quiet',
|
||||||
|
action = 'store_true',
|
||||||
|
help = 'Suppress installation output' )
|
||||||
|
|
||||||
|
parser.add_argument( '--verbose',
|
||||||
|
action = 'store_true',
|
||||||
|
help = 'Force installation output' )
|
||||||
|
|
||||||
|
parser.add_argument( '--basedir',
|
||||||
|
action = 'store',
|
||||||
|
help = 'Advanced option. '
|
||||||
|
'Base directory under which to keep gadgets, '
|
||||||
|
'configurations, etc.. Default: vimspector '
|
||||||
|
'installation dir. Useful for developers or '
|
||||||
|
'multi-user installations' )
|
||||||
|
|
||||||
|
parser.add_argument( '--no-gadget-config',
|
||||||
|
action = 'store_true',
|
||||||
|
help = "Don't write the .gagets.json, just install" )
|
||||||
|
|
||||||
|
parser.add_argument( '--update-gadget-config',
|
||||||
|
action = 'store_true',
|
||||||
|
help =
|
||||||
|
"Update the gadget config rather than overwrite it" )
|
||||||
|
|
||||||
|
parser.add_argument( '--enable-custom',
|
||||||
|
dest='custom_gadget_file',
|
||||||
|
action='append',
|
||||||
|
nargs='*',
|
||||||
|
default = [],
|
||||||
|
help = 'Read custom gadget from supplied file. This '
|
||||||
|
'can be supplied multiple times and each time '
|
||||||
|
'multiple files can be passed.' )
|
||||||
|
|
||||||
|
parser.add_argument( '--sudo',
|
||||||
|
action='store_true',
|
||||||
|
help = "If you're really really really sure you want to "
|
||||||
|
"run this as root via sudo, pass this flag." )
|
||||||
|
|
||||||
|
done_languages = set()
|
||||||
|
for name, gadget in gadgets.GADGETS.items():
|
||||||
|
langs = gadget[ 'language' ]
|
||||||
|
if not isinstance( langs, list ):
|
||||||
|
langs = [ langs ]
|
||||||
|
for lang in langs:
|
||||||
|
if lang in done_languages:
|
||||||
|
continue
|
||||||
|
|
||||||
|
done_languages.add( lang )
|
||||||
|
if not gadget.get( 'enabled', True ):
|
||||||
|
parser.add_argument(
|
||||||
|
'--force-enable-' + lang,
|
||||||
|
action = 'store_true',
|
||||||
|
help = 'Install the unsupported {} debug adapter for {} support'.format(
|
||||||
|
name,
|
||||||
|
lang ) )
|
||||||
|
continue
|
||||||
|
|
||||||
done_languages.add( lang )
|
|
||||||
if not gadget.get( 'enabled', True ):
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--force-enable-' + lang,
|
'--enable-' + lang,
|
||||||
action = 'store_true',
|
action = 'store_true',
|
||||||
help = 'Install the unsupported {} debug adapter for {} support'.format(
|
help = 'Install the {} debug adapter for {} support'.format(
|
||||||
name,
|
name,
|
||||||
lang ) )
|
lang ) )
|
||||||
continue
|
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--enable-' + lang,
|
'--disable-' + lang,
|
||||||
action = 'store_true',
|
action = 'store_true',
|
||||||
help = 'Install the {} debug adapter for {} support'.format(
|
help = "Don't install the {} debug adapter for {} support "
|
||||||
name,
|
'(when supplying --all)'.format( name, lang ) )
|
||||||
lang ) )
|
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--disable-' + lang,
|
"--no-check-certificate",
|
||||||
action = 'store_true',
|
action = "store_true",
|
||||||
help = "Don't install the {} debug adapter for {} support "
|
help = "Do not verify SSL certificates for file downloads."
|
||||||
'(when supplying --all)'.format( name, lang ) )
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
installer.AbortIfSUperUser( args.sudo )
|
||||||
|
|
||||||
|
vimspector_base = os.path.dirname( __file__ )
|
||||||
|
if args.basedir:
|
||||||
|
vimspector_base = os.path.abspath( args.basedir )
|
||||||
|
|
||||||
|
install.MakeInstallDirs( vimspector_base )
|
||||||
|
installer.Configure( vimspector_base = vimspector_base,
|
||||||
|
quiet = args.quiet and not args.verbose,
|
||||||
|
no_check_certificate = args.no_check_certificate )
|
||||||
|
|
||||||
if args.force_all and not args.all:
|
if args.force_all and not args.all:
|
||||||
args.all = True
|
args.all = True
|
||||||
|
|
||||||
|
CUSTOM_GADGETS = {}
|
||||||
|
custom_files = glob.glob( os.path.join( vimspector_base,
|
||||||
|
'gadgets',
|
||||||
|
'custom',
|
||||||
|
'*.json' ) )
|
||||||
|
for custom_file_name in functools.reduce( operator.add,
|
||||||
|
args.custom_gadget_file,
|
||||||
|
custom_files ):
|
||||||
|
with open( custom_file_name, 'r' ) as custom_file:
|
||||||
|
CUSTOM_GADGETS.update( json.loads( minify( custom_file.read() ) ) )
|
||||||
|
|
||||||
|
|
||||||
failed = []
|
failed = []
|
||||||
all_adapters = {}
|
succeeded = []
|
||||||
for name, gadget in GADGETS.items():
|
all_adapters = installer.ReadAdapters(
|
||||||
if not gadget.get( 'enabled', True ):
|
read_existing = args.update_gadget_config )
|
||||||
if ( not args.force_all
|
manifest = installer.Manifest()
|
||||||
and not getattr( args, 'force_enable_' + gadget[ 'language' ] ) ):
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
if not args.all and not getattr( args, 'enable_' + gadget[ 'language' ] ):
|
|
||||||
continue
|
|
||||||
if getattr( args, 'disable_' + gadget[ 'language' ] ):
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
for name, gadget in gadgets.GADGETS.items():
|
||||||
v = {}
|
langs = gadget[ 'language' ]
|
||||||
v.update( gadget.get( 'all', {} ) )
|
if not isinstance( langs, list ):
|
||||||
v.update( gadget.get( OS, {} ) )
|
langs = [ langs ]
|
||||||
|
skip = 0
|
||||||
if 'download' in gadget:
|
for lang in langs:
|
||||||
if 'file_name' not in v:
|
if not gadget.get( 'enabled', True ):
|
||||||
raise RuntimeError( "Unsupported OS {} for gadget {}".format( OS,
|
if ( not args.force_all
|
||||||
name ) )
|
and not getattr( args, 'force_enable_' + lang ) ):
|
||||||
|
skip = skip + 1
|
||||||
destination = os.path.join( gadget_dir, 'download', name, v[ 'version' ] )
|
continue
|
||||||
|
|
||||||
url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( v )
|
|
||||||
|
|
||||||
file_path = DownloadFileTo(
|
|
||||||
url,
|
|
||||||
destination,
|
|
||||||
file_name = gadget[ 'download' ].get( 'target' ),
|
|
||||||
checksum = v.get( 'checksum' ) )
|
|
||||||
root = os.path.join( destination, 'root' )
|
|
||||||
ExtractZipTo( file_path,
|
|
||||||
root,
|
|
||||||
format = gadget[ 'download' ].get( 'format', 'zip' ) )
|
|
||||||
elif 'repo' in gadget:
|
|
||||||
url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( v )
|
|
||||||
ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( v )
|
|
||||||
|
|
||||||
destination = os.path.join( gadget_dir, 'download', name )
|
|
||||||
CloneRepoTo( url, ref, destination )
|
|
||||||
root = destination
|
|
||||||
|
|
||||||
if 'do' in gadget:
|
|
||||||
gadget[ 'do' ]( name, root )
|
|
||||||
else:
|
else:
|
||||||
MakeExtensionSymlink( name, root )
|
if not args.all and not getattr( args, 'enable_' + lang ):
|
||||||
|
skip = skip + 1
|
||||||
|
continue
|
||||||
|
if getattr( args, 'disable_' + lang ):
|
||||||
|
skip = skip + 1
|
||||||
|
continue
|
||||||
|
if skip == len( langs ):
|
||||||
|
continue
|
||||||
|
|
||||||
all_adapters.update( gadget.get( 'adapters', {} ) )
|
if not args.upgrade:
|
||||||
|
manifest.Clear( name )
|
||||||
|
|
||||||
|
installer.InstallGagdet( name,
|
||||||
|
gadget,
|
||||||
|
manifest,
|
||||||
|
succeeded,
|
||||||
|
failed,
|
||||||
|
all_adapters )
|
||||||
|
|
||||||
|
|
||||||
print( "Done installing {}".format( name ) )
|
for name, gadget in CUSTOM_GADGETS.items():
|
||||||
except Exception as e:
|
if not args.upgrade:
|
||||||
traceback.print_exc()
|
manifest.Clear( name )
|
||||||
failed.append( name )
|
|
||||||
print( "FAILED installing {}: {}".format( name, e ) )
|
|
||||||
|
|
||||||
|
installer.InstallGagdet( name,
|
||||||
|
gadget,
|
||||||
|
manifest,
|
||||||
|
succeeded,
|
||||||
|
failed,
|
||||||
|
all_adapters )
|
||||||
|
|
||||||
with open( install.GetGadgetConfigFile( os.path.dirname( __file__ ) ),
|
if args.no_gadget_config:
|
||||||
'w' ) as f:
|
print( "" )
|
||||||
json.dump( { 'adapters': all_adapters }, f, indent=2, sort_keys=True )
|
print( "Would write the following gadgets: " )
|
||||||
|
installer.WriteAdapters( all_adapters, to_file = sys.stdout )
|
||||||
|
else:
|
||||||
|
installer.WriteAdapters( all_adapters )
|
||||||
|
|
||||||
|
manifest.Write()
|
||||||
|
|
||||||
|
if args.basedir:
|
||||||
|
print( "" )
|
||||||
|
print( "***NOTE***: You set --basedir to " + args.basedir +
|
||||||
|
". Therefore you _must_ ensure this is in your vimrc:\n"
|
||||||
|
"let g:vimspector_base_dir='" + vimspector_base + "'" )
|
||||||
|
|
||||||
|
if succeeded:
|
||||||
|
print( "Done" )
|
||||||
|
print( "The following adapters were installed successfully:\n - {}".format(
|
||||||
|
'\n - '.join( succeeded ) ) )
|
||||||
|
|
||||||
if failed:
|
if failed:
|
||||||
raise RuntimeError( 'Failed to install gadgets: {}'.format(
|
sys.exit( 'Failed to install adapters:\n * {}{}'.format(
|
||||||
','.join( failed ) ) )
|
'\n * '.join( failed ),
|
||||||
|
"\nRe-run with --verbose for more info on failures"
|
||||||
|
if args.quiet and not args.verbose else '' ) )
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,20 @@ mkdir -p ${PACK}
|
||||||
pushd ${PACK}
|
pushd ${PACK}
|
||||||
mkdir -p vimspector/opt/vimspector
|
mkdir -p vimspector/opt/vimspector
|
||||||
pushd vimspector/opt/vimspector
|
pushd vimspector/opt/vimspector
|
||||||
for d in autoload plugin python3 vendor doc; do
|
for d in autoload plugin python3 vendor doc support; do
|
||||||
if [[ -d ${ROOT}/$d ]]; then
|
if [[ -d ${ROOT}/$d ]]; then
|
||||||
cp -r ${ROOT}/$d .
|
cp -r ${ROOT}/$d .
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
mkdir -p gadgets
|
mkdir -p gadgets
|
||||||
cp -r ${ROOT}/gadgets/${OS} gadgets/
|
cp -r ${ROOT}/gadgets/${OS} gadgets/
|
||||||
|
for f in install_gadget.py \
|
||||||
|
CODE_OF_CONDUCT.md \
|
||||||
|
CONTRIBUTING.md \
|
||||||
|
LICENCE \
|
||||||
|
README.md; do
|
||||||
|
cp ${ROOT}/${f} .
|
||||||
|
done
|
||||||
popd
|
popd
|
||||||
popd
|
popd
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,13 @@
|
||||||
" See the License for the specific language governing permissions and
|
" See the License for the specific language governing permissions and
|
||||||
" limitations under the License.
|
" limitations under the License.
|
||||||
|
|
||||||
|
if !has( 'python3' )
|
||||||
|
echohl WarningMsg
|
||||||
|
echom 'Vimspector unavailable: Requires Vim compiled with +python3'
|
||||||
|
echohl None
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
" Boilerplate {{{
|
" Boilerplate {{{
|
||||||
let s:save_cpo = &cpoptions
|
let s:save_cpo = &cpoptions
|
||||||
set cpoptions&vim
|
set cpoptions&vim
|
||||||
|
|
@ -26,38 +33,127 @@ if exists( 'g:loaded_vimpector' )
|
||||||
call s:restore_cpo()
|
call s:restore_cpo()
|
||||||
finish
|
finish
|
||||||
endif
|
endif
|
||||||
|
"}}}
|
||||||
" TODO:
|
|
||||||
" - Check Vim version (for jobs)
|
|
||||||
" - Check python support
|
|
||||||
" - Add commands/mappings/menus?
|
|
||||||
|
|
||||||
let g:loaded_vimpector = 1
|
let g:loaded_vimpector = 1
|
||||||
|
let g:vimspector_home = expand( '<sfile>:p:h:h' )
|
||||||
|
|
||||||
let s:mappings = get( g:, 'vimspector_enable_mappings', '' )
|
let s:mappings = get( g:, 'vimspector_enable_mappings', '' )
|
||||||
|
|
||||||
|
nnoremap <silent> <Plug>VimspectorContinue
|
||||||
|
\ :<c-u>call vimspector#Continue()<CR>
|
||||||
|
nnoremap <silent> <Plug>VimspectorLaunch
|
||||||
|
\ :<c-u>call vimspector#Launch( v:true )<CR>
|
||||||
|
nnoremap <silent> <Plug>VimspectorStop
|
||||||
|
\ :<c-u>call vimspector#Stop()<CR>
|
||||||
|
nnoremap <silent> <Plug>VimspectorRestart
|
||||||
|
\ :<c-u>call vimspector#Restart()<CR>
|
||||||
|
nnoremap <silent> <Plug>VimspectorPause
|
||||||
|
\ :<c-u>call vimspector#Pause()<CR>
|
||||||
|
nnoremap <silent> <Plug>VimspectorToggleBreakpoint
|
||||||
|
\ :<c-u>call vimspector#ToggleBreakpoint()<CR>
|
||||||
|
nnoremap <silent> <Plug>VimspectorToggleConditionalBreakpoint
|
||||||
|
\ :<c-u>call vimspector#ToggleBreakpoint(
|
||||||
|
\ { 'condition': input( 'Enter condition expression: ' ),
|
||||||
|
\ 'hitCondition': input( 'Enter hit count expression: ' ) }
|
||||||
|
\ )<CR>
|
||||||
|
nnoremap <silent> <Plug>VimspectorAddFunctionBreakpoint
|
||||||
|
\ :<c-u>call vimspector#AddFunctionBreakpoint( expand( '<cexpr>' ) )<CR>
|
||||||
|
nnoremap <silent> <Plug>VimspectorStepOver
|
||||||
|
\ :<c-u>call vimspector#StepOver()<CR>
|
||||||
|
nnoremap <silent> <Plug>VimspectorStepInto
|
||||||
|
\ :<c-u>call vimspector#StepInto()<CR>
|
||||||
|
nnoremap <silent> <Plug>VimspectorStepOut
|
||||||
|
\ :<c-u>call vimspector#StepOut()<CR>
|
||||||
|
|
||||||
|
nnoremap <silent> <Plug>VimspectorRunToCursor
|
||||||
|
\ :<c-u>call vimspector#RunToCursor()<CR>
|
||||||
|
|
||||||
|
" Eval for normal mode
|
||||||
|
nnoremap <silent> <Plug>VimspectorBalloonEval
|
||||||
|
\ :<c-u>call vimspector#ShowEvalBalloon( 0 )<CR>
|
||||||
|
" And for visual modes
|
||||||
|
xnoremap <silent> <Plug>VimspectorBalloonEval
|
||||||
|
\ :<c-u>call vimspector#ShowEvalBalloon( 1 )<CR>
|
||||||
|
|
||||||
|
nnoremap <silent> <Plug>VimspectorUpFrame
|
||||||
|
\ :<c-u>call vimspector#UpFrame()<CR>
|
||||||
|
nnoremap <silent> <Plug>VimspectorDownFrame
|
||||||
|
\ :<c-u>call vimspector#DownFrame()<CR>
|
||||||
|
|
||||||
if s:mappings ==# 'VISUAL_STUDIO'
|
if s:mappings ==# 'VISUAL_STUDIO'
|
||||||
nnoremap <F5> :call vimspector#Continue()<CR>
|
nmap <F5> <Plug>VimspectorContinue
|
||||||
nnoremap <S-F5> :call vimspector#Stop()<CR>
|
nmap <S-F5> <Plug>VimspectorStop
|
||||||
nnoremap <C-S-F5> :call vimspector#Restart()<CR>
|
nmap <C-S-F5> <Plug>VimspectorRestart
|
||||||
nnoremap <F6> :call vimspector#Pause()<CR>
|
nmap <F6> <Plug>VimspectorPause
|
||||||
nnoremap <F9> :call vimspector#ToggleBreakpoint()<CR>
|
nmap <F9> <Plug>VimspectorToggleBreakpoint
|
||||||
nnoremap <S-F9> :call vimspector#AddFunctionBreakpoint( expand( '<cexpr>' ) )<CR>
|
nmap <S-F9> <Plug>VimspectorAddFunctionBreakpoint
|
||||||
nnoremap <F10> :call vimspector#StepOver()<CR>
|
nmap <F10> <Plug>VimspectorStepOver
|
||||||
nnoremap <F11> :call vimspector#StepInto()<CR>
|
nmap <F11> <Plug>VimspectorStepInto
|
||||||
nnoremap <S-F11> :call vimspector#StepOut()<CR>
|
nmap <S-F11> <Plug>VimspectorStepOut
|
||||||
elseif s:mappings ==# 'HUMAN'
|
elseif s:mappings ==# 'HUMAN'
|
||||||
nnoremap <F5> :call vimspector#Continue()<CR>
|
nmap <F5> <Plug>VimspectorContinue
|
||||||
nnoremap <F3> :call vimspector#Stop()<CR>
|
nmap <leader><F5> <Plug>VimspectorLaunch
|
||||||
nnoremap <F4> :call vimspector#Restart()<CR>
|
nmap <F3> <Plug>VimspectorStop
|
||||||
nnoremap <F6> :call vimspector#Pause()<CR>
|
nmap <F4> <Plug>VimspectorRestart
|
||||||
nnoremap <F9> :call vimspector#ToggleBreakpoint()<CR>
|
nmap <F6> <Plug>VimspectorPause
|
||||||
nnoremap <F8> :call vimspector#AddFunctionBreakpoint( expand( '<cexpr>' ) )<CR>
|
nmap <F9> <Plug>VimspectorToggleBreakpoint
|
||||||
nnoremap <F10> :call vimspector#StepOver()<CR>
|
nmap <leader><F9> <Plug>VimspectorToggleConditionalBreakpoint
|
||||||
nnoremap <F11> :call vimspector#StepInto()<CR>
|
nmap <F8> <Plug>VimspectorAddFunctionBreakpoint
|
||||||
nnoremap <F12> :call vimspector#StepOut()<CR>
|
nmap <leader><F8> <Plug>VimspectorRunToCursor
|
||||||
|
nmap <F10> <Plug>VimspectorStepOver
|
||||||
|
nmap <F11> <Plug>VimspectorStepInto
|
||||||
|
nmap <F12> <Plug>VimspectorStepOut
|
||||||
endif
|
endif
|
||||||
"}}}
|
|
||||||
|
command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr
|
||||||
|
\ VimspectorWatch
|
||||||
|
\ call vimspector#AddWatch( <f-args> )
|
||||||
|
command! -bar -nargs=? -complete=custom,vimspector#CompleteOutput
|
||||||
|
\ VimspectorShowOutput
|
||||||
|
\ call vimspector#ShowOutput( <f-args> )
|
||||||
|
command! -bar
|
||||||
|
\ VimspectorToggleLog
|
||||||
|
\ call vimspector#ToggleLog()
|
||||||
|
command! -bar
|
||||||
|
\ VimspectorDebugInfo
|
||||||
|
\ call vimspector#PrintDebugInfo()
|
||||||
|
command! -nargs=1 -complete=custom,vimspector#CompleteExpr
|
||||||
|
\ VimspectorEval
|
||||||
|
\ call vimspector#Evaluate( <f-args> )
|
||||||
|
command! -bar
|
||||||
|
\ VimspectorReset
|
||||||
|
\ call vimspector#Reset( { 'interactive': v:true } )
|
||||||
|
|
||||||
|
" Installer commands
|
||||||
|
command! -bar -bang -nargs=* -complete=custom,vimspector#CompleteInstall
|
||||||
|
\ VimspectorInstall
|
||||||
|
\ call vimspector#Install( <q-bang>, <f-args> )
|
||||||
|
command! -bar -bang -nargs=*
|
||||||
|
\ VimspectorUpdate
|
||||||
|
\ call vimspector#Update( <q-bang>, <f-args> )
|
||||||
|
command! -bar -nargs=0
|
||||||
|
\ VimspectorAbortInstall
|
||||||
|
\ call vimspector#AbortInstall()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
" Dummy autocommands so that we can call this whenever
|
||||||
|
augroup VimspectorUserAutoCmds
|
||||||
|
autocmd!
|
||||||
|
autocmd User VimspectorUICreated silent
|
||||||
|
autocmd User VimspectorTerminalOpened silent
|
||||||
|
autocmd user VimspectorJumpedToFrame silent
|
||||||
|
autocmd user VimspectorDebugEnded silent
|
||||||
|
augroup END
|
||||||
|
|
||||||
|
" FIXME: Only register this _while_ debugging is active
|
||||||
|
augroup Vimspector
|
||||||
|
autocmd!
|
||||||
|
autocmd BufNew * call vimspector#OnBufferCreated( expand( '<afile>' ) )
|
||||||
|
augroup END
|
||||||
|
|
||||||
|
" boilerplate {{{
|
||||||
call s:restore_cpo()
|
call s:restore_cpo()
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import os
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from vimspector import utils
|
from vimspector import utils, signs
|
||||||
|
|
||||||
|
|
||||||
class ServerBreakpointHandler( object ):
|
class ServerBreakpointHandler( object ):
|
||||||
|
|
@ -44,6 +44,7 @@ class ProjectBreakpoints( object ):
|
||||||
self._line_breakpoints = defaultdict( list )
|
self._line_breakpoints = defaultdict( list )
|
||||||
self._func_breakpoints = []
|
self._func_breakpoints = []
|
||||||
self._exception_breakpoints = None
|
self._exception_breakpoints = None
|
||||||
|
self._configured_breakpoints = {}
|
||||||
|
|
||||||
# FIXME: Remove this. Remove breakpoints nonesense from code.py
|
# FIXME: Remove this. Remove breakpoints nonesense from code.py
|
||||||
self._breakpoints_handler = None
|
self._breakpoints_handler = None
|
||||||
|
|
@ -51,9 +52,23 @@ class ProjectBreakpoints( object ):
|
||||||
|
|
||||||
self._next_sign_id = 1
|
self._next_sign_id = 1
|
||||||
|
|
||||||
# TODO: Change to sign_define ?
|
if not signs.SignDefined( 'vimspectorBP' ):
|
||||||
vim.command( 'sign define vimspectorBP text==> texthl=Error' )
|
signs.DefineSign( 'vimspectorBP',
|
||||||
vim.command( 'sign define vimspectorBPDisabled text=!> texthl=Warning' )
|
text = '●',
|
||||||
|
double_text = '●',
|
||||||
|
texthl = 'WarningMsg' )
|
||||||
|
|
||||||
|
if not signs.SignDefined( 'vimspectorBPCond' ):
|
||||||
|
signs.DefineSign( 'vimspectorBPCond',
|
||||||
|
text = '◆',
|
||||||
|
double_text = '◆',
|
||||||
|
texthl = 'WarningMsg' )
|
||||||
|
|
||||||
|
if not signs.SignDefined( 'vimspectorBPDisabled' ):
|
||||||
|
signs.DefineSign( 'vimspectorBPDisabled',
|
||||||
|
text = '●',
|
||||||
|
double_text = '●',
|
||||||
|
texthl = 'LineNr' )
|
||||||
|
|
||||||
|
|
||||||
def ConnectionUp( self, connection ):
|
def ConnectionUp( self, connection ):
|
||||||
|
|
@ -73,8 +88,10 @@ class ProjectBreakpoints( object ):
|
||||||
# NOTE: we don't reset self._exception_breakpoints because we don't want to
|
# NOTE: we don't reset self._exception_breakpoints because we don't want to
|
||||||
# re-ask the user every time for the sane info.
|
# re-ask the user every time for the sane info.
|
||||||
|
|
||||||
|
# FIXME: If the adapter type changes, we should probably forget this ?
|
||||||
|
|
||||||
def ListBreakpoints( self ):
|
|
||||||
|
def BreakpointsAsQuickFix( self ):
|
||||||
# FIXME: Handling of breakpoints is a mess, split between _codeView and this
|
# FIXME: Handling of breakpoints is a mess, split between _codeView and this
|
||||||
# object. This makes no sense and should be centralised so that we don't
|
# object. This makes no sense and should be centralised so that we don't
|
||||||
# have this duplication and bug factory.
|
# have this duplication and bug factory.
|
||||||
|
|
@ -84,14 +101,16 @@ class ProjectBreakpoints( object ):
|
||||||
else:
|
else:
|
||||||
for file_name, breakpoints in self._line_breakpoints.items():
|
for file_name, breakpoints in self._line_breakpoints.items():
|
||||||
for bp in breakpoints:
|
for bp in breakpoints:
|
||||||
|
self._SignToLine( file_name, bp )
|
||||||
qf.append( {
|
qf.append( {
|
||||||
'filename': file_name,
|
'filename': file_name,
|
||||||
'lnum': bp[ 'line' ],
|
'lnum': bp[ 'line' ],
|
||||||
'col': 1,
|
'col': 1,
|
||||||
'type': 'L',
|
'type': 'L',
|
||||||
'valid': 1 if bp[ 'state' ] == 'ENABLED' else 0,
|
'valid': 1 if bp[ 'state' ] == 'ENABLED' else 0,
|
||||||
'text': "Line breakpoint - {}".format(
|
'text': "Line breakpoint - {}: {}".format(
|
||||||
bp[ 'state' ] )
|
bp[ 'state' ],
|
||||||
|
json.dumps( bp[ 'options' ] ) )
|
||||||
} )
|
} )
|
||||||
# I think this shows that the qf list is not right for this.
|
# I think this shows that the qf list is not right for this.
|
||||||
for bp in self._func_breakpoints:
|
for bp in self._func_breakpoints:
|
||||||
|
|
@ -101,71 +120,152 @@ class ProjectBreakpoints( object ):
|
||||||
'col': 1,
|
'col': 1,
|
||||||
'type': 'F',
|
'type': 'F',
|
||||||
'valid': 1,
|
'valid': 1,
|
||||||
'text': "Function breakpoint: {}".format( bp[ 'function' ] ),
|
'text': "Function breakpoint: {}: {}".format( bp[ 'function' ],
|
||||||
|
bp[ 'options' ] ),
|
||||||
} )
|
} )
|
||||||
|
|
||||||
vim.eval( 'setqflist( {} )'.format( json.dumps( qf ) ) )
|
return qf
|
||||||
|
|
||||||
|
|
||||||
def ClearBreakpoints( self ):
|
def ClearBreakpoints( self ):
|
||||||
# These are the user-entered breakpoints.
|
# These are the user-entered breakpoints.
|
||||||
for file_name, breakpoints in self._line_breakpoints.items():
|
for file_name, breakpoints in self._line_breakpoints.items():
|
||||||
for bp in breakpoints:
|
for bp in breakpoints:
|
||||||
|
self._SignToLine( file_name, bp )
|
||||||
if 'sign_id' in bp:
|
if 'sign_id' in bp:
|
||||||
vim.command( 'sign unplace {0} group=VimspectorBP'.format(
|
signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' )
|
||||||
bp[ 'sign_id' ] ) )
|
|
||||||
|
|
||||||
self._line_breakpoints = defaultdict( list )
|
self._line_breakpoints = defaultdict( list )
|
||||||
self._func_breakpoints = []
|
self._func_breakpoints = []
|
||||||
|
self._exception_breakpoints = None
|
||||||
|
|
||||||
self.UpdateUI()
|
self.UpdateUI()
|
||||||
|
|
||||||
def ToggleBreakpoint( self ):
|
def _FindLineBreakpoint( self, file_name, line ):
|
||||||
line, column = vim.current.window.cursor
|
file_name = os.path.abspath( file_name )
|
||||||
|
for index, bp in enumerate( self._line_breakpoints[ file_name ] ):
|
||||||
|
self._SignToLine( file_name, bp )
|
||||||
|
if bp[ 'line' ] == line:
|
||||||
|
return bp, index
|
||||||
|
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
def _PutLineBreakpoint( self, file_name, line, options ):
|
||||||
|
self._line_breakpoints[ os.path.abspath( file_name ) ].append( {
|
||||||
|
'state': 'ENABLED',
|
||||||
|
'line': line,
|
||||||
|
'options': options,
|
||||||
|
# 'sign_id': <filled in when placed>,
|
||||||
|
#
|
||||||
|
# Used by other breakpoint types (specified in options):
|
||||||
|
# 'condition': ...,
|
||||||
|
# 'hitCondition': ...,
|
||||||
|
# 'logMessage': ...
|
||||||
|
} )
|
||||||
|
|
||||||
|
|
||||||
|
def _DeleteLineBreakpoint( self, bp, file_name, index ):
|
||||||
|
if 'sign_id' in bp:
|
||||||
|
signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' )
|
||||||
|
del self._line_breakpoints[ os.path.abspath( file_name ) ][ index ]
|
||||||
|
|
||||||
|
|
||||||
|
def ToggleBreakpoint( self, options ):
|
||||||
|
line, _ = vim.current.window.cursor
|
||||||
file_name = vim.current.buffer.name
|
file_name = vim.current.buffer.name
|
||||||
|
|
||||||
if not file_name:
|
if not file_name:
|
||||||
return
|
return
|
||||||
|
|
||||||
found_bp = False
|
bp, index = self._FindLineBreakpoint( file_name, line )
|
||||||
action = 'New'
|
if bp is None:
|
||||||
for index, bp in enumerate( self._line_breakpoints[ file_name ] ):
|
# ADD
|
||||||
if bp[ 'line' ] == line:
|
self._PutLineBreakpoint( file_name, line, options )
|
||||||
found_bp = True
|
elif bp[ 'state' ] == 'ENABLED' and not self._connection:
|
||||||
if bp[ 'state' ] == 'ENABLED' and not self._connection:
|
# DISABLE
|
||||||
bp[ 'state' ] = 'DISABLED'
|
bp[ 'state' ] = 'DISABLED'
|
||||||
action = 'Disable'
|
else:
|
||||||
else:
|
# DELETE
|
||||||
if 'sign_id' in bp:
|
self._DeleteLineBreakpoint( bp, file_name, index )
|
||||||
vim.command( 'sign unplace {0} group=VimspectorBP'.format(
|
|
||||||
bp[ 'sign_id' ] ) )
|
|
||||||
del self._line_breakpoints[ file_name ][ index ]
|
|
||||||
action = 'Delete'
|
|
||||||
break
|
|
||||||
|
|
||||||
self._logger.debug( "Toggle found bp at {}:{} ? {} ({})".format(
|
|
||||||
file_name,
|
|
||||||
line,
|
|
||||||
found_bp,
|
|
||||||
action ) )
|
|
||||||
|
|
||||||
if not found_bp:
|
|
||||||
self._line_breakpoints[ file_name ].append( {
|
|
||||||
'state': 'ENABLED',
|
|
||||||
'line': line,
|
|
||||||
# 'sign_id': <filled in when placed>,
|
|
||||||
#
|
|
||||||
# Used by other breakpoint types:
|
|
||||||
# 'condition': ...,
|
|
||||||
# 'hitCondition': ...,
|
|
||||||
# 'logMessage': ...
|
|
||||||
} )
|
|
||||||
|
|
||||||
self.UpdateUI()
|
self.UpdateUI()
|
||||||
|
|
||||||
def AddFunctionBreakpoint( self, function ):
|
|
||||||
|
def SetLineBreakpoint( self, file_name, line_num, options, then = None ):
|
||||||
|
bp, _ = self._FindLineBreakpoint( file_name, line_num )
|
||||||
|
if bp is not None:
|
||||||
|
bp[ 'options' ] = options
|
||||||
|
return
|
||||||
|
self._PutLineBreakpoint( file_name, line_num, options )
|
||||||
|
self.UpdateUI( then )
|
||||||
|
|
||||||
|
|
||||||
|
def ClearLineBreakpoint( self, file_name, line_num ):
|
||||||
|
bp, index = self._FindLineBreakpoint( file_name, line_num )
|
||||||
|
if bp is None:
|
||||||
|
return
|
||||||
|
self._DeleteLineBreakpoint( bp, file_name, index )
|
||||||
|
self.UpdateUI()
|
||||||
|
|
||||||
|
|
||||||
|
def ClearTemporaryBreakpoint( self, file_name, line_num ):
|
||||||
|
bp, index = self._FindLineBreakpoint( file_name, line_num )
|
||||||
|
if bp is None:
|
||||||
|
return
|
||||||
|
if bp[ 'options' ].get( 'temporary' ):
|
||||||
|
self._DeleteLineBreakpoint( bp, file_name, index )
|
||||||
|
self.UpdateUI()
|
||||||
|
|
||||||
|
|
||||||
|
def ClearTemporaryBreakpoints( self ):
|
||||||
|
to_delete = []
|
||||||
|
for file_name, breakpoints in self._line_breakpoints.items():
|
||||||
|
for index, bp in enumerate( breakpoints ):
|
||||||
|
if bp[ 'options' ].get( 'temporary' ):
|
||||||
|
to_delete.append( ( bp, file_name, index ) )
|
||||||
|
|
||||||
|
for entry in to_delete:
|
||||||
|
self._DeleteLineBreakpoint( *entry )
|
||||||
|
|
||||||
|
|
||||||
|
def _UpdateTemporaryBreakpoints( self, breakpoints, temp_idxs ):
|
||||||
|
# adjust any temporary breakpoints to match the server result
|
||||||
|
# TODO: Maybe now is the time to ditch the split breakpoints nonesense
|
||||||
|
for temp_idx, user_bp in temp_idxs:
|
||||||
|
if temp_idx >= len( breakpoints ):
|
||||||
|
# Just can't trust servers ?
|
||||||
|
self._logger.debug( "Server Error - invalid breakpoints list did not "
|
||||||
|
"contain entry for temporary breakpoint at index "
|
||||||
|
f"{ temp_idx } i.e. { user_bp }" )
|
||||||
|
continue
|
||||||
|
|
||||||
|
bp = breakpoints[ temp_idx ]
|
||||||
|
|
||||||
|
if 'line' not in bp or not bp[ 'verified' ]:
|
||||||
|
utils.UserMessage(
|
||||||
|
"Unable to set temporary breakpoint at line "
|
||||||
|
f"{ user_bp[ 'line' ] } execution will continue...",
|
||||||
|
persist = True,
|
||||||
|
error = True )
|
||||||
|
|
||||||
|
self._logger.debug( f"Updating temporary breakpoint { user_bp } line "
|
||||||
|
f"{ user_bp[ 'line' ] } to { bp[ 'line' ] }" )
|
||||||
|
|
||||||
|
# if it was moved, update the user-breakpoint so that we unset it
|
||||||
|
# again properly
|
||||||
|
user_bp[ 'line' ] = bp[ 'line' ]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def AddFunctionBreakpoint( self, function, options ):
|
||||||
self._func_breakpoints.append( {
|
self._func_breakpoints.append( {
|
||||||
'state': 'ENABLED',
|
'state': 'ENABLED',
|
||||||
'function': function,
|
'function': function,
|
||||||
|
'options': options,
|
||||||
|
# Specified in options:
|
||||||
|
# 'condition': ...,
|
||||||
|
# 'hitCondition': ...,
|
||||||
} )
|
} )
|
||||||
|
|
||||||
# TODO: We don't really have aanything to update here, but if we're going to
|
# TODO: We don't really have aanything to update here, but if we're going to
|
||||||
|
|
@ -173,11 +273,13 @@ class ProjectBreakpoints( object ):
|
||||||
self.UpdateUI()
|
self.UpdateUI()
|
||||||
|
|
||||||
|
|
||||||
def UpdateUI( self ):
|
def UpdateUI( self, then = None ):
|
||||||
if self._connection:
|
if self._connection:
|
||||||
self.SendBreakpoints()
|
self.SendBreakpoints( then )
|
||||||
else:
|
else:
|
||||||
self._ShowBreakpoints()
|
self._ShowBreakpoints()
|
||||||
|
if then:
|
||||||
|
then()
|
||||||
|
|
||||||
|
|
||||||
def SetBreakpointsHandler( self, handler ):
|
def SetBreakpointsHandler( self, handler ):
|
||||||
|
|
@ -185,6 +287,10 @@ class ProjectBreakpoints( object ):
|
||||||
self._breakpoints_handler = handler
|
self._breakpoints_handler = handler
|
||||||
|
|
||||||
|
|
||||||
|
def SetConfiguredBreakpoints( self, configured_breakpoints ):
|
||||||
|
self._configured_breakpoints = configured_breakpoints
|
||||||
|
|
||||||
|
|
||||||
def SendBreakpoints( self, doneHandler = None ):
|
def SendBreakpoints( self, doneHandler = None ):
|
||||||
assert self._breakpoints_handler is not None
|
assert self._breakpoints_handler is not None
|
||||||
|
|
||||||
|
|
@ -193,27 +299,59 @@ class ProjectBreakpoints( object ):
|
||||||
|
|
||||||
awaiting = 0
|
awaiting = 0
|
||||||
|
|
||||||
def response_handler( source, msg ):
|
def response_received( *failure_args ):
|
||||||
if msg:
|
|
||||||
self._breakpoints_handler.AddBreakpoints( source, msg )
|
|
||||||
nonlocal awaiting
|
nonlocal awaiting
|
||||||
awaiting = awaiting - 1
|
awaiting = awaiting - 1
|
||||||
|
|
||||||
|
if failure_args and self._connection:
|
||||||
|
reason, msg = failure_args
|
||||||
|
utils.UserMessage( 'Unable to set breakpoint: {0}'.format( reason ),
|
||||||
|
persist = True,
|
||||||
|
error = True )
|
||||||
|
|
||||||
if awaiting == 0 and doneHandler:
|
if awaiting == 0 and doneHandler:
|
||||||
doneHandler()
|
doneHandler()
|
||||||
|
|
||||||
|
def response_handler( source, msg, temp_idxs = [] ):
|
||||||
|
if msg:
|
||||||
|
self._breakpoints_handler.AddBreakpoints( source, msg )
|
||||||
|
|
||||||
|
breakpoints = ( msg.get( 'body' ) or {} ).get( 'breakpoints' ) or []
|
||||||
|
self._UpdateTemporaryBreakpoints( breakpoints, temp_idxs )
|
||||||
|
response_received()
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: Must do this _first_ otherwise we might send requests and get
|
||||||
|
# replies before we finished sending all the requests.
|
||||||
|
if self._exception_breakpoints is None:
|
||||||
|
self._SetUpExceptionBreakpoints( self._configured_breakpoints )
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: add the _configured_breakpoints to line_breakpoints
|
||||||
|
|
||||||
for file_name, line_breakpoints in self._line_breakpoints.items():
|
for file_name, line_breakpoints in self._line_breakpoints.items():
|
||||||
|
temp_idxs = []
|
||||||
breakpoints = []
|
breakpoints = []
|
||||||
for bp in line_breakpoints:
|
for bp in line_breakpoints:
|
||||||
|
self._SignToLine( file_name, bp )
|
||||||
if 'sign_id' in bp:
|
if 'sign_id' in bp:
|
||||||
vim.command( 'sign unplace {0} group=VimspectorBP'.format(
|
signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' )
|
||||||
bp[ 'sign_id' ] ) )
|
|
||||||
del bp[ 'sign_id' ]
|
del bp[ 'sign_id' ]
|
||||||
|
|
||||||
if bp[ 'state' ] != 'ENABLED':
|
if bp[ 'state' ] != 'ENABLED':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
breakpoints.append( { 'line': bp[ 'line' ] } )
|
dap_bp = {}
|
||||||
|
dap_bp.update( bp[ 'options' ] )
|
||||||
|
dap_bp.update( { 'line': bp[ 'line' ] } )
|
||||||
|
|
||||||
|
dap_bp.pop( 'temporary', None )
|
||||||
|
|
||||||
|
if bp[ 'options' ].get( 'temporary' ):
|
||||||
|
temp_idxs.append( [ len( breakpoints ), bp ] )
|
||||||
|
|
||||||
|
breakpoints.append( dap_bp )
|
||||||
|
|
||||||
|
|
||||||
source = {
|
source = {
|
||||||
'name': os.path.basename( file_name ),
|
'name': os.path.basename( file_name ),
|
||||||
|
|
@ -222,7 +360,13 @@ class ProjectBreakpoints( object ):
|
||||||
|
|
||||||
awaiting = awaiting + 1
|
awaiting = awaiting + 1
|
||||||
self._connection.DoRequest(
|
self._connection.DoRequest(
|
||||||
lambda msg: response_handler( source, msg ),
|
# The source=source here is critical to ensure that we capture each
|
||||||
|
# source in the iteration, rather than ending up passing the same source
|
||||||
|
# to each callback.
|
||||||
|
lambda msg, source=source, temp_idxs=temp_idxs: response_handler(
|
||||||
|
source,
|
||||||
|
msg,
|
||||||
|
temp_idxs = temp_idxs ),
|
||||||
{
|
{
|
||||||
'command': 'setBreakpoints',
|
'command': 'setBreakpoints',
|
||||||
'arguments': {
|
'arguments': {
|
||||||
|
|
@ -230,27 +374,34 @@ class ProjectBreakpoints( object ):
|
||||||
'breakpoints': breakpoints,
|
'breakpoints': breakpoints,
|
||||||
},
|
},
|
||||||
'sourceModified': False, # TODO: We can actually check this
|
'sourceModified': False, # TODO: We can actually check this
|
||||||
}
|
},
|
||||||
|
failure_handler = response_received
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# TODO: Add the _configured_breakpoints to function breakpoints
|
||||||
|
|
||||||
if self._server_capabilities.get( 'supportsFunctionBreakpoints' ):
|
if self._server_capabilities.get( 'supportsFunctionBreakpoints' ):
|
||||||
awaiting = awaiting + 1
|
awaiting = awaiting + 1
|
||||||
|
breakpoints = []
|
||||||
|
for bp in self._func_breakpoints:
|
||||||
|
if bp[ 'state' ] != 'ENABLED':
|
||||||
|
continue
|
||||||
|
dap_bp = {}
|
||||||
|
dap_bp.update( bp[ 'options' ] )
|
||||||
|
dap_bp.update( { 'name': bp[ 'function' ] } )
|
||||||
|
breakpoints.append( dap_bp )
|
||||||
|
|
||||||
self._connection.DoRequest(
|
self._connection.DoRequest(
|
||||||
lambda msg: response_handler( None, msg ),
|
lambda msg: response_handler( None, msg ),
|
||||||
{
|
{
|
||||||
'command': 'setFunctionBreakpoints',
|
'command': 'setFunctionBreakpoints',
|
||||||
'arguments': {
|
'arguments': {
|
||||||
'breakpoints': [
|
'breakpoints': breakpoints,
|
||||||
{ 'name': bp[ 'function' ] }
|
|
||||||
for bp in self._func_breakpoints if bp[ 'state' ] == 'ENABLED'
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
failure_handler = response_received
|
||||||
)
|
)
|
||||||
|
|
||||||
if self._exception_breakpoints is None:
|
|
||||||
self._SetUpExceptionBreakpoints()
|
|
||||||
|
|
||||||
if self._exception_breakpoints:
|
if self._exception_breakpoints:
|
||||||
awaiting = awaiting + 1
|
awaiting = awaiting + 1
|
||||||
self._connection.DoRequest(
|
self._connection.DoRequest(
|
||||||
|
|
@ -258,14 +409,15 @@ class ProjectBreakpoints( object ):
|
||||||
{
|
{
|
||||||
'command': 'setExceptionBreakpoints',
|
'command': 'setExceptionBreakpoints',
|
||||||
'arguments': self._exception_breakpoints
|
'arguments': self._exception_breakpoints
|
||||||
}
|
},
|
||||||
|
failure_handler = response_received
|
||||||
)
|
)
|
||||||
|
|
||||||
if awaiting == 0 and doneHandler:
|
if awaiting == 0 and doneHandler:
|
||||||
doneHandler()
|
doneHandler()
|
||||||
|
|
||||||
|
|
||||||
def _SetUpExceptionBreakpoints( self ):
|
def _SetUpExceptionBreakpoints( self, configured_breakpoints ):
|
||||||
exception_breakpoint_filters = self._server_capabilities.get(
|
exception_breakpoint_filters = self._server_capabilities.get(
|
||||||
'exceptionBreakpointFilters',
|
'exceptionBreakpointFilters',
|
||||||
[] )
|
[] )
|
||||||
|
|
@ -278,14 +430,27 @@ class ProjectBreakpoints( object ):
|
||||||
# trigger requesting threads etc.). See the note in
|
# trigger requesting threads etc.). See the note in
|
||||||
# debug_session.py:_Initialise for more detials
|
# debug_session.py:_Initialise for more detials
|
||||||
exception_filters = []
|
exception_filters = []
|
||||||
|
configured_filter_options = configured_breakpoints.get( 'exception', {} )
|
||||||
if exception_breakpoint_filters:
|
if exception_breakpoint_filters:
|
||||||
for f in exception_breakpoint_filters:
|
for f in exception_breakpoint_filters:
|
||||||
default_value = 'Y' if f.get( 'default' ) else 'N'
|
default_value = 'Y' if f.get( 'default' ) else 'N'
|
||||||
|
|
||||||
result = utils.AskForInput(
|
if f[ 'filter' ] in configured_filter_options:
|
||||||
"Break on {} (Y/N/default: {})? ".format( f[ 'label' ],
|
result = configured_filter_options[ f[ 'filter' ] ]
|
||||||
default_value ),
|
|
||||||
default_value )
|
if isinstance( result, bool ):
|
||||||
|
result = 'Y' if result else 'N'
|
||||||
|
|
||||||
|
if not isinstance( result, str ) or result not in ( 'Y', 'N', '' ):
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid value for exception breakpoint filter '{f}': "
|
||||||
|
f"'{result}'. Must be boolean, 'Y', 'N' or '' (default)" )
|
||||||
|
else:
|
||||||
|
result = utils.AskForInput(
|
||||||
|
"{}: Break on {} (Y/N/default: {})? ".format( f[ 'filter' ],
|
||||||
|
f[ 'label' ],
|
||||||
|
default_value ),
|
||||||
|
default_value )
|
||||||
|
|
||||||
if result == 'Y':
|
if result == 'Y':
|
||||||
exception_filters.append( f[ 'filter' ] )
|
exception_filters.append( f[ 'filter' ] )
|
||||||
|
|
@ -302,20 +467,46 @@ class ProjectBreakpoints( object ):
|
||||||
# pay any attention to them anyway.
|
# pay any attention to them anyway.
|
||||||
self._exception_breakpoints[ 'exceptionOptions' ] = []
|
self._exception_breakpoints[ 'exceptionOptions' ] = []
|
||||||
|
|
||||||
|
|
||||||
|
def Refresh( self, file_name ):
|
||||||
|
# TODO: Just this file ?
|
||||||
|
self._ShowBreakpoints()
|
||||||
|
|
||||||
|
|
||||||
def _ShowBreakpoints( self ):
|
def _ShowBreakpoints( self ):
|
||||||
for file_name, line_breakpoints in self._line_breakpoints.items():
|
for file_name, line_breakpoints in self._line_breakpoints.items():
|
||||||
for bp in line_breakpoints:
|
for bp in line_breakpoints:
|
||||||
|
self._SignToLine( file_name, bp )
|
||||||
if 'sign_id' in bp:
|
if 'sign_id' in bp:
|
||||||
vim.command( 'sign unplace {0} group=VimspectorBP '.format(
|
signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' )
|
||||||
bp[ 'sign_id' ] ) )
|
|
||||||
else:
|
else:
|
||||||
bp[ 'sign_id' ] = self._next_sign_id
|
bp[ 'sign_id' ] = self._next_sign_id
|
||||||
self._next_sign_id += 1
|
self._next_sign_id += 1
|
||||||
|
|
||||||
vim.command(
|
sign = ( 'vimspectorBPDisabled' if bp[ 'state' ] != 'ENABLED'
|
||||||
'sign place {0} group=VimspectorBP line={1} name={2} file={3}'.format(
|
else 'vimspectorBPCond' if 'condition' in bp[ 'options' ]
|
||||||
bp[ 'sign_id' ] ,
|
else 'vimspectorBP' )
|
||||||
bp[ 'line' ],
|
|
||||||
'vimspectorBP' if bp[ 'state' ] == 'ENABLED'
|
if utils.BufferExists( file_name ):
|
||||||
else 'vimspectorBPDisabled',
|
signs.PlaceSign( bp[ 'sign_id' ],
|
||||||
file_name ) )
|
'VimspectorBP',
|
||||||
|
sign,
|
||||||
|
file_name,
|
||||||
|
bp[ 'line' ] )
|
||||||
|
|
||||||
|
|
||||||
|
def _SignToLine( self, file_name, bp ):
|
||||||
|
if 'sign_id' not in bp:
|
||||||
|
return bp[ 'line' ]
|
||||||
|
|
||||||
|
if not utils.BufferExists( file_name ):
|
||||||
|
return bp[ 'line' ]
|
||||||
|
|
||||||
|
signs = vim.eval( "sign_getplaced( '{}', {} )".format(
|
||||||
|
utils.Escape( file_name ),
|
||||||
|
json.dumps( { 'id': bp[ 'sign_id' ], 'group': 'VimspectorBP', } ) ) )
|
||||||
|
|
||||||
|
if len( signs ) == 1 and len( signs[ 0 ][ 'signs' ] ) == 1:
|
||||||
|
bp[ 'line' ] = int( signs[ 0 ][ 'signs' ][ 0 ][ 'lnum' ] )
|
||||||
|
|
||||||
|
return bp[ 'line' ]
|
||||||
|
|
|
||||||
|
|
@ -18,63 +18,133 @@ import logging
|
||||||
import json
|
import json
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from vimspector import utils
|
from vimspector import utils, terminal, signs
|
||||||
|
|
||||||
|
|
||||||
class CodeView( object ):
|
class CodeView( object ):
|
||||||
def __init__( self, window ):
|
def __init__( self, window, api_prefix ):
|
||||||
self._window = window
|
self._window = window
|
||||||
|
self._api_prefix = api_prefix
|
||||||
|
|
||||||
self._terminal_window = None
|
self._terminal = None
|
||||||
self._terminal_buffer_number = None
|
self.current_syntax = None
|
||||||
|
|
||||||
self._logger = logging.getLogger( __name__ )
|
self._logger = logging.getLogger( __name__ )
|
||||||
utils.SetUpLogging( self._logger )
|
utils.SetUpLogging( self._logger )
|
||||||
|
|
||||||
|
# FIXME: This ID is by group, so should be module scope
|
||||||
self._next_sign_id = 1
|
self._next_sign_id = 1
|
||||||
self._breakpoints = defaultdict( list )
|
self._breakpoints = defaultdict( list )
|
||||||
self._signs = {
|
self._signs = {
|
||||||
'vimspectorPC': None,
|
'vimspectorPC': None,
|
||||||
'breakpoints': []
|
'breakpoints': []
|
||||||
}
|
}
|
||||||
|
self._current_frame = None
|
||||||
|
|
||||||
with utils.LetCurrentWindow( self._window ):
|
with utils.LetCurrentWindow( self._window ):
|
||||||
vim.command( 'nnoremenu WinBar.Continue :call vimspector#Continue()<CR>' )
|
if utils.UseWinBar():
|
||||||
vim.command( 'nnoremenu WinBar.Next :call vimspector#StepOver()<CR>' )
|
# Buggy neovim doesn't render correctly when the WinBar is defined:
|
||||||
vim.command( 'nnoremenu WinBar.Step :call vimspector#StepInto()<CR>' )
|
# https://github.com/neovim/neovim/issues/12689
|
||||||
vim.command( 'nnoremenu WinBar.Finish :call vimspector#StepOut()<CR>' )
|
vim.command( 'nnoremenu WinBar.■\\ Stop '
|
||||||
vim.command( 'nnoremenu WinBar.Pause :call vimspector#Pause()<CR>' )
|
':call vimspector#Stop()<CR>' )
|
||||||
vim.command( 'nnoremenu WinBar.Stop :call vimspector#Stop()<CR>' )
|
vim.command( 'nnoremenu WinBar.▶\\ Cont '
|
||||||
vim.command( 'nnoremenu WinBar.Restart :call vimspector#Restart()<CR>' )
|
':call vimspector#Continue()<CR>' )
|
||||||
vim.command( 'nnoremenu WinBar.Reset :call vimspector#Reset()<CR>' )
|
vim.command( 'nnoremenu WinBar.▷\\ Pause '
|
||||||
|
':call vimspector#Pause()<CR>' )
|
||||||
|
vim.command( 'nnoremenu WinBar.↷\\ Next '
|
||||||
|
':call vimspector#StepOver()<CR>' )
|
||||||
|
vim.command( 'nnoremenu WinBar.→\\ Step '
|
||||||
|
':call vimspector#StepInto()<CR>' )
|
||||||
|
vim.command( 'nnoremenu WinBar.←\\ Out '
|
||||||
|
':call vimspector#StepOut()<CR>' )
|
||||||
|
vim.command( 'nnoremenu WinBar.⟲: '
|
||||||
|
':call vimspector#Restart()<CR>' )
|
||||||
|
vim.command( 'nnoremenu WinBar.✕ '
|
||||||
|
':call vimspector#Reset()<CR>' )
|
||||||
|
|
||||||
vim.command( 'sign define vimspectorPC text=-> texthl=Search' )
|
if not signs.SignDefined( 'vimspectorPC' ):
|
||||||
|
signs.DefineSign( 'vimspectorPC',
|
||||||
|
text = '▶',
|
||||||
|
double_text = '▶',
|
||||||
|
texthl = 'MatchParen',
|
||||||
|
linehl = 'CursorLine' )
|
||||||
|
if not signs.SignDefined( 'vimspectorPCBP' ):
|
||||||
|
signs.DefineSign( 'vimspectorPCBP',
|
||||||
|
text = '●▶',
|
||||||
|
double_text = '▷',
|
||||||
|
texthl = 'MatchParen',
|
||||||
|
linehl = 'CursorLine' )
|
||||||
|
|
||||||
|
|
||||||
|
def _UndisplayPC( self, clear_pc = True ):
|
||||||
|
if clear_pc:
|
||||||
|
self._current_frame = None
|
||||||
|
if self._signs[ 'vimspectorPC' ]:
|
||||||
|
signs.UnplaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode' )
|
||||||
|
self._signs[ 'vimspectorPC' ] = None
|
||||||
|
|
||||||
|
|
||||||
|
def _DisplayPC( self ):
|
||||||
|
frame = self._current_frame
|
||||||
|
if not frame:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._UndisplayPC( clear_pc = False )
|
||||||
|
|
||||||
|
# FIXME: Do we relly need to keep using up IDs ?
|
||||||
|
self._signs[ 'vimspectorPC' ] = self._next_sign_id
|
||||||
|
self._next_sign_id += 1
|
||||||
|
|
||||||
|
sign = 'vimspectorPC'
|
||||||
|
# If there's also a breakpoint on this line, use vimspectorPCBP
|
||||||
|
for bp in self._breakpoints.get( frame[ 'source' ][ 'path' ], [] ):
|
||||||
|
if 'line' not in bp:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if bp[ 'line' ] == frame[ 'line' ]:
|
||||||
|
sign = 'vimspectorPCBP'
|
||||||
|
break
|
||||||
|
|
||||||
|
if utils.BufferExists( frame[ 'source' ][ 'path' ] ):
|
||||||
|
signs.PlaceSign( self._signs[ 'vimspectorPC' ],
|
||||||
|
'VimspectorCode',
|
||||||
|
sign,
|
||||||
|
frame[ 'source' ][ 'path' ],
|
||||||
|
frame[ 'line' ] )
|
||||||
|
|
||||||
|
|
||||||
def SetCurrentFrame( self, frame ):
|
def SetCurrentFrame( self, frame ):
|
||||||
if self._signs[ 'vimspectorPC' ]:
|
"""Returns True if the code window was updated with the frame, False
|
||||||
vim.command( 'sign unplace {} group=VimspectorCode'.format(
|
otherwise. False means either the frame is junk, we couldn't find the file
|
||||||
self._signs[ 'vimspectorPC' ] ) )
|
(or don't have the data) or the code window no longer exits."""
|
||||||
self._signs[ 'vimspectorPC' ] = None
|
|
||||||
|
|
||||||
if not frame or not frame.get( 'source' ):
|
if not frame or not frame.get( 'source' ):
|
||||||
|
self._UndisplayPC()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if 'path' not in frame[ 'source' ]:
|
if 'path' not in frame[ 'source' ]:
|
||||||
|
self._UndisplayPC()
|
||||||
|
return False
|
||||||
|
|
||||||
|
self._current_frame = frame
|
||||||
|
|
||||||
|
if not self._window.valid:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
utils.JumpToWindow( self._window )
|
utils.JumpToWindow( self._window )
|
||||||
|
|
||||||
try:
|
try:
|
||||||
utils.OpenFileInCurrentWindow( frame[ 'source' ][ 'path' ] )
|
utils.OpenFileInCurrentWindow( frame[ 'source' ][ 'path' ] )
|
||||||
|
vim.command( 'doautocmd <nomodeline> User VimspectorJumpedToFrame' )
|
||||||
except vim.error:
|
except vim.error:
|
||||||
self._logger.exception( 'Unexpected vim error opening file {}'.format(
|
self._logger.exception( 'Unexpected vim error opening file {}'.format(
|
||||||
frame[ 'source' ][ 'path' ] ) )
|
frame[ 'source' ][ 'path' ] ) )
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# SIC: column is 0-based, line is 1-based in vim. Why? Nobody knows.
|
# SIC: column is 0-based, line is 1-based in vim. Why? Nobody knows.
|
||||||
|
# Note: max() with 0 because some debug adapters (go) return 0 for the
|
||||||
|
# column.
|
||||||
try:
|
try:
|
||||||
self._window.cursor = ( frame[ 'line' ], frame[ 'column' ] - 1 )
|
self._window.cursor = ( frame[ 'line' ], max( frame[ 'column' ] - 1, 0 ) )
|
||||||
except vim.error:
|
except vim.error:
|
||||||
self._logger.exception( "Unable to jump to %s:%s in %s, maybe the file "
|
self._logger.exception( "Unable to jump to %s:%s in %s, maybe the file "
|
||||||
"doesn't exist",
|
"doesn't exist",
|
||||||
|
|
@ -83,25 +153,21 @@ class CodeView( object ):
|
||||||
frame[ 'source' ][ 'path' ] )
|
frame[ 'source' ][ 'path' ] )
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._signs[ 'vimspectorPC' ] = self._next_sign_id
|
self.current_syntax = utils.ToUnicode(
|
||||||
self._next_sign_id += 1
|
vim.current.buffer.options[ 'syntax' ] )
|
||||||
|
|
||||||
vim.command( 'sign place {0} group=VimspectorCode priority=20 '
|
self.ShowBreakpoints()
|
||||||
'line={1} name=vimspectorPC '
|
|
||||||
'file={2}'.format(
|
|
||||||
self._signs[ 'vimspectorPC' ],
|
|
||||||
frame[ 'line' ],
|
|
||||||
frame[ 'source' ][ 'path' ] ) )
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def Clear( self ):
|
def Clear( self ):
|
||||||
if self._signs[ 'vimspectorPC' ]:
|
if self._signs[ 'vimspectorPC' ]:
|
||||||
vim.command( 'sign unplace {} group=VimspectorCode'.format(
|
signs.UnplaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode' )
|
||||||
self._signs[ 'vimspectorPC' ] ) )
|
|
||||||
self._signs[ 'vimspectorPC' ] = None
|
self._signs[ 'vimspectorPC' ] = None
|
||||||
|
|
||||||
|
self._UndisplayPC()
|
||||||
self._UndisplaySigns()
|
self._UndisplaySigns()
|
||||||
|
self.current_syntax = None
|
||||||
|
|
||||||
def Reset( self ):
|
def Reset( self ):
|
||||||
self.ClearBreakpoints()
|
self.ClearBreakpoints()
|
||||||
|
|
@ -109,25 +175,29 @@ class CodeView( object ):
|
||||||
|
|
||||||
def AddBreakpoints( self, source, breakpoints ):
|
def AddBreakpoints( self, source, breakpoints ):
|
||||||
for breakpoint in breakpoints:
|
for breakpoint in breakpoints:
|
||||||
if 'source' not in breakpoint:
|
source = breakpoint.get( 'source' ) or source
|
||||||
if source:
|
if not source or 'path' not in source:
|
||||||
breakpoint[ 'source' ] = source
|
self._logger.warn( 'missing source/path in breakpoint {0}'.format(
|
||||||
else:
|
json.dumps( breakpoint ) ) )
|
||||||
self._logger.warn( 'missing source in breakpoint {0}'.format(
|
continue
|
||||||
json.dumps( breakpoint ) ) )
|
|
||||||
continue
|
|
||||||
|
|
||||||
self._breakpoints[ breakpoint[ 'source' ][ 'path' ] ].append(
|
breakpoint[ 'source' ] = source
|
||||||
breakpoint )
|
self._breakpoints[ source[ 'path' ] ].append( breakpoint )
|
||||||
|
|
||||||
self._logger.debug( 'Breakpoints at this point: {0}'.format(
|
self._logger.debug( 'Breakpoints at this point: {0}'.format(
|
||||||
json.dumps( self._breakpoints, indent = 2 ) ) )
|
json.dumps( self._breakpoints, indent = 2 ) ) )
|
||||||
|
|
||||||
self.ShowBreakpoints()
|
self.ShowBreakpoints()
|
||||||
|
|
||||||
|
|
||||||
|
def AddBreakpoint( self, breakpoint ):
|
||||||
|
self.AddBreakpoints( None, [ breakpoint ] )
|
||||||
|
|
||||||
|
|
||||||
def UpdateBreakpoint( self, bp ):
|
def UpdateBreakpoint( self, bp ):
|
||||||
if 'id' not in bp:
|
if 'id' not in bp:
|
||||||
self.AddBreakpoints( None, [ bp ] )
|
self.AddBreakpoint( bp )
|
||||||
|
return
|
||||||
|
|
||||||
for _, breakpoint_list in self._breakpoints.items():
|
for _, breakpoint_list in self._breakpoints.items():
|
||||||
for index, breakpoint in enumerate( breakpoint_list ):
|
for index, breakpoint in enumerate( breakpoint_list ):
|
||||||
|
|
@ -137,11 +207,31 @@ class CodeView( object ):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Not found. Assume new
|
# Not found. Assume new
|
||||||
self.AddBreakpoints( None, [ bp ] )
|
self.AddBreakpoint( bp )
|
||||||
|
|
||||||
|
|
||||||
|
def RemoveBreakpoint( self, bp ):
|
||||||
|
for _, breakpoint_list in self._breakpoints.items():
|
||||||
|
found_index = None
|
||||||
|
for index, breakpoint in enumerate( breakpoint_list ):
|
||||||
|
if 'id' in breakpoint and breakpoint[ 'id' ] == bp[ 'id' ]:
|
||||||
|
found_index = index
|
||||||
|
break
|
||||||
|
|
||||||
|
if found_index is not None:
|
||||||
|
del breakpoint_list[ found_index ]
|
||||||
|
self.ShowBreakpoints()
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def Refresh( self, file_name ):
|
||||||
|
# TODO: jsut the file ?
|
||||||
|
self.ShowBreakpoints()
|
||||||
|
|
||||||
|
|
||||||
def _UndisplaySigns( self ):
|
def _UndisplaySigns( self ):
|
||||||
for sign_id in self._signs[ 'breakpoints' ]:
|
for sign_id in self._signs[ 'breakpoints' ]:
|
||||||
vim.command( 'sign unplace {} group=VimspectorCode'.format( sign_id ) )
|
signs.UnplaceSign( sign_id, 'VimspectorCode' )
|
||||||
|
|
||||||
self._signs[ 'breakpoints' ] = []
|
self._signs[ 'breakpoints' ] = []
|
||||||
|
|
||||||
|
|
@ -160,17 +250,17 @@ class CodeView( object ):
|
||||||
sign_id = self._next_sign_id
|
sign_id = self._next_sign_id
|
||||||
self._next_sign_id += 1
|
self._next_sign_id += 1
|
||||||
self._signs[ 'breakpoints' ].append( sign_id )
|
self._signs[ 'breakpoints' ].append( sign_id )
|
||||||
vim.command(
|
if utils.BufferExists( file_name ):
|
||||||
'sign place {0} group=VimspectorCode priority=9 '
|
signs.PlaceSign( sign_id,
|
||||||
'line={1} '
|
'VimspectorCode',
|
||||||
'name={2} '
|
'vimspectorBP' if breakpoint[ 'verified' ]
|
||||||
'file={3}'.format(
|
else 'vimspectorBPDisabled',
|
||||||
sign_id,
|
file_name,
|
||||||
breakpoint[ 'line' ],
|
breakpoint[ 'line' ] )
|
||||||
'vimspectorBP' if breakpoint[ 'verified' ]
|
|
||||||
else 'vimspectorBPDisabled',
|
|
||||||
file_name ) )
|
|
||||||
|
|
||||||
|
# We need to also check if there's a breakpoint on this PC line and chnge
|
||||||
|
# the PC
|
||||||
|
self._DisplayPC()
|
||||||
|
|
||||||
def BreakpointsAsQuickFix( self ):
|
def BreakpointsAsQuickFix( self ):
|
||||||
qf = []
|
qf = []
|
||||||
|
|
@ -189,50 +279,11 @@ class CodeView( object ):
|
||||||
|
|
||||||
|
|
||||||
def LaunchTerminal( self, params ):
|
def LaunchTerminal( self, params ):
|
||||||
# kind = params.get( 'kind', 'integrated' )
|
self._terminal = terminal.LaunchTerminal( self._api_prefix,
|
||||||
|
params,
|
||||||
|
window_for_start = self._window,
|
||||||
|
existing_term = self._terminal )
|
||||||
|
|
||||||
# FIXME: We don't support external terminals, and only open in the
|
# FIXME: Change this tor return the PID rather than having debug_session
|
||||||
# integrated one.
|
# work that out
|
||||||
|
return self._terminal.buffer_number
|
||||||
cwd = params[ 'cwd' ]
|
|
||||||
args = params[ 'args' ]
|
|
||||||
env = params.get( 'env', {} )
|
|
||||||
|
|
||||||
options = {
|
|
||||||
'vertical': 1,
|
|
||||||
'norestore': 1,
|
|
||||||
'cwd': cwd,
|
|
||||||
'env': env,
|
|
||||||
}
|
|
||||||
|
|
||||||
window_for_start = self._window
|
|
||||||
if self._terminal_window is not None:
|
|
||||||
assert self._terminal_buffer_number
|
|
||||||
if ( self._terminal_window.buffer.number == self._terminal_buffer_number
|
|
||||||
and 'finished' in vim.eval( 'term_getstatus( {} )'.format(
|
|
||||||
self._terminal_buffer_number ) ) ):
|
|
||||||
window_for_start = self._terminal_window
|
|
||||||
options[ 'curwin' ] = 1
|
|
||||||
|
|
||||||
buffer_number = None
|
|
||||||
terminal_window = None
|
|
||||||
with utils.TemporaryVimOptions( { 'splitright': True,
|
|
||||||
'equalalways': False } ):
|
|
||||||
with utils.LetCurrentWindow( window_for_start ):
|
|
||||||
# TODO/FIXME: Do something about closing this when we reset ?
|
|
||||||
vim_cmd = 'term_start( {}, {} )'.format( json.dumps( args ),
|
|
||||||
json.dumps( options ) )
|
|
||||||
|
|
||||||
self._logger.debug( 'Start terminal: {}'.format( vim_cmd ) )
|
|
||||||
|
|
||||||
buffer_number = int( vim.eval( vim_cmd ) )
|
|
||||||
terminal_window = vim.current.window
|
|
||||||
|
|
||||||
if buffer_number is None or buffer_number <= 0:
|
|
||||||
# TODO: Do something better like reject the request?
|
|
||||||
raise ValueError( "Unable to start terminal" )
|
|
||||||
else:
|
|
||||||
self._terminal_window = terminal_window
|
|
||||||
self._terminal_buffer_number = buffer_number
|
|
||||||
|
|
||||||
return buffer_number
|
|
||||||
|
|
|
||||||
51
python3/vimspector/custom/java.py
Normal file
51
python3/vimspector/custom/java.py
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
# vimspector - A multi-language debugging system for Vim
|
||||||
|
# Copyright 2021 Ben Jackson
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from vimspector.debug_session import DebugSession
|
||||||
|
from vimspector import utils, settings
|
||||||
|
|
||||||
|
|
||||||
|
class JavaDebugAdapter( object ):
|
||||||
|
def __init__( self, debug_session: DebugSession ):
|
||||||
|
self.debug_session = debug_session
|
||||||
|
|
||||||
|
def OnEvent_hotcodereplace( self, message ):
|
||||||
|
# Hack for java debug server hot-code-replace
|
||||||
|
body = message.get( 'body' ) or {}
|
||||||
|
|
||||||
|
if body.get( 'type' ) != 'hotcodereplace':
|
||||||
|
return
|
||||||
|
|
||||||
|
if body.get( 'changeType' ) == 'BUILD_COMPLETE':
|
||||||
|
def handler( result ):
|
||||||
|
if result == 1:
|
||||||
|
self.debug_session._connection.DoRequest( None, {
|
||||||
|
'command': 'redefineClasses',
|
||||||
|
'arguments': {},
|
||||||
|
} )
|
||||||
|
|
||||||
|
mode = settings.Get( 'java_hotcodereplace_mode' )
|
||||||
|
if mode == 'ask':
|
||||||
|
utils.Confirm( self.debug_session._api_prefix,
|
||||||
|
'Code has changed, hot reload?',
|
||||||
|
handler,
|
||||||
|
default_value = 1 )
|
||||||
|
elif mode == 'always':
|
||||||
|
self.debug_session._connection.DoRequest( None, {
|
||||||
|
'command': 'redefineClasses',
|
||||||
|
'arguments': {},
|
||||||
|
} )
|
||||||
|
elif body.get( 'message' ):
|
||||||
|
utils.UserMessage( 'Hot code replace: ' + body[ 'message' ] )
|
||||||
|
|
@ -29,14 +29,14 @@ class PendingRequest( object ):
|
||||||
|
|
||||||
|
|
||||||
class DebugAdapterConnection( object ):
|
class DebugAdapterConnection( object ):
|
||||||
def __init__( self, handler, send_func ):
|
def __init__( self, handlers, send_func ):
|
||||||
self._logger = logging.getLogger( __name__ )
|
self._logger = logging.getLogger( __name__ )
|
||||||
utils.SetUpLogging( self._logger )
|
utils.SetUpLogging( self._logger )
|
||||||
|
|
||||||
self._Write = send_func
|
self._Write = send_func
|
||||||
self._SetState( 'READ_HEADER' )
|
self._SetState( 'READ_HEADER' )
|
||||||
self._buffer = bytes()
|
self._buffer = bytes()
|
||||||
self._handler = handler
|
self._handlers = handlers
|
||||||
self._next_message_id = 0
|
self._next_message_id = 0
|
||||||
self._outstanding_requests = {}
|
self._outstanding_requests = {}
|
||||||
|
|
||||||
|
|
@ -65,6 +65,33 @@ class DebugAdapterConnection( object ):
|
||||||
if not self._SendMessage( msg ):
|
if not self._SendMessage( msg ):
|
||||||
self._AbortRequest( request, 'Unable to send message' )
|
self._AbortRequest( request, 'Unable to send message' )
|
||||||
|
|
||||||
|
|
||||||
|
def DoRequestSync( self, msg, timeout = 5000 ):
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
def handler( msg ):
|
||||||
|
result[ 'response' ] = msg
|
||||||
|
|
||||||
|
def failure_handler( reason, msg ):
|
||||||
|
result[ 'response' ] = msg
|
||||||
|
result[ 'exception' ] = RuntimeError( reason )
|
||||||
|
|
||||||
|
self.DoRequest( handler, msg, failure_handler, timeout )
|
||||||
|
|
||||||
|
bug_catcher = 1000
|
||||||
|
while not result and bug_catcher >= 0:
|
||||||
|
vim.command( 'sleep 10m' )
|
||||||
|
bug_catcher -= 10
|
||||||
|
|
||||||
|
if result.get( 'exception' ) is not None:
|
||||||
|
raise result[ 'exception' ]
|
||||||
|
|
||||||
|
if result.get( 'response' ) is None:
|
||||||
|
raise RuntimeError( "No response" )
|
||||||
|
|
||||||
|
return result[ 'response' ]
|
||||||
|
|
||||||
|
|
||||||
def OnRequestTimeout( self, timer_id ):
|
def OnRequestTimeout( self, timer_id ):
|
||||||
request_id = None
|
request_id = None
|
||||||
for seq, request in self._outstanding_requests.items():
|
for seq, request in self._outstanding_requests.items():
|
||||||
|
|
@ -97,7 +124,7 @@ class DebugAdapterConnection( object ):
|
||||||
|
|
||||||
def Reset( self ):
|
def Reset( self ):
|
||||||
self._Write = None
|
self._Write = None
|
||||||
self._handler = None
|
self._handlers = None
|
||||||
|
|
||||||
while self._outstanding_requests:
|
while self._outstanding_requests:
|
||||||
_, request = self._outstanding_requests.popitem()
|
_, request = self._outstanding_requests.popitem()
|
||||||
|
|
@ -142,6 +169,10 @@ class DebugAdapterConnection( object ):
|
||||||
self._headers = {}
|
self._headers = {}
|
||||||
|
|
||||||
def _SendMessage( self, msg ):
|
def _SendMessage( self, msg ):
|
||||||
|
if not self._Write:
|
||||||
|
# Connection was destroyed
|
||||||
|
return False
|
||||||
|
|
||||||
msg = json.dumps( msg )
|
msg = json.dumps( msg )
|
||||||
self._logger.debug( 'Sending Message: {0}'.format( msg ) )
|
self._logger.debug( 'Sending Message: {0}'.format( msg ) )
|
||||||
|
|
||||||
|
|
@ -195,7 +226,12 @@ class DebugAdapterConnection( object ):
|
||||||
|
|
||||||
# self._logger.debug( 'Message received (raw): %s', payload )
|
# self._logger.debug( 'Message received (raw): %s', payload )
|
||||||
|
|
||||||
message = json.loads( payload )
|
try:
|
||||||
|
message = json.loads( payload, strict = False )
|
||||||
|
except Exception:
|
||||||
|
self._logger.exception( "Invalid message received: %s", payload )
|
||||||
|
self._SetState( 'READ_HEADER' )
|
||||||
|
raise
|
||||||
|
|
||||||
self._logger.debug( 'Message received: {0}'.format( message ) )
|
self._logger.debug( 'Message received: {0}'.format( message ) )
|
||||||
|
|
||||||
|
|
@ -206,7 +242,7 @@ class DebugAdapterConnection( object ):
|
||||||
|
|
||||||
|
|
||||||
def _OnMessageReceived( self, message ):
|
def _OnMessageReceived( self, message ):
|
||||||
if not self._handler:
|
if not self._handlers:
|
||||||
return
|
return
|
||||||
|
|
||||||
if message[ 'type' ] == 'response':
|
if message[ 'type' ] == 'response':
|
||||||
|
|
@ -239,25 +275,21 @@ class DebugAdapterConnection( object ):
|
||||||
self._logger.error( 'Request failed: {0}'.format( reason ) )
|
self._logger.error( 'Request failed: {0}'.format( reason ) )
|
||||||
if request.failure_handler:
|
if request.failure_handler:
|
||||||
request.failure_handler( reason, message )
|
request.failure_handler( reason, message )
|
||||||
elif 'OnFailure' in dir( self._handler ):
|
|
||||||
self._handler.OnFailure( reason, message )
|
|
||||||
else:
|
else:
|
||||||
utils.UserMessage( 'Request failed: {0}'.format( reason ) )
|
for h in self._handlers:
|
||||||
|
if 'OnFailure' in dir( h ):
|
||||||
|
h.OnFailure( reason, request.msg, message )
|
||||||
|
|
||||||
elif message[ 'type' ] == 'event':
|
elif message[ 'type' ] == 'event':
|
||||||
method = 'OnEvent_' + message[ 'event' ]
|
method = 'OnEvent_' + message[ 'event' ]
|
||||||
if method in dir( self._handler ):
|
for h in self._handlers:
|
||||||
getattr( self._handler, method )( message )
|
if method in dir( h ):
|
||||||
else:
|
getattr( h, method )( message )
|
||||||
utils.UserMessage( 'Unhandled event: {0}'.format( message[ 'event' ] ),
|
|
||||||
persist = True )
|
|
||||||
elif message[ 'type' ] == 'request':
|
elif message[ 'type' ] == 'request':
|
||||||
method = 'OnRequest_' + message[ 'command' ]
|
method = 'OnRequest_' + message[ 'command' ]
|
||||||
if method in dir( self._handler ):
|
for h in self._handlers:
|
||||||
getattr( self._handler, method )( message )
|
if method in dir( h ):
|
||||||
else:
|
getattr( h, method )( message )
|
||||||
utils.UserMessage(
|
|
||||||
'Unhandled request: {0}'.format( message[ 'command' ] ),
|
|
||||||
persist = True )
|
|
||||||
|
|
||||||
|
|
||||||
def _KillTimer( request ):
|
def _KillTimer( request ):
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
41
python3/vimspector/developer.py
Normal file
41
python3/vimspector/developer.py
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
# vimspector - A multi-language debugging system for Vim
|
||||||
|
# Copyright 2020 Ben Jackson
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
from vimspector import install, utils, installer
|
||||||
|
|
||||||
|
|
||||||
|
def SetUpDebugpy( wait=False, port=5678 ):
|
||||||
|
sys.path.insert(
|
||||||
|
1,
|
||||||
|
os.path.join( install.GetGadgetDir( utils.GetVimspectorBase() ),
|
||||||
|
'debugpy',
|
||||||
|
'build',
|
||||||
|
'lib' ) )
|
||||||
|
import debugpy
|
||||||
|
|
||||||
|
exe = sys.executable
|
||||||
|
try:
|
||||||
|
# debugpy uses sys.executable (which is `vim`, so we hack it)
|
||||||
|
sys.executable = installer.PathToAnyWorkingPython3()
|
||||||
|
debugpy.listen( port )
|
||||||
|
finally:
|
||||||
|
sys.executable = exe
|
||||||
|
|
||||||
|
if wait:
|
||||||
|
debugpy.wait_for_client()
|
||||||
475
python3/vimspector/gadgets.py
Normal file
475
python3/vimspector/gadgets.py
Normal file
|
|
@ -0,0 +1,475 @@
|
||||||
|
# vimspector - A multi-language debugging system for Vim
|
||||||
|
# Copyright 2020 Ben Jackson
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
from vimspector import installer
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
GADGETS = {
|
||||||
|
'vscode-cpptools': {
|
||||||
|
'language': [ 'c', 'cpp', 'rust' ],
|
||||||
|
'download': {
|
||||||
|
'url': 'https://github.com/Microsoft/vscode-cpptools/releases/download/'
|
||||||
|
'${version}/${file_name}',
|
||||||
|
},
|
||||||
|
'do': lambda name, root, gadget: installer.InstallCppTools( name,
|
||||||
|
root,
|
||||||
|
gadget ),
|
||||||
|
'all': {
|
||||||
|
'version': '1.6.0',
|
||||||
|
"adapters": {
|
||||||
|
"vscode-cpptools": {
|
||||||
|
"name": "cppdbg",
|
||||||
|
"command": [
|
||||||
|
"${gadgetDir}/vscode-cpptools/debugAdapters/bin/OpenDebugAD7"
|
||||||
|
],
|
||||||
|
"attach": {
|
||||||
|
"pidProperty": "processId",
|
||||||
|
"pidSelect": "ask"
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"type": "cppdbg",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"environment": [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'linux': {
|
||||||
|
'file_name': 'cpptools-linux.vsix',
|
||||||
|
'checksum':
|
||||||
|
'c25299bcfb46b22d41aa3f125df7184e6282a35ff9fb69c47def744cb4778f55',
|
||||||
|
},
|
||||||
|
'macos': {
|
||||||
|
'file_name': 'cpptools-osx-arm64.vsix',
|
||||||
|
'checksum':
|
||||||
|
'ceb3e8cdaa2b5bb45af50913ddd8402089969748af8d70f5d46480408287ba6f',
|
||||||
|
},
|
||||||
|
'windows': {
|
||||||
|
'file_name': 'cpptools-win32.vsix',
|
||||||
|
'checksum':
|
||||||
|
'ef7ac5831874a3c7dbf0feb826bfda2be579aff9b6d990622fff1d0d4ede00d1',
|
||||||
|
"adapters": {
|
||||||
|
"vscode-cpptools": {
|
||||||
|
"name": "cppdbg",
|
||||||
|
"command": [
|
||||||
|
"${gadgetDir}/vscode-cpptools/debugAdapters/bin/OpenDebugAD7.exe"
|
||||||
|
],
|
||||||
|
"attach": {
|
||||||
|
"pidProperty": "processId",
|
||||||
|
"pidSelect": "ask"
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"type": "cppdbg",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"environment": [],
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"MIDebuggerPath": "gdb.exe"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'debugpy': {
|
||||||
|
'language': 'python',
|
||||||
|
'download': {
|
||||||
|
'url': 'https://github.com/microsoft/debugpy/archive/${file_name}'
|
||||||
|
},
|
||||||
|
'all': {
|
||||||
|
'version': '1.2.1',
|
||||||
|
'file_name': 'v1.2.1.zip',
|
||||||
|
'checksum':
|
||||||
|
'29a6c5d1053d2b6f3b1a63e1a8ecff93f951d3cc0b7548431592e9e3007239e6'
|
||||||
|
},
|
||||||
|
'do': lambda name, root, gadget: installer.InstallDebugpy( name,
|
||||||
|
root,
|
||||||
|
gadget ),
|
||||||
|
'adapters': {
|
||||||
|
'debugpy': {
|
||||||
|
"command": [
|
||||||
|
sys.executable, # TODO: Will this work from within Vim ?
|
||||||
|
"${gadgetDir}/debugpy/build/lib/debugpy/adapter"
|
||||||
|
],
|
||||||
|
"name": "debugpy",
|
||||||
|
"configuration": {
|
||||||
|
"python": sys.executable, # TODO: Will this work from within Vim ?
|
||||||
|
# Don't debug into subprocesses, as this leads to problems (vimspector
|
||||||
|
# doesn't support the custom messages)
|
||||||
|
# https://github.com/puremourning/vimspector/issues/141
|
||||||
|
"subProcess": False,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'vscode-java-debug': {
|
||||||
|
'language': 'java',
|
||||||
|
'enabled': False,
|
||||||
|
'download': {
|
||||||
|
'url': 'https://github.com/microsoft/vscode-java-debug/releases/download/'
|
||||||
|
'${version}/${file_name}',
|
||||||
|
},
|
||||||
|
'all': {
|
||||||
|
'version': '0.26.0',
|
||||||
|
'file_name': 'vscjava.vscode-java-debug-0.26.0.vsix',
|
||||||
|
'checksum':
|
||||||
|
'de49116ff3a3c941dad0c36d9af59baa62cd931e808a2ab392056cbb235ad5ef',
|
||||||
|
},
|
||||||
|
'adapters': {
|
||||||
|
"vscode-java": {
|
||||||
|
"name": "vscode-java",
|
||||||
|
"port": "${DAPPort}",
|
||||||
|
"configuration": {
|
||||||
|
"cwd": "${workspaceRoot}"
|
||||||
|
},
|
||||||
|
'custom_handler': 'vimspector.custom.java.JavaDebugAdapter'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'java-language-server': {
|
||||||
|
'language': 'javac',
|
||||||
|
'enabled': False,
|
||||||
|
'download': {
|
||||||
|
'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/'
|
||||||
|
'publishers/georgewfraser/vsextensions/vscode-javac/${version}/'
|
||||||
|
'vspackage',
|
||||||
|
'target': 'georgewfraser.vscode-javac-0.2.31.vsix.gz',
|
||||||
|
'format': 'zip.gz',
|
||||||
|
},
|
||||||
|
'all': {
|
||||||
|
'version': '0.2.31',
|
||||||
|
'file_name': 'georgewfraser.vscode-javac-0.2.31.vsix.gz',
|
||||||
|
'checksum':
|
||||||
|
'5b0248ec1198d3ece9a9c6b9433b30c22e308f0ae6e4c7bd09cd943c454e3e1d',
|
||||||
|
},
|
||||||
|
'adapters': {
|
||||||
|
"vscode-javac": {
|
||||||
|
"name": "vscode-javac",
|
||||||
|
"type": "vscode-javac",
|
||||||
|
"command": [
|
||||||
|
"${gadgetDir}/java-language-server/dist/debug_adapter_mac.sh"
|
||||||
|
],
|
||||||
|
"attach": {
|
||||||
|
"pidSelect": "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'tclpro': {
|
||||||
|
'language': 'tcl',
|
||||||
|
'repo': {
|
||||||
|
'url': 'https://github.com/puremourning/TclProDebug',
|
||||||
|
'ref': 'v1.0.0'
|
||||||
|
},
|
||||||
|
'do': lambda name, root, gadget: installer.InstallTclProDebug( name,
|
||||||
|
root,
|
||||||
|
gadget ),
|
||||||
|
'adapters': {
|
||||||
|
"tclpro": {
|
||||||
|
"name": "tclpro",
|
||||||
|
"type": "tclpro",
|
||||||
|
"command": [
|
||||||
|
"${gadgetDir}/tclpro/bin/debugadapter"
|
||||||
|
],
|
||||||
|
"attach": {
|
||||||
|
"pidSelect": "none"
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"target": "${file}",
|
||||||
|
"args": [ "*${args}" ],
|
||||||
|
"tclsh": "tclsh",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"extensionDirs": [
|
||||||
|
"${workspaceRoot}/.tclpro/extensions",
|
||||||
|
"${HOME}/.tclpro/extensions",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'netcoredbg': {
|
||||||
|
'language': [ 'csharp', 'fsharp', 'vbnet' ],
|
||||||
|
'enabled': False,
|
||||||
|
'download': {
|
||||||
|
'url': ( 'https://github.com/Samsung/netcoredbg/releases/download/'
|
||||||
|
'${version}/${file_name}' ),
|
||||||
|
'format': 'tar',
|
||||||
|
},
|
||||||
|
'all': {
|
||||||
|
'version': '1.2.0-782'
|
||||||
|
},
|
||||||
|
'macos': {
|
||||||
|
'file_name': 'netcoredbg-osx.tar.gz',
|
||||||
|
'checksum':
|
||||||
|
'',
|
||||||
|
},
|
||||||
|
'linux': {
|
||||||
|
'file_name': 'netcoredbg-linux-bionic-amd64.tar.gz',
|
||||||
|
'checksum': '',
|
||||||
|
},
|
||||||
|
'windows': {
|
||||||
|
'file_name': 'netcoredbg-win64.zip',
|
||||||
|
'checksum': '',
|
||||||
|
},
|
||||||
|
'do': lambda name, root, gadget: installer.MakeSymlink(
|
||||||
|
name,
|
||||||
|
os.path.join( root, 'netcoredbg' ) ),
|
||||||
|
'adapters': {
|
||||||
|
'netcoredbg': {
|
||||||
|
"name": "netcoredbg",
|
||||||
|
"command": [
|
||||||
|
"${gadgetDir}/netcoredbg/netcoredbg",
|
||||||
|
"--interpreter=vscode"
|
||||||
|
],
|
||||||
|
"attach": {
|
||||||
|
"pidProperty": "processId",
|
||||||
|
"pidSelect": "ask"
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"cwd": "${workspaceRoot}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'vscode-bash-debug': {
|
||||||
|
'language': 'bash',
|
||||||
|
'download': {
|
||||||
|
'url': 'https://github.com/rogalmic/vscode-bash-debug/releases/'
|
||||||
|
'download/${version}/${file_name}',
|
||||||
|
},
|
||||||
|
'all': {
|
||||||
|
'file_name': 'bash-debug-0.3.7.vsix',
|
||||||
|
'version': 'v0.3.7',
|
||||||
|
'checksum':
|
||||||
|
'7b73e5b4604375df8658fb5a72c645c355785a289aa785a986e508342c014bb4',
|
||||||
|
},
|
||||||
|
'do': lambda name, root, gadget: installer.InstallBashDebug( name,
|
||||||
|
root,
|
||||||
|
gadget ),
|
||||||
|
'adapters': {
|
||||||
|
"vscode-bash": {
|
||||||
|
"name": "bashdb",
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"${gadgetDir}/vscode-bash-debug/out/bashDebug.js"
|
||||||
|
],
|
||||||
|
"variables": {
|
||||||
|
"BASHDB_HOME": "${gadgetDir}/vscode-bash-debug/bashdb_dir"
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"request": "launch",
|
||||||
|
"type": "bashdb",
|
||||||
|
"program": "${file}",
|
||||||
|
"args": [],
|
||||||
|
"env": {},
|
||||||
|
"pathBash": "bash",
|
||||||
|
"pathBashdb": "${BASHDB_HOME}/bashdb",
|
||||||
|
"pathBashdbLib": "${BASHDB_HOME}",
|
||||||
|
"pathCat": "cat",
|
||||||
|
"pathMkfifo": "mkfifo",
|
||||||
|
"pathPkill": "pkill",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"terminalKind": "integrated",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'vscode-go': {
|
||||||
|
'language': 'go',
|
||||||
|
'download': {
|
||||||
|
'url': 'https://github.com/golang/vscode-go/releases/download/'
|
||||||
|
'v${version}/${file_name}'
|
||||||
|
},
|
||||||
|
'all': {
|
||||||
|
'version': '0.19.1',
|
||||||
|
'file_name': 'go-0.19.1.vsix',
|
||||||
|
'checksum':
|
||||||
|
'7f9dc014245b030d9f562b28f3ea9b1fd6e2708fac996c53ff6a707f8204ec64',
|
||||||
|
},
|
||||||
|
'adapters': {
|
||||||
|
'vscode-go': {
|
||||||
|
'name': 'delve',
|
||||||
|
'command': [
|
||||||
|
'node',
|
||||||
|
'${gadgetDir}/vscode-go/dist/debugAdapter.js'
|
||||||
|
],
|
||||||
|
"configuration": {
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'vscode-php-debug': {
|
||||||
|
'language': 'php',
|
||||||
|
'enabled': False,
|
||||||
|
'download': {
|
||||||
|
'url':
|
||||||
|
'https://github.com/xdebug/vscode-php-debug/releases/download/'
|
||||||
|
'${version}/${file_name}',
|
||||||
|
},
|
||||||
|
'all': {
|
||||||
|
'version': 'v1.17.0',
|
||||||
|
'file_name': 'php-debug-1.17.0.vsix',
|
||||||
|
'checksum':
|
||||||
|
'd0fff272503414b6696cc737bc2e18e060fdd5e5dc4bcaf38ae7373afd8d8bc9',
|
||||||
|
},
|
||||||
|
'adapters': {
|
||||||
|
'vscode-php-debug': {
|
||||||
|
'name': "php-debug",
|
||||||
|
'command': [
|
||||||
|
'node',
|
||||||
|
"${gadgetDir}/vscode-php-debug/out/phpDebug.js",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'vscode-node-debug2': {
|
||||||
|
'language': 'node',
|
||||||
|
'enabled': False,
|
||||||
|
'repo': {
|
||||||
|
'url': 'https://github.com/microsoft/vscode-node-debug2',
|
||||||
|
'ref': 'v1.42.5'
|
||||||
|
},
|
||||||
|
'do': lambda name, root, gadget: installer.InstallNodeDebug( name,
|
||||||
|
root,
|
||||||
|
gadget ),
|
||||||
|
'adapters': {
|
||||||
|
'vscode-node': {
|
||||||
|
'name': 'node2',
|
||||||
|
'type': 'node2',
|
||||||
|
'command': [
|
||||||
|
'node',
|
||||||
|
'${gadgetDir}/vscode-node-debug2/out/src/nodeDebug.js'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'debugger-for-chrome': {
|
||||||
|
'language': 'chrome',
|
||||||
|
'enabled': False,
|
||||||
|
'download': {
|
||||||
|
'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/'
|
||||||
|
'publishers/msjsdiag/vsextensions/'
|
||||||
|
'debugger-for-chrome/${version}/vspackage',
|
||||||
|
'target': 'msjsdiag.debugger-for-chrome-4.12.10.vsix.gz',
|
||||||
|
'format': 'zip.gz',
|
||||||
|
},
|
||||||
|
'all': {
|
||||||
|
'version': '4.12.10',
|
||||||
|
'file_name': 'msjsdiag.debugger-for-chrome-4.12.10.vsix',
|
||||||
|
'checksum':
|
||||||
|
''
|
||||||
|
},
|
||||||
|
'adapters': {
|
||||||
|
'chrome': {
|
||||||
|
'name': 'debugger-for-chrome',
|
||||||
|
'type': 'chrome',
|
||||||
|
'command': [
|
||||||
|
'node',
|
||||||
|
'${gadgetDir}/debugger-for-chrome/out/src/chromeDebug.js'
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'CodeLLDB': {
|
||||||
|
'language': 'rust',
|
||||||
|
'enabled': True,
|
||||||
|
'download': {
|
||||||
|
'url': 'https://github.com/vadimcn/vscode-lldb/releases/download/'
|
||||||
|
'${version}/${file_name}',
|
||||||
|
},
|
||||||
|
'all': {
|
||||||
|
'version': 'v1.6.6',
|
||||||
|
},
|
||||||
|
'macos': {
|
||||||
|
'file_name': 'codelldb-aarch64-darwin.vsix',
|
||||||
|
'checksum':
|
||||||
|
'5adc3b9139eabdafd825bd5efc55df4424a203fb2b6087b425cd434956e7ec58',
|
||||||
|
'make_executable': [
|
||||||
|
'adapter/codelldb',
|
||||||
|
'lldb/bin/debugserver',
|
||||||
|
'lldb/bin/lldb',
|
||||||
|
'lldb/bin/lldb-argdumper',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'linux': {
|
||||||
|
'file_name': 'codelldb-x86_64-linux.vsix',
|
||||||
|
'checksum':
|
||||||
|
'eda2cd9b3089dcc0524c273e91ffb5875fe08c930bf643739a2cd1846e1f98d6',
|
||||||
|
'make_executable': [
|
||||||
|
'adapter/codelldb',
|
||||||
|
'lldb/bin/lldb',
|
||||||
|
'lldb/bin/lldb-server',
|
||||||
|
'lldb/bin/lldb-argdumper',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'windows': {
|
||||||
|
'file_name': 'codelldb-x86_64-windows.vsix',
|
||||||
|
'checksum':
|
||||||
|
'8ddebe8381a3d22dc3d95139c3797fda06b5cc34aadf300e13b1c516b9da95fe',
|
||||||
|
'make_executable': []
|
||||||
|
},
|
||||||
|
'adapters': {
|
||||||
|
'CodeLLDB': {
|
||||||
|
'name': 'CodeLLDB',
|
||||||
|
'type': 'CodeLLDB',
|
||||||
|
"command": [
|
||||||
|
"${gadgetDir}/CodeLLDB/adapter/codelldb",
|
||||||
|
"--port", "${unusedLocalPort}"
|
||||||
|
],
|
||||||
|
"port": "${unusedLocalPort}",
|
||||||
|
"configuration": {
|
||||||
|
"type": "lldb",
|
||||||
|
"name": "lldb",
|
||||||
|
"cargo": {},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"env": {},
|
||||||
|
"terminal": "integrated",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'local-lua-debugger-vscode': {
|
||||||
|
'language': 'lua',
|
||||||
|
'enabled': True,
|
||||||
|
'repo': {
|
||||||
|
'url': 'https://github.com/tomblind/local-lua-debugger-vscode.git',
|
||||||
|
'ref': 'release-${version}'
|
||||||
|
},
|
||||||
|
'all': {
|
||||||
|
'version': '0.2.0',
|
||||||
|
},
|
||||||
|
'do': lambda name, root, gadget: installer.InstallLuaLocal( name,
|
||||||
|
root,
|
||||||
|
gadget ),
|
||||||
|
'adapters': {
|
||||||
|
'lua-local': {
|
||||||
|
'command': [
|
||||||
|
'node',
|
||||||
|
'${gadgetDir}/local-lua-debugger-vscode/extension/debugAdapter.js'
|
||||||
|
],
|
||||||
|
'name': 'lua-local',
|
||||||
|
'configuration': {
|
||||||
|
'interpreter': 'lua',
|
||||||
|
'extensionPath': '${gadgetDir}/local-lua-debugger-vscode'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -26,10 +26,40 @@ def GetOS():
|
||||||
return 'linux'
|
return 'linux'
|
||||||
|
|
||||||
|
|
||||||
def GetGadgetDir( vimspector_base, OS ):
|
def mkdirs( p ):
|
||||||
return os.path.join( os.path.abspath( vimspector_base ), 'gadgets', OS )
|
try:
|
||||||
|
os.makedirs( p )
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def MakeInstallDirs( vimspector_base ):
|
||||||
|
mkdirs( GetGadgetConfigDir( vimspector_base ) )
|
||||||
|
mkdirs( GetConfigDirForFiletype( vimspector_base, '_all' ) )
|
||||||
|
|
||||||
|
|
||||||
|
def GetGadgetDir( vimspector_base ):
|
||||||
|
return os.path.join( os.path.abspath( vimspector_base ), 'gadgets', GetOS() )
|
||||||
|
|
||||||
|
|
||||||
|
def GetManifestFile( vimspector_base ):
|
||||||
|
return os.path.join( GetGadgetDir( vimspector_base ),
|
||||||
|
'.gadgets.manifest.json' )
|
||||||
|
|
||||||
|
|
||||||
def GetGadgetConfigFile( vimspector_base ):
|
def GetGadgetConfigFile( vimspector_base ):
|
||||||
return os.path.join( GetGadgetDir( vimspector_base, GetOS() ),
|
return os.path.join( GetGadgetDir( vimspector_base ), '.gadgets.json' )
|
||||||
'.gadgets.json' )
|
|
||||||
|
|
||||||
|
def GetGadgetConfigDir( vimspector_base ):
|
||||||
|
return os.path.join( GetGadgetDir( vimspector_base ), '.gadgets.d' )
|
||||||
|
|
||||||
|
|
||||||
|
def GetConfigDirForFiletype( vimspector_base, filetype ):
|
||||||
|
if not filetype:
|
||||||
|
filetype = 'default'
|
||||||
|
|
||||||
|
return os.path.join( os.path.abspath( vimspector_base ),
|
||||||
|
'configurations',
|
||||||
|
GetOS(),
|
||||||
|
filetype )
|
||||||
|
|
|
||||||
754
python3/vimspector/installer.py
Normal file
754
python3/vimspector/installer.py
Normal file
|
|
@ -0,0 +1,754 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# vimspector - A multi-language debugging system for Vim
|
||||||
|
# Copyright 2019 Ben Jackson
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from urllib import request
|
||||||
|
import contextlib
|
||||||
|
import functools
|
||||||
|
import gzip
|
||||||
|
import hashlib
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import ssl
|
||||||
|
import string
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tarfile
|
||||||
|
import time
|
||||||
|
import traceback
|
||||||
|
import zipfile
|
||||||
|
import json
|
||||||
|
|
||||||
|
from vimspector import install, gadgets
|
||||||
|
|
||||||
|
OUTPUT_VIEW = None
|
||||||
|
|
||||||
|
|
||||||
|
class Options:
|
||||||
|
vimspector_base = None
|
||||||
|
no_check_certificate = False
|
||||||
|
quiet = False
|
||||||
|
|
||||||
|
|
||||||
|
options = Options()
|
||||||
|
|
||||||
|
|
||||||
|
def Configure( **kwargs ):
|
||||||
|
for k, v in kwargs.items():
|
||||||
|
setattr( options, k, v )
|
||||||
|
|
||||||
|
|
||||||
|
def Print( *args, **kwargs ):
|
||||||
|
if not options.quiet:
|
||||||
|
print( *args, **kwargs )
|
||||||
|
|
||||||
|
|
||||||
|
class MissingExecutable( Exception ):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def GetPATHAsList():
|
||||||
|
paths = os.environ[ 'PATH' ].split( os.pathsep )
|
||||||
|
if install.GetOS() == 'windows':
|
||||||
|
paths.insert( 0, os.getcwd() )
|
||||||
|
return paths
|
||||||
|
|
||||||
|
|
||||||
|
def FindExecutable( executable: str, paths=None ):
|
||||||
|
if not paths:
|
||||||
|
paths = GetPATHAsList()
|
||||||
|
|
||||||
|
if install.GetOS() == 'windows':
|
||||||
|
extensions = [ '.exe', '.bat', '.cmd' ]
|
||||||
|
else:
|
||||||
|
extensions = [ '' ]
|
||||||
|
|
||||||
|
for extension in extensions:
|
||||||
|
if executable.endswith( extension ):
|
||||||
|
candidate = executable
|
||||||
|
else:
|
||||||
|
candidate = executable + extension
|
||||||
|
|
||||||
|
for path in paths:
|
||||||
|
filename = os.path.abspath( os.path.join( path, candidate ) )
|
||||||
|
if not os.path.isfile( filename ):
|
||||||
|
continue
|
||||||
|
if not os.access( filename, os.F_OK | os.X_OK ):
|
||||||
|
continue
|
||||||
|
|
||||||
|
return filename
|
||||||
|
|
||||||
|
raise MissingExecutable( f"Unable to find executable { executable } in path" )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def CheckCall( cmd, *args, **kwargs ):
|
||||||
|
cmd[ 0 ] = FindExecutable( cmd[ 0 ] )
|
||||||
|
|
||||||
|
if options.quiet:
|
||||||
|
try:
|
||||||
|
subprocess.check_output( cmd, *args, stderr=subprocess.STDOUT, **kwargs )
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print( e.output.decode( 'utf-8' ) )
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
subprocess.check_call( cmd, *args, **kwargs )
|
||||||
|
|
||||||
|
|
||||||
|
def PathToAnyWorkingPython3():
|
||||||
|
# We can't rely on sys.executable because it's usually 'vim' (fixme, not with
|
||||||
|
# neovim?)
|
||||||
|
paths = GetPATHAsList()
|
||||||
|
|
||||||
|
if install.GetOS() == 'windows':
|
||||||
|
candidates = [ os.path.join( sys.exec_prefix, 'python.exe' ),
|
||||||
|
'python.exe' ]
|
||||||
|
else:
|
||||||
|
candidates = [ os.path.join( sys.exec_prefix, 'bin', 'python3' ),
|
||||||
|
'python3',
|
||||||
|
'python' ]
|
||||||
|
|
||||||
|
for candidate in candidates:
|
||||||
|
try:
|
||||||
|
return FindExecutable( candidate, paths=paths )
|
||||||
|
except MissingExecutable:
|
||||||
|
pass
|
||||||
|
|
||||||
|
raise RuntimeError( "Unable to find a working python3" )
|
||||||
|
|
||||||
|
|
||||||
|
def RunInstaller( api_prefix, leave_open, *args, **kwargs ):
|
||||||
|
from vimspector import utils, output, settings
|
||||||
|
import vim
|
||||||
|
|
||||||
|
if not args:
|
||||||
|
args = settings.List( 'install_gadgets' )
|
||||||
|
|
||||||
|
if not args:
|
||||||
|
return
|
||||||
|
|
||||||
|
args = GadgetListToInstallerArgs( *args )
|
||||||
|
|
||||||
|
vimspector_home = utils.GetVimValue( vim.vars, 'vimspector_home' )
|
||||||
|
vimspector_base_dir = utils.GetVimspectorBase()
|
||||||
|
|
||||||
|
global OUTPUT_VIEW
|
||||||
|
_ResetInstaller()
|
||||||
|
|
||||||
|
with utils.RestoreCurrentWindow():
|
||||||
|
vim.command( f'botright { settings.Int( "bottombar_height" ) }new' )
|
||||||
|
win = vim.current.window
|
||||||
|
OUTPUT_VIEW = output.OutputView( win, api_prefix )
|
||||||
|
|
||||||
|
cmd = [
|
||||||
|
PathToAnyWorkingPython3(),
|
||||||
|
'-u',
|
||||||
|
os.path.join( vimspector_home, 'install_gadget.py' ),
|
||||||
|
'--quiet',
|
||||||
|
'--update-gadget-config',
|
||||||
|
]
|
||||||
|
if not vimspector_base_dir == vimspector_home:
|
||||||
|
cmd.extend( [ '--basedir', vimspector_base_dir ] )
|
||||||
|
cmd.extend( args )
|
||||||
|
|
||||||
|
def handler( exit_code ):
|
||||||
|
if exit_code == 0:
|
||||||
|
if not leave_open:
|
||||||
|
_ResetInstaller()
|
||||||
|
utils.UserMessage( "Vimspector gadget installation complete!" )
|
||||||
|
vim.command( 'silent doautocmd User VimspectorInstallSuccess' )
|
||||||
|
if 'then' in kwargs:
|
||||||
|
kwargs[ 'then' ]()
|
||||||
|
else:
|
||||||
|
utils.UserMessage( 'Vimspector gadget installation reported errors',
|
||||||
|
error = True )
|
||||||
|
vim.command( 'silent doautocmd User VimspectorInstallFailed' )
|
||||||
|
|
||||||
|
|
||||||
|
OUTPUT_VIEW.RunJobWithOutput( 'Installer',
|
||||||
|
cmd,
|
||||||
|
completion_handler = handler,
|
||||||
|
syntax = 'vimspector-installer' )
|
||||||
|
OUTPUT_VIEW.ShowOutput( 'Installer' )
|
||||||
|
|
||||||
|
|
||||||
|
def RunUpdate( api_prefix, leave_open, *args ):
|
||||||
|
from vimspector import utils, settings
|
||||||
|
Configure( vimspector_base = utils.GetVimspectorBase() )
|
||||||
|
|
||||||
|
insatller_args = list( args )
|
||||||
|
insatller_args.extend( settings.List( 'install_gadgets' ) )
|
||||||
|
|
||||||
|
current_adapters = ReadAdapters( read_existing = True )
|
||||||
|
for adapter_name in current_adapters.keys():
|
||||||
|
insatller_args.extend( FindGadgetForAdapter( adapter_name ) )
|
||||||
|
|
||||||
|
if insatller_args:
|
||||||
|
insatller_args.append( '--upgrade' )
|
||||||
|
RunInstaller( api_prefix, leave_open, *insatller_args )
|
||||||
|
|
||||||
|
|
||||||
|
def _ResetInstaller():
|
||||||
|
global OUTPUT_VIEW
|
||||||
|
if OUTPUT_VIEW:
|
||||||
|
OUTPUT_VIEW.Reset()
|
||||||
|
OUTPUT_VIEW = None
|
||||||
|
|
||||||
|
|
||||||
|
def Abort():
|
||||||
|
_ResetInstaller()
|
||||||
|
from vimspector import utils
|
||||||
|
utils.UserMessage( 'Vimspector installation aborted',
|
||||||
|
persist = True,
|
||||||
|
error = True )
|
||||||
|
|
||||||
|
|
||||||
|
def GadgetListToInstallerArgs( *gadget_list ):
|
||||||
|
installer_args = []
|
||||||
|
for name in gadget_list:
|
||||||
|
if name.startswith( '-' ):
|
||||||
|
installer_args.append( name )
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
gadget = gadgets.GADGETS[ name ]
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
lang = gadget[ "language" ]
|
||||||
|
if isinstance( lang, list ):
|
||||||
|
lang = lang[ 0 ]
|
||||||
|
|
||||||
|
if not gadget.get( 'enabled', True ):
|
||||||
|
installer_args.append( f'--force-enable-{lang}' )
|
||||||
|
else:
|
||||||
|
installer_args.append( f'--enable-{lang}' )
|
||||||
|
|
||||||
|
return installer_args
|
||||||
|
|
||||||
|
|
||||||
|
def FindGadgetForAdapter( adapter_name ):
|
||||||
|
candidates = []
|
||||||
|
for name, gadget in gadgets.GADGETS.items():
|
||||||
|
v = {}
|
||||||
|
v.update( gadget.get( 'all', {} ) )
|
||||||
|
v.update( gadget.get( install.GetOS(), {} ) )
|
||||||
|
|
||||||
|
adapters = {}
|
||||||
|
adapters.update( v.get( 'adapters', {} ) )
|
||||||
|
adapters.update( gadget.get( 'adapters', {} ) )
|
||||||
|
|
||||||
|
if adapter_name in adapters:
|
||||||
|
candidates.append( name )
|
||||||
|
|
||||||
|
return candidates
|
||||||
|
|
||||||
|
|
||||||
|
class Manifest:
|
||||||
|
manifest: dict
|
||||||
|
|
||||||
|
def __init__( self ):
|
||||||
|
self.manifest = {}
|
||||||
|
self.Read()
|
||||||
|
|
||||||
|
def Read( self ):
|
||||||
|
try:
|
||||||
|
with open( install.GetManifestFile( options.vimspector_base ), 'r' ) as f:
|
||||||
|
self.manifest = json.load( f )
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def Write( self ):
|
||||||
|
with open( install.GetManifestFile( options.vimspector_base ), 'w' ) as f:
|
||||||
|
json.dump( self.manifest, f )
|
||||||
|
|
||||||
|
|
||||||
|
def Clear( self, name: str ):
|
||||||
|
try:
|
||||||
|
del self.manifest[ name ]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def Update( self, name: str, gadget_spec: dict ):
|
||||||
|
self.manifest[ name ] = gadget_spec
|
||||||
|
|
||||||
|
|
||||||
|
def RequiresUpdate( self, name: str, gadget_spec: dict ):
|
||||||
|
try:
|
||||||
|
current_spec = self.manifest[ name ]
|
||||||
|
except KeyError:
|
||||||
|
# It's new.
|
||||||
|
return True
|
||||||
|
|
||||||
|
# If anything changed in the spec, update
|
||||||
|
if not current_spec == gadget_spec:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Always update if the version string is 'master'. Probably a git repo
|
||||||
|
# that pulls master (which tbh we shouldn't have)
|
||||||
|
if current_spec.get( 'version' ) in ( 'master', '' ):
|
||||||
|
return True
|
||||||
|
if current_spec.get( 'repo', {} ).get( 'ref' ) == 'master':
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def ReadAdapters( read_existing = True ):
|
||||||
|
all_adapters = {}
|
||||||
|
if read_existing:
|
||||||
|
try:
|
||||||
|
with open( install.GetGadgetConfigFile( options.vimspector_base ),
|
||||||
|
'r' ) as f:
|
||||||
|
all_adapters = json.load( f ).get( 'adapters', {} )
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Include "built-in" adapter for multi-session mode
|
||||||
|
all_adapters.update( {
|
||||||
|
'multi-session': {
|
||||||
|
'port': '${port}',
|
||||||
|
'host': '${host}'
|
||||||
|
},
|
||||||
|
} )
|
||||||
|
|
||||||
|
return all_adapters
|
||||||
|
|
||||||
|
|
||||||
|
def WriteAdapters( all_adapters, to_file=None ):
|
||||||
|
adapter_config = json.dumps ( { 'adapters': all_adapters },
|
||||||
|
indent=2,
|
||||||
|
sort_keys=True )
|
||||||
|
|
||||||
|
if to_file:
|
||||||
|
to_file.write( adapter_config )
|
||||||
|
else:
|
||||||
|
with open( install.GetGadgetConfigFile( options.vimspector_base ),
|
||||||
|
'w' ) as f:
|
||||||
|
f.write( adapter_config )
|
||||||
|
|
||||||
|
|
||||||
|
def InstallGeneric( name, root, gadget ):
|
||||||
|
extension_path = gadget.get( 'extension_path', 'extension' )
|
||||||
|
extension = os.path.join( root, extension_path )
|
||||||
|
for f in gadget.get( 'make_executable', [] ):
|
||||||
|
MakeExecutable( os.path.join( extension, f ) )
|
||||||
|
|
||||||
|
MakeExtensionSymlink( name, root, extension_path )
|
||||||
|
|
||||||
|
|
||||||
|
def InstallCppTools( name, root, gadget ):
|
||||||
|
extension = os.path.join( root, 'extension' )
|
||||||
|
|
||||||
|
# It's hilarious, but the execute bits aren't set in the vsix. So they
|
||||||
|
# actually have javascript code which does this. It's just a horrible horrible
|
||||||
|
# hack that really is not funny.
|
||||||
|
MakeExecutable(
|
||||||
|
os.path.join( extension, 'debugAdapters', 'bin', 'OpenDebugAD7' ) )
|
||||||
|
with open( os.path.join( extension, 'package.json' ) ) as f:
|
||||||
|
package = json.load( f )
|
||||||
|
runtime_dependencies = package[ 'runtimeDependencies' ]
|
||||||
|
for dependency in runtime_dependencies:
|
||||||
|
for binary in dependency.get( 'binaries' ):
|
||||||
|
file_path = os.path.abspath( os.path.join( extension, binary ) )
|
||||||
|
if os.path.exists( file_path ):
|
||||||
|
MakeExecutable( os.path.join( extension, binary ) )
|
||||||
|
|
||||||
|
MakeExtensionSymlink( name, root )
|
||||||
|
|
||||||
|
|
||||||
|
def InstallBashDebug( name, root, gadget ):
|
||||||
|
MakeExecutable( os.path.join( root, 'extension', 'bashdb_dir', 'bashdb' ) )
|
||||||
|
MakeExtensionSymlink( name, root )
|
||||||
|
|
||||||
|
|
||||||
|
def InstallDebugpy( name, root, gadget ):
|
||||||
|
wd = os.getcwd()
|
||||||
|
root = os.path.join( root, 'debugpy-{}'.format( gadget[ 'version' ] ) )
|
||||||
|
os.chdir( root )
|
||||||
|
try:
|
||||||
|
CheckCall( [ sys.executable, 'setup.py', 'build' ] )
|
||||||
|
finally:
|
||||||
|
os.chdir( wd )
|
||||||
|
|
||||||
|
MakeSymlink( name, root )
|
||||||
|
|
||||||
|
|
||||||
|
def InstallTclProDebug( name, root, gadget ):
|
||||||
|
configure = [ 'sh', './configure' ]
|
||||||
|
|
||||||
|
if install.GetOS() == 'macos':
|
||||||
|
# Apple removed the headers from system frameworks because they are
|
||||||
|
# determined to make life difficult. And the TCL configure scripts are super
|
||||||
|
# old so don't know about this. So we do their job for them and try and find
|
||||||
|
# a tclConfig.sh.
|
||||||
|
#
|
||||||
|
# NOTE however that in Apple's infinite wisdom, installing the "headers" in
|
||||||
|
# the other location is actually broken because the paths in the
|
||||||
|
# tclConfig.sh are pointing at the _old_ location. You actually do have to
|
||||||
|
# run the package installation which puts the headers back in order to work.
|
||||||
|
# This is why the below list is does not contain stuff from
|
||||||
|
# /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform
|
||||||
|
# '/Applications/Xcode.app/Contents/Developer/Platforms'
|
||||||
|
# '/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System'
|
||||||
|
# '/Library/Frameworks/Tcl.framework',
|
||||||
|
# '/Applications/Xcode.app/Contents/Developer/Platforms'
|
||||||
|
# '/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System'
|
||||||
|
# '/Library/Frameworks/Tcl.framework/Versions'
|
||||||
|
# '/Current',
|
||||||
|
for p in [ '/usr/local/opt/tcl-tk/lib' ]:
|
||||||
|
if os.path.exists( os.path.join( p, 'tclConfig.sh' ) ):
|
||||||
|
configure.append( '--with-tcl=' + p )
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
with CurrentWorkingDir( os.path.join( root, 'lib', 'tclparser' ) ):
|
||||||
|
CheckCall( configure )
|
||||||
|
CheckCall( [ 'make' ] )
|
||||||
|
|
||||||
|
MakeSymlink( name, root )
|
||||||
|
|
||||||
|
|
||||||
|
def InstallNodeDebug( name, root, gadget ):
|
||||||
|
with CurrentWorkingDir( root ):
|
||||||
|
CheckCall( [ 'npm', 'install' ] )
|
||||||
|
CheckCall( [ 'npm', 'run', 'build' ] )
|
||||||
|
MakeSymlink( name, root )
|
||||||
|
|
||||||
|
|
||||||
|
def InstallLuaLocal( name, root, gadget ):
|
||||||
|
with CurrentWorkingDir( root ):
|
||||||
|
CheckCall( [ 'npm', 'install' ] )
|
||||||
|
CheckCall( [ 'npm', 'run', 'build' ] )
|
||||||
|
MakeSymlink( name, root )
|
||||||
|
|
||||||
|
|
||||||
|
def InstallGagdet( name: str,
|
||||||
|
gadget: dict,
|
||||||
|
manifest: Manifest,
|
||||||
|
succeeded: list,
|
||||||
|
failed: list,
|
||||||
|
all_adapters: dict ):
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Spec is an os-specific definition of the gadget
|
||||||
|
spec = {}
|
||||||
|
spec.update( gadget.get( 'all', {} ) )
|
||||||
|
spec.update( gadget.get( install.GetOS(), {} ) )
|
||||||
|
|
||||||
|
def save_adapters():
|
||||||
|
# allow per-os adapter overrides. v already did that for us...
|
||||||
|
all_adapters.update( spec.get( 'adapters', {} ) )
|
||||||
|
# add any other "all" adapters
|
||||||
|
all_adapters.update( gadget.get( 'adapters', {} ) )
|
||||||
|
|
||||||
|
if 'download' in gadget:
|
||||||
|
if 'file_name' not in spec:
|
||||||
|
raise RuntimeError( "Unsupported OS {} for gadget {}".format(
|
||||||
|
install.GetOS(),
|
||||||
|
name ) )
|
||||||
|
|
||||||
|
print( f"Installing {name}@{spec[ 'version' ]}..." )
|
||||||
|
spec[ 'download' ] = gadget[ 'download' ]
|
||||||
|
if not manifest.RequiresUpdate( name, spec ):
|
||||||
|
save_adapters()
|
||||||
|
print( " - Skip - up to date" )
|
||||||
|
return
|
||||||
|
|
||||||
|
destination = os.path.join(
|
||||||
|
install.GetGadgetDir( options.vimspector_base ),
|
||||||
|
'download',
|
||||||
|
name,
|
||||||
|
spec[ 'version' ] )
|
||||||
|
|
||||||
|
url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( spec )
|
||||||
|
|
||||||
|
file_path = DownloadFileTo(
|
||||||
|
url,
|
||||||
|
destination,
|
||||||
|
file_name = gadget[ 'download' ].get( 'target' ),
|
||||||
|
checksum = spec.get( 'checksum' ),
|
||||||
|
check_certificate = not options.no_check_certificate )
|
||||||
|
|
||||||
|
root = os.path.join( destination, 'root' )
|
||||||
|
ExtractZipTo(
|
||||||
|
file_path,
|
||||||
|
root,
|
||||||
|
format = gadget[ 'download' ].get( 'format', 'zip' ) )
|
||||||
|
elif 'repo' in gadget:
|
||||||
|
url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( spec )
|
||||||
|
ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( spec )
|
||||||
|
|
||||||
|
print( f"Installing {name}@{ref}..." )
|
||||||
|
spec[ 'repo' ] = gadget[ 'repo' ]
|
||||||
|
if not manifest.RequiresUpdate( name, spec ):
|
||||||
|
save_adapters()
|
||||||
|
print( " - Skip - up to date" )
|
||||||
|
return
|
||||||
|
|
||||||
|
destination = os.path.join(
|
||||||
|
install.GetGadgetDir( options.vimspector_base ),
|
||||||
|
'download',
|
||||||
|
name )
|
||||||
|
CloneRepoTo( url, ref, destination )
|
||||||
|
root = destination
|
||||||
|
|
||||||
|
if 'do' in gadget:
|
||||||
|
gadget[ 'do' ]( name, root, spec )
|
||||||
|
else:
|
||||||
|
InstallGeneric( name, root, spec )
|
||||||
|
|
||||||
|
save_adapters()
|
||||||
|
manifest.Update( name, spec )
|
||||||
|
succeeded.append( name )
|
||||||
|
print( f" - Done installing {name}" )
|
||||||
|
except Exception as e:
|
||||||
|
if not options.quiet:
|
||||||
|
traceback.print_exc()
|
||||||
|
failed.append( name )
|
||||||
|
print( f" - FAILED installing {name}: {e}".format( name, e ) )
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def CurrentWorkingDir( d ):
|
||||||
|
cur_d = os.getcwd()
|
||||||
|
try:
|
||||||
|
os.chdir( d )
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
os.chdir( cur_d )
|
||||||
|
|
||||||
|
|
||||||
|
def MakeExecutable( file_path ):
|
||||||
|
# TODO: import stat and use them by _just_ adding the X bit.
|
||||||
|
Print( 'Making executable: {}'.format( file_path ) )
|
||||||
|
os.chmod( file_path, 0o755 )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def WithRetry( f ):
|
||||||
|
retries = 5
|
||||||
|
timeout = 1 # seconds
|
||||||
|
|
||||||
|
@functools.wraps( f )
|
||||||
|
def wrapper( *args, **kwargs ):
|
||||||
|
thrown = None
|
||||||
|
for _ in range( retries ):
|
||||||
|
try:
|
||||||
|
return f( *args, **kwargs )
|
||||||
|
except Exception as e:
|
||||||
|
thrown = e
|
||||||
|
Print( "Failed - {}, will retry in {} seconds".format( e, timeout ) )
|
||||||
|
time.sleep( timeout )
|
||||||
|
raise thrown
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
@WithRetry
|
||||||
|
def UrlOpen( *args, **kwargs ):
|
||||||
|
return request.urlopen( *args, **kwargs )
|
||||||
|
|
||||||
|
|
||||||
|
def DownloadFileTo( url,
|
||||||
|
destination,
|
||||||
|
file_name = None,
|
||||||
|
checksum = None,
|
||||||
|
check_certificate = True ):
|
||||||
|
if not file_name:
|
||||||
|
file_name = url.split( '/' )[ -1 ]
|
||||||
|
|
||||||
|
file_path = os.path.abspath( os.path.join( destination, file_name ) )
|
||||||
|
|
||||||
|
if not os.path.isdir( destination ):
|
||||||
|
os.makedirs( destination )
|
||||||
|
|
||||||
|
if os.path.exists( file_path ):
|
||||||
|
if checksum:
|
||||||
|
if ValidateCheckSumSHA256( file_path, checksum ):
|
||||||
|
Print( "Checksum matches for {}, using it".format( file_path ) )
|
||||||
|
return file_path
|
||||||
|
else:
|
||||||
|
Print( "Checksum doesn't match for {}, removing it".format(
|
||||||
|
file_path ) )
|
||||||
|
|
||||||
|
Print( "Removing existing {}".format( file_path ) )
|
||||||
|
os.remove( file_path )
|
||||||
|
|
||||||
|
r = request.Request( url, headers = { 'User-Agent': 'Vimspector' } )
|
||||||
|
|
||||||
|
Print( "Downloading {} to {}/{}".format( url, destination, file_name ) )
|
||||||
|
|
||||||
|
if not check_certificate:
|
||||||
|
context = ssl.create_default_context()
|
||||||
|
context.check_hostname = False
|
||||||
|
context.verify_mode = ssl.CERT_NONE
|
||||||
|
kwargs = { "context": context }
|
||||||
|
else:
|
||||||
|
kwargs = {}
|
||||||
|
|
||||||
|
with contextlib.closing( UrlOpen( r, **kwargs ) ) as u:
|
||||||
|
with open( file_path, 'wb' ) as f:
|
||||||
|
f.write( u.read() )
|
||||||
|
|
||||||
|
if checksum:
|
||||||
|
if not ValidateCheckSumSHA256( file_path, checksum ):
|
||||||
|
raise RuntimeError(
|
||||||
|
'Checksum for {} ({}) does not match expected {}'.format(
|
||||||
|
file_path,
|
||||||
|
GetChecksumSHA254( file_path ),
|
||||||
|
checksum ) )
|
||||||
|
else:
|
||||||
|
Print( "Checksum for {}: {}".format( file_path,
|
||||||
|
GetChecksumSHA254( file_path ) ) )
|
||||||
|
|
||||||
|
return file_path
|
||||||
|
|
||||||
|
|
||||||
|
def GetChecksumSHA254( file_path ):
|
||||||
|
with open( file_path, 'rb' ) as existing_file:
|
||||||
|
return hashlib.sha256( existing_file.read() ).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def ValidateCheckSumSHA256( file_path, checksum ):
|
||||||
|
existing_sha256 = GetChecksumSHA254( file_path )
|
||||||
|
return existing_sha256 == checksum
|
||||||
|
|
||||||
|
|
||||||
|
def RemoveIfExists( destination ):
|
||||||
|
try:
|
||||||
|
os.remove( destination )
|
||||||
|
Print( "Removed file {}".format( destination ) )
|
||||||
|
return
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
N = 1
|
||||||
|
|
||||||
|
|
||||||
|
def BackupDir():
|
||||||
|
return "{}.{}".format( destination, N )
|
||||||
|
|
||||||
|
while os.path.isdir( BackupDir() ):
|
||||||
|
Print( "Removing old dir {}".format( BackupDir() ) )
|
||||||
|
try:
|
||||||
|
shutil.rmtree( BackupDir() )
|
||||||
|
Print ( "OK, removed it" )
|
||||||
|
break
|
||||||
|
except OSError as e:
|
||||||
|
Print ( f"FAILED to remove {BackupDir()}: {e}" )
|
||||||
|
N = N + 1
|
||||||
|
|
||||||
|
if os.path.exists( destination ):
|
||||||
|
Print( "Removing dir {}".format( destination ) )
|
||||||
|
try:
|
||||||
|
shutil.rmtree( destination )
|
||||||
|
except OSError:
|
||||||
|
Print( "FAILED, moving {} to dir {}".format( destination, BackupDir() ) )
|
||||||
|
os.rename( destination, BackupDir() )
|
||||||
|
|
||||||
|
|
||||||
|
# Python's ZipFile module strips execute bits from files, for no good reason
|
||||||
|
# other than crappy code. Let's do it's job for it.
|
||||||
|
class ModePreservingZipFile( zipfile.ZipFile ):
|
||||||
|
def extract( self, member, path = None, pwd = None ):
|
||||||
|
if not isinstance( member, zipfile.ZipInfo ):
|
||||||
|
member = self.getinfo( member )
|
||||||
|
|
||||||
|
if path is None:
|
||||||
|
path = os.getcwd()
|
||||||
|
|
||||||
|
ret_val = self._extract_member( member, path, pwd )
|
||||||
|
attr = member.external_attr >> 16
|
||||||
|
os.chmod( ret_val, attr )
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
|
||||||
|
def ExtractZipTo( file_path, destination, format ):
|
||||||
|
Print( "Extracting {} to {}".format( file_path, destination ) )
|
||||||
|
RemoveIfExists( destination )
|
||||||
|
|
||||||
|
if format == 'zip':
|
||||||
|
with ModePreservingZipFile( file_path ) as f:
|
||||||
|
f.extractall( path = destination )
|
||||||
|
elif format == 'zip.gz':
|
||||||
|
with gzip.open( file_path, 'rb' ) as f:
|
||||||
|
file_contents = f.read()
|
||||||
|
|
||||||
|
with ModePreservingZipFile( io.BytesIO( file_contents ) ) as f:
|
||||||
|
f.extractall( path = destination )
|
||||||
|
|
||||||
|
elif format == 'tar':
|
||||||
|
try:
|
||||||
|
with tarfile.open( file_path ) as f:
|
||||||
|
f.extractall( path = destination )
|
||||||
|
except Exception:
|
||||||
|
# There seems to a bug in python's tarfile that means it can't read some
|
||||||
|
# windows-generated tar files
|
||||||
|
os.makedirs( destination )
|
||||||
|
with CurrentWorkingDir( destination ):
|
||||||
|
CheckCall( [ 'tar', 'zxvf', file_path ] )
|
||||||
|
|
||||||
|
|
||||||
|
def MakeExtensionSymlink( name, root, extension_path = 'extension' ):
|
||||||
|
MakeSymlink( name, os.path.join( root, extension_path ) ),
|
||||||
|
|
||||||
|
|
||||||
|
def MakeSymlink( link, pointing_to, in_folder = None ):
|
||||||
|
if not in_folder:
|
||||||
|
in_folder = install.GetGadgetDir( options.vimspector_base )
|
||||||
|
|
||||||
|
RemoveIfExists( os.path.join( in_folder, link ) )
|
||||||
|
|
||||||
|
in_folder = os.path.abspath( in_folder )
|
||||||
|
pointing_to_relative = os.path.relpath( os.path.abspath( pointing_to ),
|
||||||
|
in_folder )
|
||||||
|
link_path = os.path.join( in_folder, link )
|
||||||
|
|
||||||
|
if install.GetOS() == 'windows':
|
||||||
|
# While symlinks do exist on Windows, they require elevated privileges, so
|
||||||
|
# let's use a directory junction which is all we need.
|
||||||
|
link_path = os.path.abspath( link_path )
|
||||||
|
if os.path.isdir( link_path ):
|
||||||
|
os.rmdir( link_path )
|
||||||
|
CheckCall( [ 'cmd.exe', '/c', 'mklink', '/J', link_path, pointing_to ] )
|
||||||
|
else:
|
||||||
|
os.symlink( pointing_to_relative, link_path )
|
||||||
|
|
||||||
|
|
||||||
|
def CloneRepoTo( url, ref, destination ):
|
||||||
|
RemoveIfExists( destination )
|
||||||
|
git_in_repo = [ 'git', '-C', destination ]
|
||||||
|
CheckCall( [ 'git', 'clone', url, destination ] )
|
||||||
|
CheckCall( git_in_repo + [ 'checkout', ref ] )
|
||||||
|
CheckCall( git_in_repo + [ 'submodule', 'sync', '--recursive' ] )
|
||||||
|
CheckCall( git_in_repo + [ 'submodule', 'update', '--init', '--recursive' ] )
|
||||||
|
|
||||||
|
|
||||||
|
def AbortIfSUperUser( force_sudo ):
|
||||||
|
# TODO: We should probably check the effective uid too
|
||||||
|
is_su = False
|
||||||
|
if 'SUDO_COMMAND' in os.environ:
|
||||||
|
is_su = True
|
||||||
|
|
||||||
|
if is_su:
|
||||||
|
if force_sudo:
|
||||||
|
print( "*** RUNNING AS SUPER USER DUE TO force_sudo! "
|
||||||
|
" All bets are off. ***" )
|
||||||
|
else:
|
||||||
|
sys.exit( "This script should *not* be run as super user. Aborting." )
|
||||||
|
|
@ -13,10 +13,11 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from vimspector import utils
|
from vimspector import utils, install
|
||||||
|
|
||||||
import vim
|
import vim
|
||||||
import json
|
import json
|
||||||
|
import typing
|
||||||
|
|
||||||
|
|
||||||
class TabBuffer( object ):
|
class TabBuffer( object ):
|
||||||
|
|
@ -25,13 +26,15 @@ class TabBuffer( object ):
|
||||||
self.index = index
|
self.index = index
|
||||||
self.flag = False
|
self.flag = False
|
||||||
self.is_job = False
|
self.is_job = False
|
||||||
|
self.syntax = None
|
||||||
|
|
||||||
|
|
||||||
BUFFER_MAP = {
|
BUFFER_MAP = {
|
||||||
'console': 'Console',
|
'console': 'Console',
|
||||||
'stdout': 'Console',
|
'stdout': 'Console',
|
||||||
|
'output': 'Console',
|
||||||
'stderr': 'stderr',
|
'stderr': 'stderr',
|
||||||
'telemetry': 'Telemetry',
|
'telemetry': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -39,23 +42,34 @@ def CategoryToBuffer( category ):
|
||||||
return BUFFER_MAP.get( category, category )
|
return BUFFER_MAP.get( category, category )
|
||||||
|
|
||||||
|
|
||||||
|
VIEWS = set()
|
||||||
|
|
||||||
|
|
||||||
|
def ShowOutputInWindow( win_id, category ):
|
||||||
|
for view in VIEWS:
|
||||||
|
if view._window.valid and utils.WindowID( view._window ) == win_id:
|
||||||
|
view.ShowOutput( category )
|
||||||
|
return
|
||||||
|
|
||||||
|
raise ValueError( f'Unable to find output object for win id {win_id}!' )
|
||||||
|
|
||||||
|
|
||||||
class OutputView( object ):
|
class OutputView( object ):
|
||||||
def __init__( self, connection, window ):
|
"""Container for a 'tabbed' window of buffers that can be used to display
|
||||||
|
files or the output of commands."""
|
||||||
|
_buffers: typing.Dict[ str, TabBuffer ]
|
||||||
|
|
||||||
|
def __init__( self, window, api_prefix ):
|
||||||
self._window = window
|
self._window = window
|
||||||
self._connection = connection
|
|
||||||
self._buffers = {}
|
self._buffers = {}
|
||||||
|
self._api_prefix = api_prefix
|
||||||
|
VIEWS.add( self )
|
||||||
|
|
||||||
for b in set( BUFFER_MAP.values() ):
|
def Print( self, category, text: typing.Union[ str, list ] ):
|
||||||
self._CreateBuffer( b )
|
if not isinstance( text, list ):
|
||||||
|
text = text.splitlines()
|
||||||
|
|
||||||
self._CreateBuffer(
|
self._Print( category, text )
|
||||||
'Vimspector',
|
|
||||||
file_name = vim.eval( 'expand( "~/.vimspector.log" )' ) )
|
|
||||||
|
|
||||||
self._ShowOutput( 'Console' )
|
|
||||||
|
|
||||||
def Print( self, categroy, text ):
|
|
||||||
self._Print( 'server', text.splitlines() )
|
|
||||||
|
|
||||||
def OnOutput( self, event ):
|
def OnOutput( self, event ):
|
||||||
category = CategoryToBuffer( event.get( 'category' ) or 'output' )
|
category = CategoryToBuffer( event.get( 'category' ) or 'output' )
|
||||||
|
|
@ -67,6 +81,10 @@ class OutputView( object ):
|
||||||
self._Print( category, text_lines )
|
self._Print( category, text_lines )
|
||||||
|
|
||||||
def _Print( self, category, text_lines ):
|
def _Print( self, category, text_lines ):
|
||||||
|
if category is None:
|
||||||
|
# This category is supressed
|
||||||
|
return
|
||||||
|
|
||||||
if category not in self._buffers:
|
if category not in self._buffers:
|
||||||
self._CreateBuffer( category )
|
self._CreateBuffer( category )
|
||||||
|
|
||||||
|
|
@ -78,9 +96,187 @@ class OutputView( object ):
|
||||||
self._ToggleFlag( category, True )
|
self._ToggleFlag( category, True )
|
||||||
|
|
||||||
# Scroll the buffer
|
# Scroll the buffer
|
||||||
with utils.RestoreCurrentWindow():
|
if self._window.valid:
|
||||||
with utils.RestoreCurrentBuffer( self._window ):
|
with utils.RestoreCurrentWindow():
|
||||||
|
with utils.RestoreCurrentBuffer( self._window ):
|
||||||
|
self._ShowOutput( category )
|
||||||
|
|
||||||
|
def Reset( self ):
|
||||||
|
self.Clear()
|
||||||
|
VIEWS.remove( self )
|
||||||
|
|
||||||
|
|
||||||
|
def Clear( self ):
|
||||||
|
for category, tab_buffer in self._buffers.items():
|
||||||
|
self._CleanUpBuffer( category, tab_buffer )
|
||||||
|
|
||||||
|
# FIXME: nunmenu the WinBar ?
|
||||||
|
self._buffers = {}
|
||||||
|
|
||||||
|
|
||||||
|
def ClearCategory( self, category: str ):
|
||||||
|
if category not in self._buffers:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._CleanUpBuffer( category, self._buffers[ category ] )
|
||||||
|
|
||||||
|
|
||||||
|
def _CleanUpBuffer( self, category: str, tab_buffer: TabBuffer ):
|
||||||
|
if tab_buffer.is_job:
|
||||||
|
utils.CleanUpCommand( category, self._api_prefix )
|
||||||
|
|
||||||
|
utils.CleanUpHiddenBuffer( tab_buffer.buf )
|
||||||
|
|
||||||
|
|
||||||
|
def WindowIsValid( self ):
|
||||||
|
return self._window.valid
|
||||||
|
|
||||||
|
def UseWindow( self, win ):
|
||||||
|
assert not self._window.valid
|
||||||
|
self._window = win
|
||||||
|
# TODO: Sorting of the WinBar ?
|
||||||
|
for category, _ in self._buffers.items():
|
||||||
|
self._RenderWinBar( category )
|
||||||
|
|
||||||
|
|
||||||
|
def _ShowOutput( self, category ):
|
||||||
|
if not self._window.valid:
|
||||||
|
return
|
||||||
|
|
||||||
|
utils.JumpToWindow( self._window )
|
||||||
|
vim.current.buffer = self._buffers[ category ].buf
|
||||||
|
vim.command( 'normal G' )
|
||||||
|
|
||||||
|
def ShowOutput( self, category ):
|
||||||
|
self._ToggleFlag( category, False )
|
||||||
|
self._ShowOutput( category )
|
||||||
|
|
||||||
|
def _ToggleFlag( self, category, flag ):
|
||||||
|
if self._buffers[ category ].flag != flag:
|
||||||
|
self._buffers[ category ].flag = flag
|
||||||
|
|
||||||
|
if self._window.valid:
|
||||||
|
with utils.LetCurrentWindow( self._window ):
|
||||||
|
self._RenderWinBar( category )
|
||||||
|
|
||||||
|
|
||||||
|
def RunJobWithOutput( self, category, cmd, **kwargs ):
|
||||||
|
self._CreateBuffer( category, cmd = cmd, **kwargs )
|
||||||
|
|
||||||
|
|
||||||
|
def _CreateBuffer( self,
|
||||||
|
category,
|
||||||
|
file_name = None,
|
||||||
|
cmd = None,
|
||||||
|
completion_handler = None,
|
||||||
|
syntax = None ):
|
||||||
|
|
||||||
|
buf_to_delete = None
|
||||||
|
if ( not self._buffers
|
||||||
|
and self._window is not None
|
||||||
|
and self._window.valid
|
||||||
|
and not self._window.buffer.name ):
|
||||||
|
# If there's an empty buffer in the current window that we're not using,
|
||||||
|
# delete it. We could try and use it, but that complicates the call to
|
||||||
|
# SetUpCommandBuffer
|
||||||
|
buf_to_delete = self._window.buffer
|
||||||
|
|
||||||
|
if file_name is not None:
|
||||||
|
assert cmd is None
|
||||||
|
if install.GetOS() == "windows":
|
||||||
|
# FIXME: Can't display fiels in windows (yet?)
|
||||||
|
return
|
||||||
|
|
||||||
|
cmd = [ 'tail', '-F', '-n', '+1', '--', file_name ]
|
||||||
|
|
||||||
|
if cmd is not None:
|
||||||
|
out = utils.SetUpCommandBuffer(
|
||||||
|
cmd,
|
||||||
|
category,
|
||||||
|
self._api_prefix,
|
||||||
|
completion_handler = completion_handler )
|
||||||
|
|
||||||
|
self._buffers[ category ] = TabBuffer( out, len( self._buffers ) )
|
||||||
|
self._buffers[ category ].is_job = True
|
||||||
|
self._RenderWinBar( category )
|
||||||
|
else:
|
||||||
|
if category == 'Console':
|
||||||
|
name = 'vimspector.Console'
|
||||||
|
else:
|
||||||
|
name = 'vimspector.Output:{0}'.format( category )
|
||||||
|
|
||||||
|
tab_buffer = TabBuffer( utils.NewEmptyBuffer(), len( self._buffers ) )
|
||||||
|
|
||||||
|
self._buffers[ category ] = tab_buffer
|
||||||
|
|
||||||
|
if category == 'Console':
|
||||||
|
utils.SetUpPromptBuffer( tab_buffer.buf,
|
||||||
|
name,
|
||||||
|
'> ',
|
||||||
|
'vimspector#EvaluateConsole',
|
||||||
|
'vimspector#OmniFuncConsole' )
|
||||||
|
else:
|
||||||
|
utils.SetUpHiddenBuffer( tab_buffer.buf, name )
|
||||||
|
|
||||||
|
self._RenderWinBar( category )
|
||||||
|
|
||||||
|
self._buffers[ category ].syntax = utils.SetSyntax(
|
||||||
|
self._buffers[ category ].syntax,
|
||||||
|
syntax,
|
||||||
|
self._buffers[ category ].buf )
|
||||||
|
|
||||||
|
if buf_to_delete:
|
||||||
|
with utils.RestoreCurrentWindow():
|
||||||
self._ShowOutput( category )
|
self._ShowOutput( category )
|
||||||
|
utils.CleanUpHiddenBuffer( buf_to_delete )
|
||||||
|
|
||||||
|
def _RenderWinBar( self, category ):
|
||||||
|
if not utils.UseWinBar():
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self._window.valid:
|
||||||
|
return
|
||||||
|
|
||||||
|
with utils.LetCurrentWindow( self._window ):
|
||||||
|
tab_buffer = self._buffers[ category ]
|
||||||
|
|
||||||
|
try:
|
||||||
|
if tab_buffer.flag:
|
||||||
|
vim.command( 'nunmenu WinBar.{}'.format( utils.Escape( category ) ) )
|
||||||
|
else:
|
||||||
|
vim.command( 'nunmenu WinBar.{}*'.format( utils.Escape( category ) ) )
|
||||||
|
except vim.error as e:
|
||||||
|
# E329 means the menu doesn't exist; ignore that.
|
||||||
|
if 'E329' not in str( e ):
|
||||||
|
raise
|
||||||
|
|
||||||
|
vim.command(
|
||||||
|
"nnoremenu <silent> 1.{0} WinBar.{1}{2} "
|
||||||
|
":call vimspector#ShowOutputInWindow( {3}, '{1}' )<CR>".format(
|
||||||
|
tab_buffer.index,
|
||||||
|
utils.Escape( category ),
|
||||||
|
'*' if tab_buffer.flag else '',
|
||||||
|
utils.WindowID( self._window ) ) )
|
||||||
|
|
||||||
|
def GetCategories( self ):
|
||||||
|
return list( self._buffers.keys() )
|
||||||
|
|
||||||
|
def AddLogFileView( self, file_name = utils.LOG_FILE ):
|
||||||
|
self._CreateBuffer( 'Vimspector', file_name = file_name )
|
||||||
|
|
||||||
|
|
||||||
|
class DAPOutputView( OutputView ):
|
||||||
|
"""Specialised OutputView which adds the DAP Console (REPL)"""
|
||||||
|
def __init__( self, *args ):
|
||||||
|
super().__init__( *args )
|
||||||
|
|
||||||
|
self._connection = None
|
||||||
|
for b in set( BUFFER_MAP.values() ):
|
||||||
|
if b is not None:
|
||||||
|
self._CreateBuffer( b )
|
||||||
|
|
||||||
|
self.AddLogFileView()
|
||||||
|
self._ShowOutput( 'Console' )
|
||||||
|
|
||||||
def ConnectionUp( self, connection ):
|
def ConnectionUp( self, connection ):
|
||||||
self._connection = connection
|
self._connection = connection
|
||||||
|
|
@ -89,119 +285,30 @@ class OutputView( object ):
|
||||||
# Don't clear because output is probably still useful
|
# Don't clear because output is probably still useful
|
||||||
self._connection = None
|
self._connection = None
|
||||||
|
|
||||||
def Reset( self ):
|
def Evaluate( self, frame, expression, verbose ):
|
||||||
self.Clear()
|
if verbose:
|
||||||
|
self._Print( 'Console', f"Evaluating: { expression }" )
|
||||||
def Clear( self ):
|
|
||||||
for category, tab_buffer in self._buffers.items():
|
|
||||||
if tab_buffer.is_job:
|
|
||||||
utils.CleanUpCommand( category )
|
|
||||||
try:
|
|
||||||
vim.command( 'bdelete! {0}'.format( tab_buffer.buf.number ) )
|
|
||||||
except vim.error as e:
|
|
||||||
# FIXME: For now just ignore the "no buffers were deleted" error
|
|
||||||
if 'E516' not in e:
|
|
||||||
raise
|
|
||||||
|
|
||||||
self._buffers = {}
|
|
||||||
|
|
||||||
def _ShowOutput( self, category ):
|
|
||||||
utils.JumpToWindow( self._window )
|
|
||||||
vim.command( 'bu {0}'.format( self._buffers[ category ].buf.name ) )
|
|
||||||
vim.command( 'normal G' )
|
|
||||||
|
|
||||||
def ShowOutput( self, category ):
|
|
||||||
self._ToggleFlag( category, False )
|
|
||||||
self._ShowOutput( category )
|
|
||||||
|
|
||||||
def Evaluate( self, frame, expression ):
|
|
||||||
if not frame:
|
|
||||||
self.Print( 'Console', 'There is no current stack frame' )
|
|
||||||
return
|
|
||||||
|
|
||||||
console = self._buffers[ 'Console' ].buf
|
|
||||||
utils.AppendToBuffer( console, 'Evaluating: ' + expression )
|
|
||||||
|
|
||||||
def print_result( message ):
|
def print_result( message ):
|
||||||
utils.AppendToBuffer( console,
|
|
||||||
'Evaluated: ' + expression )
|
|
||||||
|
|
||||||
result = message[ 'body' ][ 'result' ]
|
result = message[ 'body' ][ 'result' ]
|
||||||
if result is None:
|
if result is None:
|
||||||
result = 'null'
|
result = '<no result>'
|
||||||
|
self._Print( 'Console', result.splitlines() )
|
||||||
|
|
||||||
utils.AppendToBuffer( console, ' Result: ' + result )
|
def print_failure( reason, msg ):
|
||||||
|
self._Print( 'Console', reason.splitlines() )
|
||||||
|
|
||||||
self._connection.DoRequest( print_result, {
|
request = {
|
||||||
'command': 'evaluate',
|
'command': 'evaluate',
|
||||||
'arguments': {
|
'arguments': {
|
||||||
'expression': expression,
|
'expression': expression,
|
||||||
'context': 'repl',
|
'context': 'repl',
|
||||||
'frameId': frame[ 'id' ],
|
|
||||||
}
|
}
|
||||||
} )
|
}
|
||||||
|
|
||||||
def _ToggleFlag( self, category, flag ):
|
if frame:
|
||||||
if self._buffers[ category ].flag != flag:
|
request[ 'arguments' ][ 'frameId' ] = frame[ 'id' ]
|
||||||
self._buffers[ category ].flag = flag
|
|
||||||
with utils.LetCurrentWindow( self._window ):
|
|
||||||
self._RenderWinBar( category )
|
|
||||||
|
|
||||||
|
self._connection.DoRequest( print_result,
|
||||||
def RunJobWithOutput( self, category, cmd ):
|
request,
|
||||||
self._CreateBuffer( category, cmd = cmd )
|
print_failure )
|
||||||
|
|
||||||
|
|
||||||
def _CreateBuffer( self, category, file_name = None, cmd = None ):
|
|
||||||
with utils.LetCurrentWindow( self._window ):
|
|
||||||
with utils.RestoreCurrentBuffer( self._window ):
|
|
||||||
|
|
||||||
if file_name is not None:
|
|
||||||
assert cmd is None
|
|
||||||
cmd = [ 'tail', '-F', '-n', '+1', '--', file_name ]
|
|
||||||
|
|
||||||
if cmd is not None:
|
|
||||||
out, err = utils.SetUpCommandBuffer( cmd, category )
|
|
||||||
self._buffers[ category + '-out' ] = TabBuffer( out,
|
|
||||||
len( self._buffers ) )
|
|
||||||
self._buffers[ category + '-out' ].is_job = True
|
|
||||||
self._buffers[ category + '-err' ] = TabBuffer( err,
|
|
||||||
len( self._buffers ) )
|
|
||||||
self._buffers[ category + '-err' ].is_job = False
|
|
||||||
self._RenderWinBar( category + '-out' )
|
|
||||||
self._RenderWinBar( category + '-err' )
|
|
||||||
else:
|
|
||||||
vim.command( 'enew' )
|
|
||||||
tab_buffer = TabBuffer( vim.current.buffer, len( self._buffers ) )
|
|
||||||
self._buffers[ category ] = tab_buffer
|
|
||||||
if category == 'Console':
|
|
||||||
utils.SetUpPromptBuffer( tab_buffer.buf,
|
|
||||||
'vimspector.Console',
|
|
||||||
'> ',
|
|
||||||
'vimspector#EvaluateConsole',
|
|
||||||
hidden=True )
|
|
||||||
else:
|
|
||||||
utils.SetUpHiddenBuffer(
|
|
||||||
tab_buffer.buf,
|
|
||||||
'vimspector.Output:{0}'.format( category ) )
|
|
||||||
|
|
||||||
self._RenderWinBar( category )
|
|
||||||
|
|
||||||
def _RenderWinBar( self, category ):
|
|
||||||
tab_buffer = self._buffers[ category ]
|
|
||||||
|
|
||||||
try:
|
|
||||||
if tab_buffer.flag:
|
|
||||||
vim.command( 'nunmenu WinBar.{}'.format( utils.Escape( category ) ) )
|
|
||||||
else:
|
|
||||||
vim.command( 'nunmenu WinBar.{}*'.format( utils.Escape( category ) ) )
|
|
||||||
except vim.error as e:
|
|
||||||
# E329 means the menu doesn't exist; ignore that.
|
|
||||||
if 'E329' not in str( e ):
|
|
||||||
raise
|
|
||||||
|
|
||||||
vim.command( "nnoremenu 1.{0} WinBar.{1}{2} "
|
|
||||||
":call vimspector#ShowOutput( '{1}' )<CR>".format(
|
|
||||||
tab_buffer.index,
|
|
||||||
utils.Escape( category ),
|
|
||||||
'*' if tab_buffer.flag else '' ) )
|
|
||||||
|
|
|
||||||
138
python3/vimspector/settings.py
Normal file
138
python3/vimspector/settings.py
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
# vimspector - A multi-language debugging system for Vim
|
||||||
|
# Copyright 2020 Ben Jackson
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
import vim
|
||||||
|
import builtins
|
||||||
|
from vimspector import utils
|
||||||
|
|
||||||
|
DEFAULTS = {
|
||||||
|
# UI
|
||||||
|
'ui_mode': 'auto',
|
||||||
|
'bottombar_height': 10,
|
||||||
|
|
||||||
|
# For ui_mode = 'horizontal':
|
||||||
|
'sidebar_width': 50,
|
||||||
|
'code_minwidth': 82,
|
||||||
|
'terminal_maxwidth': 80,
|
||||||
|
'terminal_minwidth': 10,
|
||||||
|
|
||||||
|
# For ui_mode = 'vertical':
|
||||||
|
'topbar_height': 15,
|
||||||
|
'code_minheight': 20,
|
||||||
|
'terminal_maxheight': 15,
|
||||||
|
'terminal_minheight': 5,
|
||||||
|
|
||||||
|
# Signs
|
||||||
|
'sign_priority': {
|
||||||
|
'vimspectorPC': 200,
|
||||||
|
'vimspectorPCBP': 200,
|
||||||
|
'vimspectorBP': 9,
|
||||||
|
'vimspectorBPCond': 9,
|
||||||
|
'vimspectorBPDisabled': 9,
|
||||||
|
'vimspectorCurrentThread': 200,
|
||||||
|
'vimspectorCurrentFrame': 200,
|
||||||
|
},
|
||||||
|
|
||||||
|
# Installer
|
||||||
|
'install_gadgets': [],
|
||||||
|
|
||||||
|
# Mappings
|
||||||
|
'mappings': {
|
||||||
|
'variables': {
|
||||||
|
'expand_collapse': [ '<CR>', '<2-LeftMouse>' ],
|
||||||
|
'delete': [ '<Del>' ],
|
||||||
|
'set_value': [ '<C-CR>', '<leader><CR>' ]
|
||||||
|
},
|
||||||
|
'stack_trace': {
|
||||||
|
'expand_or_jump': [ '<CR>', '<2-LeftMouse>' ],
|
||||||
|
'focus_thread': [ '<leader><CR>' ],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
# Custom
|
||||||
|
'java_hotcodereplace_mode': 'ask',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def Get( option: str, default=None, cls=str ):
|
||||||
|
return cls( utils.GetVimValue( vim.vars,
|
||||||
|
f'vimspector_{ option }',
|
||||||
|
DEFAULTS.get( option, cls() ) ) )
|
||||||
|
|
||||||
|
|
||||||
|
def Int( option: str ):
|
||||||
|
return Get( option, cls=builtins.int )
|
||||||
|
|
||||||
|
|
||||||
|
def List( option: str ):
|
||||||
|
return utils.GetVimList( vim.vars,
|
||||||
|
f'vimspector_{ option }',
|
||||||
|
DEFAULTS.get( option, [] ) )
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME:
|
||||||
|
# In Vim, we must use vim.Dictionary because this sorts out the annoying
|
||||||
|
# keys-as-bytes discrepancy, making things awkward. That said, we still have the
|
||||||
|
# problem where the _values_ are potentially bytes. It's very tempting to just
|
||||||
|
# make a deep copy to antive str type here.
|
||||||
|
# Of course in neovim, it's totally different and you actually get a dict type
|
||||||
|
# back (though for once, neovim is making life somewhat easier for a change).
|
||||||
|
DICT_TYPE = dict
|
||||||
|
if hasattr( vim, 'Dictionary' ):
|
||||||
|
DICT_TYPE = vim.Dictionary
|
||||||
|
|
||||||
|
|
||||||
|
def Dict( option ):
|
||||||
|
return _UpdateDict( DICT_TYPE( DEFAULTS.get( option, {} ) ),
|
||||||
|
vim.vars.get( f'vimspector_{ option }', DICT_TYPE() ) )
|
||||||
|
|
||||||
|
|
||||||
|
def _UpdateDict( target, override ):
|
||||||
|
"""Apply the updates in |override| to the dict |target|. This is like
|
||||||
|
dict.update, but recursive. i.e. if the existing element is a dict, then
|
||||||
|
override elements of the sub-dict rather than wholesale replacing.
|
||||||
|
e.g.
|
||||||
|
UpdateDict(
|
||||||
|
{
|
||||||
|
'outer': { 'inner': { 'key': 'oldValue', 'existingKey': True } }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'outer': { 'inner': { 'key': 'newValue' } },
|
||||||
|
'newKey': { 'newDict': True },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
yields:
|
||||||
|
{
|
||||||
|
'outer': {
|
||||||
|
'inner': {
|
||||||
|
'key': 'newValue',
|
||||||
|
'existingKey': True
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'newKey': { newDict: True }
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
for key, value in override.items():
|
||||||
|
current_value = target.get( key )
|
||||||
|
if not isinstance( current_value, DICT_TYPE ):
|
||||||
|
target[ key ] = value
|
||||||
|
elif isinstance( value, DICT_TYPE ):
|
||||||
|
target[ key ] = _UpdateDict( current_value, value )
|
||||||
|
else:
|
||||||
|
target[ key ] = value
|
||||||
|
|
||||||
|
return target
|
||||||
46
python3/vimspector/signs.py
Normal file
46
python3/vimspector/signs.py
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import vim
|
||||||
|
|
||||||
|
from vimspector import settings, utils
|
||||||
|
|
||||||
|
|
||||||
|
def SignDefined( name ):
|
||||||
|
if utils.Exists( "*sign_getdefined" ):
|
||||||
|
return int(
|
||||||
|
vim.eval( f"len( sign_getdefined( '{ utils.Escape( name ) }' ) )" )
|
||||||
|
)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def DefineSign( name, text, double_text, texthl, col = 'right', **kwargs ):
|
||||||
|
if utils.GetVimValue( vim.options, 'ambiwidth', '' ) == 'double':
|
||||||
|
text = double_text
|
||||||
|
|
||||||
|
if col == 'right':
|
||||||
|
if int( utils.Call( 'strdisplaywidth', text ) ) < 2:
|
||||||
|
text = ' ' + text
|
||||||
|
|
||||||
|
text = text.replace( ' ', r'\ ' )
|
||||||
|
|
||||||
|
cmd = f'sign define { name } text={ text } texthl={ texthl }'
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
cmd += f' { key }={ value }'
|
||||||
|
|
||||||
|
vim.command( cmd )
|
||||||
|
|
||||||
|
|
||||||
|
def PlaceSign( sign_id, group, name, file_name, line ):
|
||||||
|
priority = settings.Dict( 'sign_priority' )[ name ]
|
||||||
|
|
||||||
|
cmd = ( f'sign place { sign_id } '
|
||||||
|
f'group={ group } '
|
||||||
|
f'name={ name } '
|
||||||
|
f'priority={ priority } '
|
||||||
|
f'line={ line } '
|
||||||
|
f'file={ file_name }' )
|
||||||
|
|
||||||
|
vim.command( cmd )
|
||||||
|
|
||||||
|
|
||||||
|
def UnplaceSign( sign_id, group ):
|
||||||
|
vim.command( f'sign unplace { sign_id } group={ group }' )
|
||||||
|
|
@ -16,59 +16,168 @@
|
||||||
import vim
|
import vim
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
import typing
|
||||||
|
|
||||||
from vimspector import utils
|
from vimspector import utils, signs, settings
|
||||||
|
|
||||||
|
|
||||||
|
class Thread:
|
||||||
|
"""The state of a single thread."""
|
||||||
|
PAUSED = 0
|
||||||
|
RUNNING = 1
|
||||||
|
TERMINATED = 3
|
||||||
|
state = RUNNING
|
||||||
|
|
||||||
|
stopped_event: typing.Dict
|
||||||
|
thread: typing.Dict
|
||||||
|
stacktrace: typing.List[ typing.Dict ]
|
||||||
|
id: str
|
||||||
|
|
||||||
|
def __init__( self, thread ):
|
||||||
|
self.id = thread[ 'id' ]
|
||||||
|
self.stopped_event = None
|
||||||
|
self.Update( thread )
|
||||||
|
|
||||||
|
def Update( self, thread ):
|
||||||
|
self.thread = thread
|
||||||
|
self.stacktrace = None
|
||||||
|
|
||||||
|
def Paused( self, event ):
|
||||||
|
self.state = Thread.PAUSED
|
||||||
|
self.stopped_event = event
|
||||||
|
|
||||||
|
def Continued( self ):
|
||||||
|
self.state = Thread.RUNNING
|
||||||
|
self.stopped_event = None
|
||||||
|
self.Collapse()
|
||||||
|
|
||||||
|
def Exited( self ):
|
||||||
|
self.state = Thread.TERMINATED
|
||||||
|
self.stopped_event = None
|
||||||
|
|
||||||
|
def State( self ):
|
||||||
|
if self.state == Thread.PAUSED:
|
||||||
|
return self.stopped_event.get( 'description' ) or 'paused'
|
||||||
|
elif self.state == Thread.RUNNING:
|
||||||
|
return 'running'
|
||||||
|
return 'terminated'
|
||||||
|
|
||||||
|
def Expand( self, stack_trace ):
|
||||||
|
self.stacktrace = stack_trace
|
||||||
|
|
||||||
|
def Collapse( self ):
|
||||||
|
self.stacktrace = None
|
||||||
|
|
||||||
|
def IsExpanded( self ):
|
||||||
|
return self.stacktrace is not None
|
||||||
|
|
||||||
|
def CanExpand( self ):
|
||||||
|
return self.state == Thread.PAUSED
|
||||||
|
|
||||||
|
|
||||||
class StackTraceView( object ):
|
class StackTraceView( object ):
|
||||||
def __init__( self, session, connection, buf ):
|
class ThreadRequestState:
|
||||||
|
NO = 0
|
||||||
|
REQUESTING = 1
|
||||||
|
PENDING = 2
|
||||||
|
|
||||||
|
# FIXME: Make into a dict by id ?
|
||||||
|
_threads: typing.List[ Thread ]
|
||||||
|
_line_to_thread = typing.Dict[ int, Thread ]
|
||||||
|
|
||||||
|
def __init__( self, session, win ):
|
||||||
self._logger = logging.getLogger( __name__ )
|
self._logger = logging.getLogger( __name__ )
|
||||||
utils.SetUpLogging( self._logger )
|
utils.SetUpLogging( self._logger )
|
||||||
|
|
||||||
self._buf = buf
|
self._buf = win.buffer
|
||||||
self._session = session
|
self._session = session
|
||||||
self._connection = connection
|
self._connection = None
|
||||||
|
|
||||||
self._currentThread = None
|
self._current_thread = None
|
||||||
self._currentFrame = None
|
self._current_frame = None
|
||||||
|
self._current_syntax = ""
|
||||||
|
|
||||||
self._threads = []
|
self._threads = []
|
||||||
self._sources = {}
|
self._sources = {}
|
||||||
|
self._scratch_buffers = []
|
||||||
|
|
||||||
utils.SetUpScratchBuffer( self._buf, 'vimspector.StackTrace' )
|
# FIXME: This ID is by group, so should be module scope
|
||||||
vim.current.buffer = self._buf
|
self._current_thread_sign_id = 0 # 1 when used
|
||||||
vim.command( 'nnoremap <buffer> <CR> :call vimspector#GoToFrame()<CR>' )
|
self._current_frame_sign_id = 0 # 2 when used
|
||||||
|
|
||||||
|
utils.SetUpHiddenBuffer( self._buf, 'vimspector.StackTrace' )
|
||||||
|
utils.SetUpUIWindow( win )
|
||||||
|
|
||||||
|
mappings = settings.Dict( 'mappings' )[ 'stack_trace' ]
|
||||||
|
|
||||||
|
with utils.LetCurrentWindow( win ):
|
||||||
|
for mapping in utils.GetVimList( mappings, 'expand_or_jump' ):
|
||||||
|
vim.command( f'nnoremap <silent> <buffer> { mapping } '
|
||||||
|
':<C-U>call vimspector#GoToFrame()<CR>' )
|
||||||
|
|
||||||
|
for mapping in utils.GetVimList( mappings, 'focus_thread' ):
|
||||||
|
vim.command( f'nnoremap <silent> <buffer> { mapping } '
|
||||||
|
':<C-U>call vimspector#SetCurrentThread()<CR>' )
|
||||||
|
|
||||||
|
if utils.UseWinBar():
|
||||||
|
vim.command( 'nnoremenu <silent> 1.1 WinBar.Pause/Continue '
|
||||||
|
':call vimspector#PauseContinueThread()<CR>' )
|
||||||
|
vim.command( 'nnoremenu <silent> 1.2 WinBar.Expand/Collapse '
|
||||||
|
':call vimspector#GoToFrame()<CR>' )
|
||||||
|
vim.command( 'nnoremenu <silent> 1.3 WinBar.Focus '
|
||||||
|
':call vimspector#SetCurrentThread()<CR>' )
|
||||||
|
|
||||||
|
win.options[ 'cursorline' ] = False
|
||||||
|
win.options[ 'signcolumn' ] = 'auto'
|
||||||
|
|
||||||
|
|
||||||
|
if not signs.SignDefined( 'vimspectorCurrentThread' ):
|
||||||
|
signs.DefineSign( 'vimspectorCurrentThread',
|
||||||
|
text = '▶ ',
|
||||||
|
double_text = '▶',
|
||||||
|
texthl = 'MatchParen',
|
||||||
|
linehl = 'CursorLine' )
|
||||||
|
|
||||||
|
if not signs.SignDefined( 'vimspectorCurrentFrame' ):
|
||||||
|
signs.DefineSign( 'vimspectorCurrentFrame',
|
||||||
|
text = '▶ ',
|
||||||
|
double_text = '▶',
|
||||||
|
texthl = 'Special',
|
||||||
|
linehl = 'CursorLine' )
|
||||||
|
|
||||||
self._line_to_frame = {}
|
self._line_to_frame = {}
|
||||||
self._line_to_thread = {}
|
self._line_to_thread = {}
|
||||||
|
|
||||||
# TODO: We really need a proper state model
|
self._requesting_threads = StackTraceView.ThreadRequestState.NO
|
||||||
#
|
self._pending_thread_request = None
|
||||||
# AWAIT_CONNECTION -- OnServerReady / RequestThreads --> REQUESTING_THREADS
|
|
||||||
# REQUESTING -- OnGotThreads / RequestScopes --> REQUESTING_SCOPES
|
|
||||||
#
|
|
||||||
# When we attach using gdbserver, this whole thing breaks because we request
|
|
||||||
# the threads over and over and get duff data back on later threads.
|
|
||||||
self._requesting_threads = False
|
|
||||||
|
|
||||||
|
|
||||||
def GetCurrentThreadId( self ):
|
def GetCurrentThreadId( self ):
|
||||||
return self._currentThread
|
return self._current_thread
|
||||||
|
|
||||||
def GetCurrentFrame( self ):
|
def GetCurrentFrame( self ):
|
||||||
return self._currentFrame
|
return self._current_frame
|
||||||
|
|
||||||
def Clear( self ):
|
def Clear( self ):
|
||||||
self._currentFrame = None
|
self._current_frame = None
|
||||||
self._currentThread = None
|
self._current_thread = None
|
||||||
self._threads = []
|
self._current_syntax = ""
|
||||||
|
self._threads.clear()
|
||||||
self._sources = {}
|
self._sources = {}
|
||||||
|
self._requesting_threads = StackTraceView.ThreadRequestState.NO
|
||||||
|
self._pending_thread_request = None
|
||||||
|
if self._current_thread_sign_id:
|
||||||
|
signs.UnplaceSign( self._current_thread_sign_id, 'VimspectorStackTrace' )
|
||||||
|
self._current_thread_sign_id = 0
|
||||||
|
if self._current_frame_sign_id:
|
||||||
|
signs.UnplaceSign( self._current_frame_sign_id, 'VimspectorStackTrace' )
|
||||||
|
self._current_frame_sign_id = 0
|
||||||
|
|
||||||
with utils.ModifiableScratchBuffer( self._buf ):
|
with utils.ModifiableScratchBuffer( self._buf ):
|
||||||
utils.ClearBuffer( self._buf )
|
utils.ClearBuffer( self._buf )
|
||||||
|
|
||||||
def ConnectionUp( self, connection ):
|
def ConnectionUp( self, connection ):
|
||||||
self._connection = connection
|
self._connection = connection
|
||||||
self._requesting_threads = False
|
|
||||||
|
|
||||||
def ConnectionClosed( self ):
|
def ConnectionClosed( self ):
|
||||||
self.Clear()
|
self.Clear()
|
||||||
|
|
@ -76,69 +185,142 @@ class StackTraceView( object ):
|
||||||
|
|
||||||
def Reset( self ):
|
def Reset( self ):
|
||||||
self.Clear()
|
self.Clear()
|
||||||
# TODO: delete the buffer ?
|
utils.CleanUpHiddenBuffer( self._buf )
|
||||||
|
for b in self._scratch_buffers:
|
||||||
|
utils.CleanUpHiddenBuffer( b )
|
||||||
|
self._scratch_buffers = []
|
||||||
|
self._buf = None
|
||||||
|
|
||||||
def LoadThreads( self, infer_current_frame ):
|
def LoadThreads( self,
|
||||||
pending_request = False
|
infer_current_frame,
|
||||||
if self._requesting_threads:
|
reason = '',
|
||||||
pending_request = True
|
stopEvent = None ):
|
||||||
|
if self._requesting_threads != StackTraceView.ThreadRequestState.NO:
|
||||||
|
self._requesting_threads = StackTraceView.ThreadRequestState.PENDING
|
||||||
|
self._pending_thread_request = ( infer_current_frame,
|
||||||
|
reason,
|
||||||
|
stopEvent )
|
||||||
return
|
return
|
||||||
|
|
||||||
def consume_threads( message ):
|
def consume_threads( message ):
|
||||||
self._requesting_threads = False
|
requesting = False
|
||||||
|
if self._requesting_threads == StackTraceView.ThreadRequestState.PENDING:
|
||||||
|
# We may have hit a thread event, so try again.
|
||||||
|
self._requesting_threads = StackTraceView.ThreadRequestState.NO
|
||||||
|
self.LoadThreads( *self._pending_thread_request )
|
||||||
|
requesting = True
|
||||||
|
|
||||||
if not message[ 'body' ][ 'threads' ]:
|
self._requesting_threads = StackTraceView.ThreadRequestState.NO
|
||||||
if pending_request:
|
self._pending_thread_request = None
|
||||||
# We may have hit a thread event, so try again.
|
|
||||||
self.LoadThreads( infer_current_frame )
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
# This is a protocol error. It is required to return at least one!
|
|
||||||
utils.UserMessage( 'Server returned no threads. Is it running?',
|
|
||||||
persist = True )
|
|
||||||
|
|
||||||
|
if not ( message.get( 'body' ) or {} ).get( 'threads' ):
|
||||||
|
# This is a protocol error. It is required to return at least one!
|
||||||
|
utils.UserMessage( 'Protocol error: Server returned no threads',
|
||||||
|
persist = False,
|
||||||
|
error = True )
|
||||||
|
return
|
||||||
|
|
||||||
|
existing_threads = self._threads[ : ]
|
||||||
self._threads.clear()
|
self._threads.clear()
|
||||||
|
|
||||||
for thread in message[ 'body' ][ 'threads' ]:
|
if stopEvent is not None:
|
||||||
|
stoppedThreadId = stopEvent.get( 'threadId' )
|
||||||
|
allThreadsStopped = stopEvent.get( 'allThreadsStopped', False )
|
||||||
|
|
||||||
|
# FIXME: This is horribly inefficient
|
||||||
|
for t in message[ 'body' ][ 'threads' ]:
|
||||||
|
thread = None
|
||||||
|
for existing_thread in existing_threads:
|
||||||
|
if existing_thread.id == t[ 'id' ]:
|
||||||
|
thread = existing_thread
|
||||||
|
thread.Update( t )
|
||||||
|
break
|
||||||
|
|
||||||
|
if not thread:
|
||||||
|
thread = Thread( t )
|
||||||
|
|
||||||
self._threads.append( thread )
|
self._threads.append( thread )
|
||||||
|
|
||||||
if infer_current_frame and thread[ 'id' ] == self._currentThread:
|
# If the threads were requested due to a stopped event, update any
|
||||||
self._LoadStackTrace( thread, True )
|
# stopped thread state. Note we have to do this here (rather than in the
|
||||||
elif infer_current_frame and self._currentThread is None:
|
# stopped event handler) because we must apply this event to any new
|
||||||
self._currentThread = thread[ 'id' ]
|
# threads that are received here.
|
||||||
self._LoadStackTrace( thread, True )
|
if stopEvent:
|
||||||
|
if allThreadsStopped:
|
||||||
|
thread.Paused( stopEvent )
|
||||||
|
elif stoppedThreadId is not None and thread.id == stoppedThreadId:
|
||||||
|
thread.Paused( stopEvent )
|
||||||
|
|
||||||
self._DrawThreads()
|
# If this is a stopped event, load the stack trace for the "current"
|
||||||
|
# thread. Don't do this on other thrads requests because some servers
|
||||||
|
# just break when that happens.
|
||||||
|
#
|
||||||
|
# Don't do this if we're also satisfying a cached request already (we'll
|
||||||
|
# do it then)
|
||||||
|
if infer_current_frame and not requesting:
|
||||||
|
if thread.id == self._current_thread:
|
||||||
|
if thread.CanExpand():
|
||||||
|
self._LoadStackTrace( thread, True, reason )
|
||||||
|
requesting = True
|
||||||
|
elif self._current_thread is None:
|
||||||
|
self._current_thread = thread.id
|
||||||
|
if thread.CanExpand():
|
||||||
|
self._LoadStackTrace( thread, True, reason )
|
||||||
|
requesting = True
|
||||||
|
|
||||||
self._requesting_threads = True
|
if not requesting:
|
||||||
|
self._DrawThreads()
|
||||||
|
|
||||||
|
def failure_handler( reason, msg ):
|
||||||
|
# Make sure we request them again if the request fails
|
||||||
|
self._requesting_threads = StackTraceView.ThreadRequestState.NO
|
||||||
|
self._pending_thread_request = None
|
||||||
|
|
||||||
|
self._requesting_threads = StackTraceView.ThreadRequestState.REQUESTING
|
||||||
self._connection.DoRequest( consume_threads, {
|
self._connection.DoRequest( consume_threads, {
|
||||||
'command': 'threads',
|
'command': 'threads',
|
||||||
} )
|
}, failure_handler )
|
||||||
|
|
||||||
def _DrawThreads( self ):
|
def _DrawThreads( self ):
|
||||||
self._line_to_frame.clear()
|
self._line_to_frame.clear()
|
||||||
self._line_to_thread.clear()
|
self._line_to_thread.clear()
|
||||||
|
|
||||||
|
if self._current_thread_sign_id:
|
||||||
|
signs.UnplaceSign( self._current_thread_sign_id, 'VimspectorStackTrace' )
|
||||||
|
else:
|
||||||
|
self._current_thread_sign_id = 1
|
||||||
|
|
||||||
with utils.ModifiableScratchBuffer( self._buf ):
|
with utils.ModifiableScratchBuffer( self._buf ):
|
||||||
utils.ClearBuffer( self._buf )
|
with utils.RestoreCursorPosition():
|
||||||
|
utils.ClearBuffer( self._buf )
|
||||||
|
|
||||||
for thread in self._threads:
|
for thread in self._threads:
|
||||||
icon = '+' if '_frames' not in thread else '-'
|
icon = '+' if not thread.IsExpanded() else '-'
|
||||||
|
line = utils.AppendToBuffer(
|
||||||
|
self._buf,
|
||||||
|
f'{icon} Thread {thread.id}: {thread.thread["name"]} '
|
||||||
|
f'({thread.State()})' )
|
||||||
|
|
||||||
line = utils.AppendToBuffer(
|
if self._current_thread == thread.id:
|
||||||
self._buf,
|
signs.PlaceSign( self._current_thread_sign_id,
|
||||||
'{0} Thread: {1}'.format( icon, thread[ 'name' ] ) )
|
'VimspectorStackTrace',
|
||||||
|
'vimspectorCurrentThread',
|
||||||
|
self._buf.name,
|
||||||
|
line )
|
||||||
|
|
||||||
self._line_to_thread[ line ] = thread
|
self._line_to_thread[ line ] = thread
|
||||||
|
self._DrawStackTrace( thread )
|
||||||
|
|
||||||
self._DrawStackTrace( thread )
|
def _LoadStackTrace( self,
|
||||||
|
thread: Thread,
|
||||||
|
infer_current_frame,
|
||||||
|
reason = '' ):
|
||||||
|
|
||||||
def _LoadStackTrace( self, thread, infer_current_frame ):
|
|
||||||
def consume_stacktrace( message ):
|
def consume_stacktrace( message ):
|
||||||
thread[ '_frames' ] = message[ 'body' ][ 'stackFrames' ]
|
thread.Expand( message[ 'body' ][ 'stackFrames' ] )
|
||||||
if infer_current_frame:
|
if infer_current_frame:
|
||||||
for frame in thread[ '_frames' ]:
|
for frame in thread.stacktrace:
|
||||||
if self._JumpToFrame( frame ):
|
if self._JumpToFrame( frame, reason ):
|
||||||
break
|
break
|
||||||
|
|
||||||
self._DrawThreads()
|
self._DrawThreads()
|
||||||
|
|
@ -146,34 +328,116 @@ class StackTraceView( object ):
|
||||||
self._connection.DoRequest( consume_stacktrace, {
|
self._connection.DoRequest( consume_stacktrace, {
|
||||||
'command': 'stackTrace',
|
'command': 'stackTrace',
|
||||||
'arguments': {
|
'arguments': {
|
||||||
'threadId': thread[ 'id' ],
|
'threadId': thread.id,
|
||||||
}
|
}
|
||||||
} )
|
} )
|
||||||
|
|
||||||
def ExpandFrameOrThread( self ):
|
|
||||||
|
def _GetSelectedThread( self ) -> Thread:
|
||||||
if vim.current.buffer != self._buf:
|
if vim.current.buffer != self._buf:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self._line_to_thread.get( vim.current.window.cursor[ 0 ] )
|
||||||
|
|
||||||
|
|
||||||
|
def GetSelectedThreadId( self ):
|
||||||
|
thread = self._GetSelectedThread()
|
||||||
|
return thread.id if thread else thread
|
||||||
|
|
||||||
|
def _SetCurrentThread( self, thread: Thread ):
|
||||||
|
self._current_thread = thread.id
|
||||||
|
self._DrawThreads()
|
||||||
|
|
||||||
|
def SetCurrentThread( self ):
|
||||||
|
thread = self._GetSelectedThread()
|
||||||
|
if thread:
|
||||||
|
self._SetCurrentThread( thread )
|
||||||
|
elif vim.current.buffer != self._buf:
|
||||||
return
|
return
|
||||||
|
elif vim.current.window.cursor[ 0 ] in self._line_to_frame:
|
||||||
|
thread, frame = self._line_to_frame[ vim.current.window.cursor[ 0 ] ]
|
||||||
|
self._SetCurrentThread( thread )
|
||||||
|
self._JumpToFrame( frame )
|
||||||
|
else:
|
||||||
|
utils.UserMessage( "No thread selected" )
|
||||||
|
|
||||||
current_line = vim.current.window.cursor[ 0 ]
|
def ExpandFrameOrThread( self ):
|
||||||
|
thread = self._GetSelectedThread()
|
||||||
|
|
||||||
if current_line in self._line_to_frame:
|
if thread:
|
||||||
self._JumpToFrame( self._line_to_frame[ current_line ] )
|
if thread.IsExpanded():
|
||||||
elif current_line in self._line_to_thread:
|
thread.Collapse()
|
||||||
thread = self._line_to_thread[ current_line ]
|
self._DrawThreads()
|
||||||
if '_frames' in thread:
|
elif thread.CanExpand():
|
||||||
del thread[ '_frames' ]
|
|
||||||
with utils.RestoreCursorPosition():
|
|
||||||
self._DrawThreads()
|
|
||||||
else:
|
|
||||||
self._LoadStackTrace( thread, False )
|
self._LoadStackTrace( thread, False )
|
||||||
|
else:
|
||||||
|
utils.UserMessage( "Thread is not stopped" )
|
||||||
|
elif vim.current.buffer != self._buf:
|
||||||
|
return
|
||||||
|
elif vim.current.window.cursor[ 0 ] in self._line_to_frame:
|
||||||
|
thread, frame = self._line_to_frame[ vim.current.window.cursor[ 0 ] ]
|
||||||
|
self._JumpToFrame( frame )
|
||||||
|
|
||||||
def _JumpToFrame( self, frame ):
|
|
||||||
|
|
||||||
|
def _GetFrameOffset( self, delta ):
|
||||||
|
thread: Thread
|
||||||
|
for thread in self._threads:
|
||||||
|
if thread.id != self._current_thread:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not thread.stacktrace:
|
||||||
|
return
|
||||||
|
|
||||||
|
frame_idx = None
|
||||||
|
for index, frame in enumerate( thread.stacktrace ):
|
||||||
|
if frame == self._current_frame:
|
||||||
|
frame_idx = index
|
||||||
|
break
|
||||||
|
|
||||||
|
if frame_idx is not None:
|
||||||
|
target_idx = frame_idx + delta
|
||||||
|
if target_idx >= 0 and target_idx < len( thread.stacktrace ):
|
||||||
|
return thread.stacktrace[ target_idx ]
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def UpFrame( self ):
|
||||||
|
frame = self._GetFrameOffset( 1 )
|
||||||
|
if not frame:
|
||||||
|
utils.UserMessage( 'Top of stack' )
|
||||||
|
else:
|
||||||
|
self._JumpToFrame( frame, 'up' )
|
||||||
|
|
||||||
|
|
||||||
|
def DownFrame( self ):
|
||||||
|
frame = self._GetFrameOffset( -1 )
|
||||||
|
if not frame:
|
||||||
|
utils.UserMessage( 'Bottom of stack' )
|
||||||
|
else:
|
||||||
|
self._JumpToFrame( frame, 'down' )
|
||||||
|
|
||||||
|
|
||||||
|
def AnyThreadsRunning( self ):
|
||||||
|
for thread in self._threads:
|
||||||
|
if thread.state != Thread.TERMINATED:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _JumpToFrame( self, frame, reason = '' ):
|
||||||
def do_jump():
|
def do_jump():
|
||||||
if 'line' in frame and frame[ 'line' ]:
|
if 'line' in frame and frame[ 'line' ] > 0:
|
||||||
self._currentFrame = frame
|
# Should this set the current _Thread_ too ? If i jump to a frame in
|
||||||
return self._session.SetCurrentFrame( self._currentFrame )
|
# Thread 2, should that become the focussed thread ?
|
||||||
|
self._current_frame = frame
|
||||||
|
self._DrawThreads()
|
||||||
|
return self._session.SetCurrentFrame( self._current_frame, reason )
|
||||||
|
return False
|
||||||
|
|
||||||
source = frame[ 'source' ]
|
source = frame.get( 'source' ) or {}
|
||||||
if source.get( 'sourceReference', 0 ) > 0:
|
if source.get( 'sourceReference', 0 ) > 0:
|
||||||
def handle_resolved_source( resolved_source ):
|
def handle_resolved_source( resolved_source ):
|
||||||
frame[ 'source' ] = resolved_source
|
frame[ 'source' ] = resolved_source
|
||||||
|
|
@ -185,59 +449,94 @@ class StackTraceView( object ):
|
||||||
else:
|
else:
|
||||||
return do_jump()
|
return do_jump()
|
||||||
|
|
||||||
|
|
||||||
|
def PauseContinueThread( self ):
|
||||||
|
thread = self._GetSelectedThread()
|
||||||
|
if thread is None:
|
||||||
|
utils.UserMessage( 'No thread selected' )
|
||||||
|
elif thread.state == Thread.PAUSED:
|
||||||
|
self._session._connection.DoRequest(
|
||||||
|
lambda msg: self.OnContinued( {
|
||||||
|
'threadId': thread.id,
|
||||||
|
'allThreadsContinued': ( msg.get( 'body' ) or {} ).get(
|
||||||
|
'allThreadsContinued',
|
||||||
|
True )
|
||||||
|
} ),
|
||||||
|
{
|
||||||
|
'command': 'continue',
|
||||||
|
'arguments': {
|
||||||
|
'threadId': thread.id,
|
||||||
|
},
|
||||||
|
} )
|
||||||
|
elif thread.state == Thread.RUNNING:
|
||||||
|
self._session._connection.DoRequest( None, {
|
||||||
|
'command': 'pause',
|
||||||
|
'arguments': {
|
||||||
|
'threadId': thread.id,
|
||||||
|
},
|
||||||
|
} )
|
||||||
|
else:
|
||||||
|
utils.UserMessage(
|
||||||
|
f'Thread cannot be modified in state {thread.State()}' )
|
||||||
|
|
||||||
|
|
||||||
|
def OnContinued( self, event = None ):
|
||||||
|
threadId = None
|
||||||
|
allThreadsContinued = True
|
||||||
|
|
||||||
|
if event is not None:
|
||||||
|
threadId = event[ 'threadId' ]
|
||||||
|
allThreadsContinued = event.get( 'allThreadsContinued', False )
|
||||||
|
|
||||||
|
for thread in self._threads:
|
||||||
|
if allThreadsContinued:
|
||||||
|
thread.Continued()
|
||||||
|
elif thread.id == threadId:
|
||||||
|
thread.Continued()
|
||||||
|
break
|
||||||
|
|
||||||
|
self._DrawThreads()
|
||||||
|
|
||||||
def OnStopped( self, event ):
|
def OnStopped( self, event ):
|
||||||
if 'threadId' in event:
|
threadId = event.get( 'threadId' )
|
||||||
self._currentThread = event[ 'threadId' ]
|
allThreadsStopped = event.get( 'allThreadsStopped', False )
|
||||||
elif event.get( 'allThreadsStopped', False ) and self._threads:
|
|
||||||
self._currentThread = self._threads[ 0 ][ 'id' ]
|
|
||||||
|
|
||||||
if self._currentThread is not None:
|
# Work out if we should change the current thread
|
||||||
for thread in self._threads:
|
if threadId is not None:
|
||||||
if thread[ 'id' ] == self._currentThread:
|
self._current_thread = threadId
|
||||||
self._LoadStackTrace( thread, True )
|
elif self._current_thread is None and allThreadsStopped and self._threads:
|
||||||
return
|
self._current_thread = self._threads[ 0 ].id
|
||||||
|
|
||||||
self.LoadThreads( True )
|
self.LoadThreads( True, 'stopped', event )
|
||||||
|
|
||||||
def OnThreadEvent( self, event ):
|
def OnThreadEvent( self, event ):
|
||||||
if event[ 'reason' ] == 'started' and self._currentThread is None:
|
infer_current_frame = False
|
||||||
self._currentThread = event[ 'threadId' ]
|
if event[ 'reason' ] == 'started' and self._current_thread is None:
|
||||||
self.LoadThreads( True )
|
self._current_thread = event[ 'threadId' ]
|
||||||
|
infer_current_frame = True
|
||||||
|
|
||||||
def Continue( self ):
|
if event[ 'reason' ] == 'exited':
|
||||||
if self._currentThread is None:
|
for thread in self._threads:
|
||||||
utils.UserMessage( 'No current thread', persist = True )
|
if thread.id == event[ 'threadId' ]:
|
||||||
|
thread.Exited()
|
||||||
|
break
|
||||||
|
|
||||||
|
self.LoadThreads( infer_current_frame )
|
||||||
|
|
||||||
|
def OnExited( self, event ):
|
||||||
|
for thread in self._threads:
|
||||||
|
thread.Exited()
|
||||||
|
|
||||||
|
def _DrawStackTrace( self, thread: Thread ):
|
||||||
|
if not thread.IsExpanded():
|
||||||
return
|
return
|
||||||
|
|
||||||
self._session._connection.DoRequest( None, {
|
if self._current_frame_sign_id:
|
||||||
'command': 'continue',
|
signs.UnplaceSign( self._current_frame_sign_id, 'VimspectorStackTrace' )
|
||||||
'arguments': {
|
else:
|
||||||
'threadId': self._currentThread,
|
self._current_frame_sign_id = 2
|
||||||
},
|
|
||||||
} )
|
|
||||||
|
|
||||||
self._session.ClearCurrentFrame()
|
for frame in thread.stacktrace:
|
||||||
self.LoadThreads( True )
|
|
||||||
|
|
||||||
def Pause( self ):
|
|
||||||
if self._currentThread is None:
|
|
||||||
utils.UserMessage( 'No current thread', persist = True )
|
|
||||||
return
|
|
||||||
|
|
||||||
self._session._connection.DoRequest( None, {
|
|
||||||
'command': 'pause',
|
|
||||||
'arguments': {
|
|
||||||
'threadId': self._currentThread,
|
|
||||||
},
|
|
||||||
} )
|
|
||||||
|
|
||||||
def _DrawStackTrace( self, thread ):
|
|
||||||
if '_frames' not in thread:
|
|
||||||
return
|
|
||||||
|
|
||||||
stackFrames = thread[ '_frames' ]
|
|
||||||
|
|
||||||
for frame in stackFrames:
|
|
||||||
if frame.get( 'source' ):
|
if frame.get( 'source' ):
|
||||||
source = frame[ 'source' ]
|
source = frame[ 'source' ]
|
||||||
else:
|
else:
|
||||||
|
|
@ -261,7 +560,15 @@ class StackTraceView( object ):
|
||||||
source[ 'name' ],
|
source[ 'name' ],
|
||||||
frame[ 'line' ] ) )
|
frame[ 'line' ] ) )
|
||||||
|
|
||||||
self._line_to_frame[ line ] = frame
|
if ( self._current_frame is not None and
|
||||||
|
self._current_frame[ 'id' ] == frame[ 'id' ] ):
|
||||||
|
signs.PlaceSign( self._current_frame_sign_id,
|
||||||
|
'VimspectorStackTrace',
|
||||||
|
'vimspectorCurrentFrame',
|
||||||
|
self._buf.name,
|
||||||
|
line )
|
||||||
|
|
||||||
|
self._line_to_frame[ line ] = ( thread, frame )
|
||||||
|
|
||||||
def _ResolveSource( self, source, and_then ):
|
def _ResolveSource( self, source, and_then ):
|
||||||
source_reference = int( source[ 'sourceReference' ] )
|
source_reference = int( source[ 'sourceReference' ] )
|
||||||
|
|
@ -274,12 +581,14 @@ class StackTraceView( object ):
|
||||||
def consume_source( msg ):
|
def consume_source( msg ):
|
||||||
self._sources[ source_reference ] = source
|
self._sources[ source_reference ] = source
|
||||||
|
|
||||||
buf_name = os.path.join( '_vimspector_tmp', source[ 'name' ] )
|
buf_name = os.path.join( '_vimspector_tmp',
|
||||||
|
source.get( 'path', source[ 'name' ] ) )
|
||||||
|
|
||||||
self._logger.debug( "Received source %s: %s", buf_name, msg )
|
self._logger.debug( "Received source %s: %s", buf_name, msg )
|
||||||
|
|
||||||
buf = utils.BufferForFile( buf_name )
|
buf = utils.BufferForFile( buf_name )
|
||||||
utils.SetUpScratchBuffer( buf, buf_name )
|
self._scratch_buffers.append( buf )
|
||||||
|
utils.SetUpHiddenBuffer( buf, buf_name )
|
||||||
source[ 'path' ] = buf_name
|
source[ 'path' ] = buf_name
|
||||||
with utils.ModifiableScratchBuffer( buf ):
|
with utils.ModifiableScratchBuffer( buf ):
|
||||||
utils.SetBufferContents( buf, msg[ 'body' ][ 'content' ] )
|
utils.SetBufferContents( buf, msg[ 'body' ][ 'content' ] )
|
||||||
|
|
@ -293,3 +602,8 @@ class StackTraceView( object ):
|
||||||
'source': source
|
'source': source
|
||||||
}
|
}
|
||||||
} )
|
} )
|
||||||
|
|
||||||
|
def SetSyntax( self, syntax ):
|
||||||
|
self._current_syntax = utils.SetSyntax( self._current_syntax,
|
||||||
|
syntax,
|
||||||
|
self._buf )
|
||||||
|
|
|
||||||
134
python3/vimspector/terminal.py
Normal file
134
python3/vimspector/terminal.py
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
from vimspector import utils, settings
|
||||||
|
|
||||||
|
import os
|
||||||
|
import vim
|
||||||
|
|
||||||
|
|
||||||
|
class Terminal:
|
||||||
|
window = None
|
||||||
|
buffer_number: int = None
|
||||||
|
|
||||||
|
|
||||||
|
def LaunchTerminal( api_prefix,
|
||||||
|
params,
|
||||||
|
window_for_start,
|
||||||
|
existing_term ):
|
||||||
|
if not existing_term:
|
||||||
|
term = Terminal()
|
||||||
|
else:
|
||||||
|
term = existing_term
|
||||||
|
|
||||||
|
cwd = params[ 'cwd' ] or os.getcwd()
|
||||||
|
args = params[ 'args' ] or []
|
||||||
|
env = params.get( 'env' ) or {}
|
||||||
|
|
||||||
|
term_options = {
|
||||||
|
'norestore': 1,
|
||||||
|
'cwd': cwd,
|
||||||
|
'env': env,
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Get( 'ui_mode' ) == 'horizontal':
|
||||||
|
# force-horizontal
|
||||||
|
term_options[ 'vertical' ] = 1
|
||||||
|
elif utils.GetVimValue( vim.vars[ 'vimspector_session_windows' ],
|
||||||
|
'mode' ) == 'horizontal':
|
||||||
|
# horizontal, which means that we should have enough space for:
|
||||||
|
# - sidebar
|
||||||
|
# - code min
|
||||||
|
# - term min width
|
||||||
|
# - + 2 vertical spaders
|
||||||
|
# - + 3 columns for signs
|
||||||
|
term_options[ 'vertical' ] = 1
|
||||||
|
|
||||||
|
# if we don't have enough space for terminal_maxwidth, then see if we have
|
||||||
|
# enough vertically for terminal_maxheight, in which case,
|
||||||
|
# that seems a better fit
|
||||||
|
term_horiz_max = ( settings.Int( 'sidebar_width' ) +
|
||||||
|
1 + 2 + 3 +
|
||||||
|
settings.Int( 'code_minwidth' ) +
|
||||||
|
1 + settings.Int( 'terminal_maxwidth' ) )
|
||||||
|
term_vert_max = ( settings.Int( 'bottombar_height' ) + 1 +
|
||||||
|
settings.Int( 'code_minheight' ) + 1 +
|
||||||
|
settings.Int( 'terminal_minheight' ) )
|
||||||
|
|
||||||
|
if ( vim.options[ 'columns' ] < term_horiz_max and
|
||||||
|
vim.options[ 'lines' ] >= term_vert_max ):
|
||||||
|
# Looks like it, let's try that layout
|
||||||
|
term_options[ 'vertical' ] = 0
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
# vertical - we need enough space horizontally for the code+terminal, but we
|
||||||
|
# may fit better with code above terminal
|
||||||
|
term_options[ 'vertical' ] = 0
|
||||||
|
|
||||||
|
term_horiz_max = ( settings.Int( 'code_minwidth' ) + 3 +
|
||||||
|
settings.Int( 'terminal_maxwidth' ) + 1 )
|
||||||
|
|
||||||
|
if vim.options[ 'columns' ] > term_horiz_max:
|
||||||
|
term_options[ 'vertical' ] = 1
|
||||||
|
|
||||||
|
|
||||||
|
if not window_for_start or not window_for_start.valid:
|
||||||
|
# TOOD: Where? Maybe we should just use botright vertical ...
|
||||||
|
window_for_start = vim.current.window
|
||||||
|
|
||||||
|
if term.window is not None and term.window.valid:
|
||||||
|
assert term.buffer_number
|
||||||
|
window_for_start = term.window
|
||||||
|
if ( term.window.buffer.number == term.buffer_number
|
||||||
|
and int( utils.Call( 'vimspector#internal#{}term#IsFinished'.format(
|
||||||
|
api_prefix ),
|
||||||
|
term.buffer_number ) ) ):
|
||||||
|
term_options[ 'curwin' ] = 1
|
||||||
|
else:
|
||||||
|
term_options[ 'vertical' ] = 0
|
||||||
|
|
||||||
|
buffer_number = None
|
||||||
|
terminal_window = None
|
||||||
|
with utils.LetCurrentWindow( window_for_start ):
|
||||||
|
# If we're making a vertical split from the code window, make it no more
|
||||||
|
# than 80 columns and no fewer than 10. Also try and keep the code window
|
||||||
|
# at least 82 columns
|
||||||
|
if term_options.get( 'curwin', 0 ):
|
||||||
|
pass
|
||||||
|
elif term_options[ 'vertical' ]:
|
||||||
|
term_options[ 'term_cols' ] = max(
|
||||||
|
min ( int( vim.eval( 'winwidth( 0 )' ) )
|
||||||
|
- settings.Int( 'code_minwidth' ),
|
||||||
|
settings.Int( 'terminal_maxwidth' ) ),
|
||||||
|
settings.Int( 'terminal_minwidth' )
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
term_options[ 'term_rows' ] = max(
|
||||||
|
min ( int( vim.eval( 'winheight( 0 )' ) )
|
||||||
|
- settings.Int( 'code_minheight' ),
|
||||||
|
settings.Int( 'terminal_maxheight' ) ),
|
||||||
|
settings.Int( 'terminal_minheight' )
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
buffer_number = int(
|
||||||
|
utils.Call(
|
||||||
|
'vimspector#internal#{}term#Start'.format( api_prefix ),
|
||||||
|
args,
|
||||||
|
term_options ) )
|
||||||
|
terminal_window = vim.current.window
|
||||||
|
|
||||||
|
if buffer_number is None or buffer_number <= 0:
|
||||||
|
# TODO: Do something better like reject the request?
|
||||||
|
raise ValueError( "Unable to start terminal" )
|
||||||
|
|
||||||
|
term.window = terminal_window
|
||||||
|
term.buffer_number = buffer_number
|
||||||
|
|
||||||
|
vim.vars[ 'vimspector_session_windows' ][ 'terminal' ] = utils.WindowID(
|
||||||
|
term.window,
|
||||||
|
vim.current.tabpage )
|
||||||
|
with utils.RestoreCursorPosition():
|
||||||
|
with utils.RestoreCurrentWindow():
|
||||||
|
with utils.RestoreCurrentBuffer( vim.current.window ):
|
||||||
|
vim.command( 'doautocmd User VimspectorTerminalOpened' )
|
||||||
|
|
||||||
|
return term
|
||||||
|
|
@ -19,10 +19,18 @@ import os
|
||||||
import contextlib
|
import contextlib
|
||||||
import vim
|
import vim
|
||||||
import json
|
import json
|
||||||
import string
|
import functools
|
||||||
|
import subprocess
|
||||||
|
import shlex
|
||||||
|
import collections
|
||||||
|
import re
|
||||||
|
import typing
|
||||||
|
|
||||||
|
|
||||||
|
LOG_FILE = os.path.expanduser( os.path.join( '~', '.vimspector.log' ) )
|
||||||
|
|
||||||
|
_log_handler = logging.FileHandler( LOG_FILE, mode = 'w' )
|
||||||
|
|
||||||
_log_handler = logging.FileHandler( os.path.expanduser( '~/.vimspector.log' ),
|
|
||||||
mode = 'w' )
|
|
||||||
_log_handler.setFormatter(
|
_log_handler.setFormatter(
|
||||||
logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' ) )
|
logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' ) )
|
||||||
|
|
||||||
|
|
@ -37,18 +45,38 @@ _logger = logging.getLogger( __name__ )
|
||||||
SetUpLogging( _logger )
|
SetUpLogging( _logger )
|
||||||
|
|
||||||
|
|
||||||
def BufferNumberForFile( file_name ):
|
def BufferNumberForFile( file_name, create = True ):
|
||||||
return int( vim.eval( 'bufnr( "{0}", 1 )'.format( file_name ) ) )
|
return int( vim.eval( "bufnr( '{0}', {1} )".format(
|
||||||
|
Escape( file_name ),
|
||||||
|
int( create ) ) ) )
|
||||||
|
|
||||||
|
|
||||||
def BufferForFile( file_name ):
|
def BufferForFile( file_name ):
|
||||||
return vim.buffers[ BufferNumberForFile( file_name ) ]
|
return vim.buffers[ BufferNumberForFile( file_name ) ]
|
||||||
|
|
||||||
|
|
||||||
|
def BufferExists( file_name ):
|
||||||
|
return bool( int ( vim.eval( f"bufexists( '{ Escape( file_name ) }' )" ) ) )
|
||||||
|
|
||||||
|
|
||||||
|
def NewEmptyBuffer():
|
||||||
|
bufnr = int( vim.eval( 'bufadd("")' ) )
|
||||||
|
Call( 'bufload', bufnr )
|
||||||
|
return vim.buffers[ bufnr ]
|
||||||
|
|
||||||
|
|
||||||
|
def WindowForBuffer( buf ):
|
||||||
|
for w in vim.current.tabpage.windows:
|
||||||
|
if w.buffer == buf:
|
||||||
|
return w
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def OpenFileInCurrentWindow( file_name ):
|
def OpenFileInCurrentWindow( file_name ):
|
||||||
buffer_number = BufferNumberForFile( file_name )
|
buffer_number = BufferNumberForFile( file_name )
|
||||||
try:
|
try:
|
||||||
vim.command( 'bu {0}'.format( buffer_number ) )
|
vim.current.buffer = vim.buffers[ buffer_number ]
|
||||||
except vim.error as e:
|
except vim.error as e:
|
||||||
if 'E325' not in str( e ):
|
if 'E325' not in str( e ):
|
||||||
raise
|
raise
|
||||||
|
|
@ -56,36 +84,50 @@ def OpenFileInCurrentWindow( file_name ):
|
||||||
return vim.buffers[ buffer_number ]
|
return vim.buffers[ buffer_number ]
|
||||||
|
|
||||||
|
|
||||||
def SetUpCommandBuffer( cmd, name ):
|
COMMAND_HANDLERS = {}
|
||||||
bufs = vim.bindeval(
|
|
||||||
'vimspector#internal#job#StartCommandWithLog( {}, "{}" )'.format(
|
|
||||||
json.dumps( cmd ),
|
|
||||||
name ) )
|
|
||||||
|
|
||||||
if bufs is None:
|
|
||||||
|
def OnCommandWithLogComplete( name, exit_code ):
|
||||||
|
cb = COMMAND_HANDLERS.get( name )
|
||||||
|
if cb:
|
||||||
|
cb( exit_code )
|
||||||
|
|
||||||
|
|
||||||
|
def SetUpCommandBuffer( cmd, name, api_prefix, completion_handler = None ):
|
||||||
|
COMMAND_HANDLERS[ name ] = completion_handler
|
||||||
|
|
||||||
|
buf = Call( f'vimspector#internal#{api_prefix}job#StartCommandWithLog',
|
||||||
|
cmd,
|
||||||
|
name )
|
||||||
|
|
||||||
|
if buf is None:
|
||||||
raise RuntimeError( "Unable to start job {}: {}".format( cmd, name ) )
|
raise RuntimeError( "Unable to start job {}: {}".format( cmd, name ) )
|
||||||
elif not all( b > 0 for b in bufs ):
|
elif int( buf ) <= 0:
|
||||||
raise RuntimeError( "Unable to get all streams for job {}: {}".format(
|
raise RuntimeError( "Unable to get all streams for job {}: {}".format(
|
||||||
name,
|
name,
|
||||||
cmd ) )
|
cmd ) )
|
||||||
|
|
||||||
return [ vim.buffers[ b ] for b in bufs ]
|
return vim.buffers[ int( buf ) ]
|
||||||
|
|
||||||
|
|
||||||
def CleanUpCommand( name ):
|
def CleanUpCommand( name, api_prefix ):
|
||||||
return vim.eval( 'vimspector#internal#job#CleanUpCommand( "{}" )'.format(
|
return vim.eval( 'vimspector#internal#{}job#CleanUpCommand( "{}" )'.format(
|
||||||
|
api_prefix,
|
||||||
name ) )
|
name ) )
|
||||||
|
|
||||||
|
|
||||||
|
def CleanUpHiddenBuffer( buf ):
|
||||||
|
try:
|
||||||
|
vim.command( 'bdelete! {}'.format( buf.number ) )
|
||||||
|
except vim.error as e:
|
||||||
|
# FIXME: For now just ignore the "no buffers were deleted" error
|
||||||
|
if 'E516' not in str( e ):
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def SetUpScratchBuffer( buf, name ):
|
def SetUpScratchBuffer( buf, name ):
|
||||||
buf.options[ 'buftype' ] = 'nofile'
|
SetUpHiddenBuffer( buf, name )
|
||||||
buf.options[ 'swapfile' ] = False
|
|
||||||
buf.options[ 'modifiable' ] = False
|
|
||||||
buf.options[ 'modified' ] = False
|
|
||||||
buf.options[ 'readonly' ] = True
|
|
||||||
buf.options[ 'buflisted' ] = False
|
|
||||||
buf.options[ 'bufhidden' ] = 'wipe'
|
buf.options[ 'bufhidden' ] = 'wipe'
|
||||||
buf.name = name
|
|
||||||
|
|
||||||
|
|
||||||
def SetUpHiddenBuffer( buf, name ):
|
def SetUpHiddenBuffer( buf, name ):
|
||||||
|
|
@ -99,10 +141,10 @@ def SetUpHiddenBuffer( buf, name ):
|
||||||
buf.name = name
|
buf.name = name
|
||||||
|
|
||||||
|
|
||||||
def SetUpPromptBuffer( buf, name, prompt, callback, hidden=False ):
|
def SetUpPromptBuffer( buf, name, prompt, callback, omnifunc ):
|
||||||
# This feature is _super_ new, so only enable when available
|
# This feature is _super_ new, so only enable when available
|
||||||
if not int( vim.eval( "exists( '*prompt_setprompt' )" ) ):
|
if not Exists( '*prompt_setprompt' ):
|
||||||
return SetUpScratchBuffer( buf, name )
|
return SetUpHiddenBuffer( buf, name )
|
||||||
|
|
||||||
buf.options[ 'buftype' ] = 'prompt'
|
buf.options[ 'buftype' ] = 'prompt'
|
||||||
buf.options[ 'swapfile' ] = False
|
buf.options[ 'swapfile' ] = False
|
||||||
|
|
@ -110,7 +152,9 @@ def SetUpPromptBuffer( buf, name, prompt, callback, hidden=False ):
|
||||||
buf.options[ 'modified' ] = False
|
buf.options[ 'modified' ] = False
|
||||||
buf.options[ 'readonly' ] = False
|
buf.options[ 'readonly' ] = False
|
||||||
buf.options[ 'buflisted' ] = False
|
buf.options[ 'buflisted' ] = False
|
||||||
buf.options[ 'bufhidden' ] = 'wipe' if not hidden else 'hide'
|
buf.options[ 'bufhidden' ] = 'hide'
|
||||||
|
buf.options[ 'textwidth' ] = 0
|
||||||
|
buf.options[ 'omnifunc' ] = omnifunc
|
||||||
buf.name = name
|
buf.name = name
|
||||||
|
|
||||||
vim.eval( "prompt_setprompt( {0}, '{1}' )".format( buf.number,
|
vim.eval( "prompt_setprompt( {0}, '{1}' )".format( buf.number,
|
||||||
|
|
@ -119,6 +163,20 @@ def SetUpPromptBuffer( buf, name, prompt, callback, hidden=False ):
|
||||||
buf.number,
|
buf.number,
|
||||||
Escape( callback ) ) )
|
Escape( callback ) ) )
|
||||||
|
|
||||||
|
# This serves a few purposes, mainly to ensure that completion systems have
|
||||||
|
# something to work with. In particular it makes YCM use its identifier engine
|
||||||
|
# and you can config ycm to trigger semantic (annoyingly, synchronously) using
|
||||||
|
# some let g:ycm_auto_trggier
|
||||||
|
Call( 'setbufvar', buf.number, '&filetype', 'VimspectorPrompt' )
|
||||||
|
|
||||||
|
|
||||||
|
def SetUpUIWindow( win ):
|
||||||
|
win.options[ 'wrap' ] = False
|
||||||
|
win.options[ 'number' ] = False
|
||||||
|
win.options[ 'relativenumber' ] = False
|
||||||
|
win.options[ 'signcolumn' ] = 'no'
|
||||||
|
win.options[ 'spell' ] = False
|
||||||
|
win.options[ 'list' ] = False
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
|
|
@ -155,20 +213,40 @@ def RestoreCurrentWindow():
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
vim.current.tabpage = old_tabpage
|
if old_tabpage.valid and old_window.valid:
|
||||||
vim.current.window = old_window
|
vim.current.tabpage = old_tabpage
|
||||||
|
vim.current.window = old_window
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def RestoreCurrentBuffer( window ):
|
def RestoreCurrentBuffer( window ):
|
||||||
# TODO: Don't trigger autoccommands when shifting buffers
|
|
||||||
old_buffer = window.buffer
|
old_buffer = window.buffer
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
with RestoreCurrentWindow():
|
if window.valid:
|
||||||
vim.current.window = window
|
with RestoreCurrentWindow():
|
||||||
vim.current.buffer = old_buffer
|
vim.current.window = window
|
||||||
|
vim.current.buffer = old_buffer
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def AnyWindowForBuffer( buf ):
|
||||||
|
# Only checks the current tab page, which is what we want
|
||||||
|
current_win = WindowForBuffer( buf )
|
||||||
|
if current_win is not None:
|
||||||
|
with LetCurrentWindow( current_win ):
|
||||||
|
yield
|
||||||
|
else:
|
||||||
|
with LetCurrentBuffer( buf ):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def LetCurrentTabpage( tabpage ):
|
||||||
|
with RestoreCurrentWindow():
|
||||||
|
vim.current.tabpage = tabpage
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
|
|
@ -178,6 +256,14 @@ def LetCurrentWindow( window ):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def LetCurrentBuffer( buf ):
|
||||||
|
with RestoreCursorPosition():
|
||||||
|
with RestoreCurrentBuffer( vim.current.window ):
|
||||||
|
vim.current.buffer = buf
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
def JumpToWindow( window ):
|
def JumpToWindow( window ):
|
||||||
vim.current.tabpage = window.tabpage
|
vim.current.tabpage = window.tabpage
|
||||||
vim.current.window = window
|
vim.current.window = window
|
||||||
|
|
@ -207,8 +293,12 @@ def TemporaryVimOption( opt, value ):
|
||||||
vim.options[ opt ] = old_value
|
vim.options[ opt ] = old_value
|
||||||
|
|
||||||
|
|
||||||
def PathToConfigFile( file_name ):
|
def PathToConfigFile( file_name, from_directory = None ):
|
||||||
p = os.getcwd()
|
if not from_directory:
|
||||||
|
p = os.getcwd()
|
||||||
|
else:
|
||||||
|
p = os.path.abspath( os.path.realpath( from_directory ) )
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
candidate = os.path.join( p, file_name )
|
candidate = os.path.join( p, file_name )
|
||||||
if os.path.exists( candidate ):
|
if os.path.exists( candidate ):
|
||||||
|
|
@ -224,16 +314,21 @@ def Escape( msg ):
|
||||||
return msg.replace( "'", "''" )
|
return msg.replace( "'", "''" )
|
||||||
|
|
||||||
|
|
||||||
def UserMessage( msg, persist=False ):
|
def UserMessage( msg, persist=False, error=False ):
|
||||||
if persist:
|
if persist:
|
||||||
_logger.warning( 'User Msg: ' + msg )
|
_logger.warning( 'User Msg: ' + msg )
|
||||||
else:
|
else:
|
||||||
_logger.info( 'User Msg: ' + msg )
|
_logger.info( 'User Msg: ' + msg )
|
||||||
|
|
||||||
vim.command( 'redraw' )
|
|
||||||
cmd = 'echom' if persist else 'echo'
|
cmd = 'echom' if persist else 'echo'
|
||||||
for line in msg.split( '\n' ):
|
vim.command( 'redraw' )
|
||||||
vim.command( "{0} '{1}'".format( cmd, Escape( line ) ) )
|
try:
|
||||||
|
if error:
|
||||||
|
vim.command( "echohl WarningMsg" )
|
||||||
|
for line in msg.split( '\n' ):
|
||||||
|
vim.command( "{0} '{1}'".format( cmd, Escape( line ) ) )
|
||||||
|
finally:
|
||||||
|
vim.command( 'echohl None' ) if error else None
|
||||||
vim.command( 'redraw' )
|
vim.command( 'redraw' )
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -257,25 +352,69 @@ def SelectFromList( prompt, options ):
|
||||||
if selection < 0 or selection >= len( options ):
|
if selection < 0 or selection >= len( options ):
|
||||||
return None
|
return None
|
||||||
return options[ selection ]
|
return options[ selection ]
|
||||||
except KeyboardInterrupt:
|
except ( KeyboardInterrupt, vim.error ):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def AskForInput( prompt, default_value = None ):
|
def AskForInput( prompt, default_value = None, completion = None ):
|
||||||
if default_value is None:
|
if default_value is None:
|
||||||
default_option = ''
|
default_value = ''
|
||||||
else:
|
|
||||||
default_option = ", '{}'".format( Escape( default_value ) )
|
args = [ prompt, default_value ]
|
||||||
|
|
||||||
|
if completion is not None:
|
||||||
|
if completion == 'expr':
|
||||||
|
args.append( 'custom,vimspector#CompleteExpr' )
|
||||||
|
else:
|
||||||
|
args.append( completion )
|
||||||
|
|
||||||
with InputSave():
|
with InputSave():
|
||||||
try:
|
try:
|
||||||
return vim.eval( "input( '{}' {} )".format( Escape( prompt ),
|
return Call( 'input', *args )
|
||||||
default_option ) )
|
except ( KeyboardInterrupt, vim.error ):
|
||||||
except KeyboardInterrupt:
|
return None
|
||||||
return ''
|
|
||||||
|
|
||||||
|
CONFIRM = {}
|
||||||
|
CONFIRM_ID = 0
|
||||||
|
|
||||||
|
|
||||||
|
def ConfirmCallback( confirm_id, result ):
|
||||||
|
try:
|
||||||
|
handler = CONFIRM.pop( confirm_id )
|
||||||
|
except KeyError:
|
||||||
|
UserMessage( f"Internal error: unexpected callback id { confirm_id }",
|
||||||
|
persist = True,
|
||||||
|
error = True )
|
||||||
|
return
|
||||||
|
|
||||||
|
handler( result )
|
||||||
|
|
||||||
|
|
||||||
|
def Confirm( api_prefix,
|
||||||
|
prompt,
|
||||||
|
handler,
|
||||||
|
default_value = 2,
|
||||||
|
options: list = None,
|
||||||
|
keys: list = None ):
|
||||||
|
if not options:
|
||||||
|
options = [ '(Y)es', '(N)o' ]
|
||||||
|
if not keys:
|
||||||
|
keys = [ 'y', 'n' ]
|
||||||
|
|
||||||
|
global CONFIRM_ID
|
||||||
|
CONFIRM_ID += 1
|
||||||
|
CONFIRM[ CONFIRM_ID ] = handler
|
||||||
|
Call( f'vimspector#internal#{ api_prefix }popup#Confirm',
|
||||||
|
CONFIRM_ID,
|
||||||
|
prompt,
|
||||||
|
options,
|
||||||
|
default_value,
|
||||||
|
keys )
|
||||||
|
|
||||||
|
|
||||||
def AppendToBuffer( buf, line_or_lines, modified=False ):
|
def AppendToBuffer( buf, line_or_lines, modified=False ):
|
||||||
|
line = 1
|
||||||
try:
|
try:
|
||||||
# After clearing the buffer (using buf[:] = None) there is always a single
|
# After clearing the buffer (using buf[:] = None) there is always a single
|
||||||
# empty line in the buffer object and no "is empty" method.
|
# empty line in the buffer object and no "is empty" method.
|
||||||
|
|
@ -302,8 +441,10 @@ def AppendToBuffer( buf, line_or_lines, modified=False ):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def ClearBuffer( buf ):
|
def ClearBuffer( buf, modified = False ):
|
||||||
buf[ : ] = None
|
buf[ : ] = None
|
||||||
|
if not modified:
|
||||||
|
buf.options[ 'modified' ] = False
|
||||||
|
|
||||||
|
|
||||||
def SetBufferContents( buf, lines, modified=False ):
|
def SetBufferContents( buf, lines, modified=False ):
|
||||||
|
|
@ -311,7 +452,7 @@ def SetBufferContents( buf, lines, modified=False ):
|
||||||
if not isinstance( lines, list ):
|
if not isinstance( lines, list ):
|
||||||
lines = lines.splitlines()
|
lines = lines.splitlines()
|
||||||
|
|
||||||
buf[:] = lines
|
buf[ : ] = lines
|
||||||
finally:
|
finally:
|
||||||
buf.options[ 'modified' ] = modified
|
buf.options[ 'modified' ] = modified
|
||||||
|
|
||||||
|
|
@ -320,77 +461,195 @@ def IsCurrent( window, buf ):
|
||||||
return vim.current.window == window and vim.current.window.buffer == buf
|
return vim.current.window == window and vim.current.window.buffer == buf
|
||||||
|
|
||||||
|
|
||||||
# TODO: Should we just run the substitution on the whole JSON string instead?
|
def ExpandReferencesInObject( obj, mapping, calculus, user_choices ):
|
||||||
# That woul dallow expansion in bool and number values, such as ports etc. ?
|
if isinstance( obj, dict ):
|
||||||
def ExpandReferencesInDict( obj, mapping, user_choices ):
|
ExpandReferencesInDict( obj, mapping, calculus, user_choices )
|
||||||
def expand_refs_in_string( orig_s ):
|
elif isinstance( obj, list ):
|
||||||
s = os.path.expanduser( orig_s )
|
j_offset = 0
|
||||||
s = os.path.expandvars( s )
|
obj_copy = list( obj )
|
||||||
|
|
||||||
# Parse any variables passed in in mapping, and ask for any that weren't,
|
for i, _ in enumerate( obj_copy ):
|
||||||
# storing the result in mapping
|
j = i + j_offset
|
||||||
bug_catcher = 0
|
if ( isinstance( obj_copy[ i ], str ) and
|
||||||
while bug_catcher < 100:
|
len( obj_copy[ i ] ) > 2 and
|
||||||
++bug_catcher
|
obj_copy[ i ][ 0:2 ] == '*$' ):
|
||||||
|
# *${something} - expand list in place
|
||||||
|
value = ExpandReferencesInString( obj_copy[ i ][ 1: ],
|
||||||
|
mapping,
|
||||||
|
calculus,
|
||||||
|
user_choices )
|
||||||
|
obj.pop( j )
|
||||||
|
j_offset -= 1
|
||||||
|
for opt_index, opt in enumerate( shlex.split( value ) ):
|
||||||
|
obj.insert( j + opt_index, opt )
|
||||||
|
j_offset += 1
|
||||||
|
else:
|
||||||
|
obj[ j ] = ExpandReferencesInObject( obj_copy[ i ],
|
||||||
|
mapping,
|
||||||
|
calculus,
|
||||||
|
user_choices )
|
||||||
|
elif isinstance( obj, str ):
|
||||||
|
obj = ExpandReferencesInString( obj, mapping, calculus, user_choices )
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
# Based on the python standard library string.Template().substitue, enhanced to
|
||||||
|
# add ${name:default} parsing, and to remove the unnecessary generality.
|
||||||
|
VAR_MATCH = re.compile(
|
||||||
|
r"""
|
||||||
|
\$(?: # A dollar, followed by...
|
||||||
|
(?P<escaped>\$) | # Another doller = escaped
|
||||||
|
(?P<named>[_a-z][_a-z0-9]*) | # or An identifier - named param
|
||||||
|
{(?P<braced>[_a-z][_a-z0-9]*)} | # or An {identifier} - braced param
|
||||||
|
{(?P<braceddefault> # or An {id:default} - default param, as
|
||||||
|
(?P<defname>[_a-z][_a-z0-9]*) # an ID
|
||||||
|
: # then a colon
|
||||||
|
(?P<default>(?:\\}|[^}])*) # then anything up to }, or a \}
|
||||||
|
)} | # then a }
|
||||||
|
(?P<invalid>) # or Something else - invalid
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
re.IGNORECASE | re.VERBOSE )
|
||||||
|
|
||||||
|
|
||||||
|
class MissingSubstitution( Exception ):
|
||||||
|
def __init__( self, name, default_value = None ):
|
||||||
|
self.name = name
|
||||||
|
self.default_value = default_value
|
||||||
|
|
||||||
|
|
||||||
|
def _Substitute( template, mapping ):
|
||||||
|
def convert( mo ):
|
||||||
|
# Check the most common path first.
|
||||||
|
named = mo.group( 'named' ) or mo.group( 'braced' )
|
||||||
|
if named is not None:
|
||||||
|
if named not in mapping:
|
||||||
|
raise MissingSubstitution( named )
|
||||||
|
return str( mapping[ named ] )
|
||||||
|
|
||||||
|
if mo.group( 'escaped' ) is not None:
|
||||||
|
return '$'
|
||||||
|
|
||||||
|
if mo.group( 'braceddefault' ) is not None:
|
||||||
|
named = mo.group( 'defname' )
|
||||||
|
if named not in mapping:
|
||||||
|
raise MissingSubstitution(
|
||||||
|
named,
|
||||||
|
mo.group( 'default' ).replace( '\\}', '}' ) )
|
||||||
|
return str( mapping[ named ] )
|
||||||
|
|
||||||
|
if mo.group( 'invalid' ) is not None:
|
||||||
|
raise ValueError( f"Invalid placeholder in string { template }" )
|
||||||
|
|
||||||
|
raise ValueError( 'Unrecognized named group in pattern', VAR_MATCH )
|
||||||
|
|
||||||
|
return VAR_MATCH.sub( convert, template )
|
||||||
|
|
||||||
|
|
||||||
|
def ExpandReferencesInString( orig_s,
|
||||||
|
mapping,
|
||||||
|
calculus,
|
||||||
|
user_choices ):
|
||||||
|
s = os.path.expanduser( orig_s )
|
||||||
|
s = os.path.expandvars( s )
|
||||||
|
|
||||||
|
# Parse any variables passed in in mapping, and ask for any that weren't,
|
||||||
|
# storing the result in mapping
|
||||||
|
bug_catcher = 0
|
||||||
|
while bug_catcher < 100:
|
||||||
|
++bug_catcher
|
||||||
|
|
||||||
|
try:
|
||||||
|
s = _Substitute( s, mapping )
|
||||||
|
break
|
||||||
|
except MissingSubstitution as e:
|
||||||
|
key = e.name
|
||||||
|
|
||||||
|
if key in calculus:
|
||||||
|
mapping[ key ] = calculus[ key ]()
|
||||||
|
else:
|
||||||
|
default_value = user_choices.get( key )
|
||||||
|
# Allow _one_ level of additional substitution. This allows a very real
|
||||||
|
# use case of "program": ${prgram:${file\\}}
|
||||||
|
if default_value is None and e.default_value is not None:
|
||||||
|
try:
|
||||||
|
default_value = _Substitute( e.default_value, mapping )
|
||||||
|
except MissingSubstitution as e2:
|
||||||
|
if e2.name in calculus:
|
||||||
|
default_value = calculus[ e2.name ]()
|
||||||
|
else:
|
||||||
|
default_value = e.default_value
|
||||||
|
|
||||||
try:
|
|
||||||
s = string.Template( s ).substitute( mapping )
|
|
||||||
break
|
|
||||||
except KeyError as e:
|
|
||||||
# HACK: This is seemingly the only way to get the key. str( e ) returns
|
|
||||||
# the key surrounded by '' for unknowable reasons.
|
|
||||||
key = e.args[ 0 ]
|
|
||||||
default_value = user_choices.get( key, None )
|
|
||||||
mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ),
|
mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ),
|
||||||
default_value )
|
default_value )
|
||||||
|
|
||||||
|
if mapping[ key ] is None:
|
||||||
|
raise KeyboardInterrupt
|
||||||
|
|
||||||
user_choices[ key ] = mapping[ key ]
|
user_choices[ key ] = mapping[ key ]
|
||||||
_logger.debug( "Value for %s not set in %s (from %s): set to %s",
|
_logger.debug( "Value for %s not set in %s (from %s): set to %s",
|
||||||
key,
|
key,
|
||||||
s,
|
s,
|
||||||
orig_s,
|
orig_s,
|
||||||
mapping[ key ] )
|
mapping[ key ] )
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
UserMessage( 'Invalid $ in string {}: {}'.format( s, e ),
|
UserMessage( 'Invalid $ in string {}: {}'.format( s, e ),
|
||||||
persist = True )
|
persist = True )
|
||||||
break
|
break
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def expand_refs_in_object( obj ):
|
|
||||||
if isinstance( obj, dict ):
|
|
||||||
ExpandReferencesInDict( obj, mapping, user_choices )
|
|
||||||
elif isinstance( obj, list ):
|
|
||||||
for i, _ in enumerate( obj ):
|
|
||||||
# FIXME: We are assuming that it is a list of string, but could be a
|
|
||||||
# list of list of a list of dict, etc.
|
|
||||||
obj[ i ] = expand_refs_in_object( obj[ i ] )
|
|
||||||
elif isinstance( obj, str ):
|
|
||||||
obj = expand_refs_in_string( obj )
|
|
||||||
|
|
||||||
return obj
|
|
||||||
|
|
||||||
for k in obj.keys():
|
|
||||||
obj[ k ] = expand_refs_in_object( obj[ k ] )
|
|
||||||
|
|
||||||
|
|
||||||
def ParseVariables( variables_list, mapping, user_choices ):
|
def CoerceType( mapping: typing.Dict[ str, typing.Any ], key: str ):
|
||||||
|
DICT_TYPES = {
|
||||||
|
'json': json.loads,
|
||||||
|
's': str
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = key.split( '#' )
|
||||||
|
if len( parts ) > 1 and parts[ -1 ] in DICT_TYPES.keys():
|
||||||
|
value = mapping.pop( key )
|
||||||
|
|
||||||
|
new_type = parts[ -1 ]
|
||||||
|
key = '#'.join( parts[ 0 : -1 ] )
|
||||||
|
|
||||||
|
mapping[ key ] = DICT_TYPES[ new_type ]( value )
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Should we just run the substitution on the whole JSON string instead?
|
||||||
|
# That woul dallow expansion in bool and number values, such as ports etc. ?
|
||||||
|
def ExpandReferencesInDict( obj, mapping, calculus, user_choices ):
|
||||||
|
for k in list( obj.keys() ):
|
||||||
|
obj[ k ] = ExpandReferencesInObject( obj[ k ],
|
||||||
|
mapping,
|
||||||
|
calculus,
|
||||||
|
user_choices )
|
||||||
|
CoerceType( obj, k )
|
||||||
|
|
||||||
|
|
||||||
|
def ParseVariables( variables_list,
|
||||||
|
mapping,
|
||||||
|
calculus,
|
||||||
|
user_choices ):
|
||||||
new_variables = {}
|
new_variables = {}
|
||||||
new_mapping = mapping.copy()
|
new_mapping = mapping.copy()
|
||||||
|
|
||||||
if not isinstance( variables_list, list ):
|
if not isinstance( variables_list, list ):
|
||||||
variables_list = [ variables_list ]
|
variables_list = [ variables_list ]
|
||||||
|
|
||||||
|
variables: typing.Dict[ str, typing.Any ]
|
||||||
for variables in variables_list:
|
for variables in variables_list:
|
||||||
new_mapping.update( new_variables )
|
new_mapping.update( new_variables )
|
||||||
for n, v in variables.items():
|
for n, v in list( variables.items() ):
|
||||||
if isinstance( v, dict ):
|
if isinstance( v, dict ):
|
||||||
if 'shell' in v:
|
if 'shell' in v:
|
||||||
import subprocess
|
|
||||||
import shlex
|
|
||||||
|
|
||||||
new_v = v.copy()
|
new_v = v.copy()
|
||||||
# Bit of a hack. Allows environment variables to be used.
|
# Bit of a hack. Allows environment variables to be used.
|
||||||
ExpandReferencesInDict( new_v, new_mapping, user_choices )
|
ExpandReferencesInDict( new_v,
|
||||||
|
new_mapping,
|
||||||
|
calculus,
|
||||||
|
user_choices )
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env.update( new_v.get( 'env' ) or {} )
|
env.update( new_v.get( 'env' ) or {} )
|
||||||
|
|
@ -412,17 +671,29 @@ def ParseVariables( variables_list, mapping, user_choices ):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Unsupported variable defn {}: Missing 'shell'".format( n ) )
|
"Unsupported variable defn {}: Missing 'shell'".format( n ) )
|
||||||
else:
|
else:
|
||||||
new_variables[ n ] = v
|
new_variables[ n ] = ExpandReferencesInObject( v,
|
||||||
|
mapping,
|
||||||
|
calculus,
|
||||||
|
user_choices )
|
||||||
|
|
||||||
|
CoerceType( new_variables, n )
|
||||||
|
|
||||||
return new_variables
|
return new_variables
|
||||||
|
|
||||||
|
|
||||||
def DisplayBaloon( is_term, display ):
|
def DisplayBalloon( is_term, display, is_hover = False ):
|
||||||
if not is_term:
|
if not is_term:
|
||||||
|
# To enable the Windows GUI to display the balloon correctly
|
||||||
|
# Refer https://github.com/vim/vim/issues/1512#issuecomment-492070685
|
||||||
display = '\n'.join( display )
|
display = '\n'.join( display )
|
||||||
|
|
||||||
vim.eval( "balloon_show( {0} )".format(
|
created_win_id = int( vim.eval(
|
||||||
json.dumps( display ) ) )
|
"vimspector#internal#balloon#CreateTooltip({}, {})".format(
|
||||||
|
int( is_hover ), json.dumps( display )
|
||||||
|
)
|
||||||
|
) )
|
||||||
|
|
||||||
|
return created_win_id
|
||||||
|
|
||||||
|
|
||||||
def GetBufferFilepath( buf ):
|
def GetBufferFilepath( buf ):
|
||||||
|
|
@ -430,3 +701,166 @@ def GetBufferFilepath( buf ):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
return os.path.normpath( buf.name )
|
return os.path.normpath( buf.name )
|
||||||
|
|
||||||
|
|
||||||
|
def ToUnicode( b ):
|
||||||
|
if isinstance( b, bytes ):
|
||||||
|
return b.decode( 'utf-8' )
|
||||||
|
return b
|
||||||
|
|
||||||
|
|
||||||
|
# Call a vimscript function with suplied arguments.
|
||||||
|
def Call( vimscript_function, *args ):
|
||||||
|
call = vimscript_function + '('
|
||||||
|
for index, arg in enumerate( args ):
|
||||||
|
if index > 0:
|
||||||
|
call += ', '
|
||||||
|
|
||||||
|
arg_name = 'vimspector_internal_arg_{}'.format( index )
|
||||||
|
vim.vars[ arg_name ] = arg
|
||||||
|
call += 'g:' + arg_name
|
||||||
|
|
||||||
|
call += ')'
|
||||||
|
return vim.eval( call )
|
||||||
|
|
||||||
|
|
||||||
|
MEMO = {}
|
||||||
|
|
||||||
|
|
||||||
|
def memoize( func ):
|
||||||
|
global MEMO
|
||||||
|
|
||||||
|
@functools.wraps( func )
|
||||||
|
def wrapper( *args, **kwargs ):
|
||||||
|
dct = MEMO.setdefault( func, {} )
|
||||||
|
key = ( args, frozenset( kwargs.items() ) )
|
||||||
|
try:
|
||||||
|
return dct[ key ]
|
||||||
|
except KeyError:
|
||||||
|
result = func( *args, **kwargs )
|
||||||
|
dct[ key ] = result
|
||||||
|
return result
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
def Exists( expr ):
|
||||||
|
return int( vim.eval( f'exists( "{ expr }" )' ) )
|
||||||
|
|
||||||
|
|
||||||
|
def SetSyntax( current_syntax, syntax, *args ):
|
||||||
|
if not syntax:
|
||||||
|
syntax = ''
|
||||||
|
|
||||||
|
if current_syntax == syntax:
|
||||||
|
return syntax
|
||||||
|
|
||||||
|
# We use set syn= because just setting vim.Buffer.options[ 'syntax' ]
|
||||||
|
# doesn't actually trigger the Syntax autocommand, and i'm not sure that
|
||||||
|
# 'doautocmd Syntax' is the right solution or not
|
||||||
|
for buf in args:
|
||||||
|
Call( 'setbufvar', buf.number, '&syntax', syntax )
|
||||||
|
|
||||||
|
return syntax
|
||||||
|
|
||||||
|
|
||||||
|
def GetBufferFiletypes( buf ):
|
||||||
|
ft = ToUnicode( vim.eval( f"getbufvar( {buf.number}, '&ft' )" ) )
|
||||||
|
return ft.split( '.' )
|
||||||
|
|
||||||
|
|
||||||
|
def GetVisualSelection( bufnr ):
|
||||||
|
start_line, start_col = vim.current.buffer.mark( "<" )
|
||||||
|
end_line, end_col = vim.current.buffer.mark( ">" )
|
||||||
|
|
||||||
|
|
||||||
|
# lines are 1 based, but columns are 0 based
|
||||||
|
# don't ask me why...
|
||||||
|
start_line -= 1
|
||||||
|
end_line -= 1
|
||||||
|
|
||||||
|
lines = vim.buffers[ bufnr ][ start_line : end_line + 1 ]
|
||||||
|
# Do end first, in case it's on the same line as start (as doing start first
|
||||||
|
# would change the offset)
|
||||||
|
lines[ -1 ] = lines[ -1 ][ : end_col + 1 ]
|
||||||
|
lines[ 0 ] = lines[ 0 ][ start_col : ]
|
||||||
|
|
||||||
|
_logger.debug( f'Visual selection: { lines } from '
|
||||||
|
f'{ start_line }/{ start_col } -> { end_line }/{ end_col }' )
|
||||||
|
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
|
def DisplaySplash( api_prefix, splash, text ):
|
||||||
|
if splash:
|
||||||
|
return Call( f'vimspector#internal#{api_prefix}popup#UpdateSplash',
|
||||||
|
splash,
|
||||||
|
text )
|
||||||
|
else:
|
||||||
|
return Call( f'vimspector#internal#{api_prefix}popup#DisplaySplash',
|
||||||
|
text )
|
||||||
|
|
||||||
|
|
||||||
|
def HideSplash( api_prefix, splash ):
|
||||||
|
if splash:
|
||||||
|
Call( f'vimspector#internal#{api_prefix}popup#HideSplash', splash )
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def GetVimValue( vim_dict, name, default=None ):
|
||||||
|
|
||||||
|
# FIXME: use 'encoding' ?
|
||||||
|
try:
|
||||||
|
value = vim_dict[ name ]
|
||||||
|
except ( KeyError, vim.error ):
|
||||||
|
return default
|
||||||
|
|
||||||
|
if isinstance( value, bytes ):
|
||||||
|
return value.decode( 'utf-8' )
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def GetVimList( vim_dict, name, default=None ):
|
||||||
|
try:
|
||||||
|
value = vim_dict[ name ]
|
||||||
|
except ( KeyError, vim.error ):
|
||||||
|
return default
|
||||||
|
|
||||||
|
if not isinstance( value, collections.abc.Iterable ):
|
||||||
|
raise ValueError( f"Expected a list for { name }, but found "
|
||||||
|
f"{ type( value ) }" )
|
||||||
|
|
||||||
|
return [ i.decode( 'utf-8' ) if isinstance( i, bytes ) else i for i in value ]
|
||||||
|
|
||||||
|
|
||||||
|
def GetVimspectorBase():
|
||||||
|
return GetVimValue( vim.vars,
|
||||||
|
'vimspector_base_dir',
|
||||||
|
os.path.abspath(
|
||||||
|
os.path.join( os.path.dirname( __file__ ),
|
||||||
|
'..',
|
||||||
|
'..' ) ) )
|
||||||
|
|
||||||
|
|
||||||
|
def GetUnusedLocalPort():
|
||||||
|
import socket
|
||||||
|
sock = socket.socket()
|
||||||
|
# This tells the OS to give us any free port in the range [1024 - 65535]
|
||||||
|
sock.bind( ( '', 0 ) )
|
||||||
|
port = sock.getsockname()[ 1 ]
|
||||||
|
sock.close()
|
||||||
|
return port
|
||||||
|
|
||||||
|
|
||||||
|
def WindowID( window, tab=None ):
|
||||||
|
if tab is None:
|
||||||
|
tab = window.tabpage
|
||||||
|
return int( Call( 'win_getid', window.number, tab.number ) )
|
||||||
|
|
||||||
|
|
||||||
|
def UseWinBar():
|
||||||
|
# Buggy neovim doesn't render correctly when the WinBar is defined:
|
||||||
|
# https://github.com/neovim/neovim/issues/12689
|
||||||
|
return not int( Call( 'has', 'nvim' ) )
|
||||||
|
|
|
||||||
|
|
@ -13,57 +13,208 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import abc
|
||||||
import vim
|
import vim
|
||||||
import logging
|
import logging
|
||||||
from collections import namedtuple
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
import typing
|
||||||
|
|
||||||
from vimspector import utils
|
from vimspector import utils, settings
|
||||||
|
|
||||||
View = namedtuple( 'View', [ 'win', 'lines', 'draw' ] )
|
|
||||||
|
class Expandable:
|
||||||
|
EXPANDED_BY_USER = 2
|
||||||
|
EXPANDED_BY_US = 1
|
||||||
|
COLLAPSED_BY_USER = 0
|
||||||
|
COLLAPSED_BY_DEFAULT = None
|
||||||
|
|
||||||
|
"""Base for anything which might contain a hierarchy of values represented by
|
||||||
|
a 'variablesReference' to be resolved by the 'variables' request. Records the
|
||||||
|
current state expanded/collapsed. Implementations just implement
|
||||||
|
VariablesReference to get the variables."""
|
||||||
|
def __init__( self, container: 'Expandable' = None ):
|
||||||
|
self.variables: typing.List[ 'Variable' ] = None
|
||||||
|
self.container: Expandable = container
|
||||||
|
# None is Falsy and represents collapsed _by default_. WHen set to False,
|
||||||
|
# this means the user explicitly collapsed it. When True, the user expanded
|
||||||
|
# it (or we expanded it by default).
|
||||||
|
self.expanded: int = Expandable.COLLAPSED_BY_DEFAULT
|
||||||
|
|
||||||
|
def IsExpanded( self ):
|
||||||
|
return bool( self.expanded )
|
||||||
|
|
||||||
|
def ShouldDrawDrillDown( self ):
|
||||||
|
return self.IsExpanded() and self.variables is not None
|
||||||
|
|
||||||
|
def IsExpandable( self ):
|
||||||
|
return self.VariablesReference() > 0
|
||||||
|
|
||||||
|
def IsContained( self ):
|
||||||
|
return self.container is not None
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def VariablesReference( self ):
|
||||||
|
assert False
|
||||||
|
|
||||||
|
|
||||||
|
class Scope( Expandable ):
|
||||||
|
"""Holds an expandable scope (a DAP scope dict), with expand/collapse state"""
|
||||||
|
def __init__( self, scope: dict ):
|
||||||
|
super().__init__()
|
||||||
|
self.scope = scope
|
||||||
|
|
||||||
|
def VariablesReference( self ):
|
||||||
|
return self.scope.get( 'variablesReference', 0 )
|
||||||
|
|
||||||
|
def Update( self, scope ):
|
||||||
|
self.scope = scope
|
||||||
|
|
||||||
|
|
||||||
|
class WatchResult( Expandable ):
|
||||||
|
"""Holds the result of a Watch expression with expand/collapse."""
|
||||||
|
def __init__( self, result: dict ):
|
||||||
|
super().__init__()
|
||||||
|
self.result = result
|
||||||
|
# A new watch result is marked as changed
|
||||||
|
self.changed = True
|
||||||
|
|
||||||
|
def VariablesReference( self ):
|
||||||
|
return self.result.get( 'variablesReference', 0 )
|
||||||
|
|
||||||
|
def Update( self, result ):
|
||||||
|
self.changed = False
|
||||||
|
if self.result[ 'result' ] != result[ 'result' ]:
|
||||||
|
self.changed = True
|
||||||
|
self.result = result
|
||||||
|
|
||||||
|
|
||||||
|
class WatchFailure( WatchResult ):
|
||||||
|
def __init__( self, reason ):
|
||||||
|
super().__init__( { 'result': reason } )
|
||||||
|
self.changed = True
|
||||||
|
|
||||||
|
|
||||||
|
class Variable( Expandable ):
|
||||||
|
"""Holds one level of an expanded value tree. Also itself expandable."""
|
||||||
|
def __init__( self, container: Expandable, variable: dict ):
|
||||||
|
super().__init__( container = container )
|
||||||
|
self.variable = variable
|
||||||
|
# A new variable appearing is marked as changed
|
||||||
|
self.changed = True
|
||||||
|
|
||||||
|
def VariablesReference( self ):
|
||||||
|
return self.variable.get( 'variablesReference', 0 )
|
||||||
|
|
||||||
|
def Update( self, variable ):
|
||||||
|
self.changed = False
|
||||||
|
if self.variable[ 'value' ] != variable[ 'value' ]:
|
||||||
|
self.changed = True
|
||||||
|
self.variable = variable
|
||||||
|
|
||||||
|
|
||||||
|
class Watch:
|
||||||
|
"""Holds a user watch expression (DAP request) and the result (WatchResult)"""
|
||||||
|
def __init__( self, expression: dict ):
|
||||||
|
self.result: WatchResult
|
||||||
|
self.line = None
|
||||||
|
|
||||||
|
self.expression = expression
|
||||||
|
self.result = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def New( frame, expression, context ):
|
||||||
|
watch = {
|
||||||
|
'expression': expression,
|
||||||
|
'context': context,
|
||||||
|
}
|
||||||
|
if frame:
|
||||||
|
watch[ 'frameId' ] = frame[ 'id' ]
|
||||||
|
|
||||||
|
return Watch( watch )
|
||||||
|
|
||||||
|
|
||||||
|
class View:
|
||||||
|
lines: typing.Dict[ int, Expandable ]
|
||||||
|
draw: typing.Callable
|
||||||
|
syntax: str
|
||||||
|
|
||||||
|
def __init__( self, win, lines, draw ):
|
||||||
|
self.lines = lines
|
||||||
|
self.draw = draw
|
||||||
|
self.syntax = None
|
||||||
|
if win is not None:
|
||||||
|
self.buf = win.buffer
|
||||||
|
utils.SetUpUIWindow( win )
|
||||||
|
|
||||||
|
|
||||||
|
class BufView( View ):
|
||||||
|
def __init__( self, buf, lines, draw ):
|
||||||
|
super().__init__( None, lines, draw )
|
||||||
|
self.buf = buf
|
||||||
|
|
||||||
|
|
||||||
|
def AddExpandMappings( mappings = None ):
|
||||||
|
if mappings is None:
|
||||||
|
mappings = settings.Dict( 'mappings' )[ 'variables' ]
|
||||||
|
for mapping in utils.GetVimList( mappings, 'expand_collapse' ):
|
||||||
|
vim.command( f'nnoremap <silent> <buffer> { mapping } '
|
||||||
|
':<C-u>call vimspector#ExpandVariable()<CR>' )
|
||||||
|
|
||||||
|
for mapping in utils.GetVimList( mappings, 'set_value' ):
|
||||||
|
vim.command( f'nnoremap <silent> <buffer> { mapping } '
|
||||||
|
':<C-u>call vimspector#SetVariableValue()<CR>' )
|
||||||
|
|
||||||
|
|
||||||
class VariablesView( object ):
|
class VariablesView( object ):
|
||||||
def __init__( self, connection, variables_win, watches_win ):
|
def __init__( self, variables_win, watches_win ):
|
||||||
self._logger = logging.getLogger( __name__ )
|
self._logger = logging.getLogger( __name__ )
|
||||||
utils.SetUpLogging( self._logger )
|
utils.SetUpLogging( self._logger )
|
||||||
|
|
||||||
|
self._connection = None
|
||||||
|
self._current_syntax = ''
|
||||||
|
self._server_capabilities = None
|
||||||
|
|
||||||
|
self._variable_eval: Scope = None
|
||||||
|
self._variable_eval_view: View = None
|
||||||
|
|
||||||
|
mappings = settings.Dict( 'mappings' )[ 'variables' ]
|
||||||
|
|
||||||
|
# Set up the "Variables" buffer in the variables_win
|
||||||
|
self._scopes: typing.List[ Scope ] = []
|
||||||
self._vars = View( variables_win, {}, self._DrawScopes )
|
self._vars = View( variables_win, {}, self._DrawScopes )
|
||||||
|
utils.SetUpHiddenBuffer( self._vars.buf, 'vimspector.Variables' )
|
||||||
|
with utils.LetCurrentWindow( variables_win ):
|
||||||
|
if utils.UseWinBar():
|
||||||
|
vim.command( 'nnoremenu <silent> 1.1 WinBar.Set '
|
||||||
|
':call vimspector#SetVariableValue()<CR>' )
|
||||||
|
AddExpandMappings( mappings )
|
||||||
|
|
||||||
|
# Set up the "Watches" buffer in the watches_win (and create a WinBar in
|
||||||
|
# there)
|
||||||
|
self._watches: typing.List[ Watch ] = []
|
||||||
self._watch = View( watches_win, {}, self._DrawWatches )
|
self._watch = View( watches_win, {}, self._DrawWatches )
|
||||||
self._connection = connection
|
utils.SetUpPromptBuffer( self._watch.buf,
|
||||||
|
|
||||||
# Allows us to hit <CR> to expand/collapse variables
|
|
||||||
with utils.LetCurrentWindow( self._vars.win ):
|
|
||||||
vim.command(
|
|
||||||
'nnoremap <buffer> <CR> :call vimspector#ExpandVariable()<CR>' )
|
|
||||||
|
|
||||||
# This is actually the tree (scopes are alwyas the root)
|
|
||||||
# it's just a list of DAP scope dicts, with one magic key (_variables)
|
|
||||||
# _variables is a list of DAP variable with the same magic key
|
|
||||||
#
|
|
||||||
# If _variables is present, then we have requested and should display the
|
|
||||||
# children. Otherwise, we haven't or shouldn't.
|
|
||||||
self._scopes = []
|
|
||||||
|
|
||||||
# This is similar to scopes, but the top level is an "expression" (request)
|
|
||||||
# containing a special '_result' key which is the response. The response
|
|
||||||
# structure con contain _variables and is handled identically to the scopes
|
|
||||||
# above. It also has a special _line key which is where we printed it (last)
|
|
||||||
self._watches = []
|
|
||||||
|
|
||||||
# Allows us to hit <CR> to expand/collapse variables
|
|
||||||
with utils.LetCurrentWindow( self._watch.win ):
|
|
||||||
vim.command(
|
|
||||||
'nnoremap <buffer> <CR> :call vimspector#ExpandVariable()<CR>' )
|
|
||||||
vim.command(
|
|
||||||
'nnoremap <buffer> <DEL> :call vimspector#DeleteWatch()<CR>' )
|
|
||||||
|
|
||||||
utils.SetUpScratchBuffer( self._vars.win.buffer, 'vimspector.Variables' )
|
|
||||||
utils.SetUpPromptBuffer( self._watch.win.buffer,
|
|
||||||
'vimspector.Watches',
|
'vimspector.Watches',
|
||||||
'Expression: ',
|
'Expression: ',
|
||||||
'vimspector#AddWatchPrompt' )
|
'vimspector#AddWatchPrompt',
|
||||||
|
'vimspector#OmniFuncWatch' )
|
||||||
|
with utils.LetCurrentWindow( watches_win ):
|
||||||
|
AddExpandMappings( mappings )
|
||||||
|
for mapping in utils.GetVimList( mappings, 'delete' ):
|
||||||
|
vim.command(
|
||||||
|
f'nnoremap <buffer> { mapping } :call vimspector#DeleteWatch()<CR>' )
|
||||||
|
|
||||||
|
if utils.UseWinBar():
|
||||||
|
vim.command( 'nnoremenu <silent> 1.1 WinBar.New '
|
||||||
|
':call vimspector#AddWatch()<CR>' )
|
||||||
|
vim.command( 'nnoremenu <silent> 1.2 WinBar.Expand/Collapse '
|
||||||
|
':call vimspector#ExpandVariable()<CR>' )
|
||||||
|
vim.command( 'nnoremenu <silent> 1.3 WinBar.Delete '
|
||||||
|
':call vimspector#DeleteWatch()<CR>' )
|
||||||
|
vim.command( 'nnoremenu <silent> 1.1 WinBar.Set '
|
||||||
|
':call vimspector#SetVariableValue()<CR>' )
|
||||||
|
|
||||||
|
# Set the (global!) balloon expr if supported
|
||||||
has_balloon = int( vim.eval( "has( 'balloon_eval' )" ) )
|
has_balloon = int( vim.eval( "has( 'balloon_eval' )" ) )
|
||||||
has_balloon_term = int( vim.eval( "has( 'balloon_eval_term' )" ) )
|
has_balloon_term = int( vim.eval( "has( 'balloon_eval_term' )" ) )
|
||||||
|
|
||||||
|
|
@ -73,7 +224,9 @@ class VariablesView( object ):
|
||||||
'balloonexpr': vim.options[ 'balloonexpr' ],
|
'balloonexpr': vim.options[ 'balloonexpr' ],
|
||||||
'balloondelay': vim.options[ 'balloondelay' ],
|
'balloondelay': vim.options[ 'balloondelay' ],
|
||||||
}
|
}
|
||||||
vim.options[ 'balloonexpr' ] = 'vimspector#internal#balloon#BalloonExpr()'
|
vim.options[ 'balloonexpr' ] = ( "vimspector#internal#"
|
||||||
|
"balloon#HoverTooltip()" )
|
||||||
|
|
||||||
vim.options[ 'balloondelay' ] = 250
|
vim.options[ 'balloondelay' ] = 250
|
||||||
|
|
||||||
if has_balloon:
|
if has_balloon:
|
||||||
|
|
@ -87,49 +240,76 @@ class VariablesView( object ):
|
||||||
self._is_term = not bool( int( vim.eval( "has( 'gui_running' )" ) ) )
|
self._is_term = not bool( int( vim.eval( "has( 'gui_running' )" ) ) )
|
||||||
|
|
||||||
def Clear( self ):
|
def Clear( self ):
|
||||||
with utils.ModifiableScratchBuffer( self._vars.win.buffer ):
|
with utils.ModifiableScratchBuffer( self._vars.buf ):
|
||||||
utils.ClearBuffer( self._vars.win.buffer )
|
utils.ClearBuffer( self._vars.buf )
|
||||||
with utils.ModifiableScratchBuffer( self._watch.win.buffer ):
|
with utils.ModifiableScratchBuffer( self._watch.buf ):
|
||||||
utils.ClearBuffer( self._watch.win.buffer )
|
utils.ClearBuffer( self._watch.buf )
|
||||||
|
self.ClearTooltip()
|
||||||
|
self._current_syntax = ''
|
||||||
|
|
||||||
def ConnectionUp( self, connection ):
|
def ConnectionUp( self, connection ):
|
||||||
self._connection = connection
|
self._connection = connection
|
||||||
|
|
||||||
|
def SetServerCapabilities( self, capabilities ):
|
||||||
|
self._server_capabilities = capabilities
|
||||||
|
|
||||||
def ConnectionClosed( self ):
|
def ConnectionClosed( self ):
|
||||||
self.Clear()
|
self.Clear()
|
||||||
self._connection = None
|
self._connection = None
|
||||||
|
self._server_capabilities = None
|
||||||
|
|
||||||
def Reset( self ):
|
def Reset( self ):
|
||||||
|
self._server_capabilities = None
|
||||||
|
|
||||||
for k, v in self._oldoptions.items():
|
for k, v in self._oldoptions.items():
|
||||||
vim.options[ k ] = v
|
vim.options[ k ] = v
|
||||||
|
|
||||||
|
utils.CleanUpHiddenBuffer( self._vars.buf )
|
||||||
|
utils.CleanUpHiddenBuffer( self._watch.buf )
|
||||||
|
self.ClearTooltip()
|
||||||
|
|
||||||
|
|
||||||
def LoadScopes( self, frame ):
|
def LoadScopes( self, frame ):
|
||||||
def scopes_consumer( message ):
|
def scopes_consumer( message ):
|
||||||
old_scopes = self._scopes
|
new_scopes = []
|
||||||
self._scopes = []
|
expanded_some_scope = False
|
||||||
|
for scope_body in message[ 'body' ][ 'scopes' ]:
|
||||||
|
# Find it in the scopes list
|
||||||
|
found = False
|
||||||
|
for index, s in enumerate( self._scopes ):
|
||||||
|
if s.scope[ 'name' ] == scope_body[ 'name' ]:
|
||||||
|
found = True
|
||||||
|
scope = s
|
||||||
|
break
|
||||||
|
|
||||||
for i, scope in enumerate( message[ 'body' ][ 'scopes' ] ):
|
if not found:
|
||||||
if ( i < len( old_scopes ) and
|
scope = Scope( scope_body )
|
||||||
old_scopes[ i ][ 'name' ] == scope[ 'name' ] ):
|
|
||||||
scope[ '_expanded' ] = old_scopes[ i ].get( '_expanded', False )
|
|
||||||
scope[ '_old_variables' ] = old_scopes[ i ].get( '_variables', [] )
|
|
||||||
elif not scope.get( 'expensive' ):
|
|
||||||
# Expand any non-expensive scope unless manually collapsed
|
|
||||||
scope[ '_expanded' ] = True
|
|
||||||
else:
|
else:
|
||||||
scope[ '_expanded' ] = False
|
scope.Update( scope_body )
|
||||||
|
|
||||||
self._scopes.append( scope )
|
new_scopes.append( scope )
|
||||||
if scope[ '_expanded' ]:
|
|
||||||
|
# Expand the first non-expensive scope which is not manually collapsed
|
||||||
|
if ( not expanded_some_scope
|
||||||
|
and not scope.scope.get( 'expensive' )
|
||||||
|
and scope.expanded is not Expandable.COLLAPSED_BY_USER ):
|
||||||
|
scope.expanded = Expandable.EXPANDED_BY_US
|
||||||
|
expanded_some_scope = True
|
||||||
|
elif ( expanded_some_scope and scope.expanded is
|
||||||
|
Expandable.EXPANDED_BY_US ):
|
||||||
|
scope.expanded = Expandable.COLLAPSED_BY_DEFAULT
|
||||||
|
|
||||||
|
if scope.IsExpanded():
|
||||||
self._connection.DoRequest( partial( self._ConsumeVariables,
|
self._connection.DoRequest( partial( self._ConsumeVariables,
|
||||||
self._DrawScopes,
|
self._DrawScopes,
|
||||||
scope ), {
|
scope ), {
|
||||||
'command': 'variables',
|
'command': 'variables',
|
||||||
'arguments': {
|
'arguments': {
|
||||||
'variablesReference': scope[ 'variablesReference' ]
|
'variablesReference': scope.VariablesReference(),
|
||||||
},
|
},
|
||||||
} )
|
} )
|
||||||
|
|
||||||
|
self._scopes = new_scopes
|
||||||
self._DrawScopes()
|
self._DrawScopes()
|
||||||
|
|
||||||
self._connection.DoRequest( scopes_consumer, {
|
self._connection.DoRequest( scopes_consumer, {
|
||||||
|
|
@ -139,237 +319,413 @@ class VariablesView( object ):
|
||||||
},
|
},
|
||||||
} )
|
} )
|
||||||
|
|
||||||
|
def _DrawBalloonEval( self ):
|
||||||
|
watch = self._variable_eval
|
||||||
|
view = self._variable_eval_view
|
||||||
|
|
||||||
|
with utils.RestoreCursorPosition():
|
||||||
|
with utils.ModifiableScratchBuffer( view.buf ):
|
||||||
|
utils.ClearBuffer( view.buf )
|
||||||
|
view.syntax = utils.SetSyntax( view.syntax,
|
||||||
|
self._current_syntax,
|
||||||
|
view.buf )
|
||||||
|
|
||||||
|
self._DrawWatchResult( view,
|
||||||
|
0,
|
||||||
|
watch,
|
||||||
|
is_short = True )
|
||||||
|
|
||||||
|
vim.eval( "vimspector#internal#balloon#ResizeTooltip()" )
|
||||||
|
|
||||||
|
def ClearTooltip( self ):
|
||||||
|
# This will actually end up calling CleanUpTooltip via the popup close
|
||||||
|
# callback
|
||||||
|
vim.eval( 'vimspector#internal#balloon#Close()' )
|
||||||
|
|
||||||
|
def CleanUpTooltip( self ) :
|
||||||
|
# remove reference to old tooltip window
|
||||||
|
self._variable_eval_view = None
|
||||||
|
vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = None
|
||||||
|
|
||||||
|
def VariableEval( self, frame, expression, is_hover ):
|
||||||
|
"""Callback to display variable under cursor `:h ballonexpr`"""
|
||||||
|
if not self._connection:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def handler( message ):
|
||||||
|
|
||||||
|
watch = self._variable_eval
|
||||||
|
if watch.result is None:
|
||||||
|
watch.result = WatchResult( message[ 'body' ] )
|
||||||
|
else:
|
||||||
|
watch.result.Update( message[ 'body' ] )
|
||||||
|
|
||||||
|
popup_win_id = utils.DisplayBalloon( self._is_term, [], is_hover )
|
||||||
|
# record the global eval window id
|
||||||
|
vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = int( popup_win_id )
|
||||||
|
popup_bufnr = int( vim.eval( "winbufnr({})".format( popup_win_id ) ) )
|
||||||
|
|
||||||
|
# We don't need to do any UI window setup here, as it's already done as
|
||||||
|
# part of the popup creation, so just pass the buffer to the View instance
|
||||||
|
self._variable_eval_view = BufView(
|
||||||
|
vim.buffers[ popup_bufnr ],
|
||||||
|
{},
|
||||||
|
self._DrawBalloonEval
|
||||||
|
)
|
||||||
|
|
||||||
|
if watch.result.IsExpandable():
|
||||||
|
# Always expand the first level
|
||||||
|
watch.result.expanded = Expandable.EXPANDED_BY_US
|
||||||
|
|
||||||
|
if watch.result.IsExpanded():
|
||||||
|
self._connection.DoRequest( partial( self._ConsumeVariables,
|
||||||
|
self._variable_eval_view.draw,
|
||||||
|
watch.result ), {
|
||||||
|
'command': 'variables',
|
||||||
|
'arguments': {
|
||||||
|
'variablesReference': watch.result.VariablesReference(),
|
||||||
|
},
|
||||||
|
} )
|
||||||
|
|
||||||
|
self._DrawBalloonEval()
|
||||||
|
|
||||||
|
def failure_handler( reason, message ):
|
||||||
|
display = [ reason ]
|
||||||
|
float_win_id = utils.DisplayBalloon( self._is_term, display, is_hover )
|
||||||
|
# record the global eval window id
|
||||||
|
vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = int( float_win_id )
|
||||||
|
|
||||||
|
self._variable_eval = Watch.New( frame,
|
||||||
|
expression,
|
||||||
|
'hover' )
|
||||||
|
|
||||||
|
# Send async request
|
||||||
|
self._connection.DoRequest( handler, {
|
||||||
|
'command': 'evaluate',
|
||||||
|
'arguments': self._variable_eval.expression,
|
||||||
|
}, failure_handler )
|
||||||
|
|
||||||
|
# Return working (meanwhile)
|
||||||
|
return ''
|
||||||
|
|
||||||
def AddWatch( self, frame, expression ):
|
def AddWatch( self, frame, expression ):
|
||||||
watch = {
|
self._watches.append( Watch.New( frame, expression, 'watch' ) )
|
||||||
'expression': expression,
|
|
||||||
'frameId': frame[ 'id' ],
|
|
||||||
'context': 'watch',
|
|
||||||
}
|
|
||||||
self._watches.append( watch )
|
|
||||||
self.EvaluateWatches()
|
self.EvaluateWatches()
|
||||||
|
|
||||||
def DeleteWatch( self ):
|
def DeleteWatch( self ):
|
||||||
if vim.current.window != self._watch.win:
|
if vim.current.buffer != self._watch.buf:
|
||||||
utils.UserMessage( 'Not a watch window' )
|
utils.UserMessage( 'Not a watch buffer' )
|
||||||
return
|
return
|
||||||
|
|
||||||
current_line = vim.current.window.cursor[ 0 ]
|
current_line = vim.current.window.cursor[ 0 ]
|
||||||
|
|
||||||
|
best_index = -1
|
||||||
for index, watch in enumerate( self._watches ):
|
for index, watch in enumerate( self._watches ):
|
||||||
if '_line' in watch and watch[ '_line' ] == current_line:
|
if ( watch.line is not None
|
||||||
del self._watches[ index ]
|
and watch.line <= current_line
|
||||||
utils.UserMessage( 'Deleted' )
|
and watch.line > best_index ):
|
||||||
self._DrawWatches()
|
best_index = index
|
||||||
return
|
|
||||||
|
if best_index >= 0:
|
||||||
|
del self._watches[ best_index ]
|
||||||
|
utils.UserMessage( 'Deleted' )
|
||||||
|
self._DrawWatches()
|
||||||
|
return
|
||||||
|
|
||||||
utils.UserMessage( 'No watch found' )
|
utils.UserMessage( 'No watch found' )
|
||||||
|
|
||||||
def EvaluateWatches( self ):
|
def EvaluateWatches( self ):
|
||||||
for watch in self._watches:
|
for watch in self._watches:
|
||||||
self._connection.DoRequest( partial( self._UpdateWatchExpression,
|
self._connection.DoRequest(
|
||||||
watch ), {
|
partial( self._UpdateWatchExpression, watch ),
|
||||||
'command': 'evaluate',
|
{
|
||||||
'arguments': watch,
|
'command': 'evaluate',
|
||||||
} )
|
'arguments': watch.expression,
|
||||||
|
},
|
||||||
|
failure_handler = lambda reason, msg, watch=watch:
|
||||||
|
self._WatchExpressionFailed( reason, watch ) )
|
||||||
|
|
||||||
def _UpdateWatchExpression( self, watch, message ):
|
def _UpdateWatchExpression( self, watch: Watch, message: dict ):
|
||||||
old_result = None
|
if watch.result is not None:
|
||||||
if '_result' in watch:
|
watch.result.Update( message[ 'body' ] )
|
||||||
old_result = watch[ '_result' ]
|
else:
|
||||||
|
watch.result = WatchResult( message[ 'body' ] )
|
||||||
|
|
||||||
result = message[ 'body' ]
|
if ( watch.result.IsExpandable() and
|
||||||
watch[ '_result' ] = result
|
watch.result.IsExpanded() ):
|
||||||
|
|
||||||
if old_result:
|
|
||||||
if '_expanded' in old_result:
|
|
||||||
result[ '_expanded' ] = old_result[ '_expanded' ]
|
|
||||||
result[ '_old_variables' ] = old_result.get( '_variables', [] )
|
|
||||||
|
|
||||||
if ( result.get( 'variablesReference', 0 ) > 0 and
|
|
||||||
result.get( '_expanded', False ) ):
|
|
||||||
self._connection.DoRequest( partial( self._ConsumeVariables,
|
self._connection.DoRequest( partial( self._ConsumeVariables,
|
||||||
self._watch.draw,
|
self._watch.draw,
|
||||||
result ), {
|
watch.result ), {
|
||||||
'command': 'variables',
|
'command': 'variables',
|
||||||
'arguments': {
|
'arguments': {
|
||||||
'variablesReference': result[ 'variablesReference' ]
|
'variablesReference': watch.result.VariablesReference(),
|
||||||
},
|
},
|
||||||
} )
|
} )
|
||||||
|
|
||||||
self._DrawWatches()
|
self._DrawWatches()
|
||||||
|
|
||||||
def ExpandVariable( self ):
|
def _WatchExpressionFailed( self, reason: str, watch: Watch ):
|
||||||
if vim.current.window == self._vars.win:
|
if watch.result is not None:
|
||||||
|
# We already have a result for this watch. Wut ?
|
||||||
|
return
|
||||||
|
|
||||||
|
watch.result = WatchFailure( reason )
|
||||||
|
self._DrawWatches()
|
||||||
|
|
||||||
|
def _GetVariable( self, buf = None, line_num = None ):
|
||||||
|
none = ( None, None )
|
||||||
|
|
||||||
|
if buf is None:
|
||||||
|
buf = vim.current.buffer
|
||||||
|
|
||||||
|
if line_num is None:
|
||||||
|
line_num = vim.current.window.cursor[ 0 ]
|
||||||
|
|
||||||
|
if buf == self._vars.buf:
|
||||||
view = self._vars
|
view = self._vars
|
||||||
elif vim.current.window == self._watch.win:
|
elif buf == self._watch.buf:
|
||||||
view = self._watch
|
view = self._watch
|
||||||
|
elif ( self._variable_eval_view is not None
|
||||||
|
and buf == self._variable_eval_view.buf ):
|
||||||
|
view = self._variable_eval_view
|
||||||
else:
|
else:
|
||||||
|
return none
|
||||||
|
|
||||||
|
if line_num not in view.lines:
|
||||||
|
return none
|
||||||
|
|
||||||
|
return view.lines[ line_num ], view
|
||||||
|
|
||||||
|
def ExpandVariable( self, buf = None, line_num = None ):
|
||||||
|
variable, view = self._GetVariable( buf, line_num )
|
||||||
|
if variable is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
current_line = vim.current.window.cursor[ 0 ]
|
if variable.IsExpanded():
|
||||||
if current_line not in view.lines:
|
|
||||||
return
|
|
||||||
|
|
||||||
variable = view.lines[ current_line ]
|
|
||||||
|
|
||||||
if '_variables' in variable:
|
|
||||||
# Collapse
|
# Collapse
|
||||||
del variable[ '_variables' ]
|
variable.expanded = Expandable.COLLAPSED_BY_USER
|
||||||
variable[ '_expanded' ] = False
|
|
||||||
view.draw()
|
view.draw()
|
||||||
return
|
return
|
||||||
|
|
||||||
if variable.get( 'variablesReference', 0 ) <= 0:
|
if not variable.IsExpandable():
|
||||||
return
|
return
|
||||||
|
|
||||||
variable[ '_expanded' ] = True
|
variable.expanded = Expandable.EXPANDED_BY_USER
|
||||||
self._connection.DoRequest( partial( self._ConsumeVariables,
|
self._connection.DoRequest( partial( self._ConsumeVariables,
|
||||||
view.draw,
|
view.draw,
|
||||||
variable ), {
|
variable ), {
|
||||||
'command': 'variables',
|
'command': 'variables',
|
||||||
'arguments': {
|
'arguments': {
|
||||||
'variablesReference': variable[ 'variablesReference' ]
|
'variablesReference': variable.VariablesReference()
|
||||||
},
|
},
|
||||||
} )
|
} )
|
||||||
|
|
||||||
def _DrawVariables( self, view, variables, indent ):
|
def SetVariableValue( self, new_value = None, buf = None, line_num = None ):
|
||||||
|
variable: Variable
|
||||||
|
view: View
|
||||||
|
|
||||||
|
if not self._server_capabilities.get( 'supportsSetVariable' ):
|
||||||
|
return
|
||||||
|
|
||||||
|
variable, view = self._GetVariable( buf, line_num )
|
||||||
|
if variable is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not variable.IsContained():
|
||||||
|
return
|
||||||
|
|
||||||
|
if new_value is None:
|
||||||
|
new_value = utils.AskForInput( 'New Value: ',
|
||||||
|
variable.variable.get( 'value', '' ),
|
||||||
|
completion = 'expr' )
|
||||||
|
|
||||||
|
if new_value is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def handler( message ):
|
||||||
|
# Annoyingly the response to setVariable request doesn't return a
|
||||||
|
# Variable, but some part of it, so take a copy of the existing Variable
|
||||||
|
# dict and update it, then call its update method with the updated copy.
|
||||||
|
new_variable = dict( variable.variable )
|
||||||
|
new_variable.update( message[ 'body' ] )
|
||||||
|
|
||||||
|
# Clear any existing known children (FIXME: Is this the right thing to do)
|
||||||
|
variable.variables = None
|
||||||
|
|
||||||
|
# If the variable is expanded, re-request its children
|
||||||
|
if variable.IsExpanded():
|
||||||
|
self._connection.DoRequest( partial( self._ConsumeVariables,
|
||||||
|
view.draw,
|
||||||
|
variable ), {
|
||||||
|
'command': 'variables',
|
||||||
|
'arguments': {
|
||||||
|
'variablesReference': variable.VariablesReference()
|
||||||
|
},
|
||||||
|
} )
|
||||||
|
|
||||||
|
variable.Update( new_variable )
|
||||||
|
view.draw()
|
||||||
|
|
||||||
|
def failure_handler( reason, message ):
|
||||||
|
utils.UserMessage( f'Cannot set value: { reason }', error = True )
|
||||||
|
|
||||||
|
self._connection.DoRequest( handler, {
|
||||||
|
'command': 'setVariable',
|
||||||
|
'arguments': {
|
||||||
|
'variablesReference': variable.container.VariablesReference(),
|
||||||
|
'name': variable.variable[ 'name' ],
|
||||||
|
'value': new_value
|
||||||
|
},
|
||||||
|
}, failure_handler = failure_handler )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _DrawVariables( self, view, variables, indent, is_short = False ):
|
||||||
|
assert indent > 0
|
||||||
for variable in variables:
|
for variable in variables:
|
||||||
|
text = ''
|
||||||
|
if is_short:
|
||||||
|
text = '{indent}{icon} {name}: {value}'.format(
|
||||||
|
# We borrow 1 space of indent to draw the change marker
|
||||||
|
indent = ' ' * ( indent - 1 ),
|
||||||
|
icon = '+' if ( variable.IsExpandable()
|
||||||
|
and not variable.IsExpanded() ) else '-',
|
||||||
|
name = variable.variable.get( 'name', '' ),
|
||||||
|
value = variable.variable.get( 'value', '<unknown>' )
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
text = '{indent}{marker}{icon} {name} ({type_}): {value}'.format(
|
||||||
|
# We borrow 1 space of indent to draw the change marker
|
||||||
|
indent = ' ' * ( indent - 1 ),
|
||||||
|
marker = '*' if variable.changed else ' ',
|
||||||
|
icon = '+' if ( variable.IsExpandable()
|
||||||
|
and not variable.IsExpanded() ) else '-',
|
||||||
|
name = variable.variable.get( 'name', '' ),
|
||||||
|
type_ = variable.variable.get( 'type', '' ),
|
||||||
|
value = variable.variable.get( 'value', '<unknown>' )
|
||||||
|
)
|
||||||
|
|
||||||
line = utils.AppendToBuffer(
|
line = utils.AppendToBuffer(
|
||||||
view.win.buffer,
|
view.buf,
|
||||||
'{indent}{icon} {name} ({type_}): {value}'.format(
|
text.split( '\n' )
|
||||||
indent = ' ' * indent,
|
)
|
||||||
icon = '+' if ( variable.get( 'variablesReference', 0 ) > 0 and
|
|
||||||
'_variables' not in variable ) else '-',
|
|
||||||
name = variable[ 'name' ],
|
|
||||||
type_ = variable.get( 'type', '<unknown type>' ),
|
|
||||||
value = variable.get( 'value', '<unknown value>' ) ).split( '\n' ) )
|
|
||||||
view.lines[ line ] = variable
|
view.lines[ line ] = variable
|
||||||
|
|
||||||
if '_variables' in variable:
|
if variable.ShouldDrawDrillDown():
|
||||||
self._DrawVariables( view, variable[ '_variables' ], indent + 2 )
|
self._DrawVariables( view, variable.variables, indent + 2, is_short )
|
||||||
|
|
||||||
def _DrawScopes( self ):
|
def _DrawScopes( self ):
|
||||||
# FIXME: The drawing is dumb and draws from scratch every time. This is
|
# FIXME: The drawing is dumb and draws from scratch every time. This is
|
||||||
# simple and works and makes sure the line-map is always correct.
|
# simple and works and makes sure the line-map is always correct.
|
||||||
# However it is really inefficient, and makes it so that expanded results
|
# However it is pretty inefficient.
|
||||||
# are collapsed on every step.
|
|
||||||
self._vars.lines.clear()
|
self._vars.lines.clear()
|
||||||
with utils.RestoreCursorPosition():
|
with utils.RestoreCursorPosition():
|
||||||
with utils.ModifiableScratchBuffer( self._vars.win.buffer ):
|
with utils.ModifiableScratchBuffer( self._vars.buf ):
|
||||||
utils.ClearBuffer( self._vars.win.buffer )
|
utils.ClearBuffer( self._vars.buf )
|
||||||
for scope in self._scopes:
|
for scope in self._scopes:
|
||||||
self._DrawScope( 0, scope )
|
self._DrawScope( 0, scope )
|
||||||
|
|
||||||
def _DrawWatches( self ):
|
def _DrawWatches( self ):
|
||||||
# FIXME: The drawing is dumb and draws from scratch every time. This is
|
# FIXME: The drawing is dumb and draws from scratch every time. This is
|
||||||
# simple and works and makes sure the line-map is always correct.
|
# simple and works and makes sure the line-map is always correct.
|
||||||
# However it is really inefficient, and makes it so that expanded results
|
# However it is pretty inefficient.
|
||||||
# are collapsed on every step.
|
|
||||||
self._watch.lines.clear()
|
self._watch.lines.clear()
|
||||||
with utils.RestoreCursorPosition():
|
with utils.RestoreCursorPosition():
|
||||||
with utils.ModifiableScratchBuffer( self._watch.win.buffer ):
|
with utils.ModifiableScratchBuffer( self._watch.buf ):
|
||||||
utils.ClearBuffer( self._watch.win.buffer )
|
utils.ClearBuffer( self._watch.buf )
|
||||||
utils.AppendToBuffer( self._watch.win.buffer, 'Watches: ----' )
|
utils.AppendToBuffer( self._watch.buf, 'Watches: ----' )
|
||||||
for watch in self._watches:
|
for watch in self._watches:
|
||||||
line = utils.AppendToBuffer( self._watch.win.buffer,
|
line = utils.AppendToBuffer( self._watch.buf,
|
||||||
'Expression: ' + watch[ 'expression' ] )
|
'Expression: '
|
||||||
watch[ '_line' ] = line
|
+ watch.expression[ 'expression' ] )
|
||||||
self._DrawWatchResult( 2, watch )
|
watch.line = line
|
||||||
|
self._DrawWatchResult( self._watch, 2, watch )
|
||||||
|
|
||||||
def _DrawScope( self, indent, scope ):
|
def _DrawScope( self, indent, scope ):
|
||||||
icon = '+' if ( scope.get( 'variablesReference', 0 ) > 0 and
|
icon = '+' if scope.IsExpandable() and not scope.IsExpanded() else '-'
|
||||||
'_variables' not in scope ) else '-'
|
|
||||||
|
|
||||||
line = utils.AppendToBuffer( self._vars.win.buffer,
|
line = utils.AppendToBuffer( self._vars.buf,
|
||||||
'{0}{1} Scope: {2}'.format( ' ' * indent,
|
'{0}{1} Scope: {2}'.format(
|
||||||
icon,
|
' ' * indent,
|
||||||
scope[ 'name' ] ) )
|
icon,
|
||||||
|
scope.scope[ 'name' ] ) )
|
||||||
self._vars.lines[ line ] = scope
|
self._vars.lines[ line ] = scope
|
||||||
|
|
||||||
if '_variables' in scope:
|
if scope.ShouldDrawDrillDown():
|
||||||
indent += 2
|
indent += 2
|
||||||
self._DrawVariables( self._vars, scope[ '_variables' ], indent )
|
self._DrawVariables( self._vars, scope.variables, indent )
|
||||||
|
|
||||||
def _DrawWatchResult( self, indent, watch ):
|
def _DrawWatchResult( self, view, indent, watch, is_short = False ):
|
||||||
if '_result' not in watch:
|
if not watch.result:
|
||||||
return
|
return
|
||||||
|
|
||||||
result = watch[ '_result' ]
|
assert is_short or indent > 0
|
||||||
|
|
||||||
icon = '+' if ( result.get( 'variablesReference', 0 ) > 0 and
|
if is_short:
|
||||||
'_variables' not in result ) else '-'
|
# The first result is always expanded in a hover (short format)
|
||||||
|
icon = ''
|
||||||
|
marker = ''
|
||||||
|
leader = ''
|
||||||
|
else:
|
||||||
|
icon = '+' if ( watch.result.IsExpandable() and
|
||||||
|
not watch.result.IsExpanded() ) else '-'
|
||||||
|
marker = '*' if watch.result.changed else ' '
|
||||||
|
leader = ' Result: '
|
||||||
|
|
||||||
result_str = result[ 'result' ]
|
line = '{indent}{marker}{icon}{leader}{result}'.format(
|
||||||
if result_str is None:
|
# We borrow 1 space of indent to draw the change marker
|
||||||
result_str = 'null'
|
indent = ' ' * ( indent - 1 ),
|
||||||
|
marker = marker,
|
||||||
|
icon = icon,
|
||||||
|
leader = leader,
|
||||||
|
result = watch.result.result.get( 'result', '<unknown>' ) )
|
||||||
|
|
||||||
line = '{0}{1} Result: {2} '.format( ' ' * indent, icon, result_str )
|
line = utils.AppendToBuffer( view.buf, line.split( '\n' ) )
|
||||||
line = utils.AppendToBuffer( self._watch.win.buffer, line.split( '\n' ) )
|
view.lines[ line ] = watch.result
|
||||||
self._watch.lines[ line ] = result
|
|
||||||
|
|
||||||
if '_variables' in result:
|
if watch.result.ShouldDrawDrillDown():
|
||||||
indent = 4
|
self._DrawVariables( view, watch.result.variables, indent + 2, is_short )
|
||||||
self._DrawVariables( self._watch, result[ '_variables' ], indent )
|
|
||||||
|
|
||||||
def _ConsumeVariables( self, draw, parent, message ):
|
def _ConsumeVariables( self, draw, parent, message ):
|
||||||
for variable in message[ 'body' ][ 'variables' ]:
|
new_variables = []
|
||||||
if '_variables' not in parent:
|
for variable_body in message[ 'body' ][ 'variables' ]:
|
||||||
parent[ '_variables' ] = []
|
if parent.variables is None:
|
||||||
|
parent.variables = []
|
||||||
parent[ '_variables' ].append( variable )
|
|
||||||
|
|
||||||
# If the variable was previously expanded, expand it again
|
|
||||||
for index, v in enumerate( parent.get( '_old_variables', [] ) ):
|
|
||||||
if v[ 'name' ] == variable[ 'name' ]:
|
|
||||||
if ( v.get( '_expanded', False ) and
|
|
||||||
variable.get( 'variablesReference', 0 ) > 0 ):
|
|
||||||
|
|
||||||
variable[ '_expanded' ] = True
|
|
||||||
variable[ '_old_variables' ] = v.get( '_variables', [] )
|
|
||||||
|
|
||||||
self._connection.DoRequest( partial( self._ConsumeVariables,
|
|
||||||
draw,
|
|
||||||
variable ), {
|
|
||||||
'command': 'variables',
|
|
||||||
'arguments': {
|
|
||||||
'variablesReference': variable[ 'variablesReference' ]
|
|
||||||
},
|
|
||||||
} )
|
|
||||||
|
|
||||||
|
# Find the variable in parent
|
||||||
|
found = False
|
||||||
|
for index, v in enumerate( parent.variables ):
|
||||||
|
if v.variable[ 'name' ] == variable_body[ 'name' ]:
|
||||||
|
variable = v
|
||||||
|
found = True
|
||||||
break
|
break
|
||||||
|
if not found:
|
||||||
|
variable = Variable( parent, variable_body )
|
||||||
|
else:
|
||||||
|
variable.Update( variable_body )
|
||||||
|
|
||||||
if '_old_variables' in parent:
|
new_variables.append( variable )
|
||||||
del parent[ '_old_variables' ]
|
|
||||||
|
if variable.IsExpandable() and variable.IsExpanded():
|
||||||
|
self._connection.DoRequest( partial( self._ConsumeVariables,
|
||||||
|
draw,
|
||||||
|
variable ), {
|
||||||
|
'command': 'variables',
|
||||||
|
'arguments': {
|
||||||
|
'variablesReference': variable.VariablesReference()
|
||||||
|
},
|
||||||
|
} )
|
||||||
|
|
||||||
|
parent.variables = new_variables
|
||||||
|
|
||||||
draw()
|
draw()
|
||||||
|
|
||||||
def ShowBalloon( self, frame, expression ):
|
def SetSyntax( self, syntax ):
|
||||||
if not self._connection:
|
# TODO: Switch to View.syntax
|
||||||
return
|
self._current_syntax = utils.SetSyntax( self._current_syntax,
|
||||||
|
syntax,
|
||||||
def handler( message ):
|
self._vars.buf,
|
||||||
# TODO: this result count be expandable, but we have no way to allow the
|
self._watch.buf )
|
||||||
# user to interact with the balloon to expand it.
|
# vim: sw=2
|
||||||
body = message[ 'body' ]
|
|
||||||
result = body[ 'result' ]
|
|
||||||
if result is None:
|
|
||||||
result = 'null'
|
|
||||||
display = [
|
|
||||||
'Type: ' + body.get( 'type', '<unknown>' ),
|
|
||||||
'Value: ' + result
|
|
||||||
]
|
|
||||||
utils.DisplayBaloon( self._is_term, display )
|
|
||||||
|
|
||||||
def failure_handler( reason, message ):
|
|
||||||
display = [ reason ]
|
|
||||||
utils.DisplayBaloon( self._is_term, display )
|
|
||||||
|
|
||||||
|
|
||||||
self._connection.DoRequest( handler, {
|
|
||||||
'command': 'evaluate',
|
|
||||||
'arguments': {
|
|
||||||
'expression': expression,
|
|
||||||
'frameId': frame[ 'id' ],
|
|
||||||
'context': 'hover',
|
|
||||||
}
|
|
||||||
}, failure_handler )
|
|
||||||
|
|
|
||||||
97
python3/vimspector/vendor/json_minify.py
vendored
Normal file
97
python3/vimspector/vendor/json_minify.py
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
# Copyright © 2020 Kyle Simpson <getify@gmail.com>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
# a copy of this software and associated documentation files (the "Software"),
|
||||||
|
# to deal in the Software without restriction, including without limitation
|
||||||
|
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
# and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
# Software is furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included
|
||||||
|
# in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""A port of the `JSON-minify` utility to the Python language.
|
||||||
|
|
||||||
|
Based on JSON.minify.js: https://github.com/getify/JSON.minify
|
||||||
|
|
||||||
|
Contributers:
|
||||||
|
- Gerald Storer
|
||||||
|
- Contributed original version
|
||||||
|
- Felipe Machado
|
||||||
|
- Performance optimization
|
||||||
|
- Pradyun S. Gedam
|
||||||
|
- Conditions and variable names changed
|
||||||
|
- Reformatted tests and moved to separate file
|
||||||
|
- Made into a PyPI Package
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
# Vimspector modification: strip_space defaults to False; we don't actually want
|
||||||
|
# to minify - we just want to strip comments.
|
||||||
|
def minify(string, strip_space=False):
|
||||||
|
tokenizer = re.compile('"|(/\*)|(\*/)|(//)|\n|\r')
|
||||||
|
end_slashes_re = re.compile(r'(\\)*$')
|
||||||
|
|
||||||
|
in_string = False
|
||||||
|
in_multi = False
|
||||||
|
in_single = False
|
||||||
|
|
||||||
|
new_str = []
|
||||||
|
index = 0
|
||||||
|
|
||||||
|
for match in re.finditer(tokenizer, string):
|
||||||
|
|
||||||
|
if not (in_multi or in_single):
|
||||||
|
tmp = string[index:match.start()]
|
||||||
|
if not in_string and strip_space:
|
||||||
|
# replace white space as defined in standard
|
||||||
|
tmp = re.sub('[ \t\n\r]+', '', tmp)
|
||||||
|
new_str.append(tmp)
|
||||||
|
elif not strip_space:
|
||||||
|
# Replace comments with white space so that the JSON parser reports
|
||||||
|
# the correct column numbers on parsing errors.
|
||||||
|
new_str.append(' ' * (match.start() - index))
|
||||||
|
|
||||||
|
index = match.end()
|
||||||
|
val = match.group()
|
||||||
|
|
||||||
|
if val == '"' and not (in_multi or in_single):
|
||||||
|
escaped = end_slashes_re.search(string, 0, match.start())
|
||||||
|
|
||||||
|
# start of string or unescaped quote character to end string
|
||||||
|
if not in_string or (escaped is None or len(escaped.group()) % 2 == 0): # noqa
|
||||||
|
in_string = not in_string
|
||||||
|
index -= 1 # include " character in next catch
|
||||||
|
elif not (in_string or in_multi or in_single):
|
||||||
|
if val == '/*':
|
||||||
|
in_multi = True
|
||||||
|
elif val == '//':
|
||||||
|
in_single = True
|
||||||
|
elif val == '*/' and in_multi and not (in_string or in_single):
|
||||||
|
in_multi = False
|
||||||
|
if not strip_space:
|
||||||
|
new_str.append(' ' * len(val))
|
||||||
|
elif val in '\r\n' and not (in_multi or in_string) and in_single:
|
||||||
|
in_single = False
|
||||||
|
elif not ((in_multi or in_single) or (val in ' \r\n\t' and strip_space)): # noqa
|
||||||
|
new_str.append(val)
|
||||||
|
|
||||||
|
if not strip_space:
|
||||||
|
if val in '\r\n':
|
||||||
|
new_str.append(val)
|
||||||
|
elif in_multi or in_single:
|
||||||
|
new_str.append(' ' * len(val))
|
||||||
|
|
||||||
|
new_str.append(string[index:])
|
||||||
|
return ''.join(new_str)
|
||||||
190
run_tests
190
run_tests
|
|
@ -1,12 +1,130 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
RUN_VIM="vim --clean --not-a-term"
|
SetBaseDir() {
|
||||||
|
BASEDIR=$1
|
||||||
|
if [[ ! $BASEDIR = /* ]]; then
|
||||||
|
# Relative
|
||||||
|
BASEDIR=$(pwd)/${BASEDIR}
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
SetBaseDir $(dirname $0)
|
||||||
|
INSTALL=0
|
||||||
|
UPDATE=0
|
||||||
|
INSTALLER_ARGS=''
|
||||||
|
RUN_VIM="vim -N --clean --not-a-term"
|
||||||
RUN_TEST="${RUN_VIM} -S lib/run_test.vim"
|
RUN_TEST="${RUN_VIM} -S lib/run_test.vim"
|
||||||
|
BASEDIR_CMD='py3 pass'
|
||||||
|
|
||||||
|
# 1 is stdout
|
||||||
|
out_fd=1
|
||||||
|
|
||||||
|
while [ -n "$1" ]; do
|
||||||
|
case "$1" in
|
||||||
|
"--basedir"|"--base-dir"|"--test-base")
|
||||||
|
shift
|
||||||
|
SetBaseDir $1
|
||||||
|
shift
|
||||||
|
BASEDIR_CMD="let g:vimspector_base_dir='${BASEDIR}'"
|
||||||
|
;;
|
||||||
|
"--install")
|
||||||
|
INSTALL=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
"--install-method")
|
||||||
|
shift
|
||||||
|
INSTALL=$1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
"--update"|"--upgrade")
|
||||||
|
UPDATE=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
"--report")
|
||||||
|
shift
|
||||||
|
VIMSPECTOR_TEST_STDOUT=$1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
"--quiet")
|
||||||
|
shift
|
||||||
|
# send the output to /dev/null
|
||||||
|
# https://stackoverflow.com/a/47553900
|
||||||
|
# Note we can't use {out_fd} here because the bash version in CI is too
|
||||||
|
# old on macOS
|
||||||
|
out_fd=3
|
||||||
|
exec 3>/dev/null
|
||||||
|
INSTALLER_ARGS="${INSTALLER_ARGS} --quiet"
|
||||||
|
;;
|
||||||
|
"--")
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
"--help")
|
||||||
|
shift
|
||||||
|
echo "$(basename $0) [--basedir <basedir>] [--report output] [--quiet] [--install] <optional list of tests in form file:func>"
|
||||||
|
echo ""
|
||||||
|
echo " --basedir <basedir> path to runtime directory like the optino to install_gadget.py"
|
||||||
|
echo " --install run install_gadget.py, useful with --basedir"
|
||||||
|
echo " --report <messages|all> which logs to dump to stdout after a test"
|
||||||
|
echo " --quiet suppress vim's stdout"
|
||||||
|
echo "e.g.: "
|
||||||
|
echo " - run all tests: $0"
|
||||||
|
echo " - run specific tests script: $0 signature_help.test.vim"
|
||||||
|
echo " - run specific tests fun: $0 signature_help.test.vim:Test_signatures_TopLine\(\)"
|
||||||
|
echo " - run all tests in a clean env: $0 --basedir \$(mktemp -d) --install"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# We use fd 3 for vim's output. Send it to stdout if not already redirected
|
||||||
|
# Note: can't use ${out_fd} in a redirect because redirects happen before
|
||||||
|
# variable substitution
|
||||||
|
if [ "${out_fd}" = "1" ]; then
|
||||||
|
exec 3>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$INSTALL" = "1" ] || [ "$INSTALL" = "script" ]; then
|
||||||
|
if ! python3 $(dirname $0)/install_gadget.py \
|
||||||
|
--basedir ${BASEDIR} \
|
||||||
|
${INSTALLER_ARGS} \
|
||||||
|
--all \
|
||||||
|
--force-enable-csharp; then
|
||||||
|
echo "Script installation reported errors" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$INSTALL" = "1" ] || [ "$INSTALL" = "vim" ]; then
|
||||||
|
if ! $RUN_VIM -u $(dirname $0)/tests/vimrc \
|
||||||
|
--cmd "${BASEDIR_CMD}" \
|
||||||
|
-c 'autocmd User VimspectorInstallSuccess qa!' \
|
||||||
|
-c 'autocmd User VimspectorInstallFailed cquit!' \
|
||||||
|
-c "VimspectorInstall --all netcoredbg"; then
|
||||||
|
echo "Vim installation reported errors" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$UPDATE" = "1" ]; then
|
||||||
|
if ! $RUN_VIM -u $(dirname $0)/tests/vimrc \
|
||||||
|
--cmd "${BASEDIR_CMD}" \
|
||||||
|
-c 'autocmd User VimspectorInstallSuccess qa!' \
|
||||||
|
-c 'autocmd User VimspectorInstallFailed cquit!' \
|
||||||
|
-c "VimspectorUpdate"; then
|
||||||
|
echo "Vim update reported errors" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
if [ -z "$VIMSPECTOR_MIMODE" ]; then
|
if [ -z "$VIMSPECTOR_MIMODE" ]; then
|
||||||
if which -s lldb; then
|
if which lldb >/dev/null 2>&1; then
|
||||||
export VIMSPECTOR_MIMODE=lldb
|
export VIMSPECTOR_MIMODE=lldb
|
||||||
elif which -s gdb; then
|
elif which gdb >/dev/null 2>&1; then
|
||||||
export VIMSPECTOR_MIMODE=gdb
|
export VIMSPECTOR_MIMODE=gdb
|
||||||
else
|
else
|
||||||
echo "Couldn't guess VIMSPECTOR_MIMODE. Need lldb or gdb in path"
|
echo "Couldn't guess VIMSPECTOR_MIMODE. Need lldb or gdb in path"
|
||||||
|
|
@ -14,18 +132,27 @@ if [ -z "$VIMSPECTOR_MIMODE" ]; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Testing with VIMSPECTOR_MIMODE=$VIMSPECTOR_MIMODE"
|
echo "Testing with:"
|
||||||
|
echo " * VIMSPECTOR_MIMODE=$VIMSPECTOR_MIMODE"
|
||||||
|
echo " * RUN_VIM=$RUN_VIM"
|
||||||
|
echo " * RUN_TEST=$RUN_TEST"
|
||||||
|
echo " * BASEDIR=$BASEDIR"
|
||||||
|
echo " * BASEDIR_CMD=$BASEDIR_CMD"
|
||||||
|
|
||||||
|
|
||||||
echo "%SETUP - Building test programs..."
|
echo "%SETUP - Building test programs..."
|
||||||
set -e
|
set -e
|
||||||
pushd tests/testdata/cpp/simple
|
pushd tests/testdata/cpp/simple
|
||||||
make clean simple
|
make clean all
|
||||||
|
popd
|
||||||
|
pushd support/test/csharp
|
||||||
|
dotnet build
|
||||||
popd
|
popd
|
||||||
set +e
|
set +e
|
||||||
echo "%DONE - built test programs"
|
echo "%DONE - built test programs"
|
||||||
|
|
||||||
pushd tests > /dev/null
|
# Start
|
||||||
|
pushd $(dirname $0)/tests > /dev/null
|
||||||
echo "Running Vimspector Vim tests"
|
echo "Running Vimspector Vim tests"
|
||||||
|
|
||||||
RESULT=0
|
RESULT=0
|
||||||
|
|
@ -39,23 +166,62 @@ fi
|
||||||
for t in ${TESTS}; do
|
for t in ${TESTS}; do
|
||||||
echo ""
|
echo ""
|
||||||
echo "%RUN: $t"
|
echo "%RUN: $t"
|
||||||
rm -f messages debuglog
|
|
||||||
|
|
||||||
# split on : into fileName and testName
|
# split on : into fileName and testName
|
||||||
IFS=: read -s t T <<< "$t"
|
IFS=: read -s t T <<< "$t"
|
||||||
|
|
||||||
if ${RUN_TEST} --cmd 'au SwapExists * let v:swapchoice = "e"' $t $T; then
|
TESTLOGDIR=${BASEDIR}/tests/logs/$t
|
||||||
|
|
||||||
|
if ${RUN_TEST} --cmd "${BASEDIR_CMD}" \
|
||||||
|
--cmd 'au SwapExists * let v:swapchoice = "e"' $t $T \
|
||||||
|
>&3\
|
||||||
|
&& [ -f $t.res ]; then
|
||||||
echo "%PASS: $t PASSED"
|
echo "%PASS: $t PASSED"
|
||||||
else
|
else
|
||||||
cat messages
|
echo "%FAIL: $t FAILED - see $TESTLOGDIR"
|
||||||
echo "%FAIL: $t FAILED"
|
|
||||||
RESULT=1
|
RESULT=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
rm -rf $TESTLOGDIR
|
||||||
|
mkdir -p $TESTLOGDIR
|
||||||
|
${RUN_VIM} --version > ${TESTLOGDIR}/vimversion
|
||||||
|
if [ "$VIMSPECTOR_TEST_STDOUT" = "messages" ]; then
|
||||||
|
if [ -f messages ]; then
|
||||||
|
echo "%MESSAGES"
|
||||||
|
cat messages
|
||||||
|
echo "%END"
|
||||||
|
else
|
||||||
|
echo "%MESSAGES"
|
||||||
|
echo "No messages found"
|
||||||
|
echo "%END"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
for l in messages debuglog test.log *.testlog; do
|
||||||
|
# In CI we can't view the output files, so we just have to cat them
|
||||||
|
if [ -f $l ]; then
|
||||||
|
if [ "$VIMSPECTOR_TEST_STDOUT" = "all" ]; then
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
echo "*** START: $l ***"
|
||||||
|
cat $l
|
||||||
|
echo "*** END: $l ***"
|
||||||
|
fi
|
||||||
|
mv $l $TESTLOGDIR
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -f $t.res
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# close out_fd if it's not stdout/stderr/
|
||||||
|
(( out_fd > 2 )) && exec 3>&-
|
||||||
|
|
||||||
|
|
||||||
|
echo "Done running tests"
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "All done."
|
echo "All done. Exit with ${RESULT}"
|
||||||
|
|
||||||
exit $RESULT
|
exit $RESULT
|
||||||
|
|
|
||||||
161
support/custom_ui_vimrc
Normal file
161
support/custom_ui_vimrc
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
" setup boilerplate to make this file usable with vim -Nu <tihs file> {{{
|
||||||
|
scriptencoding utf-8
|
||||||
|
execute 'source' expand( '<sfile>:p:h' ) . '/minimal_vimrc'
|
||||||
|
set noequalalways
|
||||||
|
let mapleader = ','
|
||||||
|
let maplocalleader = "\<Space>"
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
" Custom Layout {{{
|
||||||
|
|
||||||
|
function! s:CustomiseUI()
|
||||||
|
let wins = g:vimspector_session_windows
|
||||||
|
|
||||||
|
" Close the Variables window
|
||||||
|
if has( 'nvim' )
|
||||||
|
" No win_execute in neovim
|
||||||
|
call win_gotoid( wins.variables )
|
||||||
|
quit
|
||||||
|
else
|
||||||
|
call win_execute( wins.variables, 'q' )
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Put the stack trace at the top of the "left bar" (rotate)
|
||||||
|
call win_gotoid( wins.stack_trace )
|
||||||
|
wincmd r
|
||||||
|
|
||||||
|
" Make the left column at least 70 chars
|
||||||
|
70wincmd |
|
||||||
|
|
||||||
|
" Make the code window at least 80 chars
|
||||||
|
call win_gotoid( wins.code )
|
||||||
|
80wincmd |
|
||||||
|
|
||||||
|
" Make the output window 10 lines high and right at the top of the screen
|
||||||
|
call win_gotoid( wins.output )
|
||||||
|
10wincmd _
|
||||||
|
wincmd K
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function s:SetUpTerminal()
|
||||||
|
if !has_key( g:vimspector_session_windows, 'terminal' )
|
||||||
|
" There's a neovim bug which means that this doesn't work in neovim
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
let terminal_win = g:vimspector_session_windows.terminal
|
||||||
|
|
||||||
|
" Make the terminal window at most 80 columns wide, ensuring there is enough
|
||||||
|
" sapce for our code window (80 columns) and the left bar (70 columns)
|
||||||
|
|
||||||
|
" Padding is 2 for the 2 vertical split markers and 2 for the sign column in
|
||||||
|
" the code window.
|
||||||
|
let left_bar = 70
|
||||||
|
let code = 80
|
||||||
|
let padding = 4
|
||||||
|
let cols = max( [ min( [ &columns - left_bar - code - padding, 80 ] ), 10 ] )
|
||||||
|
call win_gotoid( terminal_win )
|
||||||
|
execute string(cols) . 'wincmd |'
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:CustomiseWinBar()
|
||||||
|
call win_gotoid( g:vimspector_session_windows.code)
|
||||||
|
aunmenu WinBar
|
||||||
|
nnoremenu WinBar.▷\ ᶠ⁵ :call vimspector#Continue()<CR>
|
||||||
|
nnoremenu WinBar.↷\ ᶠ¹⁰ :call vimspector#StepOver()<CR>
|
||||||
|
nnoremenu WinBar.↓\ ᶠ¹¹ :call vimspector#StepInto()<CR>
|
||||||
|
nnoremenu WinBar.↑\ ˢᶠ¹¹ :call vimspector#StepOut()<CR>
|
||||||
|
nnoremenu WinBar.❘❘\ ᶠ⁶ :call vimspector#Pause()<CR>
|
||||||
|
nnoremenu WinBar.□\ ˢᶠ⁵ :call vimspector#Stop()<CR>
|
||||||
|
nnoremenu WinBar.⟲\ ᶜˢᶠ⁵ :call vimspector#Restart()<CR>
|
||||||
|
nnoremenu WinBar.✕\ ᶠ⁸ :call vimspector#Reset()<CR>
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
augroup TestUICustomistaion
|
||||||
|
autocmd!
|
||||||
|
autocmd User VimspectorUICreated call s:CustomiseUI()
|
||||||
|
autocmd User VimspectorTerminalOpened call s:SetUpTerminal()
|
||||||
|
autocmd User VimspectorUICreated call s:CustomiseWinBar()
|
||||||
|
augroup END
|
||||||
|
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
" Custom sign priority {{{
|
||||||
|
|
||||||
|
let g:vimspector_sign_priority = {
|
||||||
|
\ 'vimspectorBP': 3,
|
||||||
|
\ 'vimspectorBPCond': 2,
|
||||||
|
\ 'vimspectorBPDisabled': 1,
|
||||||
|
\ 'vimspectorPC': 999,
|
||||||
|
\ }
|
||||||
|
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
" Custom mappings while debuggins {{{
|
||||||
|
let s:mapped = {}
|
||||||
|
|
||||||
|
function! s:OnJumpToFrame() abort
|
||||||
|
if has_key( s:mapped, string( bufnr() ) )
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
nmap <silent> <buffer> <LocalLeader>dn <Plug>VimspectorStepOver
|
||||||
|
nmap <silent> <buffer> <LocalLeader>ds <Plug>VimspectorStepInto
|
||||||
|
nmap <silent> <buffer> <LocalLeader>df <Plug>VimspectorStepOut
|
||||||
|
nmap <silent> <buffer> <LocalLeader>dc <Plug>VimspectorContinue
|
||||||
|
nmap <silent> <buffer> <LocalLeader>di <Plug>VimspectorBalloonEval
|
||||||
|
xmap <silent> <buffer> <LocalLeader>di <Plug>VimspectorBalloonEval
|
||||||
|
|
||||||
|
let s:mapped[ string( bufnr() ) ] = { 'modifiable': &modifiable }
|
||||||
|
|
||||||
|
setlocal nomodifiable
|
||||||
|
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:OnDebugEnd() abort
|
||||||
|
|
||||||
|
let original_buf = bufnr()
|
||||||
|
let hidden = &hidden
|
||||||
|
|
||||||
|
try
|
||||||
|
set hidden
|
||||||
|
for bufnr in keys( s:mapped )
|
||||||
|
try
|
||||||
|
execute 'noautocmd buffer' bufnr
|
||||||
|
silent! nunmap <buffer> <LocalLeader>dn
|
||||||
|
silent! nunmap <buffer> <LocalLeader>ds
|
||||||
|
silent! nunmap <buffer> <LocalLeader>df
|
||||||
|
silent! nunmap <buffer> <LocalLeader>dc
|
||||||
|
silent! nunmap <buffer> <LocalLeader>di
|
||||||
|
silent! xunmap <buffer> <LocalLeader>di
|
||||||
|
|
||||||
|
let &l:modifiable = s:mapped[ bufnr ][ 'modifiable' ]
|
||||||
|
endtry
|
||||||
|
endfor
|
||||||
|
finally
|
||||||
|
execute 'noautocmd buffer' original_buf
|
||||||
|
let &hidden = hidden
|
||||||
|
endtry
|
||||||
|
|
||||||
|
let s:mapped = {}
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
augroup TestCustomMappings
|
||||||
|
au!
|
||||||
|
autocmd User VimspectorJumpedToFrame call s:OnJumpToFrame()
|
||||||
|
autocmd User VimspectorDebugEnded call s:OnDebugEnd()
|
||||||
|
augroup END
|
||||||
|
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
" Custom mappings for special buffers {{{
|
||||||
|
|
||||||
|
let g:vimspector_mappings = {
|
||||||
|
\ 'stack_trace': {},
|
||||||
|
\ 'variables': {
|
||||||
|
\ 'set_value': [ '<Tab>', '<C-CR>', 'C' ],
|
||||||
|
\ }
|
||||||
|
\ }
|
||||||
|
|
||||||
|
" }}}
|
||||||
|
|
||||||
|
" vim: foldmethod=marker
|
||||||
8
support/gadget_upgrade/README.md
Normal file
8
support/gadget_upgrade/README.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Manually updating shipped gadgets
|
||||||
|
|
||||||
|
Download the gadget files manuall from their official source into this dir.
|
||||||
|
Run `./checksum.py <list of files>` to get the checksums.
|
||||||
|
|
||||||
|
Update ../../python3/vimspector/gadgets.py with the new version and the
|
||||||
|
checksums.
|
||||||
|
|
||||||
13
support/gadget_upgrade/checksum.py
Executable file
13
support/gadget_upgrade/checksum.py
Executable file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def GetChecksumSHA254( file_path ):
|
||||||
|
with open( file_path, 'rb' ) as existing_file:
|
||||||
|
return hashlib.sha256( existing_file.read() ).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
for arg in sys.argv[ 1: ]:
|
||||||
|
print( f"{ arg } = { GetChecksumSHA254( arg ) }" )
|
||||||
4
support/minimal_vimrc
Normal file
4
support/minimal_vimrc
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
let s:vimspector_path = expand( '<sfile>:p:h:h' )
|
||||||
|
let g:vimspector_enable_mappings = 'HUMAN'
|
||||||
|
exe 'source ' . s:vimspector_path . '/tests/vimrc'
|
||||||
|
|
||||||
15
support/test/bash/.vimspector.json
Normal file
15
support/test/bash/.vimspector.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json",
|
||||||
|
"configurations": {
|
||||||
|
"Run Current Script": {
|
||||||
|
"adapter": "vscode-bash",
|
||||||
|
"autoselect": false,
|
||||||
|
"configuration": {
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${file}",
|
||||||
|
"cwd": "${fileDirname}",
|
||||||
|
"args": [ "*${args}" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
support/test/bash/test_script
Normal file
9
support/test/bash/test_script
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
function Test() {
|
||||||
|
echo $1
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in "$@"; do
|
||||||
|
Test $i
|
||||||
|
done
|
||||||
3
support/test/chrome/run_server
Executable file
3
support/test/chrome/run_server
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
php -S localhost:1234 -t www
|
||||||
|
|
@ -6,5 +6,12 @@ $( document ).ready( function() {
|
||||||
return msg;
|
return msg;
|
||||||
};
|
};
|
||||||
|
|
||||||
alert( 'test: ' + getMessage() );
|
var obj = {
|
||||||
|
test: getMessage(),
|
||||||
|
toast: function() { return 'egg'; },
|
||||||
|
spam: 'ham'
|
||||||
|
};
|
||||||
|
|
||||||
|
alert( 'test: ' + obj.test );
|
||||||
|
alert( 'toast: ' + obj.toast() );
|
||||||
} );
|
} );
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,27 @@
|
||||||
{
|
{
|
||||||
"adapters": {
|
|
||||||
"cppdbg": {
|
|
||||||
"name": "cppdbg",
|
|
||||||
"command": [ "$HOME/.vscode/extensions/ms-vscode.cpptools-0.20.1/debugAdapters/OpenDebugAD7" ],
|
|
||||||
"attach": {
|
|
||||||
"pidProperty": "processId",
|
|
||||||
"pidSelect": "ask"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"simple_c_program - Launch": {
|
"CodeLLDB": {
|
||||||
"adapter": "cppdbg",
|
"adapter": "CodeLLDB",
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"name": "ms Launch",
|
|
||||||
"type": "cppdbg",
|
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceRoot}/test",
|
"program": "${workspaceRoot}/test",
|
||||||
"args": [],
|
|
||||||
"cwd": "${workspaceRoot}",
|
|
||||||
"environment": [],
|
|
||||||
"MIMode": "lldb",
|
|
||||||
"stopAtEntry": true
|
"stopAtEntry": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"simple_c_program - Attach": {
|
"lldb-vscode": {
|
||||||
"adapter": "cppdbg",
|
"adapter": "lldb-vscode",
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"name": "(lldb) Attach",
|
"request": "launch",
|
||||||
"type": "cppdbg",
|
|
||||||
"request": "attach",
|
|
||||||
"program": "${workspaceRoot}/test",
|
"program": "${workspaceRoot}/test",
|
||||||
|
"stopAtEntry": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cpptools": {
|
||||||
|
"adapter": "vscode-cpptools",
|
||||||
|
"configuration": {
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceRoot}/test",
|
||||||
|
"stopOnEntry": true,
|
||||||
"MIMode": "lldb"
|
"MIMode": "lldb"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
def Settings( **kwargs ):
|
def Settings( **kwargs ):
|
||||||
return {
|
return {
|
||||||
'flags': [ '-x', 'c++', '-Wall', '-Wextra' ]
|
'flags': [ '-x', 'c++', '-Wall', '-Wextra', '-std=c++17' ]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace Test
|
namespace Test
|
||||||
|
|
@ -33,6 +35,8 @@ int main ( int argc, char ** argv )
|
||||||
{
|
{
|
||||||
int x{ 10 };
|
int x{ 10 };
|
||||||
|
|
||||||
|
printf( "HOME: %s\n", getenv( "HOME" ) );
|
||||||
|
|
||||||
Test::TestStruct t{ true, {99} };
|
Test::TestStruct t{ true, {99} };
|
||||||
foo( t );
|
foo( t );
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
support/test/csharp/.gitignore
vendored
1
support/test/csharp/.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
||||||
bin/
|
bin/
|
||||||
obj/Debug
|
obj/Debug
|
||||||
|
obj/
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,57 @@
|
||||||
{
|
{
|
||||||
"configurations": {
|
"adapters": {
|
||||||
"launch - netcoredbg": {
|
"netcoredbg-debuglog": {
|
||||||
"adapter": "netcoredbg",
|
"attach": {
|
||||||
"configuration": {
|
"pidProperty": "processId",
|
||||||
"request": "launch",
|
"pidSelect": "ask"
|
||||||
"program": "${workspaceRoot}/bin/Debug/netcoreapp2.2/csharp.dll",
|
|
||||||
"args": [],
|
|
||||||
"stopAtEntry": true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"launch - mono": {
|
"command": [
|
||||||
"adapter": "vscode-mono-debug",
|
"${gadgetDir}/netcoredbg/netcoredbg",
|
||||||
"configuration": {
|
"--interpreter=vscode",
|
||||||
"request": "launch",
|
"--engineLogging=${workspaceRoot}/netcoredbg.engine.log",
|
||||||
"program": "${workspaceRoot}/Program.exe"
|
"--log=${workspaceRoot}/netcoredbg.log"
|
||||||
}
|
],
|
||||||
|
"configuration": {
|
||||||
|
"cwd": "${workspaceRoot}"
|
||||||
|
},
|
||||||
|
"name": "netcoredbg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
//
|
||||||
|
// NOTE:
|
||||||
|
// If you add to this, you must update tests/get_configurations.test.vim
|
||||||
|
//
|
||||||
|
|
||||||
|
"launch - netcoredbg": {
|
||||||
|
"adapter": "netcoredbg",
|
||||||
|
"configuration": {
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceRoot}/bin/Debug/netcoreapp3.1/csharp.dll",
|
||||||
|
"args": [],
|
||||||
|
"stopAtEntry": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"launch - netcoredbg - with debug log": {
|
||||||
|
"adapter": "netcoredbg-debuglog",
|
||||||
|
"configuration": {
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceRoot}/bin/Debug/netcoreapp3.1/csharp.dll",
|
||||||
|
"args": [],
|
||||||
|
"stopAtEntry": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"launch - mono": {
|
||||||
|
"adapter": "vscode-mono-debug",
|
||||||
|
"configuration": {
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceRoot}/Program.exe",
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"args": [],
|
||||||
|
"env": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,36 @@
|
||||||
|
|
||||||
namespace csharp
|
namespace csharp
|
||||||
{
|
{
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
string toaster = "Making round of toast";
|
||||||
{
|
static int max_bread = 100;
|
||||||
Console.WriteLine("Hello World!");
|
int bread = max_bread;
|
||||||
}
|
|
||||||
|
void PrintToast( int r ) {
|
||||||
|
int this_round = ( max_bread - bread - r);
|
||||||
|
Console.WriteLine( this.toaster + ": " + this_round );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MakeToast( int rounds ) {
|
||||||
|
if (this.bread - rounds < 0) {
|
||||||
|
throw new Exception( "No moar bread!" );
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bread -= rounds;
|
||||||
|
for (int r = 0; r < rounds; ++r) {
|
||||||
|
this.PrintToast( r );
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine( "Got only " + this.bread + " left" );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Program p = new Program();
|
||||||
|
for (int x = 1; x < 10; ++ x) {
|
||||||
|
p.MakeToast( x );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 15.0.26124.0
|
VisualStudioVersion = 15.0.26124.0
|
||||||
MinimumVisualStudioVersion = 15.0.26124.0
|
MinimumVisualStudioVersion = 15.0.26124.0
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp", "csharp.csproj", "{91DB205F-E422-430B-BBB8-955110C7B3B6}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -15,4 +17,18 @@ Global
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{91DB205F-E422-430B-BBB8-955110C7B3B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{91DB205F-E422-430B-BBB8-955110C7B3B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{91DB205F-E422-430B-BBB8-955110C7B3B6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{91DB205F-E422-430B-BBB8-955110C7B3B6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{91DB205F-E422-430B-BBB8-955110C7B3B6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{91DB205F-E422-430B-BBB8-955110C7B3B6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{91DB205F-E422-430B-BBB8-955110C7B3B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{91DB205F-E422-430B-BBB8-955110C7B3B6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{91DB205F-E422-430B-BBB8-955110C7B3B6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{91DB205F-E422-430B-BBB8-955110C7B3B6}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{91DB205F-E422-430B-BBB8-955110C7B3B6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{91DB205F-E422-430B-BBB8-955110C7B3B6}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"dgSpecHash": "6/vdr7YprlSIoQecv/nNuLNflFpO0X7eN7jHUinZTsgian9nYpmHMWirsDWMi5l+29TH+Qy8O/QfaB/48QtjRQ==",
|
|
||||||
"success": true
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
|
||||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
|
||||||
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
|
|
||||||
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
|
||||||
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">/Users/ben/.vim/bundle/vimspector/support/test/csharp/obj/project.assets.json</ProjectAssetsFile>
|
|
||||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">/Users/ben/.nuget/packages/</NuGetPackageRoot>
|
|
||||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">/Users/ben/.nuget/packages/;/usr/local/share/dotnet/sdk/NuGetFallbackFolder</NuGetPackageFolders>
|
|
||||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
|
||||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">4.9.4</NuGetToolVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup>
|
|
||||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
|
||||||
<Import Project="/usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.netcore.app/2.2.0/build/netcoreapp2.2/Microsoft.NETCore.App.props" Condition="Exists('/usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.netcore.app/2.2.0/build/netcoreapp2.2/Microsoft.NETCore.App.props')" />
|
|
||||||
</ImportGroup>
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
|
||||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
|
||||||
<Import Project="/usr/local/share/dotnet/sdk/NuGetFallbackFolder/netstandard.library/2.0.3/build/netstandard2.0/NETStandard.Library.targets" Condition="Exists('/usr/local/share/dotnet/sdk/NuGetFallbackFolder/netstandard.library/2.0.3/build/netstandard2.0/NETStandard.Library.targets')" />
|
|
||||||
<Import Project="/usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.netcore.app/2.2.0/build/netcoreapp2.2/Microsoft.NETCore.App.targets" Condition="Exists('/usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.netcore.app/2.2.0/build/netcoreapp2.2/Microsoft.NETCore.App.targets')" />
|
|
||||||
</ImportGroup>
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,742 +0,0 @@
|
||||||
{
|
|
||||||
"version": 3,
|
|
||||||
"targets": {
|
|
||||||
".NETCoreApp,Version=v2.2": {
|
|
||||||
"Microsoft.NETCore.App/2.2.0": {
|
|
||||||
"type": "package",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.NETCore.DotNetHostPolicy": "2.2.0",
|
|
||||||
"Microsoft.NETCore.Platforms": "2.2.0",
|
|
||||||
"Microsoft.NETCore.Targets": "2.0.0",
|
|
||||||
"NETStandard.Library": "2.0.3"
|
|
||||||
},
|
|
||||||
"compile": {
|
|
||||||
"ref/netcoreapp2.2/Microsoft.CSharp.dll": {},
|
|
||||||
"ref/netcoreapp2.2/Microsoft.VisualBasic.dll": {},
|
|
||||||
"ref/netcoreapp2.2/Microsoft.Win32.Primitives.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.AppContext.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Buffers.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Collections.Concurrent.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Collections.Immutable.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Collections.NonGeneric.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Collections.Specialized.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Collections.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.Annotations.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.DataAnnotations.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.EventBasedAsync.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.Primitives.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.TypeConverter.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Configuration.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Console.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Core.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Data.Common.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Data.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Contracts.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Debug.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.DiagnosticSource.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.FileVersionInfo.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Process.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.StackTrace.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.TextWriterTraceListener.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Tools.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.TraceSource.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Tracing.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Drawing.Primitives.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Drawing.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Dynamic.Runtime.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Globalization.Calendars.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Globalization.Extensions.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Globalization.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.IO.Compression.Brotli.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.IO.Compression.FileSystem.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.IO.Compression.ZipFile.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.IO.Compression.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.IO.FileSystem.DriveInfo.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.IO.FileSystem.Primitives.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.IO.FileSystem.Watcher.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.IO.FileSystem.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.IO.IsolatedStorage.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.IO.MemoryMappedFiles.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.IO.Pipes.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.IO.UnmanagedMemoryStream.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.IO.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Linq.Expressions.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Linq.Parallel.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Linq.Queryable.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Linq.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Memory.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.Http.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.HttpListener.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.Mail.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.NameResolution.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.NetworkInformation.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.Ping.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.Primitives.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.Requests.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.Security.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.ServicePoint.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.Sockets.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebClient.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebHeaderCollection.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebProxy.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebSockets.Client.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebSockets.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Net.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Numerics.Vectors.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Numerics.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.ObjectModel.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.DispatchProxy.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Emit.ILGeneration.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Emit.Lightweight.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Emit.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Extensions.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Metadata.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Primitives.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.TypeExtensions.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Resources.Reader.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Resources.ResourceManager.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Resources.Writer.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.CompilerServices.VisualC.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Extensions.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Handles.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.InteropServices.RuntimeInformation.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.InteropServices.WindowsRuntime.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.InteropServices.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Loader.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Numerics.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Serialization.Formatters.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Serialization.Json.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Serialization.Primitives.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Serialization.Xml.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Serialization.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Security.Claims.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.Algorithms.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.Csp.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.Encoding.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.Primitives.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.X509Certificates.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Security.Principal.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Security.SecureString.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Security.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.ServiceModel.Web.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.ServiceProcess.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Text.Encoding.Extensions.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Text.Encoding.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Text.RegularExpressions.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Overlapped.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Tasks.Dataflow.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Tasks.Extensions.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Tasks.Parallel.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Tasks.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Thread.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Threading.ThreadPool.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Timer.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Threading.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Transactions.Local.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Transactions.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.ValueTuple.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Web.HttpUtility.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Web.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Windows.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Xml.Linq.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Xml.ReaderWriter.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Xml.Serialization.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Xml.XDocument.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Xml.XPath.XDocument.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Xml.XPath.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Xml.XmlDocument.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Xml.XmlSerializer.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.Xml.dll": {},
|
|
||||||
"ref/netcoreapp2.2/System.dll": {},
|
|
||||||
"ref/netcoreapp2.2/WindowsBase.dll": {},
|
|
||||||
"ref/netcoreapp2.2/mscorlib.dll": {},
|
|
||||||
"ref/netcoreapp2.2/netstandard.dll": {}
|
|
||||||
},
|
|
||||||
"build": {
|
|
||||||
"build/netcoreapp2.2/Microsoft.NETCore.App.props": {},
|
|
||||||
"build/netcoreapp2.2/Microsoft.NETCore.App.targets": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.NETCore.DotNetAppHost/2.2.0": {
|
|
||||||
"type": "package"
|
|
||||||
},
|
|
||||||
"Microsoft.NETCore.DotNetHostPolicy/2.2.0": {
|
|
||||||
"type": "package",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.NETCore.DotNetHostResolver": "2.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.NETCore.DotNetHostResolver/2.2.0": {
|
|
||||||
"type": "package",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.NETCore.DotNetAppHost": "2.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.NETCore.Platforms/2.2.0": {
|
|
||||||
"type": "package",
|
|
||||||
"compile": {
|
|
||||||
"lib/netstandard1.0/_._": {}
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/netstandard1.0/_._": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.NETCore.Targets/2.0.0": {
|
|
||||||
"type": "package",
|
|
||||||
"compile": {
|
|
||||||
"lib/netstandard1.0/_._": {}
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/netstandard1.0/_._": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"NETStandard.Library/2.0.3": {
|
|
||||||
"type": "package",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.NETCore.Platforms": "1.1.0"
|
|
||||||
},
|
|
||||||
"compile": {
|
|
||||||
"lib/netstandard1.0/_._": {}
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/netstandard1.0/_._": {}
|
|
||||||
},
|
|
||||||
"build": {
|
|
||||||
"build/netstandard2.0/NETStandard.Library.targets": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"libraries": {
|
|
||||||
"Microsoft.NETCore.App/2.2.0": {
|
|
||||||
"sha512": "7z5l8Jp324S8bU8+yyWeYHXUFYvKyiI5lqS1dXgTzOx1H69Qbf6df12kCKlNX45LpMfCMd4U3M6p7Rl5Zk7SLA==",
|
|
||||||
"type": "package",
|
|
||||||
"path": "microsoft.netcore.app/2.2.0",
|
|
||||||
"files": [
|
|
||||||
".nupkg.metadata",
|
|
||||||
".signature.p7s",
|
|
||||||
"LICENSE.TXT",
|
|
||||||
"Microsoft.NETCore.App.versions.txt",
|
|
||||||
"THIRD-PARTY-NOTICES.TXT",
|
|
||||||
"build/netcoreapp2.2/Microsoft.NETCore.App.PlatformManifest.txt",
|
|
||||||
"build/netcoreapp2.2/Microsoft.NETCore.App.props",
|
|
||||||
"build/netcoreapp2.2/Microsoft.NETCore.App.targets",
|
|
||||||
"microsoft.netcore.app.2.2.0.nupkg.sha512",
|
|
||||||
"microsoft.netcore.app.nuspec",
|
|
||||||
"ref/netcoreapp2.2/Microsoft.CSharp.dll",
|
|
||||||
"ref/netcoreapp2.2/Microsoft.CSharp.xml",
|
|
||||||
"ref/netcoreapp2.2/Microsoft.VisualBasic.dll",
|
|
||||||
"ref/netcoreapp2.2/Microsoft.VisualBasic.xml",
|
|
||||||
"ref/netcoreapp2.2/Microsoft.Win32.Primitives.dll",
|
|
||||||
"ref/netcoreapp2.2/Microsoft.Win32.Primitives.xml",
|
|
||||||
"ref/netcoreapp2.2/System.AppContext.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Buffers.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Buffers.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Collections.Concurrent.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Collections.Concurrent.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Collections.Immutable.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Collections.Immutable.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Collections.NonGeneric.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Collections.NonGeneric.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Collections.Specialized.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Collections.Specialized.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Collections.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Collections.xml",
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.Annotations.dll",
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.Annotations.xml",
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.DataAnnotations.dll",
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.EventBasedAsync.dll",
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.EventBasedAsync.xml",
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.Primitives.dll",
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.Primitives.xml",
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.TypeConverter.dll",
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.TypeConverter.xml",
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.dll",
|
|
||||||
"ref/netcoreapp2.2/System.ComponentModel.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Configuration.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Console.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Console.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Core.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Data.Common.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Data.Common.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Data.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Contracts.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Contracts.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Debug.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Debug.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.DiagnosticSource.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.DiagnosticSource.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.FileVersionInfo.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.FileVersionInfo.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Process.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Process.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.StackTrace.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.StackTrace.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.TextWriterTraceListener.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.TextWriterTraceListener.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Tools.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Tools.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.TraceSource.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.TraceSource.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Tracing.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Diagnostics.Tracing.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Drawing.Primitives.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Drawing.Primitives.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Drawing.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Dynamic.Runtime.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Globalization.Calendars.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Globalization.Extensions.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Globalization.dll",
|
|
||||||
"ref/netcoreapp2.2/System.IO.Compression.Brotli.dll",
|
|
||||||
"ref/netcoreapp2.2/System.IO.Compression.FileSystem.dll",
|
|
||||||
"ref/netcoreapp2.2/System.IO.Compression.ZipFile.dll",
|
|
||||||
"ref/netcoreapp2.2/System.IO.Compression.ZipFile.xml",
|
|
||||||
"ref/netcoreapp2.2/System.IO.Compression.dll",
|
|
||||||
"ref/netcoreapp2.2/System.IO.Compression.xml",
|
|
||||||
"ref/netcoreapp2.2/System.IO.FileSystem.DriveInfo.dll",
|
|
||||||
"ref/netcoreapp2.2/System.IO.FileSystem.DriveInfo.xml",
|
|
||||||
"ref/netcoreapp2.2/System.IO.FileSystem.Primitives.dll",
|
|
||||||
"ref/netcoreapp2.2/System.IO.FileSystem.Watcher.dll",
|
|
||||||
"ref/netcoreapp2.2/System.IO.FileSystem.Watcher.xml",
|
|
||||||
"ref/netcoreapp2.2/System.IO.FileSystem.dll",
|
|
||||||
"ref/netcoreapp2.2/System.IO.FileSystem.xml",
|
|
||||||
"ref/netcoreapp2.2/System.IO.IsolatedStorage.dll",
|
|
||||||
"ref/netcoreapp2.2/System.IO.IsolatedStorage.xml",
|
|
||||||
"ref/netcoreapp2.2/System.IO.MemoryMappedFiles.dll",
|
|
||||||
"ref/netcoreapp2.2/System.IO.MemoryMappedFiles.xml",
|
|
||||||
"ref/netcoreapp2.2/System.IO.Pipes.dll",
|
|
||||||
"ref/netcoreapp2.2/System.IO.Pipes.xml",
|
|
||||||
"ref/netcoreapp2.2/System.IO.UnmanagedMemoryStream.dll",
|
|
||||||
"ref/netcoreapp2.2/System.IO.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Linq.Expressions.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Linq.Expressions.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Linq.Parallel.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Linq.Parallel.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Linq.Queryable.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Linq.Queryable.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Linq.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Linq.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Memory.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Memory.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.Http.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.Http.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.HttpListener.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.HttpListener.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.Mail.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.Mail.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.NameResolution.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.NameResolution.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.NetworkInformation.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.NetworkInformation.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.Ping.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.Ping.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.Primitives.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.Primitives.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.Requests.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.Requests.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.Security.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.Security.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.ServicePoint.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.ServicePoint.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.Sockets.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.Sockets.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebClient.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebClient.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebHeaderCollection.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebHeaderCollection.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebProxy.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebProxy.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebSockets.Client.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebSockets.Client.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebSockets.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Net.WebSockets.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Net.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Numerics.Vectors.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Numerics.Vectors.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Numerics.dll",
|
|
||||||
"ref/netcoreapp2.2/System.ObjectModel.dll",
|
|
||||||
"ref/netcoreapp2.2/System.ObjectModel.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.DispatchProxy.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.DispatchProxy.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Emit.ILGeneration.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Emit.ILGeneration.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Emit.Lightweight.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Emit.Lightweight.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Emit.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Emit.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Extensions.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Metadata.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Metadata.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Primitives.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.Primitives.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.TypeExtensions.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.TypeExtensions.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Reflection.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Resources.Reader.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Resources.ResourceManager.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Resources.ResourceManager.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Resources.Writer.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Resources.Writer.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.CompilerServices.VisualC.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.CompilerServices.VisualC.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Extensions.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Extensions.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Handles.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.InteropServices.RuntimeInformation.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.InteropServices.RuntimeInformation.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.InteropServices.WindowsRuntime.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.InteropServices.WindowsRuntime.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.InteropServices.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.InteropServices.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Loader.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Loader.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Numerics.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Numerics.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Serialization.Formatters.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Serialization.Formatters.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Serialization.Json.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Serialization.Json.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Serialization.Primitives.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Serialization.Primitives.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Serialization.Xml.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Serialization.Xml.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.Serialization.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Runtime.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Security.Claims.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Security.Claims.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.Algorithms.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.Algorithms.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.Csp.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.Csp.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.Encoding.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.Encoding.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.Primitives.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.Primitives.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.X509Certificates.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Security.Cryptography.X509Certificates.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Security.Principal.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Security.Principal.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Security.SecureString.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Security.dll",
|
|
||||||
"ref/netcoreapp2.2/System.ServiceModel.Web.dll",
|
|
||||||
"ref/netcoreapp2.2/System.ServiceProcess.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Text.Encoding.Extensions.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Text.Encoding.Extensions.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Text.Encoding.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Text.RegularExpressions.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Text.RegularExpressions.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Overlapped.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Overlapped.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Tasks.Dataflow.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Tasks.Dataflow.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Tasks.Extensions.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Tasks.Extensions.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Tasks.Parallel.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Tasks.Parallel.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Tasks.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Tasks.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Thread.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Thread.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.ThreadPool.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.ThreadPool.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Timer.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.Timer.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Threading.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Transactions.Local.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Transactions.Local.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Transactions.dll",
|
|
||||||
"ref/netcoreapp2.2/System.ValueTuple.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Web.HttpUtility.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Web.HttpUtility.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Web.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Windows.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Xml.Linq.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Xml.ReaderWriter.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Xml.ReaderWriter.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Xml.Serialization.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Xml.XDocument.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Xml.XDocument.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Xml.XPath.XDocument.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Xml.XPath.XDocument.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Xml.XPath.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Xml.XPath.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Xml.XmlDocument.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Xml.XmlSerializer.dll",
|
|
||||||
"ref/netcoreapp2.2/System.Xml.XmlSerializer.xml",
|
|
||||||
"ref/netcoreapp2.2/System.Xml.dll",
|
|
||||||
"ref/netcoreapp2.2/System.dll",
|
|
||||||
"ref/netcoreapp2.2/WindowsBase.dll",
|
|
||||||
"ref/netcoreapp2.2/mscorlib.dll",
|
|
||||||
"ref/netcoreapp2.2/netstandard.dll",
|
|
||||||
"runtime.json"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Microsoft.NETCore.DotNetAppHost/2.2.0": {
|
|
||||||
"sha512": "DrhaKInRKKvN6Ns2VNIlC7ZffLOp9THf8cO6X4fytPRJovJUbF49/zzx4WfgX9E44FMsw9hT8hrKiIqDSHvGvA==",
|
|
||||||
"type": "package",
|
|
||||||
"path": "microsoft.netcore.dotnetapphost/2.2.0",
|
|
||||||
"files": [
|
|
||||||
".nupkg.metadata",
|
|
||||||
".signature.p7s",
|
|
||||||
"LICENSE.TXT",
|
|
||||||
"THIRD-PARTY-NOTICES.TXT",
|
|
||||||
"microsoft.netcore.dotnetapphost.2.2.0.nupkg.sha512",
|
|
||||||
"microsoft.netcore.dotnetapphost.nuspec",
|
|
||||||
"runtime.json"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Microsoft.NETCore.DotNetHostPolicy/2.2.0": {
|
|
||||||
"sha512": "FJie7IoPZFaPgNDxhZGmDBQP/Bs5vPdfca/G2Wf9gd6LIvMYkZcibtmJwB4tcf4KXkaOYfIOo4Cl9sEPMsSzkw==",
|
|
||||||
"type": "package",
|
|
||||||
"path": "microsoft.netcore.dotnethostpolicy/2.2.0",
|
|
||||||
"files": [
|
|
||||||
".nupkg.metadata",
|
|
||||||
".signature.p7s",
|
|
||||||
"LICENSE.TXT",
|
|
||||||
"THIRD-PARTY-NOTICES.TXT",
|
|
||||||
"microsoft.netcore.dotnethostpolicy.2.2.0.nupkg.sha512",
|
|
||||||
"microsoft.netcore.dotnethostpolicy.nuspec",
|
|
||||||
"runtime.json"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Microsoft.NETCore.DotNetHostResolver/2.2.0": {
|
|
||||||
"sha512": "spDm3AJYmebthDNhzY17YLPtvbc+Y1lCLVeiIH1uLJ/hZaM+40pBiPefFR8J1u66Ndkqi8ipR2tEbqPnYnjRhw==",
|
|
||||||
"type": "package",
|
|
||||||
"path": "microsoft.netcore.dotnethostresolver/2.2.0",
|
|
||||||
"files": [
|
|
||||||
".nupkg.metadata",
|
|
||||||
".signature.p7s",
|
|
||||||
"LICENSE.TXT",
|
|
||||||
"THIRD-PARTY-NOTICES.TXT",
|
|
||||||
"microsoft.netcore.dotnethostresolver.2.2.0.nupkg.sha512",
|
|
||||||
"microsoft.netcore.dotnethostresolver.nuspec",
|
|
||||||
"runtime.json"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Microsoft.NETCore.Platforms/2.2.0": {
|
|
||||||
"sha512": "T/J+XZo+YheFTJh8/4uoeJDdz5qOmOMkjg6/VL8mHJ9AnP8+fmV/kcbxeXsob0irRNiChf+V0ig1MCRLp/+Kog==",
|
|
||||||
"type": "package",
|
|
||||||
"path": "microsoft.netcore.platforms/2.2.0",
|
|
||||||
"files": [
|
|
||||||
".nupkg.metadata",
|
|
||||||
".signature.p7s",
|
|
||||||
"LICENSE.TXT",
|
|
||||||
"THIRD-PARTY-NOTICES.TXT",
|
|
||||||
"lib/netstandard1.0/_._",
|
|
||||||
"microsoft.netcore.platforms.2.2.0.nupkg.sha512",
|
|
||||||
"microsoft.netcore.platforms.nuspec",
|
|
||||||
"runtime.json",
|
|
||||||
"useSharedDesignerContext.txt",
|
|
||||||
"version.txt"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Microsoft.NETCore.Targets/2.0.0": {
|
|
||||||
"sha512": "odP/tJj1z6GylFpNo7pMtbd/xQgTC3Ex2If63dRTL38bBNMwsBnJ+RceUIyHdRBC0oik/3NehYT+oECwBhIM3Q==",
|
|
||||||
"type": "package",
|
|
||||||
"path": "microsoft.netcore.targets/2.0.0",
|
|
||||||
"files": [
|
|
||||||
".nupkg.metadata",
|
|
||||||
"LICENSE.TXT",
|
|
||||||
"THIRD-PARTY-NOTICES.TXT",
|
|
||||||
"lib/netstandard1.0/_._",
|
|
||||||
"microsoft.netcore.targets.2.0.0.nupkg.sha512",
|
|
||||||
"microsoft.netcore.targets.nuspec",
|
|
||||||
"runtime.json",
|
|
||||||
"useSharedDesignerContext.txt",
|
|
||||||
"version.txt"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"NETStandard.Library/2.0.3": {
|
|
||||||
"sha512": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
|
|
||||||
"type": "package",
|
|
||||||
"path": "netstandard.library/2.0.3",
|
|
||||||
"files": [
|
|
||||||
".nupkg.metadata",
|
|
||||||
"LICENSE.TXT",
|
|
||||||
"THIRD-PARTY-NOTICES.TXT",
|
|
||||||
"build/netstandard2.0/NETStandard.Library.targets",
|
|
||||||
"build/netstandard2.0/ref/Microsoft.Win32.Primitives.dll",
|
|
||||||
"build/netstandard2.0/ref/System.AppContext.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Collections.Concurrent.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Collections.NonGeneric.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Collections.Specialized.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Collections.dll",
|
|
||||||
"build/netstandard2.0/ref/System.ComponentModel.Composition.dll",
|
|
||||||
"build/netstandard2.0/ref/System.ComponentModel.EventBasedAsync.dll",
|
|
||||||
"build/netstandard2.0/ref/System.ComponentModel.Primitives.dll",
|
|
||||||
"build/netstandard2.0/ref/System.ComponentModel.TypeConverter.dll",
|
|
||||||
"build/netstandard2.0/ref/System.ComponentModel.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Console.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Core.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Data.Common.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Data.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Diagnostics.Contracts.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Diagnostics.Debug.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Diagnostics.FileVersionInfo.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Diagnostics.Process.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Diagnostics.StackTrace.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Diagnostics.TextWriterTraceListener.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Diagnostics.Tools.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Diagnostics.TraceSource.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Diagnostics.Tracing.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Drawing.Primitives.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Drawing.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Dynamic.Runtime.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Globalization.Calendars.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Globalization.Extensions.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Globalization.dll",
|
|
||||||
"build/netstandard2.0/ref/System.IO.Compression.FileSystem.dll",
|
|
||||||
"build/netstandard2.0/ref/System.IO.Compression.ZipFile.dll",
|
|
||||||
"build/netstandard2.0/ref/System.IO.Compression.dll",
|
|
||||||
"build/netstandard2.0/ref/System.IO.FileSystem.DriveInfo.dll",
|
|
||||||
"build/netstandard2.0/ref/System.IO.FileSystem.Primitives.dll",
|
|
||||||
"build/netstandard2.0/ref/System.IO.FileSystem.Watcher.dll",
|
|
||||||
"build/netstandard2.0/ref/System.IO.FileSystem.dll",
|
|
||||||
"build/netstandard2.0/ref/System.IO.IsolatedStorage.dll",
|
|
||||||
"build/netstandard2.0/ref/System.IO.MemoryMappedFiles.dll",
|
|
||||||
"build/netstandard2.0/ref/System.IO.Pipes.dll",
|
|
||||||
"build/netstandard2.0/ref/System.IO.UnmanagedMemoryStream.dll",
|
|
||||||
"build/netstandard2.0/ref/System.IO.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Linq.Expressions.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Linq.Parallel.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Linq.Queryable.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Linq.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Net.Http.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Net.NameResolution.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Net.NetworkInformation.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Net.Ping.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Net.Primitives.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Net.Requests.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Net.Security.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Net.Sockets.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Net.WebHeaderCollection.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Net.WebSockets.Client.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Net.WebSockets.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Net.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Numerics.dll",
|
|
||||||
"build/netstandard2.0/ref/System.ObjectModel.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Reflection.Extensions.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Reflection.Primitives.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Reflection.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Resources.Reader.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Resources.ResourceManager.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Resources.Writer.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Runtime.CompilerServices.VisualC.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Runtime.Extensions.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Runtime.Handles.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Runtime.InteropServices.RuntimeInformation.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Runtime.InteropServices.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Runtime.Numerics.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Runtime.Serialization.Formatters.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Runtime.Serialization.Json.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Runtime.Serialization.Primitives.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Runtime.Serialization.Xml.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Runtime.Serialization.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Runtime.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Security.Claims.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Security.Cryptography.Algorithms.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Security.Cryptography.Csp.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Security.Cryptography.Encoding.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Security.Cryptography.Primitives.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Security.Cryptography.X509Certificates.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Security.Principal.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Security.SecureString.dll",
|
|
||||||
"build/netstandard2.0/ref/System.ServiceModel.Web.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Text.Encoding.Extensions.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Text.Encoding.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Text.RegularExpressions.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Threading.Overlapped.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Threading.Tasks.Parallel.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Threading.Tasks.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Threading.Thread.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Threading.ThreadPool.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Threading.Timer.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Threading.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Transactions.dll",
|
|
||||||
"build/netstandard2.0/ref/System.ValueTuple.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Web.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Windows.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Xml.Linq.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Xml.ReaderWriter.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Xml.Serialization.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Xml.XDocument.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Xml.XPath.XDocument.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Xml.XPath.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Xml.XmlDocument.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Xml.XmlSerializer.dll",
|
|
||||||
"build/netstandard2.0/ref/System.Xml.dll",
|
|
||||||
"build/netstandard2.0/ref/System.dll",
|
|
||||||
"build/netstandard2.0/ref/mscorlib.dll",
|
|
||||||
"build/netstandard2.0/ref/netstandard.dll",
|
|
||||||
"build/netstandard2.0/ref/netstandard.xml",
|
|
||||||
"lib/netstandard1.0/_._",
|
|
||||||
"netstandard.library.2.0.3.nupkg.sha512",
|
|
||||||
"netstandard.library.nuspec"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"projectFileDependencyGroups": {
|
|
||||||
".NETCoreApp,Version=v2.2": [
|
|
||||||
"Microsoft.NETCore.App >= 2.2.0"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"packageFolders": {
|
|
||||||
"/Users/ben/.nuget/packages/": {},
|
|
||||||
"/usr/local/share/dotnet/sdk/NuGetFallbackFolder": {}
|
|
||||||
},
|
|
||||||
"project": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"restore": {
|
|
||||||
"projectUniqueName": "/Users/ben/.vim/bundle/vimspector/support/test/csharp/csharp.csproj",
|
|
||||||
"projectName": "csharp",
|
|
||||||
"projectPath": "/Users/ben/.vim/bundle/vimspector/support/test/csharp/csharp.csproj",
|
|
||||||
"packagesPath": "/Users/ben/.nuget/packages/",
|
|
||||||
"outputPath": "/Users/ben/.vim/bundle/vimspector/support/test/csharp/obj/",
|
|
||||||
"projectStyle": "PackageReference",
|
|
||||||
"fallbackFolders": [
|
|
||||||
"/usr/local/share/dotnet/sdk/NuGetFallbackFolder"
|
|
||||||
],
|
|
||||||
"configFilePaths": [
|
|
||||||
"/Users/ben/.nuget/NuGet/NuGet.Config"
|
|
||||||
],
|
|
||||||
"originalTargetFrameworks": [
|
|
||||||
"netcoreapp2.2"
|
|
||||||
],
|
|
||||||
"sources": {
|
|
||||||
"https://api.nuget.org/v3/index.json": {}
|
|
||||||
},
|
|
||||||
"frameworks": {
|
|
||||||
"netcoreapp2.2": {
|
|
||||||
"projectReferences": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"warningProperties": {
|
|
||||||
"warnAsError": [
|
|
||||||
"NU1605"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"frameworks": {
|
|
||||||
"netcoreapp2.2": {
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.NETCore.App": {
|
|
||||||
"suppressParent": "All",
|
|
||||||
"target": "Package",
|
|
||||||
"version": "[2.2.0, )",
|
|
||||||
"autoReferenced": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"imports": [
|
|
||||||
"net461"
|
|
||||||
],
|
|
||||||
"assetTargetFallback": true,
|
|
||||||
"warn": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
13
support/test/example/attach.vim
Normal file
13
support/test/example/attach.vim
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
if argc() < 2
|
||||||
|
echom 'Usage:' v:argv[ 0 ] 'processName binary'
|
||||||
|
cquit!
|
||||||
|
endif
|
||||||
|
|
||||||
|
setfiletype cpp
|
||||||
|
call vimspector#LaunchWithSettings( #{
|
||||||
|
\ configuration: "C++ - Attach Local Process",
|
||||||
|
\ processName: argv( 0 ),
|
||||||
|
\ binary: argv( 1 ),
|
||||||
|
\ } )
|
||||||
|
|
||||||
|
1,2argd
|
||||||
24
support/test/example/cpp.json
Normal file
24
support/test/example/cpp.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"configurations": {
|
||||||
|
"C++ - Attach Local Process": {
|
||||||
|
"adapter": "vscode-cpptools",
|
||||||
|
"variables": {
|
||||||
|
"PID": {
|
||||||
|
"shell": [ "GetPIDForProcess", "${processName}" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"name": "test",
|
||||||
|
"request": "attach",
|
||||||
|
"program": "${binary}",
|
||||||
|
"processId": "${PID}",
|
||||||
|
|
||||||
|
"type": "cppdbg",
|
||||||
|
"stopAtEntry": true,
|
||||||
|
"setupCommands": [
|
||||||
|
{ "text": "source ${initFile}", "ignoreFailures": true }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,58 @@
|
||||||
{
|
{
|
||||||
|
"adapters": {
|
||||||
|
"dlv-dap": {
|
||||||
|
"variables": {
|
||||||
|
"port": "${unusedLocalPort}"
|
||||||
|
},
|
||||||
|
"command": [
|
||||||
|
"$HOME/go/bin/dlv",
|
||||||
|
"dap",
|
||||||
|
"--listen",
|
||||||
|
"127.0.0.1:${port}"
|
||||||
|
],
|
||||||
|
"port": "${port}"
|
||||||
|
}
|
||||||
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"run": {
|
"run": {
|
||||||
"adapter": "vscode-go",
|
"adapter": "vscode-go",
|
||||||
|
"default": true,
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceRoot}/hello-world.go",
|
"program": "${workspaceRoot}/hello-world.go",
|
||||||
"mode": "debug",
|
"mode": "debug",
|
||||||
"dlvToolPath": "$HOME/go/bin/dlv",
|
"dlvToolPath": "$HOME/go/bin/dlv",
|
||||||
"trace": true
|
"trace": true,
|
||||||
|
"env": { "GO111MODULE": "off" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"run-dap": {
|
||||||
|
"adapter": "dlv-dap",
|
||||||
|
"configuration": {
|
||||||
|
"request": "launch",
|
||||||
|
"env": { "GO111MODULE": "off" },
|
||||||
|
|
||||||
|
"mode": "debug", // debug|test
|
||||||
|
"program": "${workspaceRoot}/hello-world.go"
|
||||||
|
|
||||||
|
// "args": [],
|
||||||
|
// "buildFlags": ...
|
||||||
|
// "stackTraceDepth": ...,
|
||||||
|
// "showGlobalVariables": true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"run-exec": {
|
||||||
|
// NOTE: To use this you _must_ disable optimistaion:
|
||||||
|
// go build -o hello_world -gcflags="all=-N -l"
|
||||||
|
// https://github.com/golang/vscode-go/blob/master/docs/debugging.md#troubleshooting
|
||||||
|
"adapter": "vscode-go",
|
||||||
|
"configuration": {
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceRoot}/hello-world",
|
||||||
|
"mode": "exec",
|
||||||
|
"dlvToolPath": "$HOME/go/bin/dlv",
|
||||||
|
"trace": true,
|
||||||
|
"env": { "GO111MODULE": "off" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
29
support/test/go/name-starts-with-vowel/.vimspector.json
Normal file
29
support/test/go/name-starts-with-vowel/.vimspector.json
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"configurations": {
|
||||||
|
"run-cmd": {
|
||||||
|
"adapter": "vscode-go",
|
||||||
|
"configuration": {
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceRoot}/cmd/namestartswithvowel/main.go",
|
||||||
|
"mode": "debug",
|
||||||
|
"dlvToolPath": "$HOME/go/bin/dlv",
|
||||||
|
"dlvLoadConfig": {
|
||||||
|
"maxArrayValues": 1000,
|
||||||
|
"maxStringLen": 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test-current-file": {
|
||||||
|
"adapter": "vscode-go",
|
||||||
|
"configuration": {
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "test",
|
||||||
|
"program": "${fileDirname}",
|
||||||
|
"cwd": "${fileDirname}",
|
||||||
|
"dlvToolPath": "$GOPATH/bin/dlv",
|
||||||
|
"env": {},
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
support/test/go/name-starts-with-vowel/README.md
Normal file
33
support/test/go/name-starts-with-vowel/README.md
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Purpose
|
||||||
|
|
||||||
|
This example comes with two example vimspector configs for the Go programming language.
|
||||||
|
|
||||||
|
1) `run-cmd` will launch the main programme under `cmd/namestartswithvowel`.
|
||||||
|
1) `test-current-file` will run the tests in the current file in debug mode.
|
||||||
|
|
||||||
|
## Example use-cases
|
||||||
|
|
||||||
|
### run-cmd
|
||||||
|
|
||||||
|
* Open `cmd/namestartswithvowel/main.go`
|
||||||
|
* Add a breakpoint somewhere within the programme
|
||||||
|
* Start the debugger (`:call vimspector#Continue()` or your relevant keymapping)
|
||||||
|
* Select the first launch configuration (`1: run-cmd`)
|
||||||
|
|
||||||
|
### test-current-file
|
||||||
|
|
||||||
|
* Open `internal/vowels/vowels_test.go`
|
||||||
|
* Add a breakpoint somewhere within the test
|
||||||
|
* Start the debugger (`:call vimspector#Continue()` or your relevant keymapping)
|
||||||
|
* Select the second launch configuration (`2: test-current-file`)
|
||||||
|
|
||||||
|
## Additional Configuration
|
||||||
|
|
||||||
|
There are two additional configuration options specified under `run-cmd`; these parameters configure the maximum string/array size to be shown while debugging.
|
||||||
|
|
||||||
|
```
|
||||||
|
"dlvLoadConfig": {
|
||||||
|
"maxArrayValues": 1000,
|
||||||
|
"maxStringLen": 1000
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"example.com/internal/vowels"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
names := []string{"Simon", "Bob", "Jennifer", "Amy", "Duke", "Elizabeth"}
|
||||||
|
|
||||||
|
for _, n := range names {
|
||||||
|
if vowels.NameStartsWithVowel(n) {
|
||||||
|
fmt.Printf("%s starts with a vowel!\n", n)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s does not start with a vowel!\n", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
3
support/test/go/name-starts-with-vowel/go.mod
Normal file
3
support/test/go/name-starts-with-vowel/go.mod
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
module example.com
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package vowels
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func NameStartsWithVowel(name string) bool {
|
||||||
|
s := strings.Split(strings.ToLower(name), "")
|
||||||
|
|
||||||
|
return s[0] == "a" || s[0] == "e" || s[0] == "i" || s[0] == "o" || s[0] == "u"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package vowels
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNameStartsWithVowel(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
input string
|
||||||
|
expectedOutput bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: "Simon",
|
||||||
|
expectedOutput: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "Andy",
|
||||||
|
expectedOutput: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range testCases {
|
||||||
|
t.Run(fmt.Sprintf("%s should product %t", tt.input, tt.expectedOutput), func(t *testing.T) {
|
||||||
|
out := NameStartsWithVowel(tt.input)
|
||||||
|
if out != tt.expectedOutput {
|
||||||
|
t.Errorf("%s produced %t, when %t was expected", tt.input, out, tt.expectedOutput)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,7 @@
|
||||||
{
|
{
|
||||||
"adapters": {
|
|
||||||
"java-debug-server": {
|
|
||||||
"name": "vscode-java",
|
|
||||||
"port": "ask"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"Java Launch": {
|
"Java Launch": {
|
||||||
"adapter": "java-debug-server",
|
"adapter": "vscode-java",
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mainClass": "com.vimspector.test.TestApplication",
|
"mainClass": "com.vimspector.test.TestApplication",
|
||||||
|
|
@ -15,7 +9,33 @@
|
||||||
"classPaths": [ "${workspaceRoot}/target/classes" ],
|
"classPaths": [ "${workspaceRoot}/target/classes" ],
|
||||||
"args": "hello world!",
|
"args": "hello world!",
|
||||||
"stopOnEntry": true,
|
"stopOnEntry": true,
|
||||||
"console": "integratedTerminal"
|
"console": "integratedTerminal",
|
||||||
|
"stepFilters": {
|
||||||
|
"skipClasses": [ "$$JDK" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Java Attach": {
|
||||||
|
"adapter": "vscode-java",
|
||||||
|
"configuration": {
|
||||||
|
"request": "attach",
|
||||||
|
"sourcePaths": [ "${workspaceRoot}/src/main/java" ],
|
||||||
|
"stopOnEntry": true,
|
||||||
|
"hostName": "localhost",
|
||||||
|
"port": "${JVMDebugPort}",
|
||||||
|
"stepFilters": {
|
||||||
|
"skipClasses": [ "$$JDK" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Attach with vscode-javac": {
|
||||||
|
"adapter": "vscode-javac",
|
||||||
|
"configuration": {
|
||||||
|
"request": "attach",
|
||||||
|
"port": "${debugPort}",
|
||||||
|
"sourceRoots": [
|
||||||
|
"${workspaceRoot}/src/main/java"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
support/test/java/test_project/java.vim
Normal file
24
support/test/java/test_project/java.vim
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
let g:ycm_java_jdtls_extension_path = [
|
||||||
|
\ expand( '<sfile>:p:h:h:h:h:h' ) . '/gadgets/macos'
|
||||||
|
\ ]
|
||||||
|
|
||||||
|
let s:jdt_ls_debugger_port = 0
|
||||||
|
function! s:StartDebugging()
|
||||||
|
if s:jdt_ls_debugger_port <= 0
|
||||||
|
" Get the DAP port
|
||||||
|
let s:jdt_ls_debugger_port = youcompleteme#GetCommandResponse(
|
||||||
|
\ 'ExecuteCommand',
|
||||||
|
\ 'vscode.java.startDebugSession' )
|
||||||
|
|
||||||
|
if s:jdt_ls_debugger_port == ''
|
||||||
|
echom "Unable to get DAP port - is YCM initialized?"
|
||||||
|
let s:jdt_ls_debugger_port = 0
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Start debugging with the DAP port
|
||||||
|
call vimspector#LaunchWithSettings( { 'DAPPort': s:jdt_ls_debugger_port } )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
nnoremap <silent> <buffer> <Leader><F5> :call <SID>StartDebugging()<CR>
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<artifactId>TestApplication</artifactId>
|
<artifactId>TestApplication</artifactId>
|
||||||
<version>1</version>
|
<version>1</version>
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<maven.compiler.source>11</maven.compiler.source>
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
<maven.compiler.target>11</maven.compiler.target>
|
||||||
</properties>
|
</properties>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.vimspector.test;
|
||||||
|
|
||||||
|
public class Base
|
||||||
|
{
|
||||||
|
public String DoSomething()
|
||||||
|
{
|
||||||
|
String s = new String();
|
||||||
|
s.replace( "A", "B" );
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,22 @@ public class TestApplication {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class Bass extends Base {
|
||||||
|
String bass = "Pump";
|
||||||
|
@Override
|
||||||
|
public String DoSomething() {
|
||||||
|
if ( Math.random() % 3 == 0 ) {
|
||||||
|
return bass;
|
||||||
|
}
|
||||||
|
return super.DoSomething();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T extends Base> void DoGeneric( T b ) {
|
||||||
|
TestGeneric<T> foo = new TestGeneric<>( b );
|
||||||
|
foo.DoSomethingUseful();
|
||||||
|
}
|
||||||
|
|
||||||
public static void main( String[] args ) {
|
public static void main( String[] args ) {
|
||||||
int numEntries = 0;
|
int numEntries = 0;
|
||||||
for ( String s : args ) {
|
for ( String s : args ) {
|
||||||
|
|
@ -24,6 +40,8 @@ public class TestApplication {
|
||||||
++numEntries;
|
++numEntries;
|
||||||
}
|
}
|
||||||
System.out.println( "Number of entries: " + numEntries );
|
System.out.println( "Number of entries: " + numEntries );
|
||||||
|
|
||||||
|
DoGeneric( new Bass() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.vimspector.test;
|
||||||
|
|
||||||
|
class TestGeneric<T extends Base> {
|
||||||
|
T base;
|
||||||
|
|
||||||
|
public TestGeneric( T b )
|
||||||
|
{
|
||||||
|
this.base = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String DoSomethingUseful() {
|
||||||
|
String s = "A B C" + base.DoSomething();
|
||||||
|
|
||||||
|
return s.replace( "B", "C" );
|
||||||
|
}
|
||||||
|
}
|
||||||
2
support/test/kotlin/.gitignore
vendored
Normal file
2
support/test/kotlin/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
.gradle
|
||||||
|
build
|
||||||
21
support/test/kotlin/.vimspector.json
Normal file
21
support/test/kotlin/.vimspector.json
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"configurations": {
|
||||||
|
"kotlin-debug-adapter launch": {
|
||||||
|
"adapter": "cust_kotlin-debug-adapter",
|
||||||
|
"configuration": {
|
||||||
|
"request": "launch",
|
||||||
|
"projectRoot": "${workspaceFolder}",
|
||||||
|
"mainClass": "vimspector/test/ApplicationKt"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"kotlin-debug-adapter attach": {
|
||||||
|
"adapter": "cust_kotlin-debug-adapter",
|
||||||
|
"configuration": {
|
||||||
|
"request": "attach",
|
||||||
|
"projectRoot": "${workspaceFolder}",
|
||||||
|
"hostName": "${hostName}",
|
||||||
|
"port": "${port}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
support/test/kotlin/build.gradle.kts
Normal file
25
support/test/kotlin/build.gradle.kts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm") version "1.4.0"
|
||||||
|
|
||||||
|
application
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
// Use jcenter for resolving dependencies.
|
||||||
|
// You can declare any Maven/Ivy/file repository here.
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Align versions of all Kotlin components
|
||||||
|
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
||||||
|
|
||||||
|
// Use the Kotlin JDK 8 standard library.
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
// Define the main class for the application.
|
||||||
|
mainClassName = "vimspector.test.ApplicationKt"
|
||||||
|
}
|
||||||
|
|
||||||
1
support/test/kotlin/settings.gradle.kts
Normal file
1
support/test/kotlin/settings.gradle.kts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
rootProject.name = "vimspector-test"
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue