Compare commits

..

No commits in common. "master" and "1833" have entirely different histories.

129 changed files with 2622 additions and 12935 deletions

View file

@ -17,12 +17,12 @@ assignees: ''
> Please answer the following questions
* Does your issue reproduce using `vim --clean -Nu /path/to/vimspector/support/minimal_vimrc` ? \[Yes/No]
* Does your issue reproduce using `vim -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`
> 1. Run `vim -Nu /path/to/vimspector/support/minimal_vimrc`
> 2. Open _this project_...
> 3. Press _this sequence of keys_
@ -57,12 +57,6 @@ discussing on Gitter rather than raising an issue.
* Version of Vimspector: (e.g. output of `git rev-parse HEAD` if cloned or the
name of the tarball used to install otherwise)
* Output of `:VimspectorDebugInfo`
```
paste here
```
* Output of `vim --version` or `nvim --version`
```

View file

@ -1,12 +0,0 @@
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

View file

@ -1,200 +0,0 @@
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

View file

@ -1,27 +0,0 @@
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'

2
.gitignore vendored
View file

@ -6,7 +6,6 @@ tests/*.res
tests/messages
tests/debuglog
test.log
*.testlog
gadgets/
package/
*.pyc
@ -20,4 +19,3 @@ support/test/csharp/*.exe*
configurations/
venv/
test-base/
tags

View file

@ -3,14 +3,11 @@ 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
@ -21,16 +18,12 @@ 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

@ -9,15 +9,6 @@
"configuration": {
"request": "attach"
}
},
"Python: Run current script": {
"adapter": "debugpy",
"configuration": {
"request": "launch",
"program": "${file}",
"args": [ "*${args:--update-gadget-config}" ],
"justMyCode#json": "${justMyCode:true}"
}
}
}
}

View file

@ -42,17 +42,7 @@ def Settings( **kwargs ):
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:

View file

@ -33,36 +33,12 @@ 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:
@ -70,7 +46,6 @@ of the following reasons:
* 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
@ -101,47 +76,11 @@ 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
vim -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
@ -149,8 +88,6 @@ When contributing pull requests, I ask that:
* You avoid using APIs that are not available in the versions listed in the
dependencies on README.md
* You add tests for your PR.
* You test your changes in both Vim and Neovim at the supported versions (and
state that in the PR).
* You follow the style of the code as-is; the python code is YCM-stye, it is
*not* PEP8, nor should it be.
@ -185,10 +122,10 @@ 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.
[`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`.
[`vint`][].
To run them:
@ -200,31 +137,7 @@ To run them:
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.
`azure-pipelines.yml`.
# Code of conduct

1133
README.md

File diff suppressed because it is too large Load diff

View file

@ -13,79 +13,52 @@
" 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
let s:enabled = vimspector#internal#state#Reset()
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()
function! vimspector#Launch() abort
if !s:enabled
return
endif
py3 _vimspector_session.Start( *vim.eval( 'a:000' ) )
py3 _vimspector_session.Start()
endfunction
function! vimspector#LaunchWithSettings( settings ) abort
if !s:Enabled()
if !s:enabled
return
endif
py3 _vimspector_session.Start( launch_variables = vim.eval( 'a:settings' ) )
endfunction
function! vimspector#Reset( ... ) abort
if !s:Enabled()
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' ) )
py3 _vimspector_session.Reset()
endfunction
function! vimspector#Restart() abort
if !s:Enabled()
if !s:enabled
return
endif
py3 _vimspector_session.Restart()
endfunction
function! vimspector#ClearBreakpoints() abort
if !s:Enabled()
if !s:enabled
return
endif
py3 _vimspector_session.ClearBreakpoints()
endfunction
function! vimspector#ToggleBreakpoint( ... ) abort
if !s:Enabled()
if !s:enabled
return
endif
if a:0 == 0
@ -96,43 +69,8 @@ function! vimspector#ToggleBreakpoint( ... ) abort
py3 _vimspector_session.ToggleBreakpoint( vim.eval( 'options' ) )
endfunction
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()
if !s:enabled
return
endif
if a:0 == 0
@ -145,120 +83,74 @@ function! vimspector#AddFunctionBreakpoint( function, ... ) abort
endfunction
function! vimspector#StepOver() abort
if !s:Enabled()
if !s:enabled
return
endif
py3 _vimspector_session.StepOver()
endfunction
function! vimspector#StepInto() abort
if !s:Enabled()
if !s:enabled
return
endif
py3 _vimspector_session.StepInto()
endfunction
function! vimspector#StepOut() abort
if !s:Enabled()
if !s:enabled
return
endif
py3 _vimspector_session.StepOut()
endfunction
function! vimspector#Continue() abort
if !s:Enabled()
if !s:enabled
return
endif
py3 _vimspector_session.Continue()
endfunction
function! vimspector#Pause() abort
if !s:Enabled()
if !s:enabled
return
endif
py3 _vimspector_session.Pause()
endfunction
function! vimspector#PauseContinueThread() abort
if !s:Enabled()
function! vimspector#Stop() 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' ) )
py3 _vimspector_session.Stop()
endfunction
function! vimspector#ExpandVariable() abort
if !s:Enabled()
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()
if !s:enabled
return
endif
py3 _vimspector_session.DeleteWatch()
endfunction
function! vimspector#GoToFrame() abort
if !s:Enabled()
if !s:enabled
return
endif
py3 _vimspector_session.ExpandFrameOrThread()
endfunction
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()
if !s:enabled
return
endif
if a:0 == 0
let expr = input( 'Enter watch expression: ',
\ '',
\ 'custom,vimspector#CompleteExpr' )
let expr = input( 'Enter watch expression: ' )
else
let expr = a:1
endif
@ -271,7 +163,7 @@ function! vimspector#AddWatch( ... ) abort
endfunction
function! vimspector#AddWatchPrompt( expr ) abort
if !s:Enabled()
if !s:enabled
return
endif
stopinsert
@ -280,24 +172,24 @@ function! vimspector#AddWatchPrompt( expr ) abort
endfunction
function! vimspector#Evaluate( expr ) abort
if !s:Enabled()
if !s:enabled
return
endif
py3 _vimspector_session.ShowOutput( 'Console' )
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ), True )
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) )
endfunction
function! vimspector#EvaluateConsole( expr ) abort
if !s:Enabled()
if !s:enabled
return
endif
stopinsert
setlocal nomodified
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ), False )
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) )
endfunction
function! vimspector#ShowOutput( ... ) abort
if !s:Enabled()
if !s:enabled
return
endif
if a:0 == 1
@ -308,7 +200,7 @@ function! vimspector#ShowOutput( ... ) abort
endfunction
function! vimspector#ShowOutputInWindow( win_id, category ) abort
if !s:Enabled()
if !s:enabled
return
endif
py3 __import__( 'vimspector',
@ -317,32 +209,15 @@ function! vimspector#ShowOutputInWindow( win_id, category ) abort
\ 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()
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()
if !s:enabled
return
endif
let buffers = py3eval( '_vimspector_session.GetOutputBuffers() '
@ -351,135 +226,18 @@ function! vimspector#CompleteOutput( ArgLead, CmdLine, CursorPos ) abort
endfunction
function! vimspector#CompleteExpr( ArgLead, CmdLine, CursorPos ) abort
if !s:Enabled()
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" ) ) )' ),
return join( py3eval( '_vimspector_session.GetCompletionsSync( '
\.' vim.eval( "a:CmdLine" ),'
\.' int( vim.eval( "a:CursorPos" ) ) )'
\. ' if _vimspector_session else []' ),
\ "\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()
if !s:enabled
return
endif
let prefix = vimspector#internal#state#GetAPIPrefix()
@ -491,7 +249,7 @@ function! vimspector#Install( bang, ... ) abort
endfunction
function! vimspector#CompleteInstall( ArgLead, CmdLine, CursorPos ) abort
if !s:Enabled()
if !s:enabled
return
endif
return py3eval( '"\n".join('
@ -501,7 +259,7 @@ function! vimspector#CompleteInstall( ArgLead, CmdLine, CursorPos ) abort
endfunction
function! vimspector#Update( bang, ... ) abort
if !s:Enabled()
if !s:enabled
return
endif
@ -514,7 +272,7 @@ function! vimspector#Update( bang, ... ) abort
endfunction
function! vimspector#AbortInstall() abort
if !s:Enabled()
if !s:enabled
return
endif
@ -523,49 +281,6 @@ function! vimspector#AbortInstall() 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,323 +19,16 @@ let s:save_cpo = &cpoptions
set cpoptions&vim
" }}}
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 )' )
" Returns: py.ShowBalloon( winnr, expresssion )
function! vimspector#internal#balloon#BalloonExpr() abort
" winnr + 1 because for *no good reason* winnr is 0 based here unlike
" everywhere else
" int() because for *no good reason* winnr is a string.
return py3eval('_vimspector_session.ShowBalloon('
\ . 'int( vim.eval( "v:beval_winnr" ) ) + 1,'
\ . 'vim.eval( "v:beval_text" ) )' )
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

@ -62,7 +62,7 @@ function! vimspector#internal#channel#StartDebugSession( config ) abort
\ )
endif
let l:addr = get( a:config, 'host', '127.0.0.1' ) . ':' . a:config[ 'port' ]
let l:addr = get( a:config, 'host', 'localhost' ) . ':' . a:config[ 'port' ]
echo 'Connecting to ' . l:addr . '... (waiting fo up to 10 seconds)'
let s:ch = ch_open( l:addr,
@ -95,44 +95,21 @@ EOF
endfunction
function! vimspector#internal#channel#StopDebugSession() abort
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
if exists( 's:ch' ) && ch_status( s:ch ) ==# 'open'
" 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 )
if exists( 's:job' )
if job_status( s:job ) ==# 'run'
call job_stop( s:job, 'kill' )
endif
unlet s:job
endif
endfunction
function! vimspector#internal#channel#Reset() abort

View file

@ -66,7 +66,7 @@ function! vimspector#internal#neochannel#StartDebugSession( config ) abort
endtry
endif
let l:addr = get( a:config, 'host', '127.0.0.1' ) . ':' . a:config[ 'port' ]
let l:addr = get( a:config, 'host', 'localhost' ) . ':' . a:config[ 'port' ]
let attempt = 1
while attempt <= 10

View file

@ -159,16 +159,15 @@ function! s:_OnCommandEvent( category, id, data, event ) abort
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!
" if the buffer is visible, scroll it
let w = bufwinnr( buffer )
if w > 0
let cw = winnr()
try
noautocmd execute w . 'wincmd w'
noautocmd normal! Gz-
execute w . 'wincmd w'
normal! Gz-
finally
noautocmd execute cw . 'wincmd w'
execute cw . 'wincmd w'
endtry
endif
elseif a:event ==# 'exit'

View file

@ -26,39 +26,24 @@ set cpoptions&vim
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 )
call nvim_buf_set_lines(buf, 0, -1, v:true, [ a:message ] )
let win = nvim_open_win(buf, 0, s:GetSplashConfig( message ) )
let l = len( a:message )
let opts = {
\ 'relative': 'editor',
\ 'width': l,
\ 'height': 1,
\ 'col': ( &columns / 2 ) - ( l / 2 ),
\ 'row': &lines / 2,
\ 'anchor': 'NW',
\ 'style': 'minimal',
\ 'focusable': v:false,
\ }
let win = nvim_open_win(buf, 0, opts)
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
@ -68,9 +53,7 @@ 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 ) )
call nvim_buf_set_lines(splash.buf, 0, -1, v:true, [ a:message ] )
return a:id
endfunction
@ -80,56 +63,6 @@ function! vimspector#internal#neopopup#HideSplash( id ) abort
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

@ -12,7 +12,6 @@
" 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 {{{
@ -33,113 +32,6 @@ 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

@ -34,7 +34,6 @@ function! vimspector#internal#state#Reset() abort
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

147
azure-pipelines.yml Normal file
View file

@ -0,0 +1,147 @@
trigger:
- master
pr:
- master
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/ *.py'
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/ compiler/ plugin/ tests/ syntax/
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: v2 | gadgets | $(Agent.OS) | python3/vimspector/gadgets.py
path: gadgets/linux/download
displayName: Cache gadgets
- bash: vim --version
displayName: 'Print vim version information'
- bash: |
eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
export GOPATH=$HOME/go
./run_tests --install --update --report messages --quiet
displayName: 'Run the tests'
env:
VIMSPECTOR_MIMODE: gdb
- bash: ./make_package linux $(Build.SourceVersion)
displayName: 'Package'
# TODO: test the tarball
- task: PublishPipelineArtifact@0
inputs:
artifactName: 'package-linux'
targetPath: 'package/linux-$(Build.SourceVersion).tar.gz'
- job: 'macos'
pool:
vmImage: 'macOS-10.14'
steps:
- bash: |
brew unlink node@6
brew update
for p in macvim node@10 tcl-tk llvm; do
brew install $p
brew outdated $p || brew install $p
done
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: v2 | gadgets | $(Agent.OS) | python3/vimspector/gadgets.py
path: gadgets/macos/download
displayName: Cache gadgets
- bash: vim --version
displayName: 'Print vim version information'
- bash: ./run_tests --install --update --report messages --quiet
displayName: 'Run the tests'
env:
VIMSPECTOR_MIMODE: lldb
- bash: ./make_package macos $(Build.SourceVersion)
displayName: 'Package'
# TODO: test the tarball
- 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

@ -37,68 +37,49 @@ if ! exists( ':' . s:make_cmd )
endif
function! VimGetCurrentFunction()
echom s:GetCurrentFunction()[ 0 ]
echom s:GetCurrentFunction()
endfunction
function! s:GetCurrentFunction()
" Store the cursor position; we'll need to reset it
let [ buf, row, col, offset ] = getpos( '.' )
let [ l:buf, l:row, l:col, l:offset ] = getpos( '.' )
let [ test_function, test_function_line ] = [ v:null, -1 ]
let l:test_function = ''
let pattern = '\V\C\s\*func\%\(tion\)\?!\?\s\+\(\<\w\+\>\)\.\*\$'
let l:pattern = '\V\C\s\*function!\?\s\+\(\<\w\+\>\)\.\*\$'
let lnum = prevnonblank( '.' )
let l:lnum = prevnonblank( '.' )
" Find the top-level method and class
while lnum > 0
call cursor( lnum, 1 )
let lnum = search( pattern, 'bcnWz' )
while l:lnum > 0
call cursor( l:lnum, 1 )
let l:lnum = search( l:pattern, 'bcnWz' )
if lnum <= 0
call cursor( row, col )
return [ test_function, test_function_line ]
if l:lnum <= 0
call cursor( l:row, l:col )
return l:test_function
endif
let this_decl = substitute( getline( lnum ), pattern, '\1', '' )
let this_decl_is_test = match( this_decl, '\V\C\^Test_' ) >= 0
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
if this_decl_is_test
let [ test_function, test_function_line ] = [ this_decl, lnum ]
if l:this_decl_is_test
let l:test_function = l:this_decl
if indent( lnum ) == 0
call cursor( row, col )
return [ test_function, test_function_line ]
if indent( l:lnum ) == 0
call cursor( l:row, l:col )
return l:test_function
endif
endif
let lnum = prevnonblank( lnum - 1 )
let l:lnum = prevnonblank( l: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()[ 0 ]
let l:test_func_name = s:GetCurrentFunction()
if l:test_func_name ==# ''
echo 'No test method found'
@ -151,36 +132,10 @@ 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> œ :FuncLine<CR>
nnoremap <buffer> œ :cnext<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> )

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -28,5 +28,3 @@ 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"

View file

@ -1,13 +1,13 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (6.0.3.6)
activesupport (6.0.3.1)
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)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
coffee-script (2.4.1)
coffee-script-source
@ -16,49 +16,46 @@ GEM
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)
concurrent-ruby (1.1.6)
dnsruby (1.61.3)
addressable (~> 2.5)
em-websocket (0.5.1)
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)
faraday (1.0.0)
multipart-post (>= 1.2, < 3)
ruby2_keywords
faraday-net_http (1.0.1)
ffi (1.15.0)
ffi (1.12.2)
forwardable-extended (2.6.0)
gemoji (3.0.1)
github-pages (214)
github-pages-health-check (= 1.17.0)
jekyll (= 3.9.0)
github-pages (204)
github-pages-health-check (= 1.16.1)
jekyll (= 3.8.5)
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-feed (= 0.13.0)
jekyll-gist (= 1.5.0)
jekyll-github-metadata (= 2.13.0)
jekyll-mentions (= 1.6.0)
jekyll-mentions (= 1.5.1)
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-redirect-from (= 0.15.0)
jekyll-relative-links (= 0.6.1)
jekyll-remote-theme (= 0.4.3)
jekyll-remote-theme (= 0.4.1)
jekyll-sass-converter (= 1.5.2)
jekyll-seo-tag (= 2.7.1)
jekyll-seo-tag (= 2.6.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-hacker (= 0.1.1)
jekyll-theme-leap-day (= 0.1.1)
jekyll-theme-merlot (= 0.1.1)
jekyll-theme-midnight (= 0.1.1)
@ -69,35 +66,34 @@ GEM
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)
jemoji (= 0.11.1)
kramdown (= 1.17.0)
liquid (= 4.0.3)
mercenary (~> 0.3)
minima (= 2.5.1)
nokogiri (>= 1.10.4, < 2.0)
rouge (= 3.26.0)
rouge (= 3.13.0)
terminal-table (~> 1.4)
github-pages-health-check (1.17.0)
github-pages-health-check (1.16.1)
addressable (~> 2.3)
dnsruby (~> 1.60)
octokit (~> 4.0)
public_suffix (>= 2.0.2, < 5.0)
public_suffix (~> 3.0)
typhoeus (~> 1.3)
html-pipeline (2.14.0)
html-pipeline (2.12.3)
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.6.0)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
jekyll (3.9.0)
jekyll (3.8.5)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 0.7)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 2.0)
kramdown (>= 1.17, < 3)
kramdown (~> 1.14)
liquid (~> 4.0)
mercenary (~> 0.3.3)
pathutil (~> 0.9)
@ -117,14 +113,14 @@ GEM
rouge (>= 2.0, < 4.0)
jekyll-default-layout (0.1.4)
jekyll (~> 3.0)
jekyll-feed (0.15.1)
jekyll-feed (0.13.0)
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)
jekyll-mentions (1.5.1)
html-pipeline (~> 2.3)
jekyll (>= 3.7, < 5.0)
jekyll-optional-front-matter (0.3.2)
@ -132,19 +128,18 @@ GEM
jekyll-paginate (1.1.0)
jekyll-readme-index (0.3.0)
jekyll (>= 3.0, < 5.0)
jekyll-redirect-from (0.16.0)
jekyll-redirect-from (0.15.0)
jekyll (>= 3.3, < 5.0)
jekyll-relative-links (0.6.1)
jekyll (>= 3.3, < 5.0)
jekyll-remote-theme (0.4.3)
jekyll-remote-theme (0.4.1)
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)
rubyzip (>= 1.3.0)
jekyll-sass-converter (1.5.2)
sass (~> 3.4)
jekyll-seo-tag (2.7.1)
jekyll (>= 3.8, < 5.0)
jekyll-seo-tag (2.6.1)
jekyll (>= 3.3, < 5.0)
jekyll-sitemap (1.4.0)
jekyll (>= 3.7, < 5.0)
jekyll-swiss (1.0.0)
@ -157,8 +152,8 @@ GEM
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-theme-hacker (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-leap-day (0.1.1)
jekyll (~> 3.5)
@ -192,45 +187,38 @@ GEM
jekyll (>= 3.3, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
jemoji (0.12.0)
jemoji (0.11.1)
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)
kramdown (1.17.0)
liquid (4.0.3)
listen (3.5.1)
listen (3.2.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.3.6)
mini_portile2 (2.5.1)
mini_portile2 (2.4.0)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.14.4)
minitest (5.14.1)
multipart-post (2.1.1)
nokogiri (1.11.5)
mini_portile2 (~> 2.5.0)
racc (~> 1.4)
octokit (4.20.0)
nokogiri (1.10.7)
mini_portile2 (~> 2.4.0)
octokit (4.15.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)
public_suffix (3.1.1)
rb-fsevent (0.10.3)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.5)
rouge (3.26.0)
ruby-enum (0.9.0)
rouge (3.13.0)
ruby-enum (0.7.2)
i18n
ruby2_keywords (0.0.4)
rubyzip (2.3.0)
rubyzip (2.2.0)
safe_yaml (1.0.5)
sass (3.7.4)
sass-listen (~> 4.0.0)
@ -240,21 +228,15 @@ GEM
sawyer (0.8.2)
addressable (>= 2.3.5)
faraday (> 0.8, < 2.0)
simpleidn (0.2.1)
unf (~> 0.1.4)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
typhoeus (1.4.0)
typhoeus (1.3.1)
ethon (>= 0.9.0)
tzinfo (1.2.9)
tzinfo (1.2.7)
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)
unicode-display_width (1.6.1)
zeitwerk (2.3.0)
PLATFORMS
ruby
@ -264,7 +246,6 @@ DEPENDENCIES
jekyll-feed (~> 0.6)
minima (~> 2.0)
tzinfo-data
webrick (~> 1.7)
BUNDLED WITH
2.2.3
2.0.2

View file

@ -1,7 +1,7 @@
To update/install:
gem install bundler
bundle install --path vendor/bundle
bundle install
To run a local server/test the build

View file

@ -12,8 +12,7 @@ for Vimspector.
* [Debug profile configuration](#debug-profile-configuration)
* [Replacements and variables](#replacements-and-variables)
* [The splat operator](#the-splat-operator)
* [Default values](#default-values)
* [Coercing Types](#coercing-types)
* [Default values](#default-values)
* [Configuration Format](#configuration-format)
* [Files and locations](#files-and-locations)
* [Adapter configurations](#adapter-configurations)
@ -21,7 +20,7 @@ for Vimspector.
* [Configuration selection](#configuration-selection)
* [Specifying a default configuration](#specifying-a-default-configuration)
* [Preventing automatic selection](#preventing-automatic-selection)
* [Exception Breakpoints](#exception-breakpoints)
* [Exception breakpionts](#exception-breakpionts)
* [Predefined Variables](#predefined-variables)
* [Remote Debugging Support](#remote-debugging-support)
* [Python (debugpy) Example](#python-debugpy-example)
@ -30,14 +29,14 @@ for Vimspector.
* [Appendix: Configuration file format](#appendix-configuration-file-format)
* [Appendix: Editor configuration](#appendix-editor-configuration)
<!-- Added by: ben, at: Thu 13 Aug 2020 17:42:11 BST -->
<!-- Added by: ben, at: Fri 31 Jul 2020 22:13:39 BST -->
<!--te-->
## Concepts
As Vimspector supports debugging arbitrary projects, you need to tell it a few
details about what you want to debug, and how to go about doing that.
deatils about what you want to debug, and how to go about doing that.
In order to debug things, Vimspector requires a Debug Adapter which bridges
between Vimspector and the actual debugger tool. Vimspector can be used with any
@ -161,11 +160,11 @@ the following variable substitutions:
provide a value interactively when starting debugging. Vimspector remembers
what they said and provides it as the default should they debug again.
* `${SecretToken}` - this variable is provided by the configuration's
`variables` block. Its value is taken from the `strip`'d result of running
`variables` block. Its value is taken from the `strip`ped result of running
the shell command. Note these variables can be supplied by both the debug and
adapter configurations and can be either static strings or shell commands.
#### The splat operator
### The splat operator
Often we want to create a single `.vimspector.json` entry which encompasses many
use cases, as it is tedious to write every use case/start up option in JSON.
@ -178,8 +177,8 @@ example:
"args": [ "one", "two three", "four" ],
```
To help with this sort of case, Vimspector supports a 'splat' operator for
replacement variables operating within lists. The syntax is: `"*${var}`, which
To help with this sort of case, vimspector supports a 'splat' operator for
replacement variables apperaing within lists. The syntax is: `"*${var}`, which
means roughly "splice the contents of `${var}` into the list at this position".
`${var}` is parsed like a shell command (using python's `shlex` parser) and each
word is added as a list item.
@ -206,9 +205,9 @@ You can also combine with static values:
This would yield the intuitive result:
`[ "First", "one", "two three", "four", "Last" ]`
#### Default values
### Default values
You can specify replacements with default values. In this case if the user has
You can specify replacesments with default values. In this case if the user has
not specified a value, they are prompted but with the default value
pre-populated, allowing them to just press return to accept the default.
@ -221,8 +220,8 @@ a backslash in the JSON you must write `\\`, as in:
```
The default value can also be a replacement variable. However, this _must_ be a
variable that's already defined, such as one of the [predefined
variables](#predefined-variables), or one specified in a `variables` block. In
veriable that's already defined, such as one of the [predefined
variables](#predefined-variables), or one speified in a `variables` block. In
order to reference them, you _must_ use `${var}` syntax and you _must_ escape
the closing `}`. For example, the is a common and useful case:
@ -237,76 +236,6 @@ the closing `}`. For example, the is a common and useful case:
This will prompt the user to specify `script`, but it will default to the path
to the current file.
#### Coercing Types
Sometimes, you want to provide an option for a boolean parameter, or want to
allow the user to specify more than just strings. Vimspector allows you to do
this, ensuring that the resulting JSON is valid. This is done by interpreting a
value as a JSON string and substituting the resulting JSON value in its place.
This is easier to explain with an example. Let's say we want to offer the
ability to break on entry, as an option for the user. The launch configuration
requires `stopOnEntry` to be a bool. This doesn't work:
```json
"stopOnEntry": "${StopOnEntry}"
```
The reason is that if the user types `true`, the resulting object is:
```json
"stopOnEntry": "true"
```
The problem being that is a string, not a boolean. So Vimspector allows you to
re-interpret the string as a JSON value and use that instead. To do this, add
`#json` to the key's name. You can even add a default, like this:
```json
"stopOnEntry#json": "${stopOnEntry:true}"
```
If the user accepts the default, the resulting string `"true"` is coerced to a
JSON value `true`, and the suffix is stripped fom the key, resulting in the
following:
```json
"stopOnEntry": true
```
Which is what we need.
If you happen to have a key that already ends in `#json` (unlikely!), then you
can force Vimspector to treat the value as a string by appending `#s`, as in:
```json
"unlikelyKeyName#json#s": "this is a string, not JSON data"
```
***Advanced usage:***
The most common usage for this is for number and bool types, but it works for
objects too. If you want to be able to specify a whole object (e.g. a whole
`env` dict), then you can do that too:
```json
"env#json": "${Environment:{\\}}"
```
The default value here is `{}` (note the `}` must be escaped!). The user can
then enter something like `{ "MYVAR": "MyValue", "OTHER": "Other" }` and the
resulting object would be:
```json
"env": {
"MYVAR": "MyValue",
"OTHER": "Other"
}
```
It also works for lists, though [the splat operator](#the-splat-operator)
is usually more convenient for that.
## Configuration Format
All Vimspector configuration is defined in a JSON object. The complete
@ -339,7 +268,7 @@ abbreviations:
* `<vimspector home>` means the path to the Vimspector installation (such as
`$HOME/.vim/pack/vimspector/start/vimspector`)
* `<OS>` is either `macos` or `linux` depending on the host operating system.
* `<filetype>` is the Vim filetype. Where multiple filetypes are in effect,
* `<filetype>` is the Vim fileytype. Where multiple filetypes are in effect,
typically all filetypes are checked.
## Adapter configurations
@ -394,7 +323,7 @@ read and no further searching is done.
Only a single `.vimspector.json` is read. If one is found, the location of this
file is used for `${workspaceRoot}` and other workspace-relative paths.
In addition, users can create filetype-specific configurations in the Vimspector
In addition, users can create filetype-specific configurations in the vimspector
installation directory. This can be useful where the parameters for the debug
session for a particular filetype are always known in advance, or can always be
entered by the user. This allows for debugging to "just work" without any
@ -460,7 +389,7 @@ As noted, you can specify a default configuration with `"default": true`:
}
```
If multiple configurations are found with `default` set to `true`, then the
If multiple conifigurations are found with `default` set to `true`, then the
user is prompted anyway.
#### Preventing automatic selection
@ -483,10 +412,10 @@ central (as opposed to project-local) directory. For example:
Setting `autoselect` to `false` overrides setting `default` to `true`.
### Exception Breakpoints
### Exception breakpionts
Debug adapters have arbitrary configuration for exception breakpoints. Normally
this is presented as a series of question to the user on starting the debug
this is presented as a series of question to the user on startnig the debug
session. The question includes the name of the exception breakpoint option,
the default and the list of valid responses (usually `Y` or `N`).
@ -496,7 +425,7 @@ and configure the response `exception` mapping in the `breakpoints` mapping. If
the configured response is empty string, the debug adapter default will be used.
Referring to the above example, the following tells the debug adapter to use the
default value for `caught` exceptions and to break on `uncaught` exception:
default value for `caught` exceptoins and to break on `uncaught` exception:
```json
{
@ -529,7 +458,7 @@ cpp_catch: Break on C++: on catch (Y/N/default: N)?
The exception breakpoint "type" is `cpp_catch` and the default is `N`.
Use the following to set the values in configuration and not get asked:
Use the following to set the values in config and not get asked:
```json
"configurations": {
@ -568,26 +497,26 @@ The following variables are provided:
found
* `${workspaceFolder}` - the path of the folder where `.vimspector.json` was
found
* `${gadgetDir}` - path to the OS-specific gadget dir (`<vimspector
* `${gadgetDir}` - path to the OS-specifc gadget dir (`<vimspector
home>/gadgets/<OS>`)
* `${file}` - the current opened file
* `${relativeFile}` - the current opened file relative to `workspaceRoot`
* `${fileBasename}` - the current opened file's `basename`
* `${fileBasenameNoExtension}` - the current opened file's `basename` with no
* `${relativeFile}` - the current opened file relative to workspaceRoot
* `${fileBasename}` - the current opened file's basename
* `${fileBasenameNoExtension}` - the current opened file's basename with no
file extension
* `${fileDirname}` - the current opened file's `dirname`
* `${fileDirname}` - the current opened file's dirname
* `${fileExtname}` - the current opened file's extension
* `${cwd}` - the current working directory of the active window on launch
* `${unusedLocalPort}` - an unused local TCP port
## Remote Debugging Support
Vimspector has in-built support for executing remote debuggers (such as
Vimspector has in-built support for exectuting remote debuggers (such as
`gdbserver`, `debugpy`, `llvm-server` etc.). This is useful for environments
where the development is done on one host and the runtime is
some other host, account, container, etc.
In order for it to work, you have to set up paswordless SSH between the local
In order for it to work, you have to set up passwordless SSH between the local
and remote machines/accounts. Then just tell Vimspector how to remotely launch
and/or attach to the app.
@ -598,12 +527,12 @@ research that.
Vimspector's tools are intended to automate your existing process for setting
this up rather than to offer batteries-included approach. Ultimately, all
Vimspector is going to do is run your commands over SSH, or docker, and
vimspector is going to do is run your commands over SSH, or docker, and
co-ordinate with the adapter.
### Python (debugpy) Example
Here is some examples using the Vimspector built-in
Here is some examples using the vimspector built-in
remote support (using SSH) to remotely launch and attach a python application
and connect to it using debugpy.
@ -633,26 +562,19 @@ Vimspector then orchestrates the various tools to set you up.
// Command to launch the debugee and attach the debugger;
// %CMD% replaced with the remote-cmdLine configured in the launch
// configuration. (mandatory)
"runCommand": [
"python", "-m", "debugpy",
"--listen", "0.0.0.0:${port}",
"--wait-for-client",
"launchCommmand": [
"python", "-m", "debugpy", "--listen", "0.0.0.0:${port}",
"%CMD%"
]
// Optional alternative to runCommand (if you need to run multiple
// Optional alternative to launchCommmand (if you need to run multiple
// commands)
// "runCommands": [
// "launchCommmands": [
// [ /* first command */ ],
// [ /* second command */ ]
// ]
}
// optional delay to wait after running runCommand(s). This is often
// needed because of the way docker handles TCP, or if you're using some
// wrapper (e.g. to start the JVM)
// "delay": "1000m" // format as per :help sleep
},
"attach": {
"remote": {
@ -692,10 +614,6 @@ Vimspector then orchestrates the various tools to set you up.
// "args": [ "-o", "StrictHostKeyChecking=no" ]
// },
}
// optional delay to wait after running runCommand(s). This is often
// needed because of the way docker handles TCP, or if you're using some
// wrapper (e.g. to start the JVM)
// "delay": "1000m" // format as per :help sleep
}
}
},
@ -722,7 +640,7 @@ Vimspector then orchestrates the various tools to set you up.
"variables": {
// Just an example of how to specify a variable manually rather than
// vimspector asking for input from the user
"ServiceName": "${fileBasenameNoExtension}"
"ServiceName": "${fileBasenameNoExtention}"
},
"adapter": "python-remote",
@ -744,10 +662,10 @@ Vimspector then orchestrates the various tools to set you up.
### C-family (gdbserver) Example
This example uses Vimspector to remotely launch or attach to a binary using
This example uses vimspector to remotely launch or attach to a binary using
`gdbserver` and then instructs vscode-cpptools to attach to that `gdbserver`.
The approach is very similar to the above for python, just that we use gdbserver
The appraoch is very similar to the above for python, just that we use gdbserver
and have to tell cpptools a few more options.
```json
@ -765,7 +683,7 @@ and have to tell cpptools a few more options.
"remote": {
"host": "${host}",
"account": "${account}",
"runCommand": [
"launchCommmand": [
"gdbserver",
"--once",
"--no-startup-with-shell",
@ -834,7 +752,7 @@ and have to tell cpptools a few more options.
### Docker Example
This example uses Vimspector to remotely launch or attach to a docker container
This example uses vimspector to remotely launch or attach to a docker container
port.
``` json
@ -849,25 +767,19 @@ port.
// Command to launch the debugee and attach the debugger;
// %CMD% replaced with the remote-cmdLine configured in the launch
// configuration. (mandatory)
"runCommand": [
"python", "-m", "debugpy",
"--listen", "0.0.0.0:${port}",
"--wait-for-client",
"launchCommmand": [
"python", "-m", "debugpy", "--listen", "0.0.0.0:${port}",
"%CMD%"
]
// Optional alternative to runCommand (if you need to run multiple
// Optional alternative to launchCommmand (if you need to run multiple
// commands)
// "runCommands": [
// "launchCommmands": [
// [ /* first command */ ],
// [ /* second command */ ]
// ]
}
// optional delay to wait after running runCommand(s). This is often
// needed because of the way docker handles TCP
"delay": "1000m" // format as per :help sleep
},
"attach": {
"remote": {
@ -903,11 +815,6 @@ port.
// ]
}
// optional delay to wait after running runCommand(s). This is often
// needed because of the way docker handles TCP, or if you're using some
// wrapper (e.g. to start the JVM)
"delay": "1000m" // format as per :help sleep
}
}
},
@ -1035,8 +942,8 @@ This configuration can be adapted to any other LSP-based editor configuration
and is provided just as an example.
[dap]: https://microsoft.github.io/debug-adapter-protocol/
[schema]: http://puremourning.github.io/vimspector/schema/vimspector.schema.json
[gadget-schema]: http://puremourning.github.io/vimspector/schema/gadgets.schema.json
[schema]: http://puremourning.github.io/vimspector/vimspector.schema.json
[gadget-schema]: http://puremourning.github.io/vimspector/gadgets.schema.json
[YouCompleteMe]: https://github.com/ycm-core/YouCompleteMe
[lsp-examples]: https://github.com/ycm-core/lsp-examples
[vscode-json]: https://github.com/vscode-langservers/vscode-json-languageserver

