Compare commits

...
Sign in to create a new pull request.

798 commits

Author SHA1 Message Date
Joey Yakimowich-Payne
7c12519b9d Modify for mac m1 2021-09-10 10:30:44 -06:00
mergify[bot]
2bb8561eb6
Merge pull request #452 from seezer/readme-gdb-prettyprinter
Readme: pretty printing with vscode-cpptools / gdb
2021-09-09 16:19:52 +00:00
mergify[bot]
a868102b5e
Merge branch 'master' into readme-gdb-prettyprinter 2021-09-09 16:05:28 +00:00
Sebastian Goth
dc862fe565 Readme: pretty printing with vscode-cpptools / gdb 2021-09-09 16:59:01 +02:00
Ben Jackson
b4bcfca932
Merge pull request #450 from puremourning/gha-ubuntu-18.04
Update to ubuntu 18.04 as 16.04 is deprecated
2021-09-08 22:49:22 +01:00
Ben Jackson
3df0602a69
Merge branch 'master' into gha-ubuntu-18.04 2021-09-08 22:30:49 +01:00
mergify[bot]
1c2dda4a6a
Merge pull request #449 from seezer/vscode-cpptools-1.6.0
Update vscode-cpptools from 0.27.0 to 1.6.0
2021-09-08 21:30:08 +00:00
Ben Jackson
db5ed8e802 Update to ubuntu 18.04 as 16.04 is deprecated 2021-09-08 22:20:33 +01:00
Sebastian Goth
17ca1522f8 Use correct spelling of MIMode in tests 2021-09-08 23:16:47 +02:00
Sebastian Goth
561a5b9aa2 Update variables tests to expect register scope
vscode-cpptools 1.6.0 now reports an additional scope 'Registers' after
'Locals'.
2021-09-08 21:58:32 +02:00
Sebastian Goth
46cfdc678d Update vscode-cpptools from 0.27.0 to 1.6.0 2021-09-08 21:58:32 +02:00
Ben Jackson
51d78fce5f
Note on using legacy vscode-dap 2021-09-07 17:00:04 +01:00
mergify[bot]
14f34ea6d1
Merge pull request #442 from roachsinai/master
Fix error: E806: using Float as a String.
2021-08-20 18:09:07 +00:00
roachsinai
a720d0e1d5 Fix error: E806: using Float as a String. 2021-08-21 00:57:27 +08:00
Ben Jackson
7c7e3f9c3f Add config for bash to tests 2021-08-20 11:17:05 +01:00
Ben Jackson
57ce099280 SHow the output in the console, as this is where codelldb puts it 2021-08-03 17:30:42 +01:00
mergify[bot]
27eb464b8e
Merge pull request #436 from puremourning/update-codelldb
Update codelldb
2021-08-02 16:37:04 +00:00
Ben Jackson
f1e2c12e5b Update codelldb
Add a way to checksum downloads and a little guide to updating gadgets
2021-08-02 17:23:32 +01:00
mergify[bot]
26cd7c5c7e
Merge pull request #435 from przepompownia/upgrade-vscode-php-debug-1.17.0
Upgrade vscode-php-debug to 1.17.0
2021-08-02 14:51:01 +00:00
przepompownia
59c9cd10ab Upgrade vscode-php-debug to 1.17.0 2021-08-02 16:11:04 +02:00
mergify[bot]
9c806d2a01
Merge pull request #429 from puremourning/dependabot/bundler/docs/addressable-2.8.0
Bump addressable from 2.7.0 to 2.8.0 in /docs
2021-07-13 15:07:15 +00:00
dependabot[bot]
3af97f1928
Bump addressable from 2.7.0 to 2.8.0 in /docs
Bumps [addressable](https://github.com/sporkmonger/addressable) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/sporkmonger/addressable/releases)
- [Changelog](https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sporkmonger/addressable/compare/addressable-2.7.0...addressable-2.8.0)

---
updated-dependencies:
- dependency-name: addressable
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-13 05:43:17 +00:00
mergify[bot]
da39c4955c
Merge pull request #422 from przepompownia/upgrade-vscode-php-debug-1.16.1
Upgrade vscode-php-debug to 1.16.1
2021-07-04 21:17:05 +00:00
przepompownia
21ebb22fd4 Upgrade vscode-php-debug to 1.16.1 2021-07-04 23:00:32 +02:00
mergify[bot]
aa0cddc0da
Merge pull request #420 from simondrake/master
Add a runnable Go example
2021-06-24 16:05:30 +00:00
Simon Drake
5075f3a11a Add a runnable Go example 2021-06-24 15:12:20 +01:00
mergify[bot]
bab81953d7
Merge pull request #414 from BaskovicP/patch-1
FIx typo in configuration.md
2021-06-09 18:24:31 +00:00
Paulo
0500e41429
FIx typo in configuration.md
A small typo that wastes time for people that copy and modify the config file
2021-06-09 19:24:37 +02:00
mergify[bot]
1cbb400d42
Merge pull request #413 from przepompownia/vscode-php-debug-1.16.0
Upgrade vscode-php-debug to 1.16.0
2021-06-07 12:27:55 +00:00
przepompownia
5ea1f0d9d4 Upgrade vscode-php-debug to 1.16.0 2021-06-07 14:10:44 +02:00
Ben Jackson
a51b8b23c9 Fix traceback if the current frame isn't set 2021-05-25 14:53:29 +01:00
mergify[bot]
99c0c4f763
Merge pull request #409 from puremourning/dependabot/bundler/docs/nokogiri-1.11.5
Bump nokogiri from 1.11.3 to 1.11.5 in /docs
2021-05-20 11:10:01 +00:00
dependabot[bot]
daa8865fea
Bump nokogiri from 1.11.3 to 1.11.5 in /docs
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.11.3 to 1.11.5.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.11.3...v1.11.5)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-20 10:56:04 +00:00
mergify[bot]
f4d756fe86
Merge pull request #406 from puremourning/debuginfo
Add basic VimspectorDebugInfo command
2021-05-18 19:13:33 +00:00
Ben Jackson
d43904eb57 Add basic VimspectorDebugInfo command 2021-05-15 17:55:01 +01:00
Ben Jackson
aacd62f09f Rename AssertMatchist -> AssertMatchList 2021-05-15 16:06:16 +01:00
Ben Jackson
0e9497ce8f Add cpptools to cpp test proj 2021-05-15 15:55:17 +01:00
mergify[bot]
2708e8e6ec
Merge pull request #398 from przepompownia/php-debug-1.15.1
Upgrade php-debug to v1.15.1
2021-05-11 19:16:29 +00:00
Ben Jackson
adf6163653
Merge branch 'master' into php-debug-1.15.1 2021-05-11 20:05:48 +01:00
mergify[bot]
0af9d70b0d
Merge pull request #394 from puremourning/highlight-current-frame
Add cursorline highlight for current frame
2021-05-11 18:56:14 +00:00
Ben Jackson
9113dbb698 stack_trace: Add cursorline highlight and sign for current frame 2021-05-11 19:44:42 +01:00
przepompownia
08679d1c3e Upgrade php-debug to v1.15.1 2021-05-11 00:55:35 +02:00
Ben Jackson
4e04a862cb Add note about existing github issues 2021-05-03 15:02:50 +01:00
Ben Jackson
a7e8e93920 Allow manual running of locker 2021-05-03 14:09:56 +01:00
Ben Jackson
2b84439413 Lock closed issues after 60 days 2021-05-03 14:09:14 +01:00
Ben Jackson
0c88cc8bad Fix traceback when opening popup in neovim with log visible
* We enter the popup window
* some log data is received, so we update the log buffer and jump to the
  log window to scroll it
* meanwhile, the WinLeave auto command on the popup window fires and
  closes the popup window
* Having scrolled the log window, we try to jump back to the popup
  window, which by now has been closed.

Fixes #390
2021-04-29 10:02:23 +01:00
mergify[bot]
a47d0b921c
Merge pull request #387 from lf-/typos
Fix some typos in the readme
2021-04-22 12:19:58 +00:00
Jade
026ac22280 Fix some typos in the readme 2021-04-22 03:33:02 -07:00
Ben Jackson
297c0bea56 Ensure linux tests pass on PR 2021-04-20 20:52:53 +01:00
Ben Jackson
a9a26a5a60
Merge pull request #386 from puremourning/csharp-test
Add tests for c# using netcoredbg
2021-04-15 23:23:19 +01:00
Ben Jackson
1e25313cb5 Switch to xcode 11 which apparently works with coreclr debugging
https://github.com/dotnet/runtime/issues/42311#issuecomment-700718025
2021-04-15 23:04:31 +01:00
Ben Jackson
bc15c94513 Retire mono debug - never worked and seems abandoned, and vscode-python as it is serious legacy now 2021-04-15 21:39:38 +01:00
Ben Jackson
f389d65a24 Add a test for csharp, run in CI 2021-04-15 21:09:09 +01:00
Ben Jackson
b4195eee93 Upgrade to netcoredbg v1.2.0-782
Also upgrade the test project to LTS 3.1 dotnet framework, as MS is
making it very difficult to get framework 2.2 anymore.

Also remove some object files which apparently were included in the
repo.
2021-04-15 18:02:55 +01:00
Ben Jackson
6ad9101cf2
Add FAQ about json files in each project. 2021-04-15 15:02:46 +01:00
Ben Jackson
a41db89523 Fix get_configurations test 2021-04-13 18:01:10 +01:00
Ben Jackson
dd88e051a4 Attempt to recover from broken messages 2021-04-13 17:35:04 +01:00
Ben Jackson
fa92c2a8d5 Add a mode for debugging the extremely flaky netcoredbg 2021-04-11 19:23:47 +01:00
mergify[bot]
6709b45c77
Merge pull request #382 from aqez/master
Update sha256sum of netcoredbg for macos
2021-04-09 16:45:58 +00:00
Tony Dwire
278fc3cd8c Update sha256sum of netcoredbg 2021-04-09 11:29:17 -05:00
Ben Jackson
13a5a1b947 Fix traceback when +python3 is not availble 2021-04-09 16:54:49 +01:00
Ben Jackson
7d83419a4f Update docs bundles 2021-04-07 22:45:58 +01:00
mergify[bot]
7b3016aa90
Merge pull request #381 from aqez/master
Updated netcoredbg to 1.2.0-761 to enable mac support of async/await
2021-04-07 17:33:27 +00:00
Tony Dwire
d70d51a614 Updated netcoredbg to 1.2.0-761 to enable mac support of async/await 2021-04-07 11:50:26 -05:00
mergify[bot]
caeb6610ed
Merge pull request #379 from puuuuh/master
Update CodeLLDB
2021-03-30 18:13:03 +00:00
puh
0d9e7835a8 Update CodeLLDB 2021-03-30 00:16:58 +03:00
Ben Jackson
054ea35428 Add a way to test with dlv directly. Currrently not ready for the big time 2021-03-22 15:13:58 +00:00
Ben Jackson
244d7d8fdf Add the reference material to the vim doc too 2021-03-22 14:45:21 +00:00
Ben Jackson
35e5b3d56e Add vim doc based on README
Use the following to recrate the doc:

* clone https://github.com/ycm-core/vim-tools
* create a venv and pip install the requierments
* cd to vimspector dir
* /path/to/vim-tools/html2vimdoc.py -f vimspector README.md > doc/vimspector.txt
2021-03-20 15:56:41 +00:00
Ben Jackson
842d9fbc2d Config for pyright language server 2021-03-20 15:56:41 +00:00
mergify[bot]
6a24a17f2e
Merge pull request #371 from aqez/master
Update netcoredbg to 1.2.0-738 to support async/await
2021-03-19 23:33:58 +00:00
Ben Jackson
91e0426e88
Merge branch 'master' into master 2021-03-19 23:20:41 +00:00
mergify[bot]
bf24b37190
Merge pull request #372 from puremourning/hot-code-replace
Hot code replace for java
2021-03-19 23:15:34 +00:00
Ben Jackson
2d589475eb
Merge branch 'master' into master 2021-03-19 23:05:19 +00:00
Ben Jackson
1414f261a1 Documentation for hot code replace 2021-03-19 22:59:27 +00:00
Ben Jackson
a39017dd06 Fix actually sending terminateDebugee, and fix neovim 1-based index reporting 2021-03-19 18:43:45 +00:00
Ben Jackson
c05335c799 Make default option work for terminate debugee 2021-03-19 17:51:59 +00:00
Ben Jackson
efc5c76866 Fix lints 2021-03-19 17:51:59 +00:00
Ben Jackson
154e727b96 Make confirm dialog take arbitrary keys
Confirm now takes the list of options, list of keys to select them and
the default value. Returned values are always a 1-based index into the
list (like SelectFromList) or -1 to mean esc/ctrl-c.

This uses a nice popup dialog in vim and a crappy input on neovim.
2021-03-19 17:51:59 +00:00
Ben Jackson
6b74e584d5 Create a messagebox-like interface in vim 2021-03-19 17:51:59 +00:00
Ben Jackson
85bb8594ab Allow forcing selection from the menu with <leader>F5 2021-03-19 17:51:59 +00:00
Ben Jackson
63fd3165fb Use popup for confirmations (note these have to be async) 2021-03-19 17:51:59 +00:00
Ben Jackson
afb912dd08 Print hotcodereplace messages 2021-03-19 17:51:59 +00:00
Ben Jackson
b92a89f0d4 Add a way to have adapter specific message handlers 2021-03-19 17:51:59 +00:00
Ben Jackson
6f88b400e1 Only disable mappings in the keyboard-version of the popup 2021-03-18 13:51:32 +00:00
mergify[bot]
af2670ef9a
Merge pull request #373 from puremourning/fix-calculus
Fix default variable values having substitutions from the calculus
2021-03-12 22:22:51 +00:00
mergify[bot]
2b24611036
Merge branch 'master' into fix-calculus 2021-03-12 22:08:52 +00:00
mergify[bot]
d84ebdd0ee
Merge pull request #375 from puremourning/up-down-stack
Add commands to navigate up/down the stack in focussed thread.
2021-03-12 22:08:46 +00:00
Ben Jackson
6b67d165b9 Update docs for new mappings 2021-03-12 21:50:37 +00:00
Ben Jackson
a53d68f043 Add up and down frame mappings 2021-03-12 21:27:56 +00:00
Ben Jackson
6d3f3e207b Fix default variable values having substitutions from the calculus 2021-03-12 20:15:45 +00:00
mergify[bot]
5716bbefa9
Merge pull request #369 from puremourning/narrow-mode
Narrow UI mode
2021-03-11 21:38:40 +00:00
Ben Jackson
2615cff97a If there isn't enough vertical space, we get vim errors (not enough room), so don't do that 2021-03-11 21:14:30 +00:00
Ben Jackson
01ea50cb70 use --clean for minimal vimrc 2021-03-11 21:13:58 +00:00
Ben Jackson
7d2770f3c4 Add vertical (i.e. narrow) layout
This puts the 3 utility windows at the top, horizontally split, with the
code view below.  The terminal window is drawn either vertically split
(if there's room) or horizontally split otherwise.  The output window
remains at tht bottom.

We add equivalent sizing options too, setting some defauts that roughly
work on my macbook pro.

We try to guess the best layout to use. In particular we go into
'narrow' mode if there are not enough horizonal columns to fit the
sidebar, code and at least the minimum terminal size. We also try to
move the terminal to be horizontally spit (i.e. vertically stacked) if
we can fit the max number of lines, but only the min number of columns.

This is all a little heuristic, but when testing it myself, it feels
good and tends to pick a good option by default. Users can always
customise the ui mode (g:vimspector_ui_mode and all the various specific
width options)
2021-03-10 23:44:45 +00:00
Tony Dwire
2a635294d0 Fix wrong version number for macos version. 2021-03-09 20:11:58 -06:00
Tony Dwire
38c843219e Updated file name for netcoredbg to reflect new filename. Overrode
version for macos since there is no new build for it yet.
2021-03-09 19:55:38 -06:00
Tony Dwire
c012f9520e Updated netcoredbg to 1.2.0-738 2021-03-09 19:45:27 -06:00
Ben Jackson
943ae6c7c9 Fix error when nothing to expand 2021-03-05 19:05:42 +00:00
Ben Jackson
82db32780b Add example of ssh to remote host 2021-03-04 13:28:04 +00:00
mergify[bot]
4cb4b814a1
Merge pull request #366 from puremourning/fix-stop
Fix crash using Stop function
2021-03-02 11:28:07 +00:00
Ben Jackson
d2b92b7ce5 Fix crash using STop function 2021-03-02 11:12:01 +00:00
Ben Jackson
5bd83d3e37 Add debugging instruction 2021-02-27 21:02:21 +00:00
mergify[bot]
f6517892c1
Merge pull request #357 from puremourning/set-variable-value
Set variable value
2021-02-25 22:39:45 +00:00
Ben Jackson
edcb057ead Add <leader><CR> to docs 2021-02-25 22:23:30 +00:00
Ben Jackson
06f9bfc057 Add basic tests for set variable value 2021-02-25 22:15:09 +00:00
Ben Jackson
804b499286 Allow setting the value via the api 2021-02-25 22:15:09 +00:00
Ben Jackson
49a9a4b367 Allow mappings which aren't special chars 2021-02-25 22:15:09 +00:00
Ben Jackson
f2d407256e Use expression completion for watch and set 2021-02-25 22:15:09 +00:00
Ben Jackson
94242fa532 CustomUI: Make buffers non-modifiable when opened for debugging 2021-02-25 22:15:09 +00:00
Ben Jackson
ba83a59e88 TEst overriding the mappings 2021-02-25 22:15:09 +00:00
Ben Jackson
675a68c601 Make it work in neovim too 2021-02-25 22:15:09 +00:00
Ben Jackson
e1078375fe Also allow <leader><CR> in case modifyOtherKeys mode doesn't work 2021-02-25 22:15:09 +00:00
Ben Jackson
26452289a8 Allow overriding the variables/stack trace mappings in config 2021-02-25 22:15:09 +00:00
Ben Jackson
ec9122284e Add some notes on setting values to the readme 2021-02-25 22:15:09 +00:00
Ben Jackson
131cfcdd33 Don't try and set a value if not supported 2021-02-25 22:15:09 +00:00
Ben Jackson
c2082cffae Support setting from the balloon 2021-02-25 22:15:09 +00:00
Ben Jackson
9e1a1ab4b5 Report failures 2021-02-25 22:15:09 +00:00
Ben Jackson
32f9a6ec43 Use silent for winbar menus 2021-02-25 22:15:09 +00:00
Ben Jackson
d8d6eb2286 Add ability to set a variable value
This works only for things which have known variablesReference, so
particularly currently only for scopes and theoretically for members.

I think this can work for watches too. will need to check.
2021-02-25 22:15:09 +00:00
mergify[bot]
1d38b8198f
Merge pull request #362 from puremourning/expr-completions-startup
Delay launching the python interpreter until needed
2021-02-25 22:14:59 +00:00
Ben Jackson
f40ac5db23 Delay launching the python interpreter until needed 2021-02-25 21:24:40 +00:00
Jake Zimmerman
11edcddd9c
Fix using double-quotes in VimspectorEval
The VimspectorEval command used `-bar` but this prevented the use of double quotes. This seems much more useful than a vim comment in this scenario, so remove the `-bar`.

This is _techncially_ breaking change, but I don't think it's likely anyone will be relying on doing `VimspectorEval x | something else`. If they are, sorry.
2021-02-25 11:04:12 +00:00
mergify[bot]
f9c5a33301
Merge pull request #355 from puremourning/terminate-debugee
Ask the user about terminating the debuggee
2021-02-24 18:27:16 +00:00
mergify[bot]
fdfa8b265b
Merge branch 'master' into terminate-debugee 2021-02-24 18:14:04 +00:00
mergify[bot]
a8651e257b
Merge pull request #356 from puremourning/telemetry-hide
hide the pointless telemetry data;
2021-02-24 18:09:48 +00:00
Ben Jackson
d8eb6a0463 Only prompt in 'interactive' contexts to avoid annoying questions 2021-02-24 18:00:08 +00:00
Ben Jackson
09efcf5e50 hide the controvertial telemetry data; it's not like anyone will ever look at it 2021-02-24 16:49:56 +00:00
Ben Jackson
5201995279 Ask the user about terminating the debuggee
CodeLLDB seems to actually support the terminateDebugee flag, so rather
than just forcefully killing things, ask the user if they want to.
2021-02-24 16:49:03 +00:00
mergify[bot]
fd03e074f3
Merge pull request #354 from YgorSouza/fix-readme-typos
Fix typos in README
2021-02-23 19:57:14 +00:00
Ygor Oliveira
fd0c6e7675 Fix typos in README 2021-02-23 19:51:52 +01:00
Ben Jackson
6fac220ee5 Disable mappings in the popup to ensure navigaion works 2021-02-23 09:42:57 +00:00
Ben Jackson
0810d7154c Fix syntax occasionally not working in popup, and custom vimrc crashing in neovim 2021-02-23 09:03:45 +00:00
Ben Jackson
476300f815 Fix errors resetting in neovim 2021-02-22 13:39:58 +00:00
mergify[bot]
95b900a0a7
Merge pull request #352 from puremourning/fix-restart-socket
Fix restart when using CodeLLDB
2021-02-22 12:29:37 +00:00
Ben Jackson
0e0cc6d4ae Fix restart when using CodeLLDB
Fix restarts always getting stuck "Initializing" when using CodeLLDB.

When using the restart command we re-use the configuration dict as-is,
so always re-use the same TCP port for the lldb socket. Originally it
was thought this was due to a race condition, having the port still
open, but it's not.

When doing a restart, or reset, we shut down the server after we get the
response to the disconnect message. CodeLLDB then also sends a
'terminated' message.

Previously we were forcefully closing the socket before killing the app,
after we get the 'disconnect' response. This meant that the OS buffer
for the socket to localhost:<the port> still contained the terminated
message at the point that we force-closed the socket and killed the
server.

The result was that the firt messages read from the "new" socket to
locahost:<port> were the last messagse written by the previous process,
trikcing vimspector into thinking that the server terminated the process
(before responding to the initialize request).

ANyway, the solution is to ensure that we read all messages from the
previous instance before considering it done. This is done by killing
the server if there is one *first* and then trying to read any messages
from the socket until it closes (reads EOF).

The tricky part is for when we didn't start the server (i.e. in a
multi-session setup). Here we simply _have_ to close the socket because
we can't know when we've received all of the messages, and we shouldn't
expect to receive any 'terminated' events after 'disconnect'.
2021-02-22 12:09:01 +00:00
mergify[bot]
c33fddd150
Merge pull request #351 from puremourning/doautocmd-frame-set
Add User autocommands when jumping to frame and resetting
2021-02-21 21:47:03 +00:00
Ben Jackson
448ee33a6a prevent annoying 'no matching autocmds' message 2021-02-21 21:24:02 +00:00
Ben Jackson
ae289a88c7 Update readme with example 2021-02-21 21:16:54 +00:00
Ben Jackson
3c7311e33a Test that the commands are fired when stepping through and continuing 2021-02-21 20:49:56 +00:00
Ben Jackson
d561c4aea5 Add demo of new commands for local mappings 2021-02-21 19:17:09 +00:00
przepompownia
3c54cd268f Send VimspectorDebugEnded event 2021-02-21 19:17:09 +00:00
przepompownia
bcf4120ba4 Do not send event after leave buffer. 2021-02-21 19:17:09 +00:00
__
50dc55e0e8 Fix errors reported by flake8 2021-02-21 19:17:09 +00:00
__
716181e056 fixup! Send an event before leave buffer in window 2021-02-21 19:17:09 +00:00
__
840ee09242 Send an event before leave buffer in window 2021-02-21 19:17:09 +00:00
__
0bb8416e11 Add test 2021-02-21 19:17:09 +00:00
__
d81bdf30ef Emit User VimspectorFrameWasSet event 2021-02-21 19:17:09 +00:00
mergify[bot]
e70b8f37a3
Merge pull request #350 from puremourning/nvim-float-window
Replace vim balloons with popups
2021-02-21 18:44:21 +00:00
Ben Jackson
5754e96067 FixUp: Change of mapleader 2021-02-21 18:24:26 +00:00
Ben Jackson
4958de92d3 Fix flake8 error 2021-02-21 18:05:59 +00:00
Ben Jackson
6b546cd621 Update TOC 2021-02-21 17:01:41 +00:00
Ben Jackson
323e22b8a9 Update readme 2021-02-21 17:01:41 +00:00
Ben Jackson
5a1eb9250a Fix test now that we're using a mapping 2021-02-21 17:01:41 +00:00
Ben Jackson
cc84e15932 Tidy up, refactor and fix some bugs 2021-02-21 17:01:41 +00:00
dsych
7dcb15f11c cleaning up 2021-02-21 17:01:41 +00:00
dsych
44711899cb moving stuff around 2021-02-21 17:01:41 +00:00
dsych
0313efa06f removing redundant check for array bounds 2021-02-21 17:01:41 +00:00
dsych
51d551fe52 replacing function calls with plug command 2021-02-21 17:01:41 +00:00
dsych
ae137ecdd0 removing old balloon code 2021-02-21 17:01:41 +00:00
dsych
d6c68d691c streamlining the docs 2021-02-21 17:01:41 +00:00
dsych
47680565c4 adding docs 2021-02-21 17:01:41 +00:00
dsych
8c39a861bd hopefully fixing tests on mac 2021-02-21 17:01:41 +00:00
dsych
639e89f5db fixing linting 2021-02-21 17:01:41 +00:00
dsych
5a23ec5beb adding tests for select range, invalid eval and expand/collapse 2021-02-21 17:01:41 +00:00
dsych
e0b0a7f3d2 recording popup win id on the failed evaluation 2021-02-21 17:01:41 +00:00
dsych
0ff9dc5f9e making sure that select marks were set before attempting to extract text 2021-02-21 17:01:41 +00:00
dsych
0c79384529 there is no need to execute feedkeys inside a popup context, since keys are added into the global input buffer 2021-02-21 17:01:41 +00:00
dsych
789377eab4 adding the first test! 2021-02-21 17:01:41 +00:00
dsych
4466fce20b fixing styling inside vim files 2021-02-21 17:01:41 +00:00
dsych
13102dc711 reverting back the highlight group 2021-02-21 17:01:41 +00:00
dsych
91bebc1826 adding a close button for keyboard triggered popup, since drag and resize are also enabled. removing wrap from border. 2021-02-21 17:01:41 +00:00
Ben Jackson
3239963893 Set the correct highlights in both popups in neovim 2021-02-21 17:01:41 +00:00
Ben Jackson
31e44548d3 Tidy up MouseFilter 2021-02-21 17:01:41 +00:00
Ben Jackson
e5e13ffcdd Fix typo: Baloon -> Balloon 2021-02-21 17:01:41 +00:00
Ben Jackson
894ca522d3 Use the popup callback rather than manually trying to do it 2021-02-21 17:01:41 +00:00
Ben Jackson
322b7e0a38 Enable mouse interraction for the both popups in vim 2021-02-21 17:01:41 +00:00
Ben Jackson
bc1146df3b Use normal highlighting for the popup as it's probably more readable (debatable?) 2021-02-21 17:01:41 +00:00
Ben Jackson
d2ed8a828c Use popup_filter_menu for the keyboard-popup rather than 100% our own 2021-02-21 17:01:41 +00:00
Ben Jackson
fccafd6739 FixUp: border characters - indicate that the corner can be dragged 2021-02-21 17:01:41 +00:00
Ben Jackson
672ac78fef Fix flaky syntax highlighting toggling on/off 2021-02-21 17:01:41 +00:00
Ben Jackson
64f2c8eb01 Use a fancy single line border in vim popup when we can 2021-02-21 17:01:41 +00:00
Ben Jackson
cb174c176d Disable wrapping long lines in neovim popup too 2021-02-21 17:01:41 +00:00
Ben Jackson
2d082cc923 Use a more generous maximum size for the popup (TODO: option for this ?) 2021-02-21 17:01:41 +00:00
dsych
0d703779dc evaluating select range 2021-02-21 17:01:41 +00:00
Ben Jackson
f3c39e12ab FixUp: Display in watch window, and indent in popup 2021-02-21 17:01:41 +00:00
Ben Jackson
cfae062da1 Disable cursorLine for the hover-only popup 2021-02-21 17:01:41 +00:00
Ben Jackson
0b4da4c82b Remove more text from the hover popup 2021-02-21 17:01:41 +00:00
Ben Jackson
64e38b57ab Fix crash; enable syntax highlighting in hover popup; use a Watch for the popup and re-use existing drawing code 2021-02-21 17:01:41 +00:00
Ben Jackson
0895c5e829 Add 1 cell of padding to the x direction which makes the popup more readable 2021-02-21 17:01:41 +00:00
Ben Jackson
052d63dee5 Fix mouse interraction with the popup - don't handle events outside the window 2021-02-21 17:01:41 +00:00
dsych
93568ba05d removing test code 2021-02-21 17:01:41 +00:00
dsych
e0b1d6ed81 fixing linting errors in python files 2021-02-21 17:01:41 +00:00
dsych
3c857cebf4 dynamically adjusting window size for nvim's floating window based on the buffer size 2021-02-21 17:01:41 +00:00
dsych
2f1c93a2ac only diplaying relevant info like name and value for nested types and value only for simple types 2021-02-21 17:01:41 +00:00
dsych
cb441be7bc allowing resizing and dragging with a mouse 2021-02-21 17:01:41 +00:00
dsych
b9ed7f34b4 removing redundant name for simple types, shortening variable type to 20 char and variable value to 100 chars to avoid overflowing tooltip window if the wrapped line is longer than the max size of the tooltip 2021-02-21 17:01:41 +00:00
dsych
04a5e889f9 making sure that float window is not modifiable inside nvim 2021-02-21 17:01:41 +00:00
dsych
f60b259dbc adding type information for simple types 2021-02-21 17:01:41 +00:00
dsych
b95ae00054 enabling a close button for popup 2021-02-21 17:01:41 +00:00
dsych
4617250f41 adding border to popup 2021-02-21 17:01:41 +00:00
dsych
0ced5eb1d4 splitting mouse and cursor filtering to avoid trapping keyboard for hover 2021-02-21 17:01:41 +00:00
dsych
747ad9b804 removing literal dictionary syntax, as it is not included in nvim-4.4 2021-02-21 17:01:41 +00:00
dsych
d2990d7bae making sure to open popup relative to cursor 2021-02-21 17:01:41 +00:00
dsych
819d6366ac fully implemented the hover function for vim 2021-02-21 17:01:38 +00:00
dsych
4e994968ff added popup support for vim, still need to figure out how to expand variables 2021-02-21 17:00:47 +00:00
dsych
7432be9532 implemented dynamic float windows for nvim 2021-02-21 17:00:47 +00:00
dsych
07858cc250 adding on the fly eval of inside a floating window 2021-02-21 17:00:47 +00:00
dsych
4c0ba92ac6 working on variables view 2021-02-21 17:00:47 +00:00
dsych
7c4eef9096 finally got float window working with variable evaluation 2021-02-21 17:00:47 +00:00
mergify[bot]
85ca867cc2
Merge pull request #348 from timtyrrell/patch-1
Update README typo
2021-02-16 08:59:33 +00:00
Tim Tyrrell
cb922bdc83
Update README typo 2021-02-14 11:56:27 -07:00
mergify[bot]
e99ac0d658
Merge pull request #345 from markwu/update-vscode-php-debug
Update vscode-php-debug to 1.14.9, it works for both Xdebug 2/3
2021-02-10 16:51:56 +00:00
Mark Wu
0cc4322b18 Update vscode-php-debug to 1.14.9, it works for both Xdebug 2/3 2021-02-10 20:01:18 +08:00
Ben Jackson
7f77842ab8 Make sure that tests fail properly; ensure that empty string is a valid default 2021-02-06 22:08:08 +00:00
mergify[bot]
4454dead0f
Merge pull request #344 from puremourning/multiple-vars
Fix variable substitution for multiple defaulted vars
2021-02-06 21:27:19 +00:00
Ben Jackson
30eec0d93c Fix variable substitution for multiple defaulted vars
The problem was that the python regex engine is strictly left-to-right,
so matching `[^}]|\\}` against \\}} meant that the `\\` was consumed by
the left of the `|`. The solution is to just switch them around.

