Merge pull request #209 from puremourning/install-commands

Simplify and improve gadget installation - add VimspectorInstall and VimspectorUpdate
This commit is contained in:
mergify[bot] 2020-07-22 15:12:25 +00:00 committed by GitHub
commit 89ffb1798c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1566 additions and 1022 deletions

198
README.md
View file

@ -8,15 +8,12 @@ For a tutorial and usage overview, take a look at the
<!--ts-->
* [Features and Usage](#features-and-usage)
* [Supported debugging features](#supported-debugging-features)
* [Supported languages:](#supported-languages)
* [Languages known to work](#languages-known-to-work)
* [Languages known not to work](#languages-known-not-to-work)
* [Supported languages](#supported-languages)
* [Other languages](#other-languages)
* [Installation](#installation)
* [Dependencies](#dependencies)
* [Neovim differences](#neovim-differences)
* [Windows differences](#windows-differences)
* [Language dependencies](#language-dependencies)
* [Clone the plugin](#clone-the-plugin)
* [Install some gadgets](#install-some-gadgets)
* [Manual gadget installation](#manual-gadget-installation)
@ -66,7 +63,7 @@ For a tutorial and usage overview, take a look at the
* [Customisation](#customisation)
* [Changing the default signs](#changing-the-default-signs)
* [Changing the default window sizes](#changing-the-default-window-sizes)
* [Changing the terminal size](#changing-the-terminal-size)
* [Changing the terminal size](#changing-the-terminal-size)
* [Advanced UI customisation](#advanced-ui-customisation)
* [Example](#example)
* [FAQ](#faq)
@ -74,7 +71,7 @@ For a tutorial and usage overview, take a look at the
* [License](#license)
* [Sponsorship](#sponsorship)
<!-- Added by: ben, at: Sat 18 Jul 2020 12:58:35 BST -->
<!-- Added by: ben, at: Wed 22 Jul 2020 12:38:39 BST -->
<!--te-->
@ -91,6 +88,12 @@ But for now, here's a (rather old) screenshot of Vimsepctor debugging Vim:
![vimspector-vim-screenshot](https://puremourning.github.io/vimspector-web/img/vimspector-overview.png)
And a couple of brief demos:
[![asciicast](https://asciinema.org/a/VmptWmFHTNLPfK3DVsrR2bv8S.svg)](https://asciinema.org/a/VmptWmFHTNLPfK3DVsrR2bv8S)
[![asciicast](https://asciinema.org/a/1wZJSoCgs3AvjkhKwetJOJhDh.svg)](https://asciinema.org/a/1wZJSoCgs3AvjkhKwetJOJhDh)
## Supported debugging features
- flexible configuration syntax that can be checked in to source control
@ -108,31 +111,32 @@ But for now, here's a (rather old) screenshot of Vimsepctor debugging Vim:
- logging/stdout display
- simple stable API for custom tooling (e.g. integrate with language server)
## Supported languages:
For other languages, you'll need some other way to install the gadget.
The following languages are used frequently by the author and are known to work
with little effort, and are supported as first-class languages.
## Supported languages
- C, C++, etc. (languages supported by gdb or lldb)
- Python 2 and Python 3
- TCL
- Bash scripts
- Java
The following table lists the languages that are "built-in" (along with their
runtime dependencies). They are categorised by their level of support:
## Languages known to work
* `Tested` : Fully supported, Vimspector regression tests cover them
* `Supported` : Fully supported, frequently used and manually tested
* `Experimental`: Working, but not frequently used and rarely tested
* `Legacy`: No longer supported, please migrate your config
The following languages are used frequently by the author, but require some sort
of hackery that makes it challenging to support generally. These languages are
on a best-efforts basis:
- C# (c-sharp) using dotnet core
- Go (requires separate installation of [Delve][])
- Node.js (requires node <12 for installation)
- Anything running in chrome (i.e. javascript).
## Languages known not to work
- C# (c-sharp) using mono debug adapter (vimspector unable to set breakpoints)
| Language | Status | Switch (for `install_gadget.py`) | Adapter (for `:VimspectorInstall`) | Dependencies |
|------------------|--------------|----------------------------------|------------------------------------|--------------------------------------------|
| C, C++, etc. | Tested | `--all` or `--enable-c` | vscode-cpptools | mono-core |
| Python | Tested | `--all` or `--enable-python` | debugpy | Python 2.7 or Python 3 |
| Go | Tested | `--enable-go` | vscode-go | Go, [Delve][] |
| TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 |
| Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? |
| Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm |
| Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome |
| Java | Supported | `--force-enable-java ` | vscode-java-debug | Compatible LSP plugin (see [later](#java)) |
| C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core |
| C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono |
| Rust (CodeLLDB) | Experimental | `--force-enable-rust` | CodeLLDB | Python 3 |
| Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 |
## Other languages
@ -224,33 +228,6 @@ The following features are not implemented for Windows:
* Tailing the vimspector log in the Output Window.
## Language dependencies
The debug adapters themselves have certain runtime dependencies. They are
categorised as follows:
* `Tested` : Fully supported, Vimspector regression tests cover them
* `Supported` : Fully supported, frequently used and manually tested
* `Experimental`: Working, but not frequently used and rarely tested
* `Legacy`: No longer supported, please migrate your config
| Language | Status | Switch | Adapter | Dependencies |
|------------------|--------------|--------------------------------|---------------------|--------------------------------------------|
| C, C++, etc. | Tested | `--all` or `--enable-c` | vscode-cpptools | mono-core |
| Python | Tested | `--all` or `--enable-python` | debugpy | Python 2.7 or Python 3 |
| Go | Tested | `--enable-go` | vscode-go | Go, [Delve][] |
| TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 |
| Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? |
| Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm |
| Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome |
| Java | Supported | `--force-enable-java ` | vscode-java-debug | Compatible LSP plugin (see [later](#java)) |
| C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core |
| C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono |
| Rust (CodeLLDB) | Experimental | `--force-enable-rust` | CodeLLDB | Python 3 |
| Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 |
For other languages, you'll need some other way to install the gadget.
## Clone the plugin
There are many Vim plugin managers, and I'm not going to state a particular
@ -281,10 +258,32 @@ See support/doc/example_vimrc.vim.
## Install some gadgets
There are a couple of ways of doing this, but ***using `install_gadget.py` is
highly recommended*** where that's an option.
Vimspector is a generic client for Debug Adapters. Debug Adapters (referred to
as 'gadgets' or 'adapters') are what actually do the work of talking to the real
debugers.
For supported languages, `install_gadget.py` will:
In order for Vimspector to be useful, you need to have some adapters installed.
There are a few ways to do this:
* If you downloaded a tarball, gadgets for main supported langauges are already
installed for you.
* Using `:VimspectorInstall <adapter> <args...>` (use TAB `wildmenu` to see the
options, also accepts any `install_gadget.py` option)
* Alternatively, using `python3 install_gadget.py <args>` (use `--help` to see
all options)
* When attempting to launch a debug configuration, if the configured adapter
can't be found, vimspector might suggest installing one.
* Use `:VimspectorUpdate` to install the latest supported versions of the
gadgets.
Here's a demo of doing somee installs and an upgrade:
[![asciicast](https://asciinema.org/a/Hfu4ZvuyTZun8THNen9FQbTay.svg)](https://asciinema.org/a/Hfu4ZvuyTZun8THNen9FQbTay)
Both `install_gadget.py` and `:VimspectorInstall` do the same set of things,
though the default behaviours are slightly different. For supported languages,
they will:
* Download the relevant debug adapter at a version that's been tested from the
internet, either as a 'vsix' (Visusal Studio plugin), or clone from GitHub. If
@ -298,25 +297,25 @@ For supported languages, `install_gadget.py` will:
To install the tested debug adapter for a language, run:
```
./install_gadget.py --enable-<language>
```
| To install | Script | Command |
| --- | --- | --- |
| `<adapter>` | | `:VimspectorInstall <adapter>` |
| `<adapter1>`, `<adapter2>`, ... | | `:VimspectorInstall <adapter1> <adapter2> ...` |
| `<language>` | `./install_gadget.py --enable-<language> ...` | `:VimspectorInstall --enable-<language> ...` |
| Supported adapters | `./install_gadget.py --all` | `:VimspectorInstall --all` |
| Supported adapters, but not TCL | `./install_gadget.py --all --disable-tcl` | `:VimspectorInstall --all --disable-tcl` |
| Supported and experimental adapters | `./install_gadget.py --all --force-all` | `:VimspectorInstall --all` |
| Adapter for specific debug config | | Suggested by Vimspector when starting debugging |
Or to install all supported gagtets:
`"VimspectorInstall` runs `install_gadget.py` in the background with some of
the options defaulted.
```
./install_gadget.py --all
```
By default `install_gadget.py` will overwrite your `.gadgets.json` with the set
of adapters just installed, whereas `:VimspectorInstall` will _update_ it,
overwriting only newly changed or installed adapters.
To install everything other than TCL (because TCL is sadly not as popular as it
should be):
```
./install_gadget.py --all --disable-tcl
```
If you want to just add a new adapter without destroying the exisitng ones, add
`--update-gadget-config`, as in:
If you want to just add a new adapter using the script without destroying the
exisitng ones, add `--update-gadget-config`, as in:
```bash
$ ./install_gadget.py --enable-tcl
@ -339,10 +338,16 @@ Then add this to your `.vimrc`:
let g:vimspector_base_dir=expand( '$HOME/.vim/vimspector-config' )
```
See `--help` for more info.
When usnig `:VimspectorInstall`, the `g:vimspector_base_dir` setting is
respected unless `--basedir` is manually added (not recommended).
See `--help` for more info on the various options.
## Manual gadget installation
If the language you want to debug is not in the supported list above, you can
probably still make it work, but it's more effort.
You essentially need to get a working installation of the debug adapter, find
out how to start it, and configure that in an `adapters` entry in either your
`.vimspector.json` or in `.gadgets.json`.
@ -352,7 +357,9 @@ its extension manager to install the relevant extension. You can then configure
the adapter manually in the `adapters` section of your `.vimspector.json` or in
a `gadgets.json`.
PRs are always welcome to add configuration to do this to `install_gadget.py`.
PRs are always welcome to add supported languages (which roughly translates to
updating `python/vimspector/gadgets.py` and testing it).
### The gadget directory
@ -415,7 +422,8 @@ Example:
}
```
The gadget file is automatically written by `install_gadget.py`.
The gadget file is automatically written by `install_gadget.py` (or
`:VimspectorInstall`).
Vimspector will also load any fies matching:
`</path/to/vimspector>/gadgets/<os>/.gadgets.d/*.json`. These have the same
@ -446,6 +454,11 @@ which can be used to check everything is working. This is used by the regression
tests in CI so should always work, and is a good way to check if the problem is
your configuration rather than a bug.
## Upgrade
After updating the Vimspector code (either via `git pull` or whatever pacakge
manager), run `:VimspectorUpdate` to update any already-installed gadets.
# About
## Background
@ -865,9 +878,9 @@ an example of getting Vimspector to remotely launch and attach.
## Python
* Python: [debugpy][]
* Requires `install_gadget.py --enable-python`, ideally requires a working
compiler and the python development headers/libs to build a C python extension
for performance.
* Install with `install_gadget.py --enable-python` or `:VimspectorInstall
debugpy`, ideally requires a working compiler and the python development
headers/libs to build a C python extension for performance.
* Full options: https://github.com/microsoft/debugpy/wiki/Debug-configuration-settings
@ -978,7 +991,8 @@ See [my fork of TclProDebug](https://github.com/puremourning/TclProDebug) for in
* C# - dotnet core
Requires `install_gadget.py --force-enable-csharp`
Install with `install_gadget.py --force-enable-csharp` or `:VimspectorInstall
netcoredbg`
```json
{
@ -998,7 +1012,8 @@ Requires `install_gadget.py --force-enable-csharp`
* C# - mono
Requires `install_gadget.py --force-enable-csharp`.
Install with `install_gadget.py --force-enable-csharp` or `:VimspectorInstall
vscode-mono-debug`.
***Known not to work.***
@ -1029,7 +1044,7 @@ Requires `install_gadget.py --force-enable-csharp`.
Requires:
* `install_gadget.py --enable-go`
* `install_gadget.py --enable-go` or `:VimspectorInstall vscode-go`
* [Delve][delve-install] installed, e.g. `go get -u github.com/go-delve/delve/cmd/dlv`
* Delve to be in your PATH, or specify the `dlvToolPath` launch option
@ -1057,7 +1072,8 @@ https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug
Requires:
* (optional) Xdebug helper for chrome https://chrome.google.com/webstore/detail/xdebug-helper/eadndfjplgieldjbigjakmdgkmoaaaoc
* `install_gadget.py --force-enable-php`
* `install_gadget.py --force-enable-php` or `:VimspectorInstall
vscode-php-debug`
* configured php xdebug extension
```ini
zend_extension=xdebug.so
@ -1161,7 +1177,8 @@ https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome
It allows you to debug scripts running inside chrome from within Vim.
* `./install_gadget.py --force-enable-chrome`
* `./install_gadget.py --force-enable-chrome` or `:VimspectorInstall
debugger-for-chrome`
* Example: `support/test/chrome`
```json
@ -1195,7 +1212,8 @@ use it with Vimspector.
* Set up [YCM for java][YcmJava].
* Get Vimspector to download the java debug plugin:
`install_gadget.py --force-enable-java <other options...>`
`install_gadget.py --force-enable-java <other options...>` or
`:VimspectorInstall java-debug-adapter`
* Configure Vimspector for your project using the `vscode-java` adapter, e.g.:
```json
@ -1266,7 +1284,7 @@ Rust is supported with any gdb/lldb-based debugger. So it works fine with
`vscode-cpptools` and `lldb-vscode` above. However, support for rust is best in
[`CodeLLDB`](https://github.com/vadimcn/vscode-lldb#features).
* `./install_gadget.py --force-enable-rust`
* `./install_gadget.py --force-enable-rust` or `:VimspectorInstall CodeLLDB`
* Example: `support/test/rust/vimspector_test`
```json
@ -1478,6 +1496,14 @@ hi link jsonCommentError Comment
hi link jsonComment Comment
```
7. What is the difference between a `gadget` and an `adapter`? A gadget is
somethin you install with `:VimspectorInstall` or `install_gadget.py`, an
`adapter` is something that Vimspector talks to (actually it's the Vimsepctor
config describing that thing). These are _usually_ one-to-one,
but in theory a single gadget can supply multiple `adapter` configs.
Typically this happens when a `gadget` supplies different `adapter` config
for, say remote debugging, or debugging in a container, etc.
# Motivation
A message from the author about the motivation for this plugin:

View file

@ -20,29 +20,47 @@ set cpoptions&vim
" }}}
call vimspector#internal#state#Reset()
let s:enabled = vimspector#internal#state#Reset()
function! vimspector#Launch() abort
if !s:enabled
return
endif
py3 _vimspector_session.Start()
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
if !s:enabled
return
endif
py3 _vimspector_session.Reset()
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
if !s:enabled
return
endif
if a:0 == 0
let options = {}
else
@ -52,6 +70,9 @@ function! vimspector#ToggleBreakpoint( ... ) abort
endfunction
function! vimspector#AddFunctionBreakpoint( function, ... ) abort
if !s:enabled
return
endif
if a:0 == 0
let options = {}
else
@ -62,42 +83,72 @@ function! vimspector#AddFunctionBreakpoint( function, ... ) abort
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
if !s:enabled
return
endif
py3 _vimspector_session.Stop()
endfunction
function! vimspector#ExpandVariable() abort
if !s:enabled
return
endif
py3 _vimspector_session.ExpandVariable()
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( ... ) abort
if !s:enabled
return
endif
if a:0 == 0
let expr = input( 'Enter watch expression: ' )
else
@ -112,37 +163,68 @@ function! vimspector#AddWatch( ... ) abort
endfunction
function! vimspector#AddWatchPrompt( expr ) abort
if !s:enabled
return
endif
stopinsert
setlocal nomodified
call vimspector#AddWatch( a:expr )
endfunction
function! vimspector#Evaluate( expr ) abort
if !s:enabled
return
endif
py3 _vimspector_session.ShowOutput( 'Console' )
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) )
endfunction
function! vimspector#EvaluateConsole( expr ) abort
if !s:enabled
return
endif
stopinsert
setlocal nomodified
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) )
endfunction
function! vimspector#ShowOutput( category ) abort
if !s:enabled
return
endif
py3 _vimspector_session.ShowOutput( vim.eval( 'a:category' ) )
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#ListBreakpoints() abort
if !s:enabled
return
endif
py3 _vimspector_session.ListBreakpoints()
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
return join( py3eval( '_vimspector_session.GetCompletionsSync( '
\.' vim.eval( "a:CmdLine" ),'
\.' int( vim.eval( "a:CursorPos" ) ) )'
@ -150,6 +232,52 @@ function! vimspector#CompleteExpr( ArgLead, CmdLine, CursorPos ) abort
\ "\n" )
endfunction
function! vimspector#Install( ... ) abort
if !s:enabled
return
endif
if a:0 < 1
return
endif
let prefix = vimspector#internal#state#GetAPIPrefix()
py3 __import__( 'vimspector',
\ fromlist = [ 'installer' ] ).installer.RunInstaller(
\ vim.eval( 'prefix' ),
\ *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( ... ) 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: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
" Boilerplate {{{
let &cpoptions=s:save_cpo
unlet s:save_cpo

View file

@ -146,6 +146,13 @@ function! vimspector#internal#job#Reset() abort
call vimspector#internal#job#StopDebugSession()
endfunction
function! s:_OnCommandExit( category, ch, code ) abort
py3 __import__( "vimspector",
\ fromlist = [ "utils" ] ).utils.OnCommandWithLogComplete(
\ vim.eval( 'a:category' ),
\ int( vim.eval( 'a:code' ) ) )
endfunction
function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort
if ! exists( 's:commands' )
let s:commands = {}
@ -157,14 +164,19 @@ function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort
let l:index = len( s:commands[ a:category ] )
let buf = '_vimspector_log_' . a:category
call add( s:commands[ a:category ], job_start(
\ a:cmd,
\ {
\ 'out_io': 'buffer',
\ 'in_io': 'null',
\ 'err_io': 'buffer',
\ 'out_name': '_vimspector_log_' . a:category . '_out',
\ 'err_name': '_vimspector_log_' . a:category . '_err',
\ 'out_msg': 0,
\ 'err_msg': 0,
\ 'out_name': buf,
\ 'err_name': buf,
\ 'exit_cb': funcref( 's:_OnCommandExit', [ a:category ] ),
\ 'out_modifiable': 0,
\ 'err_modifiable': 0,
\ 'stoponexit': 'kill'
@ -176,12 +188,7 @@ function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort
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

@ -116,58 +116,65 @@ function! s:_OnCommandEvent( category, id, data, event ) abort
return
endif
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 ] )
if a:event ==# 'stdout' || a:event ==# 'stderr'
if a:data == ['']
return
endif
call appendbufline( buffer, '$', a:data[ 1: ] )
finally
call s:MakeBufferReadOnly( buffer )
call setbufvar( buffer, '&modified', 0 )
endtry
if !has_key( s:commands, a:category )
return
endif
if !has_key( s:commands[ a:category ], a:id )
return
endif
if a:event ==# 'stdout'
let buffer = s:commands[ a:category ][ a:id ].stdout
elseif a:event ==# 'stderr'
let buffer = s:commands[ a:category ][ a:id ].stderr
endif
" if the buffer is visible, scroll it
let w = bufwinnr( buffer )
if w > 0
let cw = winnr()
try
execute w . 'wincmd w'
normal! Gz.
finally
execute cw . 'wincmd w'
call bufload( buffer )
catch /E325/
" Ignore E325/ATTENTION
endtry
let numlines = py3eval( "len( vim.buffers[ int( vim.eval( 'buffer' ) ) ] )" )
let last_line = getbufline( buffer, '$' )[ 0 ]
call s:MakeBufferWritable( buffer )
try
if numlines == 1 && last_line ==# ''
call setbufline( buffer, 1, a:data[ 0 ] )
else
call setbufline( buffer, '$', last_line . a:data[ 0 ] )
endif
call appendbufline( buffer, '$', a:data[ 1: ] )
finally
call s:MakeBufferReadOnly( buffer )
call setbufvar( buffer, '&modified', 0 )
endtry
" if the buffer is visible, scroll it
let w = bufwinnr( buffer )
if w > 0
let cw = winnr()
try
execute w . 'wincmd w'
normal! Gz-
finally
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
@ -198,29 +205,29 @@ function! vimspector#internal#neojob#StartCommandWithLog( cmd, category ) abort
let s:commands[ a:category ] = {}
endif
let stdout_buf = bufnr( '_vimspector_log_' . a:category . '_out', v:true )
let stderr_buf = bufnr( '_vimspector_log_' . a:category . '_err', v:true )
let buf = bufnr( '_vimspector_log_' . a:category, v:true )
" FIXME: This largely duplicates the same stuff in the python layer, but we
" don't want to potentially mess up Vim behaviour where the job output is
" attached to a buffer set up by Vim. So we sort o mimic that here.
call s:SetUpHiddenBuffer( stdout_buf )
call s:SetUpHiddenBuffer( stderr_buf )
call s:SetUpHiddenBuffer( buf )
let id = jobstart(a:cmd,
\ {
\ 'on_stdout': funcref( 's:_OnCommandEvent',
\ [ a:category ] ),
\ 'on_stderr': funcref( 's:_OnCommandEvent',
\ [ a:category ] )
\ [ a:category ] ),
\ 'on_exit': funcref( 's:_OnCommandEvent',
\ [ a:category ] ),
\ } )
let s:commands[ a:category ][ id ] = {
\ 'stdout': stdout_buf,
\ 'stderr': stderr_buf
\ 'stdout': buf,
\ 'stderr': buf
\ }
return [ stdout_buf, stderr_buf ]
return buf
endfunction
function! vimspector#internal#neojob#CleanUpCommand( category ) abort
@ -229,8 +236,11 @@ function! vimspector#internal#neojob#CleanUpCommand( category ) abort
endif
for id in keys( s:commands[ a:category ] )
call jobstop( str2nr( id ) )
call jobwait( [ str2nr( id ) ] )
let id = str2nr( id )
if jobwait( [ id ], 0 )[ 0 ] == -1
call jobstop( id )
endif
call jobwait( [ id ], -1 )
endfor
unlet! s:commands[ a:category ]
endfunction

View file

@ -19,18 +19,31 @@ let s:save_cpo = &cpoptions
set cpoptions&vim
" }}}
let s:is_neovim = has( 'nvim' )
let s:prefix = ''
if has( 'nvim' )
let s:prefix='neo'
endif
function! vimspector#internal#state#Reset() abort
let prefix = ''
if s:is_neovim
let prefix='neo'
endif
py3 << EOF
import vim
from vimspector import debug_session
_vimspector_session = debug_session.DebugSession( vim.eval( 'prefix' ) )
EOF
try
py3 import vim
py3 _vimspector_session = __import__(
\ "vimspector",
\ fromlist=[ "debug_session" ] ).debug_session.DebugSession(
\ vim.eval( 's:prefix' ) )
catch /.*/
echohl WarningMsg
echom 'Exception while loading vimspector:' v:exception
echom '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

@ -28,7 +28,7 @@ stages:
- bash: pip3 install -r dev_requirements.txt
displayName: "Install requirements"
- bash: $HOME/.local/bin/vint autoload/ compiler/ plugin/ tests/
- bash: $HOME/.local/bin/vint autoload/ compiler/ plugin/ tests/ syntax/
displayName: "Run vint"
- job: 'linux'
@ -45,20 +45,17 @@ stages:
- task: CacheBeta@0
inputs:
key: v1 | gadgets | $(Agent.OS) | install_gadget.py
key: v2 | gadgets | $(Agent.OS) | python3/vimspector/gadgets.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 --report messages --quiet
./run_tests --install --update --report messages --quiet
displayName: 'Run the tests'
env:
VIMSPECTOR_MIMODE: gdb
@ -92,17 +89,14 @@ stages:
- task: CacheBeta@0
inputs:
key: v1 | gadgets | $(Agent.OS) | install_gadget.py
key: v2 | gadgets | $(Agent.OS) | python3/vimspector/gadgets.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 --report messages --quiet
- bash: ./run_tests --install --update --report messages --quiet
displayName: 'Run the tests'
env:
VIMSPECTOR_MIMODE: lldb

View file

@ -23,9 +23,6 @@ if sys.version_info.major < 3:
import argparse
import os
import string
import subprocess
import traceback
import json
import functools
import operator
@ -35,628 +32,9 @@ import glob
sys.path.insert( 1, os.path.join( os.path.dirname( __file__ ),
'python3' ) )
from vimspector import install, installer
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, gadget: InstallCppTools( name, root, gadget ),
'all': {
'version': '0.27.0',
"adapters": {
"vscode-cpptools": {
"name": "cppdbg",
"command": [
"${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7"
],
"attach": {
"pidProperty": "processId",
"pidSelect": "ask"
},
"configuration": {
"type": "cppdbg",
"args": [],
"cwd": "${workspaceRoot}",
"environment": [],
}
},
},
},
'linux': {
'file_name': 'cpptools-linux.vsix',
'checksum':
'3695202e1e75a03de18049323b66d868165123f26151f8c974a480eaf0205435',
},
'macos': {
'file_name': 'cpptools-osx.vsix',
'checksum':
'cb061e3acd7559a539e5586f8d3f535101c4ec4e8a48195856d1d39380b5cf3c',
},
'windows': {
'file_name': 'cpptools-win32.vsix',
'checksum':
'aa294368ed16d48c59e49c8000e146eae5a19ad07b654efed5db8ec93b24229e',
"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"
}
},
},
},
},
'vscode-python': {
'language': 'python.legacy',
'enabled': False,
'download': {
'url': 'https://github.com/Microsoft/vscode-python/releases/download/'
'${version}/${file_name}',
},
'all': {
'version': '2019.11.50794',
'file_name': 'ms-python-release.vsix',
'checksum':
'6a9edf9ecabed14aac424e6007858068204a3638bf3bb4f235bd6035d823acc6',
},
'adapters': {
"vscode-python": {
"name": "vscode-python",
"command": [
"node",
"${gadgetDir}/vscode-python/out/client/debugger/debugAdapter/main.js",
],
}
},
},
'debugpy': {
'language': 'python',
'download': {
'url': 'https://github.com/microsoft/debugpy/archive/${file_name}'
},
'all': {
'version': '1.0.0b12',
'file_name': 'v1.0.0b12.zip',
'checksum':
'210632bba2221fbb841c9785a615258819ceec401d1abdbeb5f2326f12cc72a1'
},
'do': lambda name, root, gadget: InstallDebugpy( name, root, gadget ),
'adapters': {
'debugpy': {
"command": [
sys.executable,
"${gadgetDir}/debugpy/build/lib/debugpy/adapter"
],
"name": "debugpy",
"configuration": {
"python": sys.executable,
# 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}",
}
},
},
'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': 'master'
},
'do': lambda name, root, gadget: 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',
'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':
'c1dc6ed58c3f5b0473cfb4985a96552999360ceb9795e42d9c9be64af054f821',
},
'linux': {
'file_name': 'netcoredbg-linux-master.tar.gz',
'checksum': '',
},
'windows': {
'file_name': 'netcoredbg-win64-master.zip',
'checksum': '',
},
'do': lambda name, root, gadget: installer.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.7.vsix',
'version': 'v0.3.7',
'checksum':
'7b73e5b4604375df8658fb5a72c645c355785a289aa785a986e508342c014bb4',
},
'do': lambda name, root, gadget: 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/microsoft/vscode-go/releases/download/'
'${version}/${file_name}'
},
'all': {
'version': '0.11.4',
'file_name': 'Go-0.11.4.vsix',
'checksum':
'ff7d7b944da5448974cb3a0086f4a2fd48e2086742d9c013d6964283d416027e'
},
'adapters': {
'vscode-go': {
'name': 'delve',
'command': [
'node',
'${gadgetDir}/vscode-go/out/src/debugAdapter/goDebug.js'
],
},
},
},
'vscode-php-debug': {
'language': 'php',
'enabled': False,
'download': {
'url':
'https://github.com/felixfbecker/vscode-php-debug/releases/download/'
'${version}/${file_name}',
},
'all': {
'version': 'v1.13.0',
'file_name': 'php-debug.vsix',
'checksum':
'8a51e593458fd14623c1c89ebab87347b087d67087717f18bcf77bb788052718',
},
'adapters': {
'vscode-php-debug': {
'name': "php-debug",
'command': [
'node',
"${gadgetDir}/vscode-php-debug/out/phpDebug.js",
]
}
}
},
'vscode-node-debug2': {
'language': 'node',
'enabled': False,
'repo': {
'url': 'https://github.com/microsoft/vscode-node-debug2',
'ref': 'v1.42.0',
},
'do': lambda name, root, gadget: 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.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'
],
},
},
},
'CodeLLDB': {
'language': 'rust',
'enabled': False,
'download': {
'url': 'https://github.com/vadimcn/vscode-lldb/releases/download/'
'${version}/${file_name}',
},
'all': {
'version': 'v1.5.3',
},
'macos': {
'file_name': 'codelldb-x86_64-darwin.vsix',
'checksum':
'7505bc1cdfcfd1cb981e2996aec62d63577440709bac31dcadb41a3b4b44631a',
'make_executable': [
'adapter/codelldb',
'lldb/bin/debugserver',
'lldb/bin/lldb',
'lldb/bin/lldb-argdumper',
],
},
'linux': {
'file_name': 'codelldb-x86_64-linux.vsix',
'checksum':
'ce7efc3e94d775368e5942a02bf5c326b6809a0b4c389f79ffa6a8f6f6b72139',
'make_executable': [
'adapter/codelldb',
'lldb/bin/lldb',
'lldb/bin/lldb-server',
'lldb/bin/lldb-argdumper',
],
},
'windows': {
'file_name': 'codelldb-x86_64-windows.vsix',
'checksum':
'',
'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",
}
},
},
},
}
def InstallGeneric( name, root, gadget ):
extension = os.path.join( root, 'extension' )
for f in gadget.get( 'make_executable', [] ):
installer.MakeExecutable( os.path.join( extension, f ) )
installer.MakeExtensionSymlink( vimspector_base, name, root )
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.
installer.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 ):
installer.MakeExecutable( os.path.join( extension, binary ) )
installer.MakeExtensionSymlink( vimspector_base, name, root )
def InstallBashDebug( name, root, gadget ):
installer.MakeExecutable( os.path.join( root,
'extension',
'bashdb_dir',
'bashdb' ) )
installer.MakeExtensionSymlink( vimspector_base, name, root )
def InstallDebugpy( name, root, gadget ):
wd = os.getcwd()
root = os.path.join( root, 'debugpy-{}'.format( gadget[ 'version' ] ) )
os.chdir( root )
try:
subprocess.check_call( [ sys.executable, 'setup.py', 'build' ] )
finally:
os.chdir( wd )
installer.MakeSymlink( gadget_dir, name, root )
def InstallTclProDebug( name, root, gadget ):
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 installer.CurrentWorkingDir( os.path.join( root, 'lib', 'tclparser' ) ):
subprocess.check_call( configure )
subprocess.check_call( [ 'make' ] )
installer.MakeSymlink( gadget_dir, name, root )
def InstallNodeDebug( name, root, gadget ):
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 installer.CurrentWorkingDir( root ):
subprocess.check_call( [ 'npm', 'install' ] )
subprocess.check_call( [ 'npm', 'run', 'build' ] )
installer.MakeSymlink( gadget_dir, name, root )
def InstallGagdet( name, gadget, failed, all_adapters ):
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 = installer.DownloadFileTo(
url,
destination,
file_name = gadget[ 'download' ].get( 'target' ),
checksum = v.get( 'checksum' ),
check_certificate = not args.no_check_certificate )
root = os.path.join( destination, 'root' )
installer.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 )
installer.CloneRepoTo( url, ref, destination )
root = destination
if 'do' in gadget:
gadget[ 'do' ]( name, root, v )
else:
InstallGeneric( name, root, v )
# Allow per-OS adapter overrides. v already did that for us...
all_adapters.update( v.get( 'adapters', {} ) )
# Add any other "all" adapters
all_adapters.update( gadget.get( 'adapters', {} ) )
print( "Done installing {}".format( name ) )
except Exception as e:
traceback.print_exc()
failed.append( name )
print( "FAILED installing {}: {}".format( name, e ) )
# ------------------------------------------------------------------------------
# Entry point
# ------------------------------------------------------------------------------
@ -691,6 +69,14 @@ parser.add_argument( '--force-all',
action = 'store_true',
help = 'Enable all unsupported completers' )
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. '
@ -723,7 +109,7 @@ parser.add_argument( '--sudo',
"run this as root via sudo, pass this flag." )
done_languages = set()
for name, gadget in GADGETS.items():
for name, gadget in gadgets.GADGETS.items():
lang = gadget[ 'language' ]
if lang in done_languages:
continue
@ -766,12 +152,9 @@ if args.basedir:
vimspector_base = os.path.abspath( args.basedir )
install.MakeInstallDirs( vimspector_base )
OS = install.GetOS()
gadget_dir = install.GetGadgetDir( vimspector_base, OS )
print( 'OS = ' + OS )
print( 'gadget_dir = ' + gadget_dir )
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
@ -789,21 +172,11 @@ for custom_file_name in functools.reduce( operator.add,
failed = []
if args.update_gadget_config:
with open( install.GetGadgetConfigFile( vimspector_base ), 'r' ) as f:
all_adapters = json.load( f ).get( 'adapters', {} )
else:
all_adapters = {}
succeeded = []
all_adapters = installer.ReadAdapters(
read_existing = args.update_gadget_config )
# Include "built-in" adapter for multi-session mode
all_adapters.update( {
'multi-session': {
'port': '${port}',
'host': '${host}'
},
} )
for name, gadget in GADGETS.items():
for name, gadget in gadgets.GADGETS.items():
if not gadget.get( 'enabled', True ):
if ( not args.force_all
and not getattr( args, 'force_enable_' + gadget[ 'language' ] ) ):
@ -814,33 +187,36 @@ for name, gadget in GADGETS.items():
if getattr( args, 'disable_' + gadget[ 'language' ] ):
continue
InstallGagdet( name,
gadget,
failed,
all_adapters )
installer.InstallGagdet( name,
gadget,
succeeded,
failed,
all_adapters )
for name, gadget in CUSTOM_GADGETS.items():
InstallGagdet( name, gadget, failed, all_adapters )
adapter_config = json.dumps ( { 'adapters': all_adapters },
indent=2,
sort_keys=True )
installer.InstallGagdet( name, gadget, succeeded, failed, all_adapters )
if args.no_gadget_config:
print( "" )
print( "Would write the following gadgets: " )
print( adapter_config )
installer.WriteAdapters( all_adapters, to_file = sys.stdout )
else:
with open( install.GetGadgetConfigFile( vimspector_base ), 'w' ) as f:
f.write( adapter_config )
if failed:
raise RuntimeError( 'Failed to install gadgets: {}'.format(
','.join( failed ) ) )
installer.WriteAdapters( all_adapters )
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:
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

@ -34,6 +34,7 @@ endif
" - 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', '' )
@ -97,6 +98,19 @@ command! -bar
\ VimspectorReset
\ call vimspector#Reset()
" Installer commands
command! -bar -nargs=* -complete=custom,vimspector#CompleteInstall
\ VimspectorInstall
\ call vimspector#Install( <f-args> )
command! -bar -nargs=*
\ VimspectorUpdate
\ call vimspector#Update( <f-args> )
command! -bar -nargs=0
\ VimspectorAbortInstall
\ call vimspector#AbortInstall()
" Dummy autocommands so that we can call this whenever
augroup VimspectorUserAutoCmds
au!

View file

@ -30,7 +30,8 @@ from vimspector import ( breakpoints,
stack_trace,
utils,
variables,
settings )
settings,
installer )
from vimspector.vendor.json_minify import minify
# We cache this once, and don't allow it to change (FIXME?)
@ -51,8 +52,7 @@ class DebugSession( object ):
self._logger.info( "API is: {}".format( api_prefix ) )
self._logger.info( 'VIMSPECTOR_HOME = %s', VIMSPECTOR_HOME )
self._logger.info( 'gadgetDir = %s',
install.GetGadgetDir( VIMSPECTOR_HOME,
install.GetOS() ) )
install.GetGadgetDir( VIMSPECTOR_HOME ) )
self._uiTab = None
self._stackTraceView = None
@ -91,7 +91,7 @@ class DebugSession( object ):
configurations = {}
adapters = {}
glob.glob( install.GetGadgetDir( VIMSPECTOR_HOME, install.GetOS() ) )
glob.glob( install.GetGadgetDir( VIMSPECTOR_HOME ) )
for gadget_config_file in PathsToAllGadgetConfigs( VIMSPECTOR_HOME,
current_file ):
self._logger.debug( f'Reading gadget config: {gadget_config_file}' )
@ -152,6 +152,24 @@ class DebugSession( object ):
adapter_dict = adapters.get( adapter )
if adapter_dict is None:
suggested_gadgets = installer.FindGadgetForAdapter( adapter )
if suggested_gadgets:
response = utils.AskForInput(
f"The specified adapter '{adapter}' is not "
"installed. Would you like to install the following gadgets? ",
' '.join( suggested_gadgets ) )
if response:
new_launch_variables = dict( launch_variables )
new_launch_variables[ 'configuration' ] = configuration_name
installer.RunInstaller(
self._api_prefix,
*shlex.split( response ),
then = lambda: self.Start( new_launch_variables ) )
return
elif response is None:
return
utils.UserMessage( f"The specified adapter '{adapter}' is not "
"available. Did you forget to run "
"'install_gadget.py'?",
@ -192,7 +210,7 @@ class DebugSession( object ):
'dollar': '$', # HACK. Hote '$$' also works.
'workspaceRoot': self._workspace_root,
'workspaceFolder': self._workspace_root,
'gadgetDir': install.GetGadgetDir( VIMSPECTOR_HOME, install.GetOS() ),
'gadgetDir': install.GetGadgetDir( VIMSPECTOR_HOME ),
'file': current_file,
}
@ -529,7 +547,6 @@ class DebugSession( object ):
stack_trace_window = vim.current.window
one_third = int( vim.eval( 'winheight( 0 )' ) ) / 3
self._stackTraceView = stack_trace.StackTraceView( self,
self._connection,
stack_trace_window )
# Watches
@ -547,17 +564,15 @@ class DebugSession( object ):
with utils.LetCurrentWindow( stack_trace_window ):
vim.command( f'{ one_third }wincmd _' )
self._variablesView = variables.VariablesView( self._connection,
vars_window,
self._variablesView = variables.VariablesView( vars_window,
watch_window )
# Output/logging
vim.current.window = code_window
vim.command( f'rightbelow { settings.Int( "bottombar_height", 10 ) }new' )
output_window = vim.current.window
self._outputView = output.OutputView( self._connection,
output_window,
self._api_prefix )
self._outputView = output.DAPOutputView( output_window,
self._api_prefix )
# TODO: If/when we support multiple sessions, we'll need some way to
# indicate which tab was created and store all the tabs

View file

@ -23,8 +23,7 @@ from vimspector import install, utils
def SetUpDebugpy( wait=False, port=5678 ):
sys.path.insert(
1,
os.path.join( install.GetGadgetDir( utils.GetVimspectorBase(),
install.GetOS() ),
os.path.join( install.GetGadgetDir( utils.GetVimspectorBase() ),
'debugpy',
'build',
'lib' ) )

View file

@ -0,0 +1,490 @@
# 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',
'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': '0.27.0',
"adapters": {
"vscode-cpptools": {
"name": "cppdbg",
"command": [
"${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7"
],
"attach": {
"pidProperty": "processId",
"pidSelect": "ask"
},
"configuration": {
"type": "cppdbg",
"args": [],
"cwd": "${workspaceRoot}",
"environment": [],
}
},
},
},
'linux': {
'file_name': 'cpptools-linux.vsix',
'checksum':
'3695202e1e75a03de18049323b66d868165123f26151f8c974a480eaf0205435',
},
'macos': {
'file_name': 'cpptools-osx.vsix',
'checksum':
'cb061e3acd7559a539e5586f8d3f535101c4ec4e8a48195856d1d39380b5cf3c',
},
'windows': {
'file_name': 'cpptools-win32.vsix',
'checksum':
'aa294368ed16d48c59e49c8000e146eae5a19ad07b654efed5db8ec93b24229e',
"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"
}
},
},
},
},
'vscode-python': {
'language': 'python.legacy',
'enabled': False,
'download': {
'url': 'https://github.com/Microsoft/vscode-python/releases/download/'
'${version}/${file_name}',
},
'all': {
'version': '2019.11.50794',
'file_name': 'ms-python-release.vsix',
'checksum':
'6a9edf9ecabed14aac424e6007858068204a3638bf3bb4f235bd6035d823acc6',
},
'adapters': {
"vscode-python": {
"name": "vscode-python",
"command": [
"node",
"${gadgetDir}/vscode-python/out/client/debugger/debugAdapter/main.js",
],
}
},
},
'debugpy': {
'language': 'python',
'download': {
'url': 'https://github.com/microsoft/debugpy/archive/${file_name}'
},
'all': {
'version': '1.0.0b12',
'file_name': 'v1.0.0b12.zip',
'checksum':
'210632bba2221fbb841c9785a615258819ceec401d1abdbeb5f2326f12cc72a1'
},
'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}",
}
},
},
'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': 'master'
},
'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',
'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':
'c1dc6ed58c3f5b0473cfb4985a96552999360ceb9795e42d9c9be64af054f821',
},
'linux': {
'file_name': 'netcoredbg-linux-master.tar.gz',
'checksum': '',
},
'windows': {
'file_name': 'netcoredbg-win64-master.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"
},
},
}
},
'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.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/microsoft/vscode-go/releases/download/'
'${version}/${file_name}'
},
'all': {
'version': '0.11.4',
'file_name': 'Go-0.11.4.vsix',
'checksum':
'ff7d7b944da5448974cb3a0086f4a2fd48e2086742d9c013d6964283d416027e'
},
'adapters': {
'vscode-go': {
'name': 'delve',
'command': [
'node',
'${gadgetDir}/vscode-go/out/src/debugAdapter/goDebug.js'
],
},
},
},
'vscode-php-debug': {
'language': 'php',
'enabled': False,
'download': {
'url':
'https://github.com/felixfbecker/vscode-php-debug/releases/download/'
'${version}/${file_name}',
},
'all': {
'version': 'v1.13.0',
'file_name': 'php-debug.vsix',
'checksum':
'8a51e593458fd14623c1c89ebab87347b087d67087717f18bcf77bb788052718',
},
'adapters': {
'vscode-php-debug': {
'name': "php-debug",
'command': [
'node',
"${gadgetDir}/vscode-php-debug/out/phpDebug.js",
]
}
}
},
'vscode-node-debug2': {
'language': 'node',
'enabled': False,
'repo': {
'url': 'https://github.com/microsoft/vscode-node-debug2',
'ref': 'v1.42.0',
},
'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.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'
],
},
},
},
'CodeLLDB': {
'language': 'rust',
'enabled': False,
'download': {
'url': 'https://github.com/vadimcn/vscode-lldb/releases/download/'
'${version}/${file_name}',
},
'all': {
'version': 'v1.5.3',
},
'macos': {
'file_name': 'codelldb-x86_64-darwin.vsix',
'checksum':
'7505bc1cdfcfd1cb981e2996aec62d63577440709bac31dcadb41a3b4b44631a',
'make_executable': [
'adapter/codelldb',
'lldb/bin/debugserver',
'lldb/bin/lldb',
'lldb/bin/lldb-argdumper',
],
},
'linux': {
'file_name': 'codelldb-x86_64-linux.vsix',
'checksum':
'ce7efc3e94d775368e5942a02bf5c326b6809a0b4c389f79ffa6a8f6f6b72139',
'make_executable': [
'adapter/codelldb',
'lldb/bin/lldb',
'lldb/bin/lldb-server',
'lldb/bin/lldb-argdumper',
],
},
'windows': {
'file_name': 'codelldb-x86_64-windows.vsix',
'checksum':
'',
'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",
}
},
},
},
}