View file

@ -38,144 +38,11 @@
]
}
},
"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": {
"adapter-common": {
"allOf": [
{ "type": "object" },
{ "$ref": "#/definitions/variables" },
{ "$ref": "#/definitions/adapter-remote" },
{ "$ref": "#/definitions/adapter-attach" },
{
"properties": {
"name": {
@ -187,30 +54,136 @@
"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" },
}
]
},
"adapter-attach": {
"properties": {
"attach": {
"type": "object",
"required": [ "pidSelect" ],
"properties": {
"pidSelect": {
"enum": [ "ask", "none" ]
},
"pidProperty": {
"type": "string",
"description": "The launch config property which the PID should be injected into. Required when 'pidSelect' is 'ask'."
}
}
}
}
},
"adapter-remote": {
"properties": {
"remote": {
"type": "object",
"oneOf": [
{
"required": [
"host"
]
},
{
"required": [
"container"
]
}
],
"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)",
"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 -it)."
},
"pidCommand": {
"type": "array",
"items": { "type": "string" },
"description": "Required for remote-attach. Remote command to execute to return the PID to attach to."
},
"initCompleteCommand": {
"type": "array",
"items": { "type": "string" },
"description": "For remote-attach. Remote command to execute after initialization of the debug adapter. Can be used to work around buggy attach behaviour on certain platforms (advanced usage). Can contain the special token %PID% which is replaced with the PID returned by 'pidCommand'"
},
"attachCommands": {
"type": [ "array" ],
"items": { "type": "array", "items": { "type": "string" } },
"description": "For remote-attach. List of commands to execute remotely to set up the attach. Can contain the special token %PID% which is replaced with the PID returned by the remote 'pidCommand'."
},
"attachCommand": {
"type": "array",
"items": { "type": "string" },
"description": "A single command to execute for remote-attach. Like attachCommands but for a single command. If attachCommands is supplied, this is not used."
},
"runCommands": {
"type": [ "array" ],
"items": { "type": "array", "items": { "type": "string" } },
"description": "For remote-launch. List of commands to execute remotely to set up the launch. An entry in the array can be the special token '%CMD%' which is replaced with the evaluated 'remote-cmdLine' value in the debug configuration. This is useful to parameterize launcging remotely under something like gdbserver."
},
"runCommand": {
"type": "array",
"items": { "type": "string" },
"description": "A single command to execute for remote-launch. Like runCommands but for a single command."
}
}
}
}
},
"adapter": {
"oneOf": [
{
"anyOf": [
{ "required": [ "command" ] },
{ "required": [ "port" ] },
{ "required": [ "command", "port" ] }
"allOf": [
{ "$ref": "#/definitions/adapter-common" },
{
"required": [ "port" ],
"properties": {
"port": {
"oneOf": [
{
"type": "string",
"enum": [ "ask" ]
},
{
"type": "integer"
}
],
"description": "If supplied, indicates that a socket connection should be made to this port on 'localhost'. If the value is 'ask', then the user is asked to enter the port number to connect to."
}
}
}
]
},
{
"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."
"allOf": [
{ "$ref": "#/definitions/adapter-common" },
{
"required": [ "command" ],
"properties": {
"command": {
"type": [ "string", "array" ],
"description": "Command line to execute the debug adapter.",
"items": { "type": "string" }
},
"env": {
"type": "object",
"description": "Name/value pairs to set in the environment when starting the adapter."
},
"cwd": {
"type": "string",
"description": "Directory from which to start the adapter. Defaults to the working directory of the window on launch"
}
}
}
}
]
}
]
}