Also add a way to run python tests from within vim, so we can actually
test this stuff.
2021-02-06 20:55:24 +00:00
Ben Jackson
52eff32651
Telemetry data myth busted. 2021-01-10 12:59:59 +00:00
mergify[bot]
5303de8195
Merge pull request #339 from ailrk/master
fixed some typos in the readme
2021-01-09 15:19:46 +00:00
Jimmy
75e8450cf3 fixed some typos in the readme 2021-01-09 07:47:33 -08:00
Ben Jackson
35f7e08fbb Bit of readme restructuring and improve docs on breakpionts API 2021-01-09 13:14:14 +00:00
Ben Jackson
d38da376c2 There are <plug> mappings 2021-01-09 12:46:13 +00:00
Ben Jackson
1f20115960 Update docs bundle to stfu security warnings 2021-01-09 12:15:55 +00:00
Ben Jackson
442a18f8f2
Merge pull request #338 from puremourning/new-issue
add some links to the new issue page
2021-01-09 12:04:02 +00:00
Ben Jackson
e010d3217c add some links to the new issue page 2021-01-09 12:03:18 +00:00
mergify[bot]
1556e63ef0
Merge pull request #317 from yatli/master
multiple languages per adapter
2021-01-09 11:50:10 +00:00
Ben Jackson
205eb7cdb3
Merge branch 'master' into master 2021-01-09 10:33:38 +00:00
mergify[bot]
44a0f819e2
Merge pull request #336 from puremourning/localhost
Use 127.0.0.1 rather than localhost to avoid issues with ipv6, and lo…
2021-01-08 16:08:12 +00:00
Ben Jackson
4206d0e57c Use 127.0.0.1 rather than localhost to avoid issues with ipv6, and long timeout connecting the socket 2021-01-08 15:54:49 +00:00
mergify[bot]
b180b14ed4
Merge pull request #334 from puremourning/fix-node-thread
Fix node thread
2021-01-08 11:49:20 +00:00
Ben Jackson
d52eb3a6e9 Fix thread state and PC for starting node app
The problem is the sequence of events sent by the debug adapter.
Vimspector requests threads:

* When the initialisation exchange completes (requests all threads)
* Whenever a thread event is received
* whenever a stopped event is received.

If any of those happens while any other request is in progress, we cache
the request and handle it later. The latest request is processed when
the response to the outstanding request is received.

The problem is if the event is a stopped event, it is the handling of
the threads request that actually sets the thread state internally to
stopped. In a sequence where the first event is a stopped event, we end
up discarding the stopped event. like:

1. Stopped event (thread 1 = stopped) (request threads)
2. Initialisation complete (cache request)
3. threads response received (discard response and process cached request)
4. response received (but forgotten about the stopped event).

The solution is to always process the thread response, even if we send
the cached request. To avoid flicker, we don't draw the screen, or
expand any threads/stacks in the case where we're sending a cached
request.
2021-01-08 11:35:01 +00:00
Ben Jackson
b72aa18dec Add a list to tcl tets 2021-01-08 11:06:33 +00:00
mergify[bot]
07ea3880ac
Merge pull request #328 from AllanGuigou/master
Allow a gadget to specify a custom extension path
2021-01-07 08:09:14 +00:00
Ben Jackson
38ed75dda7
Merge branch 'master' into master 2021-01-06 09:56:40 +00:00
Ben Jackson
0ad826704b
Merge branch 'master' into master 2021-01-06 09:54:16 +00:00
mergify[bot]
65708f55e0
Merge pull request #329 from lelutin/debug_py_1.2.1
bump debugpy gadget to version 1.2.1
2021-01-02 22:36:28 +00:00
Gabriel Filion
7bd22d2bbd bump debugpy gadget to version 1.2.1
1.2.1 is, as of this commit, the most recent version of the gadget.

The version currently used, 1.0.0b12 is not compatible with Python 3.9:
the build errors out on missing arguments for _PyEval_EvalFrameDefault.

Python 3.9 is the version of Python that will be shipped with the
upcoming debian release.

Support for 3.9 was merged in before the final 1.0.0 version. So,
upgrading the gadget will bring in python 3.9 support
2021-01-02 15:46:53 -05:00
Allan Guigou
ce8dd9113c Remove gradle wrapper
The gradle wrapper is too large of a dependency to include for every
download of vimspector. Users can use a local installation of gradle to
run the sample kotlin application if necessary.
2020-12-31 10:39:01 -05:00
Allan Guigou
ec68811c8a Add support test for kotlin 2020-12-24 15:10:56 -05:00
Allan Guigou
47c404f823 Allow a gadget to specify a custom extension path
When installing a custom gadget not officially supported by vimspector the
default extension path is 'extension', this works for vscode extensions but
does not support some debug adapters. This commit gives the ability to change
that default extension path by specifying 'extension_path' within the gadget
installer file.

Installing 'cust_adapter' would use the extension path 'adapter' rather
than 'extension' (ie ${gadgetDir}/cust_adapter/${version}/root/adapter)

{
  "cust_adapter": {
    "download": { ... },
    "all": {
      "extension_path": "adapter",
      "adapters": { ... }
    }
  }
}
2020-12-24 14:55:40 -05:00
mergify[bot]
41a98026fa
Merge pull request #327 from AW3i/master
Fix Typo in PHP section
2020-12-23 09:19:49 +00:00
Alex Weigl
523ea29faa
Fix Typo in PHP section 2020-12-22 23:49:25 +02:00
mergify[bot]
d0fc7815b3
Merge pull request #319 from puremourning/docker-test
Fix docker example
2020-12-22 16:04:25 +00:00
Ben Jackson
0942aa4523 Fix docker example for python
The example was was using 'launchCommand' which is not valid according
to the schema; it should be 'runCommand'.

But also, it never really worked. Vimspector would start the "adapter"
(in this case, try and connect to the TCP port) before running the
"prepare" commands, wich in this case would actually start debugpy
listening. So to solve that we run the prepare commands earlier.
Hopefully this won't cause a regression for Java and C++ remote attach,
which we don't really have tests for.

Finally, due to the way docker works, when you forward a port and
nothing is listening on it, docker _accepts_ the connection then
immediately drops it. This is _super_ annoying meaning that it looks to
vimspector liek the server instantly dies if it takes nonzero time for
the remote commands to open the port. So to solve this we add loaunch
and attach delays which can be configured in the adapter config. This
actually solves a prolem where the java debugger just takes agest to
attach on remote launch too.

(Finally, finally...) updated the vimspector schema to represent
the real launch/attach remote configuration, which was incorrectly
spec'd at the adapter level, but it's actually per launch/attach block.
2020-12-22 15:45:26 +00:00
Ben Jackson
f59ba048cb
Merge branch 'master' into master 2020-12-19 18:30:02 +00:00
Yatao Li
f20c4c9725 fix linter errors 2020-12-19 22:35:12 +08:00
Ben Jackson
f8d1e484f9
Update CONTRIBUTING.md
Minor fix and correct CI path
2020-12-18 17:10:29 +00:00
Yatao Li
66130389c5 code format fixes, update docs 2020-12-15 16:25:26 +08:00
Ben Jackson
61179b7670 Update CONTRIBUTING.md for PRs 2020-12-14 20:55:13 +00:00
Ben Jackson
f161ce1e8c Remove redundant comments 2020-12-14 20:55:03 +00:00
mergify[bot]
d04a8400f9
Merge pull request #318 from cposture/feat-update-gadgets-vscode-go
update gadgets vscode-go version
2020-12-14 17:03:34 +00:00
cposture
b65d9536ad update gadgets vscode-go version 2020-12-15 00:22:35 +08:00
Yatao Li
28c5534704 language list 2020-12-14 22:57:32 +08:00
mergify[bot]
2eb32f3153
Merge pull request #315 from puremourning/env-dup-fix
Don't copy the environment to update it
2020-12-05 23:15:43 +00:00
Ben Jackson
8261cde3c9 Don't copy the environment to update it
This causes problems on windows, and is wasteful anyway. The subprocess
will pick up the environment from its parent.
2020-12-05 16:42:07 +00:00
Ben Jackson
573121ee08
Merge pull request #312 from puremourning/watch-errors
Print failure when watch expression errors
2020-12-02 18:19:39 +00:00
Ben Jackson
632e6696ef Add SSH login for failures 2020-12-02 18:06:37 +00:00
Ben Jackson
c531686d39 Use the cask of macvim because apparently the formula requires building gcc from source 2020-12-02 18:06:37 +00:00
Ben Jackson
2eac9ddff8 Print failure when watch expression errors 2020-12-02 15:26:30 +00:00
Ben Jackson
8e2d352eb8 Fix local adapter specs
Somehow we lost the ability to define adapters in the local
.vimspector.json, I think when GetConfigurations was added.

Put that feature back.
2020-11-30 10:19:02 +00:00
mergify[bot]
4ac9785217
Merge pull request #308 from andwilley/docs/update-java-example
Add detail about potential troubleshooting. Recommend jsdls_extension…
2020-11-28 11:51:17 +00:00
Drew Willey
4a45753a4c Add detail about potential troubleshooting. Recommend jsdls_extension_path be set in vimrc. 2020-11-27 21:16:51 -07:00
Ben Jackson
e15c50a4f4 Don't request strack trace if the thread isn't stopped 2020-11-28 00:17:42 +00:00
Ben Jackson
17a9494dbc
Merge pull request #306 from felixfeit/patch-1
Fix typos.
2020-11-26 18:43:04 +00:00
felixfeit
4db4880b6d
Fix typos. 2020-11-26 19:31:46 +01:00
Ben Jackson
4c2b3bd886
Make it clear that the mappings have to be enabled 2020-11-26 12:38:34 +00:00
Ben Jackson
e6500d39d8
Neovim has prompt buffers in nightly 2020-11-26 12:11:23 +00:00
mergify[bot]
ff4acb17d1
Merge pull request #303 from eduardomezencio/lua-fix-configs
Improve lua configuration and test files
2020-11-22 21:55:53 +00:00
Eduardo Mezêncio
4f03e4f65a Improve lua configuration and test files
Change lua test files to call `require 'lldebugger'` only when using
love, because it's not needed with lua or luajit. Also add `stopOnEntry`
key to test `.vimspector.json` because it works correctly with this
change.
2020-11-22 18:38:40 -03:00
mergify[bot]
23130d74ad
Merge pull request #297 from puremourning/threads
[WIP] Improve Threads Handling
2020-11-22 15:07:22 +00:00
Ben Jackson
5ab92a7e67 Breakpont tests too flaky 2020-11-22 14:54:02 +00:00
Ben Jackson
979c1e8779 Reshuffle README 2020-11-22 14:35:30 +00:00
Ben Jackson
a9d0ebde0b Add mapping for Focus, and make focusing a stack frame focus the thread 2020-11-22 14:13:15 +00:00
Ben Jackson
82307ff1ba Correctly handle the 'continue' response when continuing a specific thread 2020-11-22 14:13:15 +00:00
Ben Jackson
7d5ad3ffa1 Another flaky test 2020-11-22 14:13:15 +00:00
Ben Jackson
8801c2dac4 Fix pause/continue of individual threads
work around buggy java server sending invalid threads response.

java server supports this separate threads running/paused as a test
case.
2020-11-22 14:13:14 +00:00
Ben Jackson
e1c1347bdd Fix lint 2020-11-22 14:13:14 +00:00
Ben Jackson
c769e8a479 Fix up the tests to work in linux container too 2020-11-22 14:13:14 +00:00
Ben Jackson
53b1d12447 Allow setting the current thread, use a sign to highlight the line with the current thread 2020-11-22 14:13:14 +00:00
Ben Jackson
8e3a734141 Support continued event properly 2020-11-22 14:13:14 +00:00
Ben Jackson
0f0d684e92 FixUp: comment about why we apply thread state in consume_threads 2020-11-22 14:13:14 +00:00
Ben Jackson
42cdff043a Redraw the screen each 20 retries
This allows us to eyeball why something is failing
2020-11-22 14:13:14 +00:00
Ben Jackson
f0785c11f2 Allow pausing individual threads (in theory) 2020-11-22 14:13:14 +00:00
Ben Jackson
a5d66a7477 Correctly track and now actually report running/paused status
It's quirky, we have to pass the stopped event to LoadThreads so that it
can correctly work out the state of any _newly_ added threads. We now
also correctly apply the allThreadsStopped=False behaviour where you
must not allow expansion of such threads (in theory, that's untested).
2020-11-22 14:13:14 +00:00
Ben Jackson
2399a79cae start to track individual thread state 2020-11-22 14:13:14 +00:00
Ben Jackson
e9e0e9e5b9 Test for new thread creation
- don't clear the stack trace on continue - track running status
  properly (ish)
- mark threads (running) when the app is executing
- indicate the "current" thread with a different icon

TODO:
- allow user to specify current thread?
- track running status of threads individually?
- allow to pause/continue specific threads?
2020-11-22 14:13:14 +00:00
Ben Jackson
e2ca9b5318 Threads tests WIP 2020-11-22 14:13:14 +00:00
mergify[bot]
e5a765409a
Merge pull request #302 from puremourning/updated-go
Update vscode-go to latest version
2020-11-22 13:39:46 +00:00
Ben Jackson
f87aa4aa19 Undo flakiness hack 2020-11-22 13:30:14 +00:00
Ben Jackson
07ec08e664 Fix clearing temp breakpoints - ensure that the isngs are undisplayed 2020-11-22 13:25:10 +00:00
Ben Jackson
3330c704d7 Too many flakes 2020-11-22 12:46:15 +00:00
Ben Jackson
87ce1734ea Update vscode-go to latest version 2020-11-22 12:31:21 +00:00
mergify[bot]
48e075624a
Merge pull request #301 from puremourning/winbar-neovim
Work around neovim WinBar rendering bug
2020-11-19 23:45:01 +00:00
Ben Jackson
b36f9e893a Work around neovim WinBar rendering bug 2020-11-19 23:29:13 +00:00
mergify[bot]
b7de25e3d1
Merge pull request #296 from puremourning/update-servers
Update servers
2020-11-16 22:09:48 +00:00
Ben Jackson
47c2cef2a1 Use a tag for tclpro to avoid installing every time 2020-11-16 21:17:36 +00:00
Ben Jackson
cd3b5f5baa Update mono debug; even though it doesn't work 2020-11-16 21:17:22 +00:00
Ben Jackson
5b88837919 Print the correct version for a git tag 2020-11-16 20:14:39 +00:00
Ben Jackson
888c558aa4
Merge pull request #294 from eduardomezencio/lua-support
Add lua support through local-lua-debugger-vscode
2020-11-16 19:35:13 +00:00
Eduardo Mezêncio
85865e0012 Add regression tests for lua support
Change Dockerfile to install lua, luajit and love and also to install
nodejs 12 needed to build the lua debug adapter. Create the
love-headless test in support/test/lua to test love without an x server.
2020-11-16 15:08:55 -03:00
Eduardo Mezêncio
7be6d852c6
Merge branch 'master' into lua-support 2020-11-16 15:04:26 -03:00
mergify[bot]
96a594e4cf
Merge pull request #295 from TamaMcGlinn/master
Fixed typo 'exisitng' in README
2020-11-16 10:49:58 +00:00
Tama
69bb2737d1 Fixed typo 'exisitng' in README 2020-11-16 11:37:12 +01:00
Eduardo Mezêncio
1eb2bc2199 Add lua to README index. 2020-11-14 20:27:27 -03:00
Eduardo Mezêncio
2819e224e7 Add lua support through local-lua-debugger-vscode
Add the lua adapter to gadgets.py and installer.py, update the README.md
file and create basic tests using lua, luajit and love.
2020-11-14 19:34:15 -03:00
Ben Jackson
2bdb30a45e update docs 2020-11-14 12:34:15 +00:00
mergify[bot]
3f3a001283
Merge pull request #292 from puremourning/codelldb-default
Recommend CodeLLDB
2020-11-13 17:02:14 +00:00
mergify[bot]
5ae3d9f336
Merge branch 'master' into codelldb-default 2020-11-09 22:41:50 +00:00
mergify[bot]
30741f8813
Merge pull request #291 from puremourning/netcoredbg-update
Update netcoredbg
2020-11-09 22:33:09 +00:00
Ben Jackson
2225735b80 Recommend CodeLLDB 2020-11-09 22:23:37 +00:00
Ben Jackson
fb86ef924b Update netcoredbg 2020-11-09 21:57:47 +00:00
mergify[bot]
97bef33660
Merge pull request #289 from sharksforarms/list-configurations
Add a GetConfigurations function
2020-11-06 17:32:59 +00:00
Emmanuel Thompson
d5b9411256 Add a GetConfigurations function 2020-11-06 12:05:14 -05:00
mergify[bot]
7b048367f7
Merge pull request #282 from puremourning/run-to-cursor
Run to cursor
2020-10-23 22:10:55 +00:00
Ben Jackson
80985148e7 Add "run to cursor" support
We add a 'temporary' option to line breakpionts and try and clear any
temporary breakpionts on the line we end up stopping on. This might not
be art, but _probably_ works in almost all cases that matter.

it's a bit hacky the way we have to push the reason around, but we don't
know where we stopped until we actually get the stack trace response and
SetCurrentFrame

Move temporary breakpionts to match server response

Also delete any existing ones when adding a new one and add tests for
run-to-cursor.

Only continue after we successfully set the breakpoints. This makes it
work in go
2020-10-23 22:53:04 +01:00
Ben Jackson
0d112d70a0 Add SetLineBreakpoint and ClaerLineBreakpoint APIs
These are useful for running tests (i.e. ensure there's a breakpiont at
the start of the test) and/or other programmatic usages.

They will also be needed for setting temporary breakpionts.
2020-10-17 22:40:08 +01:00
Ben Jackson
78bec87f4e
Link to wiki for additional language support 2020-10-14 22:33:04 +01:00
mergify[bot]
c76e20cd9b
Merge pull request #280 from camilo-schoeningh-sociomantic/vscode-go-moved
vscode-go: Update plugin and use new repository address.
2020-10-14 21:18:17 +00:00
Camilo Schoeningh
ea36b60a07
vscode-go: Update version and use new repository.
As of June 2020, the vscode-go repository has moved to https://github.com/golang/vscode-go.
2020-10-14 21:21:11 +02:00
Ben Jackson
5057906317
Merge pull request #281 from puremourning/java-cwd
Java: Set cwd by default
2020-10-14 17:02:03 +01:00
Ben Jackson
8d17572538 brew update is failing as some taps have been removed
Per https://github.com/Homebrew/homebrew-core/issues/30917, use brew
update-reset to fix it
2020-10-14 15:50:10 +01:00
Ben Jackson
f3c5944dd2 Java: Set cwd by default 2020-10-14 15:26:54 +01:00
mergify[bot]
1eb9933e2a
Merge pull request #279 from puremourning/breakpoint-event
Fix breakpoint event
2020-10-10 15:29:38 +00:00
Ben Jackson
1b9763a4fc Minor improvements to console usage
Display failures and stop adding random text which makes the (very
useful) CodeLLDB interface look messy
2020-10-10 16:20:55 +01:00
Ben Jackson
16f22b396f Fix breakpoint event
Few problems:
 - we were passing a dict instead of a list of breakpoints
 - if the breakpoint had a source which was {} we crashed
 - we didn't support the 'removed' event
2020-10-10 16:04:46 +01:00
mergify[bot]
f6726de058
Merge pull request #272 from ayberkydn/patch-1
Update README.md
2020-10-01 21:30:12 +00:00
Ayberk Aydın
3d442c978f
Update README.md
fix typo
2020-10-02 00:20:50 +03:00
mergify[bot]
692a0d8d39
Merge pull request #268 from puremourning/breakpoints-file-while-debugging
While debugging, use the correct path for breakpoints
2020-09-27 22:12:33 +00:00
Ben Jackson
bd09206caf While debugging, use the correct path for breakpoints 2020-09-27 22:47:22 +01:00
mergify[bot]
d1bfe18e18
Merge pull request #266 from puremourning/dblclick
Add double-click to expand/collapse
2020-09-27 12:24:15 +00:00
Ben Jackson
b2f41643f4 Add double-click to expand/collapse 2020-09-27 13:14:09 +01:00
mergify[bot]
42b79d8cbd
Merge pull request #264 from puremourning/fidessa-upstream
Launch remote commands in the terminal
2020-09-25 15:57:43 +00:00
Ben Jackson
b34ccd679d Fix ambiwidth=double again and make sure it doesn't break again 2020-09-25 16:36:19 +01:00
Ben Jackson
069224e28d Allow defaults for variables with multiple underscore characters 2020-09-25 16:36:19 +01:00
Ben Jackson
6fd4724189 Launch remote commands in a termianl
This allows stdin and mans you don't need the output window visible to
see the useful process output when debugging a remote-Launch.
2020-09-25 16:36:19 +01:00
mergify[bot]
6a5c0cf63e
Merge pull request #261 from puremourning/breakpoint-pc-line
Display a different sign when there's a breakpoint on the PC line
2020-09-19 14:30:21 +00:00
Ben Jackson
2ad1a3e502 Display a different sign when there's a breakpoint on the PC line
Vim only renders a single sign-per-line. If we have the PC _and_ a
breakpoint, we should make that clear. Do this using a vimspectorPCBP
sign which combines both vimspectorPC and vimspectorBP (sort of).

We can't (unfortuantely) render the breakpoint blob in a different
colour, but it's at least obvious when we toggle on the PC line.
2020-09-19 15:18:05 +01:00
Ben Jackson
26277cefbd Another flaky test 2020-09-19 11:36:28 +01:00
Ben Jackson
ce4f341feb Correct some typos 2020-09-19 11:25:53 +01:00
Ben Jackson
4ed9158282 Accomodate flaky tests that I can't diagnose 2020-09-19 11:18:40 +01:00
Ben Jackson
9e2526c4a4 Fix logs in the wrong place when runnning tests withotu basedir 2020-09-19 11:18:40 +01:00
mergify[bot]
75e1c6b1c1
Merge pull request #260 from puremourning/windows-install-cmd
Fix installation issues on windows - find npm.cmd
2020-09-18 20:07:23 +00:00
Ben.Jackson
4424969028 Fix installation issues on windows - find npm.cmd 2020-09-18 20:59:21 +01:00
Ben Jackson
173f2d6f37 CI: maybe this is the pattern we need ? 2020-09-11 17:06:05 +01:00
Ben Jackson
e76d6a5c97 Use a sensible tag 2020-09-11 16:50:10 +01:00
Ben Jackson
1db1bd6d5b Need to specify the exact asset path 2020-09-11 16:44:57 +01:00
Ben Jackson
a037e4d31a We need to globstar the test log dirs 2020-09-11 16:25:00 +01:00
Ben Jackson
2e3bbef6df The steps context doesn't seem to work 2020-09-11 16:14:26 +01:00
Ben Jackson
277994e27c Add missing test file 2020-09-11 15:56:04 +01:00
Ben Jackson
853af58124 Update badge and upload logs if tests fail 2020-09-11 15:43:58 +01:00
Ben Jackson
778745b5c2 Add a test for ruby 2020-09-11 15:43:53 +01:00
Ben Jackson
db4e60aca5 Add a manual test for the calculated variables 2020-09-11 15:43:53 +01:00
Ben Jackson
522d8fbc99
Merge pull request #251 from puremourning/gh-actions
Migrate CI to GitHub actions
2020-09-11 15:29:29 +01:00
Ben Jackson
99d20bb277 Update mergify config for GHA 2020-09-11 15:01:37 +01:00
Ben Jackson
8b8be9f5b5 Port builds to GitHub Actions too 2020-09-04 18:57:56 +01:00
Ben Jackson
3e452b73d1 Migrate linting to GitHub actions 2020-09-04 16:52:32 +01:00
Ben Jackson
f1cc01a399 FixUp: Actually wait for buffers to close 2020-09-04 12:57:13 +01:00
mergify[bot]
2c401a859c
Merge pull request #248 from puremourning/completion
Support completion for console and watches.
2020-09-04 00:52:40 +00:00
Ben Jackson
ca804bda99 Fix flake when terminal takes a while to close 2020-09-04 01:41:01 +01:00
Ben Jackson
4e5011fe1b Make command line completion work too 2020-09-04 01:18:59 +01:00
Ben Jackson
45f0b68d87 Remove slow debugging code 2020-09-04 00:52:36 +01:00
Ben Jackson
1ace7b648e FixUp: vint 2020-09-04 00:49:37 +01:00
Ben Jackson
710cffe2da update docs 2020-09-04 00:48:40 +01:00
Ben Jackson
0867edd81c FixUp: Correct return values from omnifunc 2020-09-04 00:37:39 +01:00
Ben Jackson
3a79ce9ab7 Add a bash test script 2020-09-04 00:31:34 +01:00
Ben Jackson
2710ee2bfa When the start parameter is missing, the behabiour is arbitary
It seems that the behaviour of the start parameter being missing is
server (or perhaps a specific client) dependent. The specification
clearely says that it should be inserted at the column of the original
request, but the servers clearly expect either for that column to be the
beginning of an identifier or for the client to ignore the spec and
request from that position anyway.

Reading the VSCode code, we see that the 'word' before the cursor is
guessed, and if only if BOTH 'start' AND 'length' are supplied, then
they are used to determine where insertion starts, otherwise the current
'word' is used. Unclear what 'word' means in the specific contexts, but
we're relying on iskeyword.
2020-09-04 00:29:39 +01:00
Ben Jackson
3aa949431e Upgrade chrome debugger to 4.12.10 2020-09-03 22:11:57 +01:00
Ben Jackson
97bdb0d0cc Show launch failure reason in the splash 2020-09-03 22:09:22 +01:00
Ben Jackson
733843a6d4 Support completion for console and watches.
Add omnifunc for prompt buffers

This synchronous completion can be used with any completion system
including built-in CTRL-X CTRL-O.

The filetype of the prompt buffers is set to VimspectorPrompt so that it
can be identified by completion systems. For example, this works well
with YCM:

let g:ycm_semantic_triggers =  {
  \   'VimspectorPrompt': [ '.', '->', ':', '<' ]
  \ }
2020-09-03 17:48:46 +01:00
mergify[bot]
e81be848a1
Merge pull request #244 from puremourning/sign-pri
Highlight the current line using linehl=CursorLine
2020-09-01 16:22:48 +00:00
Ben Jackson
b22d8e570e Try to determine what is flaking the tests; make sure to reset the priorities 2020-09-01 17:06:52 +01:00
Ben Jackson
dffd65f241 Use CursorLine highlihgt to highlight current PC line 2020-09-01 16:31:25 +01:00
Ben Jackson
b2456b587f Centralise the default settings 2020-09-01 16:31:04 +01:00
mergify[bot]
7a6fea120b
Merge pull request #243 from puremourning/hidden-no-name
Don't leak buffers when creating output view
2020-09-01 15:22:12 +00:00
Ben Jackson
f538102d33 Don't leak buffers when creating output view 2020-09-01 15:40:16 +01:00
mergify[bot]
18fd56484e
Merge pull request #242 from puremourning/sign-pri
Allow customisation of the signs
2020-09-01 14:35:19 +00:00
Ben Jackson
c1b544fb3c Allow customisation of the signs
Too many plugins use the default priority of 10 so they race/chase.
Allow uses to configure the priorities and make sure that the defaults
are documented.
2020-09-01 13:56:12 +01:00
mergify[bot]
e634982d78
Merge pull request #240 from tamago324/fix-balloon-display
Fix balloon display
2020-08-29 13:32:09 +00:00
tamago324
8b488d479d To enable the Windows GUI to display the balloon correctly
Refer https://github.com/vim/vim/issues/1512#issuecomment-492070685
2020-08-29 22:19:46 +09:00
tamago324
9b7540fbf6 Format flake8 2020-08-29 15:22:19 +09:00
tamago324
95fa0c5105 Fix balloon display ... 2020-08-29 15:17:24 +09:00
mergify[bot]
7a8bdef088
Merge pull request #239 from puremourning/togglelog
Add VimspectorToggleLog
2020-08-28 13:42:56 +00:00
Ben Jackson
51cc6c4d3a Add VimspectorToggleLog 2020-08-28 14:32:17 +01:00
mergify[bot]
3374d32891
Merge pull request #237 from puremourning/neovim-exceptions
Fix neovim (again) - incompatible exception behaviour
2020-08-24 17:43:03 +00:00
Ben Jackson
ef94b1bc49 Fix neovim (again) - incompatible exception behabiour
Neovim does not raise:

* KeyError when accessing a vim dict
* KeyboardInterrupt when ctrl-c at a prompt

Instead it raises some internal subclass of vim.errro which cannot
easily be identified, so we just catch any vim.error.
2020-08-24 18:31:47 +01:00
Ben Jackson
24193a17ff
Fix minor readme errors 2020-08-23 18:01:46 +01:00
mergify[bot]
3b03cedc2b
Merge pull request #235 from puremourning/double-width
Fix signs when ambiwidth=double
2020-08-23 12:51:26 +00:00
Ben Jackson
dae5760900 Ensure signs are only padded when 1 display cell 2020-08-23 13:42:40 +01:00
Ben Jackson
3c1ac36e6e Add some instructions for cutomising the unicode signs 2020-08-23 12:27:03 +01:00
mergify[bot]
961557975c
Merge pull request #232 from tamago324/fix-schema-url
Fix schema url
2020-08-23 07:23:56 +00:00
tamago324
9de4d07955 Fix schema url 2020-08-23 12:02:06 +09:00
Ben Jackson
e90093870e
task does not support displayName 2020-08-21 18:28:20 +01:00
Ben Jackson
d263568ef7
DOn't link node that you didn't install 2020-08-21 18:19:45 +01:00
Ben Jackson
dc57ed7a67
Use the node installer task 2020-08-21 18:14:59 +01:00
Ben Jackson
3573439a5d
apparently /usr/local/node is not writable; make it 2020-08-21 18:05:57 +01:00
Ben Jackson
044e27144e
Force link latest node 2020-08-21 17:54:12 +01:00
Ben Jackson
cc06605b40
try with the latest node, as we no longer require node 10 2020-08-21 17:49:07 +01:00
Ben Jackson
67a380c9fa
Fix homebrew installation 2020-08-21 17:44:35 +01:00
Ben Jackson
213a02dcbe Remove junk website stuff 2020-08-15 16:41:48 +01:00
Ben Jackson
51bbadc4f5 Update website dependencies 2020-08-15 16:39:32 +01:00
Ben Jackson
57f1c128c5 update doc for coercing types 2020-08-13 17:30:31 +01:00
mergify[bot]
9a9d43cb82
Merge pull request #229 from puremourning/variables-coerce-type
Ability to coerce types in the vimspector config
2020-08-13 14:04:35 +00:00
Ben Jackson
f0d4716783
Merge branch 'master' into variables-coerce-type 2020-08-13 11:38:39 +01:00
Ben Jackson
ca63c08d6a Add a way to force a str in case #json is really required in a key 2020-08-05 22:24:35 +01:00
Ben Jackson
be44a22903 Allow coercing a type in vimspector config
Initially I considered using #i, #s, etc. to coerce to specific types,
but then it wasn't clear of the semantics (particularly for bool, where
JSON bool true/false, Python bool True/False).