View file

@ -38,18 +38,16 @@ def MakeInstallDirs( vimspector_base ):
mkdirs( GetConfigDirForFiletype( vimspector_base, '_all' ) )
def GetGadgetDir( vimspector_base, OS ):
return os.path.join( os.path.abspath( vimspector_base ), 'gadgets', OS )
def GetGadgetDir( vimspector_base ):
return os.path.join( os.path.abspath( vimspector_base ), 'gadgets', GetOS() )
def GetGadgetConfigFile( vimspector_base ):
return os.path.join( GetGadgetDir( vimspector_base, GetOS() ),
'.gadgets.json' )
return os.path.join( GetGadgetDir( vimspector_base ), '.gadgets.json' )
def GetGadgetConfigDir( vimspector_base ):
return os.path.join( GetGadgetDir( vimspector_base, GetOS() ),
'.gadgets.d' )
return os.path.join( GetGadgetDir( vimspector_base ), '.gadgets.d' )
def GetConfigDirForFiletype( vimspector_base, filetype ):

View file

@ -16,21 +16,397 @@
# limitations under the License.
from urllib import request
import io
import contextlib
import zipfile
import gzip
import shutil
import tarfile
import hashlib
import time
import ssl
import subprocess
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
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 )
def CheckCall( *args, **kwargs ):
if options.quiet:
out = subprocess.PIPE
else:
out = sys.stdout
kwargs[ 'stdout' ] = out
kwargs[ 'stderr' ] = subprocess.STDOUT
subprocess.check_call( *args, **kwargs )
def PathToAnyWorkingPython3():
# We can't rely on sys.executable because it's usually 'vim' (fixme, not with
# neovim?)
paths = os.environ[ 'PATH' ].split( os.pathsep )
if install.GetOS() == 'windows':
paths.insert( 0, os.getcwd() )
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:
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 RuntimeError( "Unable to find a working python3" )
def RunInstaller( api_prefix, *args, **kwargs ):
from vimspector import utils, output, settings
import vim
args = GadgetListToInstallerArgs( *args )
vimspector_home = utils.GetVimString( vim.vars, 'vimspector_home' )
vimspector_base_dir = utils.GetVimspectorBase()
global OUTPUT_VIEW
_ResetInstaller()
with utils.RestoreCurrentWindow():
vim.command( f'botright { settings.Int( "bottombar_height", 10 ) }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:
_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, *args ):
from vimspector import utils
Configure( vimspector_base = utils.GetVimspectorBase() )
args = list( args )
current_adapters = ReadAdapters( read_existing = True )
for adapter_name in current_adapters.keys():
args.extend( FindGadgetForAdapter( adapter_name ) )
if args:
RunInstaller( api_prefix, *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
if not gadget.get( 'enabled', True ):
installer_args.append( f'--force-enable-{ gadget[ "language" ] }' )
else:
installer_args.append( f'--enable-{ gadget[ "language" ] }' )
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
def InstallGeneric( name, root, gadget ):
extension = os.path.join( root, 'extension' )
for f in gadget.get( 'make_executable', [] ):
MakeExecutable( os.path.join( extension, f ) )
MakeExtensionSymlink( name, root )
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', '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 = [ './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 ):
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( 'Node 10 is required to install node debugger (sadly)' )
with CurrentWorkingDir( root ):
CheckCall( [ 'npm', 'install' ] )
CheckCall( [ 'npm', 'run', 'build' ] )
MakeSymlink( name, root )
def InstallGagdet( name, gadget, succeeded, failed, all_adapters ):
try:
print( f"Installing {name}..." )
v = {}
v.update( gadget.get( 'all', {} ) )
v.update( gadget.get( install.GetOS(), {} ) )
if 'download' in gadget:
if 'file_name' not in v:
raise RuntimeError( "Unsupported OS {} for gadget {}".format(
install.GetOS(),
name ) )
destination = os.path.join(
install.GetGadgetDir( options.vimspector_base ),
'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' ),
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( v )
ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( v )
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, v )
else:
InstallGeneric( name, root, v )
# Allow per-OS adapter overrides. v already did that for us...
all_adapters.update( v.get( 'adapters', {} ) )
# Add any other "all" adapters
all_adapters.update( gadget.get( 'adapters', {} ) )
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 ) )
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 )
@contextlib.contextmanager
@ -45,7 +421,7 @@ def CurrentWorkingDir( d ):
def MakeExecutable( file_path ):
# TODO: import stat and use them by _just_ adding the X bit.
print( 'Making executable: {}'.format( file_path ) )
Print( 'Making executable: {}'.format( file_path ) )
os.chmod( file_path, 0o755 )
@ -62,7 +438,7 @@ def WithRetry( f ):
return f( *args, **kwargs )
except Exception as e:
thrown = e
print( "Failed - {}, will retry in {} seconds".format( e, timeout ) )
Print( "Failed - {}, will retry in {} seconds".format( e, timeout ) )
time.sleep( timeout )
raise thrown
@ -90,13 +466,13 @@ def DownloadFileTo( url,
if os.path.exists( file_path ):
if checksum:
if ValidateCheckSumSHA256( file_path, checksum ):
print( "Checksum matches for {}, using it".format( file_path ) )
Print( "Checksum matches for {}, using it".format( file_path ) )
return file_path
else:
print( "Checksum doesn't match for {}, removing it".format(
Print( "Checksum doesn't match for {}, removing it".format(
file_path ) )
print( "Removing existing {}".format( file_path ) )
Print( "Removing existing {}".format( file_path ) )
os.remove( file_path )
r = request.Request( url, headers = { 'User-Agent': 'Vimspector' } )
@ -123,7 +499,7 @@ def DownloadFileTo( url,
GetChecksumSHA254( file_path ),
checksum ) )
else:
print( "Checksum for {}: {}".format( file_path,
Print( "Checksum for {}: {}".format( file_path,
GetChecksumSHA254( file_path ) ) )
return file_path
@ -141,7 +517,7 @@ def ValidateCheckSumSHA256( file_path, checksum ):
def RemoveIfExists( destination ):
if os.path.islink( destination ):
print( "Removing file {}".format( destination ) )
Print( "Removing file {}".format( destination ) )
os.remove( destination )
return
@ -152,21 +528,21 @@ def RemoveIfExists( destination ):
return "{}.{}".format( destination, N )
while os.path.isdir( BackupDir() ):
print( "Removing old dir {}".format( BackupDir() ) )
Print( "Removing old dir {}".format( BackupDir() ) )
try:
shutil.rmtree( BackupDir() )
print ( "OK, removed it" )
Print ( "OK, removed it" )
break
except OSError:
print ( "FAILED" )
Print ( "FAILED" )
N = N + 1
if os.path.exists( destination ):
print( "Removing dir {}".format( destination ) )
Print( "Removing dir {}".format( destination ) )
try:
shutil.rmtree( destination )
except OSError:
print( "FAILED, moving {} to dir {}".format( destination, BackupDir() ) )
Print( "FAILED, moving {} to dir {}".format( destination, BackupDir() ) )
os.rename( destination, BackupDir() )
@ -187,7 +563,7 @@ class ModePreservingZipFile( zipfile.ZipFile ):
def ExtractZipTo( file_path, destination, format ):
print( "Extracting {} to {}".format( file_path, destination ) )
Print( "Extracting {} to {}".format( file_path, destination ) )
RemoveIfExists( destination )
if format == 'zip':
@ -209,17 +585,17 @@ def ExtractZipTo( file_path, destination, format ):
# windows-generated tar files
os.makedirs( destination )
with CurrentWorkingDir( destination ):
subprocess.check_call( [ 'tar', 'zxvf', file_path ] )
CheckCall( [ 'tar', 'zxvf', file_path ] )
def MakeExtensionSymlink( vimspector_base, name, root ):
MakeSymlink( install.GetGadgetDir( vimspector_base,
install.GetOS() ),
name,
os.path.join( root, 'extension' ) ),
def MakeExtensionSymlink( name, root ):
MakeSymlink( name, os.path.join( root, 'extension' ) ),
def MakeSymlink( in_folder, link, pointing_to ):
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 )
@ -233,12 +609,7 @@ def MakeSymlink( in_folder, link, pointing_to ):
link_path = os.path.abspath( link_path )
if os.path.isdir( link_path ):
os.rmdir( link_path )
subprocess.check_call( [ 'cmd.exe',
'/c',
'mklink',
'/J',
link_path,
pointing_to ] )
CheckCall( [ 'cmd.exe', '/c', 'mklink', '/J', link_path, pointing_to ] )
else:
os.symlink( pointing_to_relative, link_path )
@ -246,13 +617,10 @@ def MakeSymlink( in_folder, link, pointing_to ):
def CloneRepoTo( url, ref, destination ):
RemoveIfExists( destination )
git_in_repo = [ 'git', '-C', destination ]
subprocess.check_call( [ 'git', 'clone', url, destination ] )
subprocess.check_call( git_in_repo + [ 'checkout', ref ] )
subprocess.check_call( git_in_repo + [ 'submodule', 'sync', '--recursive' ] )
subprocess.check_call( git_in_repo + [ 'submodule',
'update',
'--init',
'--recursive' ] )
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 ):

View file

@ -25,7 +25,7 @@ class TabBuffer( object ):
self.index = index
self.flag = False
self.is_job = False
self.job_category = None
self.syntax = None
BUFFER_MAP = {
@ -40,18 +40,26 @@ def CategoryToBuffer( category ):
return BUFFER_MAP.get( category, category )
VIEWS = set()
def ShowOutputInWindow( win_id, category ):
for view in VIEWS:
if view._window.valid and utils.WindowID( view._window ) == win_id:
view.ShowOutput( category )
return
raise ValueError( f'Unable to find output object for win id {win_id}!' )
class OutputView( object ):
def __init__( self, connection, window, api_prefix ):
"""Container for a 'tabbed' window of buffers that can be used to display
files or the output of commands."""
def __init__( self, window, api_prefix ):
self._window = window
self._connection = connection
self._buffers = {}
self._api_prefix = api_prefix
for b in set( BUFFER_MAP.values() ):
self._CreateBuffer( b )
self._CreateBuffer( 'Vimspector', file_name = utils.LOG_FILE )
self._ShowOutput( 'Console' )
VIEWS.add( self )
def Print( self, categroy, text ):
self._Print( 'server', text.splitlines() )
@ -82,21 +90,15 @@ class OutputView( object ):
with utils.RestoreCurrentBuffer( self._window ):
self._ShowOutput( category )
def ConnectionUp( self, connection ):
self._connection = connection
def ConnectionClosed( self ):
# Don't clear because output is probably still useful
self._connection = None
def Reset( self ):
self.Clear()
VIEWS.remove( self )
def Clear( self ):
for category, tab_buffer in self._buffers.items():
if tab_buffer.is_job:
utils.CleanUpCommand( tab_buffer.job_category or category,
self._api_prefix )
utils.CleanUpCommand( category, self._api_prefix )
utils.CleanUpHiddenBuffer( tab_buffer.buf )
# FIXME: nunmenu the WinBar ?
@ -125,6 +127,115 @@ class OutputView( object ):
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 ):
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' )
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 )
def _RenderWinBar( self, category ):
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 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() )
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() ):
self._CreateBuffer( b )
self._CreateBuffer( 'Vimspector', file_name = utils.LOG_FILE )
self._ShowOutput( 'Console' )
def ConnectionUp( self, connection ):
self._connection = connection
def ConnectionClosed( self ):
# Don't clear because output is probably still useful
self._connection = None
def Evaluate( self, frame, expression ):
self._Print( 'Console', [ 'Evaluating: ' + expression ] )
@ -146,82 +257,3 @@ class OutputView( object ):
request[ 'arguments' ][ 'frameId' ] = frame[ 'id' ]
self._connection.DoRequest( print_result, request )
def _ToggleFlag( self, category, flag ):
if self._buffers[ category ].flag != flag:
self._buffers[ category ].flag = flag
if self._window.valid:
with utils.LetCurrentWindow( self._window ):
self._RenderWinBar( category )
def RunJobWithOutput( self, category, cmd ):
self._CreateBuffer( category, cmd = cmd )
def _CreateBuffer( self, category, file_name = None, cmd = None ):
win = self._window
if not win.valid:
# We need to borrow the current window
win = vim.current.window
with utils.LetCurrentWindow( win ):
with utils.RestoreCurrentBuffer( win ):
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, err = utils.SetUpCommandBuffer( cmd, category, self._api_prefix )
self._buffers[ category + '-out' ] = TabBuffer( out,
len( self._buffers ) )
self._buffers[ category + '-out' ].is_job = True
self._buffers[ category + '-out' ].job_category = category
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' )
else:
utils.SetUpHiddenBuffer(
tab_buffer.buf,
'vimspector.Output:{0}'.format( category ) )
self._RenderWinBar( category )
def _RenderWinBar( self, category ):
tab_buffer = self._buffers[ category ]
try:
if tab_buffer.flag:
vim.command( 'nunmenu WinBar.{}'.format( utils.Escape( category ) ) )
else:
vim.command( 'nunmenu WinBar.{}*'.format( utils.Escape( category ) ) )
except vim.error as e:
# E329 means the menu doesn't exist; ignore that.
if 'E329' not in str( e ):
raise
vim.command( "nnoremenu 1.{0} WinBar.{1}{2} "
":call vimspector#ShowOutput( '{1}' )<CR>".format(
tab_buffer.index,
utils.Escape( category ),
'*' if tab_buffer.flag else '' ) )
def GetCategories( self ):
return list( self._buffers.keys() )