View file

@ -114,35 +114,32 @@ parser.add_argument( '--sudo',
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
lang = gadget[ 'language' ]
if lang in done_languages:
continue
done_languages.add( lang )
if not gadget.get( 'enabled', True ):
parser.add_argument(
'--enable-' + lang,
'--force-enable-' + lang,
action = 'store_true',
help = 'Install the {} debug adapter for {} support'.format(
help = 'Install the unsupported {} debug adapter for {} support'.format(
name,
lang ) )
continue
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(
'--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(
"--no-check-certificate",
@ -185,25 +182,15 @@ all_adapters = installer.ReadAdapters(
manifest = installer.Manifest()
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:
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
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
if not args.upgrade:
manifest.Clear( name )

View file

@ -13,13 +13,6 @@
" 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
@ -35,6 +28,11 @@ if exists( 'g:loaded_vimpector' )
endif
"}}}
" TODO:
" - Check Vim version (for jobs)
" - Check python support
" - Add commands/mappings/menus?
let g:loaded_vimpector = 1
let g:vimspector_home = expand( '<sfile>:p:h:h' )
@ -42,8 +40,6 @@ 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
@ -66,21 +62,6 @@ nnoremap <silent> <Plug>VimspectorStepInto
nnoremap <silent> <Plug>VimspectorStepOut
\ :<c-u>call vimspector#StepOut()<CR>
nnoremap <silent> <Plug>VimspectorRunToCursor
\ :<c-u>call vimspector#RunToCursor()<CR>
" Eval for normal mode
nnoremap <silent> <Plug>VimspectorBalloonEval
\ :<c-u>call vimspector#ShowEvalBalloon( 0 )<CR>
" And for visual modes
xnoremap <silent> <Plug>VimspectorBalloonEval
\ :<c-u>call vimspector#ShowEvalBalloon( 1 )<CR>
nnoremap <silent> <Plug>VimspectorUpFrame
\ :<c-u>call vimspector#UpFrame()<CR>
nnoremap <silent> <Plug>VimspectorDownFrame
\ :<c-u>call vimspector#DownFrame()<CR>
if s:mappings ==# 'VISUAL_STUDIO'
nmap <F5> <Plug>VimspectorContinue
nmap <S-F5> <Plug>VimspectorStop
@ -93,14 +74,12 @@ if s:mappings ==# 'VISUAL_STUDIO'
nmap <S-F11> <Plug>VimspectorStepOut
elseif s:mappings ==# 'HUMAN'
nmap <F5> <Plug>VimspectorContinue
nmap <leader><F5> <Plug>VimspectorLaunch
nmap <F3> <Plug>VimspectorStop
nmap <F4> <Plug>VimspectorRestart
nmap <F6> <Plug>VimspectorPause
nmap <F9> <Plug>VimspectorToggleBreakpoint
nmap <leader><F9> <Plug>VimspectorToggleConditionalBreakpoint
nmap <F8> <Plug>VimspectorAddFunctionBreakpoint
nmap <leader><F8> <Plug>VimspectorRunToCursor
nmap <F10> <Plug>VimspectorStepOver
nmap <F11> <Plug>VimspectorStepInto
nmap <F12> <Plug>VimspectorStepOut
@ -112,18 +91,12 @@ command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr
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
command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr
\ VimspectorEval
\ call vimspector#Evaluate( <f-args> )
command! -bar
\ VimspectorReset
\ call vimspector#Reset( { 'interactive': v:true } )
\ call vimspector#Reset()
" Installer commands
command! -bar -bang -nargs=* -complete=custom,vimspector#CompleteInstall
@ -140,17 +113,9 @@ command! -bar -nargs=0
" 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>' ) )
au!
au User VimspectorUICreated silent
au User VimspectorTerminalOpened silent
augroup END
" boilerplate {{{

View file

@ -21,7 +21,7 @@ import os
import logging
import json
from vimspector import utils, signs
from vimspector import utils
class ServerBreakpointHandler( object ):
@ -52,23 +52,14 @@ class ProjectBreakpoints( object ):
self._next_sign_id = 1
if not signs.SignDefined( 'vimspectorBP' ):
signs.DefineSign( 'vimspectorBP',
text = '●',
double_text = '●',
texthl = 'WarningMsg' )
if not utils.SignDefined( 'vimspectorBP' ):
vim.command( 'sign define vimspectorBP text=\\ ● texthl=WarningMsg' )
if not signs.SignDefined( 'vimspectorBPCond' ):
signs.DefineSign( 'vimspectorBPCond',
text = '◆',
double_text = '◆',
texthl = 'WarningMsg' )
if not utils.SignDefined( 'vimspectorBPCond' ):
vim.command( 'sign define vimspectorBPCond text=\\ ◆ texthl=WarningMsg' )
if not signs.SignDefined( 'vimspectorBPDisabled' ):
signs.DefineSign( 'vimspectorBPDisabled',
text = '●',
double_text = '●',
texthl = 'LineNr' )
if not utils.SignDefined( 'vimspectorBPDisabled' ):
vim.command( 'sign define vimspectorBPDisabled text=\\ ● texthl=LineNr' )
def ConnectionUp( self, connection ):
@ -133,7 +124,8 @@ class ProjectBreakpoints( object ):
for bp in breakpoints:
self._SignToLine( file_name, bp )
if 'sign_id' in bp:
signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' )
vim.command( 'sign unplace {0} group=VimspectorBP'.format(
bp[ 'sign_id' ] ) )
self._line_breakpoints = defaultdict( list )
self._func_breakpoints = []
@ -141,123 +133,51 @@ class ProjectBreakpoints( object ):
self.UpdateUI()
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
line, column = vim.current.window.cursor
file_name = vim.current.buffer.name
if not file_name:
return
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 )
found_bp = False
action = 'New'
for index, bp in enumerate( self._line_breakpoints[ file_name ] ):
self._SignToLine( file_name, bp )
if bp[ 'line' ] == line:
found_bp = True
if bp[ 'state' ] == 'ENABLED' and not self._connection:
bp[ 'state' ] = 'DISABLED'
action = 'Disable'
else:
if 'sign_id' in bp:
vim.command( 'sign unplace {0} group=VimspectorBP'.format(
bp[ 'sign_id' ] ) )
del self._line_breakpoints[ file_name ][ index ]
action = 'Delete'
break
self._logger.debug( "Toggle found bp at {}:{} ? {} ({})".format(
file_name,
line,
found_bp,
action ) )
if not found_bp:
self._line_breakpoints[ file_name ].append( {
'state': 'ENABLED',
'line': line,
'options': options,
# 'sign_id': <filled in when placed>,
#
# Used by other breakpoint types (specified in options):
# 'condition': ...,
# 'hitCondition': ...,
# 'logMessage': ...
} )
self.UpdateUI()
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',
@ -273,13 +193,11 @@ class ProjectBreakpoints( object ):
self.UpdateUI()
def UpdateUI( self, then = None ):
def UpdateUI( self ):
if self._connection:
self.SendBreakpoints( then )
self.SendBreakpoints()
else:
self._ShowBreakpoints()
if then:
then()
def SetBreakpointsHandler( self, handler ):
@ -299,25 +217,15 @@ class ProjectBreakpoints( object ):
awaiting = 0
def response_received( *failure_args ):
def response_received():
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 = [] ):
def response_handler( source, msg ):
if msg:
self._breakpoints_handler.AddBreakpoints( source, msg )
breakpoints = ( msg.get( 'body' ) or {} ).get( 'breakpoints' ) or []
self._UpdateTemporaryBreakpoints( breakpoints, temp_idxs )
response_received()
@ -328,14 +236,15 @@ class ProjectBreakpoints( object ):
# TODO: add the _configured_breakpoints to line_breakpoints
# TODO: the line numbers might have changed since pressing the F9 key!
for file_name, line_breakpoints in self._line_breakpoints.items():
temp_idxs = []
breakpoints = []
for bp in line_breakpoints:
self._SignToLine( file_name, bp )
if 'sign_id' in bp:
signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' )
vim.command( 'sign unplace {0} group=VimspectorBP'.format(
bp[ 'sign_id' ] ) )
del bp[ 'sign_id' ]
if bp[ 'state' ] != 'ENABLED':
@ -344,15 +253,8 @@ class ProjectBreakpoints( object ):
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 ),
'path': file_name,
@ -360,13 +262,7 @@ class ProjectBreakpoints( object ):
awaiting = awaiting + 1
self._connection.DoRequest(
# 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 ),
lambda msg: response_handler( source, msg ),
{
'command': 'setBreakpoints',
'arguments': {
@ -375,7 +271,7 @@ class ProjectBreakpoints( object ):
},
'sourceModified': False, # TODO: We can actually check this
},
failure_handler = response_received
failure_handler = lambda *_: response_received()
)
# TODO: Add the _configured_breakpoints to function breakpoints
@ -399,7 +295,7 @@ class ProjectBreakpoints( object ):
'breakpoints': breakpoints,
}
},
failure_handler = response_received
failure_handler = lambda *_: response_received()
)
if self._exception_breakpoints:
@ -410,7 +306,7 @@ class ProjectBreakpoints( object ):
'command': 'setExceptionBreakpoints',
'arguments': self._exception_breakpoints
},
failure_handler = response_received
failure_handler = lambda *_: response_received()
)
if awaiting == 0 and doneHandler:
@ -446,11 +342,14 @@ class ProjectBreakpoints( object ):
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 )
try:
result = utils.AskForInput(
"{}: Break on {} (Y/N/default: {})? ".format( f[ 'filter' ],
f[ 'label' ],
default_value ),
default_value )
except KeyboardInterrupt:
result = ''
if result == 'Y':
exception_filters.append( f[ 'filter' ] )
@ -467,18 +366,13 @@ 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:
signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' )
vim.command( 'sign unplace {0} group=VimspectorBP '.format(
bp[ 'sign_id' ] ) )
else:
bp[ 'sign_id' ] = self._next_sign_id
self._next_sign_id += 1
@ -487,24 +381,21 @@ class ProjectBreakpoints( object ):
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' ] )
vim.command(
'sign place {0} group=VimspectorBP line={1} name={2} file={3}'.format(
bp[ 'sign_id' ] ,
bp[ 'line' ],
sign,
file_name ) )
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', } ) ) )
json.dumps( { 'id': file_name, 'group': 'VimspectorBP', } ) ) )
if len( signs ) == 1 and len( signs[ 0 ][ 'signs' ] ) == 1:
bp[ 'line' ] = int( signs[ 0 ][ 'signs' ][ 0 ][ 'lnum' ] )

View file