But it turns out that we can just coerce any key from a JSON string.
Users can _probably_ type JSON strings for most things, or use variables
to run scripts to generate them, this allows essentially complete
flexibility to define the data types needed to populate the launch spec.

The purpose of this is to allow some level of automated setup by
requesting data from the user and then (subsequently) saving the
flattneed config to the vimspector config file.
2020-08-05 22:19:38 +01:00
Ben Jackson
7824bb29b2
Update bug_report.md 2020-08-02 18:25:06 +01:00
Ben Jackson
750e68bdf5
Require minimal reproduction in contributing guide. 2020-08-02 18:16:32 +01:00
mergify[bot]
1f270ed423
Merge pull request #215 from puremourning/variables-default-value
Add ability to specify defaults for variables
2020-07-31 21:30:13 +00:00
Ben Jackson
3a160aa77a Fix: Crash when deleting a watch that never got a result 2020-07-31 22:19:10 +01:00
Ben Jackson
d86b42bf5b Allow VimspectorShowOutput with no argument 2020-07-31 22:19:10 +01:00
Ben Jackson
10e9a75fc7 Tidy UI - use some unicode symbols and organise winbar 2020-07-31 22:19:10 +01:00
Ben Jackson
6593f383cf Use any python for debugpy 2020-07-31 22:19:10 +01:00
Ben Jackson
f6158d0ffb Make installer quiet too 2020-07-31 22:19:10 +01:00
Ben Jackson
aa26d4bc1a Add ability to specify defaults for variables 2020-07-31 22:19:10 +01:00
mergify[bot]
9f57cb0042
Merge pull request #221 from roachsinai/custom_winbar
Custom text of Winbar buttons.
2020-07-25 09:21:40 +00:00
mergify[bot]
b02b1835d4
Merge branch 'master' into custom_winbar 2020-07-25 09:14:33 +00:00
raochsinai
cb0eee180f Correct test function for CustomWinBar in ui.test.vim. 2020-07-25 17:03:04 +08:00
raochsinai
917b737486 Custom text of Winbar buttons. 2020-07-25 16:12:27 +08:00
mergify[bot]
effc49eed9
Merge pull request #220 from puremourning/installer-windows-junction
Use a junction, as symlinks require escalated priviledges on some win…
2020-07-24 21:17:07 +00:00
Ben Jackson
1e43cd4870 Use a junction, as symlinks require escalated priviledges on some windows 2020-07-24 21:27:47 +01:00
mergify[bot]
db2288c6ed
Merge pull request #218 from puremourning/installer-upgrade
Upgrade gadgets when they change
2020-07-24 16:20:03 +00:00
Ben Jackson
f9d20b9537 Upgrade gadgets when they change
This adds a --upgrade option to install_gadget.py and makes
VimspectorUpdate only update things which have changed.

To do this, we record the gadget spec in a manfiest file and compare it
with the current spec when in upgrade mode.

