Compare commits
673 commits
| 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 |
161 changed files with 19104 additions and 4102 deletions
73
.github/ISSUE_TEMPLATE/bug_report.md
vendored
73
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -7,40 +7,62 @@ assignees: ''
|
|||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
> DO NOT DELETE THIS TEMPLATE. IF YOU DELETE THIS TEMPLATE AND DO NOT COMPLETE IT, YOUR ISSUE WILL BE CLOSED.
|
||||
|
||||
**To Reproduce**
|
||||
List of steps to reproduce
|
||||
### Describe the bug
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
### Expected behaviour
|
||||
|
||||
**Actual behaviour**
|
||||
What actually happened, including output, log files etc.
|
||||
> Provide A clear and concise description of what you expected to happen.
|
||||
|
||||
Please include:
|
||||
* Vimspector log (~/.vimspector.log)
|
||||
* Output from any or all UI diagnostic tabs (Server, etc.)
|
||||
### Actual behaviour
|
||||
|
||||
**Environemnt**
|
||||
> What actually happened, including output, log files etc.
|
||||
|
||||
NOTE: NeoVim is supported only on a best-effort basis. Please check the README
|
||||
> Please include:
|
||||
> * Vimspector log (~/.vimspector.log)
|
||||
> * Output from any or all UI diagnostic tabs (Server, etc.)
|
||||
|
||||
### 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 is not supported. There is a branch with windows support, which
|
||||
you can contribute to, rather than opening an issue.
|
||||
***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
|
||||
```
|
||||
|
||||
* Output of `vim --version` or `nvim --version`
|
||||
|
||||
```
|
||||
|
|
@ -53,7 +75,7 @@ paste here
|
|||
paste here
|
||||
```
|
||||
|
||||
* Output of `:py3 pass`:
|
||||
* Output of `:py3 print( __import__( 'sys' ).version )`:
|
||||
|
||||
```
|
||||
paste here
|
||||
|
|
@ -65,6 +87,13 @@ paste here
|
|||
paste here
|
||||
```
|
||||
|
||||
* Output of `:py3 import vimspector`:
|
||||
|
||||
```
|
||||
paste here
|
||||
```
|
||||
|
||||
|
||||
* For neovim: output of `:checkhealth`
|
||||
|
||||
```
|
||||
|
|
@ -73,5 +102,13 @@ paste here
|
|||
|
||||
* Operating system: <linux or macOS> and version
|
||||
|
||||
**Additional context**
|
||||
### 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
|
||||
|
||||
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'
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -6,6 +6,7 @@ tests/*.res
|
|||
tests/messages
|
||||
tests/debuglog
|
||||
test.log
|
||||
*.testlog
|
||||
gadgets/
|
||||
package/
|
||||
*.pyc
|
||||
|
|
@ -16,3 +17,7 @@ README.md.toc.*
|
|||
*.vimspector.log
|
||||
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:
|
||||
- author=puremourning
|
||||
- base=master
|
||||
# Review
|
||||
- status-success=code-review/reviewable
|
||||
- status-success=puremourning.vimspector # Azure pipeline
|
||||
|
||||
- "#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
|
||||
merge:
|
||||
method: merge
|
||||
|
|
@ -18,12 +21,16 @@ pull_request_rules:
|
|||
conditions:
|
||||
- author!=puremourning
|
||||
- base=master
|
||||
# Review
|
||||
- status-success=code-review/reviewable
|
||||
- status-success=puremourning.vimspector # Azure pipeline
|
||||
- approved-reviews-by=puremourning
|
||||
|
||||
- "#approved-reviews-by>=1"
|
||||
- "#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:
|
||||
<<: *merge-actions
|
||||
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": {
|
||||
"simple_c_program - lldb-mi Launch": {
|
||||
"adapter": "lldb-mi",
|
||||
"Python: Attach To Vim": {
|
||||
"variables": {
|
||||
"port": "5678",
|
||||
"host": "localhost"
|
||||
},
|
||||
"adapter": "multi-session",
|
||||
"configuration": {
|
||||
"request": "launch",
|
||||
"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"
|
||||
"request": "attach"
|
||||
}
|
||||
},
|
||||
"simple_c_progra - ms Launch": {
|
||||
"adapter": "cppdbg",
|
||||
"Python: Run current script": {
|
||||
"adapter": "debugpy",
|
||||
"configuration": {
|
||||
"name": "ms Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/support/test/cpp/simple_c_program/test",
|
||||
"args": [],
|
||||
"cwd": "$HOME",
|
||||
"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"
|
||||
"program": "${file}",
|
||||
"args": [ "*${args:--update-gadget-config}" ],
|
||||
"justMyCode#json": "${justMyCode:true}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
3
.vintrc.yml
Normal file
3
.vintrc.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
cmdargs:
|
||||
color: true
|
||||
severity: style_problem
|
||||
|
|
@ -38,6 +38,23 @@ def Settings( **kwargs ):
|
|||
}
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
|
|
|
|||
162
CONTRIBUTING.md
162
CONTRIBUTING.md
|
|
@ -10,18 +10,67 @@ forms, such as:
|
|||
|
||||
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 was not completed
|
||||
* 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
|
||||
|
|
@ -37,8 +86,62 @@ 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
|
||||
|
|
@ -46,6 +149,8 @@ When contributing pull requests, I ask that:
|
|||
* 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.
|
||||
|
||||
|
|
@ -62,7 +167,12 @@ There are 2 ways:
|
|||
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.
|
||||
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
|
||||
|
|
@ -72,6 +182,54 @@ 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,120 +13,559 @@
|
|||
" See the License for the specific language governing permissions and
|
||||
" limitations under the License.
|
||||
|
||||
if !has( 'python3' )
|
||||
finish
|
||||
endif
|
||||
|
||||
" Boilerplate {{{
|
||||
let s:save_cpo = &cpoptions
|
||||
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
|
||||
py3 _vimspector_session.Start()
|
||||
let s:enabled = v:null
|
||||
|
||||
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
|
||||
|
||||
function! vimspector#LaunchWithSettings( settings ) abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
py3 _vimspector_session.Start( launch_variables = vim.eval( 'a:settings' ) )
|
||||
endfunction
|
||||
|
||||
function! vimspector#Reset() abort
|
||||
py3 _vimspector_session.Reset()
|
||||
function! vimspector#Reset( ... ) abort
|
||||
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
|
||||
|
||||
function! vimspector#Restart() abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
py3 _vimspector_session.Restart()
|
||||
endfunction
|
||||
|
||||
function! vimspector#ClearBreakpoints() abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
py3 _vimspector_session.ClearBreakpoints()
|
||||
endfunction
|
||||
|
||||
function! vimspector#ToggleBreakpoint() abort
|
||||
py3 _vimspector_session.ToggleBreakpoint()
|
||||
function! vimspector#ToggleBreakpoint( ... ) abort
|
||||
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
|
||||
|
||||
function! vimspector#AddFunctionBreakpoint( function ) abort
|
||||
py3 _vimspector_session.AddFunctionBreakpoint( vim.eval( 'a:function' ) )
|
||||
function! vimspector#SetLineBreakpoint( file_name, line_num, ... ) abort
|
||||
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
|
||||
|
||||
function! vimspector#StepOver() abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
py3 _vimspector_session.StepOver()
|
||||
endfunction
|
||||
|
||||
function! vimspector#StepInto() abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
py3 _vimspector_session.StepInto()
|
||||
endfunction
|
||||
|
||||
function! vimspector#StepOut() abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
py3 _vimspector_session.StepOut()
|
||||
endfunction
|
||||
|
||||
function! vimspector#Continue() abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
py3 _vimspector_session.Continue()
|
||||
endfunction
|
||||
|
||||
function! vimspector#Pause() abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
py3 _vimspector_session.Pause()
|
||||
endfunction
|
||||
|
||||
function! vimspector#Stop() abort
|
||||
py3 _vimspector_session.Stop()
|
||||
function! vimspector#PauseContinueThread() abort
|
||||
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
|
||||
|
||||
function! vimspector#ExpandVariable() abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
py3 _vimspector_session.ExpandVariable()
|
||||
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
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
py3 _vimspector_session.DeleteWatch()
|
||||
endfunction
|
||||
|
||||
function! vimspector#GoToFrame() abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
py3 _vimspector_session.ExpandFrameOrThread()
|
||||
endfunction
|
||||
|
||||
function! vimspector#AddWatch( expr ) abort
|
||||
py3 _vimspector_session.AddWatch( vim.eval( 'a:expr' ) )
|
||||
function! vimspector#UpFrame() abort
|
||||
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
|
||||
|
||||
function! vimspector#AddWatchPrompt( expr ) abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
stopinsert
|
||||
setlocal nomodified
|
||||
call vimspector#AddWatch( a:expr )
|
||||
endfunction
|
||||
|
||||
function! vimspector#Evaluate( expr ) abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
py3 _vimspector_session.ShowOutput( 'Console' )
|
||||
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) )
|
||||
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ), True )
|
||||
endfunction
|
||||
|
||||
function! vimspector#EvaluateConsole( expr ) abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
stopinsert
|
||||
setlocal nomodified
|
||||
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) )
|
||||
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ), False )
|
||||
endfunction
|
||||
|
||||
function! vimspector#ShowOutput( category ) abort
|
||||
py3 _vimspector_session.ShowOutput( vim.eval( 'a:category' ) )
|
||||
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
|
||||
|
||||
function! vimspector#ListBreakpoints() abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
py3 _vimspector_session.ListBreakpoints()
|
||||
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
|
||||
let buffers = py3eval( '_vimspector_session.GetOutputBuffers()' )
|
||||
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
|
||||
return join( py3eval( '_vimspector_session.GetCompletionsSync( '
|
||||
\.' vim.eval( "a:CmdLine" ),'
|
||||
\.' int( vim.eval( "a:CursorPos" ) ) )' ),
|
||||
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 {{{
|
||||
let &cpoptions=s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
|
|
|||
|
|
@ -19,16 +19,323 @@ let s:save_cpo = &cpoptions
|
|||
set cpoptions&vim
|
||||
" }}}
|
||||
|
||||
" Returns: py.ShowBalloon( winnr, expresssion )
|
||||
function! vimspector#internal#balloon#BalloonExpr() abort
|
||||
" winnr + 1 because for *no good reason* winnr is 0 based here unlike
|
||||
" everywhere else
|
||||
" int() because for *no good reason* winnr is a string.
|
||||
return py3eval('_vimspector_session.ShowBalloon('
|
||||
\ . 'int( vim.eval( "v:beval_winnr" ) ) + 1,'
|
||||
\ . 'vim.eval( "v:beval_text" ) )' )
|
||||
scriptencoding utf-8
|
||||
|
||||
let s:popup_win_id = 0
|
||||
let s:nvim_border_win_id = 0
|
||||
"
|
||||
" tooltip dimensions
|
||||
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
|
||||
|
||||
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 {{{
|
||||
let &cpoptions=s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
|
|
|||
|
|
@ -20,12 +20,20 @@ set cpoptions&vim
|
|||
" }}}
|
||||
|
||||
function! s:_OnServerData( channel, data ) abort
|
||||
if !exists( 's:ch' ) || s:ch isnot a:channel
|
||||
return
|
||||
endif
|
||||
|
||||
py3 << EOF
|
||||
_vimspector_session.OnChannelData( vim.eval( 'a:data' ) )
|
||||
EOF
|
||||
endfunction
|
||||
|
||||
function! s:_OnClose( channel ) abort
|
||||
if !exists( 's:ch' ) || s:ch isnot a:channel
|
||||
return
|
||||
endif
|
||||
|
||||
echom 'Channel closed'
|
||||
redraw
|
||||
unlet s:ch
|
||||
|
|
@ -39,7 +47,22 @@ function! vimspector#internal#channel#StartDebugSession( config ) abort
|
|||
return v:false
|
||||
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)'
|
||||
let s:ch = ch_open( l:addr,
|
||||
|
|
@ -52,7 +75,7 @@ function! vimspector#internal#channel#StartDebugSession( config ) abort
|
|||
\ )
|
||||
|
||||
if ch_status( s:ch ) !=# 'open'
|
||||
echom 'Unable to connect to debug adapter'
|
||||
echom 'Unable to connect to' l:addr
|
||||
redraw
|
||||
return v:false
|
||||
endif
|
||||
|
|
@ -72,22 +95,48 @@ EOF
|
|||
endfunction
|
||||
|
||||
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_
|
||||
" 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
|
||||
" nothing.
|
||||
call ch_close( s:ch )
|
||||
call s:_OnClose( s:ch )
|
||||
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
|
||||
|
||||
function! vimspector#internal#channel#Reset() abort
|
||||
if exists( 's:ch' )
|
||||
if exists( 's:ch' ) || exists( 's:job' )
|
||||
call vimspector#internal#channel#StopDebugSession()
|
||||
endif
|
||||
endfunction
|
||||
|
|
|
|||
|
|
@ -20,28 +20,54 @@ set cpoptions&vim
|
|||
" }}}
|
||||
|
||||
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' ) )
|
||||
endfunction
|
||||
|
||||
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' ) )
|
||||
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
|
||||
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
|
||||
redraw
|
||||
unlet s:job
|
||||
if exists( 's:job' )
|
||||
unlet s:job
|
||||
endif
|
||||
py3 _vimspector_session.OnServerExit( vim.eval( 'a:status' ) )
|
||||
endfunction
|
||||
|
||||
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'
|
||||
redraw
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#job#StartDebugSession( config ) abort
|
||||
if exists( 's:job' )
|
||||
echom 'Not starging: Job is already running'
|
||||
echom 'Not starting: Job is already running'
|
||||
redraw
|
||||
return v:false
|
||||
endif
|
||||
|
|
@ -61,12 +87,17 @@ function! vimspector#internal#job#StartDebugSession( config ) abort
|
|||
\ }
|
||||
\ )
|
||||
|
||||
echom 'Started job, status is: ' . job_status( s:job )
|
||||
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 job_status( s:job ) !=# 'run'
|
||||
echom 'Unable to start job, status is: ' . job_status( s:job )
|
||||
redraw
|
||||
if status !=# 'run'
|
||||
return v:false
|
||||
endif
|
||||
|
||||
|
|
@ -105,8 +136,8 @@ function! vimspector#internal#job#StopDebugSession() abort
|
|||
endif
|
||||
|
||||
if job_status( s:job ) ==# 'run'
|
||||
echom 'Terminating job'
|
||||
redraw
|
||||
echom 'Terminating job'
|
||||
redraw
|
||||
call job_stop( s:job, 'kill' )
|
||||
endif
|
||||
endfunction
|
||||
|
|
@ -115,6 +146,13 @@ function! vimspector#internal#job#Reset() abort
|
|||
call vimspector#internal#job#StopDebugSession()
|
||||
endfunction
|
||||
|
||||
function! s:_OnCommandExit( category, ch, code ) abort
|
||||
py3 __import__( "vimspector",
|
||||
\ fromlist = [ "utils" ] ).utils.OnCommandWithLogComplete(
|
||||
\ vim.eval( 'a:category' ),
|
||||
\ int( vim.eval( 'a:code' ) ) )
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort
|
||||
if ! exists( 's:commands' )
|
||||
let s:commands = {}
|
||||
|
|
@ -126,31 +164,30 @@ function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort
|
|||
|
||||
let l:index = len( s:commands[ a:category ] )
|
||||
|
||||
let buf = '_vimspector_log_' . a:category
|
||||
|
||||
call add( s:commands[ a:category ], job_start(
|
||||
\ a:cmd,
|
||||
\ {
|
||||
\ 'out_io': 'buffer',
|
||||
\ 'in_io': 'null',
|
||||
\ 'err_io': 'buffer',
|
||||
\ 'out_name': '_vimspector_log_' . a:category . '_out',
|
||||
\ 'err_name': '_vimspector_log_' . a:category . '_err',
|
||||
\ 'out_msg': 0,
|
||||
\ 'err_msg': 0,
|
||||
\ 'out_name': buf,
|
||||
\ 'err_name': buf,
|
||||
\ 'exit_cb': funcref( 's:_OnCommandExit', [ a:category ] ),
|
||||
\ 'out_modifiable': 0,
|
||||
\ 'err_modifiable': 0,
|
||||
\ 'stoponexit': 'kill'
|
||||
\ } ) )
|
||||
|
||||
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
|
||||
return v:none
|
||||
endif
|
||||
|
||||
let l:stdout = ch_getbufnr(
|
||||
\ 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 ]
|
||||
return bufnr( buf )
|
||||
endfunction
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,14 @@ 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
|
||||
|
|
@ -34,20 +42,50 @@ endfunction
|
|||
|
||||
function! vimspector#internal#neochannel#StartDebugSession( config ) abort
|
||||
if exists( 's:ch' )
|
||||
echom 'Not starging: Channel is already running'
|
||||
echom 'Not starting: Channel is already running'
|
||||
redraw
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let addr = 'localhost:' . a:config[ 'port' ]
|
||||
|
||||
let s:ch = sockconnect( 'tcp', addr, { 'on_data': funcref( 's:_OnEvent' ) } )
|
||||
if s:ch <= 0
|
||||
unlet s:ch
|
||||
return v:false
|
||||
" 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
|
||||
|
||||
return v:true
|
||||
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
|
||||
|
|
@ -62,16 +100,19 @@ function! vimspector#internal#neochannel#Send( msg ) abort
|
|||
endfunction
|
||||
|
||||
function! vimspector#internal#neochannel#StopDebugSession() abort
|
||||
if !exists( 's:ch' )
|
||||
echom "Not stopping session: Channel doesn't exist"
|
||||
redraw
|
||||
return
|
||||
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
|
||||
|
||||
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' )
|
||||
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
|
||||
|
|
|
|||
|
|
@ -22,6 +22,14 @@ 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' ) ) )
|
||||
|
|
@ -31,8 +39,6 @@ function! s:_OnEvent( chan_id, data, event ) abort
|
|||
echom 'Channel exit with status ' . a:data
|
||||
redraw
|
||||
unlet s:job
|
||||
" This causes terminal spam in neovim due to
|
||||
" https://github.com/neovim/neovim/issues/11725
|
||||
py3 _vimspector_session.OnServerExit( vim.eval( 'a:data' ) )
|
||||
endif
|
||||
endfunction
|
||||
|
|
@ -68,8 +74,8 @@ function! vimspector#internal#neojob#StartDebugSession( config ) abort
|
|||
return v:true
|
||||
endfunction
|
||||
|
||||
function! s:JobIsRunning( job ) abort
|
||||
return jobwait( [ s:job ], 0 )[ 0 ] == -1
|
||||
function! vimspector#internal#neojob#JobIsRunning( job ) abort
|
||||
return jobwait( [ a:job ], 0 )[ 0 ] == -1
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neojob#Send( msg ) abort
|
||||
|
|
@ -79,7 +85,7 @@ function! vimspector#internal#neojob#Send( msg ) abort
|
|||
return 0
|
||||
endif
|
||||
|
||||
if !s:JobIsRunning( s:job )
|
||||
if !vimspector#internal#neojob#JobIsRunning( s:job )
|
||||
echom "Can't send message: Job is not running"
|
||||
redraw
|
||||
return 0
|
||||
|
|
@ -94,7 +100,7 @@ function! vimspector#internal#neojob#StopDebugSession() abort
|
|||
return
|
||||
endif
|
||||
|
||||
if s:JobIsRunning( s:job )
|
||||
if vimspector#internal#neojob#JobIsRunning( s:job )
|
||||
echom 'Terminating job'
|
||||
redraw
|
||||
call jobstop( s:job )
|
||||
|
|
@ -106,50 +112,70 @@ function! vimspector#internal#neojob#Reset() abort
|
|||
endfunction
|
||||
|
||||
function! s:_OnCommandEvent( category, id, data, event ) abort
|
||||
if a:data == ['']
|
||||
if v:exiting isnot# v:null
|
||||
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 ] )
|
||||
if a:event ==# 'stdout' || a:event ==# 'stderr'
|
||||
if a:data == ['']
|
||||
return
|
||||
endif
|
||||
|
||||
call appendbufline( buffer, '$', a:data[ 1: ] )
|
||||
finally
|
||||
call s:MakeBufferReadOnly( buffer )
|
||||
call setbufvar( buffer, '&modified', 0 )
|
||||
endtry
|
||||
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
|
||||
|
||||
" if the buffer is visible, scroll it
|
||||
let w = bufwinnr( buffer )
|
||||
if w > 0
|
||||
let cw = winnr()
|
||||
try
|
||||
execute w . 'wincmd w'
|
||||
normal! Gz.
|
||||
finally
|
||||
execute cw . 'wincmd w'
|
||||
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
|
||||
|
||||
|
|
@ -158,6 +184,7 @@ function! s:SetUpHiddenBuffer( buffer ) abort
|
|||
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
|
||||
|
||||
|
|
@ -179,29 +206,29 @@ function! vimspector#internal#neojob#StartCommandWithLog( cmd, category ) abort
|
|||
let s:commands[ a:category ] = {}
|
||||
endif
|
||||
|
||||
let stdout_buf = bufnr( '_vimspector_log_' . a:category . '_out', v:true )
|
||||
let stderr_buf = bufnr( '_vimspector_log_' . a:category . '_err', v:true )
|
||||
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( stdout_buf )
|
||||
call s:SetUpHiddenBuffer( stderr_buf )
|
||||
call s:SetUpHiddenBuffer( buf )
|
||||
|
||||
let id = jobstart(a:cmd,
|
||||
\ {
|
||||
\ 'on_stdout': funcref( 's:_OnCommandEvent',
|
||||
\ [ a:category ] ),
|
||||
\ 'on_stderr': funcref( 's:_OnCommandEvent',
|
||||
\ [ a:category ] )
|
||||
\ [ a:category ] ),
|
||||
\ 'on_exit': funcref( 's:_OnCommandEvent',
|
||||
\ [ a:category ] ),
|
||||
\ } )
|
||||
|
||||
let s:commands[ a:category ][ id ] = {
|
||||
\ 'stdout': stdout_buf,
|
||||
\ 'stderr': stderr_buf
|
||||
\ 'stdout': buf,
|
||||
\ 'stderr': buf
|
||||
\ }
|
||||
|
||||
return [ stdout_buf, stderr_buf ]
|
||||
return buf
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neojob#CleanUpCommand( category ) abort
|
||||
|
|
@ -210,8 +237,11 @@ function! vimspector#internal#neojob#CleanUpCommand( category ) abort
|
|||
endif
|
||||
|
||||
for id in keys( s:commands[ a:category ] )
|
||||
call jobstop( id )
|
||||
call jobwait( id )
|
||||
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
|
||||
|
|
|
|||
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
|
||||
" }}}
|
||||
|
||||
|
|
@ -28,10 +28,9 @@ let s:buffer_to_id = {}
|
|||
function! vimspector#internal#neoterm#PrepareEnvironment( env ) abort
|
||||
let old_env = {}
|
||||
|
||||
let new_env = copy( environ() )
|
||||
for key in keys( a:env )
|
||||
if has_key( new_env, key )
|
||||
let old_env[ key ] = new_env[ key ]
|
||||
if exists( '$' . key )
|
||||
let old_env[ key ] = getenv( key )
|
||||
endif
|
||||
call setenv( key, a:env[ key ] )
|
||||
endfor
|
||||
|
|
@ -47,12 +46,17 @@ function! vimspector#internal#neoterm#ResetEnvironment( env, old_env ) abort
|
|||
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 )
|
||||
vsplit
|
||||
let mods .= 'vertical '
|
||||
let mods .= get( a:opts, 'term_cols', '' )
|
||||
else
|
||||
split
|
||||
let mods .= get( a:opts, 'term_rows', '' )
|
||||
endif
|
||||
|
||||
execute mods . 'new'
|
||||
endif
|
||||
|
||||
" HACK: Neovim's termopen doesn't support env
|
||||
|
|
@ -61,6 +65,7 @@ function! vimspector#internal#neoterm#Start( cmd, opts ) abort
|
|||
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' ],
|
||||
|
|
|
|||
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,18 +19,32 @@ let s:save_cpo = &cpoptions
|
|||
set cpoptions&vim
|
||||
" }}}
|
||||
|
||||
let s:is_neovim = has( 'nvim' )
|
||||
let s:prefix = ''
|
||||
if has( 'nvim' )
|
||||
let s:prefix='neo'
|
||||
endif
|
||||
|
||||
function! vimspector#internal#state#Reset() abort
|
||||
let prefix = ''
|
||||
if s:is_neovim
|
||||
let prefix='neo'
|
||||
endif
|
||||
py3 << EOF
|
||||
import vim
|
||||
from vimspector import debug_session
|
||||
_vimspector_session = debug_session.DebugSession( vim.eval( 'prefix' ) )
|
||||
EOF
|
||||
try
|
||||
py3 import vim
|
||||
py3 _vimspector_session = __import__(
|
||||
\ "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
|
||||
|
||||
" Boilerplate {{{
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ set cpoptions&vim
|
|||
" }}}
|
||||
|
||||
function! vimspector#internal#term#Start( cmd, opts ) abort
|
||||
return term_start( a:cmd, a:opts )
|
||||
rightbelow return term_start( a:cmd, a:opts )
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#term#IsFinished( bufno ) abort
|
||||
|
|
|
|||
|
|
@ -1,159 +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
|
||||
VIMSPECTOR_TEST_STDOUT: true
|
||||
VIMSPECTOR_MI_DEBUGGER: ""
|
||||
|
||||
- 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.14'
|
||||
steps:
|
||||
- bash: |
|
||||
brew unlink node@6
|
||||
brew install macvim node@10 tcl-tk llvm
|
||||
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: |
|
||||
set -ex
|
||||
mkdir -p lldb-mi
|
||||
pushd lldb-mi
|
||||
curl -LO https://github.com/puremourning/lldb-mi/releases/download/1041/lldb-mi-db65c20a3d8e09d9ff2f3748c01cf43ec035a4d8.tar.gz
|
||||
tar zxvf lldb-mi-db65c20a3d8e09d9ff2f3748c01cf43ec035a4d8.tar.gz
|
||||
cp usr/local/bin/lldb-mi /usr/local/bin
|
||||
popd
|
||||
displayName: 'Install lldb-mi'
|
||||
|
||||
- bash: ./run_tests
|
||||
displayName: 'Run the tests'
|
||||
env:
|
||||
VIMSPECTOR_MIMODE: lldb
|
||||
VIMSPECTOR_TEST_STDOUT: true
|
||||
VIMSPECTOR_MI_DEBUGGER: "/usr/local/bin/lldb-mi"
|
||||
LLDB_DEBUGSERVER_PATH: "/Library/Developer/CommandLineTools/Library/PrivateFrameworks/LLDB.framework/Resources/debugserver"
|
||||
|
||||
- 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
|
||||
" limitations under the License.
|
||||
|
||||
scriptencoding utf-8
|
||||
|
||||
" Compiler plugin to help running vimspector tests
|
||||
|
||||
if exists("current_compiler")
|
||||
if exists('current_compiler')
|
||||
finish
|
||||
endif
|
||||
let current_compiler = "vimspector_test"
|
||||
let current_compiler = 'vimspector_test'
|
||||
|
||||
setlocal errorformat=
|
||||
\Found\ errors\ in\ %f:%.%#:
|
||||
|
|
@ -35,52 +37,71 @@ if ! exists( ':' . s:make_cmd )
|
|||
endif
|
||||
|
||||
function! VimGetCurrentFunction()
|
||||
echom s:GetCurrentFunction()
|
||||
echom s:GetCurrentFunction()[ 0 ]
|
||||
endfunction
|
||||
|
||||
function! s:GetCurrentFunction()
|
||||
" 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
|
||||
while l:lnum > 0
|
||||
call cursor( l:lnum, 1 )
|
||||
let l:lnum = search( l:pattern, 'bcnWz' )
|
||||
while lnum > 0
|
||||
call cursor( lnum, 1 )
|
||||
let lnum = search( pattern, 'bcnWz' )
|
||||
|
||||
if l:lnum <= 0
|
||||
call cursor( l:row, l:col )
|
||||
return l:test_function
|
||||
if lnum <= 0
|
||||
call cursor( row, col )
|
||||
return [ test_function, test_function_line ]
|
||||
endif
|
||||
|
||||
let l:this_decl = substitute( getline( l:lnum ), l:pattern, '\1', '' )
|
||||
let l:this_decl_is_test = match( l:this_decl, '\V\C\^Test_' ) >= 0
|
||||
let this_decl = substitute( getline( lnum ), pattern, '\1', '' )
|
||||
let this_decl_is_test = match( this_decl, '\V\C\^Test_' ) >= 0
|
||||
|
||||
if l:this_decl_is_test
|
||||
let l:test_function = l:this_decl
|
||||
if this_decl_is_test
|
||||
let [ test_function, test_function_line ] = [ this_decl, lnum ]
|
||||
|
||||
if indent( l:lnum ) == 0
|
||||
call cursor( l:row, l:col )
|
||||
return l:test_function
|
||||
if indent( lnum ) == 0
|
||||
call cursor( row, col )
|
||||
return [ test_function, test_function_line ]
|
||||
endif
|
||||
endif
|
||||
|
||||
let l:lnum = prevnonblank( l:lnum - 1 )
|
||||
let lnum = prevnonblank( lnum - 1 )
|
||||
endwhile
|
||||
|
||||
return [ v:null, -1 ]
|
||||
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()
|
||||
update
|
||||
let l:test_func_name = s:GetCurrentFunction()
|
||||
let l:test_func_name = s:GetCurrentFunction()[ 0 ]
|
||||
|
||||
if l:test_func_name ==# ''
|
||||
echo "No test method found"
|
||||
echo 'No test method found'
|
||||
return
|
||||
endif
|
||||
|
||||
|
|
@ -90,7 +111,9 @@ function! s:RunTestUnderCursor()
|
|||
let l:cwd = getcwd()
|
||||
execute 'lcd ' . s:root_dir
|
||||
try
|
||||
execute s:make_cmd . ' ' . l:test_arg
|
||||
execute s:make_cmd . ' --report messages '
|
||||
\ . get( g:, 'vimspector_test_args', '' ) . ' '
|
||||
\ . l:test_arg
|
||||
finally
|
||||
execute 'lcd ' . l:cwd
|
||||
endtry
|
||||
|
|
@ -101,7 +124,9 @@ function! s:RunTest()
|
|||
let l:cwd = getcwd()
|
||||
execute 'lcd ' . s:root_dir
|
||||
try
|
||||
execute s:make_cmd . ' %:p:t'
|
||||
execute s:make_cmd . ' --report messages '
|
||||
\ . get( g:, 'vimspector_test_args', '' )
|
||||
\ . ' %:p:t'
|
||||
finally
|
||||
execute 'lcd ' . l:cwd
|
||||
endtry
|
||||
|
|
@ -112,7 +137,8 @@ function! s:RunAllTests()
|
|||
let l:cwd = getcwd()
|
||||
execute 'lcd ' . s:root_dir
|
||||
try
|
||||
execute s:make_cmd
|
||||
execute s:make_cmd . ' --report messages '
|
||||
\ . get( g:, 'vimspector_test_args', '' )
|
||||
finally
|
||||
execute 'lcd ' . l:cwd
|
||||
endtry
|
||||
|
|
@ -125,10 +151,36 @@ if ! has( 'gui_running' )
|
|||
nnoremap <buffer> Â :call <SID>RunAllTests()<CR>
|
||||
" † is right-option+t
|
||||
nnoremap <buffer> † :call <SID>RunTestUnderCursor()<CR>
|
||||
nnoremap <buffer> <leader>† :call <SID>RunTestUnderCursorInVimspector()<CR>
|
||||
" å is the right-option+q
|
||||
nnoremap <buffer> å :cfirst<CR>
|
||||
" å is the right-option+a
|
||||
nnoremap <buffer> œ :cnext<CR>
|
||||
nnoremap <buffer> œ :FuncLine<CR>
|
||||
" Ω is the right-option+z
|
||||
nnoremap <buffer> Ω :cprevious<CR>
|
||||
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
|
||||
vim-vint==0.3.21
|
||||
flake8==3.8.3
|
||||
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
|
|
@ -28,3 +28,5 @@ 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"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (6.0.2.1)
|
||||
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)
|
||||
addressable (2.7.0)
|
||||
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
|
||||
|
|
@ -16,46 +16,49 @@ GEM
|
|||
colorator (1.1.0)
|
||||
commonmarker (0.17.13)
|
||||
ruby-enum (~> 0.5)
|
||||
concurrent-ruby (1.1.5)
|
||||
dnsruby (1.61.3)
|
||||
addressable (~> 2.5)
|
||||
em-websocket (0.5.1)
|
||||
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.0.0)
|
||||
faraday (1.3.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffi (1.12.2)
|
||||
ruby2_keywords
|
||||
faraday-net_http (1.0.1)
|
||||
ffi (1.15.0)
|
||||
forwardable-extended (2.6.0)
|
||||
gemoji (3.0.1)
|
||||
github-pages (204)
|
||||
github-pages-health-check (= 1.16.1)
|
||||
jekyll (= 3.8.5)
|
||||
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.13.0)
|
||||
jekyll-feed (= 0.15.1)
|
||||
jekyll-gist (= 1.5.0)
|
||||
jekyll-github-metadata (= 2.13.0)
|
||||
jekyll-mentions (= 1.5.1)
|
||||
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.15.0)
|
||||
jekyll-redirect-from (= 0.16.0)
|
||||
jekyll-relative-links (= 0.6.1)
|
||||
jekyll-remote-theme (= 0.4.1)
|
||||
jekyll-remote-theme (= 0.4.3)
|
||||
jekyll-sass-converter (= 1.5.2)
|
||||
jekyll-seo-tag (= 2.6.1)
|
||||
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.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)
|
||||
|
|
@ -66,34 +69,35 @@ GEM
|
|||
jekyll-theme-tactile (= 0.1.1)
|
||||
jekyll-theme-time-machine (= 0.1.1)
|
||||
jekyll-titles-from-headings (= 0.5.3)
|
||||
jemoji (= 0.11.1)
|
||||
kramdown (= 1.17.0)
|
||||
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.13.0)
|
||||
rouge (= 3.26.0)
|
||||
terminal-table (~> 1.4)
|
||||
github-pages-health-check (1.16.1)
|
||||
github-pages-health-check (1.17.0)
|
||||
addressable (~> 2.3)
|
||||
dnsruby (~> 1.60)
|
||||
octokit (~> 4.0)
|
||||
public_suffix (~> 3.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
typhoeus (~> 1.3)
|
||||
html-pipeline (2.12.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.8.5)
|
||||
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.14)
|
||||
kramdown (>= 1.17, < 3)
|
||||
liquid (~> 4.0)
|
||||
mercenary (~> 0.3.3)
|
||||
pathutil (~> 0.9)
|
||||
|
|
@ -113,14 +117,14 @@ GEM
|
|||
rouge (>= 2.0, < 4.0)
|
||||
jekyll-default-layout (0.1.4)
|
||||
jekyll (~> 3.0)
|
||||
jekyll-feed (0.13.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.5.1)
|
||||
jekyll-mentions (1.6.0)
|
||||
html-pipeline (~> 2.3)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-optional-front-matter (0.3.2)
|
||||
|
|
@ -128,18 +132,19 @@ GEM
|
|||
jekyll-paginate (1.1.0)
|
||||
jekyll-readme-index (0.3.0)
|
||||
jekyll (>= 3.0, < 5.0)
|
||||
jekyll-redirect-from (0.15.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.1)
|
||||
jekyll-remote-theme (0.4.3)
|
||||
addressable (~> 2.0)
|
||||
jekyll (>= 3.5, < 5.0)
|
||||
rubyzip (>= 1.3.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.6.1)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
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)
|
||||
|
|
@ -152,8 +157,8 @@ GEM
|
|||
jekyll-theme-dinky (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-hacker (0.1.1)
|
||||
jekyll (~> 3.5)
|
||||
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)
|
||||
|
|
@ -187,38 +192,45 @@ GEM
|
|||
jekyll (>= 3.3, < 5.0)
|
||||
jekyll-watch (2.2.1)
|
||||
listen (~> 3.0)
|
||||
jemoji (0.11.1)
|
||||
jemoji (0.12.0)
|
||||
gemoji (~> 3.0)
|
||||
html-pipeline (~> 2.2)
|
||||
jekyll (>= 3.0, < 5.0)
|
||||
kramdown (1.17.0)
|
||||
kramdown (2.3.1)
|
||||
rexml
|
||||
kramdown-parser-gfm (1.1.0)
|
||||
kramdown (~> 2.0)
|
||||
liquid (4.0.3)
|
||||
listen (3.2.1)
|
||||
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.4.0)
|
||||
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.0)
|
||||
minitest (5.14.4)
|
||||
multipart-post (2.1.1)
|
||||
nokogiri (1.10.7)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
octokit (4.15.0)
|
||||
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 (3.1.1)
|
||||
rb-fsevent (0.10.3)
|
||||
public_suffix (4.0.6)
|
||||
racc (1.5.2)
|
||||
rb-fsevent (0.10.4)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rouge (3.13.0)
|
||||
ruby-enum (0.7.2)
|
||||
rexml (3.2.5)
|
||||
rouge (3.26.0)
|
||||
ruby-enum (0.9.0)
|
||||
i18n
|
||||
rubyzip (2.2.0)
|
||||
ruby2_keywords (0.0.4)
|
||||
rubyzip (2.3.0)
|
||||
safe_yaml (1.0.5)
|
||||
sass (3.7.4)
|
||||
sass-listen (~> 4.0.0)
|
||||
|
|
@ -228,15 +240,21 @@ GEM
|
|||
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.3.1)
|
||||
typhoeus (1.4.0)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (1.2.6)
|
||||
tzinfo (1.2.9)
|
||||
thread_safe (~> 0.1)
|
||||
unicode-display_width (1.6.1)
|
||||
zeitwerk (2.2.2)
|
||||
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
|
||||
|
|
@ -246,6 +264,7 @@ DEPENDENCIES
|
|||
jekyll-feed (~> 0.6)
|
||||
minima (~> 2.0)
|
||||
tzinfo-data
|
||||
webrick (~> 1.7)
|
||||
|
||||
BUNDLED WITH
|
||||
2.0.2
|
||||
2.2.3
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
To update/install:
|
||||
|
||||
gem install bundler
|
||||
bundle install
|
||||
bundle install --path vendor/bundle
|
||||
|
||||
To run a local server/test the build
|
||||
|
||||
|
|
|
|||
|
|
@ -11,22 +11,33 @@ for Vimspector.
|
|||
* [Debug adapter configuration](#debug-adapter-configuration)
|
||||
* [Debug profile configuration](#debug-profile-configuration)
|
||||
* [Replacements and variables](#replacements-and-variables)
|
||||
* [The splat operator](#the-splat-operator)
|
||||
* [Default values](#default-values)
|
||||
* [Coercing Types](#coercing-types)
|
||||
* [Configuration Format](#configuration-format)
|
||||
* [Files and locations](#files-and-locations)
|
||||
* [Adapter configurations](#adapter-configurations)
|
||||
* [Debug configurations](#debug-configurations)
|
||||
* [Exception breakpionts](#exception-breakpionts)
|
||||
* [Configuration selection](#configuration-selection)
|
||||
* [Specifying a default configuration](#specifying-a-default-configuration)
|
||||
* [Preventing automatic selection](#preventing-automatic-selection)
|
||||
* [Exception Breakpoints](#exception-breakpoints)
|
||||
* [Predefined Variables](#predefined-variables)
|
||||
* [Remote Debugging Support](#remote-debugging-support)
|
||||
* [Python (debugpy) Example](#python-debugpy-example)
|
||||
* [C-family (gdbserver) Example](#c-family-gdbserver-example)
|
||||
* [Docker Example](#docker-example)
|
||||
* [Appendix: Configuration file format](#appendix-configuration-file-format)
|
||||
* [Appendix: Editor configuration](#appendix-editor-configuration)
|
||||
|
||||
<!-- Added by: ben, at: Tue 28 Jan 2020 08:47:40 GMT -->
|
||||
<!-- Added by: ben, at: Thu 13 Aug 2020 17:42:11 BST -->
|
||||
|
||||
<!--te-->
|
||||
|
||||
## Concepts
|
||||
|
||||
As Vimspector supports debugging arbitrary projects, you need to tell it a few
|
||||
deatils about what you want to debug, and how to go about doing that.
|
||||
details about what you want to debug, and how to go about doing that.
|
||||
|
||||
In order to debug things, Vimspector requires a Debug Adapter which bridges
|
||||
between Vimspector and the actual debugger tool. Vimspector can be used with any
|
||||
|
|
@ -110,14 +121,15 @@ But for now, consider the following example snippet:
|
|||
{
|
||||
"configurations": {
|
||||
"example-debug-configuration": {
|
||||
// This is a single-line comment explaining the purpose
|
||||
"adapter": "example-adapter-name",
|
||||
"variables": {
|
||||
"SecretToken": {
|
||||
"SecretToken": { // Variables should start with upper-case letters
|
||||
"shell" : [ "cat", "${HOME}/.secret_token" ]
|
||||
}
|
||||
},
|
||||
"configuration": {
|
||||
"request": "launch",
|
||||
"request": "launch" /* or it could be "attach" */,
|
||||
"program": [
|
||||
"${fileBasenameNoExtension}",
|
||||
"-c", "configuration_file.cfg",
|
||||
|
|
@ -149,10 +161,152 @@ the following variable substitutions:
|
|||
provide a value interactively when starting debugging. Vimspector remembers
|
||||
what they said and provides it as the default should they debug again.
|
||||
* `${SecretToken}` - this variable is provided by the configuration's
|
||||
`variables` block. Its value is taken from the `strip`ped result of running
|
||||
`variables` block. Its value is taken from the `strip`'d result of running
|
||||
the shell command. Note these variables can be supplied by both the debug and
|
||||
adapter configurations and can be either static strings or shell commands.
|
||||
|
||||
#### The splat operator
|
||||
|
||||
Often we want to create a single `.vimspector.json` entry which encompasses many
|
||||
use cases, as it is tedious to write every use case/start up option in JSON.
|
||||
This is why we have the replacement variables after all.
|
||||
|
||||
Frequently debug adapters request command arguments as a JSON array, for
|
||||
example:
|
||||
|
||||
```json
|
||||
"args": [ "one", "two three", "four" ],
|
||||
```
|
||||
|
||||
To help with this sort of case, Vimspector supports a 'splat' operator for
|
||||
replacement variables operating within lists. The syntax is: `"*${var}`, which
|
||||
means roughly "splice the contents of `${var}` into the list at this position".
|
||||
`${var}` is parsed like a shell command (using python's `shlex` parser) and each
|
||||
word is added as a list item.
|
||||
|
||||
For example:
|
||||
|
||||
```json
|
||||
"args": [ "*${CommandLineArgs}" ]
|
||||
```
|
||||
|
||||
This would:
|
||||
|
||||
* Ask the user to provide the variable `CommandLineArgs`. Let's say they entered
|
||||
`one "two three" four`
|
||||
* Split `CommandLineArgs` like shell arguments: `one`, `two three` and `four`
|
||||
* Set `args` in the settings dict to: `[ "one", "two three", "four" ]`
|
||||
|
||||
You can also combine with static values:
|
||||
|
||||
```json
|
||||
"args": [ "First", "*${CommandLineArgs}", "Last" ]
|
||||
```
|
||||
|
||||
This would yield the intuitive result:
|
||||
`[ "First", "one", "two three", "four", "Last" ]`
|
||||
|
||||
#### Default values
|
||||
|
||||
You can specify replacements with default values. In this case if the user has
|
||||
not specified a value, they are prompted but with the default value
|
||||
pre-populated, allowing them to just press return to accept the default.
|
||||
|
||||
The syntax is `${variableName:default value}`. The default value can contain any
|
||||
character, but to include a `}` you must escape it with a backslash. To include
|
||||
a backslash in the JSON you must write `\\`, as in:
|
||||
|
||||
```json
|
||||
{ "key": "${value:default {\\} stuff}" }
|
||||
```
|
||||
|
||||
The default value can also be a replacement variable. However, this _must_ be a
|
||||
variable that's already defined, such as one of the [predefined
|
||||
variables](#predefined-variables), or one specified in a `variables` block. In
|
||||
order to reference them, you _must_ use `${var}` syntax and you _must_ escape
|
||||
the closing `}`. For example, the is a common and useful case:
|
||||
|
||||
```json
|
||||
{
|
||||
"configuration": {
|
||||
"program": "${script:${file\\}}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This will prompt the user to specify `script`, but it will default to the path
|
||||
to the current file.
|
||||
|
||||
#### Coercing Types
|
||||
|
||||
Sometimes, you want to provide an option for a boolean parameter, or want to
|
||||
allow the user to specify more than just strings. Vimspector allows you to do
|
||||
this, ensuring that the resulting JSON is valid. This is done by interpreting a
|
||||
value as a JSON string and substituting the resulting JSON value in its place.
|
||||
|
||||
This is easier to explain with an example. Let's say we want to offer the
|
||||
ability to break on entry, as an option for the user. The launch configuration
|
||||
requires `stopOnEntry` to be a bool. This doesn't work:
|
||||
|
||||
```json
|
||||
"stopOnEntry": "${StopOnEntry}"
|
||||
```
|
||||
|
||||
The reason is that if the user types `true`, the resulting object is:
|
||||
|
||||
```json
|
||||
"stopOnEntry": "true"
|
||||
```
|
||||
|
||||
The problem being that is a string, not a boolean. So Vimspector allows you to
|
||||
re-interpret the string as a JSON value and use that instead. To do this, add
|
||||
`#json` to the key's name. You can even add a default, like this:
|
||||
|
||||
```json
|
||||
"stopOnEntry#json": "${stopOnEntry:true}"
|
||||
```
|
||||
|
||||
If the user accepts the default, the resulting string `"true"` is coerced to a
|
||||
JSON value `true`, and the suffix is stripped fom the key, resulting in the
|
||||
following:
|
||||
|
||||
```json
|
||||
"stopOnEntry": true
|
||||
```
|
||||
|
||||
Which is what we need.
|
||||
|
||||
If you happen to have a key that already ends in `#json` (unlikely!), then you
|
||||
can force Vimspector to treat the value as a string by appending `#s`, as in:
|
||||
|
||||
```json
|
||||
"unlikelyKeyName#json#s": "this is a string, not JSON data"
|
||||
```
|
||||
|
||||
***Advanced usage:***
|
||||
|
||||
The most common usage for this is for number and bool types, but it works for
|
||||
objects too. If you want to be able to specify a whole object (e.g. a whole
|
||||
`env` dict), then you can do that too:
|
||||
|
||||
```json
|
||||
"env#json": "${Environment:{\\}}"
|
||||
```
|
||||
|
||||
The default value here is `{}` (note the `}` must be escaped!). The user can
|
||||
then enter something like `{ "MYVAR": "MyValue", "OTHER": "Other" }` and the
|
||||
resulting object would be:
|
||||
|
||||
```json
|
||||
"env": {
|
||||
"MYVAR": "MyValue",
|
||||
"OTHER": "Other"
|
||||
}
|
||||
```
|
||||
|
||||
It also works for lists, though [the splat operator](#the-splat-operator)
|
||||
is usually more convenient for that.
|
||||
|
||||
## Configuration Format
|
||||
|
||||
All Vimspector configuration is defined in a JSON object. The complete
|
||||
|
|
@ -172,7 +326,7 @@ embedded within `<debug configuration>`, though this is not recommended usage.
|
|||
## Files and locations
|
||||
|
||||
The above configuration object is constructed from a number of configuration
|
||||
files, by merging objects i specified order.
|
||||
files, by merging objects in a specified order.
|
||||
|
||||
In a minimal sense, the only file required is a `.vimspector.json` file in the
|
||||
root of your project which defines the [full configuration object][schema], but
|
||||
|
|
@ -185,6 +339,8 @@ abbreviations:
|
|||
* `<vimspector home>` means the path to the Vimspector installation (such as
|
||||
`$HOME/.vim/pack/vimspector/start/vimspector`)
|
||||
* `<OS>` is either `macos` or `linux` depending on the host operating system.
|
||||
* `<filetype>` is the Vim filetype. Where multiple filetypes are in effect,
|
||||
typically all filetypes are checked.
|
||||
|
||||
## Adapter configurations
|
||||
|
||||
|
|
@ -199,7 +355,7 @@ definition.
|
|||
These files are user-supplied and override the above.
|
||||
* The first such `.gadgets.json` file found in all parent directories of the
|
||||
file open in Vim.
|
||||
* The `.vimspector.json` (see below)
|
||||
* The `.vimspector.json` and any filetype-specific configurations (see below)
|
||||
|
||||
In all cases, the required format is:
|
||||
|
||||
|
|
@ -225,14 +381,33 @@ The specification for the gadget object is defined in the [gadget schema][].
|
|||
|
||||
## Debug configurations
|
||||
|
||||
The debug configurations are read from `.vimspector.json`. The file is found
|
||||
(like `.gadgets.json` above) by recursively searching up the directory hierarchy
|
||||
from the directory of the file open in Vim. The first file found is read and no
|
||||
further searching is done.
|
||||
There are two locations for debug configurations for a project:
|
||||
|
||||
Only a single `.vimspector.json` is read.
|
||||
* `<vimspector home>/configurations/<OS>/<filetype>/*.json`
|
||||
* `.vimspector.json` in the project source
|
||||
|
||||
Debug configurations are re-read at the start of each debug session.
|
||||
Typically, the debug configurations are read from `.vimspector.json`. The file
|
||||
is found (like `.gadgets.json` above) by recursively searching up the directory
|
||||
hierarchy from the directory of the file open in Vim. The first file found is
|
||||
read and no further searching is done.
|
||||
|
||||
Only a single `.vimspector.json` is read. If one is found, the location of this
|
||||
file is used for `${workspaceRoot}` and other workspace-relative paths.
|
||||
|
||||
In addition, users can create filetype-specific configurations in the Vimspector
|
||||
installation directory. This can be useful where the parameters for the debug
|
||||
session for a particular filetype are always known in advance, or can always be
|
||||
entered by the user. This allows for debugging to "just work" without any
|
||||
modification to the project source (no need to add a `.vimspector.json`). In
|
||||
this case, the `${workspaceRoot}` and workspace-relative paths are interpreted
|
||||
relative to the file open in Vim. This isn't ideal, but there is no other
|
||||
obvious way to default this variable.
|
||||
|
||||
As with gadgets, any debug configurations appearing within `.vimspector.json`
|
||||
override any that appear in the common configuration dir.
|
||||
|
||||
Debug configurations are re-read at the start of each debug session, so
|
||||
modifications are picked up without any restarts of Vim.
|
||||
|
||||
The specification for the gadget object is defined in the [schema][], but a
|
||||
typical example looks like this:
|
||||
|
|
@ -252,10 +427,66 @@ typical example looks like this:
|
|||
}
|
||||
```
|
||||
|
||||
### Exception breakpionts
|
||||
### Configuration selection
|
||||
|
||||
When starting debugging, you can specify which debug configuration to launch
|
||||
with `call vimspector#LaunchWithSettings( #{ configuration: 'name here' } )`.
|
||||
|
||||
Otherwise, if there's only one configuration found, Vimspector will use that
|
||||
configuration, unless it contains a key `"autoselect": false`.
|
||||
|
||||
If multiple debug configurations are found, and no explicit configuration was
|
||||
selected on Launch, the user is prompted to select a configuration, unless a
|
||||
single debug configuration is found with a key `"default": true`.
|
||||
|
||||
#### Specifying a default configuration
|
||||
|
||||
As noted, you can specify a default configuration with `"default": true`:
|
||||
|
||||
```json
|
||||
{
|
||||
"configurations": {
|
||||
"use this one": {
|
||||
"default": true,
|
||||
"adapter": " ... ",
|
||||
"configuation": {
|
||||
// ...
|
||||
}
|
||||
},
|
||||
"don't use this one": {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If multiple configurations are found with `default` set to `true`, then the
|
||||
user is prompted anyway.
|
||||
|
||||
#### Preventing automatic selection
|
||||
|
||||
If you don't want a configuration to be selected automatically, then set
|
||||
`"autoselect": false`. This particularly useful for configurations in the
|
||||
central (as opposed to project-local) directory. For example:
|
||||
|
||||
```json
|
||||
"configurations": {
|
||||
"Don't use this by default!": {
|
||||
"autoselect": false,
|
||||
"adapter": " ... ",
|
||||
"configuation": {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Setting `autoselect` to `false` overrides setting `default` to `true`.
|
||||
|
||||
### Exception Breakpoints
|
||||
|
||||
Debug adapters have arbitrary configuration for exception breakpoints. Normally
|
||||
this is presented as a series of question to the user on startnig the debug
|
||||
this is presented as a series of question to the user on starting the debug
|
||||
session. The question includes the name of the exception breakpoint option,
|
||||
the default and the list of valid responses (usually `Y` or `N`).
|
||||
|
||||
|
|
@ -265,9 +496,9 @@ and configure the response `exception` mapping in the `breakpoints` mapping. If
|
|||
the configured response is empty string, the debug adapter default will be used.
|
||||
|
||||
Referring to the above example, the following tells the debug adapter to use the
|
||||
default value for `caught` exceptoins and to break on `uncaught` exception:
|
||||
default value for `caught` exceptions and to break on `uncaught` exception:
|
||||
|
||||
```
|
||||
```json
|
||||
{
|
||||
"configurations": {
|
||||
"example-debug-configuration": {
|
||||
|
|
@ -281,6 +512,52 @@ default value for `caught` exceptoins and to break on `uncaught` exception:
|
|||
...
|
||||
```
|
||||
|
||||
The keys in the `exception` mapping are what Vimspector includes in the prompt.
|
||||
For example, when prompted with the following:
|
||||
|
||||
```
|
||||
cpp_throw: Break on C++: on throw (Y/N/default: Y)?
|
||||
```
|
||||
|
||||
The exception breakpoint "type" is `cpp_throw` and the default is `Y`.
|
||||
|
||||
Similarly:
|
||||
|
||||
```
|
||||
cpp_catch: Break on C++: on catch (Y/N/default: N)?
|
||||
```
|
||||
|
||||
The exception breakpoint "type" is `cpp_catch` and the default is `N`.
|
||||
|
||||
Use the following to set the values in configuration and not get asked:
|
||||
|
||||
```json
|
||||
"configurations": {
|
||||
"example-debug-configuration": {
|
||||
"adapter": "example-adapter-name",
|
||||
"breakpoints": {
|
||||
"exception": {
|
||||
"cpp_throw": "Y",
|
||||
"cpp_catch": "Y"
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
To just accept the defaults for these exception breakpoint types, don't specify
|
||||
a value, as in :
|
||||
|
||||
```json
|
||||
"configurations": {
|
||||
"example-debug-configuration": {
|
||||
"adapter": "example-adapter-name",
|
||||
"breakpoints": {
|
||||
"exception": {
|
||||
"cpp_throw": "",
|
||||
"cpp_catch": ""
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
## Predefined Variables
|
||||
|
||||
The following variables are provided:
|
||||
|
|
@ -291,16 +568,410 @@ The following variables are provided:
|
|||
found
|
||||
* `${workspaceFolder}` - the path of the folder where `.vimspector.json` was
|
||||
found
|
||||
* `${gadgetDir}` - path to the OS-specifc gadget dir (`<vimspector
|
||||
* `${gadgetDir}` - path to the OS-specific gadget dir (`<vimspector
|
||||
home>/gadgets/<OS>`)
|
||||
* `${file}` - the current opened file
|
||||
* `${relativeFile}` - the current opened file relative to workspaceRoot
|
||||
* `${fileBasename}` - the current opened file's basename
|
||||
* `${fileBasenameNoExtension}` - the current opened file's basename with no
|
||||
* `${relativeFile}` - the current opened file relative to `workspaceRoot`
|
||||
* `${fileBasename}` - the current opened file's `basename`
|
||||
* `${fileBasenameNoExtension}` - the current opened file's `basename` with no
|
||||
file extension
|
||||
* `${fileDirname}` - the current opened file's dirname
|
||||
* `${fileDirname}` - the current opened file's `dirname`
|
||||
* `${fileExtname}` - the current opened file's extension
|
||||
* `${cwd}` - the current working directory of the active window on launch
|
||||
* `${unusedLocalPort}` - an unused local TCP port
|
||||
|
||||
## Remote Debugging Support
|
||||
|
||||
Vimspector has in-built support for executing remote debuggers (such as
|
||||
`gdbserver`, `debugpy`, `llvm-server` etc.). This is useful for environments
|
||||
where the development is done on one host and the runtime is
|
||||
some other host, account, container, etc.
|
||||
|
||||
In order for it to work, you have to set up paswordless SSH between the local
|
||||
and remote machines/accounts. Then just tell Vimspector how to remotely launch
|
||||
and/or attach to the app.
|
||||
|
||||
This is presented as examples with commentary, as it's a fairly advanced/niche
|
||||
case. If you're not already familiar with remote debugging tools (such as
|
||||
gdbserver) or not familiar with ssh or such, you might need to independently
|
||||
research that.
|
||||
|
||||
Vimspector's tools are intended to automate your existing process for setting
|
||||
this up rather than to offer batteries-included approach. Ultimately, all
|
||||
Vimspector is going to do is run your commands over SSH, or docker, and
|
||||
co-ordinate with the adapter.
|
||||
|
||||
### Python (debugpy) Example
|
||||
|
||||
Here is some examples using the Vimspector built-in
|
||||
remote support (using SSH) to remotely launch and attach a python application
|
||||
and connect to it using debugpy.
|
||||
|
||||
The usage pattern is to hit `<F5>`, enter `host` (the host where your app runs),
|
||||
`account` (the account it runs under), and `port` (a port that will be opened on
|
||||
the remote host). Vimspector also supports exec'ing into Docker run containers
|
||||
with `container` (the container name or id your app is running in).
|
||||
Vimspector then orchestrates the various tools to set you up.
|
||||
|
||||
```json
|
||||
|
||||
{
|
||||
"adapters": {
|
||||
"python-remote": {
|
||||
"port": "${port}",
|
||||
"host": "${host}",
|
||||
"launch": {
|
||||
"remote": {
|
||||
"host": "${host}", // Remote host to ssh to (mandatory if not using container)
|
||||
"account": "${account}", // User to connect as (optional)
|
||||
|
||||
// Optional.... Manual additional arguments for ssh
|
||||
// "ssh": {
|
||||
// "args": [ "-o", "StrictHostKeyChecking=no" ]
|
||||
// },
|
||||
|
||||
// Command to launch the debugee and attach the debugger;
|
||||
// %CMD% replaced with the remote-cmdLine configured in the launch
|
||||
// configuration. (mandatory)
|
||||
"runCommand": [
|
||||
"python", "-m", "debugpy",
|
||||
"--listen", "0.0.0.0:${port}",
|
||||
"--wait-for-client",
|
||||
"%CMD%"
|
||||
]
|
||||
|
||||
// Optional alternative to runCommand (if you need to run multiple
|
||||
// commands)
|
||||
// "runCommands": [
|
||||
// [ /* first command */ ],
|
||||
// [ /* second command */ ]
|
||||
// ]
|
||||
|
||||
}
|
||||
|
||||
// optional delay to wait after running runCommand(s). This is often
|
||||
// needed because of the way docker handles TCP, or if you're using some
|
||||
// wrapper (e.g. to start the JVM)
|
||||
// "delay": "1000m" // format as per :help sleep
|
||||
},
|
||||
"attach": {
|
||||
"remote": {
|
||||
"host": "${host}", // Remote host to ssh to (mandatory if not using container)
|
||||
"account": "${account}", // User to connect as (optional)
|
||||
// Command to get the PID of the process to attach (mandatory)
|
||||
"pidCommand": [
|
||||
//
|
||||
// Remember taht you can use ${var} to ask for input. I use this to
|
||||
// call a custom command to returm the PID for a named service, so
|
||||
// here's an examle:
|
||||
//
|
||||
"/path/to/secret/script/GetPIDForService", "${ServiceName}"
|
||||
],
|
||||
|
||||
// Command to attach the debugger; %PID% replaced with output of
|
||||
// pidCommand above (mandatory)
|
||||
"attachCommand": [
|
||||
"python", "-m", "debugpy", "--listen", "0.0.0.0:${port}",
|
||||
"--pid", "%PID%"
|
||||
]
|
||||
|
||||
// Optional alternative to attachCommand (if you need to run multiple
|
||||
// commands)
|
||||
// "attachCommands": [
|
||||
// [ /* first command */ ],
|
||||
// [ /* second command */ ]
|
||||
// ],
|
||||
|
||||
// Optional.... useful with buggy gdbservers to kill -TRAP %PID%
|
||||
// "initCompleteCommand": [
|
||||
// /* optional command to run after initialized */
|
||||
// ]
|
||||
|
||||
// Optional.... Manual additional arguments for ssh
|
||||
// "ssh": {
|
||||
// "args": [ "-o", "StrictHostKeyChecking=no" ]
|
||||
// },
|
||||
}
|
||||
// optional delay to wait after running runCommand(s). This is often
|
||||
// needed because of the way docker handles TCP, or if you're using some
|
||||
// wrapper (e.g. to start the JVM)
|
||||
// "delay": "1000m" // format as per :help sleep
|
||||
}
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"remote-launch": {
|
||||
"adapter": "python-remote",
|
||||
|
||||
"remote-request": "launch",
|
||||
"remote-cmdLine": [
|
||||
"${RemoteRoot}/${fileBasename}", "*${args}"
|
||||
],
|
||||
|
||||
"configuration": {
|
||||
"request": "attach",
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceRoot}",
|
||||
"remoteRoot": "${RemoteRoot}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"remote-attach": {
|
||||
"variables": {
|
||||
// Just an example of how to specify a variable manually rather than
|
||||
// vimspector asking for input from the user
|
||||
"ServiceName": "${fileBasenameNoExtension}"
|
||||
},
|
||||
|
||||
"adapter": "python-remote",
|
||||
"remote-request": "attach",
|
||||
|
||||
"configuration": {
|
||||
"request": "attach",
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceRoot}",
|
||||
"remoteRoot": "${RemoteRoot}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### C-family (gdbserver) Example
|
||||
|
||||
This example uses Vimspector to remotely launch or attach to a binary using
|
||||
`gdbserver` and then instructs vscode-cpptools to attach to that `gdbserver`.
|
||||
|
||||
The approach is very similar to the above for python, just that we use gdbserver
|
||||
and have to tell cpptools a few more options.
|
||||
|
||||
```json
|
||||
{
|
||||
"adapters": {
|
||||
"cpptools-remote": {
|
||||
"command": [
|
||||
"${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7"
|
||||
],
|
||||
"name": "cppdbg",
|
||||
"configuration": {
|
||||
"type": "cppdbg"
|
||||
},
|
||||
"launch": {
|
||||
"remote": {
|
||||
"host": "${host}",
|
||||
"account": "${account}",
|
||||
"runCommand": [
|
||||
"gdbserver",
|
||||
"--once",
|
||||
"--no-startup-with-shell",
|
||||
"--disable-randomisation",
|
||||
"0.0.0.0:${port}",
|
||||
"%CMD%"
|
||||
}
|
||||
},
|
||||
"attach": {
|
||||
"remote": {
|
||||
"host": "${host}",
|
||||
"account": "${account}",
|
||||
"pidCommand": [
|
||||
"/path/to/secret/script/GetPIDForService", "${ServiceName}"
|
||||
],
|
||||
"attachCommand": [
|
||||
"gdbserver",
|
||||
"--once",
|
||||
"--attach",
|
||||
"0.0.0.0:${port}",
|
||||
"%PID%"
|
||||
],
|
||||
//
|
||||
// If your application is started by a wrapper script, then you might
|
||||
// need the followin. GDB can't pause an application because it only
|
||||
// sends the signal to the process group leader. Or something.
|
||||
// Basically, if you find that everything just hangs and the
|
||||
// application never attaches, try using the following to manually
|
||||
// force the trap signal.
|
||||
//
|
||||
"initCompleteCommand": [
|
||||
"kill",
|
||||
"-TRAP",
|
||||
"%PID%"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"remote launch": {
|
||||
"adapter": "cpptools-remote",
|
||||
"remote-cmdLine": [ "/path/to/the/remote/executable", "args..." ],
|
||||
"remote-request": "launch",
|
||||
"configuration": {
|
||||
"request": "attach", // yes, attach!
|
||||
|
||||
"program": "/path/to/the/local/executable",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerAddress": "${host}:${port}"
|
||||
}
|
||||
},
|
||||
"remote attach": {
|
||||
"adapter": "cpptools-remote",
|
||||
"remote-request": "attach",
|
||||
"configuration": {
|
||||
"request": "attach",
|
||||
|
||||
"program": "/path/to/the/local/executable",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerAddress": "${host}:${port}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Docker Example
|
||||
|
||||
This example uses Vimspector to remotely launch or attach to a docker container
|
||||
port.
|
||||
|
||||
``` json
|
||||
{
|
||||
"adapters": {
|
||||
"python-remote": {
|
||||
"port": "${port}",
|
||||
"launch": {
|
||||
"remote": {
|
||||
"container": "${container}", // Docker container id or name to exec into to.
|
||||
|
||||
// Command to launch the debugee and attach the debugger;
|
||||
// %CMD% replaced with the remote-cmdLine configured in the launch
|
||||
// configuration. (mandatory)
|
||||
"runCommand": [
|
||||
"python", "-m", "debugpy",
|
||||
"--listen", "0.0.0.0:${port}",
|
||||
"--wait-for-client",
|
||||
"%CMD%"
|
||||
]
|
||||
|
||||
// Optional alternative to runCommand (if you need to run multiple
|
||||
// commands)
|
||||
// "runCommands": [
|
||||
// [ /* first command */ ],
|
||||
// [ /* second command */ ]
|
||||
// ]
|
||||
|
||||
}
|
||||
|
||||
// optional delay to wait after running runCommand(s). This is often
|
||||
// needed because of the way docker handles TCP
|
||||
"delay": "1000m" // format as per :help sleep
|
||||
},
|
||||
"attach": {
|
||||
"remote": {
|
||||
"container": "${container}", // Docker container id or name to exec into.
|
||||
// Command to get the PID of the process to attach (mandatory)
|
||||
// This command gets appended to "docker exec ${container}"
|
||||
"pidCommand": [
|
||||
//
|
||||
// Remember taht you can use ${var} to ask for input. I use this to
|
||||
// call a custom command to returm the PID for a named service, so
|
||||
// here's an examle:
|
||||
//
|
||||
"sh", "-c", "pgrep", "-f", "${filename}"
|
||||
],
|
||||
|
||||
// Command to attach the debugger; %PID% replaced with output of
|
||||
// pidCommand above (mandatory)
|
||||
"attachCommand": [
|
||||
"sh", "-c", "python", "-m", "debugpy", "--listen", "0.0.0.0:${port}",
|
||||
"--pid", "%PID%"
|
||||
]
|
||||
|
||||
// Optional alternative to attachCommand (if you need to run multiple
|
||||
// commands)
|
||||
// "attachCommands": [
|
||||
// [ /* first command */ ],
|
||||
// [ /* second command */ ]
|
||||
// ],
|
||||
|
||||
// Optional.... useful with buggy gdbservers to kill -TRAP %PID%
|
||||
// "initCompleteCommand": [
|
||||
// /* optional command to run after initialized */
|
||||
// ]
|
||||
|
||||
}
|
||||
|
||||
// optional delay to wait after running runCommand(s). This is often
|
||||
// needed because of the way docker handles TCP, or if you're using some
|
||||
// wrapper (e.g. to start the JVM)
|
||||
"delay": "1000m" // format as per :help sleep
|
||||
}
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"remote-launch": {
|
||||
"adapter": "python-remote",
|
||||
|
||||
"remote-request": "launch",
|
||||
"remote-cmdLine": [
|
||||
"${RemoteRoot}/${fileBasename}", "*${args}"
|
||||
],
|
||||
|
||||
"configuration": {
|
||||
"request": "attach",
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceRoot}",
|
||||
"remoteRoot": "${RemoteRoot}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"remote-attach": {
|
||||
"variables": {
|
||||
// Just an example of how to specify a variable manually rather than
|
||||
// vimspector asking for input from the user
|
||||
"FileName": "${fileName}"
|
||||
},
|
||||
|
||||
"adapter": "python-remote",
|
||||
"remote-request": "attach",
|
||||
|
||||
"configuration": {
|
||||
"request": "attach",
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceRoot}",
|
||||
"remoteRoot": "${RemoteRoot}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Appendix: Configuration file format
|
||||
|
||||
The configuration files are text files which must be UTF-8 encoded. They
|
||||
contain a single JSON object, along with optional comments.
|
||||
|
||||
Comments are "c-style", i.e.:
|
||||
|
||||
* `// single line comment ...`
|
||||
* `/* inline comment */`
|
||||
|
||||
There is much debate about whether JSON files should contain comments. I have
|
||||
added them because they are useful in the context of configuration files.
|
||||
Unforutnately this may mean your editor doesn't like them (they are strictly
|
||||
invalid JSON) so it's up to you if you use them.
|
||||
|
||||
Technically, Vimspector uses [JSON
|
||||
minify](https://github.com/getify/JSON.minify) to strip comments before parsing
|
||||
the JSON.
|
||||
|
||||
## Appendix: Editor configuration
|
||||
|
||||
|
|
@ -364,8 +1035,8 @@ This configuration can be adapted to any other LSP-based editor configuration
|
|||
and is provided just as an example.
|
||||
|
||||
[dap]: https://microsoft.github.io/debug-adapter-protocol/
|
||||
[schema]: http://puremourning.github.io/vimspector/vimspector.schema.json
|
||||
[gadget-schema]: http://puremourning.github.io/vimspector/gadgets.schema.json
|
||||
[schema]: http://puremourning.github.io/vimspector/schema/vimspector.schema.json
|
||||
[gadget-schema]: http://puremourning.github.io/vimspector/schema/gadgets.schema.json
|
||||
[YouCompleteMe]: https://github.com/ycm-core/YouCompleteMe
|
||||
[lsp-examples]: https://github.com/ycm-core/lsp-examples
|
||||
[vscode-json]: https://github.com/vscode-langservers/vscode-json-languageserver
|
||||
|
|
|
|||
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.
|
||||
|
|
@ -38,11 +38,144 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"adapter-common": {
|
||||
"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" },
|
||||
{ "$ref": "#/definitions/adapter-remote" },
|
||||
{ "$ref": "#/definitions/adapter-attach" },
|
||||
{
|
||||
"properties": {
|
||||
"name": {
|
||||
|
|
@ -54,121 +187,30 @@
|
|||
"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."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"adapter-attach": {
|
||||
"properties": {
|
||||
"attach": {
|
||||
"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'."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"adapter-remote": {
|
||||
"properties": {
|
||||
"remote": {
|
||||
"type": "object",
|
||||
"required": [ "host" ],
|
||||
"description": "Configures how Vimspector will marshal remote debugging requests. When remote debugging, Vimspector will ssh to 'account'@'host' 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)",
|
||||
"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). "
|
||||
},
|
||||
"pidCommand": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Required for remote-attach. Remote command to execute to return the PID to attach to."
|
||||
},
|
||||
"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'"
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"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": {
|
||||
"oneOf": [
|
||||
},
|
||||
{ "$ref": "#/definitions/adapter-launchattach" },
|
||||
{
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/adapter-common" },
|
||||
{
|
||||
"required": [ "port" ],
|
||||
"properties": {
|
||||
"port": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [ "ask" ]
|
||||
},
|
||||
{
|
||||
"type": "integer"
|
||||
}
|
||||
],
|
||||
"description": "If supplied, indicates that a socket connection should be made to this port on 'localhost'. If the value is 'ask', then the user is asked to enter the port number to connect to."
|
||||
}
|
||||
}
|
||||
}
|
||||
"anyOf": [
|
||||
{ "required": [ "command" ] },
|
||||
{ "required": [ "port" ] },
|
||||
{ "required": [ "command", "port" ] }
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/adapter-common" },
|
||||
{
|
||||
"required": [ "command" ],
|
||||
"properties": {
|
||||
"command": {
|
||||
"type": [ "string", "array" ],
|
||||
"description": "Command line to execute the debug adapter.",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"env": {
|
||||
"type": "object",
|
||||
"description": "Name/value pairs to set in the environment when starting the adapter."
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string",
|
||||
"description": "Directory from which to start the adapter. Defaults to the working directory of the window on launch"
|
||||
}
|
||||
}
|
||||
"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."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# vimspector - A multi-language debugging system for Vim
|
||||
# Copyright 2019 Ben Jackson
|
||||
|
|
@ -15,621 +15,52 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
try:
|
||||
import urllib.request as urllib2
|
||||
except ImportError:
|
||||
import urllib2
|
||||
import sys
|
||||
|
||||
if sys.version_info.major < 3:
|
||||
sys.exit( "You need to run this with python 3. Your version is " +
|
||||
'.'.join( map( str, sys.version_info[ :3 ] ) ) )
|
||||
|
||||
import argparse
|
||||
import contextlib
|
||||
import os
|
||||
import string
|
||||
import zipfile
|
||||
import gzip
|
||||
import shutil
|
||||
import subprocess
|
||||
import traceback
|
||||
import tarfile
|
||||
import hashlib
|
||||
import sys
|
||||
import json
|
||||
import functools
|
||||
import time
|
||||
import ssl
|
||||
|
||||
try:
|
||||
from io import BytesIO # for Python 3
|
||||
except ImportError:
|
||||
from BytesIO import BytesIO
|
||||
import operator
|
||||
import glob
|
||||
|
||||
# Include vimspector source, for utils
|
||||
sys.path.insert( 1, os.path.join( os.path.dirname( __file__ ),
|
||||
'python3' ) )
|
||||
|
||||
from vimspector import install
|
||||
from vimspector import install, installer, gadgets
|
||||
from vimspector.vendor.json_minify import minify
|
||||
|
||||
GADGETS = {
|
||||
'vscode-cpptools': {
|
||||
'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.26.3',
|
||||
},
|
||||
'linux': {
|
||||
'file_name': 'cpptools-linux.vsix',
|
||||
'checksum':
|
||||
'd660367fbedd6acaffcc233c1d41ef280ff79aeba81846b86bf9bd97b4887379'
|
||||
},
|
||||
'macos': {
|
||||
'file_name': 'cpptools-osx.vsix',
|
||||
'checksum':
|
||||
'ca0793dd1dfd70757491da80cf04b2b15631048b572de2ebe3864f9eed96dff9',
|
||||
},
|
||||
'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.11.50794',
|
||||
'file_name': 'ms-python-release.vsix',
|
||||
'checksum':
|
||||
'6a9edf9ecabed14aac424e6007858068204a3638bf3bb4f235bd6035d823acc6',
|
||||
},
|
||||
'adapters': {
|
||||
"vscode-python": {
|
||||
"name": "vscode-python",
|
||||
"command": [
|
||||
"node",
|
||||
"${gadgetDir}/vscode-python/out/client/debugger/debugAdapter/main.js",
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
'vscode-java-debug': {
|
||||
'language': 'java',
|
||||
'enabled': False,
|
||||
'download': {
|
||||
'url': 'https://github.com/microsoft/vscode-java-debug/releases/download/'
|
||||
'${version}/${file_name}',
|
||||
},
|
||||
'all': {
|
||||
'version': '0.23.0',
|
||||
'file_name': 'vscode-java-debug-0.23.0.vsix',
|
||||
'checksum':
|
||||
'f98a35c8dd4f3079ecd9657b04f1edbfea04c364c0fbf7eee1285f942782710b',
|
||||
},
|
||||
'adapters': {
|
||||
"vscode-java": {
|
||||
"name": "vscode-java",
|
||||
"port": "ask",
|
||||
}
|
||||
},
|
||||
},
|
||||
'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': '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.6.vsix',
|
||||
'version': 'v0.3.6',
|
||||
'checksum':
|
||||
'502ee5732851fc4f309294fc296a291b1a114008a1fbcb232f3763be2b8d9c1f',
|
||||
},
|
||||
'do': lambda name, root: InstallBashDebug( name, root ),
|
||||
'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/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-php-debug': {
|
||||
'language': 'php',
|
||||
'enabled': False,
|
||||
'download': {
|
||||
'url': 'https://github.com/felixfbecker/vscode-php-debug/releases/download/'
|
||||
'${version}/${file_name}',
|
||||
},
|
||||
'all': {
|
||||
'version': 'v1.13.0',
|
||||
'file_name': 'php-debug.vsix',
|
||||
'checksum':
|
||||
'8a51e593458fd14623c1c89ebab87347b087d67087717f18bcf77bb788052718',
|
||||
},
|
||||
'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.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'
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
# ------------------------------------------------------------------------------
|
||||
# Entry point
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def CurrentWorkingDir( d ):
|
||||
cur_d = os.getcwd()
|
||||
try:
|
||||
os.chdir( d )
|
||||
yield
|
||||
finally:
|
||||
os.chdir( cur_d )
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class = argparse.RawDescriptionHelpFormatter,
|
||||
description = 'Install DAP Servers for use with Vimspector.',
|
||||
epilog =
|
||||
"""
|
||||
If you're not sure, normally --all is enough to get started.
|
||||
|
||||
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 ):
|
||||
# TODO: import stat and use them by _just_ adding the X bit.
|
||||
print( 'Making executable: {}'.format( file_path ) )
|
||||
os.chmod( file_path, 0o755 )
|
||||
The format of the file can be found on the Vimspector reference guide:
|
||||
https://puremourning.github.io/vimspector
|
||||
|
||||
|
||||
def InstallCppTools( name, root ):
|
||||
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', '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 ):
|
||||
MakeExecutable( os.path.join( root, 'extension', 'bashdb_dir', 'bashdb' ) )
|
||||
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, sslcheck = 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 = urllib2.Request( url, headers = { 'User-Agent': 'Vimspector' } )
|
||||
|
||||
print( "Downloading {} to {}/{}".format( url, destination, file_name ) )
|
||||
|
||||
if not sslcheck:
|
||||
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 ):
|
||||
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 )
|
||||
git_in_repo = [ 'git', '-C', destination ]
|
||||
subprocess.check_call( [ 'git', 'clone', url, destination ] )
|
||||
subprocess.check_call( git_in_repo + [ 'checkout', ref ] )
|
||||
subprocess.check_call( git_in_repo + [ 'submodule', 'sync', '--recursive' ] )
|
||||
subprocess.check_call( git_in_repo + [ '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()
|
||||
NOTE: This script should usually _not_ be run under `sudo` or as root. It
|
||||
downloads and extracts things only to directories under its own path. No
|
||||
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
|
||||
to do.
|
||||
"""
|
||||
)
|
||||
parser.add_argument( '--all',
|
||||
action = 'store_true',
|
||||
help = 'Enable all supported completers' )
|
||||
|
|
@ -638,38 +69,80 @@ parser.add_argument( '--force-all',
|
|||
action = 'store_true',
|
||||
help = 'Enable all unsupported completers' )
|
||||
|
||||
parser.add_argument( '--upgrade',
|
||||
action = 'store_true',
|
||||
help = 'Only update adapters changed from the manifest' )
|
||||
|
||||
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" )
|
||||
|
||||
done_languages = set()
|
||||
for name, gadget in GADGETS.items():
|
||||
lang = gadget[ 'language' ]
|
||||
if lang in done_languages:
|
||||
continue
|
||||
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(
|
||||
'--force-enable-' + lang,
|
||||
'--enable-' + lang,
|
||||
action = 'store_true',
|
||||
help = 'Install the unsupported {} debug adapter for {} support'.format(
|
||||
help = 'Install the {} debug adapter for {} support'.format(
|
||||
name,
|
||||
lang ) )
|
||||
continue
|
||||
|
||||
parser.add_argument(
|
||||
'--enable-' + lang,
|
||||
action = 'store_true',
|
||||
help = 'Install the {} debug adapter for {} support'.format(
|
||||
name,
|
||||
lang ) )
|
||||
|
||||
parser.add_argument(
|
||||
'--disable-' + lang,
|
||||
action = 'store_true',
|
||||
help = "Don't install the {} debug adapter for {} support "
|
||||
'(when supplying --all)'.format( name, lang ) )
|
||||
parser.add_argument(
|
||||
'--disable-' + lang,
|
||||
action = 'store_true',
|
||||
help = "Don't install the {} debug adapter for {} support "
|
||||
'(when supplying --all)'.format( name, lang ) )
|
||||
|
||||
parser.add_argument(
|
||||
"--no-check-certificate",
|
||||
|
|
@ -679,83 +152,103 @@ parser.add_argument(
|
|||
|
||||
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:
|
||||
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 = []
|
||||
all_adapters = {}
|
||||
for name, gadget in GADGETS.items():
|
||||
if not gadget.get( 'enabled', True ):
|
||||
if ( not args.force_all
|
||||
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
|
||||
succeeded = []
|
||||
all_adapters = installer.ReadAdapters(
|
||||
read_existing = args.update_gadget_config )
|
||||
manifest = installer.Manifest()
|
||||
|
||||
try:
|
||||
v = {}
|
||||
v.update( gadget.get( 'all', {} ) )
|
||||
v.update( gadget.get( OS, {} ) )
|
||||
|
||||
if 'download' in gadget:
|
||||
if 'file_name' not in v:
|
||||
raise RuntimeError( "Unsupported OS {} for gadget {}".format( OS,
|
||||
name ) )
|
||||
|
||||
destination = os.path.join( gadget_dir, 'download', name, v[ 'version' ] )
|
||||
|
||||
url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( v )
|
||||
verify_cert_off = args.no_check_certificate
|
||||
|
||||
file_path = DownloadFileTo(
|
||||
url,
|
||||
destination,
|
||||
file_name = gadget[ 'download' ].get( 'target' ),
|
||||
checksum = v.get( 'checksum' ),
|
||||
sslcheck = not verify_cert_off)
|
||||
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 )
|
||||
for name, gadget in gadgets.GADGETS.items():
|
||||
langs = gadget[ 'language' ]
|
||||
if not isinstance( langs, list ):
|
||||
langs = [ langs ]
|
||||
skip = 0
|
||||
for lang in langs:
|
||||
if not gadget.get( 'enabled', True ):
|
||||
if ( not args.force_all
|
||||
and not getattr( args, 'force_enable_' + lang ) ):
|
||||
skip = skip + 1
|
||||
continue
|
||||
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 ) )
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
failed.append( name )
|
||||
print( "FAILED installing {}: {}".format( name, e ) )
|
||||
for name, gadget in CUSTOM_GADGETS.items():
|
||||
if not args.upgrade:
|
||||
manifest.Clear( name )
|
||||
|
||||
|
||||
adapter_config = json.dumps ( { 'adapters': all_adapters },
|
||||
indent=2,
|
||||
sort_keys=True )
|
||||
installer.InstallGagdet( name,
|
||||
gadget,
|
||||
manifest,
|
||||
succeeded,
|
||||
failed,
|
||||
all_adapters )
|
||||
|
||||
if args.no_gadget_config:
|
||||
print( "" )
|
||||
print( "Would write the following gadgets: " )
|
||||
print( adapter_config )
|
||||
installer.WriteAdapters( all_adapters, to_file = sys.stdout )
|
||||
else:
|
||||
with open( install.GetGadgetConfigFile( os.path.dirname( __file__ ) ),
|
||||
'w' ) as f:
|
||||
f.write( adapter_config )
|
||||
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:
|
||||
raise RuntimeError( 'Failed to install gadgets: {}'.format(
|
||||
','.join( failed ) ) )
|
||||
sys.exit( 'Failed to install adapters:\n * {}{}'.format(
|
||||
'\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}
|
||||
mkdir -p 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
|
||||
cp -r ${ROOT}/$d .
|
||||
fi
|
||||
done
|
||||
mkdir -p 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
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,13 @@
|
|||
" See the License for the specific language governing permissions and
|
||||
" limitations under the License.
|
||||
|
||||
if !has( 'python3' )
|
||||
echohl WarningMsg
|
||||
echom 'Vimspector unavailable: Requires Vim compiled with +python3'
|
||||
echohl None
|
||||
finish
|
||||
endif
|
||||
|
||||
" Boilerplate {{{
|
||||
let s:save_cpo = &cpoptions
|
||||
set cpoptions&vim
|
||||
|
|
@ -28,26 +35,51 @@ if exists( 'g:loaded_vimpector' )
|
|||
endif
|
||||
"}}}
|
||||
|
||||
" TODO:
|
||||
" - Check Vim version (for jobs)
|
||||
" - Check python support
|
||||
" - Add commands/mappings/menus?
|
||||
|
||||
let g:loaded_vimpector = 1
|
||||
let g:vimspector_home = expand( '<sfile>:p:h:h' )
|
||||
|
||||
let s:mappings = get( g:, 'vimspector_enable_mappings', '' )
|
||||
|
||||
nnoremap <Plug>VimspectorContinue :<c-u>call vimspector#Continue()<CR>
|
||||
nnoremap <Plug>VimspectorStop :<c-u>call vimspector#Stop()<CR>
|
||||
nnoremap <Plug>VimspectorRestart :<c-u>call vimspector#Restart()<CR>
|
||||
nnoremap <Plug>VimspectorPause :<c-u>call vimspector#Pause()<CR>
|
||||
nnoremap <Plug>VimspectorToggleBreakpoint
|
||||
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 <Plug>VimspectorAddFunctionBreakpoint
|
||||
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 <Plug>VimspectorStepOver :<c-u>call vimspector#StepOver()<CR>
|
||||
nnoremap <Plug>VimspectorStepInto :<c-u>call vimspector#StepInto()<CR>
|
||||
nnoremap <Plug>VimspectorStepOut :<c-u>call vimspector#StepOut()<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'
|
||||
nmap <F5> <Plug>VimspectorContinue
|
||||
|
|
@ -61,11 +93,14 @@ if s:mappings ==# 'VISUAL_STUDIO'
|
|||
nmap <S-F11> <Plug>VimspectorStepOut
|
||||
elseif s:mappings ==# 'HUMAN'
|
||||
nmap <F5> <Plug>VimspectorContinue
|
||||
nmap <leader><F5> <Plug>VimspectorLaunch
|
||||
nmap <F3> <Plug>VimspectorStop
|
||||
nmap <F4> <Plug>VimspectorRestart
|
||||
nmap <F6> <Plug>VimspectorPause
|
||||
nmap <F9> <Plug>VimspectorToggleBreakpoint
|
||||
nmap <leader><F9> <Plug>VimspectorToggleConditionalBreakpoint
|
||||
nmap <F8> <Plug>VimspectorAddFunctionBreakpoint
|
||||
nmap <leader><F8> <Plug>VimspectorRunToCursor
|
||||
nmap <F10> <Plug>VimspectorStepOver
|
||||
nmap <F11> <Plug>VimspectorStepInto
|
||||
nmap <F12> <Plug>VimspectorStepOut
|
||||
|
|
@ -74,15 +109,49 @@ endif
|
|||
command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr
|
||||
\ VimspectorWatch
|
||||
\ call vimspector#AddWatch( <f-args> )
|
||||
command! -bar -nargs=1 -complete=custom,vimspector#CompleteOutput
|
||||
command! -bar -nargs=? -complete=custom,vimspector#CompleteOutput
|
||||
\ VimspectorShowOutput
|
||||
\ call vimspector#ShowOutput( <f-args> )
|
||||
command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr
|
||||
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()
|
||||
\ 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()
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import os
|
|||
import logging
|
||||
|
||||
import json
|
||||
from vimspector import utils
|
||||
from vimspector import utils, signs
|
||||
|
||||
|
||||
class ServerBreakpointHandler( object ):
|
||||
|
|
@ -52,11 +52,23 @@ class ProjectBreakpoints( object ):
|
|||
|
||||
self._next_sign_id = 1
|
||||
|
||||
if not utils.SignDefined( 'vimspectorBP' ):
|
||||
vim.command( 'sign define vimspectorBP text==> texthl=Error' )
|
||||
if not signs.SignDefined( 'vimspectorBP' ):
|
||||
signs.DefineSign( 'vimspectorBP',
|
||||
text = '●',
|
||||
double_text = '●',
|
||||
texthl = 'WarningMsg' )
|
||||
|
||||
if not utils.SignDefined( 'vimspectorBPDisabled' ):
|
||||
vim.command( 'sign define vimspectorBPDisabled text=!> texthl=Warning' )
|
||||
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 ):
|
||||
|
|
@ -79,7 +91,7 @@ class ProjectBreakpoints( object ):
|
|||
# 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
|
||||
# object. This makes no sense and should be centralised so that we don't
|
||||
# have this duplication and bug factory.
|
||||
|
|
@ -96,8 +108,9 @@ class ProjectBreakpoints( object ):
|
|||
'col': 1,
|
||||
'type': 'L',
|
||||
'valid': 1 if bp[ 'state' ] == 'ENABLED' else 0,
|
||||
'text': "Line breakpoint - {}".format(
|
||||
bp[ 'state' ] )
|
||||
'text': "Line breakpoint - {}: {}".format(
|
||||
bp[ 'state' ],
|
||||
json.dumps( bp[ 'options' ] ) )
|
||||
} )
|
||||
# I think this shows that the qf list is not right for this.
|
||||
for bp in self._func_breakpoints:
|
||||
|
|
@ -107,10 +120,12 @@ class ProjectBreakpoints( object ):
|
|||
'col': 1,
|
||||
'type': 'F',
|
||||
'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 ):
|
||||
# These are the user-entered breakpoints.
|
||||
|
|
@ -118,62 +133,139 @@ class ProjectBreakpoints( object ):
|
|||
for bp in breakpoints:
|
||||
self._SignToLine( file_name, bp )
|
||||
if 'sign_id' in bp:
|
||||
vim.command( 'sign unplace {0} group=VimspectorBP'.format(
|
||||
bp[ 'sign_id' ] ) )
|
||||
signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' )
|
||||
|
||||
self._line_breakpoints = defaultdict( list )
|
||||
self._func_breakpoints = []
|
||||
self._exception_breakpoints = None
|
||||
|
||||
self.UpdateUI()
|
||||
|
||||
def ToggleBreakpoint( self ):
|
||||
line, column = vim.current.window.cursor
|
||||
def _FindLineBreakpoint( self, file_name, line ):
|
||||
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
|
||||
|
||||
if not file_name:
|
||||
return
|
||||
|
||||
found_bp = False
|
||||
action = 'New'
|
||||
for index, bp in enumerate( self._line_breakpoints[ file_name ] ):
|
||||
self._SignToLine( file_name, bp )
|
||||
if bp[ 'line' ] == line:
|
||||
found_bp = True
|
||||
if bp[ 'state' ] == 'ENABLED' and not self._connection:
|
||||
bp[ 'state' ] = 'DISABLED'
|
||||
action = 'Disable'
|
||||
else:
|
||||
if 'sign_id' in bp:
|
||||
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': ...
|
||||
} )
|
||||
bp, index = self._FindLineBreakpoint( file_name, line )
|
||||
if bp is None:
|
||||
# ADD
|
||||
self._PutLineBreakpoint( file_name, line, options )
|
||||
elif bp[ 'state' ] == 'ENABLED' and not self._connection:
|
||||
# DISABLE
|
||||
bp[ 'state' ] = 'DISABLED'
|
||||
else:
|
||||
# DELETE
|
||||
self._DeleteLineBreakpoint( bp, file_name, index )
|
||||
|
||||
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( {
|
||||
'state': 'ENABLED',
|
||||
'function': function,
|
||||
'state': 'ENABLED',
|
||||
'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
|
||||
|
|
@ -181,11 +273,13 @@ class ProjectBreakpoints( object ):
|
|||
self.UpdateUI()
|
||||
|
||||
|
||||
def UpdateUI( self ):
|
||||
def UpdateUI( self, then = None ):
|
||||
if self._connection:
|
||||
self.SendBreakpoints()
|
||||
self.SendBreakpoints( then )
|
||||
else:
|
||||
self._ShowBreakpoints()
|
||||
if then:
|
||||
then()
|
||||
|
||||
|
||||
def SetBreakpointsHandler( self, handler ):
|
||||
|
|
@ -205,35 +299,59 @@ class ProjectBreakpoints( object ):
|
|||
|
||||
awaiting = 0
|
||||
|
||||
def response_received():
|
||||
def response_received( *failure_args ):
|
||||
nonlocal awaiting
|
||||
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:
|
||||
doneHandler()
|
||||
|
||||
def response_handler( source, msg ):
|
||||
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
|
||||
# TODO: the line numbers might have changed since pressing the F9 key!
|
||||
|
||||
for file_name, line_breakpoints in self._line_breakpoints.items():
|
||||
temp_idxs = []
|
||||
breakpoints = []
|
||||
for bp in line_breakpoints:
|
||||
self._SignToLine( file_name, bp )
|
||||
if 'sign_id' in bp:
|
||||
vim.command( 'sign unplace {0} group=VimspectorBP'.format(
|
||||
bp[ 'sign_id' ] ) )
|
||||
signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' )
|
||||
del bp[ 'sign_id' ]
|
||||
|
||||
if bp[ 'state' ] != 'ENABLED':
|
||||
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 = {
|
||||
'name': os.path.basename( file_name ),
|
||||
|
|
@ -242,7 +360,13 @@ class ProjectBreakpoints( object ):
|
|||
|
||||
awaiting = awaiting + 1
|
||||
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',
|
||||
'arguments': {
|
||||
|
|
@ -251,30 +375,33 @@ class ProjectBreakpoints( object ):
|
|||
},
|
||||
'sourceModified': False, # TODO: We can actually check this
|
||||
},
|
||||
failure_handler = lambda *_: response_received()
|
||||
failure_handler = response_received
|
||||
)
|
||||
|
||||
# TODO: Add the _configured_breakpoints to function breakpoints
|
||||
|
||||
if self._server_capabilities.get( 'supportsFunctionBreakpoints' ):
|
||||
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(
|
||||
lambda msg: response_handler( None, msg ),
|
||||
{
|
||||
'command': 'setFunctionBreakpoints',
|
||||
'arguments': {
|
||||
'breakpoints': [
|
||||
{ 'name': bp[ 'function' ] }
|
||||
for bp in self._func_breakpoints if bp[ 'state' ] == 'ENABLED'
|
||||
],
|
||||
'breakpoints': breakpoints,
|
||||
}
|
||||
},
|
||||
failure_handler = lambda *_: response_received()
|
||||
failure_handler = response_received
|
||||
)
|
||||
|
||||
if self._exception_breakpoints is None:
|
||||
self._SetUpExceptionBreakpoints( self._configured_breakpoints )
|
||||
|
||||
if self._exception_breakpoints:
|
||||
awaiting = awaiting + 1
|
||||
self._connection.DoRequest(
|
||||
|
|
@ -283,7 +410,7 @@ class ProjectBreakpoints( object ):
|
|||
'command': 'setExceptionBreakpoints',
|
||||
'arguments': self._exception_breakpoints
|
||||
},
|
||||
failure_handler = lambda *_: response_received()
|
||||
failure_handler = response_received
|
||||
)
|
||||
|
||||
if awaiting == 0 and doneHandler:
|
||||
|
|
@ -340,33 +467,44 @@ class ProjectBreakpoints( object ):
|
|||
# pay any attention to them anyway.
|
||||
self._exception_breakpoints[ 'exceptionOptions' ] = []
|
||||
|
||||
|
||||
def Refresh( self, file_name ):
|
||||
# TODO: Just this file ?
|
||||
self._ShowBreakpoints()
|
||||
|
||||
|
||||
def _ShowBreakpoints( self ):
|
||||
for file_name, line_breakpoints in self._line_breakpoints.items():
|
||||
for bp in line_breakpoints:
|
||||
self._SignToLine( file_name, bp )
|
||||
if 'sign_id' in bp:
|
||||
vim.command( 'sign unplace {0} group=VimspectorBP '.format(
|
||||
bp[ 'sign_id' ] ) )
|
||||
signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' )
|
||||
else:
|
||||
bp[ 'sign_id' ] = self._next_sign_id
|
||||
self._next_sign_id += 1
|
||||
|
||||
vim.command(
|
||||
'sign place {0} group=VimspectorBP line={1} name={2} file={3}'.format(
|
||||
bp[ 'sign_id' ] ,
|
||||
bp[ 'line' ],
|
||||
'vimspectorBP' if bp[ 'state' ] == 'ENABLED'
|
||||
else 'vimspectorBPDisabled',
|
||||
file_name ) )
|
||||
sign = ( 'vimspectorBPDisabled' if bp[ 'state' ] != 'ENABLED'
|
||||
else 'vimspectorBPCond' if 'condition' in bp[ 'options' ]
|
||||
else 'vimspectorBP' )
|
||||
|
||||
if utils.BufferExists( file_name ):
|
||||
signs.PlaceSign( bp[ 'sign_id' ],
|
||||
'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': file_name, 'group': 'VimspectorBP', } ) ) )
|
||||
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' ] )
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import logging
|
|||
import json
|
||||
from collections import defaultdict
|
||||
|
||||
from vimspector import utils
|
||||
from vimspector import utils, terminal, signs
|
||||
|
||||
|
||||
class CodeView( object ):
|
||||
|
|
@ -26,50 +26,115 @@ class CodeView( object ):
|
|||
self._window = window
|
||||
self._api_prefix = api_prefix
|
||||
|
||||
self._terminal_window = None
|
||||
self._terminal_buffer_number = None
|
||||
self._terminal = None
|
||||
self.current_syntax = None
|
||||
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
utils.SetUpLogging( self._logger )
|
||||
|
||||
# FIXME: This ID is by group, so should be module scope
|
||||
self._next_sign_id = 1
|
||||
self._breakpoints = defaultdict( list )
|
||||
self._signs = {
|
||||
'vimspectorPC': None,
|
||||
'breakpoints': []
|
||||
}
|
||||
self._current_frame = None
|
||||
|
||||
with utils.LetCurrentWindow( self._window ):
|
||||
vim.command( 'nnoremenu WinBar.Continue :call vimspector#Continue()<CR>' )
|
||||
vim.command( 'nnoremenu WinBar.Next :call vimspector#StepOver()<CR>' )
|
||||
vim.command( 'nnoremenu WinBar.Step :call vimspector#StepInto()<CR>' )
|
||||
vim.command( 'nnoremenu WinBar.Finish :call vimspector#StepOut()<CR>' )
|
||||
vim.command( 'nnoremenu WinBar.Pause :call vimspector#Pause()<CR>' )
|
||||
vim.command( 'nnoremenu WinBar.Stop :call vimspector#Stop()<CR>' )
|
||||
vim.command( 'nnoremenu WinBar.Restart :call vimspector#Restart()<CR>' )
|
||||
vim.command( 'nnoremenu WinBar.Reset :call vimspector#Reset()<CR>' )
|
||||
if utils.UseWinBar():
|
||||
# Buggy neovim doesn't render correctly when the WinBar is defined:
|
||||
# https://github.com/neovim/neovim/issues/12689
|
||||
vim.command( 'nnoremenu WinBar.■\\ Stop '
|
||||
':call vimspector#Stop()<CR>' )
|
||||
vim.command( 'nnoremenu WinBar.▶\\ Cont '
|
||||
':call vimspector#Continue()<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>' )
|
||||
|
||||
if not utils.SignDefined( 'vimspectorPC' ):
|
||||
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 ):
|
||||
if self._signs[ 'vimspectorPC' ]:
|
||||
vim.command( 'sign unplace {} group=VimspectorCode'.format(
|
||||
self._signs[ 'vimspectorPC' ] ) )
|
||||
self._signs[ 'vimspectorPC' ] = None
|
||||
"""Returns True if the code window was updated with the frame, False
|
||||
otherwise. False means either the frame is junk, we couldn't find the file
|
||||
(or don't have the data) or the code window no longer exits."""
|
||||
|
||||
if not frame or not frame.get( 'source' ):
|
||||
self._UndisplayPC()
|
||||
return False
|
||||
|
||||
if 'path' not in frame[ 'source' ]:
|
||||
self._UndisplayPC()
|
||||
return False
|
||||
|
||||
self._current_frame = frame
|
||||
|
||||
if not self._window.valid:
|
||||
return False
|
||||
|
||||
utils.JumpToWindow( self._window )
|
||||
|
||||
try:
|
||||
utils.OpenFileInCurrentWindow( frame[ 'source' ][ 'path' ] )
|
||||
vim.command( 'doautocmd <nomodeline> User VimspectorJumpedToFrame' )
|
||||
except vim.error:
|
||||
self._logger.exception( 'Unexpected vim error opening file {}'.format(
|
||||
frame[ 'source' ][ 'path' ] ) )
|
||||
|
|
@ -88,27 +153,19 @@ class CodeView( object ):
|
|||
frame[ 'source' ][ 'path' ] )
|
||||
return False
|
||||
|
||||
self._signs[ 'vimspectorPC' ] = self._next_sign_id
|
||||
self._next_sign_id += 1
|
||||
|
||||
vim.command( 'sign place {0} group=VimspectorCode priority=20 '
|
||||
'line={1} name=vimspectorPC '
|
||||
'file={2}'.format(
|
||||
self._signs[ 'vimspectorPC' ],
|
||||
frame[ 'line' ],
|
||||
frame[ 'source' ][ 'path' ] ) )
|
||||
|
||||
self.current_syntax = utils.ToUnicode(
|
||||
vim.current.buffer.options[ 'syntax' ] )
|
||||
|
||||
self.ShowBreakpoints()
|
||||
|
||||
return True
|
||||
|
||||
def Clear( self ):
|
||||
if self._signs[ 'vimspectorPC' ]:
|
||||
vim.command( 'sign unplace {} group=VimspectorCode'.format(
|
||||
self._signs[ 'vimspectorPC' ] ) )
|
||||
signs.UnplaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode' )
|
||||
self._signs[ 'vimspectorPC' ] = None
|
||||
|
||||
self._UndisplayPC()
|
||||
self._UndisplaySigns()
|
||||
self.current_syntax = None
|
||||
|
||||
|
|
@ -118,25 +175,29 @@ class CodeView( object ):
|
|||
|
||||
def AddBreakpoints( self, source, breakpoints ):
|
||||
for breakpoint in breakpoints:
|
||||
if 'source' not in breakpoint:
|
||||
if source:
|
||||
breakpoint[ 'source' ] = source
|
||||
else:
|
||||
self._logger.warn( 'missing source in breakpoint {0}'.format(
|
||||
json.dumps( breakpoint ) ) )
|
||||
continue
|
||||
source = breakpoint.get( 'source' ) or source
|
||||
if not source or 'path' not in source:
|
||||
self._logger.warn( 'missing source/path in breakpoint {0}'.format(
|
||||
json.dumps( breakpoint ) ) )
|
||||
continue
|
||||
|
||||
self._breakpoints[ breakpoint[ 'source' ][ 'path' ] ].append(
|
||||
breakpoint )
|
||||
breakpoint[ 'source' ] = source
|
||||
self._breakpoints[ source[ 'path' ] ].append( breakpoint )
|
||||
|
||||
self._logger.debug( 'Breakpoints at this point: {0}'.format(
|
||||
json.dumps( self._breakpoints, indent = 2 ) ) )
|
||||
|
||||
self.ShowBreakpoints()
|
||||
|
||||
|
||||
def AddBreakpoint( self, breakpoint ):
|
||||
self.AddBreakpoints( None, [ breakpoint ] )
|
||||
|
||||
|
||||
def UpdateBreakpoint( self, bp ):
|
||||
if 'id' not in bp:
|
||||
self.AddBreakpoints( None, [ bp ] )
|
||||
self.AddBreakpoint( bp )
|
||||
return
|
||||
|
||||
for _, breakpoint_list in self._breakpoints.items():
|
||||
for index, breakpoint in enumerate( breakpoint_list ):
|
||||
|
|
@ -146,11 +207,31 @@ class CodeView( object ):
|
|||
return
|
||||
|
||||
# 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 ):
|
||||
for sign_id in self._signs[ 'breakpoints' ]:
|
||||
vim.command( 'sign unplace {} group=VimspectorCode'.format( sign_id ) )
|
||||
signs.UnplaceSign( sign_id, 'VimspectorCode' )
|
||||
|
||||
self._signs[ 'breakpoints' ] = []
|
||||
|
||||
|
|
@ -169,17 +250,17 @@ class CodeView( object ):
|
|||
sign_id = self._next_sign_id
|
||||
self._next_sign_id += 1
|
||||
self._signs[ 'breakpoints' ].append( sign_id )
|
||||
vim.command(
|
||||
'sign place {0} group=VimspectorCode priority=9 '
|
||||
'line={1} '
|
||||
'name={2} '
|
||||
'file={3}'.format(
|
||||
sign_id,
|
||||
breakpoint[ 'line' ],
|
||||
'vimspectorBP' if breakpoint[ 'verified' ]
|
||||
else 'vimspectorBPDisabled',
|
||||
file_name ) )
|
||||
if utils.BufferExists( file_name ):
|
||||
signs.PlaceSign( sign_id,
|
||||
'VimspectorCode',
|
||||
'vimspectorBP' if breakpoint[ 'verified' ]
|
||||
else 'vimspectorBPDisabled',
|
||||
file_name,
|
||||
breakpoint[ 'line' ] )
|
||||
|
||||
# We need to also check if there's a breakpoint on this PC line and chnge
|
||||
# the PC
|
||||
self._DisplayPC()
|
||||
|
||||
def BreakpointsAsQuickFix( self ):
|
||||
qf = []
|
||||
|
|
@ -198,49 +279,11 @@ class CodeView( object ):
|
|||
|
||||
|
||||
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
|
||||
# integrated one.
|
||||
|
||||
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 int( utils.Call( 'vimspector#internal#{}term#IsFinished'.format(
|
||||
self._api_prefix ),
|
||||
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 ):
|
||||
buffer_number = int(
|
||||
utils.Call(
|
||||
'vimspector#internal#{}term#Start'.format( self._api_prefix ),
|
||||
args,
|
||||
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" )
|
||||
else:
|
||||
self._terminal_window = terminal_window
|
||||
self._terminal_buffer_number = buffer_number
|
||||
|
||||
return buffer_number
|
||||
# FIXME: Change this tor return the PID rather than having debug_session
|
||||
# work that out
|
||||
return self._terminal.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 ):
|
||||
def __init__( self, handler, send_func ):
|
||||
def __init__( self, handlers, send_func ):
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
utils.SetUpLogging( self._logger )
|
||||
|
||||
self._Write = send_func
|
||||
self._SetState( 'READ_HEADER' )
|
||||
self._buffer = bytes()
|
||||
self._handler = handler
|
||||
self._handlers = handlers
|
||||
self._next_message_id = 0
|
||||
self._outstanding_requests = {}
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ class DebugAdapterConnection( object ):
|
|||
|
||||
def Reset( self ):
|
||||
self._Write = None
|
||||
self._handler = None
|
||||
self._handlers = None
|
||||
|
||||
while self._outstanding_requests:
|
||||
_, request = self._outstanding_requests.popitem()
|
||||
|
|
@ -169,6 +169,10 @@ class DebugAdapterConnection( object ):
|
|||
self._headers = {}
|
||||
|
||||
def _SendMessage( self, msg ):
|
||||
if not self._Write:
|
||||
# Connection was destroyed
|
||||
return False
|
||||
|
||||
msg = json.dumps( msg )
|
||||
self._logger.debug( 'Sending Message: {0}'.format( msg ) )
|
||||
|
||||
|
|
@ -222,7 +226,12 @@ class DebugAdapterConnection( object ):
|
|||
|
||||
# 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 ) )
|
||||
|
||||
|
|
@ -233,7 +242,7 @@ class DebugAdapterConnection( object ):
|
|||
|
||||
|
||||
def _OnMessageReceived( self, message ):
|
||||
if not self._handler:
|
||||
if not self._handlers:
|
||||
return
|
||||
|
||||
if message[ 'type' ] == 'response':
|
||||
|
|
@ -266,25 +275,21 @@ class DebugAdapterConnection( object ):
|
|||
self._logger.error( 'Request failed: {0}'.format( reason ) )
|
||||
if request.failure_handler:
|
||||
request.failure_handler( reason, message )
|
||||
elif 'OnFailure' in dir( self._handler ):
|
||||
self._handler.OnFailure( reason, message )
|
||||
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':
|
||||
method = 'OnEvent_' + message[ 'event' ]
|
||||
if method in dir( self._handler ):
|
||||
getattr( self._handler, method )( message )
|
||||
else:
|
||||
utils.UserMessage( 'Unhandled event: {0}'.format( message[ 'event' ] ),
|
||||
persist = True )
|
||||
for h in self._handlers:
|
||||
if method in dir( h ):
|
||||
getattr( h, method )( message )
|
||||
elif message[ 'type' ] == 'request':
|
||||
method = 'OnRequest_' + message[ 'command' ]
|
||||
if method in dir( self._handler ):
|
||||
getattr( self._handler, method )( message )
|
||||
else:
|
||||
utils.UserMessage(
|
||||
'Unhandled request: {0}'.format( message[ 'command' ] ),
|
||||
persist = True )
|
||||
for h in self._handlers:
|
||||
if method in dir( h ):
|
||||
getattr( h, method )( message )
|
||||
|
||||
|
||||
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,15 +26,40 @@ def GetOS():
|
|||
return 'linux'
|
||||
|
||||
|
||||
def GetGadgetDir( vimspector_base, OS ):
|
||||
return os.path.join( os.path.abspath( vimspector_base ), 'gadgets', OS )
|
||||
def mkdirs( p ):
|
||||
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 ):
|
||||
return os.path.join( GetGadgetDir( vimspector_base, GetOS() ),
|
||||
'.gadgets.json' )
|
||||
return os.path.join( GetGadgetDir( vimspector_base ), '.gadgets.json' )
|
||||
|
||||
|
||||
def GetGadgetConfigDir( vimspector_base ):
|
||||
return os.path.join( GetGadgetDir( vimspector_base, GetOS() ),
|
||||
'.gadgets.d' )
|
||||
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
|
||||
# limitations under the License.
|
||||
|
||||
from vimspector import utils
|
||||
from vimspector import utils, install
|
||||
|
||||
import vim
|
||||
import json
|
||||
import typing
|
||||
|
||||
|
||||
class TabBuffer( object ):
|
||||
|
|
@ -25,13 +26,15 @@ class TabBuffer( object ):
|
|||
self.index = index
|
||||
self.flag = False
|
||||
self.is_job = False
|
||||
self.syntax = None
|
||||
|
||||
|
||||
BUFFER_MAP = {
|
||||
'console': 'Console',
|
||||
'stdout': 'Console',
|
||||
'output': 'Console',
|
||||
'stderr': 'stderr',
|
||||
'telemetry': 'Telemetry',
|
||||
'telemetry': None,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -39,24 +42,34 @@ def CategoryToBuffer( 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 ):
|
||||
def __init__( self, connection, window, api_prefix ):
|
||||
"""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._connection = connection
|
||||
self._buffers = {}
|
||||
self._api_prefix = api_prefix
|
||||
VIEWS.add( self )
|
||||
|
||||
for b in set( BUFFER_MAP.values() ):
|
||||
self._CreateBuffer( b )
|
||||
def Print( self, category, text: typing.Union[ str, list ] ):
|
||||
if not isinstance( text, list ):
|
||||
text = text.splitlines()
|
||||
|
||||
self._CreateBuffer(
|
||||
'Vimspector',
|
||||
file_name = vim.eval( 'expand( "~/.vimspector.log" )' ) )
|
||||
|
||||
self._ShowOutput( 'Console' )
|
||||
|
||||
def Print( self, categroy, text ):
|
||||
self._Print( 'server', text.splitlines() )
|
||||
self._Print( category, text )
|
||||
|
||||
def OnOutput( self, event ):
|
||||
category = CategoryToBuffer( event.get( 'category' ) or 'output' )
|
||||
|
|
@ -68,6 +81,10 @@ class OutputView( object ):
|
|||
self._Print( category, text_lines )
|
||||
|
||||
def _Print( self, category, text_lines ):
|
||||
if category is None:
|
||||
# This category is supressed
|
||||
return
|
||||
|
||||
if category not in self._buffers:
|
||||
self._CreateBuffer( category )
|
||||
|
||||
|
|
@ -79,9 +96,187 @@ class OutputView( object ):
|
|||
self._ToggleFlag( category, True )
|
||||
|
||||
# Scroll the buffer
|
||||
with utils.RestoreCurrentWindow():
|
||||
with utils.RestoreCurrentBuffer( self._window ):
|
||||
if self._window.valid:
|
||||
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 )
|
||||
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 ):
|
||||
self._connection = connection
|
||||
|
|
@ -90,46 +285,18 @@ class OutputView( object ):
|
|||
# Don't clear because output is probably still useful
|
||||
self._connection = None
|
||||
|
||||
def Reset( self ):
|
||||
self.Clear()
|
||||
|
||||
def Clear( self ):
|
||||
for category, tab_buffer in self._buffers.items():
|
||||
if tab_buffer.is_job:
|
||||
utils.CleanUpCommand( category, self._api_prefix )
|
||||
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 str( 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 ):
|
||||
console = self._buffers[ 'Console' ].buf
|
||||
with utils.ModifiableScratchBuffer( console ):
|
||||
utils.AppendToBuffer( console, 'Evaluating: ' + expression )
|
||||
def Evaluate( self, frame, expression, verbose ):
|
||||
if verbose:
|
||||
self._Print( 'Console', f"Evaluating: { expression }" )
|
||||
|
||||
def print_result( message ):
|
||||
with utils.ModifiableScratchBuffer( console ):
|
||||
utils.AppendToBuffer( console,
|
||||
'Evaluated: ' + expression )
|
||||
result = message[ 'body' ][ 'result' ]
|
||||
if result is None:
|
||||
result = '<no result>'
|
||||
self._Print( 'Console', result.splitlines() )
|
||||
|
||||
result = message[ 'body' ][ 'result' ]
|
||||
if result is None:
|
||||
result = 'null'
|
||||
|
||||
utils.AppendToBuffer( console, ' Result: ' + result )
|
||||
def print_failure( reason, msg ):
|
||||
self._Print( 'Console', reason.splitlines() )
|
||||
|
||||
request = {
|
||||
'command': 'evaluate',
|
||||
|
|
@ -142,72 +309,6 @@ class OutputView( object ):
|
|||
if frame:
|
||||
request[ 'arguments' ][ 'frameId' ] = frame[ 'id' ]
|
||||
|
||||
self._connection.DoRequest( print_result, request )
|
||||
|
||||
def _ToggleFlag( self, category, flag ):
|
||||
if self._buffers[ category ].flag != flag:
|
||||
self._buffers[ category ].flag = flag
|
||||
with utils.LetCurrentWindow( self._window ):
|
||||
self._RenderWinBar( category )
|
||||
|
||||
|
||||
def RunJobWithOutput( self, category, cmd ):
|
||||
self._CreateBuffer( category, cmd = cmd )
|
||||
|
||||
|
||||
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._api_prefix )
|
||||
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 '' ) )
|
||||
|
||||
def GetCategories( self ):
|
||||
return [ category for category in self._buffers.keys() ]
|
||||
self._connection.DoRequest( print_result,
|
||||
request,
|
||||
print_failure )
|
||||
|
|
|
|||
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,18 +16,82 @@
|
|||
import vim
|
||||
import os
|
||||
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 ):
|
||||
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__ )
|
||||
utils.SetUpLogging( self._logger )
|
||||
|
||||
self._buf = buf
|
||||
self._buf = win.buffer
|
||||
self._session = session
|
||||
self._connection = connection
|
||||
self._connection = None
|
||||
|
||||
self._current_thread = None
|
||||
self._current_frame = None
|
||||
|
|
@ -35,27 +99,57 @@ class StackTraceView( object ):
|
|||
|
||||
self._threads = []
|
||||
self._sources = {}
|
||||
self._scratch_buffers = []
|
||||
|
||||
utils.SetUpScratchBuffer( self._buf, 'vimspector.StackTrace' )
|
||||
# FIXME: This ID is by group, so should be module scope
|
||||
self._current_thread_sign_id = 0 # 1 when used
|
||||
self._current_frame_sign_id = 0 # 2 when used
|
||||
|
||||
vim.current.buffer = self._buf
|
||||
# FIXME: Remove all usage of "Windown" and just use buffers to prevent all
|
||||
# the bugs around the window being closed.
|
||||
self._win = vim.current.window
|
||||
utils.SetUpHiddenBuffer( self._buf, 'vimspector.StackTrace' )
|
||||
utils.SetUpUIWindow( win )
|
||||
|
||||
vim.command( 'nnoremap <buffer> <CR> :call vimspector#GoToFrame()<CR>' )
|
||||
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_thread = {}
|
||||
|
||||
# TODO: We really need a proper state model
|
||||
#
|
||||
# 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
|
||||
self._requesting_threads = StackTraceView.ThreadRequestState.NO
|
||||
self._pending_thread_request = None
|
||||
|
||||
|
||||
def GetCurrentThreadId( self ):
|
||||
|
|
@ -68,14 +162,22 @@ class StackTraceView( object ):
|
|||
self._current_frame = None
|
||||
self._current_thread = None
|
||||
self._current_syntax = ""
|
||||
self._threads = []
|
||||
self._threads.clear()
|
||||
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 ):
|
||||
utils.ClearBuffer( self._buf )
|
||||
|
||||
def ConnectionUp( self, connection ):
|
||||
self._connection = connection
|
||||
self._requesting_threads = False
|
||||
|
||||
def ConnectionClosed( self ):
|
||||
self.Clear()
|
||||
|
|
@ -83,69 +185,142 @@ class StackTraceView( object ):
|
|||
|
||||
def Reset( self ):
|
||||
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 ):
|
||||
pending_request = False
|
||||
if self._requesting_threads:
|
||||
pending_request = True
|
||||
def LoadThreads( self,
|
||||
infer_current_frame,
|
||||
reason = '',
|
||||
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
|
||||
|
||||
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' ]:
|
||||
if pending_request:
|
||||
# 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 )
|
||||
self._requesting_threads = StackTraceView.ThreadRequestState.NO
|
||||
self._pending_thread_request = None
|
||||
|
||||
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()
|
||||
|
||||
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 )
|
||||
|
||||
if infer_current_frame and thread[ 'id' ] == self._current_thread:
|
||||
self._LoadStackTrace( thread, True )
|
||||
elif infer_current_frame and self._current_thread is None:
|
||||
self._current_thread = thread[ 'id' ]
|
||||
self._LoadStackTrace( thread, True )
|
||||
# If the threads were requested due to a stopped event, update any
|
||||
# stopped thread state. Note we have to do this here (rather than in the
|
||||
# stopped event handler) because we must apply this event to any new
|
||||
# threads that are received here.
|
||||
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, {
|
||||
'command': 'threads',
|
||||
} )
|
||||
}, failure_handler )
|
||||
|
||||
def _DrawThreads( self ):
|
||||
self._line_to_frame.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 ):
|
||||
utils.ClearBuffer( self._buf )
|
||||
with utils.RestoreCursorPosition():
|
||||
utils.ClearBuffer( self._buf )
|
||||
|
||||
for thread in self._threads:
|
||||
icon = '+' if '_frames' not in thread else '-'
|
||||
for thread in self._threads:
|
||||
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(
|
||||
self._buf,
|
||||
'{0} Thread: {1}'.format( icon, thread[ 'name' ] ) )
|
||||
if self._current_thread == thread.id:
|
||||
signs.PlaceSign( self._current_thread_sign_id,
|
||||
'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 ):
|
||||
thread[ '_frames' ] = message[ 'body' ][ 'stackFrames' ]
|
||||
thread.Expand( message[ 'body' ][ 'stackFrames' ] )
|
||||
if infer_current_frame:
|
||||
for frame in thread[ '_frames' ]:
|
||||
if self._JumpToFrame( frame ):
|
||||
for frame in thread.stacktrace:
|
||||
if self._JumpToFrame( frame, reason ):
|
||||
break
|
||||
|
||||
self._DrawThreads()
|
||||
|
|
@ -153,32 +328,113 @@ class StackTraceView( object ):
|
|||
self._connection.DoRequest( consume_stacktrace, {
|
||||
'command': 'stackTrace',
|
||||
'arguments': {
|
||||
'threadId': thread[ 'id' ],
|
||||
'threadId': thread.id,
|
||||
}
|
||||
} )
|
||||
|
||||
def ExpandFrameOrThread( self ):
|
||||
|
||||
def _GetSelectedThread( self ) -> Thread:
|
||||
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
|
||||
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:
|
||||
self._JumpToFrame( self._line_to_frame[ current_line ] )
|
||||
elif current_line in self._line_to_thread:
|
||||
thread = self._line_to_thread[ current_line ]
|
||||
if '_frames' in thread:
|
||||
del thread[ '_frames' ]
|
||||
with utils.RestoreCursorPosition():
|
||||
self._DrawThreads()
|
||||
else:
|
||||
if thread:
|
||||
if thread.IsExpanded():
|
||||
thread.Collapse()
|
||||
self._DrawThreads()
|
||||
elif thread.CanExpand():
|
||||
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():
|
||||
if 'line' in frame and frame[ 'line' ] > 0:
|
||||
# Should this set the current _Thread_ too ? If i jump to a frame in
|
||||
# Thread 2, should that become the focussed thread ?
|
||||
self._current_frame = frame
|
||||
return self._session.SetCurrentFrame( self._current_frame )
|
||||
self._DrawThreads()
|
||||
return self._session.SetCurrentFrame( self._current_frame, reason )
|
||||
return False
|
||||
|
||||
source = frame.get( 'source' ) or {}
|
||||
|
|
@ -193,59 +449,94 @@ class StackTraceView( object ):
|
|||
else:
|
||||
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 ):
|
||||
if 'threadId' in event:
|
||||
self._current_thread = event[ 'threadId' ]
|
||||
elif event.get( 'allThreadsStopped', False ) and self._threads:
|
||||
self._current_thread = self._threads[ 0 ][ 'id' ]
|
||||
threadId = event.get( 'threadId' )
|
||||
allThreadsStopped = event.get( 'allThreadsStopped', False )
|
||||
|
||||
if self._current_thread is not None:
|
||||
for thread in self._threads:
|
||||
if thread[ 'id' ] == self._current_thread:
|
||||
self._LoadStackTrace( thread, True )
|
||||
return
|
||||
# Work out if we should change the current thread
|
||||
if threadId is not None:
|
||||
self._current_thread = threadId
|
||||
elif self._current_thread is None and allThreadsStopped and self._threads:
|
||||
self._current_thread = self._threads[ 0 ].id
|
||||
|
||||
self.LoadThreads( True )
|
||||
self.LoadThreads( True, 'stopped', event )
|
||||
|
||||
def OnThreadEvent( self, event ):
|
||||
infer_current_frame = False
|
||||
if event[ 'reason' ] == 'started' and self._current_thread is None:
|
||||
self._current_thread = event[ 'threadId' ]
|
||||
self.LoadThreads( True )
|
||||
infer_current_frame = True
|
||||
|
||||
def Continue( self ):
|
||||
if self._current_thread is None:
|
||||
utils.UserMessage( 'No current thread', persist = True )
|
||||
if event[ 'reason' ] == 'exited':
|
||||
for thread in self._threads:
|
||||
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
|
||||
|
||||
self._session._connection.DoRequest( None, {
|
||||
'command': 'continue',
|
||||
'arguments': {
|
||||
'threadId': self._current_thread,
|
||||
},
|
||||
} )
|
||||
if self._current_frame_sign_id:
|
||||
signs.UnplaceSign( self._current_frame_sign_id, 'VimspectorStackTrace' )
|
||||
else:
|
||||
self._current_frame_sign_id = 2
|
||||
|
||||
self._session.ClearCurrentFrame()
|
||||
self.LoadThreads( True )
|
||||
|
||||
def Pause( self ):
|
||||
if self._current_thread is None:
|
||||
utils.UserMessage( 'No current thread', persist = True )
|
||||
return
|
||||
|
||||
self._session._connection.DoRequest( None, {
|
||||
'command': 'pause',
|
||||
'arguments': {
|
||||
'threadId': self._current_thread,
|
||||
},
|
||||
} )
|
||||
|
||||
def _DrawStackTrace( self, thread ):
|
||||
if '_frames' not in thread:
|
||||
return
|
||||
|
||||
stackFrames = thread[ '_frames' ]
|
||||
|
||||
for frame in stackFrames:
|
||||
for frame in thread.stacktrace:
|
||||
if frame.get( 'source' ):
|
||||
source = frame[ 'source' ]
|
||||
else:
|
||||
|
|
@ -269,7 +560,15 @@ class StackTraceView( object ):
|
|||
source[ 'name' ],
|
||||
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 ):
|
||||
source_reference = int( source[ 'sourceReference' ] )
|
||||
|
|
@ -282,12 +581,14 @@ class StackTraceView( object ):
|
|||
def consume_source( msg ):
|
||||
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 )
|
||||
|
||||
buf = utils.BufferForFile( buf_name )
|
||||
utils.SetUpScratchBuffer( buf, buf_name )
|
||||
self._scratch_buffers.append( buf )
|
||||
utils.SetUpHiddenBuffer( buf, buf_name )
|
||||
source[ 'path' ] = buf_name
|
||||
with utils.ModifiableScratchBuffer( buf ):
|
||||
utils.SetBufferContents( buf, msg[ 'body' ][ 'content' ] )
|
||||
|
|
@ -305,4 +606,4 @@ class StackTraceView( object ):
|
|||
def SetSyntax( self, syntax ):
|
||||
self._current_syntax = utils.SetSyntax( self._current_syntax,
|
||||
syntax,
|
||||
self._win )
|
||||
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,12 +19,18 @@ import os
|
|||
import contextlib
|
||||
import vim
|
||||
import json
|
||||
import string
|
||||
import functools
|
||||
import subprocess
|
||||
import shlex
|
||||
import collections
|
||||
import re
|
||||
import typing
|
||||
|
||||
|
||||
_log_handler = logging.FileHandler( os.path.expanduser( '~/.vimspector.log' ),
|
||||
mode = 'w' )
|
||||
LOG_FILE = os.path.expanduser( os.path.join( '~', '.vimspector.log' ) )
|
||||
|
||||
_log_handler = logging.FileHandler( LOG_FILE, mode = 'w' )
|
||||
|
||||
_log_handler.setFormatter(
|
||||
logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' ) )
|
||||
|
||||
|
|
@ -39,18 +45,38 @@ _logger = logging.getLogger( __name__ )
|
|||
SetUpLogging( _logger )
|
||||
|
||||
|
||||
def BufferNumberForFile( file_name ):
|
||||
return int( vim.eval( 'bufnr( "{0}", 1 )'.format( file_name ) ) )
|
||||
def BufferNumberForFile( file_name, create = True ):
|
||||
return int( vim.eval( "bufnr( '{0}', {1} )".format(
|
||||
Escape( file_name ),
|
||||
int( create ) ) ) )
|
||||
|
||||
|
||||
def BufferForFile( 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 ):
|
||||
buffer_number = BufferNumberForFile( file_name )
|
||||
try:
|
||||
vim.command( 'bu {0}'.format( buffer_number ) )
|
||||
vim.current.buffer = vim.buffers[ buffer_number ]
|
||||
except vim.error as e:
|
||||
if 'E325' not in str( e ):
|
||||
raise
|
||||
|
|
@ -58,21 +84,30 @@ def OpenFileInCurrentWindow( file_name ):
|
|||
return vim.buffers[ buffer_number ]
|
||||
|
||||
|
||||
def SetUpCommandBuffer( cmd, name, api_prefix ):
|
||||
bufs = vim.eval(
|
||||
'vimspector#internal#{}job#StartCommandWithLog( {}, "{}" )'.format(
|
||||
api_prefix,
|
||||
json.dumps( cmd ),
|
||||
name ) )
|
||||
COMMAND_HANDLERS = {}
|
||||
|
||||
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 ) )
|
||||
elif not all( int( b ) > 0 for b in bufs ):
|
||||
elif int( buf ) <= 0:
|
||||
raise RuntimeError( "Unable to get all streams for job {}: {}".format(
|
||||
name,
|
||||
cmd ) )
|
||||
|
||||
return [ vim.buffers[ int( b ) ] for b in bufs ]
|
||||
return vim.buffers[ int( buf ) ]
|
||||
|
||||
|
||||
def CleanUpCommand( name, api_prefix ):
|
||||
|
|
@ -81,15 +116,18 @@ def CleanUpCommand( name, api_prefix ):
|
|||
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 ):
|
||||
buf.options[ 'buftype' ] = 'nofile'
|
||||
buf.options[ 'swapfile' ] = False
|
||||
buf.options[ 'modifiable' ] = False
|
||||
buf.options[ 'modified' ] = False
|
||||
buf.options[ 'readonly' ] = True
|
||||
buf.options[ 'buflisted' ] = False
|
||||
SetUpHiddenBuffer( buf, name )
|
||||
buf.options[ 'bufhidden' ] = 'wipe'
|
||||
buf.name = name
|
||||
|
||||
|
||||
def SetUpHiddenBuffer( buf, name ):
|
||||
|
|
@ -103,7 +141,7 @@ def SetUpHiddenBuffer( buf, 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
|
||||
if not Exists( '*prompt_setprompt' ):
|
||||
return SetUpHiddenBuffer( buf, name )
|
||||
|
|
@ -114,7 +152,9 @@ def SetUpPromptBuffer( buf, name, prompt, callback, hidden=False ):
|
|||
buf.options[ 'modified' ] = False
|
||||
buf.options[ 'readonly' ] = 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
|
||||
|
||||
vim.eval( "prompt_setprompt( {0}, '{1}' )".format( buf.number,
|
||||
|
|
@ -123,6 +163,20 @@ def SetUpPromptBuffer( buf, name, prompt, callback, hidden=False ):
|
|||
buf.number,
|
||||
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
|
||||
|
|
@ -159,20 +213,40 @@ def RestoreCurrentWindow():
|
|||
try:
|
||||
yield
|
||||
finally:
|
||||
vim.current.tabpage = old_tabpage
|
||||
vim.current.window = old_window
|
||||
if old_tabpage.valid and old_window.valid:
|
||||
vim.current.tabpage = old_tabpage
|
||||
vim.current.window = old_window
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def RestoreCurrentBuffer( window ):
|
||||
# TODO: Don't trigger autoccommands when shifting buffers
|
||||
old_buffer = window.buffer
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
with RestoreCurrentWindow():
|
||||
vim.current.window = window
|
||||
vim.current.buffer = old_buffer
|
||||
if window.valid:
|
||||
with RestoreCurrentWindow():
|
||||
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
|
||||
|
|
@ -182,6 +256,14 @@ def LetCurrentWindow( window ):
|
|||
yield
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def LetCurrentBuffer( buf ):
|
||||
with RestoreCursorPosition():
|
||||
with RestoreCurrentBuffer( vim.current.window ):
|
||||
vim.current.buffer = buf
|
||||
yield
|
||||
|
||||
|
||||
def JumpToWindow( window ):
|
||||
vim.current.tabpage = window.tabpage
|
||||
vim.current.window = window
|
||||
|
|
@ -232,7 +314,7 @@ def Escape( msg ):
|
|||
return msg.replace( "'", "''" )
|
||||
|
||||
|
||||
def UserMessage( msg, persist=False, error=False):
|
||||
def UserMessage( msg, persist=False, error=False ):
|
||||
if persist:
|
||||
_logger.warning( 'User Msg: ' + msg )
|
||||
else:
|
||||
|
|
@ -242,11 +324,11 @@ def UserMessage( msg, persist=False, error=False):
|
|||
vim.command( 'redraw' )
|
||||
try:
|
||||
if error:
|
||||
vim.command("echohl WarningMsg")
|
||||
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( 'echohl None' ) if error else None
|
||||
vim.command( 'redraw' )
|
||||
|
||||
|
||||
|
|
@ -270,25 +352,69 @@ def SelectFromList( prompt, options ):
|
|||
if selection < 0 or selection >= len( options ):
|
||||
return None
|
||||
return options[ selection ]
|
||||
except KeyboardInterrupt:
|
||||
except ( KeyboardInterrupt, vim.error ):
|
||||
return None
|
||||
|
||||
|
||||
def AskForInput( prompt, default_value = None ):
|
||||
def AskForInput( prompt, default_value = None, completion = None ):
|
||||
if default_value is None:
|
||||
default_option = ''
|
||||
else:
|
||||
default_option = ", '{}'".format( Escape( default_value ) )
|
||||
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():
|
||||
try:
|
||||
return vim.eval( "input( '{}' {} )".format( Escape( prompt ),
|
||||
default_option ) )
|
||||
except KeyboardInterrupt:
|
||||
return ''
|
||||
return Call( 'input', *args )
|
||||
except ( KeyboardInterrupt, vim.error ):
|
||||
return None
|
||||
|
||||
|
||||
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 ):
|
||||
line = 1
|
||||
try:
|
||||
# After clearing the buffer (using buf[:] = None) there is always a single
|
||||
# empty line in the buffer object and no "is empty" method.
|
||||
|
|
@ -315,8 +441,10 @@ def AppendToBuffer( buf, line_or_lines, modified=False ):
|
|||
|
||||
|
||||
|
||||
def ClearBuffer( buf ):
|
||||
def ClearBuffer( buf, modified = False ):
|
||||
buf[ : ] = None
|
||||
if not modified:
|
||||
buf.options[ 'modified' ] = False
|
||||
|
||||
|
||||
def SetBufferContents( buf, lines, modified=False ):
|
||||
|
|
@ -324,7 +452,7 @@ def SetBufferContents( buf, lines, modified=False ):
|
|||
if not isinstance( lines, list ):
|
||||
lines = lines.splitlines()
|
||||
|
||||
buf[:] = lines
|
||||
buf[ : ] = lines
|
||||
finally:
|
||||
buf.options[ 'modified' ] = modified
|
||||
|
||||
|
|
@ -333,21 +461,96 @@ def IsCurrent( window, buf ):
|
|||
return vim.current.window == window and vim.current.window.buffer == buf
|
||||
|
||||
|
||||
def ExpandReferencesInObject( obj, mapping, user_choices ):
|
||||
def ExpandReferencesInObject( obj, mapping, calculus, user_choices ):
|
||||
if isinstance( obj, dict ):
|
||||
ExpandReferencesInDict( obj, mapping, user_choices )
|
||||
ExpandReferencesInDict( obj, mapping, calculus, 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 ] = ExpandReferencesInObject( obj[ i ], mapping, user_choices )
|
||||
j_offset = 0
|
||||
obj_copy = list( obj )
|
||||
|
||||
for i, _ in enumerate( obj_copy ):
|
||||
j = i + j_offset
|
||||
if ( isinstance( obj_copy[ i ], str ) and
|
||||
len( obj_copy[ i ] ) > 2 and
|
||||
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, user_choices )
|
||||
obj = ExpandReferencesInString( obj, mapping, calculus, user_choices )
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
def ExpandReferencesInString( orig_s, mapping, user_choices):
|
||||
# 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 )
|
||||
|
||||
|
|
@ -358,21 +561,38 @@ def ExpandReferencesInString( orig_s, mapping, user_choices):
|
|||
++bug_catcher
|
||||
|
||||
try:
|
||||
s = string.Template( s ).substitute( mapping )
|
||||
s = _Substitute( s, 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 ),
|
||||
default_value )
|
||||
user_choices[ key ] = mapping[ key ]
|
||||
_logger.debug( "Value for %s not set in %s (from %s): set to %s",
|
||||
key,
|
||||
s,
|
||||
orig_s,
|
||||
mapping[ key ] )
|
||||
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
|
||||
|
||||
mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ),
|
||||
default_value )
|
||||
|
||||
if mapping[ key ] is None:
|
||||
raise KeyboardInterrupt
|
||||
|
||||
user_choices[ key ] = mapping[ key ]
|
||||
_logger.debug( "Value for %s not set in %s (from %s): set to %s",
|
||||
key,
|
||||
s,
|
||||
orig_s,
|
||||
mapping[ key ] )
|
||||
except ValueError as e:
|
||||
UserMessage( 'Invalid $ in string {}: {}'.format( s, e ),
|
||||
persist = True )
|
||||
|
|
@ -381,31 +601,55 @@ def ExpandReferencesInString( orig_s, mapping, user_choices):
|
|||
return s
|
||||
|
||||
|
||||
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, user_choices ):
|
||||
for k in obj.keys():
|
||||
obj[ k ] = ExpandReferencesInObject( obj[ k ], mapping, user_choices )
|
||||
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, user_choices ):
|
||||
def ParseVariables( variables_list,
|
||||
mapping,
|
||||
calculus,
|
||||
user_choices ):
|
||||
new_variables = {}
|
||||
new_mapping = mapping.copy()
|
||||
|
||||
if not isinstance( variables_list, list ):
|
||||
variables_list = [ variables_list ]
|
||||
|
||||
variables: typing.Dict[ str, typing.Any ]
|
||||
for variables in variables_list:
|
||||
new_mapping.update( new_variables )
|
||||
for n, v in variables.items():
|
||||
for n, v in list( variables.items() ):
|
||||
if isinstance( v, dict ):
|
||||
if 'shell' in v:
|
||||
import subprocess
|
||||
import shlex
|
||||
|
||||
new_v = v.copy()
|
||||
# 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.update( new_v.get( 'env' ) or {} )
|
||||
|
|
@ -429,17 +673,27 @@ def ParseVariables( variables_list, mapping, user_choices ):
|
|||
else:
|
||||
new_variables[ n ] = ExpandReferencesInObject( v,
|
||||
mapping,
|
||||
calculus,
|
||||
user_choices )
|
||||
|
||||
CoerceType( new_variables, n )
|
||||
|
||||
return new_variables
|
||||
|
||||
|
||||
def DisplayBaloon( is_term, display ):
|
||||
def DisplayBalloon( is_term, display, is_hover = False ):
|
||||
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 )
|
||||
|
||||
vim.eval( "balloon_show( {0} )".format(
|
||||
json.dumps( display ) ) )
|
||||
created_win_id = int( vim.eval(
|
||||
"vimspector#internal#balloon#CreateTooltip({}, {})".format(
|
||||
int( is_hover ), json.dumps( display )
|
||||
)
|
||||
) )
|
||||
|
||||
return created_win_id
|
||||
|
||||
|
||||
def GetBufferFilepath( buf ):
|
||||
|
|
@ -467,17 +721,9 @@ def Call( vimscript_function, *args ):
|
|||
call += 'g:' + arg_name
|
||||
|
||||
call += ')'
|
||||
_logger.debug( 'Calling: {}'.format( call ) )
|
||||
return vim.eval( call )
|
||||
|
||||
|
||||
def SignDefined( name ):
|
||||
if Exists( "*sign_getdefined" ):
|
||||
return int( vim.eval( f"len( sign_getdefined( '{ Escape( name ) }' ) )" ) )
|
||||
|
||||
return False
|
||||
|
||||
|
||||
MEMO = {}
|
||||
|
||||
|
||||
|
|
@ -508,10 +754,113 @@ def SetSyntax( current_syntax, syntax, *args ):
|
|||
syntax = ''
|
||||
|
||||
if current_syntax == syntax:
|
||||
return
|
||||
return syntax
|
||||
|
||||
for win in args:
|
||||
with LetCurrentWindow( win ):
|
||||
vim.command( 'set syntax={}'.format( Escape( 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,58 +13,208 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import abc
|
||||
import vim
|
||||
import logging
|
||||
from collections import namedtuple
|
||||
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 ):
|
||||
def __init__( self, connection, variables_win, watches_win ):
|
||||
def __init__( self, variables_win, watches_win ):
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
utils.SetUpLogging( self._logger )
|
||||
|
||||
self._vars = View( variables_win, {}, self._DrawScopes )
|
||||
self._watch = View( watches_win, {}, self._DrawWatches )
|
||||
self._connection = connection
|
||||
self._connection = None
|
||||
self._current_syntax = ''
|
||||
self._server_capabilities = None
|
||||
|
||||
# Allows us to hit <CR> to expand/collapse variables
|
||||
with utils.LetCurrentWindow( self._vars.win ):
|
||||
vim.command(
|
||||
'nnoremap <buffer> <CR> :call vimspector#ExpandVariable()<CR>' )
|
||||
self._variable_eval: Scope = None
|
||||
self._variable_eval_view: View = None
|
||||
|
||||
# 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 = []
|
||||
mappings = settings.Dict( 'mappings' )[ 'variables' ]
|
||||
|
||||
# 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 = []
|
||||
# Set up the "Variables" buffer in the variables_win
|
||||
self._scopes: typing.List[ Scope ] = []
|
||||
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 )
|
||||
|
||||
# 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,
|
||||
# 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 )
|
||||
utils.SetUpPromptBuffer( self._watch.buf,
|
||||
'vimspector.Watches',
|
||||
'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_term = int( vim.eval( "has( 'balloon_eval_term' )" ) )
|
||||
|
||||
|
|
@ -74,7 +224,9 @@ class VariablesView( object ):
|
|||
'balloonexpr': vim.options[ 'balloonexpr' ],
|
||||
'balloondelay': vim.options[ 'balloondelay' ],
|
||||
}
|
||||
vim.options[ 'balloonexpr' ] = 'vimspector#internal#balloon#BalloonExpr()'
|
||||
vim.options[ 'balloonexpr' ] = ( "vimspector#internal#"
|
||||
"balloon#HoverTooltip()" )
|
||||
|
||||
vim.options[ 'balloondelay' ] = 250
|
||||
|
||||
if has_balloon:
|
||||
|
|
@ -88,53 +240,76 @@ class VariablesView( object ):
|
|||
self._is_term = not bool( int( vim.eval( "has( 'gui_running' )" ) ) )
|
||||
|
||||
def Clear( self ):
|
||||
with utils.ModifiableScratchBuffer( self._vars.win.buffer ):
|
||||
utils.ClearBuffer( self._vars.win.buffer )
|
||||
with utils.ModifiableScratchBuffer( self._watch.win.buffer ):
|
||||
utils.ClearBuffer( self._watch.win.buffer )
|
||||
with utils.ModifiableScratchBuffer( self._vars.buf ):
|
||||
utils.ClearBuffer( self._vars.buf )
|
||||
with utils.ModifiableScratchBuffer( self._watch.buf ):
|
||||
utils.ClearBuffer( self._watch.buf )
|
||||
self.ClearTooltip()
|
||||
self._current_syntax = ''
|
||||
|
||||
def ConnectionUp( self, connection ):
|
||||
self._connection = connection
|
||||
|
||||
def SetServerCapabilities( self, capabilities ):
|
||||
self._server_capabilities = capabilities
|
||||
|
||||
def ConnectionClosed( self ):
|
||||
self.Clear()
|
||||
self._connection = None
|
||||
self._server_capabilities = None
|
||||
|
||||
def Reset( self ):
|
||||
self._server_capabilities = None
|
||||
|
||||
for k, v in self._oldoptions.items():
|
||||
vim.options[ k ] = v
|
||||
|
||||
vim.command( 'bdelete! ' + str( self._watch.win.buffer.number ) )
|
||||
vim.command( 'bdelete! ' + str( self._vars.win.buffer.number ) )
|
||||
utils.CleanUpHiddenBuffer( self._vars.buf )
|
||||
utils.CleanUpHiddenBuffer( self._watch.buf )
|
||||
self.ClearTooltip()
|
||||
|
||||
|
||||
def LoadScopes( self, frame ):
|
||||
def scopes_consumer( message ):
|
||||
old_scopes = self._scopes
|
||||
self._scopes = []
|
||||
new_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 ( i < len( old_scopes ) and
|
||||
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
|
||||
if not found:
|
||||
scope = Scope( scope_body )
|
||||
else:
|
||||
scope[ '_expanded' ] = False
|
||||
scope.Update( scope_body )
|
||||
|
||||
self._scopes.append( scope )
|
||||
if scope[ '_expanded' ]:
|
||||
new_scopes.append( scope )
|
||||
|
||||
# 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._DrawScopes,
|
||||
scope ), {
|
||||
'command': 'variables',
|
||||
'arguments': {
|
||||
'variablesReference': scope[ 'variablesReference' ]
|
||||
'variablesReference': scope.VariablesReference(),
|
||||
},
|
||||
} )
|
||||
|
||||
self._scopes = new_scopes
|
||||
self._DrawScopes()
|
||||
|
||||
self._connection.DoRequest( scopes_consumer, {
|
||||
|
|
@ -144,252 +319,413 @@ class VariablesView( object ):
|
|||
},
|
||||
} )
|
||||
|
||||
def AddWatch( self, frame, expression ):
|
||||
watch = {
|
||||
'expression': expression,
|
||||
'context': 'watch',
|
||||
}
|
||||
if frame:
|
||||
watch[ 'frameId' ] = frame[ 'id' ]
|
||||
def _DrawBalloonEval( self ):
|
||||
watch = self._variable_eval
|
||||
view = self._variable_eval_view
|
||||
|
||||
self._watches.append( watch )
|
||||
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 ):
|
||||
self._watches.append( Watch.New( frame, expression, 'watch' ) )
|
||||
self.EvaluateWatches()
|
||||
|
||||
def DeleteWatch( self ):
|
||||
if vim.current.window != self._watch.win:
|
||||
utils.UserMessage( 'Not a watch window' )
|
||||
if vim.current.buffer != self._watch.buf:
|
||||
utils.UserMessage( 'Not a watch buffer' )
|
||||
return
|
||||
|
||||
current_line = vim.current.window.cursor[ 0 ]
|
||||
|
||||
best_index = -1
|
||||
for index, watch in enumerate( self._watches ):
|
||||
if '_line' in watch and watch[ '_line' ] == current_line:
|
||||
del self._watches[ index ]
|
||||
utils.UserMessage( 'Deleted' )
|
||||
self._DrawWatches()
|
||||
return
|
||||
if ( watch.line is not None
|
||||
and watch.line <= current_line
|
||||
and watch.line > best_index ):
|
||||
best_index = index
|
||||
|
||||
if best_index >= 0:
|
||||
del self._watches[ best_index ]
|
||||
utils.UserMessage( 'Deleted' )
|
||||
self._DrawWatches()
|
||||
return
|
||||
|
||||
utils.UserMessage( 'No watch found' )
|
||||
|
||||
def EvaluateWatches( self ):
|
||||
for watch in self._watches:
|
||||
self._connection.DoRequest( partial( self._UpdateWatchExpression,
|
||||
watch ), {
|
||||
'command': 'evaluate',
|
||||
'arguments': watch,
|
||||
} )
|
||||
self._connection.DoRequest(
|
||||
partial( self._UpdateWatchExpression, watch ),
|
||||
{
|
||||
'command': 'evaluate',
|
||||
'arguments': watch.expression,
|
||||
},
|
||||
failure_handler = lambda reason, msg, watch=watch:
|
||||
self._WatchExpressionFailed( reason, watch ) )
|
||||
|
||||
def _UpdateWatchExpression( self, watch, message ):
|
||||
old_result = None
|
||||
if '_result' in watch:
|
||||
old_result = watch[ '_result' ]
|
||||
def _UpdateWatchExpression( self, watch: Watch, message: dict ):
|
||||
if watch.result is not None:
|
||||
watch.result.Update( message[ 'body' ] )
|
||||
else:
|
||||
watch.result = WatchResult( message[ 'body' ] )
|
||||
|
||||
result = message[ 'body' ]
|
||||
watch[ '_result' ] = result
|
||||
|
||||
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 ) ):
|
||||
if ( watch.result.IsExpandable() and
|
||||
watch.result.IsExpanded() ):
|
||||
self._connection.DoRequest( partial( self._ConsumeVariables,
|
||||
self._watch.draw,
|
||||
result ), {
|
||||
watch.result ), {
|
||||
'command': 'variables',
|
||||
'arguments': {
|
||||
'variablesReference': result[ 'variablesReference' ]
|
||||
'variablesReference': watch.result.VariablesReference(),
|
||||
},
|
||||
} )
|
||||
|
||||
self._DrawWatches()
|
||||
|
||||
def ExpandVariable( self ):
|
||||
if vim.current.window == self._vars.win:
|
||||
def _WatchExpressionFailed( self, reason: str, watch: Watch ):
|
||||
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
|
||||
elif vim.current.window == self._watch.win:
|
||||
elif buf == self._watch.buf:
|
||||
view = self._watch
|
||||
elif ( self._variable_eval_view is not None
|
||||
and buf == self._variable_eval_view.buf ):
|
||||
view = self._variable_eval_view
|
||||
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
|
||||
|
||||
current_line = vim.current.window.cursor[ 0 ]
|
||||
if current_line not in view.lines:
|
||||
return
|
||||
|
||||
variable = view.lines[ current_line ]
|
||||
|
||||
if '_variables' in variable:
|
||||
if variable.IsExpanded():
|
||||
# Collapse
|
||||
del variable[ '_variables' ]
|
||||
variable[ '_expanded' ] = False
|
||||
variable.expanded = Expandable.COLLAPSED_BY_USER
|
||||
view.draw()
|
||||
return
|
||||
|
||||
if variable.get( 'variablesReference', 0 ) <= 0:
|
||||
if not variable.IsExpandable():
|
||||
return
|
||||
|
||||
variable[ '_expanded' ] = True
|
||||
variable.expanded = Expandable.EXPANDED_BY_USER
|
||||
self._connection.DoRequest( partial( self._ConsumeVariables,
|
||||
view.draw,
|
||||
variable ), {
|
||||
'command': 'variables',
|
||||
'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:
|
||||
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(
|
||||
view.win.buffer,
|
||||
'{indent}{icon} {name} ({type_}): {value}'.format(
|
||||
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.buf,
|
||||
text.split( '\n' )
|
||||
)
|
||||
|
||||
view.lines[ line ] = variable
|
||||
|
||||
if '_variables' in variable:
|
||||
self._DrawVariables( view, variable[ '_variables' ], indent + 2 )
|
||||
if variable.ShouldDrawDrillDown():
|
||||
self._DrawVariables( view, variable.variables, indent + 2, is_short )
|
||||
|
||||
def _DrawScopes( self ):
|
||||
# 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.
|
||||
# However it is really inefficient, and makes it so that expanded results
|
||||
# are collapsed on every step.
|
||||
# However it is pretty inefficient.
|
||||
self._vars.lines.clear()
|
||||
with utils.RestoreCursorPosition():
|
||||
with utils.ModifiableScratchBuffer( self._vars.win.buffer ):
|
||||
utils.ClearBuffer( self._vars.win.buffer )
|
||||
with utils.ModifiableScratchBuffer( self._vars.buf ):
|
||||
utils.ClearBuffer( self._vars.buf )
|
||||
for scope in self._scopes:
|
||||
self._DrawScope( 0, scope )
|
||||
|
||||
def _DrawWatches( self ):
|
||||
# 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.
|
||||
# However it is really inefficient, and makes it so that expanded results
|
||||
# are collapsed on every step.
|
||||
# However it is pretty inefficient.
|
||||
self._watch.lines.clear()
|
||||
with utils.RestoreCursorPosition():
|
||||
with utils.ModifiableScratchBuffer( self._watch.win.buffer ):
|
||||
utils.ClearBuffer( self._watch.win.buffer )
|
||||
utils.AppendToBuffer( self._watch.win.buffer, 'Watches: ----' )
|
||||
with utils.ModifiableScratchBuffer( self._watch.buf ):
|
||||
utils.ClearBuffer( self._watch.buf )
|
||||
utils.AppendToBuffer( self._watch.buf, 'Watches: ----' )
|
||||
for watch in self._watches:
|
||||
line = utils.AppendToBuffer( self._watch.win.buffer,
|
||||
'Expression: ' + watch[ 'expression' ] )
|
||||
watch[ '_line' ] = line
|
||||
self._DrawWatchResult( 2, watch )
|
||||
line = utils.AppendToBuffer( self._watch.buf,
|
||||
'Expression: '
|
||||
+ watch.expression[ 'expression' ] )
|
||||
watch.line = line
|
||||
self._DrawWatchResult( self._watch, 2, watch )
|
||||
|
||||
def _DrawScope( self, indent, scope ):
|
||||
icon = '+' if ( scope.get( 'variablesReference', 0 ) > 0 and
|
||||
'_variables' not in scope ) else '-'
|
||||
icon = '+' if scope.IsExpandable() and not scope.IsExpanded() else '-'
|
||||
|
||||
line = utils.AppendToBuffer( self._vars.win.buffer,
|
||||
'{0}{1} Scope: {2}'.format( ' ' * indent,
|
||||
icon,
|
||||
scope[ 'name' ] ) )
|
||||
line = utils.AppendToBuffer( self._vars.buf,
|
||||
'{0}{1} Scope: {2}'.format(
|
||||
' ' * indent,
|
||||
icon,
|
||||
scope.scope[ 'name' ] ) )
|
||||
self._vars.lines[ line ] = scope
|
||||
|
||||
if '_variables' in scope:
|
||||
if scope.ShouldDrawDrillDown():
|
||||
indent += 2
|
||||
self._DrawVariables( self._vars, scope[ '_variables' ], indent )
|
||||
self._DrawVariables( self._vars, scope.variables, indent )
|
||||
|
||||
def _DrawWatchResult( self, indent, watch ):
|
||||
if '_result' not in watch:
|
||||
def _DrawWatchResult( self, view, indent, watch, is_short = False ):
|
||||
if not watch.result:
|
||||
return
|
||||
|
||||
result = watch[ '_result' ]
|
||||
assert is_short or indent > 0
|
||||
|
||||
icon = '+' if ( result.get( 'variablesReference', 0 ) > 0 and
|
||||
'_variables' not in result ) else '-'
|
||||
if is_short:
|
||||
# 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' ]
|
||||
if result_str is None:
|
||||
result_str = 'null'
|
||||
line = '{indent}{marker}{icon}{leader}{result}'.format(
|
||||
# We borrow 1 space of indent to draw the change marker
|
||||
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( self._watch.win.buffer, line.split( '\n' ) )
|
||||
self._watch.lines[ line ] = result
|
||||
line = utils.AppendToBuffer( view.buf, line.split( '\n' ) )
|
||||
view.lines[ line ] = watch.result
|
||||
|
||||
if '_variables' in result:
|
||||
indent = 4
|
||||
self._DrawVariables( self._watch, result[ '_variables' ], indent )
|
||||
if watch.result.ShouldDrawDrillDown():
|
||||
self._DrawVariables( view, watch.result.variables, indent + 2, is_short )
|
||||
|
||||
def _ConsumeVariables( self, draw, parent, message ):
|
||||
for variable in message[ 'body' ][ 'variables' ]:
|
||||
if '_variables' not in parent:
|
||||
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' ]
|
||||
},
|
||||
} )
|
||||
new_variables = []
|
||||
for variable_body in message[ 'body' ][ 'variables' ]:
|
||||
if parent.variables is None:
|
||||
parent.variables = []
|
||||
|
||||
# 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
|
||||
if not found:
|
||||
variable = Variable( parent, variable_body )
|
||||
else:
|
||||
variable.Update( variable_body )
|
||||
|
||||
if '_old_variables' in parent:
|
||||
del parent[ '_old_variables' ]
|
||||
new_variables.append( variable )
|
||||
|
||||
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()
|
||||
|
||||
def ShowBalloon( self, frame, expression ):
|
||||
"""Callback to display variable under cursor `:h ballonexpr`"""
|
||||
if not self._connection:
|
||||
return ''
|
||||
|
||||
def handler( message ):
|
||||
# TODO: this result count be expandable, but we have no way to allow the
|
||||
# user to interact with the balloon to expand it.
|
||||
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 )
|
||||
|
||||
# Send async request
|
||||
self._connection.DoRequest( handler, {
|
||||
'command': 'evaluate',
|
||||
'arguments': {
|
||||
'expression': expression,
|
||||
'frameId': frame[ 'id' ],
|
||||
'context': 'hover',
|
||||
}
|
||||
}, failure_handler )
|
||||
|
||||
# Return working (meanwhile)
|
||||
return '...'
|
||||
|
||||
|
||||
def SetSyntax( self, syntax ):
|
||||
# TODO: Switch to View.syntax
|
||||
self._current_syntax = utils.SetSyntax( self._current_syntax,
|
||||
syntax,
|
||||
self._vars.win,
|
||||
self._watch.win )
|
||||
|
||||
self._vars.buf,
|
||||
self._watch.buf )
|
||||
# vim: sw=2
|
||||
|
|
|
|||
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)
|
||||
171
run_tests
171
run_tests
|
|
@ -1,25 +1,126 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if [ "$1" == "--help" ]; then
|
||||
echo "$(basename $0) <optional list of tests in form file:func>"
|
||||
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\(\)"
|
||||
exit 0
|
||||
fi
|
||||
SetBaseDir() {
|
||||
BASEDIR=$1
|
||||
if [[ ! $BASEDIR = /* ]]; then
|
||||
# Relative
|
||||
BASEDIR=$(pwd)/${BASEDIR}
|
||||
fi
|
||||
}
|
||||
|
||||
RUN_VIM="vim --clean --not-a-term"
|
||||
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"
|
||||
BASEDIR_CMD='py3 pass'
|
||||
|
||||
NODE_VERSION_MAJ=$(node --version \
|
||||
| awk 'BEGIN { FS="." } { print substr( $1, 2 ) }')
|
||||
if [ ${NODE_VERSION_MAJ} -gt "10" ]; then
|
||||
echo "ERROR: Tests must be run against node 10 or earlier because python." >&2
|
||||
echo "You have $(node --version)" >&2
|
||||
exit 1
|
||||
# 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 which lldb >/dev/null 2>&1; then
|
||||
export VIMSPECTOR_MIMODE=lldb
|
||||
|
|
@ -31,12 +132,21 @@ if [ -z "$VIMSPECTOR_MIMODE" ]; then
|
|||
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..."
|
||||
set -e
|
||||
pushd tests/testdata/cpp/simple
|
||||
make clean simple
|
||||
make clean all
|
||||
popd
|
||||
pushd support/test/csharp
|
||||
dotnet build
|
||||
popd
|
||||
set +e
|
||||
echo "%DONE - built test programs"
|
||||
|
|
@ -60,9 +170,11 @@ for t in ${TESTS}; do
|
|||
# split on : into fileName and testName
|
||||
IFS=: read -s t T <<< "$t"
|
||||
|
||||
TESTLOGDIR=$(pwd)/logs/$t
|
||||
TESTLOGDIR=${BASEDIR}/tests/logs/$t
|
||||
|
||||
if ${RUN_TEST} --cmd 'au SwapExists * let v:swapchoice = "e"' $t $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"
|
||||
else
|
||||
|
|
@ -73,10 +185,22 @@ for t in ${TESTS}; do
|
|||
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" ]; then
|
||||
if [ "$VIMSPECTOR_TEST_STDOUT" = "all" ]; then
|
||||
echo ""
|
||||
echo ""
|
||||
echo "*** START: $l ***"
|
||||
|
|
@ -90,11 +214,14 @@ for t in ${TESTS}; do
|
|||
rm -f $t.res
|
||||
done
|
||||
|
||||
# close out_fd if it's not stdout/stderr/
|
||||
(( out_fd > 2 )) && exec 3>&-
|
||||
|
||||
|
||||
echo "Done running tests"
|
||||
popd > /dev/null
|
||||
|
||||
echo ""
|
||||
echo "All done."
|
||||
|
||||
echo "All done. Exit with ${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'
|
||||
|
||||
2
support/site/jekyll/.gitignore
vendored
2
support/site/jekyll/.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
|||
vendor/
|
||||
.bundle
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
source 'https://rubygems.org'
|
||||
gem 'jekyll', '3.8.5'
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
addressable (2.7.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
colorator (1.1.0)
|
||||
concurrent-ruby (1.1.5)
|
||||
em-websocket (0.5.1)
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
eventmachine (1.2.7)
|
||||
ffi (1.11.1)
|
||||
forwardable-extended (2.6.0)
|
||||
http_parser.rb (0.6.0)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jekyll (3.8.5)
|
||||
addressable (~> 2.4)
|
||||
colorator (~> 1.0)
|
||||
em-websocket (~> 0.5)
|
||||
i18n (~> 0.7)
|
||||
jekyll-sass-converter (~> 1.0)
|
||||
jekyll-watch (~> 2.0)
|
||||
kramdown (~> 1.14)
|
||||
liquid (~> 4.0)
|
||||
mercenary (~> 0.3.3)
|
||||
pathutil (~> 0.9)
|
||||
rouge (>= 1.7, < 4)
|
||||
safe_yaml (~> 1.0)
|
||||
jekyll-sass-converter (1.5.2)
|
||||
sass (~> 3.4)
|
||||
jekyll-watch (2.2.1)
|
||||
listen (~> 3.0)
|
||||
kramdown (1.17.0)
|
||||
liquid (4.0.3)
|
||||
listen (3.2.0)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
mercenary (0.3.6)
|
||||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (4.0.1)
|
||||
rb-fsevent (0.10.3)
|
||||
rb-inotify (0.10.0)
|
||||
ffi (~> 1.0)
|
||||
rouge (3.12.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)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
jekyll (= 3.8.5)
|
||||
|
||||
BUNDLED WITH
|
||||
2.0.2
|
||||
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;
|
||||
};
|
||||
|
||||
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": {
|
||||
"simple_c_program - Launch": {
|
||||
"adapter": "cppdbg",
|
||||
"CodeLLDB": {
|
||||
"adapter": "CodeLLDB",
|
||||
"configuration": {
|
||||
"name": "ms Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/test",
|
||||
"args": [],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"environment": [],
|
||||
"MIMode": "lldb",
|
||||
"stopAtEntry": true
|
||||
}
|
||||
},
|
||||
"simple_c_program - Attach": {
|
||||
"adapter": "cppdbg",
|
||||
"lldb-vscode": {
|
||||
"adapter": "lldb-vscode",
|
||||
"configuration": {
|
||||
"name": "(lldb) Attach",
|
||||
"type": "cppdbg",
|
||||
"request": "attach",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/test",
|
||||
"stopAtEntry": true
|
||||
}
|
||||
},
|
||||
"cpptools": {
|
||||
"adapter": "vscode-cpptools",
|
||||
"configuration": {
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/test",
|
||||
"stopOnEntry": true,
|
||||
"MIMode": "lldb"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
def Settings( **kwargs ):
|
||||
return {
|
||||
'flags': [ '-x', 'c++', '-Wall', '-Wextra' ]
|
||||
'flags': [ '-x', 'c++', '-Wall', '-Wextra', '-std=c++17' ]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
namespace Test
|
||||
|
|
@ -33,6 +35,8 @@ int main ( int argc, char ** argv )
|
|||
{
|
||||
int x{ 10 };
|
||||
|
||||
printf( "HOME: %s\n", getenv( "HOME" ) );
|
||||
|
||||
Test::TestStruct t{ true, {99} };
|
||||
foo( t );
|
||||
}
|
||||
|
|
|
|||
1
support/test/csharp/.gitignore
vendored
1
support/test/csharp/.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
bin/
|
||||
obj/Debug
|
||||
obj/
|
||||
|
|
|
|||
|
|
@ -1,21 +1,57 @@
|
|||
{
|
||||
"configurations": {
|
||||
"launch - netcoredbg": {
|
||||
"adapter": "netcoredbg",
|
||||
"configuration": {
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/bin/Debug/netcoreapp2.2/csharp.dll",
|
||||
"args": [],
|
||||
"stopAtEntry": true
|
||||
}
|
||||
"adapters": {
|
||||
"netcoredbg-debuglog": {
|
||||
"attach": {
|
||||
"pidProperty": "processId",
|
||||
"pidSelect": "ask"
|
||||
},
|
||||
"launch - mono": {
|
||||
"adapter": "vscode-mono-debug",
|
||||
"configuration": {
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/Program.exe"
|
||||
}
|
||||
"command": [
|
||||
"${gadgetDir}/netcoredbg/netcoredbg",
|
||||
"--interpreter=vscode",
|
||||
"--engineLogging=${workspaceRoot}/netcoredbg.engine.log",
|
||||
"--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
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Hello World!");
|
||||
}
|
||||
class Program
|
||||
{
|
||||
string toaster = "Making round of toast";
|
||||
static int max_bread = 100;
|
||||
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>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||
# Visual Studio 15
|
||||
VisualStudioVersion = 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
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -15,4 +17,18 @@ Global
|
|||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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": {
|
||||
"run": {
|
||||
"adapter": "vscode-go",
|
||||
"default": true,
|
||||
"configuration": {
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/hello-world.go",
|
||||
"mode": "debug",
|
||||
"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": {
|
||||
"Java Launch": {
|
||||
"adapter": "java-debug-server",
|
||||
"adapter": "vscode-java",
|
||||
"configuration": {
|
||||
"request": "launch",
|
||||
"mainClass": "com.vimspector.test.TestApplication",
|
||||
|
|
@ -15,7 +9,23 @@
|
|||
"classPaths": [ "${workspaceRoot}/target/classes" ],
|
||||
"args": "hello world!",
|
||||
"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": {
|
||||
|
|
|
|||
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>
|
||||
<version>1</version>
|
||||
<properties>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
</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;
|
||||
}
|
||||
|
||||
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 ) {
|
||||
int numEntries = 0;
|
||||
for ( String s : args ) {
|
||||
|
|
@ -24,6 +40,8 @@ public class TestApplication {
|
|||
++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"
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package vimspector.test
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println("Hello World!")
|
||||
}
|
||||
18
support/test/lua/love-headless/.vimspector.json
Normal file
18
support/test/lua/love-headless/.vimspector.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#",
|
||||
"configurations": {
|
||||
"love": {
|
||||
"adapter": "lua-local",
|
||||
"configuration": {
|
||||
"request": "launch",
|
||||
"type": "lua-local",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"program": {
|
||||
"command": "love"
|
||||
},
|
||||
"args": ["${workspaceFolder}"],
|
||||
"stopOnEntry": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
support/test/lua/love-headless/conf.lua
Normal file
12
support/test/lua/love-headless/conf.lua
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
function love.conf(t)
|
||||
t.modules.audio = false
|
||||
t.modules.font = false
|
||||
t.modules.graphics = false
|
||||
t.modules.image = false
|
||||
t.modules.mouse = false
|
||||
t.modules.physics = false
|
||||
t.modules.sound = false
|
||||
t.modules.touch = false
|
||||
t.modules.video = false
|
||||
t.modules.window = false
|
||||
end
|
||||
10
support/test/lua/love-headless/main.lua
Normal file
10
support/test/lua/love-headless/main.lua
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
if pcall(require, 'lldebugger') then
|
||||
require('lldebugger').start()
|
||||
end
|
||||
|
||||
local time = 0
|
||||
|
||||
function love.update(dt)
|
||||
time = time + dt
|
||||
love.event.quit()
|
||||
end
|
||||
18
support/test/lua/love/.vimspector.json
Normal file
18
support/test/lua/love/.vimspector.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#",
|
||||
"configurations": {
|
||||
"love": {
|
||||
"adapter": "lua-local",
|
||||
"configuration": {
|
||||
"request": "launch",
|
||||
"type": "lua-local",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"program": {
|
||||
"command": "love"
|
||||
},
|
||||
"args": ["${workspaceFolder}"],
|
||||
"stopOnEntry": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
support/test/lua/love/main.lua
Normal file
21
support/test/lua/love/main.lua
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
if pcall(require, 'lldebugger') then
|
||||
require('lldebugger').start()
|
||||
end
|
||||
|
||||
local rect = {0, 0, 64, 64}
|
||||
|
||||
|
||||
function love.update(dt)
|
||||
rect[1] = rect[1] + 10 * dt
|
||||
rect[2] = rect[2] + 10 * dt
|
||||
end
|
||||
|
||||
|
||||
function love.draw()
|
||||
love.graphics.rectangle('fill', rect[1], rect[2], rect[3], rect[4])
|
||||
end
|
||||
|
||||
|
||||
function love.keypressed()
|
||||
love.event.quit()
|
||||
end
|
||||
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