@ -18,7 +18,7 @@ import logging
import json
from collections import defaultdict
from vimspector import utils, terminal, signs
from vimspector import utils, settings
class CodeView( object ):
@ -26,91 +26,32 @@ class CodeView( object ):
self._window = window
self._api_prefix = api_prefix
self._terminal = None
self._terminal_window = None
self._terminal_buffer_number = 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 ):
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( 'nnoremenu WinBar.■\\ Stop :call vimspector#Stop()<CR>' )
vim.command( 'nnoremenu WinBar.▶\\ Cont :call vimspector#Continue()<CR>' )
vim.command( 'nnoremenu WinBar.▷\\ Pause :call vimspector#Pause()<CR>' )
vim.command( 'nnoremenu WinBar.↷\\ Next :call vimspector#StepOver()<CR>' )
vim.command( 'nnoremenu WinBar.→\\ Step :call vimspector#StepInto()<CR>' )
vim.command( 'nnoremenu WinBar.←\\ Out :call vimspector#StepOut()<CR>' )
vim.command( 'nnoremenu WinBar.⟲: :call vimspector#Restart()<CR>' )
vim.command( 'nnoremenu WinBar.✕ :call vimspector#Reset()<CR>' )
if not 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' ] )
if not utils.SignDefined( 'vimspectorPC' ):
vim.command( 'sign define vimspectorPC text=\\ ▶ texthl=MatchParen' )
def SetCurrentFrame( self, frame ):
@ -118,15 +59,31 @@ class CodeView( object ):
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 self._signs[ 'vimspectorPC' ]:
vim.command( 'sign unplace {} group=VimspectorCode'.format(
self._signs[ 'vimspectorPC' ] ) )
self._signs[ 'vimspectorPC' ] = None
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
self._signs[ 'vimspectorPC' ] = self._next_sign_id
self._next_sign_id += 1
try:
vim.command( 'sign place {0} group=VimspectorCode priority=20 '
'line={1} name=vimspectorPC '
'file={2}'.format(
self._signs[ 'vimspectorPC' ],
frame[ 'line' ],
frame[ 'source' ][ 'path' ] ) )
except vim.error as e:
# Ignore 'invalid buffer name'
if 'E158' not in str( e ):
raise
if not self._window.valid:
return False
@ -134,7 +91,6 @@ class CodeView( object ):
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' ] ) )
@ -156,16 +112,14 @@ class CodeView( object ):
self.current_syntax = utils.ToUnicode(
vim.current.buffer.options[ 'syntax' ] )
self.ShowBreakpoints()
return True
def Clear( self ):
if self._signs[ 'vimspectorPC' ]:
signs.UnplaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode' )
vim.command( 'sign unplace {} group=VimspectorCode'.format(
self._signs[ 'vimspectorPC' ] ) )
self._signs[ 'vimspectorPC' ] = None
self._UndisplayPC()
self._UndisplaySigns()
self.current_syntax = None
@ -175,29 +129,25 @@ class CodeView( object ):
def AddBreakpoints( self, source, breakpoints ):
for breakpoint in breakpoints:
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
if 'source' not in breakpoint:
if source:
breakpoint[ 'source' ] = source
else:
self._logger.warn( 'missing source in breakpoint {0}'.format(
json.dumps( breakpoint ) ) )
continue
breakpoint[ 'source' ] = source
self._breakpoints[ source[ 'path' ] ].append( breakpoint )
self._breakpoints[ breakpoint[ '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.AddBreakpoint( bp )
return
self.AddBreakpoints( None, [ bp ] )
for _, breakpoint_list in self._breakpoints.items():
for index, breakpoint in enumerate( breakpoint_list ):
@ -207,31 +157,11 @@ class CodeView( object ):
return
# Not found. Assume new
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()
self.AddBreakpoints( None, [ bp ] )
def _UndisplaySigns( self ):
for sign_id in self._signs[ 'breakpoints' ]:
signs.UnplaceSign( sign_id, 'VimspectorCode' )
vim.command( 'sign unplace {} group=VimspectorCode'.format( sign_id ) )
self._signs[ 'breakpoints' ] = []
@ -250,17 +180,17 @@ class CodeView( object ):
sign_id = self._next_sign_id
self._next_sign_id += 1
self._signs[ 'breakpoints' ].append( sign_id )
if utils.BufferExists( file_name ):
signs.PlaceSign( sign_id,
'VimspectorCode',
'vimspectorBP' if breakpoint[ 'verified' ]
else 'vimspectorBPDisabled',
file_name,
breakpoint[ 'line' ] )
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 ) )
# We need to also check if there's a breakpoint on this PC line and chnge
# the PC
self._DisplayPC()
def BreakpointsAsQuickFix( self ):
qf = []
@ -279,11 +209,73 @@ class CodeView( object ):
def LaunchTerminal( self, params ):
self._terminal = terminal.LaunchTerminal( self._api_prefix,
params,
window_for_start = self._window,
existing_term = self._terminal )
# kind = params.get( 'kind', 'integrated' )
# FIXME: Change this tor return the PID rather than having debug_session
# work that out
return self._terminal.buffer_number
# FIXME: We don't support external terminals, and only open in the
# integrated one.
cwd = params[ 'cwd' ]
args = params[ 'args' ]
env = params.get( 'env', {} )
term_options = {
'vertical': 1,
'norestore': 1,
'cwd': cwd,
'env': env,
}
if self._window.valid:
window_for_start = self._window
else:
# TOOD: Where? Maybe we should just use botright vertical ...
window_for_start = vim.current.window
if self._terminal_window is not None and self._terminal_window.valid:
assert self._terminal_buffer_number
window_for_start = self._terminal_window
if ( self._terminal_window.buffer.number == self._terminal_buffer_number
and int( utils.Call( 'vimspector#internal#{}term#IsFinished'.format(
self._api_prefix ),
self._terminal_buffer_number ) ) ):
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[ 'vertical' ] and not term_options.get( 'curwin', 0 ):
term_options[ 'term_cols' ] = max(
min ( int( vim.eval( 'winwidth( 0 )' ) )
- settings.Int( 'code_minwidth', 82 ),
settings.Int( 'terminal_maxwidth', 80 ) ),
settings.Int( 'terminal_minwidth' , 10 )
)
buffer_number = int(
utils.Call(
'vimspector#internal#{}term#Start'.format( self._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" )
self._terminal_window = terminal_window
self._terminal_buffer_number = buffer_number
vim.vars[ 'vimspector_session_windows' ][ 'terminal' ] = utils.WindowID(
self._terminal_window,
vim.current.tabpage )
with utils.RestoreCursorPosition():
with utils.RestoreCurrentWindow():
with utils.RestoreCurrentBuffer( vim.current.window ):
vim.command( 'doautocmd User VimspectorTerminalOpened' )
return buffer_number

View file

@ -1,51 +0,0 @@
# 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, handlers, send_func ):
def __init__( self, handler, send_func ):
self._logger = logging.getLogger( __name__ )
utils.SetUpLogging( self._logger )
self._Write = send_func
self._SetState( 'READ_HEADER' )
self._buffer = bytes()
self._handlers = handlers
self._handler = handler
self._next_message_id = 0
self._outstanding_requests = {}
@ -124,7 +124,7 @@ class DebugAdapterConnection( object ):
def Reset( self ):
self._Write = None
self._handlers = None
self._handler = None
while self._outstanding_requests:
_, request = self._outstanding_requests.popitem()
@ -169,10 +169,6 @@ 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 ) )
@ -226,12 +222,7 @@ class DebugAdapterConnection( object ):
# self._logger.debug( 'Message received (raw): %s', payload )
try:
message = json.loads( payload, strict = False )
except Exception:
self._logger.exception( "Invalid message received: %s", payload )
self._SetState( 'READ_HEADER' )
raise
message = json.loads( payload )
self._logger.debug( 'Message received: {0}'.format( message ) )
@ -242,7 +233,7 @@ class DebugAdapterConnection( object ):
def _OnMessageReceived( self, message ):
if not self._handlers:
if not self._handler:
return
if message[ 'type' ] == 'response':
@ -275,21 +266,25 @@ 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, request.msg, message )
else:
for h in self._handlers:
if 'OnFailure' in dir( h ):
h.OnFailure( reason, request.msg, message )
utils.UserMessage( 'Request failed: {0}'.format( reason ) )
elif message[ 'type' ] == 'event':
method = 'OnEvent_' + message[ 'event' ]
for h in self._handlers:
if method in dir( h ):
getattr( h, method )( message )
if method in dir( self._handler ):
getattr( self._handler, method )( message )
else:
utils.UserMessage( 'Unhandled event: {0}'.format( message[ 'event' ] ),
persist = True )
elif message[ 'type' ] == 'request':
method = 'OnRequest_' + message[ 'command' ]
for h in self._handlers:
if method in dir( h ):
getattr( h, method )( message )
if method in dir( self._handler ):
getattr( self._handler, method )( message )
else:
utils.UserMessage(
'Unhandled request: {0}'.format( message[ 'command' ] ),
persist = True )
def _KillTimer( request ):

View file