'Changed' in this case means that the gadget spec has changed from the
last time the installer was run. It does _not_ actually check the
presence of the gadget.
2020-07-24 17:12:07 +01:00
mergify[bot]
56418e3233
Merge pull request #216 from puremourning/installer-list
Allow a statically configured list of gadgets
2020-07-23 15:45:12 +00:00
Ben Jackson
8f5b928e4b Allow a statically configured list of gadgets
Useful for storing config in source control
2020-07-23 16:37:03 +01:00
mergify[bot]
5fbc70165d
Merge pull request #214 from adelarsq/patch-2
Spelling correction
2020-07-22 22:57:10 +00:00
Adelar da Silva Queiróz
8438dd40a1
Spelling correction
Just a spelling correction
2020-07-22 19:17:39 -03:00
mergify[bot]
7acf6ab45c
Merge pull request #213 from puremourning/install-bang
Add bang versions of install commands to leave the output open
2020-07-22 21:29:31 +00:00
Ben Jackson
29cb5c914b Add bang versions of install commands to leave the output open 2020-07-22 22:18:19 +01:00
mergify[bot]
357e112cdf
Merge pull request #212 from puremourning/installer-windows
Fix windows installer issues - use symlinks not junctions and fix dum…
2020-07-22 18:38:53 +00:00
Ben Jackson
26d7e95adc Fix windows installer issues - use symlinks not junctions and fix dumb use of PIPE with check_call 2020-07-22 19:27:42 +01:00
mergify[bot]
89ffb1798c
Merge pull request #209 from puremourning/install-commands
Simplify and improve gadget installation - add VimspectorInstall and VimspectorUpdate
2020-07-22 15:12:25 +00:00
Ben Jackson
c50c99ef34 Don't spam echo when jobs finish, revert sudo exit 2020-07-22 16:01:44 +01:00
Ben Jackson
2ea112ded9 No args for VimspectorAbortInstall 2020-07-22 15:48:16 +01:00
Ben Jackson
8d1c723b28 FixUp: Azure 2020-07-22 15:16:09 +01:00
Ben Jackson
e603520860 FixUp: Flake8 2020-07-22 15:14:59 +01:00
Ben Jackson
8a6d56d3e1 Run the upate in CI too 2020-07-22 15:05:21 +01:00
Ben Jackson
2d6cada5a9 Azure - check gadgets.py 2020-07-22 15:04:37 +01:00
Ben Jackson
d7eff46e0b Vint the syntax file too 2020-07-22 14:59:54 +01:00
Ben Jackson
625da3fcbe Tarballs still require no installation 2020-07-22 14:57:26 +01:00
Ben Jackson
000f7a9232 Prettify the output with some syntax and quiet option 2020-07-22 14:40:23 +01:00
Ben Jackson
4144631d03 Add :VimspectorUpdate 2020-07-22 12:50:44 +01:00
Ben Jackson
8275d2fafb README updates 2020-07-22 12:40:32 +01:00
Ben Jackson
98bef3db03 Fix - don't switch windows/buffers to create a new hidden buffer 2020-07-22 10:54:06 +01:00
Ben Jackson
375ff4aa27 Suggest installing gadget if possible 2020-07-22 10:52:48 +01:00
Ben Jackson
cd5ca37ce1 Neovim support 2020-07-22 10:52:48 +01:00
Ben Jackson
05bbafd60c Close the intaller output when complete 2020-07-22 10:52:48 +01:00
Ben Jackson
ca4ab52f8d Fix regression: Don't render winbar if the window isn't valid 2020-07-22 10:52:48 +01:00
Ben Jackson
0140a607b1 Raise autocommand when installer completes. use this in testing 2020-07-22 10:52:48 +01:00
Ben Jackson
23e5f6bbf4 Switch to running the actual install_gadget.py
This re-uses the OutputView code to run the installer script. Refactor
to remove connection from the base OutputView (and other places, it
wasn't used - only used after ConnectionUp).

This also consolidates the stdout and stderr buffers for running jobs.
The distinction was always arbitrary and probably an error, based on the
fact that they were separate in the APIs not based on usability.
2020-07-22 10:52:48 +01:00
Ben Jackson
025d193493 Add VimspectorInstall command with sort-of completion 2020-07-22 10:48:12 +01:00
Ben Jackson
f945dbcfdd Move gadget config file writing too 2020-07-22 10:48:12 +01:00
Ben Jackson
8f3de079bc Use --install to run_tests instead of manually running the installer 2020-07-22 10:48:12 +01:00
Ben Jackson
6b89df173f Remove pointless calls to GetOS() everywhere 2020-07-22 10:48:12 +01:00
Ben Jackson
d3fd0a38f0 Refactor installer fully into installer module 2020-07-22 10:48:12 +01:00
Ben Jackson
43d438dfc9
Better python diags 2020-07-22 09:53:03 +01:00
Ben Jackson
daf854e3e1
Any charity 2020-07-21 23:26:34 +01:00
Ben Jackson
0c6715e95e
Charityware 2020-07-21 23:24:53 +01:00
mergify[bot]
db5073aec1
Merge pull request #208 from puremourning/eval-cursorpos
Put the cursor at the end of the buffer after evaluations
2020-07-18 17:23:49 +00:00
Ben Jackson
e885c95daa Put the cursor at the end of the buffer after evaluations 2020-07-18 18:14:39 +01:00
mergify[bot]
c1e29be9b8
Merge pull request #198 from puremourning/ui-custom
UI customisation
2020-07-18 14:20:18 +00:00
Ben Jackson
99b582378a Allow ctrl-c to cancel when asked for a variable 2020-07-18 14:39:54 +01:00
Ben Jackson
47ace82364 FixUp: output window should set the global win id 2020-07-18 13:55:00 +01:00
Ben Jackson
3726766694 Documentation for the UI customisation 2020-07-18 13:41:22 +01:00
Ben Jackson
80afb153b9 FixUp: Closing the output window causes errors on output 2020-07-18 13:21:17 +01:00
Ben Jackson
f8cbb7c5b6 Add options to control window sizes
This adds the following options, allowing the default sizes to be
overridden:

- g:vimspector_sidebar_width: Controls the width of the left utility
  windows (variables, watches, stack trace)
- g:vimspector_bottombar_height: Controls the height of the output
  window below the code window

The terminal is typically created as a vertical split of the code
window.  The following control the sizing of the terminal window used
for debuggee input/output when using Vim's built-in terminal.

- g:vimspector_code_minwidth: Minimum number of columns to try and
  maintain for the code window.
- g:vimspector_terminal_maxwidth: Maximum number of columns to use for
  th terminal, when vertically splitting the code window.
- g:vimspector_terminal_minwidth: Minimum number of columns to use when
  it is not possible to fit g:vimspector_terminal_maxwidth columns next
  to the code window with g:vimspector_code_minwidth columns.
2020-07-17 16:52:41 +01:00
Ben Jackson
7a9f75a06e Don't change the value of equalalways
We were trying to avoid equalalways from changing the UI layout by
unsetting it and resetting it after changes. However, re-setting
equalalways actually resizes all the windows, so this never worked.

Instead we judiciously use rightbelow, leftabove, etc. and specify the
exact window sizes we want.

As a side-effect we make the terminal sizing a little more pleasant by
default, ensuring that it is no wider than 80 chars, and tries to use
any remianing vertical space after reserving 80 chars for the code
window.
2020-07-16 16:05:04 +01:00
Ben Jackson
b1fd15c56a Add a test for expanding watches 2020-07-16 08:33:49 +01:00
Ben Jackson
9faa8aa6f7 Fix test timing issue, so it passes on llvm and gdb 2020-07-16 08:33:49 +01:00
Ben Jackson
e884a5f71c run_tests: add --help properly 2020-07-16 08:33:49 +01:00
Ben Jackson
4f0847bcf8 Match the output on linux and mac 2020-07-16 08:33:49 +01:00
Ben Jackson
6d020a50de Fix removing all assert errors, properly this time 2020-07-16 08:33:49 +01:00
Ben Jackson
231720b5b5 Make the messages clearer 2020-07-16 08:33:49 +01:00
Ben Jackson
79a8ad40a4 Use my fork of vint; fix some lint 2020-07-16 08:33:49 +01:00
Ben Jackson
82ec19fff9 Update tocs 2020-07-16 08:33:49 +01:00
Ben Jackson
97f6dd29a6 Add some tests for expand/collapse variables; todo - fails on gdb/linux 2020-07-16 08:33:49 +01:00
Ben Jackson
9df680089b Allow default configuraiton to be specified; document selection 2020-07-16 08:33:49 +01:00
Ben Jackson
db344148ea Example of setting terminal window size sensibly 2020-07-16 08:33:49 +01:00
Ben Jackson
3beb25f949 Raise an autocommand for the terminal too 2020-07-16 08:33:49 +01:00
Ben Jackson
cd796d1500 Just print messages to azure too 2020-07-16 08:33:49 +01:00
Ben Jackson
727214c599 Add a bunch of tests for the ui customisation 2020-07-16 08:33:49 +01:00
Ben Jackson
ebc0b3607a Neovim sigh 2020-07-16 08:33:49 +01:00
Ben Jackson
2c5937c2c1 Support basic UI customisation via a User autocommand 2020-07-16 08:33:49 +01:00
Ben Jackson
de2a924c38 use simpler UI setup commands 2020-07-16 08:33:49 +01:00
mergify[bot]
05b855e605
Merge pull request #206 from puremourning/bugfixes
A number of Bugfixes
2020-07-16 07:28:53 +00:00
Ben Jackson
c6e8e8038f Remove hard-coded terminal width which doesn't work on smaller width screens 2020-07-16 08:14:54 +01:00
Ben Jackson
a4f3fd9c5e Disable relativenumber in utility windows; disable textwidth in neovim too 2020-07-16 08:13:27 +01:00
Ben Jackson
2440a987b2 Tidy unknwon types and values 2020-07-16 08:10:49 +01:00
Ben Jackson
434a6f41d4 Only expand one cheap scope by default - don't keep others expanded from previous frames 2020-07-16 08:10:23 +01:00
Ben Jackson
fc7725fefb Only expand one inexpensive scope; codeLLDB lies about how expensive things are 2020-07-16 08:10:04 +01:00
Ben Jackson
0938d72a8c Re-use a window if we can, as it's more efficient; don't wipe out the generated code buffers in case the code window gets used for temporary buffer switch 2020-07-16 08:09:53 +01:00
Ben Jackson
8d2ea44cb9 Make Restart and Stop work properly (and together) 2020-07-16 08:09:13 +01:00
Ben Jackson
97a5d737ee Ignore venv and test basedir 2020-07-16 08:04:03 +01:00
Ben Jackson
b3c86bc757 Use the original java test code 2020-07-16 08:04:03 +01:00
mergify[bot]
1ae5c6fdcc
Merge pull request #201 from lilwayne1556/master
No longer throws a KeyError when checking for non expensive scope
2020-07-14 19:40:10 +00:00
Wayne Bowie
81fffbe80e Merge branch 'master' of https://github.com/lilwayne1556/vimspector 2020-07-14 14:05:40 -05:00
Wayne Bowie
f81cc126c3 Format fix 2020-07-14 14:04:31 -05:00
Wayne Bowie
4d88dd6eff
Merge branch 'master' into master 2020-07-14 14:01:08 -05:00
Wayne Bowie
f9f0d5a83e No longer throws a KeyError when checking for non expensive scope 2020-07-14 12:23:09 -05:00
mergify[bot]
f383c62542
Merge pull request #200 from lilwayne1556/master
Added Windows support for C# debugging
2020-07-14 15:11:28 +00:00
Wayne Bowie
99518e7eba Added Windows support for C# debugging 2020-07-14 09:22:55 -05:00
mergify[bot]
fe58b94bb0
Merge pull request #197 from puremourning/use-buffers-not-windows
Allow users to close UI windows
2020-07-11 13:50:34 +00:00
Ben Jackson
0de023e3c4 Re-raise errors that aren't expectd 2020-07-11 14:42:35 +01:00
Ben Jackson
7c943c2ed7 Fix the terminal window to 80 columns 2020-07-11 14:08:45 +01:00
Ben Jackson
036c9b9605 Fix neovim not having the same classes as vim 2020-07-11 14:08:29 +01:00
Ben Jackson
193196cc03 Allow users to close the code window and not crash 2020-07-11 14:08:12 +01:00
Ben Jackson
43dd282702 Fix handling data after exit - channels and neovim 2020-07-11 14:07:28 +01:00
Ben Jackson
bdca96f663 Fix up java test 2020-07-11 14:06:32 +01:00
Ben Jackson
b0d41eb347 Scratch buffer still makes sense for the code window buffer where we ask for it from the sevrer 2020-07-11 11:53:39 +01:00
Ben Jackson
cb39e2b511 Allow closing of the UI windows; check valid flags and set buffers to be hidden. delete them on Reset 2020-07-11 11:44:15 +01:00
Ben Jackson
3643c2effd Only respond to data events for the _current_ job, not buffered data for old jobs 2020-07-11 11:43:28 +01:00
Ben Jackson
5ce4147361 Manipulate buffers for variables, watches and stack trace 2020-07-11 10:13:45 +01:00
mergify[bot]
b049ba6d8d
Merge pull request #192 from puremourning/shutdown-remote-job
Fix shutting down jobs when resetting - use the correct category for …
2020-07-11 08:06:31 +00:00
Ben Jackson
6cfc313234 Do a better job of tidying up on test failure; note: We can still receive data callbacks after _OnExit, so just ignore that data 2020-07-10 22:49:30 +01:00
Ben Jackson
8e286be6ee Fix shutting down jobs when resetting - use the correct category for CleanUpCommand 2020-07-09 21:18:27 +01:00
mergify[bot]
915fb29a9f
Merge pull request #196 from puremourning/auto-port
Automatically pick a local port for CodeLLDB
2020-07-09 18:51:53 +00:00
Ben Jackson
9f6caadc40 Pre-calculate the gadgetDir, as this is likely used every time 2020-07-09 18:57:28 +01:00
Ben Jackson
a4abe511c7 Update contents links 2020-07-09 18:29:41 +01:00
Ben Jackson
9baba5afab Document --basedir and ClearBreakpoints() and Exception Breakpoints 2020-07-09 18:19:11 +01:00
Ben Jackson
fe8d7251a4 Add unusedLocalPort to docs 2020-07-09 18:07:58 +01:00
Ben Jackson
a647b65983 Use an unused local port for CodeLLDB 2020-07-09 13:36:52 +01:00
Ben Jackson
81712b124f Fix traceback when (sometimes?) using the watch window 2020-07-09 13:08:15 +01:00
Ben Jackson
044804ca20 Calculate variables on-demand; add an unused-local-port variable-function 2020-07-09 13:07:38 +01:00
mergify[bot]
85667a5bac
Merge pull request #193 from puremourning/variables
[WIP] Add change marker for variables and watches
2020-07-08 15:28:02 +00:00
Ben Jackson
50b8b0103c Fix sequence of scopes/vars to match the server 2020-07-08 16:13:15 +01:00
Ben Jackson
402071065f Update tclpro; add a test 2020-07-08 13:19:14 +01:00
Ben Jackson
e24d0a4659 Simplify extracting the result from a WatchResult 2020-07-08 11:45:20 +01:00
Ben Jackson
8143992345 Restore spacing (don't waste a whole column); re-use one of the indent columns for the marker 2020-07-08 11:37:24 +01:00
Ben Jackson
c43f16e288 FixUp: remove elements in reverse order to avoid invalidating indices 2020-07-07 22:45:12 +01:00
Ben Jackson
4b7fc83ce3 Add a marker when a variable value changes 2020-07-07 22:44:47 +01:00
Ben Jackson
39212f4ffc Add some type hints (requires python 3.6) and update flake8 2020-07-07 22:18:43 +01:00
Ben Jackson
2117a0515d Remove hacky use of dicts and magic keys for variables 2020-07-07 20:22:01 +01:00
mergify[bot]
669b019e25
Merge pull request #190 from puremourning/codelldb
Support running a command and connecting to a port
2020-07-06 13:00:52 +00:00
Ben Jackson
b486839269 Update README TOC and restructure 2020-07-06 11:30:45 +01:00
Ben Jackson
a2ae1c288e Documentation for codelldb/rust 2020-07-06 11:28:24 +01:00
Ben Jackson
0e56146726 Add installer support for CodeLLDB 2020-07-06 11:24:54 +01:00
Ben Jackson
32c7960b5f Support channel-based adapters with jobs in neovim
Again, the neovim API is lacking - we have to hack around our own retry
loop (where vim offers 'waittime').

Note neovim documentation says that it returns 0 on connection failure,
but actually it throws an error, so we catch that. There are probably a
ton of other problems with error handling, but i'll rely on user
testing/feedback for that.
2020-07-06 10:44:14 +01:00
Ben Jackson
99faf76dcc Fix traceback when a request is rejected 2020-07-06 10:38:53 +01:00
Ben Jackson
7848629a1c Add simple rust program tested with CodeLLDB 2020-07-06 10:38:53 +01:00
Ben Jackson
b322a2e89c Allow 'channel' based transports to also start a job 2020-07-06 10:38:53 +01:00
mergify[bot]
596ea65ca6
Merge pull request #191 from puremourning/update-debugpy
UPdate debugpy
2020-07-04 09:52:15 +00:00
Ben Jackson
ec009a1332 UPdate debugpy 2020-07-04 10:33:15 +01:00
Ben Jackson
8e2313bf85
Correct status of windows support 2020-06-28 16:47:54 +01:00
mergify[bot]
df8389380a
Merge pull request #189 from puremourning/windows-cpptools
Windows support for cpptools
2020-06-27 19:29:45 +00:00
Ben Jackson
6f3884253c Windows support for cpptools 2020-06-27 17:28:05 +01:00
mergify[bot]
d1f2df36cc
Merge pull request #184 from JMcKiern/fix-typo
Fix typo in README.md
2020-06-14 19:20:33 +00:00
JMcKiern
de63700bf4
Merge branch 'master' into fix-typo 2020-06-12 21:04:51 +01:00
mergify[bot]
c0c3bd6645
Merge pull request #185 from puremourning/upgrade-debugpy
Upgrade debugpy
2020-06-08 19:04:53 +00:00
Ben Jackson
7f625fee9f Upgrade debugpy 2020-06-08 19:58:52 +01:00
jmckiern
f217a1d9ef Fix typo in README.md 2020-06-06 15:39:25 +01:00
mergify[bot]
3595c8c638
Merge pull request #180 from ray-x/goModuleOff
[#178]Set GO111MODULE to off and the sample project won't fail in go 1.12+
2020-06-04 06:55:27 +00:00
Ray Xu
d6a6184421 set GO111MODULE to off and the debug wont fail in go 1.11+ 2020-06-04 16:39:36 +10:00
Ben Jackson
46e8e4c56b
Note that Huge build of Vim is required 2020-05-28 21:00:30 +01:00
mergify[bot]
a90da6219f
Merge pull request #175 from puremourning/dependabot/bundler/docs/activesupport-6.0.3.1
Bump activesupport from 6.0.2.1 to 6.0.3.1 in /docs
2020-05-26 18:39:23 +00:00
dependabot[bot]
f2994dfad7
Bump activesupport from 6.0.2.1 to 6.0.3.1 in /docs
Bumps [activesupport](https://github.com/rails/rails) from 6.0.2.1 to 6.0.3.1.
- [Release notes](https://github.com/rails/rails/releases)
- [Changelog](https://github.com/rails/rails/blob/v6.0.3.1/activesupport/CHANGELOG.md)
- [Commits](https://github.com/rails/rails/compare/v6.0.2.1...v6.0.3.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-05-26 18:05:00 +00:00
mergify[bot]
5faf56de9d
Merge pull request #174 from puremourning/neovim
Fix traceback in neovim: vim.vars returns str
2020-05-24 16:10:38 +00:00
Ben Jackson
7705d6020c Fix traceback in neovim: vim.vars returns str 2020-05-24 13:17:36 +01:00
mergify[bot]
2b22d3d29f
Merge pull request #172 from puremourning/python-subprocess
Disable subprocess debugging by default in debugpy
2020-05-24 11:30:59 +00:00
mergify[bot]
435e33e23e
Merge branch 'master' into python-subprocess 2020-05-24 11:25:10 +00:00
mergify[bot]
71ad4c2022
Merge pull request #170 from awwalker/aw-docker
add docker exec as an attach command
2020-05-24 11:21:29 +00:00
Aaron Walker
ae2ba01c5b lint 2020-05-21 23:43:37 -04:00
Aaron Walker
3d113eaec4 comments 2020-05-21 23:43:37 -04:00
Aaron Walker
cd1b304d30 relint 2020-05-21 23:43:37 -04:00
Aaron Walker
5e64b07e8d flake and update docker exec command 2020-05-21 23:43:37 -04:00
Aaron Walker
0bf511debc add docker docs 2020-05-21 23:43:37 -04:00
Aaron Walker
62b3070c50 lint 2020-05-21 23:43:37 -04:00
Aaron Walker
c9b1456759 add docker exec as an attach command 2020-05-21 23:43:37 -04:00
Ben Jackson
2437aace41 Disable subprocess debugging by default in debugpy 2020-05-19 21:53:20 +01:00
mergify[bot]
b80c483284
Merge pull request #169 from puremourning/breakpoints-clear
Fix listing breakpoints while debugging
2020-05-19 19:38:19 +00:00
Ben Jackson
5f11fe4e6e Fix up vint errors 2020-05-19 20:27:28 +01:00
Ben Jackson
58a95402c4 Use gloabl for test args as i always use a basedir now 2020-05-17 21:41:24 +01:00
Ben Jackson
8c4112cd1f breakpoints list: fix listing while debugging
Also, open the quickfix list when listing breakpoints, and add a test
2020-05-17 21:40:51 +01:00
Ben Jackson
6a1c9a6b52 Enable embedded vim python debugging
This allows us to use vimspector to debug vimspector. The idea is that
in Vim you call 'py3 from vimspector.developer import SetUpDebugpy;
SetUpDebugpy()' and then just attach to localhost:5678 using the new
multi-session mode support. Oof.
2020-05-17 19:17:09 +01:00
mergify[bot]
060a9f4597
Merge pull request #166 from puremourning/debugpy-remote
Debugpy remote debugging and install issues
2020-05-17 13:16:03 +00:00
Ben Jackson
90bbbe90f5 Default some cpptools args: 2020-05-17 14:07:56 +01:00
Ben Jackson
c6d6c7f876 Support "multi-session" mode properly
Document remote debugging and add a test
2020-05-17 14:07:56 +01:00
Ben Jackson
36253e58ce Optionally Update gadget config rather than repolace 2020-05-16 22:55:01 +01:00
Ben Jackson
dc3deadb26 Use java 11 2020-05-16 22:55:01 +01:00
Ben Jackson
dd13c7d543 Move directories if they cannot be deleted 2020-05-16 22:55:01 +01:00
mergify[bot]
4946e95d4c
Merge pull request #167 from esetnik/patch-1
Fix incorrect naming of `.viminspector.json`
2020-05-15 15:48:58 +00:00
Ethan Setnik
62c1fe79da
Fix incorrect naming of .viminspector.json
I got tripped up getting started because the documentation incorrectly uses `vimspector.json` and I was getting an error about no debug configurations found.
2020-05-15 11:32:09 -04:00
Ben Jackson
0452329203 Update java debugger plugin 2020-05-13 17:18:57 +01:00
mergify[bot]
4c96655526
Merge pull request #160 from puremourning/basedir
Allow moving the gadget/configuration dirs to arbitrary location
2020-05-12 19:02:05 +00:00
Ben Jackson
ae0ba29d5e
Merge branch 'master' into basedir 2020-05-12 19:48:46 +01:00
mergify[bot]
dd101aedca
Merge pull request #164 from puremourning/threads-fail
Make sure we can still request threads if one threads request fails
2020-05-12 18:40:56 +00:00
Ben Jackson
fd6b353782 Reset the tab test more robustly 2020-05-12 19:39:32 +01:00
Ben Jackson
5837135fee Allow moving the gadget/configuration dirs to arbitrary location 2020-05-12 19:39:32 +01:00
Ben Jackson
c103e2adf5 Make sure we can still request threads if one threads request fails 2020-05-12 19:30:00 +01:00
mergify[bot]
fa0171f464
Merge pull request #162 from eyalk5/patch-1
Update FAQ
2020-05-10 07:21:47 +00:00
eyalk5
ce6ad5d1ca
Update FAQ
Added question about $file
2020-05-10 02:42:02 +03:00
Ben Jackson
2451d56de4 Make tests work in manual container: install delve. This has to be done as the user because go 2020-05-08 13:11:17 +01:00
Ben Jackson
abd8ba7d38 Stabilise some tests 2020-05-08 11:50:38 +01:00
Ben Jackson
bdfa0f92ab Fix typo 2020-05-07 22:33:01 +01:00
Ben Jackson
3354db275b Update TOC 2020-05-07 22:32:20 +01:00
Ben Jackson
ccf427a7c8 Update java instructions now YCM has nice API for it 2020-05-07 22:28:31 +01:00
mergify[bot]
3bf461c621
Merge pull request #157 from puremourning/update-debugpy
Update debugpy to v1.0.0b8
2020-04-28 22:02:05 +00:00
Ben Jackson
56c0c721db Update debugpy to v1.0.0b8 2020-04-28 22:55:42 +01:00
mergify[bot]
16e1ae143b
Merge pull request #156 from puremourning/watch-improve
Improve the watch window with a WinBar
2020-04-27 20:52:42 +00:00
Ben Jackson
a87c9e8715 Add motivation - people buy why not what 2020-04-27 20:59:12 +01:00
Ben Jackson
4586aa36ff Improve UI for watch window
Add a WinBar as the whole prompt buffer thing is not that well
undersood. Also allow you to delete a watch from any part of the
expression result too.
2020-04-26 21:31:22 +01:00
Ben Jackson
80ba644732 Use silent for mappings 2020-04-26 20:03:33 +01:00
mergify[bot]
f4d855e1df
Merge pull request #155 from puremourning/conditional-breakpoints
Conditional breakpoints tests
2020-04-26 13:09:11 +00:00
Ben Jackson
77522cbd44 Add way to run tests using my in-development vim debugger 2020-04-26 13:08:27 +01:00
Ben Jackson
77dc400077 test and doc updates for conditional breakpoints
use a better description of hit condition and describe the options dict.
2020-04-26 13:04:51 +01:00
Ben Jackson
e153b9536e No longer need node 10 for the tests since debugpy 2020-04-26 11:34:03 +01:00
Ben Jackson
c25e9256aa Fix restart command in neovim when terminal in use
Neovim doesn't allow you to replace a terminal buffer if the buffer has
received some output, so we tell it that the buffer is not modified as a
hackaround.

Fixes #154
2020-04-26 10:21:24 +01:00
mergify[bot]
1b71d84f58
Merge pull request #153 from puremourning/conditional-breakpoints
Add basic support for conditional breakpoints
2020-04-26 06:44:57 +00:00
Ben Jackson
b20f61bcff Sigh, neovim doesn't support default method arguments 2020-04-25 20:07:03 +01:00
Ben Jackson
f6d12eff72 Few more minor doc updates 2020-04-25 19:57:53 +01:00
Ben Jackson
7a70519b03 Add basic support for conditional breakpoints
This is the minimal required for a user to use conditional breakpoint -
we add an options dict to each breakpoint (line and function) and allow
the condition to be supplied. We add a plug mapping and a default
shortcut (<leader><F9>) to add one where we ask the user to enter the
condition and hit expression. This isn't great but it works.

We don't check the capabilities, so they would just be ignored if used
on a server that doesn't support them. We also ask for a hit expression
which most users won't understand so this isn't ideal either.

No tests yet.
2020-04-25 19:57:42 +01:00
Ben Jackson
aed4144fe5 Update bashdb checkusm 2020-04-15 16:13:19 +01:00
Ben Jackson
f0ee397f5d Update bashdb 2020-04-15 13:15:33 +01:00
mergify[bot]
31c13bd8eb
Merge pull request #150 from puremourning/troubleshooting
Add troubleshooting guide
2020-04-05 14:16:39 +00:00
Ben Jackson
d8d7a3f7c6
Merge branch 'master' into troubleshooting 2020-04-05 15:11:54 +01:00
mergify[bot]
b3760f43f9
Merge pull request #151 from puremourning/update-debugpy
Update debugpy
2020-04-05 14:11:03 +00:00
Ben Jackson
980b80ebbb Add troubleshooting guide
Also make sure that the gadget installer and other docs are included in
the tarball installation.
2020-04-05 15:06:41 +01:00
Ben Jackson
a5a2966ba7 Update debugpy 2020-04-05 15:03:35 +01:00
mergify[bot]
c3105cf912
Merge pull request #148 from puremourning/cpptools-update
Remove workaround for broken cpptools
2020-04-03 20:11:59 +00:00
Ben Jackson
be26098315 Remove workaround for broken cpptools 2020-04-03 21:05:34 +01:00
mergify[bot]
0918a72dd9
Merge pull request #147 from neiljohari/upgrade-cpptools
Bump version of vscode-cpptools from 0.26.3 to 0.27.0
2020-04-03 19:48:33 +00:00
Neil Johari
db6d9efb75
Bump version of vscode-cpptools from 0.26.3 to 0.27.0 2020-04-02 14:28:32 -04:00
mergify[bot]
7e29143a6f
Merge pull request #142 from puremourning/tweaks
Tweaks
2020-04-01 16:03:27 +00:00
Ben Jackson
93edbe468b Add autoselect to configs - set to false to prevent being used when no other configs are defined 2020-04-01 16:46:06 +01:00
Ben Jackson
1b7d0caf26 Ignore loadedSource event 2020-04-01 16:44:51 +01:00
Ben Jackson
0e4cad302d Don't terminal debuggee 2020-04-01 16:44:51 +01:00
Ben Jackson
d0652187a3
Note what diagnostics are required, 2020-03-31 13:33:58 +01:00
Ben Jackson
ae7e6d0b0c Clarify that Python 3.6 is required
closes #143
2020-03-28 20:28:29 +00:00
Ben Jackson
5cb9c62e7f Fix tab creation when current buffer contains no file 2020-03-28 13:07:00 +00:00
mergify[bot]
0a0cd88545
Merge pull request #139 from puremourning/no-name
Don't create a new buffer when opening the vimspector tab
2020-03-28 10:19:25 +00:00
Ben Jackson
59f23c7545
Merge branch 'master' into no-name 2020-03-27 15:09:02 +00:00
mergify[bot]
87e80db94d
Merge pull request #140 from puremourning/loading
Add a splash popup while starting up
2020-03-26 23:31:13 +00:00
Ben Jackson
1003cdc0b2 Add a splash popup while starting up
Sometimes it can take quite a while to start up and initialise the debug
adapter. So we use popup/float to display the status as we start up and
shut down.

This increases minimum Vim version to 8.2, but that's been out for ages
now and I intend to agressively require latest/later vim/neovim
versions.
2020-03-26 23:25:50 +00:00
Ben Jackson
8664c0ad78 Don't create a new buffer when opening the vimspector tab 2020-03-26 21:13:04 +00:00
Ben Jackson
03d97dc58f
Merge pull request #136 from puremourning/json-comments
Support c-style comments in JSON
2020-03-23 11:06:21 +00:00
Ben Jackson
5d404708cf Fix CI on linux
Don't use lldb-mi as the MIDebugger when we're in gdb mode (it's on the
PATH because llvm is installed)
2020-03-21 17:01:15 +00:00
Ben Jackson
d42fd51f35 Support c-style comments in JSON
Debate rages about whether JSON should have comments. The specification
says it shouldn't but the author of JSON suggested that if you want to
use JSON for configuration, then pipe it through jsmin before parsing.

So that's what we do, using a tiny JSON minifier from
https://github.com/getify/JSON.minify/tree/python

Closes #135
2020-03-21 15:16:07 +00:00
Ben Jackson
9393c1d80e Don't run under sudo 2020-03-21 14:04:48 +00:00
mergify[bot]
36fbe5bba5
Merge pull request #134 from puremourning/docs-update
Document: splat, central config dirs
2020-03-18 08:04:49 +00:00
Ben Jackson
a5765b153c Azure: Set explicit triggers (https://status.dev.azure.com/_event/179641421) 2020-03-14 16:46:53 +00:00
Ben Jackson
ec36099f97 Document: splat, central config dirs 2020-03-14 09:26:49 +00:00
Ben Jackson
de04598bc6 Clear exception breakpoints when calling ClearBreakpoints 2020-03-09 21:31:14 +00:00
Ben Jackson
6c8657c9a7 Guess VIMSPECTOR_MI_DEBUGGER path 2020-03-09 21:25:55 +00:00
Ben Jackson
c1283a292c When creating a terminal window in neovim, use a new buffer
neovim's termopen() replaces the buffer object in the current window
with a terminal, internally, this completely breaks the buffer list.

Repro:
* Create test.vim:

```
vsplit
call termopen( '/bin/bash', { 'cwd': getcwd() } )
call bufload( expand( '<sfile>' ) )
```

Then:
* `nvim -Nu NONE test.vim`
* `:source %`

Error is "Invald buffer name 'test.vim'"

Anyway, the correct thing to do is to create a _new_ buffer before
making it into a terminal (vnew, new) rather than a split of the current
one. This was only working before because the CodeView window never had
any buffer in it and was broken by the change to use the current buffer
when staring debugging.

Fixes #131
2020-03-09 20:52:34 +00:00
Ben Jackson
1b7402915c Catch adapter being None and print user-friendly message: 2020-03-08 18:18:29 +00:00
Ben Jackson
cd26e81fd9 When requesting source from debugger, use the path, if supplied 2020-02-23 16:12:35 +00:00
mergify[bot]
b7dee6410a
Merge pull request #125 from puremourning/win-setup
Set the code window to the current buffer
2020-02-16 23:11:25 +00:00
Ben Jackson
27e11d5265
Merge branch 'master' into win-setup 2020-02-16 23:06:49 +00:00
mergify[bot]
7da627f95e
Merge pull request #124 from puremourning/list-splat
List splat
2020-02-16 23:03:50 +00:00
Ben Jackson
2c1060c49e Support *\$var in lists to splat (expand in place) shell-split user input. Useful for commandline-> args list 2020-02-16 22:59:15 +00:00
Ben Jackson
e61e7fefe6
Merge branch 'master' into win-setup 2020-02-16 22:52:46 +00:00
Ben Jackson
c4bedcee89 Set the code window to the current buffer 2020-02-16 22:49:15 +00:00
mergify[bot]
5e8d4f97c4
Merge pull request #123 from puremourning/win-setup
Set some window options to their deafults
2020-02-16 22:35:40 +00:00
Ben Jackson
926029e343 Set some window options to their deafults 2020-02-16 22:30:55 +00:00
mergify[bot]
c8dcc47b1c
Merge pull request #121 from puremourning/windows
Experimental Windows support
2020-02-16 21:11:26 +00:00
Ben Jackson
a74783513c Update README 2020-02-16 20:21:17 +00:00
Ben Jackson
0aba8e0179 Work around neovim bug where environ() crashes 2020-02-16 20:15:41 +00:00
Ben Jackson
3366e1c784 Use the python used to run the installer to run debugpy 2020-02-16 20:15:41 +00:00
Ben Jackson
4a7e3b9229 Fix opening paths on Windows 2020-02-16 20:15:41 +00:00
Ben Jackson
37fefafe35 Fix tracebacks when the debug adapter dies very quickly 2020-02-16 20:15:41 +00:00
mergify[bot]
866e6fe9b1
Merge pull request #116 from puremourning/filetype-configs
Support per-filetype global launch configurations, and support trivial custom gadget installation
2020-02-16 20:05:48 +00:00
Ben Jackson
68a45b5771 Fix workspace root when no vimspector conf - use dir of current file 2020-02-16 20:01:00 +00:00
Ben Jackson
267f202dad Fix LGTM error: python default arguments are mutable\! 2020-02-16 19:45:58 +00:00
Ben Jackson
e37ef18c28 Move installation routines into proper library 2020-02-16 17:31:00 +00:00
Ben Jackson
be32a0a1a8 Add --enable-custom to load custom gadget meta 2020-02-16 17:23:49 +00:00
Ben Jackson
583fb95ea0 Read configurations from a per-filetype directory or a default location as well as local config file 2020-02-16 17:22:53 +00:00
mergify[bot]
86afe89d63
Merge pull request #119 from puremourning/drop-py2
Drop python 2 support in installer
2020-02-15 11:58:12 +00:00
Ben Jackson
6b735ce4d0 Drop python 2 support in installer 2020-02-15 11:30:42 +00:00
mergify[bot]
79df51e83c Merge pull request #115 from puremourning/debugpy
Switch to debugpy rather than vscode-python
2020-02-08 21:23:01 +00:00
Ben Jackson
dcabc52937 Update readme 2020-02-08 21:15:27 +00:00
Ben Jackson
8052484cc7 Use YCM python style 2020-02-08 21:15:27 +00:00
Ben Jackson
a56bee7b0a Switch to debugpy over vscode-python
This is just better in every way, and the vscode-python typescript
adapter is being phased out.
2020-02-08 21:15:27 +00:00
Ben Jackson
933f946801 Fix CI - use latest lldb-mi 2020-02-08 21:15:27 +00:00
mergify[bot]
6fd8d16487
Merge pull request #114 from noscript/readme_typo
README referenced link typo
2020-02-05 15:13:15 +00:00
Sergey Vlasov
a90a529ced
README referenced link typo 2020-02-05 16:55:20 +02:00
mergify[bot]
ac56e3adef
Merge pull request #112 from puremourning/syntax-stack
Set syntax in stack trace too
2020-02-04 20:19:13 +00:00
Ben Jackson
19cc58f09e Set syntax in stack trace too 2020-02-04 13:04:09 +00:00
Ben Jackson
db95fe0c1d Update github pages to fix website, maybe 2020-02-02 18:27:01 +00:00
mergify[bot]
8c01ea7bfd
Merge pull request #110 from puremourning/neovim-env
Support setting environment in neovim
2020-02-02 17:44:00 +00:00
Ben Jackson
6878c80cfb Update readme for environment hackaround 2020-02-02 17:37:53 +00:00
Ben Jackson
896b20f490 Hackaround for missing 'env' in termopen and jobstart in neovim 2020-02-02 17:37:53 +00:00
mergify[bot]
fb4411676c
Merge pull request #108 from puremourning/debugpy
Document how to use debugpy directly instead of vscode-python
2020-02-01 00:28:47 +00:00
Ben Jackson
3f3ede48ba Update TOC 2020-02-01 00:09:02 +00:00
Ben Jackson
44efc0a1b3 Neovim 0.4 doesn't work for debugpy because it is missing features 2020-02-01 00:09:02 +00:00
Ben Jackson
6bcc58a39b Document how to use debugpy directly instead of vscode-python 2020-02-01 00:09:02 +00:00
mergify[bot]
9485a9f46c
Merge pull request #103 from axrt/no_ssl
Add a --no-ssl flag that switches off certificate verification
2020-01-31 23:26:22 +00:00
Ben Jackson
5eb042be14
Merge branch 'master' into no_ssl 2020-01-31 23:20:28 +00:00
Alexander Tuzhikov
7f3d7a46e4 Address cl requests in pr-103; 2020-01-31 08:50:06 -08:00
mergify[bot]
9c18f3e802
Merge pull request #107 from puremourning/fixups
Improve workflow for launching vimspector from scripts
2020-01-31 16:19:06 +00:00
Ben Jackson
018ae05de8 Add 'example' allowing simple command-line attachment 2020-01-31 16:11:36 +00:00
Ben Jackson
74eb94fe53 Allow PID to be set by variables, e.g. shell 2020-01-31 16:07:26 +00:00
Ben Jackson
8d4ec3f53a Supress errors in more appropriate places 2020-01-31 16:07:26 +00:00
Ben Jackson
309c292413 Store variables supplied _before_ parsing adapter/config varibales 2020-01-31 16:07:26 +00:00
Ben Jackson
c1851a38e5
Merge pull request #104 from adelarsq/patch-2
Spell correction on README
2020-01-30 16:33:59 +00:00
Adelar da Silva Queiróz
96a7083f29
Spell correction 2020-01-29 00:16:13 -03:00
Alexander Tuzhikov
bd794526b7 Add a --no-ssl flag that switches off certificate verification 2020-01-28 16:11:21 -08:00
mergify[bot]
4e1f14261b
Merge pull request #102 from puremourning/import-vim
Import vim
2020-01-28 20:59:48 +00:00
Ben Jackson
ab56d2cef4 Import the vim module
In Vim, the vim module is always imported by magic in the global scope,
but the docs suggest that you're supposed to import it anyway.

In NeoVim it's never imported so we were relying on some other plugins
having already imported it.
2020-01-28 20:52:33 +00:00
Ben Jackson
b915ada0c8 Update issue template a bit more for neovim 2020-01-28 20:52:33 +00:00
mergify[bot]
e26fc6bd8c
Merge pull request #99 from puremourning/readme-launch-settings
Document LaunchWithSettings
2020-01-28 09:05:01 +00:00
Ben Jackson
3d4ce6d7d4
Merge branch 'master' into readme-launch-settings 2020-01-28 08:57:00 +00:00
mergify[bot]
d6994c2ba6
Merge pull request #98 from puremourning/lldb-mi
Use my lldb-mi build
2020-01-28 08:56:31 +00:00
Ben Jackson
23b3bb719b Document exception breakpints 2020-01-28 08:52:23 +00:00
Ben Jackson
8253c1687b Document LaunchWithSettings
This is useful enough to be made public. Closes #97.
2020-01-28 08:52:23 +00:00
Ben Jackson
40bec48e97 Use my lldb-mi build 2020-01-27 22:14:30 +00:00
mergify[bot]
c6c0144f85
Merge pull request #91 from puremourning/macos-10.14
Switch to macOS 10.14 in CI (10.13 will be disabled soon)
2020-01-27 21:46:51 +00:00
Ben Jackson
ea1a56ed0d Update checksums in install_gadget.py 2020-01-27 21:36:21 +00:00
Ben Jackson
1da905a955 Upgrade to macOS 10.14
In Mojave, the TCL headers were removed, so we install brew tcl.

Oh and the all of the nonesense around authorizing applications to
connect a debugger mean the tests don't work, so we force LLDB to use
the system debugserver with LLDB_DEBUGSERVER_PATH.
2020-01-27 21:35:15 +00:00
mergify[bot]
04aaafd69a
Merge pull request #89 from panders23/add-php-debug-adapter
add debug adapter for php (based on felixfbecker/vscode-php-debug)
2020-01-27 20:01:46 +00:00
panders23
da0fc65333 Mention xdebug helper extension for php 2020-01-27 20:35:26 +01:00
panders23
eda6b890de add readme.md entry for php 2020-01-27 20:26:26 +01:00
panders23
c6e192d5f5 add felixfbecker/vscode-php-debug 2020-01-27 20:22:39 +01:00
Ben Jackson
7903e0e0fe
Add screenshot to README 2020-01-26 23:23:12 +00:00
mergify[bot]
41c5093568
Merge pull request #94 from puremourning/customise-signs
Add customisation of signs
2020-01-26 23:08:29 +00:00
Ben Jackson
b8d2b548d8 Add customisation of signs 2020-01-26 23:02:26 +00:00
Ben Jackson
9b2710dd47 Update README.me to have languages in TOC 2020-01-26 21:42:31 +00:00
mergify[bot]
239b538bfc
Merge pull request #93 from puremourning/upgrade-cpptools
upgrade cpptools to 0.26.3
2020-01-26 21:37:22 +00:00
Ben Jackson
bd820b2a8b upgrade cpptools to 0.26.3
This sort-of fixes macOS issues when used with my lldb-mi.
2020-01-26 21:32:55 +00:00
Ben Jackson
a524762f98 Fix link in contrinbuting guide 2020-01-26 21:08:02 +00:00
Ben Jackson
8a4a8b39da Add contributing.md 2020-01-26 21:04:17 +00:00
Ben Jackson
9c3813f11b Add code of conduct 2020-01-26 20:05:50 +00:00
Ben Jackson
ffe5209ea6 Correct template for neovim support 2020-01-26 20:04:45 +00:00
Ben Jackson
94f19459b9 Update TOC 2020-01-26 17:49:20 +00:00
Ben Jackson
d69e63719b Elaborate neovim deficiencies 2020-01-26 17:42:03 +00:00
Ben Jackson
9b5960f81e Fix go language server returning 0 column causing errors in neovim 2020-01-22 21:35:06 +00:00
Ben Jackson
287286cadb Add way to install a different java debugger, though it doens't work well 2020-01-21 22:25:11 +00:00
mergify[bot]
42d4b80200
Merge pull request #77 from puremourning/neovim
Support neovim
2020-01-19 20:40:08 +00:00
Ben Jackson
9e3d7237a5 Update README 2020-01-19 20:33:24 +00:00
mergify[bot]
4318e567f2
Merge pull request #77 from puremourning/neovim
Support neovim
2020-01-19 20:21:56 +00:00
Ben Jackson
5ee8ffc3f6 Catch E325 in neovim 2020-01-19 20:16:52 +00:00
Ben Jackson
6cf4f9f9ab FixUp: Some comments 2020-01-19 20:07:18 +00:00
Ben Jackson
ef4a6fc10c FixUp: use normal! to avoid user mappings, thanks vint! 2020-01-19 10:28:34 +00:00
Ben Jackson
7ca51f8f08 FixUp: Node <10 not node=10 2020-01-19 10:23:05 +00:00
Ben Jackson
098cd0929b
Merge branch 'master' into neovim 2020-01-19 08:33:25 +00:00
mergify[bot]
b2b08530db
Merge pull request #87 from puremourning/upgrade-bashdb
Upgrade bashdb and add more useful defaults
2020-01-17 18:01:08 +00:00
Ben Jackson
df759ec9dc Upgrade bashdb and add more useful defaults 2020-01-17 15:28:44 +00:00
Ben Jackson
355f0f0e0c Implement command line completion for watch/eval 2020-01-17 00:02:24 +00:00
Ben Jackson
1e8bd384b9 FixUp: importing ycm test lib changs' 2020-01-15 23:23:22 +00:00
Ben Jackson
099ffc2af9 Disable swap files in the neovim buffers too 2020-01-15 23:17:15 +00:00
Ben Jackson
c2b0cc8f18 Attempt to make the output windows a little less rubbish on neovim 2020-01-15 23:17:15 +00:00
Ben Jackson
2252a2bf02 Delete the watches and vars buffers when resetting 2020-01-15 23:17:15 +00:00
Ben Jackson
3b97500289 Make sure the buffer is modifiable when updating the console 2020-01-15 23:17:15 +00:00
Ben Jackson
f6eccf1314 More: Import latest YCM test framework changes 2020-01-15 23:17:15 +00:00
Ben Jackson
d711d655aa FixUp: Brokern StopOver mapping 2020-01-15 23:17:15 +00:00
Ben Jackson
6ee8f7875d Tests: Import test framework changs from YCM 2020-01-15 23:17:15 +00:00
Ben Jackson
149d48b688 Scroll the output buffer when visible 2020-01-15 23:17:15 +00:00
Ben Jackson
c898eb47b2 Fix filtering VimspectorShowOutput 2020-01-15 23:17:15 +00:00
Ben Jackson
a00e6a19d6 Fix output buffers. bufnr creates an _unloaded_ buffer, so we have to load it 2020-01-15 23:17:15 +00:00
Ben Jackson
7456c26c2a Add some :commands for basic usages 2020-01-15 23:17:15 +00:00
Ben Jackson
04bb03da0a Add channel support for neovim 2020-01-15 23:17:15 +00:00
Ben Jackson
29c26996fb Add neovim job/terminal APIs 2020-01-15 23:17:15 +00:00
Ben Jackson
18627b9244 Add a way to have multiple vim API layers 2020-01-15 23:17:15 +00:00
Ben Jackson
5aa33c19f7 Remove unused ForceRead function 2020-01-15 23:17:15 +00:00
Ben Jackson
ee1bb009ea Wrap the terminal API in vimscript layer 2020-01-15 23:17:15 +00:00
Ben Jackson
d1e3b648d3 Use eval for lists of buffers too 2020-01-15 23:17:15 +00:00
Ben Jackson
dcdab63516 Remove use of bindeval() as it is not suported in neovim 2020-01-15 23:17:15 +00:00
mergify[bot]
cb2e08b850
Merge pull request #85 from tinmarino/merge_check_connection
Check connection before some operation (`next`) to avoid Python errors in vim messages
2020-01-15 21:49:38 +00:00
tinmarino
9be0f43a5f Feature: Silent Errors if not connected 2020-01-15 10:59:17 -03:00
mergify[bot]
1277ec6347
Merge pull request #84 from puremourning/fid-upstream
A selection of misc fixes and enhancements from ongoing usage
2020-01-10 12:44:49 +00:00
Ben Jackson
9f0becab7b Flake8 fixes 2020-01-10 12:19:58 +00:00
Ben Jackson
8a57104a0e Expand references in non-shell variables too 2020-01-10 09:28:02 +00:00
Ben Jackson
bbaab0ebc1 Fix true/false in JSON when setting up adapter 2020-01-10 09:28:02 +00:00
Ben Jackson
55bb50d296 line sometiems not supplied; source is optional 2020-01-10 09:28:02 +00:00
Ben Jackson
8bb7017a49 Handle failed breakpoint messages 2020-01-10 09:28:02 +00:00
Ben Jackson
d950352545 Install vscode java debugger somewhere 2020-01-10 09:28:01 +00:00
Ben Jackson
2c347c7920 Enalbe snippets for json 2020-01-10 09:26:13 +00:00
Ben Jackson
b95fe20845 Frame is not reqiured for evaluation 2020-01-10 09:26:13 +00:00
Ben Jackson
8d7de7172a Allow arbitrary ssh args 2020-01-10 09:26:13 +00:00
mergify[bot]
bda1bb05fd
Merge pull request #80 from tinmarino/merge_ballon
Fix: Remove balloon '...' in windows != code_window
2020-01-10 07:41:22 +00:00
tinmarino
1e153910fa Fix: Remove balloon '...' in windows != code_window
Problem: In non-code window, user see a '...' balloon even if python
knows it does not need to work
Solution: use pyeval in vim so that python's knowledge is getting back
from the stack as return value
2020-01-09 20:37:03 -03:00
mergify[bot]
12b6c925dc
Merge pull request #79 from puremourning/fix-install-node
Fix node dap installation when run outside of a git repo
2020-01-09 17:24:54 +00:00
Ben Jackson
05d377217d Run git commands in the right repo 2020-01-09 17:19:35 +00:00
Ben Jackson
68c47edabb
Merge pull request #75 from orlangure/add-reset-info
Document closing the debugger
2020-01-07 16:58:11 +00:00
mergify[bot]
fec355cab4
Merge branch 'master' into add-reset-info 2020-01-07 13:25:41 +00:00
mergify[bot]
abb3c6b387
Merge pull request #76 from puremourning/readme-deps
Make statement about neovim support
2020-01-07 13:23:27 +00:00
yury
c8334dc30c Fix Watches link in readme 2020-01-07 15:20:10 +02:00
yury
0ecc99a75c Add instruction on how to close the debugger
Omit `Reset` mapping
2020-01-07 15:20:04 +02:00
Ben Jackson
a74b9f2c65
Make statement about neovim support
Currently unsupported for technical reasons, not philosophical ones.
2020-01-07 13:12:06 +00:00
yury
c8a99831d6 Remove colon for consistency 2020-01-07 11:59:44 +02:00
Ben Jackson
144efab83d Add workaround for broken cpptools on macOS 2020-01-01 15:19:13 +00:00
Ben Jackson
e2738de64e Update vscode-python 2019-12-30 20:33:37 +00:00
Ben Jackson
6944cb7c1f Fix tracebnack when no source in stack frame 2019-12-22 21:33:16 +00:00
mergify[bot]
415a601963
Merge pull request #73 from puremourning/syntax
Syntax highlighting for locals and watches
2019-12-15 10:53:11 +00:00
Ben Jackson
ed6beff03b Enable syntax highlighting for watches and locals 2019-12-15 10:48:21 +00:00
Ben Jackson
94b8f0a81d Update cpptools 2019-12-15 09:16:04 +00:00
mergify[bot]
c02e308d4f
Merge pull request #72 from puremourning/breakpoints-move
Fix issues with breakpoints not being in the correct location
2019-12-15 09:01:18 +00:00
Ben Jackson
74ef72b150 which -s is mac only 2019-12-14 18:03:53 +00:00
Ben Jackson
7d046574cb Add tests for toggling breakpoint and inserting line 2019-12-14 18:03:34 +00:00
Ben Jackson
16c3b65cae If signs are moved by user actions, use the current lnum of the sign for the breakpoint. 2019-12-14 15:51:04 +00:00
Ben Jackson
8d025c475d Remove old jekyll config 2019-11-06 15:55:33 +00:00
Ben Jackson
bed68aa691 Fix up the site 2019-11-06 15:53:03 +00:00
Ben Jackson
ccc966f985 Fix formatting errors 2019-11-06 12:14:43 +00:00
Ben Jackson
6791b4bb58
Rename CONFIGURATION.md to configuration.md 2019-11-06 12:13:51 +00:00
Ben Jackson
28a02617ee Set theme jekyll-theme-tactile 2019-11-06 12:07:20 +00:00
mergify[bot]
105e6d4460
Merge pull request #70 from puremourning/working-dir
[READY]: Document config format and add some usability improvements
2019-11-06 11:58:36 +00:00
Ben Jackson
7297e12605 Add schema definition for the config 2019-11-06 11:44:23 +00:00
Ben Jackson
25b22d2a9e Allow gadget config to set up default values for launch configuration 2019-11-05 19:02:17 +00:00
Ben Jackson
0057094179 Allow gadget config in .gadgets.d to override the installed config 2019-11-05 19:02:17 +00:00
Ben Jackson
5586d9e694 Allow setting exception breakpoints in debug config 2019-11-05 18:35:36 +00:00
Ben Jackson
f173a043be Find the vimspector config from the parent of the open file not the current working directory 2019-11-05 18:35:23 +00:00
168 changed files with 21489 additions and 3633 deletions

View file

@ -7,52 +7,108 @@ 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 not supported.
NOTE: Windows is not supported.
> Please include:
> * Vimspector log (~/.vimspector.log)
> * Output from any or all UI diagnostic tabs (Server, etc.)
* Output of `vim --version`
### Environemnt
***NOTE***: NeoVim is supported only on a best-effort basis. Please check the README
for limitations of neovim. Don't be offended if I ask you to reproduce issues in
Vim.
***NOTE***: Windows support is experimental and best-efrort only. If you find an
issue related to Windows or windows-isms, consider sending a PR or
discussing on Gitter rather than raising an issue.
* Version of Vimspector: (e.g. output of `git rev-parse HEAD` if cloned or the
name of the tarball used to install otherwise)
* Output of `:VimspectorDebugInfo`
```
paste here
```
* Output of `which vim`:
* Output of `vim --version` or `nvim --version`
```
paste here
```
* Output of `:py3 pass`:
* Output of `which vim` or `which nvim`:
```
paste here
```
* Output of `:py3 print( __import__( 'sys' ).version )`:
```
paste here
```
* Output of `:py3 import vim`:
```
paste here
```
* Output of `:py3 import vimspector`:
```
paste here
```
* For neovim: output of `:checkhealth`
```
paste here
```
* Operating system: <linux or macOS> and version
### Declaration
> Please complete the following declaration. If this declaration is not completed, your issue may be closed without comment.
* I have read and understood [CONTRIBUTING.md](https://github.com/puremourning/vimspector/blob/master/CONTRIBUTING.md) \[Yes/No]
### Additional information
**Additional context**
Add any other context about the problem here.

12
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View 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
View 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
View file

@ -0,0 +1,27 @@
name: "Lock Old Issues"
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2
with:
github-token: ${{ github.token }}
issue-lock-inactive-days: '60'
# issue-exclude-created-before: ''
# issue-exclude-labels: ''
# issue-lock-labels: ''
# issue-lock-comment: ''
# issue-lock-reason: 'resolved'
# pr-lock-inactive-days: '365'
# pr-exclude-created-before: ''
# pr-exclude-labels: ''
# pr-lock-labels: ''
# pr-lock-comment: ''
# pr-lock-reason: 'resolved'
process-only: 'issues'

6
.gitignore vendored
View file

@ -6,6 +6,7 @@ tests/*.res
tests/messages
tests/debuglog
test.log
*.testlog
gadgets/
package/
*.pyc
@ -15,3 +16,8 @@ README.md.toc.*
.DS_Store
*.vimspector.log
support/test/csharp/*.exe*
.neomake.log
configurations/
venv/
test-base/
tags

View file

@ -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:

View file

@ -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
View file

@ -0,0 +1,3 @@
cmdargs:
color: true
severity: style_problem

61
.ycm_extra_conf.py Normal file
View file

@ -0,0 +1,61 @@
try:
from ycmd.extra_conf_support import IgnoreExtraConf
except ImportError:
IgnoreExtraConf = None
import os.path as p
PATH_TO_THIS_DIR = p.dirname( p.abspath( __file__ ) )
def Settings( **kwargs ):
if kwargs[ 'language' ] == 'json':
return {
'ls': {
'json': {
'schemas': [
{
'fileMatch': [ '.vimspector.json' ],
'url':
f'file://{PATH_TO_THIS_DIR}/docs/schema/vimspector.schema.json'
},
{
'fileMatch': [ '.gadgets.json', '.gadgets.d/*.json' ],
'url':
f'file://{PATH_TO_THIS_DIR}/docs/schema/gadgets.schema.json'
}
]
}
},
'capabilities': {
'textDocument': {
'completion': {
'completionItem': {
'snippetSupport': True
}
}
}
}
}
if kwargs[ 'language' ] == 'python':
return {
'sys_path': [
p.join( PATH_TO_THIS_DIR, 'python3' )
],
'ls': {
'python': {
'analysis': {
'extraPaths': [
p.join( PATH_TO_THIS_DIR, 'python3' ),
],
'useLibraryCodeForTypes': True
}
}
}
}
if IgnoreExtraConf:
raise IgnoreExtraConf()
return None

128
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
`benjacksonbkg@gmail.com`. All complaints will be reviewed and investigated
promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

235
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,235 @@
# Contributing to Vimspector
Contributions to Vimspector are always welcome. Contributions can take many
forms, such as:
* Raising, responding to, or reacting to Issues or Pull Requests
* Testing new in-progress changes and providing feedback
* Discussing in the Gitter channel
* etc.
At all times the [code of conduct](#code-of-conduct) applies.
## Troubleshooting
It's not completely trivial to configure Vimspector and there is a fairly large
amount of documentation. I know full well that documentation isn't everything,
so the first step in troubleshooting is to try a sample project that's known to
work, to check if the problem is your project configuration rather than an
actual bug.
Therefore before raising an issue for a supported language, please check with
the sample projects in `support/test/<language>` and `tests/testdata/` to see if
the problem is with your project settings, rather than with vimspector.
Information on these is in [the README](README.md#trying-it-out).
If in doubt, ask on Gitter.
## Diagnostics
Whenever reporting any type of fault, or difficulty in making the plugin
work, please always include _all_ of the diagnostics requested in the
[issue template][issue-template]. Please do not be offended if your request
is ignored if it does not include the requested diagnostics.
The Vimspector log file contains a full trace of the communication between
Vimspector and the debug adapter. This is the primary source of diagnostic
information when something goes wrong that's not a clear Vim traceback.
If you just want to see the Vimspector log file, use `:VimspectorToggleLog`,
which will tail it in a little window (doesn't work on Windows).
## Issues
The GitHub issue tracker is for *bug reports* and *features requests* for the
Vimspector project, and on-topic comments and follow-ups to them. It is not for
general discussion, general support or for any other purpose.
Please **search the issue tracker for similar issues** before creating a new
one. There's no point in duplication; if an existing open issue addresses your
problem, please comment there instead of creating a duplicate. However, if the
issue you found is **closed as resolved** (e.g. with a PR or the original user's
problem was resolved), raise a **new issue**, because you've found a new
problem. Reference the original issue if you think that's useful information.
Closed issues which have been inactive for 60 days will be locked, this helps to
keep discussions focussed. If you believe you are still experiencing an issue
which has been closed, please raise a new issue, completing the issue template.
If you do find a similar _open_ issue, **don't just post 'me too' or similar**
responses. This almost never helps resolve the issue, and just causes noise for
the maintainers. Only post if it will aid the maintainers in solving the issue;
if there are existing diagnostics requested in the thread, perform
them and post the results.
Please do not be offended if your Issue or comment is closed or hidden, for any
of the following reasons:
* The [issue template][issue-template] was not completed
* The issue or comment is off-topic
* The issue does not represent a Vimspector bug or feature request
* The issue cannot be reasonably reproduced using the minimal vimrc
* The issue is a duplicate of an existing issue
* etc.
Issue titles are important. It's not usually helpful to write a title like
`Issue with Vimspector` or `Issue configuring` or even pasting an error message.
Spend a minute to come up with a consise summary of the problem. This helps with
management of issues, with triage, and above all with searching.
But above all else, please *please* complete the issue template. I know it is a
little tedious to get all the various diagnostics, but you *must* provide them,
*even if you think they are irrelevant*. This is important, because the
maintainer(s) can quickly cross-check theories by inspecting the provided
diagnostics without having to spend time asking for them, and waiting for the
response. This means *you get a better answer, faster*. So it's worth it,
honestly.
### Reproduce your issue with the minimal vimrc
Many problems can be caused by unexpected configuration or other plugins.
Therefore when raising an issue, you must attempt to reproduce your issue
with the minimal vimrc provided, and to provide any additional changes required
to that file in order to reproduce it. The purpose of this is to ensure that
the issue is not a conflict with another plugin, or a problem unique to your
configuration.
If your issue does _not_ reproduce with the minimal vimrc, then you must say so
in the issue report.
The minimal vimrc is in `support/test/minimal_vimrc` and can be used as follows:
```
vim --clean -Nu /path/to/vimspector/support/minimal_vimrc
```
## Pull Requests
Vimspector is open to all contributors with ideas great and small! However,
there is a limit to the intended scope of the plugin and the amount of time the
maintainer has to support and... well... maintain features. It's probably well
understood that the contributor's input typically ends when a PR is megred, but
the maintainers have to keep it working forever.
### Small changes
For bug fixes, documentation changes, gadget versin updates, etc. please just
send a PR, I'm super happy to merge these!
If you are unsure, or looking for some pointers, feel free to ask in Gitter, or
mention is in the PR.
### Larger changes
For larger features that might be in any way controvertial, or increase the
complexity of the overall plugin, please come to Gitter and talk to the
maintainer(s) first. This saves a lot of potential back-and-forth and makes sure
that we're "on the same page" about the idea and the ongoing maintenance.
In addition, if you like hacking, feel free to raise a PR tagged with `[RFC]` in
the title and we can discuss the idea. I still prefer to discuss these things on
Gitter rather than back-and-forth on GitHub, though.
Please don't be offended if the maintainer(s) request significant rework for (or
perhaps even dismiss) a PR that's not gone through this process.
Please also don't be offended if the maintainer(s) ask if you're willing to
provide ongoing support for the feature. As an OSS project manned entirely in
what little spare time the maintainer(s) have, we're always looking for
contributions and contributors who will help with support and maintenance of
larger new features.
### PR Guidelines
When contributing pull requests, I ask that:
* You provide a clear and complete summary of the change, the use case and how
the change was tested.
* You avoid using APIs that are not available in the versions listed in the
dependencies on README.md
* You add tests for your PR.
* You test your changes in both Vim and Neovim at the supported versions (and
state that in the PR).
* You follow the style of the code as-is; the python code is YCM-stye, it is
*not* PEP8, nor should it be.
### Running the tests locally
There are 2 ways:
1. In the docker container. The CI tests for linux run in a container, so as to
ensure a consistent test environment. The container is defined in
`./tests/ci/`. There is also a container in `./tests/manual` which can be
used to run the tests interractively. To do this install and start docker,
then run `./tests/manual/run`. This will drop you into a bash shell inside
the linux container with your local vimspector code mounted. You can then
follow the instructions for running tets directly.
1. Directly: Run `./install_gadget.py --all` and then `./run_tests`. Note that
this depends on your runtime environment and might not match CI. I recommend
running the tests in the docker container. If you have your own custom
gadgets and/or custom configurations (in `vimspector/configurations` and/or
`vimspector/gadget`, then consider using `./run_tests --install --basedir
/tmp/vimspector_test` (then delete `/tmp/vimspector_test`). This will install
the gadgets to that dir and use it for the gadget dir/config dir so that your
custom configuration won't interfere with the tess.
When tests fail, they dump a load of logs to a directory for each failed tests.
Usually the most useful output is `messages`, which tells you what actually
failed.
For more infomration on the test framework, see
[this article](https://vimways.org/2019/a-test-to-attest-to/), authored by the
Vimspector creator.
### Code Style
The code style of the Python code is "YCM" style, because that's how I like it.
`flake8` is used to check for certain errors and code style.
The code style of the Vimscript is largely the same, and it is linted by
`vint`.
To run them:
* (optional) Create and activate a virtual env:
`python3 -m venv venv ; source venv/bin/activate`
* Install the development dependencies: `pip install -r dev_requirements.txt`
* Run `flake8`: `flake8 python3/ *.py`
* Run `vint`: `vint autoload/ plugin/ tests/`
They're also run by CI, so please check for lint failures. The canonical
definition of the command to run is the command run in CI, i.e. in
`.git/workflows/build.yml`.
### Debugging Vimspector
You can debug vimspector's python code using vimspector! We can use debugpy,
from within Vim's embedded python and connect to it. Here's how:
1. In one instance of vim, run the following to get debugpy to start listening
for us to connect: `:py3 __import__( 'vimspector', fromlist=[ 'developer' ]
).developer.SetUpDebugpy()`
2. In another instance of Vim, set a breakpoint in the vimspector python code
you want to debug and launch vimspector (e.g. `<F5>`). Select the `Python:
attach to vim` profile. This will attach to the debugpy running in the other
vim.
3. Back in the first vim (the debuggee), trigger the vimspector code in
question, e.g. by starting to debug something else.
4. You'll see it pause, and the 2nd vim (the debugger), you should be able to
step through and inspect as with any other python remote debugging.
NB. It's also possible to debug the vimscript code using vimspector, but this
requires unreleased vim patches and a fair amount of faff. You can always use
`:debug` (see the help) for this though.
# Code of conduct
Please see [code of conduct](CODE_OF_CONDUCT.md).
[vint]: https://github.com/Vimjas/vint
[flake8]: https://flake8.pycqa.org/en/latest/
[issue-template]: https://github.com/puremourning/vimspector/blob/master/.github/ISSUE_TEMPLATE/bug_report.md

1886
README.md

File diff suppressed because it is too large Load diff

View file

@ -13,103 +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#EvaluateConsole( expr ) abort
stopinsert
setlocal nomodified
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) )
function! vimspector#Evaluate( expr ) abort
if !s:Enabled()
return
endif
py3 _vimspector_session.ShowOutput( 'Console' )
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ), True )
endfunction
function! vimspector#ShowOutput( category ) abort
py3 _vimspector_session.ShowOutput( vim.eval( 'a:category' ) )
function! vimspector#EvaluateConsole( expr ) abort
if !s:Enabled()
return
endif
stopinsert
setlocal nomodified
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ), False )
endfunction
function! vimspector#ShowOutput( ... ) abort
if !s:Enabled()
return
endif
if a:0 == 1
py3 _vimspector_session.ShowOutput( vim.eval( 'a:1' ) )
else
py3 _vimspector_session.ShowOutput( 'Console' )
endif
endfunction
function! vimspector#ShowOutputInWindow( win_id, category ) abort
if !s:Enabled()
return
endif
py3 __import__( 'vimspector',
\ fromlist = [ 'output' ] ).output.ShowOutputInWindow(
\ int( vim.eval( 'a:win_id' ) ),
\ vim.eval( 'a:category' ) )
endfunction
function! vimspector#ToggleLog() abort
if !s:Enabled()
return
endif
py3 _vimspector_session.ToggleLog()
endfunction
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
if !s:Enabled()
return
endif
let buffers = py3eval( '_vimspector_session.GetOutputBuffers() '
\ . ' if _vimspector_session else []' )
return join( buffers, "\n" )
endfunction
function! vimspector#CompleteExpr( ArgLead, CmdLine, CursorPos ) abort
if !s:Enabled()
return
endif
let col = len( a:ArgLead )
let prev_non_keyword_char = match( a:ArgLead[ 0 : col - 1 ], '\k*$' ) + 1
return join( py3eval( '_vimspector_session.GetCommandLineCompletions( '
\ . 'vim.eval( "a:ArgLead" ), '
\ . 'int( vim.eval( "prev_non_keyword_char" ) ) )' ),
\ "\n" )
endfunction
let s:latest_completion_request = {}
function! vimspector#CompleteFuncSync( prompt, find_start, query ) abort
if py3eval( 'not _vimspector_session' )
if a:find_start
return -3
endif
return v:none
endif
if a:find_start
" We're busy
if !empty( s:latest_completion_request )
return -3
endif
let line = getline( line( '.' ) )[ len( a:prompt ) : ]
let col = col( '.' ) - len( a:prompt )
" It seems that most servers don't implement the 'start' parameter, which is
" clearly necessary, as they all seem to assume a specific behaviour, which
" is undocumented.
let s:latest_completion_request.items =
\ py3eval( '_vimspector_session.GetCompletionsSync( '
\.' vim.eval( "line" ), '
\.' int( vim.eval( "col" ) ) )' )
let s:latest_completion_request.line = line
let s:latest_completion_request.col = col
let prev_non_keyword_char = match( line[ 0 : col - 1 ], '\k*$' ) + 1
let query_len = col - prev_non_keyword_char
let start_pos = col
for item in s:latest_completion_request.items
if !has_key( item, 'start' ) || !has_key( item, 'length' )
" The specification states that if start is not supplied, isertion
" should be at the requested column. But about 0 of the servers actually
" implement that
" (https://github.com/microsoft/debug-adapter-protocol/issues/138)
let item.start = prev_non_keyword_char
let item.length = query_len
else
" For some reason, the returned start value is 0-indexed even though we
" use columnsStartAt1
let item.start += 1
endif
if !has_key( item, 'text' )
let item.text = item.label
endif
if item.start < start_pos
let start_pos = item.start
endif
endfor
let s:latest_completion_request.start_pos = start_pos
let s:latest_completion_request.prompt = a:prompt
" call s:Debug( 'FindStart: %s', {
" \ 'line': line,
" \ 'col': col,
" \ 'prompt': len( a:prompt ),
" \ 'start_pos': start_pos,
" \ 'returning': ( start_pos + len( a:prompt ) ) - 1,
" \ } )
" start_pos is 1-based and the return of findstart is 0-based
return ( start_pos + len( a:prompt ) ) - 1
else
let items = []
let pfxlen = len( s:latest_completion_request.prompt )
for item in s:latest_completion_request.items
if item.start > s:latest_completion_request.start_pos
" fix up the text (insert anything that is already present in the line
" that would be erased by the fixed-up earlier start position)
"
" both start_pos and item.start are 1-based
let item.text = s:latest_completion_request.line[
\ s:latest_completion_request.start_pos + pfxlen - 1 :
\ item.start + pfxlen - 1 ] . item.text
endif
if item.length > len( a:query )
" call s:Debug( 'Rejecting %s, length is greater than %s',
" \ item,
" \ len( a:query ) )
continue
endif
call add( items, { 'word': item.text,
\ 'abbr': item.label,
\ 'menu': get( item, 'type', '' ),
\ 'icase': 1,
\ } )
endfor
let s:latest_completion_request = {}
" call s:Debug( 'Items: %s', items )
return { 'words': items, 'refresh': 'always' }
endif
endfunction
function! vimspector#OmniFuncWatch( find_start, query ) abort
return vimspector#CompleteFuncSync( 'Expression: ', a:find_start, a:query )
endfunction
function! vimspector#OmniFuncConsole( find_start, query ) abort
return vimspector#CompleteFuncSync( '> ', a:find_start, a:query )
endfunction
function! vimspector#Install( bang, ... ) abort
if !s:Enabled()
return
endif
let prefix = vimspector#internal#state#GetAPIPrefix()
py3 __import__( 'vimspector',
\ fromlist = [ 'installer' ] ).installer.RunInstaller(
\ vim.eval( 'prefix' ),
\ vim.eval( 'a:bang' ) == '!',
\ *vim.eval( 'a:000' ) )
endfunction
function! vimspector#CompleteInstall( ArgLead, CmdLine, CursorPos ) abort
if !s:Enabled()
return
endif
return py3eval( '"\n".join('
\ . '__import__( "vimspector", fromlist = [ "gadgets" ] )'
\ . '.gadgets.GADGETS.keys() '
\ . ')' )
endfunction
function! vimspector#Update( bang, ... ) abort
if !s:Enabled()
return
endif
let prefix = vimspector#internal#state#GetAPIPrefix()
py3 __import__( 'vimspector',
\ fromlist = [ 'installer' ] ).installer.RunUpdate(
\ vim.eval( 'prefix' ),
\ vim.eval( 'a:bang' ) == '!',
\ *vim.eval( 'a:000' ) )
endfunction
function! vimspector#AbortInstall() abort
if !s:Enabled()
return
endif
let prefix = vimspector#internal#state#GetAPIPrefix()
py3 __import__( 'vimspector', fromlist = [ 'installer' ] ).installer.Abort()
endfunction
function! vimspector#OnBufferCreated( file_name ) abort
if len( a:file_name ) == 0
return
endif
" Don't actually load up vimsepctor python in autocommands that trigger
" regularly. We'll only create the session obkect in s:Enabled()
if !s:Initialised()
return
endif
if !s:Enabled()
return
endif
py3 _vimspector_session.RefreshSigns( vim.eval( 'a:file_name' ) )
endfunction
function! vimspector#ShowEvalBalloon( is_visual ) abort
if a:is_visual
let expr = py3eval( '__import__( "vimspector", fromlist = [ "utils" ] )'
\ . '.utils.GetVisualSelection('
\ . ' int( vim.eval( "winbufnr( winnr() )" ) ) )' )
let expr = join( expr, '\n' )
else
let expr = expand( '<cexpr>' )
endif
return py3eval( '_vimspector_session.ShowEvalBalloon('
\ . ' int( vim.eval( "winnr()" ) ), "'
\ . expr
\ . '", 0 )' )
endfunction
function! vimspector#PrintDebugInfo() abort
if !s:Enabled()
return
endif
py3 _vimspector_session.PrintDebugInfo()
endfunction
" Boilerplate {{{
let &cpoptions=s:save_cpo
unlet s:save_cpo

View file

@ -19,15 +19,323 @@ let s:save_cpo = &cpoptions
set cpoptions&vim
" }}}
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.
py3 _vimspector_session.ShowBalloon( int( vim.eval( 'v:beval_winnr' ) ) + 1,
\ vim.eval( 'v:beval_text' ) )
return '...'
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

View file

@ -20,42 +20,49 @@ 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:_OnServerError( channel, data ) abort
echom 'Channel received error: ' . a:data
redraw
endfunction
function! s:_OnClose( channel ) abort
if !exists( 's:ch' ) || s:ch isnot a:channel
return
endif
echom 'Channel closed'
redraw
unlet s:ch
py3 _vimspector_session.OnServerExit( 0 )
endfunction
function! s:_Send( msg ) abort
call ch_sendraw( s:ch, a:msg )
return 1
endfunction
function! vimspector#internal#channel#Timeout( id ) abort
py3 << EOF
_vimspector_session.OnRequestTimeout( vim.eval( 'a:id' ) )
EOF
endfunction
function! vimspector#internal#channel#StartDebugSession( config ) abort
if exists( 's:ch' )
echo 'Channel is already running'
return v:none
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,
@ -68,44 +75,72 @@ 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:none
return v:false
endif
return funcref( 's:_Send' )
return v:true
endfunction
function! vimspector#internal#channel#Send( msg ) abort
call ch_sendraw( s:ch, a:msg )
return 1
endfunction
function! vimspector#internal#channel#Timeout( id ) abort
py3 << EOF
_vimspector_session.OnRequestTimeout( vim.eval( 'a:id' ) )
EOF
endfunction
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
function! vimspector#internal#channel#ForceRead() abort
if exists( 's:ch' )
let data = ch_readraw( s:ch, { 'timeout': 1000 } )
if data !=# ''
call s:_OnServerData( s:ch, data )
endif
endif
endfunction
" Boilerplate {{{
let &cpoptions=s:save_cpo
unlet s:save_cpo

View file

@ -20,26 +20,91 @@ 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! s:_Send( msg ) abort
function! vimspector#internal#job#StartDebugSession( config ) abort
if exists( 's:job' )
echom 'Not starting: Job is already running'
redraw
return v:false
endif
let s:job = job_start( a:config[ 'command' ],
\ {
\ 'in_mode': 'raw',
\ 'out_mode': 'raw',
\ 'err_mode': 'raw',
\ 'exit_cb': funcref( 's:_OnExit' ),
\ 'close_cb': funcref( 's:_OnClose' ),
\ 'out_cb': funcref( 's:_OnServerData' ),
\ 'err_cb': funcref( 's:_OnServerError' ),
\ 'stoponexit': 'term',
\ 'env': a:config[ 'env' ],
\ 'cwd': a:config[ 'cwd' ],
\ }
\ )
if !exists( 's:job' )
" The job died immediately after starting and we cleaned up
return v:false
endif
let status = job_status( s:job )
echom 'Started job, status is: ' . status
redraw
if status !=# 'run'
return v:false
endif
return v:true
endfunction
function! vimspector#internal#job#Send( msg ) abort
if ! exists( 's:job' )
echom "Can't send message: Job was not initialised correctly"
redraw
@ -63,40 +128,6 @@ function! s:_Send( msg ) abort
return 1
endfunction
function! vimspector#internal#job#StartDebugSession( config ) abort
if exists( 's:job' )
echom 'Not starging: Job is already running'
redraw
return v:none
endif
let s:job = job_start( a:config[ 'command' ],
\ {
\ 'in_mode': 'raw',
\ 'out_mode': 'raw',
\ 'err_mode': 'raw',
\ 'exit_cb': funcref( 's:_OnExit' ),
\ 'close_cb': funcref( 's:_OnClose' ),
\ 'out_cb': funcref( 's:_OnServerData' ),
\ 'err_cb': funcref( 's:_OnServerError' ),
\ 'stoponexit': 'term',
\ 'env': a:config[ 'env' ],
\ 'cwd': a:config[ 'cwd' ],
\ }
\ )
echom 'Started job, status is: ' . job_status( s:job )
redraw
if job_status( s:job ) !=# 'run'
echom 'Unable to start job, status is: ' . job_status( s:job )
redraw
return v:none
endif
return funcref( 's:_Send' )
endfunction
function! vimspector#internal#job#StopDebugSession() abort
if !exists( 's:job' )
echom "Not stopping session: Job doesn't exist"
@ -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,13 +146,11 @@ function! vimspector#internal#job#Reset() abort
call vimspector#internal#job#StopDebugSession()
endfunction
function! vimspector#internal#job#ForceRead() abort
if exists( 's:job' )
let data = ch_readraw( job_getchannel( s:job ), { 'timeout': 1000 } )
if data !=# ''
call s:_OnServerData( job_getchannel( s:job ), data )
endif
endif
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
@ -135,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,
\ 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

View file

@ -0,0 +1,126 @@
" vimspector - A multi-language debugging system for Vim
" Copyright 2020 Ben Jackson
"
" Licensed under the Apache License, Version 2.0 (the "License");
" you may not use this file except in compliance with the License.
" You may obtain a copy of the License at
"
" http://www.apache.org/licenses/LICENSE-2.0
"
" Unless required by applicable law or agreed to in writing, software
" distributed under the License is distributed on an "AS IS" BASIS,
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
" See the License for the specific language governing permissions and
" limitations under the License.
" Boilerplate {{{
let s:save_cpo = &cpoptions
set cpoptions&vim
" }}}
function! s:_OnEvent( chan_id, data, event ) abort
if v:exiting isnot# v:null
return
endif
if !exists( 's:ch' ) || a:chan_id != s:ch
return
endif
if a:data == ['']
echom 'Channel closed'
redraw
unlet s:ch
py3 _vimspector_session.OnServerExit( 0 )
else
py3 _vimspector_session.OnChannelData( '\n'.join( vim.eval( 'a:data' ) ) )
endif
endfunction
function! vimspector#internal#neochannel#StartDebugSession( config ) abort
if exists( 's:ch' )
echom 'Not starting: Channel is already running'
redraw
return v:false
endif
" If we _also_ have a command line, then start the actual job. This allows for
" servers which start up and listen on some port
if has_key( a:config, 'command' )
let old_env={}
try
let old_env = vimspector#internal#neoterm#PrepareEnvironment(
\ a:config[ 'env' ] )
let s:job = jobstart( a:config[ 'command' ],
\ {
\ 'cwd': a:config[ 'cwd' ],
\ 'env': a:config[ 'env' ],
\ }
\ )
finally
call vimspector#internal#neoterm#ResetEnvironment( a:config[ 'env' ],
\ old_env )
endtry
endif
let l:addr = get( a:config, 'host', '127.0.0.1' ) . ':' . a:config[ 'port' ]
let attempt = 1
while attempt <= 10
echo 'Connecting to ' . l:addr . '... (attempt' attempt 'of 10)'
try
let s:ch = sockconnect( 'tcp',
\ addr,
\ { 'on_data': funcref( 's:_OnEvent' ) } )
redraw
return v:true
catch /connection refused/
sleep 1
endtry
let attempt += 1
endwhile
echom 'Unable to connect to' l:addr 'after 10 attempts'
redraw
return v:false
endfunction
function! vimspector#internal#neochannel#Send( msg ) abort
if ! exists( 's:ch' )
echom "Can't send message: Channel was not initialised correctly"
redraw
return 0
endif
call chansend( s:ch, a:msg )
return 1
endfunction
function! vimspector#internal#neochannel#StopDebugSession() abort
if exists( 's:ch' )
call chanclose( s:ch )
" It doesn't look like we get a callback after chanclos. Who knows if we
" will subsequently receive data callbacks.
call s:_OnEvent( s:ch, [ '' ], 'data' )
endif
if exists( 's:job' )
if vimspector#internal#neojob#JobIsRunning( s:job )
call jobstop( s:job )
endif
unlet s:job
endif
endfunction
function! vimspector#internal#neochannel#Reset() abort
call vimspector#internal#neochannel#StopDebugSession()
endfunction
" Boilerplate {{{
let &cpoptions=s:save_cpo
unlet s:save_cpo
" }}}

View file

@ -0,0 +1,252 @@
" vimspector - A multi-language debugging system for Vim
" Copyright 2020 Ben Jackson
"
" Licensed under the Apache License, Version 2.0 (the "License");
" you may not use this file except in compliance with the License.
" You may obtain a copy of the License at
"
" http://www.apache.org/licenses/LICENSE-2.0
"
" Unless required by applicable law or agreed to in writing, software
" distributed under the License is distributed on an "AS IS" BASIS,
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
" See the License for the specific language governing permissions and
" limitations under the License.
" Boilerplate {{{
let s:save_cpo = &cpoptions
set cpoptions&vim
" }}}
function! s:_OnEvent( chan_id, data, event ) abort
if v:exiting isnot# v:null
return
endif
if !exists( 's:job' ) || a:chan_id != s:job
return
endif
" In neovim, the data argument is a list.
if a:event ==# 'stdout'
py3 _vimspector_session.OnChannelData( '\n'.join( vim.eval( 'a:data' ) ) )
elseif a:event ==# 'stderr'
py3 _vimspector_session.OnServerStderr( '\n'.join( vim.eval( 'a:data' ) ) )
elseif a:event ==# 'exit'
echom 'Channel exit with status ' . a:data
redraw
unlet s:job
py3 _vimspector_session.OnServerExit( vim.eval( 'a:data' ) )
endif
endfunction
function! vimspector#internal#neojob#StartDebugSession( config ) abort
if exists( 's:job' )
echom 'Not starging: Job is already running'
redraw
return v:false
endif
" HACK: Workaround for 'env' not being supported.
let old_env={}
try
let old_env = vimspector#internal#neoterm#PrepareEnvironment(
\ a:config[ 'env' ] )
let s:job = jobstart( a:config[ 'command' ],
\ {
\ 'on_stdout': funcref( 's:_OnEvent' ),
\ 'on_stderr': funcref( 's:_OnEvent' ),
\ 'on_exit': funcref( 's:_OnEvent' ),
\ 'cwd': a:config[ 'cwd' ],
\ 'env': a:config[ 'env' ],
\ }
\ )
finally
call vimspector#internal#neoterm#ResetEnvironment( a:config[ 'env' ],
\ old_env )
endtry
return v:true
endfunction
function! vimspector#internal#neojob#JobIsRunning( job ) abort
return jobwait( [ a:job ], 0 )[ 0 ] == -1
endfunction
function! vimspector#internal#neojob#Send( msg ) abort
if ! exists( 's:job' )
echom "Can't send message: Job was not initialised correctly"
redraw
return 0
endif
if !vimspector#internal#neojob#JobIsRunning( s:job )
echom "Can't send message: Job is not running"
redraw
return 0
endif
call chansend( s:job, a:msg )
return 1
endfunction
function! vimspector#internal#neojob#StopDebugSession() abort
if !exists( 's:job' )
return
endif
if vimspector#internal#neojob#JobIsRunning( s:job )
echom 'Terminating job'
redraw
call jobstop( s:job )
endif
endfunction
function! vimspector#internal#neojob#Reset() abort
call vimspector#internal#neojob#StopDebugSession()
endfunction
function! s:_OnCommandEvent( category, id, data, event ) abort
if v:exiting isnot# v:null
return
endif
if a:event ==# 'stdout' || a:event ==# 'stderr'
if a:data == ['']
return
endif
if !has_key( s:commands, a:category )
return
endif
if !has_key( s:commands[ a:category ], a:id )
return
endif
if a:event ==# 'stdout'
let buffer = s:commands[ a:category ][ a:id ].stdout
elseif a:event ==# 'stderr'
let buffer = s:commands[ a:category ][ a:id ].stderr
endif
try
call bufload( buffer )
catch /E325/
" Ignore E325/ATTENTION
endtry
let numlines = py3eval( "len( vim.buffers[ int( vim.eval( 'buffer' ) ) ] )" )
let last_line = getbufline( buffer, '$' )[ 0 ]
call s:MakeBufferWritable( buffer )
try
if numlines == 1 && last_line ==# ''
call setbufline( buffer, 1, a:data[ 0 ] )
else
call setbufline( buffer, '$', last_line . a:data[ 0 ] )
endif
call appendbufline( buffer, '$', a:data[ 1: ] )
finally
call s:MakeBufferReadOnly( buffer )
call setbufvar( buffer, '&modified', 0 )
endtry
" if the buffer is visible, scroll it, but don't allow autocommands to fire,
" as this may close the current window!
let w = bufwinnr( buffer )
if w > 0
let cw = winnr()
try
noautocmd execute w . 'wincmd w'
noautocmd normal! Gz-
finally
noautocmd execute cw . 'wincmd w'
endtry
endif
elseif a:event ==# 'exit'
py3 __import__( "vimspector",
\ fromlist = [ "utils" ] ).utils.OnCommandWithLogComplete(
\ vim.eval( 'a:category' ),
\ int( vim.eval( 'a:data' ) ) )
endif
endfunction
function! s:SetUpHiddenBuffer( buffer ) abort
call setbufvar( a:buffer, '&hidden', 1 )
call setbufvar( a:buffer, '&bufhidden', 'hide' )
call setbufvar( a:buffer, '&wrap', 0 )
call setbufvar( a:buffer, '&swapfile', 0 )
call setbufvar( a:buffer, '&textwidth', 0 )
call s:MakeBufferReadOnly( a:buffer )
endfunction
function! s:MakeBufferReadOnly( buffer ) abort
call setbufvar( a:buffer, '&modifiable', 0 )
call setbufvar( a:buffer, '&readonly', 1 )
endfunction
function! s:MakeBufferWritable( buffer ) abort
call setbufvar( a:buffer, '&readonly', 0 )
call setbufvar( a:buffer, '&modifiable', 1 )
endfunction
let s:commands = {}
function! vimspector#internal#neojob#StartCommandWithLog( cmd, category ) abort
if ! has_key( s:commands, a:category )
let s:commands[ a:category ] = {}
endif
let buf = bufnr( '_vimspector_log_' . a:category, v:true )
" FIXME: This largely duplicates the same stuff in the python layer, but we
" don't want to potentially mess up Vim behaviour where the job output is
" attached to a buffer set up by Vim. So we sort o mimic that here.
call s:SetUpHiddenBuffer( buf )
let id = jobstart(a:cmd,
\ {
\ 'on_stdout': funcref( 's:_OnCommandEvent',
\ [ a:category ] ),
\ 'on_stderr': funcref( 's:_OnCommandEvent',
\ [ a:category ] ),
\ 'on_exit': funcref( 's:_OnCommandEvent',
\ [ a:category ] ),
\ } )
let s:commands[ a:category ][ id ] = {
\ 'stdout': buf,
\ 'stderr': buf
\ }
return buf
endfunction
function! vimspector#internal#neojob#CleanUpCommand( category ) abort
if ! has_key( s:commands, a:category )
return
endif
for id in keys( s:commands[ a:category ] )
let id = str2nr( id )
if jobwait( [ id ], 0 )[ 0 ] == -1
call jobstop( id )
endif
call jobwait( [ id ], -1 )
endfor
unlet! s:commands[ a:category ]
endfunction
" Boilerplate {{{
let &cpoptions=s:save_cpo
unlet s:save_cpo
" }}}

View 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
" }}}

View file

@ -0,0 +1,106 @@
" vimspector - A multi-language debugging system for Vim
" Copyright 2018 Ben Jackson
"
" Licensed under the Apache License, Version 2.0 (the "License");
" you may not use this file except in compliance with the License.
" You may obtain a copy of the License at
"
" http://www.apache.org/licenses/LICENSE-2.0
"
" Unless required by applicable law or agreed to in writing, software
" distributed under the License is distributed on an "AS IS" BASIS,
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
" See the License for the specific language governing permissions and
" limitations under the License.
" Boilerplate {{{
let s:save_cpo = &cpoptions
set cpoptions&vim
" }}}
" Ids are unique throughtout the life of neovim, but obviously buffer numbers
" aren't
"
" FIXME: Tidy this map when buffers are closed ?
let s:buffer_to_id = {}
function! vimspector#internal#neoterm#PrepareEnvironment( env ) abort
let old_env = {}
for key in keys( a:env )
if exists( '$' . key )
let old_env[ key ] = getenv( key )
endif
call setenv( key, a:env[ key ] )
endfor
return old_env
endfunction
function! vimspector#internal#neoterm#ResetEnvironment( env, old_env ) abort
for key in keys( a:env )
let value = get( a:old_env, key, v:null )
call setenv( key, value )
endfor
endfunction
function! vimspector#internal#neoterm#Start( cmd, opts ) abort
" Prepare current buffer to be turned into a term if curwin is not set
if ! get( a:opts, 'curwin', 0 )
let mods = 'rightbelow '
if get( a:opts, 'vertical', 0 )
let mods .= 'vertical '
let mods .= get( a:opts, 'term_cols', '' )
else
let mods .= get( a:opts, 'term_rows', '' )
endif
execute mods . 'new'
endif
" HACK: Neovim's termopen doesn't support env
let old_env={}
try
let old_env = vimspector#internal#neoterm#PrepareEnvironment(
\ a:opts[ 'env' ] )
setlocal nomodified
let id = termopen( a:cmd, {
\ 'cwd': a:opts[ 'cwd' ],
\ 'env': a:opts[ 'env' ],
\ } )
finally
call vimspector#internal#neoterm#ResetEnvironment( a:opts[ 'env' ],
\ old_env )
endtry
let bufnr = bufnr()
let s:buffer_to_id[ bufnr ] = id
return bufnr
endfunction
function! s:JobIsRunning( job ) abort
return jobwait( [ a:job ], 0 )[ 0 ] == -1
endfunction
function! vimspector#internal#neoterm#IsFinished( bufno ) abort
if !has_key( s:buffer_to_id, a:bufno )
return v:true
endif
return !s:JobIsRunning( s:buffer_to_id[ a:bufno ] )
endfunction
function! vimspector#internal#neoterm#GetPID( bufno ) abort
if !has_key( s:buffer_to_id, a:bufno )
return -1
endif
return jobpid( s:buffer_to_id[ a:bufno ] )
endfunction
" Boilerplate {{{
let &cpoptions=s:save_cpo
unlet s:save_cpo
" }}}

View 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
" }}}

View file

@ -19,11 +19,32 @@ let s:save_cpo = &cpoptions
set cpoptions&vim
" }}}
let s:prefix = ''
if has( 'nvim' )
let s:prefix='neo'
endif
function! vimspector#internal#state#Reset() abort
py3 << EOF
from vimspector import debug_session
_vimspector_session = debug_session.DebugSession()
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 {{{

View file

@ -0,0 +1,37 @@
" vimspector - A multi-language debugging system for Vim
" Copyright 2018 Ben Jackson
"
" Licensed under the Apache License, Version 2.0 (the "License");
" you may not use this file except in compliance with the License.
" You may obtain a copy of the License at
"
" http://www.apache.org/licenses/LICENSE-2.0
"
" Unless required by applicable law or agreed to in writing, software
" distributed under the License is distributed on an "AS IS" BASIS,
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
" See the License for the specific language governing permissions and
" limitations under the License.
" Boilerplate {{{
let s:save_cpo = &cpoptions
set cpoptions&vim
" }}}
function! vimspector#internal#term#Start( cmd, opts ) abort
rightbelow return term_start( a:cmd, a:opts )
endfunction
function! vimspector#internal#term#IsFinished( bufno ) abort
return index( split( term_getstatus( a:bufno ), ',' ), 'finished' ) >= 0
endfunction
function! vimspector#internal#term#GetPID( bufno ) abort
return job_info( term_getjob( a:bufno ) ).process
endfunction
" Boilerplate {{{
let &cpoptions=s:save_cpo
unlet s:save_cpo
" }}}

View file

@ -1,144 +0,0 @@
# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml
stages:
- stage: Build
jobs:
- job: 'PythonLint'
displayName: "Python Lint"
pool:
vmImage: 'ubuntu-16.04'
container: 'puremourning/vimspector:test'
steps:
- bash: pip3 install -r dev_requirements.txt
displayName: "Install requirements"
- bash: $HOME/.local/bin/flake8 python3/
displayName: "Run flake8"
- job: 'Vimscript'
displayName: "Vimscript Lint"
pool:
vmImage: 'ubuntu-16.04'
container: 'puremourning/vimspector:test'
steps:
- bash: pip3 install -r dev_requirements.txt
displayName: "Install requirements"
- bash: $HOME/.local/bin/vint autoload/ plugin/
displayName: "Run vint"
- job: 'linux'
pool:
vmImage: 'ubuntu-16.04'
container:
image: 'puremourning/vimspector:test'
options: --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
steps:
- bash: |
eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
go get -u github.com/go-delve/delve/cmd/dlv
displayName: 'Install Delve for Go'
- task: CacheBeta@0
inputs:
key: v1 | gadgets | $(Agent.OS) | install_gadget.py
path: gadgets/linux/download
displayName: Cache gadgets
- bash: python3 install_gadget.py --all
displayName: 'Install gadgets - python3'
- bash: vim --version
displayName: 'Print vim version information'
- bash: |
eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
export GOPATH=$HOME/go
./run_tests
displayName: 'Run the tests'
env:
VIMSPECTOR_MIMODE: gdb
- bash: ./make_package linux $(Build.SourceVersion)
displayName: 'Package'
- task: PublishPipelineArtifact@0
inputs:
artifactName: 'package-linux'
targetPath: 'package/linux-$(Build.SourceVersion).tar.gz'
- job: 'macos'
pool:
vmImage: 'macOS-10.13'
steps:
- bash: |
brew unlink node@6
brew install macvim node@10
brew link --force --overwrite node@10
displayName: 'Install vim and node'
- bash: go get -u github.com/go-delve/delve/cmd/dlv
displayName: 'Install Delve for Go'
- task: CacheBeta@0
inputs:
key: v1 | gadgets | $(Agent.OS) | install_gadget.py
path: gadgets/macos/download
displayName: Cache gadgets
- bash: python3 install_gadget.py --all
displayName: 'Install gadgets - python3'
- bash: vim --version
displayName: 'Print vim version information'
- bash: ./run_tests
displayName: 'Run the tests'
env:
VIMSPECTOR_MIMODE: lldb
- bash: ./make_package macos $(Build.SourceVersion)
displayName: 'Package'
- task: PublishPipelineArtifact@0
inputs:
artifactName: 'package-macos'
targetPath: 'package/macos-$(Build.SourceVersion).tar.gz'
- stage: "Publish"
dependsOn:
- "Build"
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
jobs:
- job: 'Publish'
pool:
vmImage: 'ubuntu-16.04'
steps:
- task: DownloadPipelineArtifact@0
inputs:
artifactName: 'package-linux'
targetPath: $(Build.ArtifactStagingDirectory)
- task: DownloadPipelineArtifact@0
inputs:
artifactName: 'package-macos'
targetPath: $(Build.ArtifactStagingDirectory)
- task: GitHubRelease@0
inputs:
gitHubConnection: puremourning
repositoryName: '$(Build.Repository.Name)'
action: 'create' # Options: create, edit, delete
target: '$(Build.SourceVersion)' # Required when action == Create || Action == Edit
tagSource: 'manual' # Required when action == Create# Options: auto, manual
tag: "$(Build.BuildId)"
#title: # Optional
#releaseNotesSource: 'file' # Optional. Options: file, input
#releaseNotesFile: # Optional
#releaseNotes: # Optional
#assets: '$(Build.ArtifactStagingDirectory)/*' # Optional
#assetUploadMode: 'delete' # Optional. Options: delete, replace
#isDraft: false # Optional
isPreRelease: true # Optional
#addChangeLog: true # Optional

View file

@ -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> )

View file

@ -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

File diff suppressed because it is too large Load diff

2394
doc/vimspector.txt Normal file

File diff suppressed because it is too large Load diff

5
docs/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
_site
.sass-cache
.jekyll-metadata
vendor/
.bundle/

24
docs/404.html Normal file
View file

@ -0,0 +1,24 @@
---
layout: default
---
<style type="text/css" media="screen">
.container {
margin: 10px auto;
max-width: 600px;
text-align: center;
}
h1 {
margin: 30px 0;
font-size: 4em;
line-height: 1;
letter-spacing: -1px;
}
</style>
<div class="container">
<h1>404</h1>
<p><strong>Page not found :(</strong></p>
<p>The requested page could not be found.</p>
</div>

32
docs/Gemfile Normal file
View file

@ -0,0 +1,32 @@
source "https://rubygems.org"
# Hello! This is where you manage which Jekyll version is used to run.
# When you want to use a different version, change it below, save the
# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
#
# bundle exec jekyll serve
#
# This will help ensure the proper Jekyll version is running.
# Happy Jekylling!
#gem "jekyll", "~> 3.8.5"
# This is the default theme for new Jekyll sites. You may change this to anything you like.
gem "minima", "~> 2.0"
# If you want to use GitHub Pages, remove the "gem "jekyll"" above and
# uncomment the line below. To upgrade, run `bundle update github-pages`.
gem "github-pages", group: :jekyll_plugins
# If you have any plugins, put them here!
group :jekyll_plugins do
gem "jekyll-feed", "~> 0.6"
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
# Performance-booster for watching directories on Windows
gem "wdm", "~> 0.1.0" if Gem.win_platform?
gem "webrick", "~> 1.7"

270
docs/Gemfile.lock Normal file
View file

@ -0,0 +1,270 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (6.0.3.6)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.2, >= 2.2.2)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.11.1)
colorator (1.1.0)
commonmarker (0.17.13)
ruby-enum (~> 0.5)
concurrent-ruby (1.1.8)
dnsruby (1.61.5)
simpleidn (~> 0.1)
em-websocket (0.5.2)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0)
ethon (0.12.0)
ffi (>= 1.3.0)
eventmachine (1.2.7)
execjs (2.7.0)
faraday (1.3.0)
faraday-net_http (~> 1.0)
multipart-post (>= 1.2, < 3)
ruby2_keywords
faraday-net_http (1.0.1)
ffi (1.15.0)
forwardable-extended (2.6.0)
gemoji (3.0.1)
github-pages (214)
github-pages-health-check (= 1.17.0)
jekyll (= 3.9.0)
jekyll-avatar (= 0.7.0)
jekyll-coffeescript (= 1.1.1)
jekyll-commonmark-ghpages (= 0.1.6)
jekyll-default-layout (= 0.1.4)
jekyll-feed (= 0.15.1)
jekyll-gist (= 1.5.0)
jekyll-github-metadata (= 2.13.0)
jekyll-mentions (= 1.6.0)
jekyll-optional-front-matter (= 0.3.2)
jekyll-paginate (= 1.1.0)
jekyll-readme-index (= 0.3.0)
jekyll-redirect-from (= 0.16.0)
jekyll-relative-links (= 0.6.1)
jekyll-remote-theme (= 0.4.3)
jekyll-sass-converter (= 1.5.2)
jekyll-seo-tag (= 2.7.1)
jekyll-sitemap (= 1.4.0)
jekyll-swiss (= 1.0.0)
jekyll-theme-architect (= 0.1.1)
jekyll-theme-cayman (= 0.1.1)
jekyll-theme-dinky (= 0.1.1)
jekyll-theme-hacker (= 0.1.2)
jekyll-theme-leap-day (= 0.1.1)
jekyll-theme-merlot (= 0.1.1)
jekyll-theme-midnight (= 0.1.1)
jekyll-theme-minimal (= 0.1.1)
jekyll-theme-modernist (= 0.1.1)
jekyll-theme-primer (= 0.5.4)
jekyll-theme-slate (= 0.1.1)
jekyll-theme-tactile (= 0.1.1)
jekyll-theme-time-machine (= 0.1.1)
jekyll-titles-from-headings (= 0.5.3)
jemoji (= 0.12.0)
kramdown (= 2.3.1)
kramdown-parser-gfm (= 1.1.0)
liquid (= 4.0.3)
mercenary (~> 0.3)
minima (= 2.5.1)
nokogiri (>= 1.10.4, < 2.0)
rouge (= 3.26.0)
terminal-table (~> 1.4)
github-pages-health-check (1.17.0)
addressable (~> 2.3)
dnsruby (~> 1.60)
octokit (~> 4.0)
public_suffix (>= 2.0.2, < 5.0)
typhoeus (~> 1.3)
html-pipeline (2.14.0)
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.6.0)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
jekyll (3.9.0)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 0.7)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 2.0)
kramdown (>= 1.17, < 3)
liquid (~> 4.0)
mercenary (~> 0.3.3)
pathutil (~> 0.9)
rouge (>= 1.7, < 4)
safe_yaml (~> 1.0)
jekyll-avatar (0.7.0)
jekyll (>= 3.0, < 5.0)
jekyll-coffeescript (1.1.1)
coffee-script (~> 2.2)
coffee-script-source (~> 1.11.1)
jekyll-commonmark (1.3.1)
commonmarker (~> 0.14)
jekyll (>= 3.7, < 5.0)
jekyll-commonmark-ghpages (0.1.6)
commonmarker (~> 0.17.6)
jekyll-commonmark (~> 1.2)
rouge (>= 2.0, < 4.0)
jekyll-default-layout (0.1.4)
jekyll (~> 3.0)
jekyll-feed (0.15.1)
jekyll (>= 3.7, < 5.0)
jekyll-gist (1.5.0)
octokit (~> 4.2)
jekyll-github-metadata (2.13.0)
jekyll (>= 3.4, < 5.0)
octokit (~> 4.0, != 4.4.0)
jekyll-mentions (1.6.0)
html-pipeline (~> 2.3)
jekyll (>= 3.7, < 5.0)
jekyll-optional-front-matter (0.3.2)
jekyll (>= 3.0, < 5.0)
jekyll-paginate (1.1.0)
jekyll-readme-index (0.3.0)
jekyll (>= 3.0, < 5.0)
jekyll-redirect-from (0.16.0)
jekyll (>= 3.3, < 5.0)
jekyll-relative-links (0.6.1)
jekyll (>= 3.3, < 5.0)
jekyll-remote-theme (0.4.3)
addressable (~> 2.0)
jekyll (>= 3.5, < 5.0)
jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0)
rubyzip (>= 1.3.0, < 3.0)
jekyll-sass-converter (1.5.2)
sass (~> 3.4)
jekyll-seo-tag (2.7.1)
jekyll (>= 3.8, < 5.0)
jekyll-sitemap (1.4.0)
jekyll (>= 3.7, < 5.0)
jekyll-swiss (1.0.0)
jekyll-theme-architect (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-cayman (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-dinky (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-hacker (0.1.2)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-leap-day (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-merlot (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-midnight (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-minimal (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-modernist (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-primer (0.5.4)
jekyll (> 3.5, < 5.0)
jekyll-github-metadata (~> 2.9)
jekyll-seo-tag (~> 2.0)
jekyll-theme-slate (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-tactile (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-time-machine (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-titles-from-headings (0.5.3)
jekyll (>= 3.3, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
jemoji (0.12.0)
gemoji (~> 3.0)
html-pipeline (~> 2.2)
jekyll (>= 3.0, < 5.0)
kramdown (2.3.1)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.3)
listen (3.5.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.3.6)
mini_portile2 (2.5.1)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.14.4)
multipart-post (2.1.1)
nokogiri (1.11.5)
mini_portile2 (~> 2.5.0)
racc (~> 1.4)
octokit (4.20.0)
faraday (>= 0.9)
sawyer (~> 0.8.0, >= 0.5.3)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (4.0.6)
racc (1.5.2)
rb-fsevent (0.10.4)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.5)
rouge (3.26.0)
ruby-enum (0.9.0)
i18n
ruby2_keywords (0.0.4)
rubyzip (2.3.0)
safe_yaml (1.0.5)
sass (3.7.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sawyer (0.8.2)
addressable (>= 2.3.5)
faraday (> 0.8, < 2.0)
simpleidn (0.2.1)
unf (~> 0.1.4)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (1.2.9)
thread_safe (~> 0.1)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.7)
unicode-display_width (1.7.0)
webrick (1.7.0)
zeitwerk (2.4.2)
PLATFORMS
ruby
DEPENDENCIES
github-pages
jekyll-feed (~> 0.6)
minima (~> 2.0)
tzinfo-data
webrick (~> 1.7)
BUNDLED WITH
2.2.3

13
docs/README.local Normal file
View file

@ -0,0 +1,13 @@
To update/install:
gem install bundler
bundle install --path vendor/bundle
To run a local server/test the build
bundle exec jekyll serve
To update deps
bundle update
or bundle update github-pages

42
docs/_config.yml Normal file
View file

@ -0,0 +1,42 @@
# Welcome to Jekyll!
#
# This config file is meant for settings that affect your whole blog, values
# which you are expected to set up once and rarely edit after that. If you find
# yourself editing this file very often, consider using Jekyll's data files
# feature for the data you need to update frequently.
#
# For technical reasons, this file is *NOT* reloaded automatically when you use
# 'bundle exec jekyll serve'. If you change this file, please restart the server process.
# Site settings
# These are used to personalize your new site. If you look in the HTML files,
# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on.
# You can create any custom variable you would like, and they will be accessible
# in the templates via {{ site.myvariable }}.
title: Vimspector Documentation
description: |
Reference Documentation for Vimspector: A multi-language debugging front
end for Vim
# Build settings
markdown: kramdown
theme: minima
plugins:
- jekyll-feed
header_pages:
- index.md
- configuration.md
- schema/index.md
# Exclude from processing.
# The following items will not be processed, by default. Create a custom list
# to override the default setting.
# exclude:
# - Gemfile
# - Gemfile.lock
# - node_modules
# - vendor/bundle/
# - vendor/cache/
# - vendor/gems/
# - vendor/ruby/

1043
docs/configuration.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,25 @@
---
title: Configuration
---
This document describes how to use vimspector's `install_gadget.py` to install
custom debug adapters. This can be useful as a way to get an adapter working
that isn't officially supported by Vimspector, but otherwise can be made to work
by simply downloading the VScode extension into the gadget directory.
## Usage
```
./install_gadget.py --enable-custom=/path/to/a.json \
--enable-custom=/path/to/b.json`
```
This tells `install_gadget.py` to read `a.json` and `b.json` as _gadget
definitions_ and download/unpack the specified gadgets into the gadget dir, just
like the supported adapters.
## Gadget Definitions
A _gadget definition_ is a file containing a single JSON object definition,
describing the debug adapter and how to download and install it. This mechanism
is crude but can be effective.

23
docs/index.md Normal file
View file

@ -0,0 +1,23 @@
---
title: Vimspector Reference Guide
---
This section contains reference material for configuring and using
[Vimspector][vimspector]. It is intentionally technical in nature and should
serve as a reference guide, rather than a user guide or tutorial.
It complements the following:
* The [Vimspector README][readme], which contains a general overview and is the
main go-to for getting started, installation, etc.
* The [Vimspector Website][website], which contains a step-by-step tutorial and
some detail about the user interface.
## Contents
* [Configuring Vimspector for your projects](configuration.html)
* [Full JSON Schema reference](schema/)
[vimspector]: https://github.com/puremourning/vimspector
[readme]: https://github.com/puremourning/vimspector/blob/master/README.md
[website]: https://puremourning.github.io/vimspector-web

View file

@ -0,0 +1,11 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://puremourning.github.io/vimspector/schema/gadgets.schema.json",
"type": "object",
"properties": {
"adapters": {
"type": "object",
"additionalProperties": { "$ref": "vimspector.schema.json#/definitions/adapter" }
}
}
}

21
docs/schema/index.md Normal file
View file

@ -0,0 +1,21 @@
---
title: Configuration Schema
---
This area contains the [JSON Schema][json-schema] representation of the
configuration objects used for configuring Vimspector. For more information on
JSON Schema, check out [Understanding JSON
Schema](http://json-schema.org/understanding-json-schema)
Vimsepctor specification is based on [Draft 7][draft-7] of JSON Schema
standard.
## The schemas
* [`vimspector.schema.json`](vimspector.schema.json) - contains the full
configuration defnition (`.vimspector.json`).
* [`gadgets.schema.json`](gadgets.schema.json) - contains the specification for
gadget-only objects (`.gadgets.json`).
[json-schema]: http://json-schema.org
[draft-7]: https://json-schema.org/specification-links.html#draft-7

View file

@ -0,0 +1,298 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json",
"definitions": {
"variables": {
"description": "A mappoing of name/value pairs to set variables to be used elsewhere in the definition, or a list of such mappings.",
"properties": {
"variables": {
"oneOf": [
{ "$ref": "#/definitions/variables-mapping" },
{ "$ref": "#/definitions/variables-list" }
]
}
}
},
"variables-list": {
"type": "array",
"description": "A list of variable mappings. This can be useful where variable definitions rely on each other. By using a list, you can control the sequence in which variables are defined.",
"items": { "$ref": "#/definitions/variables-mapping" }
},
"variables-mapping": {
"type": "object",
"additionalProperties": {
"oneOf": [
{ "type": "string" },
{
"type": "object",
"required": [ "shell" ],
"properties": {
"shell": {
"type": [ "array", "string" ],
"description": "Command to run. If it's a string, it's split using Python's shelex splitting. Can contain other variable references."
},
"cwd": { "type": "string" },
"env": { "type": "object" }
}
}
]
}
},
"adapter-launchattach": {
"properties": {
"launch": {
"allOf": [
{ "$ref": "#/definitions/adapter-remote" },
{
"properties": {
"delay": {
"type": "string",
"description": "A time in the format understood by :help :sleep to wait after running the attachCommand(s)"
}
}
}
]
},
"attach": {
"allOf": [
{ "$ref": "#/definitions/adapter-remote" },
{
"type": "object",
"required": [ "pidSelect" ],
"properties": {
"pidSelect": {
"enum": [ "ask", "none" ]
},
"pidProperty": {
"type": "string",
"description": "The launch config property which the PID should be injected into. Required when 'pidSelect' is 'ask'."
},
"delay": {
"type": "string",
"description": "A time in the format understood by :help :sleep to wait after running the attachCommand(s)"
}
}
}
]
}
}
},
"adapter-remote": {
"type": "object",
"properties": {
"remote": {
"type": "object",
"description": "Configures how Vimspector will marshal remote debugging requests. When remote debugging, Vimspector will either ssh to 'account'@'host' or docker exec -it to 'container' and run 'pidCommand', 'attachCommands', 'runCommands', etc. based on the 'remote-command' option in the debug configuration. If 'remote-command' is 'launch', it runs 'runCommand(s)', otherwise (it's 'attach') vimspector runs 'pidCommand', followed by 'attachCommand(s)'.Then it starts up the debug adapter with the debug configuration as normal. Usually this is configured with an 'attach' request (whether we remotely 'launched' or not). Once the initialization exchange is complete, Vimspector runs the optional 'initCompleteCommand' which can be used to force the application to break, e.g. by sending it SIGINT. This is required on some platforms which have buggy gdbservers (for example)",
"allOf": [
{
"oneOf": [
{ "required": [ "host" ] },
{ "required": [ "container" ] }
]
},
{
"properties": {
"account": {
"type": "string",
"description": "Remote account name used when ssh'ing. Defaults to the current user account."
},
"host": {
"type": "string",
"description": "Name of the remote host to connect to (via passwordless SSH)."
},
"container": {
"type": "string",
"description": "Name or container id of the docker run container to connect to (via docker exec). Note the container must already be running (Vimspector will not start it) and it must have the port forwarded to the host if subsequently connecting via a port (for example <tt>docker run -p 8765:8765 -it simple_python</tt>)."
}
}
},
{
"oneOf": [
{
"allOf": [
{
"oneOf": [
{ "required": [ "attachCommand" ] },
{ "required": [ "attachCommands" ] }
]
},
{
"properties": {
"initCompleteCommand": {
"type": "array",
"items": { "type": "string" },
"description": "For remote-attach. Remote command to execute after initialization of the debug adapter. Can be used to work around buggy attach behaviour on certain platforms (advanced usage). Can contain the special token %PID% which is replaced with the PID returned by 'pidCommand'"
},
"pidCommand": {
"type": "array",
"items": { "type": "string" },
"description": "Required for remote-attach. Remote command to execute to return the PID to attach to."
},
"attachCommands": {
"type": [ "array" ],
"items": { "type": "array", "items": { "type": "string" } },
"description": "For remote-attach. List of commands to execute remotely to set up the attach. Can contain the special token %PID% which is replaced with the PID returned by the remote 'pidCommand'."
},
"attachCommand": {
"type": "array",
"items": { "type": "string" },
"description": "A single command to execute for remote-attach. Like attachCommands but for a single command. If attachCommands is supplied, this is not used."
}
}
}
]
},
{
"allOf": [
{
"oneOf": [
{ "required": [ "runCommand" ] },
{ "required": [ "runCommands" ] }
]
},
{
"properties": {
"runCommands": {
"type": [ "array" ],
"items": { "type": "array", "items": { "type": "string" } },
"description": "For remote-launch. List of commands to execute remotely to set up the launch. An entry in the array can be the special token '%CMD%' which is replaced with the evaluated 'remote-cmdLine' value in the debug configuration. This is useful to parameterize launcging remotely under something like gdbserver."
},
"runCommand": {
"type": "array",
"items": { "type": "string" },
"description": "A single command to execute for remote-launch. Like runCommands but for a single command."
}
}
}
]
}
]
}
]
}
}
},
"adapter": {
"allOf": [
{ "type": "object" },
{ "$ref": "#/definitions/variables" },
{
"properties": {
"name": {
"type": "string",
"description": "Passed to the adapter in the initialization request. Some adapters are particularly picky about what value goes here. Usually it can be omitted and Vimspector will send a generic value"
},
"configuration": {
"type": "object",
"description": "Base debug configuration. Can be used to set default values for all debug configurations. When reading individual debug configurations from 'configurations', those configurations are merged with this object. Definitions in the debug configuration override anything in this object. Typical usage for this is to set the 'type' parameter, which some debug adapters are very picky about, or to set e.g. the path to an underlying debugger."
}
}
},
{ "$ref": "#/definitions/adapter-launchattach" },
{
"anyOf": [
{ "required": [ "command" ] },
{ "required": [ "port" ] },
{ "required": [ "command", "port" ] }
]
},
{
"properties": {
"host": {
"type": "string",
"default": "127.0.0.1",
"description": "Connect to this host in multi-session mode"
},
"port": {
"oneOf": [
{ "type": "string" },
{ "type": "integer" }
],
"description": "If supplied, indicates that a socket connection should be made to this port on 'host'. If the value is 'ask', then the user is asked to enter the port number to connect to."
}
}
}
]
}
},
"type": "object",
"required": [ "configurations" ],
"properties": {
"adapters": {
"type": "object",
"additionalProperties": { "$ref": "#/definitions/adapter" }
},
"configurations": {
"type": "object",
"additionalProperties": {
"allOf": [
{ "$ref": "#/definitions/variables" },
{
"type": "object",
"properties": {
"adapter": {
"description": "Adapter configuration to use for this debug session",
"oneOf": [
{ "$ref": "#/definitions/adapter" },
{
"type": "string",
"description": "Name of an adapter in the 'adapters' mapping"
}
]
},
"remote-request": {
"enum": [ "launch", "attach" ],
"description": "When the 'remote' block is defined in the adapter configuration, this can be used to override the actual action taken (remotely). Usually the actual 'configuration' will contain 'request' of 'attach', but in order to remotely 'launch' the process (e.g. under gdbserver or equivalent), use remote-attach set to 'launch'"
},
"remote-cmdLine": {
"type": [ "string", "array" ],
"description": "Defines the value of the special token %CMD% in remote-launch 'runCommand(s)'. The value is inserted into the command line where an entry matching '%CMD%' is found in 'runCommand(s)' command array."
},
"configuration": {
"type": "object",
"required": [ "request" ],
"properties": {
"request": {
"enum": [ "launch", "attach" ],
"description": "Type of session - launch process or attach to process"
}
},
"additionalProperties": {
"description": "Additional properties are passed to the debug adapter in the 'launch' or 'attach' request and are specific to the debug adapter."
}
},
"breakpoints": {
"type": "object",
"properties": {
"line": {
"$comment": "TBA: Not supported yet"
},
"function": {
"$comment": "TBA: Not supported yet"
},
"exception": {
"type": "object",
"description": "Exception breakpoints configuration, mapping the server's exception filter to enabled/disable/default flag",
"additionalProperties": {
"oneOf": [
{
"type": "boolean",
"description": "true = enable, false = disable"
},
{
"type": "string",
"enum": [ "Y", "N", "" ],
"description": "Y = enable, N = disable, '' = default"
}
]
}
}
}
}
}
}
]
}
}
}
}

View file

@ -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,506 +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
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.23.1',
},
'linux': {
'file_name': 'cpptools-linux.vsix',
'checksum':
'c0f424bd6d5e016d70126587c80b92d981729c708ce524f2cce4c3f524b41d71'
},
'macos': {
'file_name': 'cpptools-osx.vsix',
'checksum':
'431692395ba243ea20428e083d5df3201a0dbda31a66eab7729da0f377def5fd',
},
'windows': {
'file_name': 'cpptools-win32.vsix',
'checksum': None,
},
"adapters": {
"vscode-cpptools": {
"name": "cppdbg",
"command": [
"${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7"
],
"attach": {
"pidProperty": "processId",
"pidSelect": "ask"
},
},
},
},
'vscode-python': {
'language': 'python',
'download': {
'url': 'https://github.com/Microsoft/vscode-python/releases/download/'
'${version}/${file_name}',
},
'all': {
'version': '2019.10.41019',
'file_name': 'ms-python-release.vsix',
'checksum':
'38e8bf782fc6d2dc904868add2e1e5dc66197a06a902f6d17e15f96d4e9bf16b',
},
'adapters': {
"vscode-python": {
"name": "vscode-python",
"command": [
"node",
"${gadgetDir}/vscode-python/out/client/debugger/debugAdapter/main.js",
],
}
},
},
'tclpro': {
'language': 'tcl',
'repo': {
'url': 'https://github.com/puremourning/TclProDebug',
'ref': 'f5c56b7067661ce84e205765060224076569ae0e', # master 26/10/2019
},
'do': lambda name, root: InstallTclProDebug( name, root )
},
'netcoredbg': {
'language': 'csharp',
'enabled': False,
'download': {
'url': 'https://github.com/Samsung/netcoredbg/releases/download/latest/'
'${file_name}',
'format': 'tar',
},
'all': {
'version': 'master'
},
'macos': {
'file_name': 'netcoredbg-osx-master.tar.gz',
'checksum': '',
},
'linux': {
'file_name': 'netcoredbg-linux-master.tar.gz',
'checksum': '',
},
'do': lambda name, root: MakeSymlink( gadget_dir,
name,
os.path.join( root, 'netcoredbg' ) ),
'adapters': {
'netcoredbg': {
"name": "netcoredbg",
"command": [
"${gadgetDir}/netcoredbg/netcoredbg",
"--interpreter=vscode"
],
"attach": {
"pidProperty": "processId",
"pidSelect": "ask"
},
},
}
},
'vscode-mono-debug': {
'language': 'csharp',
'enabled': False,
'download': {
'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/'
'publishers/ms-vscode/vsextensions/mono-debug/${version}/'
'vspackage',
'target': 'vscode-mono-debug.vsix.gz',
'format': 'zip.gz',
},
'all': {
'file_name': 'vscode-mono-debug.vsix',
'version': '0.15.8',
'checksum':
'723eb2b621b99d65a24f215cb64b45f5fe694105613a900a03c859a62a810470',
},
'adapters': {
'vscode-mono-debug': {
"name": "mono-debug",
"command": [
"mono",
"${gadgetDir}/vscode-mono-debug/bin/Release/mono-debug.exe"
],
"attach": {
"pidSelect": "none"
},
},
}
},
'vscode-bash-debug': {
'language': 'bash',
'download': {
'url': 'https://github.com/rogalmic/vscode-bash-debug/releases/'
'download/${version}/${file_name}',
},
'all': {
'file_name': 'bash-debug-0.3.5.vsix',
'version': 'v0.3.5',
'checksum': '',
}
},
'vscode-go': {
'language': 'go',
'download': {
'url': 'https://github.com/microsoft/vscode-go/releases/download/'
'${version}/${file_name}'
},
'all': {
'version': '0.11.4',
'file_name': 'Go-0.11.4.vsix',
'checksum':
'ff7d7b944da5448974cb3a0086f4a2fd48e2086742d9c013d6964283d416027e'
},
'adapters': {
'vscode-go': {
'name': 'delve',
'command': [
'node',
'${gadgetDir}/vscode-go/out/src/debugAdapter/goDebug.js'
],
},
},
},
'vscode-node-debug2': {
'language': 'node',
'enabled': False,
'repo': {
'url': 'https://github.com/microsoft/vscode-node-debug2',
'ref': 'v1.39.1',
},
'do': lambda name, root: InstallNodeDebug( name, root ),
'adapters': {
'vscode-node': {
'name': 'node2',
'type': 'node2',
'command': [
'node',
'${gadgetDir}/vscode-node-debug2/out/src/nodeDebug.js'
]
},
},
},
'debugger-for-chrome': {
'language': 'chrome',
'enabled': False,
'download': {
'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/'
'publishers/msjsdiag/vsextensions/'
'debugger-for-chrome/${version}/vspackage',
'target': 'msjsdiag.debugger-for-chrome-4.12.0.vsix.gz',
'format': 'zip.gz',
},
'all': {
'version': '4.12.0',
'file_name': 'msjsdiag.debugger-for-chrome-4.12.0.vsix',
'checksum':
'0df2fe96d059a002ebb0936b0003e6569e5a5c35260dc3791e1657d27d82ccf5'
},
'adapters': {
'chrome': {
'name': 'debugger-for-chrome',
'type': 'chrome',
'command': [
'node',
'${gadgetDir}/debugger-for-chrome/out/src/chromeDebug.js'
],
},
},
},
}
# ------------------------------------------------------------------------------
# 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 InstallTclProDebug( name, root ):
configure = [ './configure' ]
if OS == 'macos':
# Apple removed the headers from system frameworks because they are
# determined to make life difficult. And the TCL configure scripts are super
# old so don't know about this. So we do their job for them and try and find
# a tclConfig.sh.
#
# NOTE however that in Apple's infinite wisdom, installing the "headers" in
# the other location is actually broken because the paths in the
# tclConfig.sh are pointing at the _old_ location. You actually do have to
# run the package installation which puts the headers back in order to work.
# This is why the below list is does not contain stuff from
# /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform
# '/Applications/Xcode.app/Contents/Developer/Platforms'
# '/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System'
# '/Library/Frameworks/Tcl.framework',
# '/Applications/Xcode.app/Contents/Developer/Platforms'
# '/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System'
# '/Library/Frameworks/Tcl.framework/Versions'
# '/Current',
for p in [ '/usr/local/opt/tcl-tk/lib' ]:
if os.path.exists( os.path.join( p, 'tclConfig.sh' ) ):
configure.append( '--with-tcl=' + p )
break
with CurrentWorkingDir( os.path.join( root, 'lib', 'tclparser' ) ):
subprocess.check_call( configure )
subprocess.check_call( [ 'make' ] )
MakeSymlink( gadget_dir, name, root )
def InstallNodeDebug( name, root ):
node_version = subprocess.check_output( [ 'node', '--version' ],
universal_newlines=True ).strip()
print( "Node.js version: {}".format( node_version ) )
if list( map( int, node_version[ 1: ].split( '.' ) ) ) >= [ 12, 0, 0 ]:
print( "Can't install vscode-debug-node2:" )
print( "Sorry, you appear to be running node 12 or later. That's not "
"compatible with the build system for this extension, and as far as "
"we know, there isn't a pre-built independent package." )
print( "My advice is to install nvm, then do:" )
print( " $ nvm install --lts 10" )
print( " $ nvm use --lts 10" )
print( " $ ./install_gadget.py --enable-node ..." )
raise RuntimeError( 'Invalid node environent for node debugger' )
with CurrentWorkingDir( root ):
subprocess.check_call( [ 'npm', 'install' ] )
subprocess.check_call( [ 'npm', 'run', 'build' ] )
MakeSymlink( gadget_dir, name, root )
def WithRetry( f ):
retries = 5
timeout = 1 # seconds
@functools.wraps( f )
def wrapper( *args, **kwargs ):
thrown = None
for _ in range( retries ):
try:
return f( *args, **kwargs )
except Exception as e:
thrown = e
print( "Failed - {}, will retry in {} seconds".format( e, timeout ) )
time.sleep( timeout )
raise thrown
return wrapper
@WithRetry
def UrlOpen( *args, **kwargs ):
return urllib2.urlopen( *args, **kwargs )
def DownloadFileTo( url, destination, file_name = None, checksum = None ):
if not file_name:
file_name = url.split( '/' )[ -1 ]
file_path = os.path.abspath( os.path.join( destination, file_name ) )
if not os.path.isdir( destination ):
os.makedirs( destination )
if os.path.exists( file_path ):
if checksum:
if ValidateCheckSumSHA256( file_path, checksum ):
print( "Checksum matches for {}, using it".format( file_path ) )
return file_path
else:
print( "Checksum doesn't match for {}, removing it".format(
file_path ) )
print( "Removing existing {}".format( file_path ) )
os.remove( file_path )
r = urllib2.Request( url, headers = { 'User-Agent': 'Vimspector' } )
print( "Downloading {} to {}/{}".format( url, destination, file_name ) )
with contextlib.closing( UrlOpen( r ) ) as u:
with open( file_path, 'wb' ) as f:
f.write( u.read() )
if checksum:
if not ValidateCheckSumSHA256( file_path, checksum ):
raise RuntimeError(
'Checksum for {} ({}) does not match expected {}'.format(
file_path,
GetChecksumSHA254( file_path ),
checksum ) )
else:
print( "Checksum for {}: {}".format( file_path,
GetChecksumSHA254( file_path ) ) )
return file_path
def GetChecksumSHA254( file_path ):
with open( file_path, 'rb' ) as existing_file:
return hashlib.sha256( existing_file.read() ).hexdigest()
def ValidateCheckSumSHA256( file_path, checksum ):
existing_sha256 = GetChecksumSHA254( file_path )
return existing_sha256 == checksum
def RemoveIfExists( destination ):
if os.path.exists( destination ) or os.path.islink( destination ):
if os.path.islink( destination ):
print( "Removing file {}".format( destination ) )
os.remove( destination )
else:
print( "Removing dir {}".format( destination ) )
shutil.rmtree( destination )
# Python's ZipFile module strips execute bits from files, for no good reason
# other than crappy code. Let's do it's job for it.
class ModePreservingZipFile( zipfile.ZipFile ):
def extract( self, member, path = None, pwd = None ):
if not isinstance( member, zipfile.ZipInfo ):
member = self.getinfo( member )
if path is None:
path = os.getcwd()
ret_val = self._extract_member( member, path, pwd )
attr = member.external_attr >> 16
os.chmod( ret_val, attr )
return ret_val
def ExtractZipTo( file_path, destination, format ):
print( "Extracting {} to {}".format( file_path, destination ) )
RemoveIfExists( destination )
if format == 'zip':
with ModePreservingZipFile( file_path ) as f:
f.extractall( path = destination )
elif format == 'zip.gz':
with gzip.open( file_path, 'rb' ) as f:
file_contents = f.read()
with ModePreservingZipFile( BytesIO( file_contents ) ) as f:
f.extractall( path = destination )
elif format == 'tar':
try:
with tarfile.open( file_path ) as f:
f.extractall( path = destination )
except Exception:
# There seems to a bug in python's tarfile that means it can't read some
# windows-generated tar files
os.makedirs( destination )
with CurrentWorkingDir( destination ):
subprocess.check_call( [ 'tar', 'zxvf', file_path ] )
def MakeExtensionSymlink( name, root ):
MakeSymlink( gadget_dir, name, os.path.join( root, 'extension' ) ),
def MakeSymlink( in_folder, link, pointing_to ):
RemoveIfExists( os.path.join( in_folder, link ) )
in_folder = os.path.abspath( in_folder )
pointing_to = os.path.relpath( os.path.abspath( pointing_to ),
in_folder )
os.symlink( pointing_to, os.path.join( in_folder, link ) )
def CloneRepoTo( url, ref, destination ):
RemoveIfExists( destination )
subprocess.check_call( [ 'git', 'clone', url, destination ] )
subprocess.check_call( [ 'git', '-C', destination, 'checkout', ref ] )
subprocess.check_call( [ 'git', 'submodule', 'sync', '--recursive' ] )
subprocess.check_call( [ 'git',
'submodule',
'update',
'--init',
'--recursive' ] )
OS = install.GetOS()
gadget_dir = install.GetGadgetDir( os.path.dirname( __file__ ), OS )
print( 'OS = ' + OS )
print( 'gadget_dir = ' + gadget_dir )
parser = argparse.ArgumentParser()
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' )
@ -523,103 +69,186 @@ parser.add_argument( '--force-all',
action = 'store_true',
help = 'Enable all unsupported completers' )
done_languages = set()
for name, gadget in GADGETS.items():
lang = gadget[ 'language' ]
if lang in done_languages:
continue
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" )
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",
action = "store_true",
help = "Do not verify SSL certificates for file downloads."
)
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 )
file_path = DownloadFileTo(
url,
destination,
file_name = gadget[ 'download' ].get( 'target' ),
checksum = v.get( 'checksum' ) )
root = os.path.join( destination, 'root' )
ExtractZipTo( file_path,
root,
format = gadget[ 'download' ].get( 'format', 'zip' ) )
elif 'repo' in gadget:
url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( v )
ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( v )
destination = os.path.join( gadget_dir, 'download', name )
CloneRepoTo( url, ref, destination )
root = destination
if 'do' in gadget:
gadget[ 'do' ]( name, root )
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 )
installer.InstallGagdet( name,
gadget,
manifest,
succeeded,
failed,
all_adapters )
with open( install.GetGadgetConfigFile( os.path.dirname( __file__ ) ),
'w' ) as f:
json.dump( { 'adapters': all_adapters }, f, indent=2, sort_keys=True )
if args.no_gadget_config:
print( "" )
print( "Would write the following gadgets: " )
installer.WriteAdapters( all_adapters, to_file = sys.stdout )
else:
installer.WriteAdapters( all_adapters )
manifest.Write()
if args.basedir:
print( "" )
print( "***NOTE***: You set --basedir to " + args.basedir +
". Therefore you _must_ ensure this is in your vimrc:\n"
"let g:vimspector_base_dir='" + vimspector_base + "'" )
if succeeded:
print( "Done" )
print( "The following adapters were installed successfully:\n - {}".format(
'\n - '.join( succeeded ) ) )
if failed:
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 '' ) )

View file

@ -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

View file

@ -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
@ -26,38 +33,127 @@ if exists( 'g:loaded_vimpector' )
call s:restore_cpo()
finish
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 <silent> <Plug>VimspectorContinue
\ :<c-u>call vimspector#Continue()<CR>
nnoremap <silent> <Plug>VimspectorLaunch
\ :<c-u>call vimspector#Launch( v:true )<CR>
nnoremap <silent> <Plug>VimspectorStop
\ :<c-u>call vimspector#Stop()<CR>
nnoremap <silent> <Plug>VimspectorRestart
\ :<c-u>call vimspector#Restart()<CR>
nnoremap <silent> <Plug>VimspectorPause
\ :<c-u>call vimspector#Pause()<CR>
nnoremap <silent> <Plug>VimspectorToggleBreakpoint
\ :<c-u>call vimspector#ToggleBreakpoint()<CR>
nnoremap <silent> <Plug>VimspectorToggleConditionalBreakpoint
\ :<c-u>call vimspector#ToggleBreakpoint(
\ { 'condition': input( 'Enter condition expression: ' ),
\ 'hitCondition': input( 'Enter hit count expression: ' ) }
\ )<CR>
nnoremap <silent> <Plug>VimspectorAddFunctionBreakpoint
\ :<c-u>call vimspector#AddFunctionBreakpoint( expand( '<cexpr>' ) )<CR>
nnoremap <silent> <Plug>VimspectorStepOver
\ :<c-u>call vimspector#StepOver()<CR>
nnoremap <silent> <Plug>VimspectorStepInto
\ :<c-u>call vimspector#StepInto()<CR>
nnoremap <silent> <Plug>VimspectorStepOut
\ :<c-u>call vimspector#StepOut()<CR>
nnoremap <silent> <Plug>VimspectorRunToCursor
\ :<c-u>call vimspector#RunToCursor()<CR>
" Eval for normal mode
nnoremap <silent> <Plug>VimspectorBalloonEval
\ :<c-u>call vimspector#ShowEvalBalloon( 0 )<CR>
" And for visual modes
xnoremap <silent> <Plug>VimspectorBalloonEval
\ :<c-u>call vimspector#ShowEvalBalloon( 1 )<CR>
nnoremap <silent> <Plug>VimspectorUpFrame
\ :<c-u>call vimspector#UpFrame()<CR>
nnoremap <silent> <Plug>VimspectorDownFrame
\ :<c-u>call vimspector#DownFrame()<CR>
if s:mappings ==# 'VISUAL_STUDIO'
nnoremap <F5> :call vimspector#Continue()<CR>
nnoremap <S-F5> :call vimspector#Stop()<CR>
nnoremap <C-S-F5> :call vimspector#Restart()<CR>
nnoremap <F6> :call vimspector#Pause()<CR>
nnoremap <F9> :call vimspector#ToggleBreakpoint()<CR>
nnoremap <S-F9> :call vimspector#AddFunctionBreakpoint( expand( '<cexpr>' ) )<CR>
nnoremap <F10> :call vimspector#StepOver()<CR>
nnoremap <F11> :call vimspector#StepInto()<CR>
nnoremap <S-F11> :call vimspector#StepOut()<CR>
nmap <F5> <Plug>VimspectorContinue
nmap <S-F5> <Plug>VimspectorStop
nmap <C-S-F5> <Plug>VimspectorRestart
nmap <F6> <Plug>VimspectorPause
nmap <F9> <Plug>VimspectorToggleBreakpoint
nmap <S-F9> <Plug>VimspectorAddFunctionBreakpoint
nmap <F10> <Plug>VimspectorStepOver
nmap <F11> <Plug>VimspectorStepInto
nmap <S-F11> <Plug>VimspectorStepOut
elseif s:mappings ==# 'HUMAN'
nnoremap <F5> :call vimspector#Continue()<CR>
nnoremap <F3> :call vimspector#Stop()<CR>
nnoremap <F4> :call vimspector#Restart()<CR>
nnoremap <F6> :call vimspector#Pause()<CR>
nnoremap <F9> :call vimspector#ToggleBreakpoint()<CR>
nnoremap <F8> :call vimspector#AddFunctionBreakpoint( expand( '<cexpr>' ) )<CR>
nnoremap <F10> :call vimspector#StepOver()<CR>
nnoremap <F11> :call vimspector#StepInto()<CR>
nnoremap <F12> :call vimspector#StepOut()<CR>
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
endif
"}}}
command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr
\ VimspectorWatch
\ call vimspector#AddWatch( <f-args> )
command! -bar -nargs=? -complete=custom,vimspector#CompleteOutput
\ VimspectorShowOutput
\ call vimspector#ShowOutput( <f-args> )
command! -bar
\ VimspectorToggleLog
\ call vimspector#ToggleLog()
command! -bar
\ VimspectorDebugInfo
\ call vimspector#PrintDebugInfo()
command! -nargs=1 -complete=custom,vimspector#CompleteExpr
\ VimspectorEval
\ call vimspector#Evaluate( <f-args> )
command! -bar
\ VimspectorReset
\ call vimspector#Reset( { 'interactive': v:true } )
" Installer commands
command! -bar -bang -nargs=* -complete=custom,vimspector#CompleteInstall
\ VimspectorInstall
\ call vimspector#Install( <q-bang>, <f-args> )
command! -bar -bang -nargs=*
\ VimspectorUpdate
\ call vimspector#Update( <q-bang>, <f-args> )
command! -bar -nargs=0
\ VimspectorAbortInstall
\ call vimspector#AbortInstall()
" Dummy autocommands so that we can call this whenever
augroup VimspectorUserAutoCmds
autocmd!
autocmd User VimspectorUICreated silent
autocmd User VimspectorTerminalOpened silent
autocmd user VimspectorJumpedToFrame silent
autocmd user VimspectorDebugEnded silent
augroup END
" FIXME: Only register this _while_ debugging is active
augroup Vimspector
autocmd!
autocmd BufNew * call vimspector#OnBufferCreated( expand( '<afile>' ) )
augroup END
" boilerplate {{{
call s:restore_cpo()
" }}}

View file

@ -21,7 +21,7 @@ import os
import logging
import json
from vimspector import utils
from vimspector import utils, signs
class ServerBreakpointHandler( object ):
@ -44,6 +44,7 @@ class ProjectBreakpoints( object ):
self._line_breakpoints = defaultdict( list )
self._func_breakpoints = []
self._exception_breakpoints = None
self._configured_breakpoints = {}
# FIXME: Remove this. Remove breakpoints nonesense from code.py
self._breakpoints_handler = None
@ -51,9 +52,23 @@ class ProjectBreakpoints( object ):
self._next_sign_id = 1
# TODO: Change to sign_define ?
vim.command( 'sign define vimspectorBP text==> texthl=Error' )
vim.command( 'sign define vimspectorBPDisabled text=!> texthl=Warning' )
if not signs.SignDefined( 'vimspectorBP' ):
signs.DefineSign( 'vimspectorBP',
text = '',
double_text = '',
texthl = 'WarningMsg' )
if not signs.SignDefined( 'vimspectorBPCond' ):
signs.DefineSign( 'vimspectorBPCond',
text = '',
double_text = '',
texthl = 'WarningMsg' )
if not signs.SignDefined( 'vimspectorBPDisabled' ):
signs.DefineSign( 'vimspectorBPDisabled',
text = '',
double_text = '',
texthl = 'LineNr' )
def ConnectionUp( self, connection ):
@ -73,8 +88,10 @@ class ProjectBreakpoints( object ):
# NOTE: we don't reset self._exception_breakpoints because we don't want to
# re-ask the user every time for the sane info.
# FIXME: If the adapter type changes, we should probably forget this ?
def ListBreakpoints( self ):
def BreakpointsAsQuickFix( self ):
# FIXME: Handling of breakpoints is a mess, split between _codeView and this
# object. This makes no sense and should be centralised so that we don't
# have this duplication and bug factory.
@ -84,14 +101,16 @@ class ProjectBreakpoints( object ):
else:
for file_name, breakpoints in self._line_breakpoints.items():
for bp in breakpoints:
self._SignToLine( file_name, bp )
qf.append( {
'filename': file_name,
'lnum': bp[ 'line' ],
'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:
@ -101,71 +120,152 @@ 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.
for file_name, breakpoints in self._line_breakpoints.items():
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 ] ):
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
@ -173,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 ):
@ -185,6 +287,10 @@ class ProjectBreakpoints( object ):
self._breakpoints_handler = handler
def SetConfiguredBreakpoints( self, configured_breakpoints ):
self._configured_breakpoints = configured_breakpoints
def SendBreakpoints( self, doneHandler = None ):
assert self._breakpoints_handler is not None
@ -193,27 +299,59 @@ class ProjectBreakpoints( object ):
awaiting = 0
def response_handler( source, msg ):
if msg:
self._breakpoints_handler.AddBreakpoints( source, msg )
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, temp_idxs = [] ):
if msg:
self._breakpoints_handler.AddBreakpoints( source, msg )
breakpoints = ( msg.get( 'body' ) or {} ).get( 'breakpoints' ) or []
self._UpdateTemporaryBreakpoints( breakpoints, temp_idxs )
response_received()
# NOTE: Must do this _first_ otherwise we might send requests and get
# replies before we finished sending all the requests.
if self._exception_breakpoints is None:
self._SetUpExceptionBreakpoints( self._configured_breakpoints )
# TODO: add the _configured_breakpoints to line_breakpoints
for file_name, line_breakpoints in self._line_breakpoints.items():
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 ),
@ -222,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': {
@ -230,27 +374,34 @@ class ProjectBreakpoints( object ):
'breakpoints': breakpoints,
},
'sourceModified': False, # TODO: We can actually check this
}
},
failure_handler = response_received
)
# TODO: Add the _configured_breakpoints to function breakpoints
if self._server_capabilities.get( 'supportsFunctionBreakpoints' ):
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 = response_received
)
if self._exception_breakpoints is None:
self._SetUpExceptionBreakpoints()
if self._exception_breakpoints:
awaiting = awaiting + 1
self._connection.DoRequest(
@ -258,14 +409,15 @@ class ProjectBreakpoints( object ):
{
'command': 'setExceptionBreakpoints',
'arguments': self._exception_breakpoints
}
},
failure_handler = response_received
)
if awaiting == 0 and doneHandler:
doneHandler()
def _SetUpExceptionBreakpoints( self ):
def _SetUpExceptionBreakpoints( self, configured_breakpoints ):
exception_breakpoint_filters = self._server_capabilities.get(
'exceptionBreakpointFilters',
[] )
@ -278,14 +430,27 @@ class ProjectBreakpoints( object ):
# trigger requesting threads etc.). See the note in
# debug_session.py:_Initialise for more detials
exception_filters = []
configured_filter_options = configured_breakpoints.get( 'exception', {} )
if exception_breakpoint_filters:
for f in exception_breakpoint_filters:
default_value = 'Y' if f.get( 'default' ) else 'N'
result = utils.AskForInput(
"Break on {} (Y/N/default: {})? ".format( f[ 'label' ],
default_value ),
default_value )
if f[ 'filter' ] in configured_filter_options:
result = configured_filter_options[ f[ 'filter' ] ]
if isinstance( result, bool ):
result = 'Y' if result else 'N'
if not isinstance( result, str ) or result not in ( 'Y', 'N', '' ):
raise ValueError(
f"Invalid value for exception breakpoint filter '{f}': "
f"'{result}'. Must be boolean, 'Y', 'N' or '' (default)" )
else:
result = utils.AskForInput(
"{}: Break on {} (Y/N/default: {})? ".format( f[ 'filter' ],
f[ 'label' ],
default_value ),
default_value )
if result == 'Y':
exception_filters.append( f[ 'filter' ] )
@ -302,20 +467,46 @@ 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': bp[ 'sign_id' ], 'group': 'VimspectorBP', } ) ) )
if len( signs ) == 1 and len( signs[ 0 ][ 'signs' ] ) == 1:
bp[ 'line' ] = int( signs[ 0 ][ 'signs' ][ 0 ][ 'lnum' ] )
return bp[ 'line' ]

View file

@ -18,63 +18,133 @@ import logging
import json
from collections import defaultdict
from vimspector import utils
from vimspector import utils, terminal, signs
class CodeView( object ):
def __init__( self, window ):
def __init__( self, window, api_prefix ):
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>' )
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' ] ) )
return False
# SIC: column is 0-based, line is 1-based in vim. Why? Nobody knows.
# Note: max() with 0 because some debug adapters (go) return 0 for the
# column.
try:
self._window.cursor = ( frame[ 'line' ], frame[ 'column' ] - 1 )
self._window.cursor = ( frame[ 'line' ], max( frame[ 'column' ] - 1, 0 ) )
except vim.error:
self._logger.exception( "Unable to jump to %s:%s in %s, maybe the file "
"doesn't exist",
@ -83,25 +153,21 @@ class CodeView( object ):
frame[ 'source' ][ 'path' ] )
return False
self._signs[ 'vimspectorPC' ] = self._next_sign_id
self._next_sign_id += 1
self.current_syntax = utils.ToUnicode(
vim.current.buffer.options[ 'syntax' ] )
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.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
def Reset( self ):
self.ClearBreakpoints()
@ -109,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 ):
@ -137,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' ] = []
@ -160,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 = []
@ -189,50 +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 'finished' in vim.eval( 'term_getstatus( {} )'.format(
self._terminal_buffer_number ) ) ):
window_for_start = self._terminal_window
options[ 'curwin' ] = 1
buffer_number = None
terminal_window = None
with utils.TemporaryVimOptions( { 'splitright': True,
'equalalways': False } ):
with utils.LetCurrentWindow( window_for_start ):
# TODO/FIXME: Do something about closing this when we reset ?
vim_cmd = 'term_start( {}, {} )'.format( json.dumps( args ),
json.dumps( options ) )
self._logger.debug( 'Start terminal: {}'.format( vim_cmd ) )
buffer_number = int( vim.eval( vim_cmd ) )
terminal_window = vim.current.window
if buffer_number is None or buffer_number <= 0:
# TODO: Do something better like reject the request?
raise ValueError( "Unable to start terminal" )
else:
self._terminal_window = terminal_window
self._terminal_buffer_number = buffer_number
return buffer_number
# FIXME: Change this tor return the PID rather than having debug_session
# work that out
return self._terminal.buffer_number

View 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' ] )

View file

@ -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 = {}
@ -65,6 +65,33 @@ class DebugAdapterConnection( object ):
if not self._SendMessage( msg ):
self._AbortRequest( request, 'Unable to send message' )
def DoRequestSync( self, msg, timeout = 5000 ):
result = {}
def handler( msg ):
result[ 'response' ] = msg
def failure_handler( reason, msg ):
result[ 'response' ] = msg
result[ 'exception' ] = RuntimeError( reason )
self.DoRequest( handler, msg, failure_handler, timeout )
bug_catcher = 1000
while not result and bug_catcher >= 0:
vim.command( 'sleep 10m' )
bug_catcher -= 10
if result.get( 'exception' ) is not None:
raise result[ 'exception' ]
if result.get( 'response' ) is None:
raise RuntimeError( "No response" )
return result[ 'response' ]
def OnRequestTimeout( self, timer_id ):
request_id = None
for seq, request in self._outstanding_requests.items():
@ -97,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()
@ -142,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 ) )
@ -195,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 ) )
@ -206,7 +242,7 @@ class DebugAdapterConnection( object ):
def _OnMessageReceived( self, message ):
if not self._handler:
if not self._handlers:
return
if message[ 'type' ] == 'response':
@ -239,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

View 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()

View 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'
}
}
},
},
}

View file

@ -26,10 +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 ), '.gadgets.d' )
def GetConfigDirForFiletype( vimspector_base, filetype ):
if not filetype:
filetype = 'default'
return os.path.join( os.path.abspath( vimspector_base ),
'configurations',
GetOS(),
filetype )

View 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." )

View file

@ -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,23 +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 ):
"""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' )
@ -67,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 )
@ -78,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
@ -89,119 +285,30 @@ 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 )
try:
vim.command( 'bdelete! {0}'.format( tab_buffer.buf.number ) )
except vim.error as e:
# FIXME: For now just ignore the "no buffers were deleted" error
if 'E516' not in e:
raise
self._buffers = {}
def _ShowOutput( self, category ):
utils.JumpToWindow( self._window )
vim.command( 'bu {0}'.format( self._buffers[ category ].buf.name ) )
vim.command( 'normal G' )
def ShowOutput( self, category ):
self._ToggleFlag( category, False )
self._ShowOutput( category )
def Evaluate( self, frame, expression ):
if not frame:
self.Print( 'Console', 'There is no current stack frame' )
return
console = self._buffers[ 'Console' ].buf
utils.AppendToBuffer( console, 'Evaluating: ' + expression )
def Evaluate( self, frame, expression, verbose ):
if verbose:
self._Print( 'Console', f"Evaluating: { expression }" )
def print_result( message ):
utils.AppendToBuffer( console,
'Evaluated: ' + expression )
result = message[ 'body' ][ 'result' ]
if result is None:
result = 'null'
result = '<no result>'
self._Print( 'Console', result.splitlines() )
utils.AppendToBuffer( console, ' Result: ' + result )
def print_failure( reason, msg ):
self._Print( 'Console', reason.splitlines() )
self._connection.DoRequest( print_result, {
request = {
'command': 'evaluate',
'arguments': {
'expression': expression,
'context': 'repl',
'frameId': frame[ 'id' ],
}
} )
}
def _ToggleFlag( self, category, flag ):
if self._buffers[ category ].flag != flag:
self._buffers[ category ].flag = flag
with utils.LetCurrentWindow( self._window ):
self._RenderWinBar( category )
if frame:
request[ 'arguments' ][ 'frameId' ] = frame[ 'id' ]
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._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 '' ) )
self._connection.DoRequest( print_result,
request,
print_failure )

View 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

View 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 }' )

View file

@ -16,59 +16,168 @@
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._currentThread = None
self._currentFrame = None
self._current_thread = None
self._current_frame = None
self._current_syntax = ""
self._threads = []
self._sources = {}
self._scratch_buffers = []
utils.SetUpScratchBuffer( self._buf, 'vimspector.StackTrace' )
vim.current.buffer = self._buf
vim.command( 'nnoremap <buffer> <CR> :call vimspector#GoToFrame()<CR>' )
# 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
utils.SetUpHiddenBuffer( self._buf, 'vimspector.StackTrace' )
utils.SetUpUIWindow( win )
mappings = settings.Dict( 'mappings' )[ 'stack_trace' ]
with utils.LetCurrentWindow( win ):
for mapping in utils.GetVimList( mappings, 'expand_or_jump' ):
vim.command( f'nnoremap <silent> <buffer> { mapping } '
':<C-U>call vimspector#GoToFrame()<CR>' )
for mapping in utils.GetVimList( mappings, 'focus_thread' ):
vim.command( f'nnoremap <silent> <buffer> { mapping } '
':<C-U>call vimspector#SetCurrentThread()<CR>' )
if utils.UseWinBar():
vim.command( 'nnoremenu <silent> 1.1 WinBar.Pause/Continue '
':call vimspector#PauseContinueThread()<CR>' )
vim.command( 'nnoremenu <silent> 1.2 WinBar.Expand/Collapse '
':call vimspector#GoToFrame()<CR>' )
vim.command( 'nnoremenu <silent> 1.3 WinBar.Focus '
':call vimspector#SetCurrentThread()<CR>' )
win.options[ 'cursorline' ] = False
win.options[ 'signcolumn' ] = 'auto'
if not signs.SignDefined( 'vimspectorCurrentThread' ):
signs.DefineSign( 'vimspectorCurrentThread',
text = '',
double_text = '',
texthl = 'MatchParen',
linehl = 'CursorLine' )
if not signs.SignDefined( 'vimspectorCurrentFrame' ):
signs.DefineSign( 'vimspectorCurrentFrame',
text = '',
double_text = '',
texthl = 'Special',
linehl = 'CursorLine' )
self._line_to_frame = {}
self._line_to_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 ):
return self._currentThread
return self._current_thread
def GetCurrentFrame( self ):
return self._currentFrame
return self._current_frame
def Clear( self ):
self._currentFrame = None
self._currentThread = None
self._threads = []
self._current_frame = None
self._current_thread = None
self._current_syntax = ""
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()
@ -76,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._currentThread:
self._LoadStackTrace( thread, True )
elif infer_current_frame and self._currentThread is None:
self._currentThread = 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()
@ -146,34 +328,116 @@ 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' ]:
self._currentFrame = frame
return self._session.SetCurrentFrame( self._currentFrame )
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
self._DrawThreads()
return self._session.SetCurrentFrame( self._current_frame, reason )
return False
source = frame[ 'source' ]
source = frame.get( 'source' ) or {}
if source.get( 'sourceReference', 0 ) > 0:
def handle_resolved_source( resolved_source ):
frame[ 'source' ] = resolved_source
@ -185,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._currentThread = event[ 'threadId' ]
elif event.get( 'allThreadsStopped', False ) and self._threads:
self._currentThread = self._threads[ 0 ][ 'id' ]
threadId = event.get( 'threadId' )
allThreadsStopped = event.get( 'allThreadsStopped', False )
if self._currentThread is not None:
for thread in self._threads:
if thread[ 'id' ] == self._currentThread:
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 ):
if event[ 'reason' ] == 'started' and self._currentThread is None:
self._currentThread = event[ 'threadId' ]
self.LoadThreads( True )
infer_current_frame = False
if event[ 'reason' ] == 'started' and self._current_thread is None:
self._current_thread = event[ 'threadId' ]
infer_current_frame = True
def Continue( self ):
if self._currentThread 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._currentThread,
},
} )
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._currentThread is None:
utils.UserMessage( 'No current thread', persist = True )
return
self._session._connection.DoRequest( None, {
'command': 'pause',
'arguments': {
'threadId': self._currentThread,
},
} )
def _DrawStackTrace( self, thread ):
if '_frames' not in thread:
return
stackFrames = thread[ '_frames' ]
for frame in stackFrames:
for frame in thread.stacktrace:
if frame.get( 'source' ):
source = frame[ 'source' ]
else:
@ -261,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' ] )
@ -274,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' ] )
@ -293,3 +602,8 @@ class StackTraceView( object ):
'source': source
}
} )
def SetSyntax( self, syntax ):
self._current_syntax = utils.SetSyntax( self._current_syntax,
syntax,
self._buf )

View 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

View file

@ -19,10 +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_FILE = os.path.expanduser( os.path.join( '~', '.vimspector.log' ) )
_log_handler = logging.FileHandler( LOG_FILE, mode = 'w' )
_log_handler = logging.FileHandler( os.path.expanduser( '~/.vimspector.log' ),
mode = 'w' )
_log_handler.setFormatter(
logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' ) )
@ -37,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
@ -56,36 +84,50 @@ def OpenFileInCurrentWindow( file_name ):
return vim.buffers[ buffer_number ]
def SetUpCommandBuffer( cmd, name ):
bufs = vim.bindeval(
'vimspector#internal#job#StartCommandWithLog( {}, "{}" )'.format(
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( 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[ b ] for b in bufs ]
return vim.buffers[ int( buf ) ]
def CleanUpCommand( name ):
return vim.eval( 'vimspector#internal#job#CleanUpCommand( "{}" )'.format(
def CleanUpCommand( name, api_prefix ):
return vim.eval( 'vimspector#internal#{}job#CleanUpCommand( "{}" )'.format(
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 ):
@ -99,10 +141,10 @@ 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 int( vim.eval( "exists( '*prompt_setprompt' )" ) ):
return SetUpScratchBuffer( buf, name )
if not Exists( '*prompt_setprompt' ):
return SetUpHiddenBuffer( buf, name )
buf.options[ 'buftype' ] = 'prompt'
buf.options[ 'swapfile' ] = False
@ -110,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,
@ -119,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
@ -155,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
@ -178,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
@ -207,8 +293,12 @@ def TemporaryVimOption( opt, value ):
vim.options[ opt ] = old_value
def PathToConfigFile( file_name ):
p = os.getcwd()
def PathToConfigFile( file_name, from_directory = None ):
if not from_directory:
p = os.getcwd()
else:
p = os.path.abspath( os.path.realpath( from_directory ) )
while True:
candidate = os.path.join( p, file_name )
if os.path.exists( candidate ):
@ -224,16 +314,21 @@ def Escape( msg ):
return msg.replace( "'", "''" )
def UserMessage( msg, persist=False ):
def UserMessage( msg, persist=False, error=False ):
if persist:
_logger.warning( 'User Msg: ' + msg )
else:
_logger.info( 'User Msg: ' + msg )
vim.command( 'redraw' )
cmd = 'echom' if persist else 'echo'
for line in msg.split( '\n' ):
vim.command( "{0} '{1}'".format( cmd, Escape( line ) ) )
vim.command( 'redraw' )
try:
if error:
vim.command( "echohl WarningMsg" )
for line in msg.split( '\n' ):
vim.command( "{0} '{1}'".format( cmd, Escape( line ) ) )
finally:
vim.command( 'echohl None' ) if error else None
vim.command( 'redraw' )
@ -257,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.
@ -302,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 ):
@ -311,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
@ -320,77 +461,195 @@ def IsCurrent( window, buf ):
return vim.current.window == window and vim.current.window.buffer == buf
# TODO: Should we just run the substitution on the whole JSON string instead?
# That woul dallow expansion in bool and number values, such as ports etc. ?
def ExpandReferencesInDict( obj, mapping, user_choices ):
def expand_refs_in_string( orig_s ):
s = os.path.expanduser( orig_s )
s = os.path.expandvars( s )
def ExpandReferencesInObject( obj, mapping, calculus, user_choices ):
if isinstance( obj, dict ):
ExpandReferencesInDict( obj, mapping, calculus, user_choices )
elif isinstance( obj, list ):
j_offset = 0
obj_copy = list( obj )
# Parse any variables passed in in mapping, and ask for any that weren't,
# storing the result in mapping
bug_catcher = 0
while bug_catcher < 100:
++bug_catcher
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, calculus, user_choices )
return obj
# Based on the python standard library string.Template().substitue, enhanced to
# add ${name:default} parsing, and to remove the unnecessary generality.
VAR_MATCH = re.compile(
r"""
\$(?: # A dollar, followed by...
(?P<escaped>\$) | # Another doller = escaped
(?P<named>[_a-z][_a-z0-9]*) | # or An identifier - named param
{(?P<braced>[_a-z][_a-z0-9]*)} | # or An {identifier} - braced param
{(?P<braceddefault> # or An {id:default} - default param, as
(?P<defname>[_a-z][_a-z0-9]*) # an ID
: # then a colon
(?P<default>(?:\\}|[^}])*) # then anything up to }, or a \}
)} | # then a }
(?P<invalid>) # or Something else - invalid
)
""",
re.IGNORECASE | re.VERBOSE )
class MissingSubstitution( Exception ):
def __init__( self, name, default_value = None ):
self.name = name
self.default_value = default_value
def _Substitute( template, mapping ):
def convert( mo ):
# Check the most common path first.
named = mo.group( 'named' ) or mo.group( 'braced' )
if named is not None:
if named not in mapping:
raise MissingSubstitution( named )
return str( mapping[ named ] )
if mo.group( 'escaped' ) is not None:
return '$'
if mo.group( 'braceddefault' ) is not None:
named = mo.group( 'defname' )
if named not in mapping:
raise MissingSubstitution(
named,
mo.group( 'default' ).replace( '\\}', '}' ) )
return str( mapping[ named ] )
if mo.group( 'invalid' ) is not None:
raise ValueError( f"Invalid placeholder in string { template }" )
raise ValueError( 'Unrecognized named group in pattern', VAR_MATCH )
return VAR_MATCH.sub( convert, template )
def ExpandReferencesInString( orig_s,
mapping,
calculus,
user_choices ):
s = os.path.expanduser( orig_s )
s = os.path.expandvars( s )
# Parse any variables passed in in mapping, and ask for any that weren't,
# storing the result in mapping
bug_catcher = 0
while bug_catcher < 100:
++bug_catcher
try:
s = _Substitute( s, mapping )
break
except MissingSubstitution as e:
key = e.name
if key in calculus:
mapping[ key ] = calculus[ key ]()
else:
default_value = user_choices.get( key )
# Allow _one_ level of additional substitution. This allows a very real
# use case of "program": ${prgram:${file\\}}
if default_value is None and e.default_value is not None:
try:
default_value = _Substitute( e.default_value, mapping )
except MissingSubstitution as e2:
if e2.name in calculus:
default_value = calculus[ e2.name ]()
else:
default_value = e.default_value
try:
s = string.Template( s ).substitute( mapping )
break
except KeyError as e:
# HACK: This is seemingly the only way to get the key. str( e ) returns
# the key surrounded by '' for unknowable reasons.
key = e.args[ 0 ]
default_value = user_choices.get( key, None )
mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ),
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 )
break
except ValueError as e:
UserMessage( 'Invalid $ in string {}: {}'.format( s, e ),
persist = True )
break
return s
def expand_refs_in_object( obj ):
if isinstance( obj, dict ):
ExpandReferencesInDict( obj, mapping, user_choices )
elif isinstance( obj, list ):
for i, _ in enumerate( obj ):
# FIXME: We are assuming that it is a list of string, but could be a
# list of list of a list of dict, etc.
obj[ i ] = expand_refs_in_object( obj[ i ] )
elif isinstance( obj, str ):
obj = expand_refs_in_string( obj )
return obj
for k in obj.keys():
obj[ k ] = expand_refs_in_object( obj[ k ] )
return s
def ParseVariables( variables_list, mapping, user_choices ):
def CoerceType( mapping: typing.Dict[ str, typing.Any ], key: str ):
DICT_TYPES = {
'json': json.loads,
's': str
}
parts = key.split( '#' )
if len( parts ) > 1 and parts[ -1 ] in DICT_TYPES.keys():
value = mapping.pop( key )
new_type = parts[ -1 ]
key = '#'.join( parts[ 0 : -1 ] )
mapping[ key ] = DICT_TYPES[ new_type ]( value )
# TODO: Should we just run the substitution on the whole JSON string instead?
# That woul dallow expansion in bool and number values, such as ports etc. ?
def ExpandReferencesInDict( obj, mapping, calculus, user_choices ):
for k in list( obj.keys() ):
obj[ k ] = ExpandReferencesInObject( obj[ k ],
mapping,
calculus,
user_choices )
CoerceType( obj, k )
def ParseVariables( variables_list,
mapping,
calculus,
user_choices ):
new_variables = {}
new_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 {} )
@ -412,17 +671,29 @@ def ParseVariables( variables_list, mapping, user_choices ):
raise ValueError(
"Unsupported variable defn {}: Missing 'shell'".format( n ) )
else:
new_variables[ n ] = v
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 ):
@ -430,3 +701,166 @@ def GetBufferFilepath( buf ):
return ''
return os.path.normpath( buf.name )
def ToUnicode( b ):
if isinstance( b, bytes ):
return b.decode( 'utf-8' )
return b
# Call a vimscript function with suplied arguments.
def Call( vimscript_function, *args ):
call = vimscript_function + '('
for index, arg in enumerate( args ):
if index > 0:
call += ', '
arg_name = 'vimspector_internal_arg_{}'.format( index )
vim.vars[ arg_name ] = arg
call += 'g:' + arg_name
call += ')'
return vim.eval( call )
MEMO = {}
def memoize( func ):
global MEMO
@functools.wraps( func )
def wrapper( *args, **kwargs ):
dct = MEMO.setdefault( func, {} )
key = ( args, frozenset( kwargs.items() ) )
try:
return dct[ key ]
except KeyError:
result = func( *args, **kwargs )
dct[ key ] = result
return result
return wrapper
@memoize
def Exists( expr ):
return int( vim.eval( f'exists( "{ expr }" )' ) )
def SetSyntax( current_syntax, syntax, *args ):
if not syntax:
syntax = ''
if current_syntax == syntax:
return syntax
# We use set syn= because just setting vim.Buffer.options[ 'syntax' ]
# doesn't actually trigger the Syntax autocommand, and i'm not sure that
# 'doautocmd Syntax' is the right solution or not
for buf in args:
Call( 'setbufvar', buf.number, '&syntax', syntax )
return syntax
def GetBufferFiletypes( buf ):
ft = ToUnicode( vim.eval( f"getbufvar( {buf.number}, '&ft' )" ) )
return ft.split( '.' )
def GetVisualSelection( bufnr ):
start_line, start_col = vim.current.buffer.mark( "<" )
end_line, end_col = vim.current.buffer.mark( ">" )
# lines are 1 based, but columns are 0 based
# don't ask me why...
start_line -= 1
end_line -= 1
lines = vim.buffers[ bufnr ][ start_line : end_line + 1 ]
# Do end first, in case it's on the same line as start (as doing start first
# would change the offset)
lines[ -1 ] = lines[ -1 ][ : end_col + 1 ]
lines[ 0 ] = lines[ 0 ][ start_col : ]
_logger.debug( f'Visual selection: { lines } from '
f'{ start_line }/{ start_col } -> { end_line }/{ end_col }' )
return lines
def DisplaySplash( api_prefix, splash, text ):
if splash:
return Call( f'vimspector#internal#{api_prefix}popup#UpdateSplash',
splash,
text )
else:
return Call( f'vimspector#internal#{api_prefix}popup#DisplaySplash',
text )
def HideSplash( api_prefix, splash ):
if splash:
Call( f'vimspector#internal#{api_prefix}popup#HideSplash', splash )
return None
def GetVimValue( vim_dict, name, default=None ):
# FIXME: use 'encoding' ?
try:
value = vim_dict[ name ]
except ( KeyError, vim.error ):
return default
if isinstance( value, bytes ):
return value.decode( 'utf-8' )
return value
def GetVimList( vim_dict, name, default=None ):
try:
value = vim_dict[ name ]
except ( KeyError, vim.error ):
return default
if not isinstance( value, collections.abc.Iterable ):
raise ValueError( f"Expected a list for { name }, but found "
f"{ type( value ) }" )
return [ i.decode( 'utf-8' ) if isinstance( i, bytes ) else i for i in value ]
def GetVimspectorBase():
return GetVimValue( vim.vars,
'vimspector_base_dir',
os.path.abspath(
os.path.join( os.path.dirname( __file__ ),
'..',
'..' ) ) )
def GetUnusedLocalPort():
import socket
sock = socket.socket()
# This tells the OS to give us any free port in the range [1024 - 65535]
sock.bind( ( '', 0 ) )
port = sock.getsockname()[ 1 ]
sock.close()
return port
def WindowID( window, tab=None ):
if tab is None:
tab = window.tabpage
return int( Call( 'win_getid', window.number, tab.number ) )
def UseWinBar():
# Buggy neovim doesn't render correctly when the WinBar is defined:
# https://github.com/neovim/neovim/issues/12689
return not int( Call( 'has', 'nvim' ) )

View file

@ -13,57 +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._connection = None
self._current_syntax = ''
self._server_capabilities = None
self._variable_eval: Scope = None
self._variable_eval_view: View = None
mappings = settings.Dict( 'mappings' )[ 'variables' ]
# Set up the "Variables" buffer in the variables_win
self._scopes: typing.List[ Scope ] = []
self._vars = View( variables_win, {}, self._DrawScopes )
utils.SetUpHiddenBuffer( self._vars.buf, 'vimspector.Variables' )
with utils.LetCurrentWindow( variables_win ):
if utils.UseWinBar():
vim.command( 'nnoremenu <silent> 1.1 WinBar.Set '
':call vimspector#SetVariableValue()<CR>' )
AddExpandMappings( mappings )
# Set up the "Watches" buffer in the watches_win (and create a WinBar in
# there)
self._watches: typing.List[ Watch ] = []
self._watch = View( watches_win, {}, self._DrawWatches )
self._connection = connection
# Allows us to hit <CR> to expand/collapse variables
with utils.LetCurrentWindow( self._vars.win ):
vim.command(
'nnoremap <buffer> <CR> :call vimspector#ExpandVariable()<CR>' )
# This is actually the tree (scopes are alwyas the root)
# it's just a list of DAP scope dicts, with one magic key (_variables)
# _variables is a list of DAP variable with the same magic key
#
# If _variables is present, then we have requested and should display the
# children. Otherwise, we haven't or shouldn't.
self._scopes = []
# This is similar to scopes, but the top level is an "expression" (request)
# containing a special '_result' key which is the response. The response
# structure con contain _variables and is handled identically to the scopes
# above. It also has a special _line key which is where we printed it (last)
self._watches = []
# Allows us to hit <CR> to expand/collapse variables
with utils.LetCurrentWindow( self._watch.win ):
vim.command(
'nnoremap <buffer> <CR> :call vimspector#ExpandVariable()<CR>' )
vim.command(
'nnoremap <buffer> <DEL> :call vimspector#DeleteWatch()<CR>' )
utils.SetUpScratchBuffer( self._vars.win.buffer, 'vimspector.Variables' )
utils.SetUpPromptBuffer( self._watch.win.buffer,
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' )" ) )
@ -73,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:
@ -87,49 +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
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, {
@ -139,237 +319,413 @@ class VariablesView( object ):
},
} )
def _DrawBalloonEval( self ):
watch = self._variable_eval
view = self._variable_eval_view
with utils.RestoreCursorPosition():
with utils.ModifiableScratchBuffer( view.buf ):
utils.ClearBuffer( view.buf )
view.syntax = utils.SetSyntax( view.syntax,
self._current_syntax,
view.buf )
self._DrawWatchResult( view,
0,
watch,
is_short = True )
vim.eval( "vimspector#internal#balloon#ResizeTooltip()" )
def ClearTooltip( self ):
# This will actually end up calling CleanUpTooltip via the popup close
# callback
vim.eval( 'vimspector#internal#balloon#Close()' )
def CleanUpTooltip( self ) :
# remove reference to old tooltip window
self._variable_eval_view = None
vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = None
def VariableEval( self, frame, expression, is_hover ):
"""Callback to display variable under cursor `:h ballonexpr`"""
if not self._connection:
return ''
def handler( message ):
watch = self._variable_eval
if watch.result is None:
watch.result = WatchResult( message[ 'body' ] )
else:
watch.result.Update( message[ 'body' ] )
popup_win_id = utils.DisplayBalloon( self._is_term, [], is_hover )
# record the global eval window id
vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = int( popup_win_id )
popup_bufnr = int( vim.eval( "winbufnr({})".format( popup_win_id ) ) )
# We don't need to do any UI window setup here, as it's already done as
# part of the popup creation, so just pass the buffer to the View instance
self._variable_eval_view = BufView(
vim.buffers[ popup_bufnr ],
{},
self._DrawBalloonEval
)
if watch.result.IsExpandable():
# Always expand the first level
watch.result.expanded = Expandable.EXPANDED_BY_US
if watch.result.IsExpanded():
self._connection.DoRequest( partial( self._ConsumeVariables,
self._variable_eval_view.draw,
watch.result ), {
'command': 'variables',
'arguments': {
'variablesReference': watch.result.VariablesReference(),
},
} )
self._DrawBalloonEval()
def failure_handler( reason, message ):
display = [ reason ]
float_win_id = utils.DisplayBalloon( self._is_term, display, is_hover )
# record the global eval window id
vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = int( float_win_id )
self._variable_eval = Watch.New( frame,
expression,
'hover' )
# Send async request
self._connection.DoRequest( handler, {
'command': 'evaluate',
'arguments': self._variable_eval.expression,
}, failure_handler )
# Return working (meanwhile)
return ''
def AddWatch( self, frame, expression ):
watch = {
'expression': expression,
'frameId': frame[ 'id' ],
'context': 'watch',
}
self._watches.append( watch )
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 ):
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 )
self._connection.DoRequest( handler, {
'command': 'evaluate',
'arguments': {
'expression': expression,
'frameId': frame[ 'id' ],
'context': 'hover',
}
}, failure_handler )
def SetSyntax( self, syntax ):
# TODO: Switch to View.syntax
self._current_syntax = utils.SetSyntax( self._current_syntax,
syntax,
self._vars.buf,
self._watch.buf )
# vim: sw=2

View file

@ -0,0 +1,97 @@
# Copyright © 2020 Kyle Simpson <getify@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
"""A port of the `JSON-minify` utility to the Python language.
Based on JSON.minify.js: https://github.com/getify/JSON.minify
Contributers:
- Gerald Storer
- Contributed original version
- Felipe Machado
- Performance optimization
- Pradyun S. Gedam
- Conditions and variable names changed
- Reformatted tests and moved to separate file
- Made into a PyPI Package
"""
import re
# Vimspector modification: strip_space defaults to False; we don't actually want
# to minify - we just want to strip comments.
def minify(string, strip_space=False):
tokenizer = re.compile('"|(/\*)|(\*/)|(//)|\n|\r')
end_slashes_re = re.compile(r'(\\)*$')
in_string = False
in_multi = False
in_single = False
new_str = []
index = 0
for match in re.finditer(tokenizer, string):
if not (in_multi or in_single):
tmp = string[index:match.start()]
if not in_string and strip_space:
# replace white space as defined in standard
tmp = re.sub('[ \t\n\r]+', '', tmp)
new_str.append(tmp)
elif not strip_space:
# Replace comments with white space so that the JSON parser reports
# the correct column numbers on parsing errors.
new_str.append(' ' * (match.start() - index))
index = match.end()
val = match.group()
if val == '"' and not (in_multi or in_single):
escaped = end_slashes_re.search(string, 0, match.start())
# start of string or unescaped quote character to end string
if not in_string or (escaped is None or len(escaped.group()) % 2 == 0): # noqa
in_string = not in_string
index -= 1 # include " character in next catch
elif not (in_string or in_multi or in_single):
if val == '/*':
in_multi = True
elif val == '//':
in_single = True
elif val == '*/' and in_multi and not (in_string or in_single):
in_multi = False
if not strip_space:
new_str.append(' ' * len(val))
elif val in '\r\n' and not (in_multi or in_string) and in_single:
in_single = False
elif not ((in_multi or in_single) or (val in ' \r\n\t' and strip_space)): # noqa
new_str.append(val)
if not strip_space:
if val in '\r\n':
new_str.append(val)
elif in_multi or in_single:
new_str.append(' ' * len(val))
new_str.append(string[index:])
return ''.join(new_str)

190
run_tests
View file

@ -1,12 +1,130 @@
#!/usr/bin/env bash
RUN_VIM="vim --clean --not-a-term"
SetBaseDir() {
BASEDIR=$1
if [[ ! $BASEDIR = /* ]]; then
# Relative
BASEDIR=$(pwd)/${BASEDIR}
fi
}
SetBaseDir $(dirname $0)
INSTALL=0
UPDATE=0
INSTALLER_ARGS=''
RUN_VIM="vim -N --clean --not-a-term"
RUN_TEST="${RUN_VIM} -S lib/run_test.vim"
BASEDIR_CMD='py3 pass'
# 1 is stdout
out_fd=1
while [ -n "$1" ]; do
case "$1" in
"--basedir"|"--base-dir"|"--test-base")
shift
SetBaseDir $1
shift
BASEDIR_CMD="let g:vimspector_base_dir='${BASEDIR}'"
;;
"--install")
INSTALL=1
shift
;;
"--install-method")
shift
INSTALL=$1
shift
;;
"--update"|"--upgrade")
UPDATE=1
shift
;;
"--report")
shift
VIMSPECTOR_TEST_STDOUT=$1
shift
;;
"--quiet")
shift
# send the output to /dev/null
# https://stackoverflow.com/a/47553900
# Note we can't use {out_fd} here because the bash version in CI is too
# old on macOS
out_fd=3
exec 3>/dev/null
INSTALLER_ARGS="${INSTALLER_ARGS} --quiet"
;;
"--")
shift
break
;;
"--help")
shift
echo "$(basename $0) [--basedir <basedir>] [--report output] [--quiet] [--install] <optional list of tests in form file:func>"
echo ""
echo " --basedir <basedir> path to runtime directory like the optino to install_gadget.py"
echo " --install run install_gadget.py, useful with --basedir"
echo " --report <messages|all> which logs to dump to stdout after a test"
echo " --quiet suppress vim's stdout"
echo "e.g.: "
echo " - run all tests: $0"
echo " - run specific tests script: $0 signature_help.test.vim"
echo " - run specific tests fun: $0 signature_help.test.vim:Test_signatures_TopLine\(\)"
echo " - run all tests in a clean env: $0 --basedir \$(mktemp -d) --install"
exit 0
;;
*)
break
;;
esac
done
# We use fd 3 for vim's output. Send it to stdout if not already redirected
# Note: can't use ${out_fd} in a redirect because redirects happen before
# variable substitution
if [ "${out_fd}" = "1" ]; then
exec 3>&1
fi
if [ "$INSTALL" = "1" ] || [ "$INSTALL" = "script" ]; then
if ! python3 $(dirname $0)/install_gadget.py \
--basedir ${BASEDIR} \
${INSTALLER_ARGS} \
--all \
--force-enable-csharp; then
echo "Script installation reported errors" >&2
exit 1
fi
fi
if [ "$INSTALL" = "1" ] || [ "$INSTALL" = "vim" ]; then
if ! $RUN_VIM -u $(dirname $0)/tests/vimrc \
--cmd "${BASEDIR_CMD}" \
-c 'autocmd User VimspectorInstallSuccess qa!' \
-c 'autocmd User VimspectorInstallFailed cquit!' \
-c "VimspectorInstall --all netcoredbg"; then
echo "Vim installation reported errors" >&2
exit 1
fi
fi
if [ "$UPDATE" = "1" ]; then
if ! $RUN_VIM -u $(dirname $0)/tests/vimrc \
--cmd "${BASEDIR_CMD}" \
-c 'autocmd User VimspectorInstallSuccess qa!' \
-c 'autocmd User VimspectorInstallFailed cquit!' \
-c "VimspectorUpdate"; then
echo "Vim update reported errors" >&2
exit 1
fi
fi
if [ -z "$VIMSPECTOR_MIMODE" ]; then
if which -s lldb; then
if which lldb >/dev/null 2>&1; then
export VIMSPECTOR_MIMODE=lldb
elif which -s gdb; then
elif which gdb >/dev/null 2>&1; then
export VIMSPECTOR_MIMODE=gdb
else
echo "Couldn't guess VIMSPECTOR_MIMODE. Need lldb or gdb in path"
@ -14,18 +132,27 @@ 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"
pushd tests > /dev/null
# Start
pushd $(dirname $0)/tests > /dev/null
echo "Running Vimspector Vim tests"
RESULT=0
@ -39,23 +166,62 @@ fi
for t in ${TESTS}; do
echo ""
echo "%RUN: $t"
rm -f messages debuglog
# split on : into fileName and testName
IFS=: read -s t T <<< "$t"
if ${RUN_TEST} --cmd 'au SwapExists * let v:swapchoice = "e"' $t $T; then
TESTLOGDIR=${BASEDIR}/tests/logs/$t
if ${RUN_TEST} --cmd "${BASEDIR_CMD}" \
--cmd 'au SwapExists * let v:swapchoice = "e"' $t $T \
>&3\
&& [ -f $t.res ]; then
echo "%PASS: $t PASSED"
else
cat messages
echo "%FAIL: $t FAILED"
echo "%FAIL: $t FAILED - see $TESTLOGDIR"
RESULT=1
fi
rm -rf $TESTLOGDIR
mkdir -p $TESTLOGDIR
${RUN_VIM} --version > ${TESTLOGDIR}/vimversion
if [ "$VIMSPECTOR_TEST_STDOUT" = "messages" ]; then
if [ -f messages ]; then
echo "%MESSAGES"
cat messages
echo "%END"
else
echo "%MESSAGES"
echo "No messages found"
echo "%END"
fi
fi
for l in messages debuglog test.log *.testlog; do
# In CI we can't view the output files, so we just have to cat them
if [ -f $l ]; then
if [ "$VIMSPECTOR_TEST_STDOUT" = "all" ]; then
echo ""
echo ""
echo "*** START: $l ***"
cat $l
echo "*** END: $l ***"
fi
mv $l $TESTLOGDIR
fi
done
rm -f $t.res
done
# 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
View 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

View 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.

View 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
View 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'

View 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}" ]
}
}
}
}

View 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
View file

@ -0,0 +1,3 @@
#!/usr/bin/env bash
php -S localhost:1234 -t www

View file

@ -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() );
} );

View file

@ -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"
}
}

View file

@ -1,4 +1,4 @@
def Settings( **kwargs ):
return {
'flags': [ '-x', 'c++', '-Wall', '-Wextra' ]
'flags': [ '-x', 'c++', '-Wall', '-Wextra', '-std=c++17' ]
}

View file

@ -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 );
}

View file

@ -1,2 +1,3 @@
bin/
obj/Debug
obj/

View file

@ -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": {}
}
}
}
}

View file

@ -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 );
}
}
}
}

View file

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
</Project>

View file

@ -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

View file

@ -1,5 +0,0 @@
{
"version": 1,
"dgSpecHash": "6/vdr7YprlSIoQecv/nNuLNflFpO0X7eN7jHUinZTsgian9nYpmHMWirsDWMi5l+29TH+Qy8O/QfaB/48QtjRQ==",
"success": true
}

View file

@ -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>

View file

@ -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>

View file

@ -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
}
}
}
}

View file

@ -0,0 +1,13 @@
if argc() < 2
echom 'Usage:' v:argv[ 0 ] 'processName binary'
cquit!
endif
setfiletype cpp
call vimspector#LaunchWithSettings( #{
\ configuration: "C++ - Attach Local Process",
\ processName: argv( 0 ),
\ binary: argv( 1 ),
\ } )
1,2argd

View file

@ -0,0 +1,24 @@
{
"configurations": {
"C++ - Attach Local Process": {
"adapter": "vscode-cpptools",
"variables": {
"PID": {
"shell": [ "GetPIDForProcess", "${processName}" ]
}
},
"configuration": {
"name": "test",
"request": "attach",
"program": "${binary}",
"processId": "${PID}",
"type": "cppdbg",
"stopAtEntry": true,
"setupCommands": [
{ "text": "source ${initFile}", "ignoreFailures": true }
]
}
}
}
}

View file

@ -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" }
}
}
}

View 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": []
}
}
}
}

View 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
}
```

View file

@ -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)
}
}

View file

@ -0,0 +1,3 @@
module example.com
go 1.16

View file

@ -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"
}

View file

@ -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)
}
})
}
}

View file

@ -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,33 @@
"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": {
"adapter": "vscode-javac",
"configuration": {
"request": "attach",
"port": "${debugPort}",
"sourceRoots": [
"${workspaceRoot}/src/main/java"
]
}
}
}

View 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>

View file

@ -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>

View file

@ -0,0 +1,11 @@
package com.vimspector.test;
public class Base
{
public String DoSomething()
{
String s = new String();
s.replace( "A", "B" );
return s;
}
}

View file

@ -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() );
}
}

View file

@ -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
View file

@ -0,0 +1,2 @@
.gradle
build

View 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}"
}
}
}
}

View 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"
}

View file

@ -0,0 +1 @@
rootProject.name = "vimspector-test"

Some files were not shown because too many files have changed in this diff Show more