View file

@ -21,13 +21,13 @@ from vimspector import utils
class StackTraceView( object ):
def __init__( self, session, connection, win ):
def __init__( self, session, win ):
self._logger = logging.getLogger( __name__ )
utils.SetUpLogging( self._logger )
self._buf = win.buffer
self._session = session
self._connection = connection
self._connection = None
self._current_thread = None
self._current_frame = None

View file

@ -52,6 +52,12 @@ def BufferForFile( file_name ):
return vim.buffers[ BufferNumberForFile( 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:
@ -71,21 +77,30 @@ def OpenFileInCurrentWindow( file_name ):
return vim.buffers[ buffer_number ]
def SetUpCommandBuffer( cmd, name, api_prefix ):
bufs = vim.eval(
'vimspector#internal#{}job#StartCommandWithLog( {}, "{}" )'.format(
api_prefix,
json.dumps( cmd ),
name ) )
COMMAND_HANDLERS = {}
if bufs is None:
def OnCommandWithLogComplete( name, exit_code ):
cb = COMMAND_HANDLERS.get( name )
if cb:
cb( exit_code )
def SetUpCommandBuffer( cmd, name, api_prefix, completion_handler = None ):
COMMAND_HANDLERS[ name ] = completion_handler
buf = Call( f'vimspector#internal#{api_prefix}job#StartCommandWithLog',
cmd,
name )
if buf is None:
raise RuntimeError( "Unable to start job {}: {}".format( cmd, name ) )
elif not all( int( b ) > 0 for b in bufs ):
elif int( buf ) <= 0:
raise RuntimeError( "Unable to get all streams for job {}: {}".format(
name,
cmd ) )
return [ vim.buffers[ int( b ) ] for b in bufs ]
return vim.buffers[ int( buf ) ]
def CleanUpCommand( name, api_prefix ):
@ -184,9 +199,8 @@ def RestoreCurrentWindow():
try:
yield
finally:
if old_tabpage.valid:
if old_tabpage.valid and old_window.valid:
vim.current.tabpage = old_tabpage
if old_window.valid:
vim.current.window = old_window
@ -343,6 +357,7 @@ def AskForInput( prompt, default_value = None ):
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.
@ -558,7 +573,6 @@ def Call( vimscript_function, *args ):
call += 'g:' + arg_name
call += ')'
_logger.debug( 'Calling: {}'.format( call ) )
return vim.eval( call )
@ -605,8 +619,7 @@ def SetSyntax( current_syntax, syntax, *args ):
# 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:
with AnyWindowForBuffer( buf ):
vim.command( 'set syntax={}'.format( Escape( syntax ) ) )
Call( 'setbufvar', buf.number, '&syntax', syntax )
return syntax
@ -633,16 +646,26 @@ def HideSplash( api_prefix, splash ):
return None
def GetVimString( vim_dict, name, default=None ):
# FIXME: use 'encoding' ?
try:
value = vim_dict[ name ]
except KeyError:
return default
if isinstance( value, bytes ):
return value.decode( 'utf-8' )
return value
def GetVimspectorBase():
base = vim.vars.get( 'vimspector_base_dir' )
if base is None:
return os.path.abspath( os.path.join( os.path.dirname( __file__ ),
'..',
'..' ) )
elif isinstance( base, bytes ):
return base.decode( 'utf-8' )
else:
return base
return GetVimString( vim.vars,
'vimspector_base_dir',
os.path.abspath(
os.path.join( os.path.dirname( __file__ ),
'..',
'..' ) ) )
def GetUnusedLocalPort():
@ -655,5 +678,7 @@ def GetUnusedLocalPort():
return port
def WindowID( window, tab ):
def WindowID( window, tab=None ):
if tab is None:
tab = window.tabpage
return int( Call( 'win_getid', window.number, tab.number ) )

View file

@ -124,11 +124,11 @@ class View:
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 = connection
self._connection = None
self._current_syntax = ''
# Set up the "Variables" buffer in the variables_win

View file

@ -2,6 +2,7 @@
BASEDIR=$(dirname $0)
INSTALL=0
UPDATE=0
RUN_VIM="vim -N --clean --not-a-term"
RUN_TEST="${RUN_VIM} -S lib/run_test.vim"
BASEDIR_CMD='py3 pass'
@ -25,6 +26,15 @@ while [ -n "$1" ]; do
INSTALL=1
shift
;;
"--install-method")
shift
INSTALL=$1
shift
;;
"--update")
UPDATE=1
shift
;;
"--report")
shift
VIMSPECTOR_TEST_STDOUT=$1
@ -71,10 +81,36 @@ if [ "${out_fd}" = "1" ]; then
exec 3>&1
fi
if [ $INSTALL = 1 ]; then
python3 $(dirname $0)/install_gadget.py --basedir ${BASEDIR} --all
if [ "$INSTALL" = "1" ] || [ "$INSTALL" = "script" ]; then
if ! python3 $(dirname $0)/install_gadget.py --basedir ${BASEDIR} --all; 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"; then
echo "Vim installation reported errors" >&2
exit 1
fi
fi
if [ "$UPDATE" = "1" ]; then
if ! $RUN_VIM -u $(dirname $0)/tests/vimrc \
--cmd "${BASEDIR_CMD}" \
-c 'autocmd User VimspectorInstallSuccess qa!' \
-c 'autocmd User VimspectorInstallFailed cquit!' \
-c "VimspectorUpdate"; then
echo "Vim update reported errors" >&2
exit 1
fi
fi
if [ -z "$VIMSPECTOR_MIMODE" ]; then
if which lldb >/dev/null 2>&1; then
export VIMSPECTOR_MIMODE=lldb

View file

@ -0,0 +1,13 @@
if exists( 'b:current_syntax' )
finish
endif
let b:current_syntax = 'vimspector-installer'
syn keyword VimspectorInstalling Installing
syn keyword VimspectorDone Done
syn keyword VimspectorError Failed FAILED
hi default link VimspectorInstalling Constant
hi default link VimspectorDone DiffAdd
hi default link VimspectorError WarningMsg