@ -21,7 +21,6 @@ import shlex
import subprocess
import functools
import vim
import importlib
from vimspector import ( breakpoints,
code,
@ -32,7 +31,6 @@ from vimspector import ( breakpoints,
utils,
variables,
settings,
terminal,
installer )
from vimspector.vendor.json_minify import minify
@ -57,19 +55,16 @@ class DebugSession( object ):
install.GetGadgetDir( VIMSPECTOR_HOME ) )
self._uiTab = None
self._logView = None
self._stackTraceView = None
self._variablesView = None
self._outputView = None
self._breakpoints = breakpoints.ProjectBreakpoints()
self._splash_screen = None
self._remote_term = None
self._run_on_server_exit = None
self._configuration = None
self._adapter = None
self._launch_config = None
self._ResetServerState()
@ -79,28 +74,8 @@ class DebugSession( object ):
self._launch_complete = False
self._on_init_complete_handlers = []
self._server_capabilities = {}
self.ClearTemporaryBreakpoints()
def GetConfigurations( self, adapters ):
current_file = utils.GetBufferFilepath( vim.current.buffer )
filetypes = utils.GetBufferFiletypes( vim.current.buffer )
configurations = {}
for launch_config_file in PathsToAllConfigFiles( VIMSPECTOR_HOME,
current_file,
filetypes ):
self._logger.debug( f'Reading configurations from: {launch_config_file}' )
if not launch_config_file or not os.path.exists( launch_config_file ):
continue
with open( launch_config_file, 'r' ) as f:
database = json.loads( minify( f.read() ) )
configurations.update( database.get( 'configurations' ) or {} )
adapters.update( database.get( 'adapters' ) or {} )
return launch_config_file, configurations
def Start( self, force_choose=False, launch_variables = None ):
def Start( self, launch_variables = None ):
# We mutate launch_variables, so don't mutate the default argument.
# https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments
if launch_variables is None:
@ -110,17 +85,11 @@ class DebugSession( object ):
launch_variables )
self._configuration = None
self._adapter = None
self._launch_config = None
current_file = utils.GetBufferFilepath( vim.current.buffer )
filetypes = utils.GetBufferFiletypes( vim.current.buffer )
configurations = {}
adapters = {}
launch_config_file, configurations = self.GetConfigurations( adapters )
if not configurations:
utils.UserMessage( 'Unable to find any debug configurations. '
'You need to tell vimspector how to launch your '
'application.' )
return
glob.glob( install.GetGadgetDir( VIMSPECTOR_HOME ) )
for gadget_config_file in PathsToAllGadgetConfigs( VIMSPECTOR_HOME,
@ -133,13 +102,26 @@ class DebugSession( object ):
a = json.loads( minify( f.read() ) ).get( 'adapters' ) or {}
adapters.update( a )
for launch_config_file in PathsToAllConfigFiles( VIMSPECTOR_HOME,
current_file,
filetypes ):
self._logger.debug( f'Reading configurations from: {launch_config_file}' )
if not launch_config_file or not os.path.exists( launch_config_file ):
continue
with open( launch_config_file, 'r' ) as f:
database = json.loads( minify( f.read() ) )
adapters.update( database.get( 'adapters' ) or {} )
configurations.update( database.get( 'configurations' or {} ) )
if not configurations:
utils.UserMessage( 'Unable to find any debug configurations. '
'You need to tell vimspector how to launch your '
'application.' )
return
if 'configuration' in launch_variables:
configuration_name = launch_variables.pop( 'configuration' )
elif force_choose:
# Always display the menu
configuration_name = utils.SelectFromList(
'Which launch configuration?',
sorted( configurations.keys() ) )
elif ( len( configurations ) == 1 and
next( iter( configurations.values() ) ).get( "autoselect", True ) ):
configuration_name = next( iter( configurations.keys() ) )
@ -288,7 +270,6 @@ class DebugSession( object ):
def start():
self._configuration = configuration
self._adapter = adapter
self._launch_config = None
self._logger.info( 'Configuration: %s',
json.dumps( self._configuration ) )
@ -300,7 +281,6 @@ class DebugSession( object ):
else:
vim.current.tabpage = self._uiTab
self._Prepare()
self._StartDebugAdapter()
self._Initialise()
@ -326,7 +306,7 @@ class DebugSession( object ):
if self._connection:
self._logger.debug( "_StopDebugAdapter with callback: start" )
self._StopDebugAdapter( interactive = False, callback = start )
self._StopDebugAdapter( start )
return
start()
@ -352,15 +332,12 @@ class DebugSession( object ):
return wrapper
return decorator
def _HasUI( self ):
return self._uiTab and self._uiTab.valid
def RequiresUI( otherwise=None ):
"""Decorator, call fct if self._connected else echo warning"""
def decorator( fct ):
@functools.wraps( fct )
def wrapper( self, *args, **kwargs ):
if not self._HasUI():
if not self._uiTab or not self._uiTab.valid:
utils.UserMessage(
'Vimspector is not active',
persist=False,
@ -379,6 +356,7 @@ class DebugSession( object ):
def OnServerStderr( self, data ):
self._logger.info( "Server stderr: %s", data )
if self._outputView:
self._outputView.Print( 'server', data )
@ -391,15 +369,14 @@ class DebugSession( object ):
self._connection = None
@IfConnected()
def Stop( self, interactive = False ):
def Stop( self ):
self._logger.debug( "Stop debug adapter with no callback" )
self._StopDebugAdapter( interactive = interactive )
self._StopDebugAdapter()
def Reset( self, interactive = False ):
def Reset( self ):
if self._connection:
self._logger.debug( "Stop debug adapter with callback : self._Reset()" )
self._StopDebugAdapter( interactive = interactive,
callback = lambda: self._Reset() )
self._StopDebugAdapter( lambda: self._Reset() )
else:
self._Reset()
@ -419,12 +396,10 @@ class DebugSession( object ):
self._outputView.Reset()
self._codeView.Reset()
vim.command( 'tabclose!' )
vim.command( 'doautocmd <nomodeline> User VimspectorDebugEnded' )
self._stackTraceView = None
self._variablesView = None
self._outputView = None
self._codeView = None
self._remote_term = None
self._uiTab = None
# make sure that we're displaying signs in any still-open buffers
@ -442,98 +417,43 @@ class DebugSession( object ):
},
} )
self._stackTraceView.OnContinued()
self._codeView.SetCurrentFrame( None )
@IfConnected()
def StepInto( self ):
threadId = self._stackTraceView.GetCurrentThreadId()
if threadId is None:
if self._stackTraceView.GetCurrentThreadId() is None:
return
def handler( *_ ):
self._stackTraceView.OnContinued( { 'threadId': threadId } )
self._codeView.SetCurrentFrame( None )
self._connection.DoRequest( handler, {
self._connection.DoRequest( None, {
'command': 'stepIn',
'arguments': {
'threadId': threadId
'threadId': self._stackTraceView.GetCurrentThreadId()
},
} )
@IfConnected()
def StepOut( self ):
threadId = self._stackTraceView.GetCurrentThreadId()
if threadId is None:
return
def handler( *_ ):
self._stackTraceView.OnContinued( { 'threadId': threadId } )
self._codeView.SetCurrentFrame( None )
self._connection.DoRequest( handler, {
'command': 'stepOut',
'arguments': {
'threadId': threadId
},
} )
def Continue( self ):
if not self._connection:
self.Start()
return
threadId = self._stackTraceView.GetCurrentThreadId()
if threadId is None:
utils.UserMessage( 'No current thread', persist = True )
return
def handler( msg ):
self._stackTraceView.OnContinued( {
'threadId': threadId,
'allThreadsContinued': ( msg.get( 'body' ) or {} ).get(
'allThreadsContinued',
True )
} )
self._codeView.SetCurrentFrame( None )
self._connection.DoRequest( handler, {
'command': 'continue',
'arguments': {
'threadId': threadId,
},
} )
@IfConnected()
def Pause( self ):
if self._stackTraceView.GetCurrentThreadId() is None:
utils.UserMessage( 'No current thread', persist = True )
return
self._connection.DoRequest( None, {
'command': 'pause',
'command': 'stepOut',
'arguments': {
'threadId': self._stackTraceView.GetCurrentThreadId(),
'threadId': self._stackTraceView.GetCurrentThreadId()
},
} )
@IfConnected()
def PauseContinueThread( self ):
self._stackTraceView.PauseContinueThread()
def Continue( self ):
if self._connection:
self._stackTraceView.Continue()
else:
self.Start()
@IfConnected()
def SetCurrentThread( self ):
self._stackTraceView.SetCurrentThread()
def Pause( self ):
self._stackTraceView.Pause()
@IfConnected()
def ExpandVariable( self, buf = None, line_num = None ):
self._variablesView.ExpandVariable( buf, line_num )
@IfConnected()
def SetVariableValue( self, new_value = None, buf = None, line_num = None ):
self._variablesView.SetVariableValue( new_value, buf, line_num )
def ExpandVariable( self ):
self._variablesView.ExpandVariable()
@IfConnected()
def AddWatch( self, expression ):
@ -541,22 +461,21 @@ class DebugSession( object ):
expression )
@IfConnected()
def EvaluateConsole( self, expression, verbose ):
def EvaluateConsole( self, expression ):
self._outputView.Evaluate( self._stackTraceView.GetCurrentFrame(),
expression,
verbose )
expression )
@IfConnected()
def DeleteWatch( self ):
self._variablesView.DeleteWatch()
@IfConnected()
def ShowEvalBalloon( self, winnr, expression, is_hover ):
def ShowBalloon( self, winnr, expression ):
"""Proxy: ballonexpr -> variables.ShowBallon"""
frame = self._stackTraceView.GetCurrentFrame()
# Check if RIP is in a frame
if frame is None:
self._logger.debug( 'Tooltip: Not in a stack frame' )
self._logger.debug( 'Balloon: Not in a stack frame' )
return ''
# Check if cursor in code window
@ -567,44 +486,12 @@ class DebugSession( object ):
return ''
# Return variable aware function
return self._variablesView.VariableEval( frame, expression, is_hover )
def CleanUpTooltip( self ):
return self._variablesView.CleanUpTooltip()
return self._variablesView.ShowBalloon( frame, expression )
@IfConnected()
def ExpandFrameOrThread( self ):
self._stackTraceView.ExpandFrameOrThread()
@IfConnected()
def UpFrame( self ):
self._stackTraceView.UpFrame()
@IfConnected()
def DownFrame( self ):
self._stackTraceView.DownFrame()
def ToggleLog( self ):
if self._HasUI():
return self.ShowOutput( 'Vimspector' )
if self._logView and self._logView.WindowIsValid():
self._logView.Reset()
self._logView = None
return
if self._logView:
self._logView.Reset()
# TODO: The UI code is too scattered. Re-organise into a UI class that
# just deals with these thigns like window layout and custmisattion.
vim.command( f'botright { settings.Int( "bottombar_height" ) }new' )
win = vim.current.window
self._logView = output.OutputView( win, self._api_prefix )
self._logView.AddLogFileView()
self._logView.ShowOutput( 'Vimspector' )
@RequiresUI()
def ShowOutput( self, category ):
if not self._outputView.WindowIsValid():
@ -614,7 +501,7 @@ class DebugSession( object ):
# and poking into each View class to check its window is valid also feels
# wrong.
with utils.LetCurrentTabpage( self._uiTab ):
vim.command( f'botright { settings.Int( "bottombar_height" ) }new' )
vim.command( f'botright { settings.Int( "bottombar_height", 10 ) }new' )
self._outputView.UseWindow( vim.current.window )
vim.vars[ 'vimspector_session_windows' ][ 'output' ] = utils.WindowID(
vim.current.window,
@ -643,78 +530,21 @@ class DebugSession( object ):
# TODO:
# - start / length
# - sortText
return response[ 'body' ][ 'targets' ]
@IfConnected( otherwise=[] )
def GetCommandLineCompletions( self, ArgLead, prev_non_keyword_char ):
items = []
for candidate in self.GetCompletionsSync( ArgLead, prev_non_keyword_char ):
label = candidate.get( 'text', candidate[ 'label' ] )
start = prev_non_keyword_char - 1
if 'start' in candidate and 'length' in candidate:
start = candidate[ 'start' ]
items.append( ArgLead[ 0 : start ] + label )
return items
def RefreshSigns( self, file_name ):
if self._connection:
self._codeView.Refresh( file_name )
else:
self._breakpoints.Refresh( file_name )
return [ i.get( 'text' ) or i[ 'label' ]
for i in response[ 'body' ][ 'targets' ] ]
def _SetUpUI( self ):
vim.command( 'tab split' )
self._uiTab = vim.current.tabpage
mode = settings.Get( 'ui_mode' )
if mode == 'auto':
# Go vertical if there isn't enough horizontal space for at least:
# the left bar width
# + the code min width
# + the terminal min width
# + enough space for a sign column and number column?
min_width = ( settings.Int( 'sidebar_width' )
+ 1 + 2 + 3
+ settings.Int( 'code_minwidth' )
+ 1 + settings.Int( 'terminal_minwidth' ) )
min_height = ( settings.Int( 'code_minheight' ) + 1 +
settings.Int( 'topbar_height' ) + 1 +
settings.Int( 'bottombar_height' ) + 1 +
2 )
mode = ( 'vertical'
if vim.options[ 'columns' ] < min_width
else 'horizontal' )
if vim.options[ 'lines' ] < min_height:
mode = 'horizontal'
self._logger.debug( 'min_width/height: %s/%s, actual: %s/%s - result: %s',
min_width,
min_height,
vim.options[ 'columns' ],
vim.options[ 'lines' ],
mode )
if mode == 'vertical':
self._SetUpUIVertical()
else:
self._SetUpUIHorizontal()
def _SetUpUIHorizontal( self ):
# Code window
code_window = vim.current.window
self._codeView = code.CodeView( code_window, self._api_prefix )
# Call stack
vim.command(
f'topleft vertical { settings.Int( "sidebar_width" ) }new' )
f'topleft vertical { settings.Int( "sidebar_width", 50 ) }new' )
stack_trace_window = vim.current.window
one_third = int( vim.eval( 'winheight( 0 )' ) ) / 3
self._stackTraceView = stack_trace.StackTraceView( self,
@ -740,7 +570,7 @@ class DebugSession( object ):
# Output/logging
vim.current.window = code_window
vim.command( f'rightbelow { settings.Int( "bottombar_height" ) }new' )
vim.command( f'rightbelow { settings.Int( "bottombar_height", 10 ) }new' )
output_window = vim.current.window
self._outputView = output.DAPOutputView( output_window,
self._api_prefix )
@ -748,73 +578,12 @@ class DebugSession( object ):
# TODO: If/when we support multiple sessions, we'll need some way to
# indicate which tab was created and store all the tabs
vim.vars[ 'vimspector_session_windows' ] = {
'mode': 'horizontal',
'tabpage': self._uiTab.number,
'code': utils.WindowID( code_window, self._uiTab ),
'stack_trace': utils.WindowID( stack_trace_window, self._uiTab ),
'variables': utils.WindowID( vars_window, self._uiTab ),
'watches': utils.WindowID( watch_window, self._uiTab ),
'output': utils.WindowID( output_window, self._uiTab ),
'eval': None # this is going to be updated every time eval popup is opened
}
with utils.RestoreCursorPosition():
with utils.RestoreCurrentWindow():
with utils.RestoreCurrentBuffer( vim.current.window ):
vim.command( 'doautocmd User VimspectorUICreated' )
def _SetUpUIVertical( self ):
# Code window
code_window = vim.current.window
self._codeView = code.CodeView( code_window, self._api_prefix )
# Call stack
vim.command(
f'topleft { settings.Int( "topbar_height" ) }new' )
stack_trace_window = vim.current.window
one_third = int( vim.eval( 'winwidth( 0 )' ) ) / 3
self._stackTraceView = stack_trace.StackTraceView( self,
stack_trace_window )
# Watches
vim.command( 'leftabove vertical new' )
watch_window = vim.current.window
# Variables
vim.command( 'leftabove vertical new' )
vars_window = vim.current.window
with utils.LetCurrentWindow( vars_window ):
vim.command( f'{ one_third }wincmd |' )
with utils.LetCurrentWindow( watch_window ):
vim.command( f'{ one_third }wincmd |' )
with utils.LetCurrentWindow( stack_trace_window ):
vim.command( f'{ one_third }wincmd |' )
self._variablesView = variables.VariablesView( vars_window,
watch_window )
# Output/logging
vim.current.window = code_window
vim.command( f'rightbelow { settings.Int( "bottombar_height" ) }new' )
output_window = vim.current.window
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
vim.vars[ 'vimspector_session_windows' ] = {
'mode': 'vertical',
'tabpage': self._uiTab.number,
'code': utils.WindowID( code_window, self._uiTab ),
'stack_trace': utils.WindowID( stack_trace_window, self._uiTab ),
'variables': utils.WindowID( vars_window, self._uiTab ),
'watches': utils.WindowID( watch_window, self._uiTab ),
'output': utils.WindowID( output_window, self._uiTab ),
'eval': None # this is going to be updated every time eval popup is opened
}
with utils.RestoreCursorPosition():
with utils.RestoreCurrentWindow():
@ -827,7 +596,7 @@ class DebugSession( object ):
self.SetCurrentFrame( None )
@RequiresUI()
def SetCurrentFrame( self, frame, reason = '' ):
def SetCurrentFrame( self, frame ):
if not frame:
self._stackTraceView.Clear()
self._variablesView.Clear()
@ -835,16 +604,11 @@ class DebugSession( object ):
if not self._codeView.SetCurrentFrame( frame ):
return False
# the codeView.SetCurrentFrame already checked the frame was valid and
# countained a valid source
self._variablesView.SetSyntax( self._codeView.current_syntax )
self._stackTraceView.SetSyntax( self._codeView.current_syntax )
self._variablesView.LoadScopes( frame )
self._variablesView.EvaluateWatches()
if reason == 'stopped':
self._breakpoints.ClearTemporaryBreakpoint( frame[ 'source' ][ 'path' ],
frame[ 'line' ] )
if frame:
self._variablesView.SetSyntax( self._codeView.current_syntax )
self._stackTraceView.SetSyntax( self._codeView.current_syntax )
self._variablesView.LoadScopes( frame )
self._variablesView.EvaluateWatches()
return True
@ -863,6 +627,7 @@ class DebugSession( object ):
json.dumps( self._adapter ) )
self._init_complete = False
self._on_init_complete_handlers = []
self._launch_complete = False
self._run_on_server_exit = None
@ -878,9 +643,12 @@ class DebugSession( object ):
self._adapter[ 'port' ] = port
self._connection_type = self._api_prefix + self._connection_type
self._logger.debug( f"Connection Type: { self._connection_type }" )
self._adapter[ 'env' ] = self._adapter.get( 'env', {} )
# TODO: Do we actually need to copy and update or does Vim do that?
env = os.environ.copy()
if 'env' in self._adapter:
env.update( self._adapter[ 'env' ] )
self._adapter[ 'env' ] = env
if 'cwd' not in self._adapter:
self._adapter[ 'cwd' ] = os.getcwd()
@ -894,93 +662,56 @@ class DebugSession( object ):
self._splash_screen,
"Unable to start adapter" )
else:
if 'custom_handler' in self._adapter:
spec = self._adapter[ 'custom_handler' ]
if isinstance( spec, dict ):
module = spec[ 'module' ]
cls = spec[ 'class' ]
else:
module, cls = spec.rsplit( '.', 1 )
CustomHandler = getattr( importlib.import_module( module ), cls )
handlers = [ CustomHandler( self ), self ]
else:
handlers = [ self ]
self._connection = debug_adapter_connection.DebugAdapterConnection(
handlers,
self,
lambda msg: utils.Call(
"vimspector#internal#{}#Send".format( self._connection_type ),
msg ) )
self._logger.info( 'Debug Adapter Started' )
def _StopDebugAdapter( self, interactive = False, callback = None ):
def _StopDebugAdapter( self, callback = None ):
self._splash_screen = utils.DisplaySplash(
self._api_prefix,
self._splash_screen,
"Shutting down debug adapter..." )
def handler( *args ):
self._splash_screen = utils.HideSplash( self._api_prefix,
self._splash_screen )
if callback:
self._logger.debug( "Setting server exit handler before disconnect" )
assert not self._run_on_server_exit
self._run_on_server_exit = callback
vim.eval( 'vimspector#internal#{}#StopDebugSession()'.format(
self._connection_type ) )
arguments = {}
if self._server_capabilities.get( 'supportTerminateDebuggee' ):
# If we attached, we should _not_ terminate the debuggee
arguments[ 'terminateDebuggee' ] = False
def disconnect():
self._splash_screen = utils.DisplaySplash(
self._api_prefix,
self._splash_screen,
"Shutting down debug adapter..." )
self._connection.DoRequest( handler, {
'command': 'disconnect',
'arguments': arguments,
}, failure_handler = handler, timeout = 5000 )
def handler( *args ):
self._splash_screen = utils.HideSplash( self._api_prefix,
self._splash_screen )
if callback:
self._logger.debug( "Setting server exit handler before disconnect" )
assert not self._run_on_server_exit
self._run_on_server_exit = callback
vim.eval( 'vimspector#internal#{}#StopDebugSession()'.format(
self._connection_type ) )
self._connection.DoRequest( handler, {
'command': 'disconnect',
'arguments': arguments,
}, failure_handler = handler, timeout = 5000 )
if not interactive:
disconnect()
elif not self._server_capabilities.get( 'supportTerminateDebuggee' ):
disconnect()
elif not self._stackTraceView.AnyThreadsRunning():
disconnect()
else:
def handle_choice( choice ):
if choice == 1:
# yes
arguments[ 'terminateDebuggee' ] = True
elif choice == 2:
# no
arguments[ 'terminateDebuggee' ] = False
elif choice <= 0:
# Abort
return
# Else, use server default
disconnect()
utils.Confirm( self._api_prefix,
"Terminate debuggee?",
handle_choice,
default_value = 3,
options = [ '(Y)es', '(N)o', '(D)efault' ],
keys = [ 'y', 'n', 'd' ] )
# TODO: Use the 'tarminate' request if supportsTerminateRequest set
def _PrepareAttach( self, adapter_config, launch_config ):
attach_config = adapter_config.get( 'attach' )
atttach_config = adapter_config.get( 'attach' )
if not attach_config:
if not atttach_config:
return
if 'remote' in attach_config:
if 'remote' in atttach_config:
# FIXME: We almost want this to feed-back variables to be expanded later,
# e.g. expand variables when we use them, not all at once. This would
# remove the whole %PID% hack.
remote = attach_config[ 'remote' ]
remote = atttach_config[ 'remote' ]
remote_exec_cmd = self._GetRemoteExecCommand( remote )
# FIXME: Why does this not use self._GetCommands ?
@ -1006,38 +737,28 @@ class DebugSession( object ):
commands = self._GetCommands( remote, 'attach' )
for command in commands:
cmd = remote_exec_cmd + command
cmd = remote_exec_cmd + command[ : ]
for index, item in enumerate( cmd ):
cmd[ index ] = item.replace( '%PID%', pid )
self._logger.debug( 'Running remote app: %s', cmd )
self._remote_term = terminal.LaunchTerminal(
self._api_prefix,
{
'args': cmd,
'cwd': os.getcwd()
},
self._codeView._window,
self._remote_term )
self._outputView.RunJobWithOutput( 'Remote', cmd )
else:
if attach_config[ 'pidSelect' ] == 'ask':
prop = attach_config[ 'pidProperty' ]
if atttach_config[ 'pidSelect' ] == 'ask':
prop = atttach_config[ 'pidProperty' ]
if prop not in launch_config:
pid = utils.AskForInput( 'Enter PID to attach to: ' )
if pid is None:
return
launch_config[ prop ] = pid
return
elif attach_config[ 'pidSelect' ] == 'none':
elif atttach_config[ 'pidSelect' ] == 'none':
return
raise ValueError( 'Unrecognised pidSelect {0}'.format(
attach_config[ 'pidSelect' ] ) )
atttach_config[ 'pidSelect' ] ) )
if 'delay' in attach_config:
utils.UserMessage( f"Waiting ( { attach_config[ 'delay' ] } )..." )
vim.command( f'sleep { attach_config[ "delay" ] }' )
def _PrepareLaunch( self, command_line, adapter_config, launch_config ):
@ -1061,19 +782,8 @@ class DebugSession( object ):
full_cmd.append( item.replace( '%CMD%', command_line ) )
self._logger.debug( 'Running remote app: %s', full_cmd )
self._remote_term = terminal.LaunchTerminal(
self._api_prefix,
{
'args': full_cmd,
'cwd': os.getcwd()
},
self._codeView._window,
self._remote_term )
if 'delay' in run_config:
utils.UserMessage( f"Waiting ( {run_config[ 'delay' ]} )..." )
vim.command( f'sleep { run_config[ "delay" ] }' )
self._outputView.RunJobWithOutput( 'Remote{}'.format( index ),
full_cmd )
def _GetSSHCommand( self, remote ):
@ -1148,7 +858,6 @@ class DebugSession( object ):
def handle_initialize_response( msg ):
self._server_capabilities = msg.get( 'body' ) or {}
self._breakpoints.SetServerCapabilities( self._server_capabilities )
self._variablesView.SetServerCapabilities( self._server_capabilities )
self._Launch()
self._connection.DoRequest( handle_initialize_response, {
@ -1174,63 +883,47 @@ class DebugSession( object ):
message )
self._outputView.Print( 'server', msg )
def _Prepare( self ):
self._on_init_complete_handlers = []
def _Launch( self ):
self._logger.debug( "LAUNCH!" )
self._launch_config = {}
self._launch_config.update( self._adapter.get( 'configuration', {} ) )
self._launch_config.update( self._configuration[ 'configuration' ] )
adapter_config = self._adapter
launch_config = {}
launch_config.update( self._adapter.get( 'configuration', {} ) )
launch_config.update( self._configuration[ 'configuration' ] )
request = self._configuration.get(
'remote-request',
self._launch_config.get( 'request', 'launch' ) )
launch_config.get( 'request', 'launch' ) )
if request == "attach":
self._splash_screen = utils.DisplaySplash(
self._api_prefix,
self._splash_screen,
"Attaching to debuggee..." )
"Attaching to debugee..." )
self._PrepareAttach( self._adapter, self._launch_config )
self._PrepareAttach( adapter_config, launch_config )
elif request == "launch":
self._splash_screen = utils.DisplaySplash(
self._api_prefix,
self._splash_screen,
"Launching debuggee..." )
"Launching debugee..." )
# FIXME: This cmdLine hack is not fun.
self._PrepareLaunch( self._configuration.get( 'remote-cmdLine', [] ),
self._adapter,
self._launch_config )
adapter_config,
launch_config )
# FIXME: name is mandatory. Forcefully add it (we should really use the
# _actual_ name, but that isn't actually remembered at this point)
if 'name' not in self._launch_config:
self._launch_config[ 'name' ] = 'test'
def _Launch( self ):
def failure_handler( reason, msg ):
text = [
'Launch Failed',
'',
reason,
'',
'Use :VimspectorReset to close'
]
self._splash_screen = utils.DisplaySplash( self._api_prefix,
self._splash_screen,
text )
if 'name' not in launch_config:
launch_config[ 'name' ] = 'test'
self._connection.DoRequest(
lambda msg: self._OnLaunchComplete(),
{
'command': self._launch_config[ 'request' ],
'arguments': self._launch_config
},
failure_handler )
'command': launch_config[ 'request' ],
'arguments': launch_config
}
)
def _OnLaunchComplete( self ):
@ -1270,37 +963,6 @@ class DebugSession( object ):
self._stackTraceView.LoadThreads( True )
@IfConnected()
@RequiresUI()
def PrintDebugInfo( self ):
def Line():
return ( "--------------------------------------------------------------"
"------------------" )
def Pretty( obj ):
if obj is None:
return [ "None" ]
return [ Line() ] + json.dumps( obj, indent=2 ).splitlines() + [ Line() ]
debugInfo = [
"Vimspector Debug Info",
Line(),
f"ConnectionType: { self._connection_type }",
"Adapter: " ] + Pretty( self._adapter ) + [
"Configuration: " ] + Pretty( self._configuration ) + [
f"API Prefix: { self._api_prefix }",
f"Launch/Init: { self._launch_complete } / { self._init_complete }",
f"Workspace Root: { self._workspace_root }",
"Launch Config: " ] + Pretty( self._launch_config ) + [
"Server Capabilities: " ] + Pretty( self._server_capabilities ) + [
]
self._outputView.ClearCategory( 'DebugInfo' )
self._outputView.Print( "DebugInfo", debugInfo )
self.ShowOutput( "DebugInfo" )
def OnEvent_loadedSource( self, msg ):
pass
@ -1337,9 +999,7 @@ class DebugSession( object ):
if reason == 'changed':
self._codeView.UpdateBreakpoint( bp )
elif reason == 'new':
self._codeView.AddBreakpoint( bp )
elif reason == 'removed':
self._codeView.RemoveBreakpoint( bp )
self._codeView.AddBreakpoints( None, bp )
else:
utils.UserMessage(
'Unrecognised breakpoint event (undocumented): {0}'.format( reason ),
@ -1363,33 +1023,19 @@ class DebugSession( object ):
self._connection.DoResponse( message, None, response )
def OnEvent_terminated( self, message ):
# The debugging _session_ has terminated. This does not mean that the
# debuggee has terminated (that's the exited event).
#
# We will handle this when the server actually exists.
#
# FIXME we should always wait for this event before disconnecting closing
# any socket connection
self.SetCurrentFrame( None )
def OnEvent_exited( self, message ):
utils.UserMessage( 'The debuggee exited with status code: {}'.format(
utils.UserMessage( 'The debugee exited with status code: {}'.format(
message[ 'body' ][ 'exitCode' ] ) )
self._stackTraceView.OnExited( message )
self._codeView.SetCurrentFrame( None )
def OnEvent_process( self, message ):
utils.UserMessage( 'The debuggee was started: {}'.format(
utils.UserMessage( 'The debugee was started: {}'.format(
message[ 'body' ][ 'name' ] ) )
def OnEvent_module( self, message ):
pass
def OnEvent_continued( self, message ):
self._stackTraceView.OnContinued( message[ 'body' ] )
self._codeView.SetCurrentFrame( None )
pass
def Clear( self ):
self._codeView.Clear()
@ -1421,6 +1067,10 @@ class DebugSession( object ):
else:
self._logger.debug( "No server exit handler" )
def OnEvent_terminated( self, message ):
# We will handle this when the server actually exists
utils.UserMessage( "Debugging was terminated by the server." )
def OnEvent_output( self, message ):
if self._outputView:
self._outputView.OnOutput( message[ 'body' ] )
@ -1461,26 +1111,6 @@ class DebugSession( object ):
def ToggleBreakpoint( self, options ):
return self._breakpoints.ToggleBreakpoint( options )
def RunTo( self, file_name, line ):
self.ClearTemporaryBreakpoints()
self.SetLineBreakpoint( file_name,
line,
{ 'temporary': True },
lambda: self.Continue() )
def ClearTemporaryBreakpoints( self ):
return self._breakpoints.ClearTemporaryBreakpoints()
def SetLineBreakpoint( self, file_name, line_num, options, then = None ):
return self._breakpoints.SetLineBreakpoint( file_name,
line_num,
options,
then )
def ClearLineBreakpoint( self, file_name, line_num ):
return self._breakpoints.ClearLineBreakpoint( file_name, line_num )
def ClearBreakpoints( self ):
if self._connection:
self._codeView.ClearBreakpoints()

View file

@ -21,7 +21,7 @@ import os
GADGETS = {
'vscode-cpptools': {
'language': [ 'c', 'cpp', 'rust' ],
'language': 'c',
'download': {
'url': 'https://github.com/Microsoft/vscode-cpptools/releases/download/'
'${version}/${file_name}',
@ -30,12 +30,12 @@ GADGETS = {
root,
gadget ),
'all': {
'version': '1.6.0',
'version': '0.27.0',
"adapters": {
"vscode-cpptools": {
"name": "cppdbg",
"command": [
"${gadgetDir}/vscode-cpptools/debugAdapters/bin/OpenDebugAD7"
"${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7"
],
"attach": {
"pidProperty": "processId",
@ -53,17 +53,17 @@ GADGETS = {
'linux': {
'file_name': 'cpptools-linux.vsix',
'checksum':
'c25299bcfb46b22d41aa3f125df7184e6282a35ff9fb69c47def744cb4778f55',
'3695202e1e75a03de18049323b66d868165123f26151f8c974a480eaf0205435',
},
'macos': {
'file_name': 'cpptools-osx-arm64.vsix',
'file_name': 'cpptools-osx.vsix',
'checksum':
'ceb3e8cdaa2b5bb45af50913ddd8402089969748af8d70f5d46480408287ba6f',
'cb061e3acd7559a539e5586f8d3f535101c4ec4e8a48195856d1d39380b5cf3c',
},
'windows': {
'file_name': 'cpptools-win32.vsix',
'checksum':
'ef7ac5831874a3c7dbf0feb826bfda2be579aff9b6d990622fff1d0d4ede00d1',
'aa294368ed16d48c59e49c8000e146eae5a19ad07b654efed5db8ec93b24229e',
"adapters": {
"vscode-cpptools": {
"name": "cppdbg",
@ -86,16 +86,39 @@ GADGETS = {
},
},
},
'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.2.1',
'file_name': 'v1.2.1.zip',
'version': '1.0.0b12',
'file_name': 'v1.0.0b12.zip',
'checksum':
'29a6c5d1053d2b6f3b1a63e1a8ecff93f951d3cc0b7548431592e9e3007239e6'
'210632bba2221fbb841c9785a615258819ceec401d1abdbeb5f2326f12cc72a1'
},
'do': lambda name, root, gadget: installer.InstallDebugpy( name,
root,
@ -134,10 +157,6 @@ GADGETS = {
"vscode-java": {
"name": "vscode-java",
"port": "${DAPPort}",
"configuration": {
"cwd": "${workspaceRoot}"
},
'custom_handler': 'vimspector.custom.java.JavaDebugAdapter'
}
},
},
@ -174,7 +193,7 @@ GADGETS = {
'language': 'tcl',
'repo': {
'url': 'https://github.com/puremourning/TclProDebug',
'ref': 'v1.0.0'
'ref': 'master'
},
'do': lambda name, root, gadget: installer.InstallTclProDebug( name,
root,
@ -203,27 +222,26 @@ GADGETS = {
},
},
'netcoredbg': {
'language': [ 'csharp', 'fsharp', 'vbnet' ],
'language': 'csharp',
'enabled': False,
'download': {
'url': ( 'https://github.com/Samsung/netcoredbg/releases/download/'
'${version}/${file_name}' ),
'url': 'https://github.com/Samsung/netcoredbg/releases/download/latest/'
'${file_name}',
'format': 'tar',
},
'all': {
'version': '1.2.0-782'
'version': 'master'
},
'macos': {
'file_name': 'netcoredbg-osx.tar.gz',
'checksum':
'',
'file_name': 'netcoredbg-osx-master.tar.gz',
'checksum': '',
},
'linux': {
'file_name': 'netcoredbg-linux-bionic-amd64.tar.gz',
'file_name': 'netcoredbg-linux-master.tar.gz',
'checksum': '',
},
'windows': {
'file_name': 'netcoredbg-win64.zip',
'file_name': 'netcoredbg-win64-master.zip',
'checksum': '',
},
'do': lambda name, root, gadget: installer.MakeSymlink(
@ -240,9 +258,35 @@ GADGETS = {
"pidProperty": "processId",
"pidSelect": "ask"
},
"configuration": {
"cwd": "${workspaceRoot}"
}
},
}
},
'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"
},
},
}
},
@ -292,25 +336,22 @@ GADGETS = {
'vscode-go': {
'language': 'go',
'download': {
'url': 'https://github.com/golang/vscode-go/releases/download/'
'v${version}/${file_name}'
'url': 'https://github.com/microsoft/vscode-go/releases/download/'
'${version}/${file_name}'
},
'all': {
'version': '0.19.1',
'file_name': 'go-0.19.1.vsix',
'version': '0.11.4',
'file_name': 'Go-0.11.4.vsix',
'checksum':
'7f9dc014245b030d9f562b28f3ea9b1fd6e2708fac996c53ff6a707f8204ec64',
'ff7d7b944da5448974cb3a0086f4a2fd48e2086742d9c013d6964283d416027e'
},
'adapters': {
'vscode-go': {
'name': 'delve',
'command': [
'node',
'${gadgetDir}/vscode-go/dist/debugAdapter.js'
'${gadgetDir}/vscode-go/out/src/debugAdapter/goDebug.js'
],
"configuration": {
"cwd": "${workspaceRoot}",
}
},
},
},
@ -319,14 +360,14 @@ GADGETS = {
'enabled': False,
'download': {
'url':
'https://github.com/xdebug/vscode-php-debug/releases/download/'
'https://github.com/felixfbecker/vscode-php-debug/releases/download/'
'${version}/${file_name}',
},
'all': {
'version': 'v1.17.0',
'file_name': 'php-debug-1.17.0.vsix',
'version': 'v1.13.0',
'file_name': 'php-debug.vsix',
'checksum':
'd0fff272503414b6696cc737bc2e18e060fdd5e5dc4bcaf38ae7373afd8d8bc9',
'8a51e593458fd14623c1c89ebab87347b087d67087717f18bcf77bb788052718',
},
'adapters': {
'vscode-php-debug': {
@ -366,14 +407,14 @@ GADGETS = {
'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',
'target': 'msjsdiag.debugger-for-chrome-4.12.0.vsix.gz',
'format': 'zip.gz',
},
'all': {
'version': '4.12.10',
'file_name': 'msjsdiag.debugger-for-chrome-4.12.10.vsix',
'version': '4.12.0',
'file_name': 'msjsdiag.debugger-for-chrome-4.12.0.vsix',
'checksum':
''
'0df2fe96d059a002ebb0936b0003e6569e5a5c35260dc3791e1657d27d82ccf5'
},
'adapters': {
'chrome': {
@ -388,18 +429,18 @@ GADGETS = {
},
'CodeLLDB': {
'language': 'rust',
'enabled': True,
'enabled': False,
'download': {
'url': 'https://github.com/vadimcn/vscode-lldb/releases/download/'
'${version}/${file_name}',
},
'all': {
'version': 'v1.6.6',
'version': 'v1.5.3',
},
'macos': {
'file_name': 'codelldb-aarch64-darwin.vsix',
'file_name': 'codelldb-x86_64-darwin.vsix',
'checksum':
'5adc3b9139eabdafd825bd5efc55df4424a203fb2b6087b425cd434956e7ec58',
'7505bc1cdfcfd1cb981e2996aec62d63577440709bac31dcadb41a3b4b44631a',
'make_executable': [
'adapter/codelldb',
'lldb/bin/debugserver',
@ -410,7 +451,7 @@ GADGETS = {
'linux': {
'file_name': 'codelldb-x86_64-linux.vsix',
'checksum':
'eda2cd9b3089dcc0524c273e91ffb5875fe08c930bf643739a2cd1846e1f98d6',
'ce7efc3e94d775368e5942a02bf5c326b6809a0b4c389f79ffa6a8f6f6b72139',
'make_executable': [
'adapter/codelldb',
'lldb/bin/lldb',
@ -421,7 +462,7 @@ GADGETS = {
'windows': {
'file_name': 'codelldb-x86_64-windows.vsix',
'checksum':
'8ddebe8381a3d22dc3d95139c3797fda06b5cc34aadf300e13b1c516b9da95fe',
'',
'make_executable': []
},
'adapters': {
@ -445,31 +486,4 @@ GADGETS = {
},
},
},
'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

@ -57,64 +57,24 @@ def Print( *args, **kwargs ):
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 ] )
def CheckCall( *args, **kwargs ):
if options.quiet:
try:
subprocess.check_output( cmd, *args, stderr=subprocess.STDOUT, **kwargs )
subprocess.check_output( *args, stderr=subprocess.STDOUT, **kwargs )
except subprocess.CalledProcessError as e:
print( e.output.decode( 'utf-8' ) )
raise
else:
subprocess.check_call( cmd, *args, **kwargs )
subprocess.check_call( *args, **kwargs )
def PathToAnyWorkingPython3():
# We can't rely on sys.executable because it's usually 'vim' (fixme, not with
# neovim?)
paths = GetPATHAsList()
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:
@ -123,10 +83,14 @@ def PathToAnyWorkingPython3():
'python' ]
for candidate in candidates:
try:
return FindExecutable( candidate, paths=paths )
except MissingExecutable:
pass
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" )
@ -150,7 +114,7 @@ def RunInstaller( api_prefix, leave_open, *args, **kwargs ):
_ResetInstaller()
with utils.RestoreCurrentWindow():
vim.command( f'botright { settings.Int( "bottombar_height" ) }new' )
vim.command( f'botright { settings.Int( "bottombar_height", 10 ) }new' )
win = vim.current.window
OUTPUT_VIEW = output.OutputView( win, api_prefix )
@ -229,14 +193,10 @@ def GadgetListToInstallerArgs( *gadget_list ):
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}' )
installer_args.append( f'--force-enable-{ gadget[ "language" ] }' )
else:
installer_args.append( f'--enable-{lang}' )
installer_args.append( f'--enable-{ gadget[ "language" ] }' )
return installer_args
@ -344,12 +304,11 @@ def WriteAdapters( all_adapters, to_file=None ):
def InstallGeneric( name, root, gadget ):
extension_path = gadget.get( 'extension_path', 'extension' )
extension = os.path.join( root, extension_path )
extension = os.path.join( root, 'extension' )
for f in gadget.get( 'make_executable', [] ):
MakeExecutable( os.path.join( extension, f ) )
MakeExtensionSymlink( name, root, extension_path )
MakeExtensionSymlink( name, root )
def InstallCppTools( name, root, gadget ):
@ -358,8 +317,7 @@ def InstallCppTools( name, root, gadget ):
# 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' ) )
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' ]
@ -390,7 +348,7 @@ def InstallDebugpy( name, root, gadget ):
def InstallTclProDebug( name, root, gadget ):
configure = [ 'sh', './configure' ]
configure = [ './configure' ]
if install.GetOS() == 'macos':
# Apple removed the headers from system frameworks because they are
@ -431,13 +389,6 @@ def InstallNodeDebug( name, root, gadget ):
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,
@ -494,7 +445,7 @@ def InstallGagdet( name: str,
url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( spec )
ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( spec )
print( f"Installing {name}@{ref}..." )
print( f"Installing {name}@{gadget[ 'repo' ][ 'ref' ]}..." )
spec[ 'repo' ] = gadget[ 'repo' ]
if not manifest.RequiresUpdate( name, spec ):
save_adapters()
@ -705,8 +656,8 @@ def ExtractZipTo( file_path, destination, format ):
CheckCall( [ 'tar', 'zxvf', file_path ] )
def MakeExtensionSymlink( name, root, extension_path = 'extension' ):
MakeSymlink( name, os.path.join( root, extension_path ) ),
def MakeExtensionSymlink( name, root ):
MakeSymlink( name, os.path.join( root, 'extension' ) ),
def MakeSymlink( link, pointing_to, in_folder = None ):

View file

@ -17,7 +17,6 @@ from vimspector import utils, install
import vim
import json
import typing
class TabBuffer( object ):
@ -32,9 +31,8 @@ class TabBuffer( object ):
BUFFER_MAP = {
'console': 'Console',
'stdout': 'Console',
'output': 'Console',
'stderr': 'stderr',
'telemetry': None,
'telemetry': 'Telemetry',
}
@ -57,19 +55,14 @@ def ShowOutputInWindow( win_id, category ):
class OutputView( object ):
"""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._buffers = {}
self._api_prefix = api_prefix
VIEWS.add( self )
def Print( self, category, text: typing.Union[ str, list ] ):
if not isinstance( text, list ):
text = text.splitlines()
self._Print( category, text )
def Print( self, categroy, text ):
self._Print( 'server', text.splitlines() )
def OnOutput( self, event ):
category = CategoryToBuffer( event.get( 'category' ) or 'output' )
@ -81,10 +74,6 @@ 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 )
@ -108,26 +97,13 @@ class OutputView( object ):
def Clear( self ):
for category, tab_buffer in self._buffers.items():
self._CleanUpBuffer( category, tab_buffer )
if tab_buffer.is_job:
utils.CleanUpCommand( category, self._api_prefix )
utils.CleanUpHiddenBuffer( tab_buffer.buf )
# 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
@ -170,17 +146,6 @@ class OutputView( object ):
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":
@ -195,7 +160,6 @@ class OutputView( object ):
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 )
@ -206,15 +170,13 @@ class OutputView( object ):
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' )
'vimspector#EvaluateConsole' )
else:
utils.SetUpHiddenBuffer( tab_buffer.buf, name )
@ -225,15 +187,8 @@ class OutputView( object ):
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
@ -251,7 +206,7 @@ class OutputView( object ):
raise
vim.command(
"nnoremenu <silent> 1.{0} WinBar.{1}{2} "
"nnoremenu 1.{0} WinBar.{1}{2} "
":call vimspector#ShowOutputInWindow( {3}, '{1}' )<CR>".format(
tab_buffer.index,
utils.Escape( category ),
@ -261,9 +216,6 @@ class OutputView( object ):
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)"""
@ -272,10 +224,9 @@ class DAPOutputView( OutputView ):
self._connection = None
for b in set( BUFFER_MAP.values() ):
if b is not None:
self._CreateBuffer( b )
self._CreateBuffer( b )
self.AddLogFileView()
self._CreateBuffer( 'Vimspector', file_name = utils.LOG_FILE )
self._ShowOutput( 'Console' )
def ConnectionUp( self, connection ):
@ -285,18 +236,14 @@ class DAPOutputView( OutputView ):
# Don't clear because output is probably still useful
self._connection = None
def Evaluate( self, frame, expression, verbose ):
if verbose:
self._Print( 'Console', f"Evaluating: { expression }" )
def Evaluate( self, frame, expression ):
self._Print( 'Console', [ 'Evaluating: ' + expression ] )
def print_result( message ):
result = message[ 'body' ][ 'result' ]
if result is None:
result = '<no result>'
self._Print( 'Console', result.splitlines() )
def print_failure( reason, msg ):
self._Print( 'Console', reason.splitlines() )
self._Print( 'Console', f' Result: { result }' )
request = {
'command': 'evaluate',
@ -309,6 +256,4 @@ class DAPOutputView( OutputView ):
if frame:
request[ 'arguments' ][ 'frameId' ] = frame[ 'id' ]
self._connection.DoRequest( print_result,
request,
print_failure )
self._connection.DoRequest( print_result, request )

View file

@ -18,121 +18,16 @@ 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() ) ) )
default ) )
def Int( option: str ):
return Get( option, cls=builtins.int )
def Int( option: str, default=0 ):
return Get( option, default=default, 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
def List( option: str, default=[] ):
return utils.GetVimList( vim.vars, f'vimspector_{ option }', default )

View file

@ -1,46 +0,0 @@
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,75 +16,11 @@
import vim
import os
import logging
import typing
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
from vimspector import utils
class StackTraceView( object ):
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 )
@ -101,55 +37,22 @@ class StackTraceView( object ):
self._sources = {}
self._scratch_buffers = []
# 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' )
vim.command( 'nnoremap <buffer> <CR> :call vimspector#GoToFrame()<CR>' )
self._line_to_frame = {}
self._line_to_thread = {}
self._requesting_threads = StackTraceView.ThreadRequestState.NO
self._pending_thread_request = None
# 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
def GetCurrentThreadId( self ):
@ -162,22 +65,14 @@ class StackTraceView( object ):
self._current_frame = None
self._current_thread = None
self._current_syntax = ""
self._threads.clear()
self._threads = []
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()
@ -188,95 +83,47 @@ class StackTraceView( object ):
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,
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 )
def LoadThreads( self, infer_current_frame ):
pending_request = False
if self._requesting_threads:
pending_request = True
return
def consume_threads( message ):
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
self._requesting_threads = False
self._requesting_threads = StackTraceView.ThreadRequestState.NO
self._pending_thread_request = None
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 )
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()
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 )
for thread in message[ 'body' ][ 'threads' ]:
self._threads.append( thread )
# 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 )
if infer_current_frame and thread[ 'id' ] == self._current_thread:
self._LoadStackTrace( thread, True )
elif infer_current_frame and self._current_thread is None:
self._current_thread = thread[ 'id' ]
self._LoadStackTrace( thread, True )
# If 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
if not requesting:
self._DrawThreads()
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 = False
self._requesting_threads = StackTraceView.ThreadRequestState.REQUESTING
self._requesting_threads = True
self._connection.DoRequest( consume_threads, {
'command': 'threads',
}, failure_handler )
@ -285,42 +132,26 @@ class StackTraceView( object ):
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 ):
with utils.RestoreCursorPosition():
utils.ClearBuffer( self._buf )
utils.ClearBuffer( self._buf )
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()})' )
for thread in self._threads:
icon = '+' if '_frames' not in thread else '-'
if self._current_thread == thread.id:
signs.PlaceSign( self._current_thread_sign_id,
'VimspectorStackTrace',
'vimspectorCurrentThread',
self._buf.name,
line )
line = utils.AppendToBuffer(
self._buf,
'{0} Thread: {1}'.format( icon, thread[ 'name' ] ) )
self._line_to_thread[ line ] = thread
self._DrawStackTrace( thread )
self._line_to_thread[ line ] = thread
def _LoadStackTrace( self,
thread: Thread,
infer_current_frame,
reason = '' ):
self._DrawStackTrace( thread )
def _LoadStackTrace( self, thread, infer_current_frame ):
def consume_stacktrace( message ):
thread.Expand( message[ 'body' ][ 'stackFrames' ] )
thread[ '_frames' ] = message[ 'body' ][ 'stackFrames' ]
if infer_current_frame:
for frame in thread.stacktrace:
if self._JumpToFrame( frame, reason ):
for frame in thread[ '_frames' ]:
if self._JumpToFrame( frame ):
break
self._DrawThreads()
@ -328,113 +159,32 @@ class StackTraceView( object ):
self._connection.DoRequest( consume_stacktrace, {
'command': 'stackTrace',
'arguments': {
'threadId': thread.id,
'threadId': thread[ 'id' ],
}
} )
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" )
def ExpandFrameOrThread( self ):
thread = self._GetSelectedThread()
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:
if 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 )
current_line = vim.current.window.cursor[ 0 ]
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:
self._LoadStackTrace( thread, False )
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 _JumpToFrame( self, frame ):
def do_jump():
if 'line' in frame and frame[ 'line' ] > 0:
# Should this set the current _Thread_ too ? If i jump to a frame in
# Thread 2, should that become the focussed thread ?
self._current_frame = frame
self._DrawThreads()
return self._session.SetCurrentFrame( self._current_frame, reason )
return self._session.SetCurrentFrame( self._current_frame )
return False
source = frame.get( 'source' ) or {}
@ -449,94 +199,59 @@ 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 ):
threadId = event.get( 'threadId' )
allThreadsStopped = event.get( 'allThreadsStopped', False )
if 'threadId' in event:
self._current_thread = event[ 'threadId' ]
elif event.get( 'allThreadsStopped', False ) and self._threads:
self._current_thread = self._threads[ 0 ][ 'id' ]
# 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
if self._current_thread is not None:
for thread in self._threads:
if thread[ 'id' ] == self._current_thread:
self._LoadStackTrace( thread, True )
return
self.LoadThreads( True, 'stopped', event )
self.LoadThreads( True )
def OnThreadEvent( self, event ):
infer_current_frame = False
if event[ 'reason' ] == 'started' and self._current_thread is None:
self._current_thread = event[ 'threadId' ]
infer_current_frame = True
self.LoadThreads( 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():
def Continue( self ):
if self._current_thread is None:
utils.UserMessage( 'No current thread', persist = True )
return
if self._current_frame_sign_id:
signs.UnplaceSign( self._current_frame_sign_id, 'VimspectorStackTrace' )
else:
self._current_frame_sign_id = 2
self._session._connection.DoRequest( None, {
'command': 'continue',
'arguments': {
'threadId': self._current_thread,
},
} )
for frame in thread.stacktrace:
self._session.ClearCurrentFrame()
self.LoadThreads( True )
def Pause( self ):
if self._current_thread is None:
utils.UserMessage( 'No current thread', persist = True )
return
self._session._connection.DoRequest( None, {
'command': 'pause',
'arguments': {
'threadId': self._current_thread,
},
} )
def _DrawStackTrace( self, thread ):
if '_frames' not in thread:
return
stackFrames = thread[ '_frames' ]
for frame in stackFrames:
if frame.get( 'source' ):
source = frame[ 'source' ]
else:
@ -560,15 +275,7 @@ class StackTraceView( object ):
source[ 'name' ],
frame[ 'line' ] ) )
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 )
self._line_to_frame[ line ] = frame
def _ResolveSource( self, source, and_then ):
source_reference = int( source[ 'sourceReference' ] )

View file

@ -1,134 +0,0 @@
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

@ -45,20 +45,14 @@ _logger = logging.getLogger( __name__ )
SetUpLogging( _logger )
def BufferNumberForFile( file_name, create = True ):
return int( vim.eval( "bufnr( '{0}', {1} )".format(
Escape( file_name ),
int( create ) ) ) )
def BufferNumberForFile( file_name ):
return int( vim.eval( "bufnr( '{0}', 1 )".format( Escape( file_name ) ) ) )
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 )
@ -141,7 +135,7 @@ def SetUpHiddenBuffer( buf, name ):
buf.name = name
def SetUpPromptBuffer( buf, name, prompt, callback, omnifunc ):
def SetUpPromptBuffer( buf, name, prompt, callback ):
# This feature is _super_ new, so only enable when available
if not Exists( '*prompt_setprompt' ):
return SetUpHiddenBuffer( buf, name )
@ -154,7 +148,6 @@ def SetUpPromptBuffer( buf, name, prompt, callback, omnifunc ):
buf.options[ 'buflisted' ] = False
buf.options[ 'bufhidden' ] = 'hide'
buf.options[ 'textwidth' ] = 0
buf.options[ 'omnifunc' ] = omnifunc
buf.name = name
vim.eval( "prompt_setprompt( {0}, '{1}' )".format( buf.number,
@ -163,12 +156,6 @@ def SetUpPromptBuffer( buf, name, prompt, callback, omnifunc ):
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
@ -352,67 +339,24 @@ def SelectFromList( prompt, options ):
if selection < 0 or selection >= len( options ):
return None
return options[ selection ]
except ( KeyboardInterrupt, vim.error ):
except KeyboardInterrupt:
return None
def AskForInput( prompt, default_value = None, completion = None ):
def AskForInput( prompt, default_value = None ):
if default_value is None:
default_value = ''
args = [ prompt, default_value ]
if completion is not None:
if completion == 'expr':
args.append( 'custom,vimspector#CompleteExpr' )
else:
args.append( completion )
default_option = ''
else:
default_option = ", '{}'".format( Escape( default_value ) )
with InputSave():
try:
return Call( 'input', *args )
except ( KeyboardInterrupt, vim.error ):
return vim.eval( "input( '{}' {} )".format( Escape( prompt ),
default_option ) )
except KeyboardInterrupt:
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:
@ -441,10 +385,8 @@ def AppendToBuffer( buf, line_or_lines, modified=False ):
def ClearBuffer( buf, modified = False ):
def ClearBuffer( buf ):
buf[ : ] = None
if not modified:
buf.options[ 'modified' ] = False
def SetBufferContents( buf, lines, modified=False ):
@ -503,10 +445,10 @@ VAR_MATCH = re.compile(
(?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
(?P<defname>[_a-z][a-z0-9]*) # an ID
: # then a colon
(?P<default>(?:\\}|[^}])*) # then anything up to }, or a \}
)} | # then a }
(?P<default>(?:[^}]|\})*) # then anything up to }, or a \}
)} | #
(?P<invalid>) # or Something else - invalid
)
""",
@ -534,6 +476,7 @@ def _Substitute( template, mapping ):
if mo.group( 'braceddefault' ) is not None:
named = mo.group( 'defname' )
if named not in mapping:
''
raise MissingSubstitution(
named,
mo.group( 'default' ).replace( '\\}', '}' ) )
@ -575,11 +518,8 @@ def ExpandReferencesInString( orig_s,
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
except MissingSubstitution:
default_value = e.default_value
mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ),
default_value )
@ -681,19 +621,12 @@ def ParseVariables( variables_list,
return new_variables
def DisplayBalloon( is_term, display, is_hover = False ):
def DisplayBaloon( is_term, display ):
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 )
created_win_id = int( vim.eval(
"vimspector#internal#balloon#CreateTooltip({}, {})".format(
int( is_hover ), json.dumps( display )
)
) )
return created_win_id
vim.eval( "balloon_show( {0} )".format(
json.dumps( display ) ) )
def GetBufferFilepath( buf ):
@ -724,6 +657,13 @@ def Call( vimscript_function, *args ):
return vim.eval( call )
def SignDefined( name ):
if Exists( "*sign_getdefined" ):
return int( vim.eval( f"len( sign_getdefined( '{ Escape( name ) }' ) )" ) )
return False
MEMO = {}
@ -754,7 +694,7 @@ def SetSyntax( current_syntax, syntax, *args ):
syntax = ''
if current_syntax == syntax:
return syntax
return
# We use set syn= because just setting vim.Buffer.options[ 'syntax' ]
# doesn't actually trigger the Syntax autocommand, and i'm not sure that
@ -770,28 +710,6 @@ def GetBufferFiletypes( buf ):
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',
@ -814,7 +732,7 @@ def GetVimValue( vim_dict, name, default=None ):
# FIXME: use 'encoding' ?
try:
value = vim_dict[ name ]
except ( KeyError, vim.error ):
except KeyError:
return default
if isinstance( value, bytes ):
@ -825,7 +743,7 @@ def GetVimValue( vim_dict, name, default=None ):
def GetVimList( vim_dict, name, default=None ):
try:
value = vim_dict[ name ]
except ( KeyError, vim.error ):
except KeyError:
return default
if not isinstance( value, collections.abc.Iterable ):
@ -835,6 +753,7 @@ def GetVimList( vim_dict, name, default=None ):
return [ i.decode( 'utf-8' ) if isinstance( i, bytes ) else i for i in value ]
def GetVimspectorBase():
return GetVimValue( vim.vars,
'vimspector_base_dir',
@ -858,9 +777,3 @@ 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

@ -19,7 +19,7 @@ import logging
from functools import partial
import typing
from vimspector import utils, settings
from vimspector import utils
class Expandable:
@ -32,9 +32,8 @@ class Expandable:
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 ):
def __init__( self ):
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).
@ -49,9 +48,6 @@ class Expandable:
def IsExpandable( self ):
return self.VariablesReference() > 0
def IsContained( self ):
return self.container is not None
@abc.abstractmethod
def VariablesReference( self ):
assert False
@ -88,16 +84,10 @@ class WatchResult( Expandable ):
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 )
def __init__( self, variable: dict ):
super().__init__()
self.variable = variable
# A new variable appearing is marked as changed
self.changed = True
@ -121,48 +111,17 @@ class Watch:
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 )
self.buf = win.buffer
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>' )
utils.SetUpUIWindow( win )
class VariablesView( object ):
@ -172,22 +131,14 @@ class VariablesView( object ):
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 )
vim.command(
'nnoremap <buffer> <CR> :call vimspector#ExpandVariable()<CR>' )
# Set up the "Watches" buffer in the watches_win (and create a WinBar in
# there)
@ -196,23 +147,19 @@ class VariablesView( object ):
utils.SetUpPromptBuffer( self._watch.buf,
'vimspector.Watches',
'Expression: ',
'vimspector#AddWatchPrompt',
'vimspector#OmniFuncWatch' )
'vimspector#AddWatchPrompt' )
with utils.LetCurrentWindow( watches_win ):
AddExpandMappings( mappings )
for mapping in utils.GetVimList( mappings, 'delete' ):
vim.command(
f'nnoremap <buffer> { mapping } :call vimspector#DeleteWatch()<CR>' )
vim.command(
'nnoremap <buffer> <CR> :call vimspector#ExpandVariable()<CR>' )
vim.command(
'nnoremap <buffer> <DEL> :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>' )
vim.command( 'nnoremenu 1.1 WinBar.New '
':call vimspector#AddWatch()<CR>' )
vim.command( 'nnoremenu 1.2 WinBar.Expand/Collapse '
':call vimspector#ExpandVariable()<CR>' )
vim.command( 'nnoremenu 1.3 WinBar.Delete '
':call vimspector#DeleteWatch()<CR>' )
# Set the (global!) balloon expr if supported
has_balloon = int( vim.eval( "has( 'balloon_eval' )" ) )
@ -224,9 +171,7 @@ class VariablesView( object ):
'balloonexpr': vim.options[ 'balloonexpr' ],
'balloondelay': vim.options[ 'balloondelay' ],
}
vim.options[ 'balloonexpr' ] = ( "vimspector#internal#"
"balloon#HoverTooltip()" )
vim.options[ 'balloonexpr' ] = 'vimspector#internal#balloon#BalloonExpr()'
vim.options[ 'balloondelay' ] = 250
if has_balloon:
@ -244,30 +189,21 @@ class VariablesView( object ):
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 ):
@ -319,97 +255,15 @@ 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 ):
self._watches.append( Watch.New( frame, expression, 'watch' ) )
watch = {
'expression': expression,
'context': 'watch',
}
if frame:
watch[ 'frameId' ] = frame[ 'id' ]
self._watches.append( Watch( watch ) )
self.EvaluateWatches()
def DeleteWatch( self ):
@ -436,14 +290,11 @@ class VariablesView( object ):
def EvaluateWatches( self ):
for watch in self._watches:
self._connection.DoRequest(
partial( self._UpdateWatchExpression, watch ),
{
'command': 'evaluate',
'arguments': watch.expression,
},
failure_handler = lambda reason, msg, watch=watch:
self._WatchExpressionFailed( reason, watch ) )
self._connection.DoRequest( partial( self._UpdateWatchExpression,
watch ), {
'command': 'evaluate',
'arguments': watch.expression,
} )
def _UpdateWatchExpression( self, watch: Watch, message: dict ):
if watch.result is not None:
@ -464,43 +315,20 @@ class VariablesView( object ):
self._DrawWatches()
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:
def ExpandVariable( self ):
if vim.current.buffer == self._vars.buf:
view = self._vars
elif buf == self._watch.buf:
elif vim.current.buffer == 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 variable.IsExpanded():
# Collapse
variable.expanded = Expandable.COLLAPSED_BY_USER
@ -520,101 +348,25 @@ class VariablesView( object ):
},
} )
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 ):
def _DrawVariables( self, view, variables, indent ):
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(
line = utils.AppendToBuffer(
view.buf,
'{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', '' ),
name = variable.variable[ 'name' ],
type_ = variable.variable.get( 'type', '' ),
value = variable.variable.get( 'value', '<unknown>' )
)
line = utils.AppendToBuffer(
view.buf,
text.split( '\n' )
)
value = variable.variable.get( 'value',
'<unknown>' ) ).split( '\n' ) )
view.lines[ line ] = variable
if variable.ShouldDrawDrillDown():
self._DrawVariables( view, variable.variables, indent + 2, is_short )
self._DrawVariables( view, variable.variables, indent + 2 )
def _DrawScopes( self ):
# FIXME: The drawing is dumb and draws from scratch every time. This is
@ -641,7 +393,7 @@ class VariablesView( object ):
'Expression: '
+ watch.expression[ 'expression' ] )
watch.line = line
self._DrawWatchResult( self._watch, 2, watch )
self._DrawWatchResult( 2, watch )
def _DrawScope( self, indent, scope ):
icon = '+' if scope.IsExpandable() and not scope.IsExpanded() else '-'
@ -657,36 +409,27 @@ class VariablesView( object ):
indent += 2
self._DrawVariables( self._vars, scope.variables, indent )
def _DrawWatchResult( self, view, indent, watch, is_short = False ):
def _DrawWatchResult( self, indent, watch ):
if not watch.result:
return
assert is_short or indent > 0
assert indent > 0
icon = '+' if ( watch.result.IsExpandable() and
not watch.result.IsExpanded() ) 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: '
line = '{indent}{marker}{icon}{leader}{result}'.format(
line = '{indent}{marker}{icon} Result: {result}'.format(
# We borrow 1 space of indent to draw the change marker
indent = ' ' * ( indent - 1 ),
marker = marker,
marker = '*' if watch.result.changed else ' ',
icon = icon,
leader = leader,
result = watch.result.result.get( 'result', '<unknown>' ) )
line = utils.AppendToBuffer( view.buf, line.split( '\n' ) )
view.lines[ line ] = watch.result
line = utils.AppendToBuffer( self._watch.buf, line.split( '\n' ) )
self._watch.lines[ line ] = watch.result
if watch.result.ShouldDrawDrillDown():
self._DrawVariables( view, watch.result.variables, indent + 2, is_short )
indent = 4
self._DrawVariables( self._watch, watch.result.variables, indent )
def _ConsumeVariables( self, draw, parent, message ):
new_variables = []
@ -701,8 +444,9 @@ class VariablesView( object ):
variable = v
found = True
break
if not found:
variable = Variable( parent, variable_body )
variable = Variable( variable_body )
else:
variable.Update( variable_body )
@ -722,10 +466,47 @@ class VariablesView( object ):
draw()
def ShowBalloon( self, frame, expression ):
"""Callback to display variable under cursor `:h ballonexpr`"""
if not self._connection:
return ''
def handler( message ):
# TODO: this result count be expandable, but we have no way to allow the
# user to interact with the balloon to expand it, unless we use a popup
# instead, but even then we don't really want to trap the cursor.
body = message[ 'body' ]
result = body[ 'result' ]
if result is None:
result = 'null'
display = [
'Type: ' + body.get( 'type', '<unknown>' ),
'Value: ' + result
]
utils.DisplayBaloon( self._is_term, display )
def failure_handler( reason, message ):
display = [ reason ]
utils.DisplayBaloon( self._is_term, display )
# Send async request
self._connection.DoRequest( handler, {
'command': 'evaluate',
'arguments': {
'expression': expression,
'frameId': frame[ 'id' ],
'context': 'hover',
}
}, failure_handler )
# Return working (meanwhile)
return '...'
def SetSyntax( self, syntax ):
# TODO: Switch to View.syntax
self._current_syntax = utils.SetSyntax( self._current_syntax,
syntax,
self._vars.buf,
self._watch.buf )
# vim: sw=2

View file

@ -1,14 +1,6 @@
#!/usr/bin/env bash
SetBaseDir() {
BASEDIR=$1
if [[ ! $BASEDIR = /* ]]; then
# Relative
BASEDIR=$(pwd)/${BASEDIR}
fi
}
SetBaseDir $(dirname $0)
BASEDIR=$(dirname $0)
INSTALL=0
UPDATE=0
INSTALLER_ARGS=''
@ -21,10 +13,14 @@ out_fd=1
while [ -n "$1" ]; do
case "$1" in
"--basedir"|"--base-dir"|"--test-base")
"--basedir")
shift
SetBaseDir $1
BASEDIR=$1
shift
if [[ ! $BASEDIR = /* ]]; then
# Relative
BASEDIR=$(pwd)/${BASEDIR}
fi
BASEDIR_CMD="let g:vimspector_base_dir='${BASEDIR}'"
;;
"--install")
@ -36,7 +32,7 @@ while [ -n "$1" ]; do
INSTALL=$1
shift
;;
"--update"|"--upgrade")
"--update")
UPDATE=1
shift
;;
@ -91,8 +87,7 @@ if [ "$INSTALL" = "1" ] || [ "$INSTALL" = "script" ]; then
if ! python3 $(dirname $0)/install_gadget.py \
--basedir ${BASEDIR} \
${INSTALLER_ARGS} \
--all \
--force-enable-csharp; then
--all; then
echo "Script installation reported errors" >&2
exit 1
fi
@ -103,7 +98,7 @@ if [ "$INSTALL" = "1" ] || [ "$INSTALL" = "vim" ]; then
--cmd "${BASEDIR_CMD}" \
-c 'autocmd User VimspectorInstallSuccess qa!' \
-c 'autocmd User VimspectorInstallFailed cquit!' \
-c "VimspectorInstall --all netcoredbg"; then
-c "VimspectorInstall --all"; then
echo "Vim installation reported errors" >&2
exit 1
fi
@ -136,7 +131,6 @@ 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"
@ -145,9 +139,6 @@ set -e
pushd tests/testdata/cpp/simple
make clean all
popd
pushd support/test/csharp
dotnet build
popd
set +e
echo "%DONE - built test programs"

View file

@ -1,12 +1,5 @@
" 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
@ -38,10 +31,6 @@ function! s:CustomiseUI()
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
@ -54,7 +43,7 @@ function s:SetUpTerminal()
let padding = 4
let cols = max( [ min( [ &columns - left_bar - code - padding, 80 ] ), 10 ] )
call win_gotoid( terminal_win )
execute string(cols) . 'wincmd |'
execute cols . 'wincmd |'
endfunction
function! s:CustomiseWinBar()
@ -76,86 +65,3 @@ augroup TestUICustomistaion
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

@ -1,8 +0,0 @@
# 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

@ -1,13 +0,0 @@
#!/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 ) }" )

2
support/site/jekyll/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
vendor/
.bundle

View file

@ -0,0 +1,2 @@
source 'https://rubygems.org'
gem 'jekyll', '3.8.5'

View file

@ -0,0 +1,61 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
colorator (1.1.0)
concurrent-ruby (1.1.5)
em-websocket (0.5.1)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0)
eventmachine (1.2.7)
ffi (1.11.1)
forwardable-extended (2.6.0)
http_parser.rb (0.6.0)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
jekyll (3.8.5)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 0.7)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 2.0)
kramdown (~> 1.14)
liquid (~> 4.0)
mercenary (~> 0.3.3)
pathutil (~> 0.9)
rouge (>= 1.7, < 4)
safe_yaml (~> 1.0)
jekyll-sass-converter (1.5.2)
sass (~> 3.4)
jekyll-watch (2.2.1)
listen (~> 3.0)
kramdown (1.17.0)
liquid (4.0.3)
listen (3.2.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.3.6)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (4.0.1)
rb-fsevent (0.10.3)
rb-inotify (0.10.0)
ffi (~> 1.0)
rouge (3.12.0)
safe_yaml (1.0.5)
sass (3.7.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
PLATFORMS
ruby
DEPENDENCIES
jekyll (= 3.8.5)
BUNDLED WITH
2.0.2

View file

@ -1,15 +0,0 @@
{
"$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

@ -1,9 +0,0 @@
#!/usr/bin/env bash
function Test() {
echo $1
}
for i in "$@"; do
Test $i
done

View file

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

View file

@ -6,12 +6,5 @@ $( document ).ready( function() {
return msg;
};
var obj = {
test: getMessage(),
toast: function() { return 'egg'; },
spam: 'ham'
};
alert( 'test: ' + obj.test );
alert( 'toast: ' + obj.toast() );
alert( 'test: ' + getMessage() );
} );

View file

@ -1,28 +1,11 @@
{
"configurations": {
"CodeLLDB": {
"adapter": "CodeLLDB",
"configuration": {
"request": "launch",
"program": "${workspaceRoot}/test",
"stopAtEntry": true
}
},
"lldb-vscode": {
"adapter": "lldb-vscode",
"configuration": {
"request": "launch",
"program": "${workspaceRoot}/test",
"stopAtEntry": true
}
},
"cpptools": {
"simple_c_program - Launch": {
"adapter": "vscode-cpptools",
"configuration": {
"request": "launch",
"program": "${workspaceRoot}/test",
"stopOnEntry": true,
"MIMode": "lldb"
"stopAtEntry": true
}
}
}

View file

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

View file

@ -1,5 +1,3 @@
#include <cstdio>
#include <cstdlib>
#include <iostream>
namespace Test
@ -35,8 +33,6 @@ 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,3 +1,2 @@
bin/
obj/Debug
obj/

View file

@ -1,57 +1,21 @@
{
"adapters": {
"netcoredbg-debuglog": {
"attach": {
"pidProperty": "processId",
"pidSelect": "ask"
"configurations": {
"launch - netcoredbg": {
"adapter": "netcoredbg",
"configuration": {
"request": "launch",
"program": "${workspaceRoot}/bin/Debug/netcoreapp2.2/csharp.dll",
"args": [],
"stopAtEntry": true
}
},
"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": {}
"launch - mono": {
"adapter": "vscode-mono-debug",
"configuration": {
"request": "launch",
"program": "${workspaceRoot}/Program.exe"
}
}
}
}
}

View file

@ -2,36 +2,11 @@
namespace csharp
{
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)
class Program
{
Program p = new Program();
for (int x = 1; x < 10; ++ x) {
p.MakeToast( x );
}
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
}

View file

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

View file

@ -3,8 +3,6 @@ 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
@ -17,18 +15,4 @@ 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

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

View file

@ -0,0 +1,18 @@
<?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

@ -0,0 +1,10 @@
<?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

@ -0,0 +1,742 @@
{
"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

@ -1,22 +1,7 @@
{
"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",
@ -25,35 +10,6 @@
"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

@ -1,29 +0,0 @@
{
"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

@ -1,33 +0,0 @@
# 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

@ -1,20 +0,0 @@
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

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

View file

@ -1,9 +0,0 @@
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

@ -1,30 +0,0 @@
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

@ -4,7 +4,7 @@
<artifactId>TestApplication</artifactId>
<version>1</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>

View file

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

View file

@ -1,21 +0,0 @@
{
"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

@ -1,25 +0,0 @@
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

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

View file

@ -1,5 +0,0 @@
package vimspector.test
fun main(args: Array<String>) {
println("Hello World!")
}

View file

@ -1,18 +0,0 @@
{
"$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#",
"configurations": {
"love": {
"adapter": "lua-local",
"configuration": {
"request": "launch",
"type": "lua-local",
"cwd": "${workspaceFolder}",
"program": {
"command": "love"
},
"args": ["${workspaceFolder}"],
"stopOnEntry": false
}
}
}
}

View file

@ -1,12 +0,0 @@
function love.conf(t)
t.modules.audio = false
t.modules.font = false
t.modules.graphics = false
t.modules.image = false
t.modules.mouse = false
t.modules.physics = false
t.modules.sound = false
t.modules.touch = false
t.modules.video = false
t.modules.window = false
end

View file

@ -1,10 +0,0 @@
if pcall(require, 'lldebugger') then
require('lldebugger').start()
end
local time = 0
function love.update(dt)
time = time + dt
love.event.quit()
end

View file

@ -1,18 +0,0 @@
{
"$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#",
"configurations": {
"love": {
"adapter": "lua-local",
"configuration": {
"request": "launch",
"type": "lua-local",
"cwd": "${workspaceFolder}",
"program": {
"command": "love"
},
"args": ["${workspaceFolder}"],
"stopOnEntry": false
}
}
}
}

View file

@ -1,21 +0,0 @@
if pcall(require, 'lldebugger') then
require('lldebugger').start()
end
local rect = {0, 0, 64, 64}
function love.update(dt)
rect[1] = rect[1] + 10 * dt
rect[2] = rect[2] + 10 * dt
end
function love.draw()
love.graphics.rectangle('fill', rect[1], rect[2], rect[3], rect[4])
end
function love.keypressed()
love.event.quit()
end

View file

@ -1,31 +0,0 @@
{
"$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#",
"configurations": {
"lua": {
"adapter": "lua-local",
"configuration": {
"request": "launch",
"type": "lua-local",
"cwd": "${workspaceFolder}",
"program": {
"lua": "lua",
"file": "simple.lua",
"stopOnEntry": false
}
}
},
"luajit": {
"adapter": "lua-local",
"configuration": {
"request": "launch",
"type": "lua-local",
"cwd": "${workspaceFolder}",
"program": {
"lua": "luajit",
"file": "simple.lua",
"stopOnEntry": false
}
}
}
}
}

View file

@ -1,21 +0,0 @@
local separator = ' '
local function createMessage()
local words = {}
table.insert(words, 'Hello')
table.insert(words, 'world')
return table.concat(words, separator)
end
local function withEmphasis(func)
return function()
return func() .. '!'
end
end
createMessage = withEmphasis(createMessage)
print(createMessage())

View file

@ -1,10 +1,3 @@
var msg = 'Hello, world!'
var obj = {
test: 'testing',
toast: function() {
return 'toasty' + this.test;
}
}
console.log( "OK stuff happened " + obj.toast() )
console.log( "OK stuff happened" )

View file

@ -1,21 +0,0 @@
{
"configurations": {
"run": {
"adapter": "debugpy",
"default": true,
"configuration": {
"request": "launch",
"program": "${workspaceRoot}/moo.py",
"cwd": "${workspaceRoot}",
"stopOnEntry": true
},
"breakpoints": {
"exception": {
"raised": "N",
"uncaught": "",
"userUnhandled": ""
}
}
}
}
}

View file

@ -1,15 +0,0 @@
def Say( *args, **kwargs ):
print( *args, **kwargs )
def Quiet():
pass

View file

@ -1,13 +0,0 @@
import cow
def Moo():
for i in range( 1, 100 ):
cow.Say( 'Moo' )
for i in range( 1, 100 ):
cow.Say( 'Ooom' )
if __name__ == '__main__':
Moo()

View file

@ -1,56 +1,5 @@
{
"$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json",
"adapters": {
"run_with_debugpy": {
"command": [ "${workspaceRoot}/run_with_debugpy" ],
"port": 9876,
"env": {
"DEBUG_PORT": "9876"
}
},
"python-remote-docker": {
"variables": {
"port": "8765"
},
"port": "${port}",
"launch": {
"remote": {
"container": "${ContainerID}",
"runCommand": [
"python3", "-m", "debugpy", "--listen", "0.0.0.0:${port}",
"--wait-for-client",
"%CMD%"
]
},
"delay": "5000m"
}
},
"python-remote-ssh": {
"variables": {
"port": "8765"
},
"port": "${port}",
"host": "${host}",
"launch": {
"remote": {
"host": "${host}",
"account": "${account}",
"runCommand": [
"python3", "-m", "debugpy", "--listen", "0.0.0.0:${port}",
"--wait-for-client",
"%CMD%"
]
}
}
}
},
"configurations": {
"Use custom gadget": {
"adapter": "test_custom",
"configuration": {
"request": "launch"
}
},
// This is a comment.
"run legacy vscode-python": {
"adapter": "vscode-python", /* coment goes here too */
@ -83,33 +32,6 @@
}
}
},
"attach-run": {
"adapter": "run_with_debugpy",
"configuration": {
"request": "attach"
},
"breakpoints": {
"exception": {
"raised": "N",
"uncaught": "",
"userUnhandled": ""
}
}
},
"docker-attach": {
"adapter": "python-remote-docker",
"remote-cmdLine": [ "/root/main.py" ],
"remote-request": "launch",
"configuration": {
"request": "attach",
"pathMappings": [
{
"localRoot": "${workspaceRoot}",
"remoteRoot": "/root"
}
]
}
},
"run": {
"adapter": "debugpy",
"configuration": {
@ -130,11 +52,6 @@
},
"run - default": {
"adapter": "debugpy",
"variables": {
"MAKE_ENV_OUTPUT": {
"shell": "${workspaceRoot}/make_env.sh"
}
},
"configuration": {
"request": "launch",
"type": "python",
@ -143,8 +60,8 @@
"stopOnEntry#json": "${StopOnEntry:true}",
"console": "integratedTerminal",
"args#json": "${args:[]}",
"igored#json#s": "string not json",
"env#json": "${MAKE_ENV_OUTPUT}"
"env#json": "${env:{\\}}",
"igored#json#s": "string not json"
},
"breakpoints": {
"exception": {
@ -182,23 +99,6 @@
"stopOnEntry": false,
"console": "integratedTerminal"
}
},
"run - remote host": {
"adapter": "python-remote-ssh",
"remote-cmdLine": [
"${remoteRoot}/main.py"
],
"remote-request": "launch",
"configuration": {
"request": "attach",
"redirectOutput": true,
"pathMappings": [
{
"localRoot": "${workspaceRoot}",
"remoteRoot": "${remoteRoot}"
}
]
}
}
}
}

View file

@ -1,22 +0,0 @@
FROM ubuntu:18.04
RUN apt-get update && \
apt-get -y dist-upgrade && \
apt-get -y install sudo \
lsb-release \
ca-certificates \
python3-dev \
python3-pip \
ca-cacert \
locales \
language-pack-en \
libncurses5-dev libncursesw5-dev \
git && \
apt-get -y autoremove
## cleanup of files from setup
RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
RUN pip3 install debugpy
ADD main.py /root/main.py

View file

@ -1,11 +0,0 @@
#!/usr/bin/env bash
set -e
if [ "$1" = "--continue" ]; then
OPTS=""
else
OPTS="--no-cache"
fi
docker build ${OPTS} -t puremourning/vimspector:simple_python .

View file

@ -1,9 +0,0 @@
#!/usr/bin/env bash
cat <<"EOF"
{
"Something": "Value1",
"SomethingElse": "Value2"
}
EOF

View file

@ -1,15 +0,0 @@
#!/usr/bin/env python
import os
def Main():
print( os.environ.get( 'Something', 'ERROR' ) )
print( os.environ.get( 'SomethingElse', 'ERROR' ) )
for k, v in os.environ:
print( f'{ k } = "{ v }"' )
Main()

View file

@ -1,7 +1,9 @@
#!/usr/bin/env bash
if [ -z "$DEBUG_PORT" ]; then
DEBUG_PORT=5678
PYTHON=python3
if [ -n "$1" ]; then
PYTHON=$1
fi
pushd $(dirname $0)
@ -9,11 +11,10 @@ pushd $(dirname $0)
rm -rf env
fi
python3 -m venv env
$PYTHON -m venv env
. env/bin/activate
python -m pip install -r requirements.txt
echo "using port $DEBUG_PORT"
echo "*** Launching ***"
python -m debugpy --wait-for-client --listen 0.0.0.0:${DEBUG_PORT} main.py
python -m debugpy --wait-for-client --listen 0.0.0.0:5678 main.py
popd

View file

@ -1,2 +0,0 @@
---
BUNDLE_PATH: "vendor/bundle"

View file

@ -1,2 +0,0 @@
Gemfile.lock
vendor/

View file

@ -1,15 +0,0 @@
{
"configurations": {
"launch current file": {
"adapter": "cust_vscode-ruby",
"configuration": {
"request": "launch",
"program": "${file}",
"args": [ "*${args}" ],
"useBundler": true,
"trace": true,
"showDebuggerOutput": true
}
}
}
}

View file

@ -1,11 +0,0 @@
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
# gem "rails"
gem "ruby-debug-ide", "~> 0.7.2"
gem "debase", "~> 0.2.4"

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