diff --git a/README.md b/README.md index 674bb0b..61422aa 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,11 @@ For a tutorial and usage overview, take a look at the * [Stepping](#stepping) * [Variables and scopes](#variables-and-scopes) * [Watches](#watches) + * [Watch autocompletion](#watch-autocompletion) * [Stack Traces](#stack-traces) * [Program Output](#program-output) * [Console](#console) + * [Console autocompletion](#console-autocompletion) * [Log View](#log-view) * [Closing debugger](#closing-debugger) * [Debug adapter configuration](#debug-adapter-configuration) @@ -77,7 +79,7 @@ For a tutorial and usage overview, take a look at the * [License](#license) * [Sponsorship](#sponsorship) - + @@ -109,10 +111,10 @@ And a couple of brief demos: - launch and attach - remote launch, remote attach - locals and globals display -- watch expressions +- watch expressions with autocompletion - call stack display and navigation - variable value display hover -- interactive debug console +- interactive debug console with autocompletion - launch debugee within Vim's embedded terminal - logging/stdout display - simple stable API for custom tooling (e.g. integrate with language server) @@ -742,6 +744,22 @@ to add a new watch expression. The watches are represented by the buffer `vimspector.StackTrace`. +### Watch autocompletion + +The watch prompt buffer has its `omnifunc` set to a function that will +calcualte completion for the current expression. This is trivailly used with +`` (see `:help ins-completion`), or integrated with your +favourite completion system. The filetype in the buffer is set to +`VimspectorPrompt`. + +For YouCompleteMe, the following config works well: + +```viml +let g:ycm_semantic_triggers = { + \ 'VimspectorPrompt': [ '.', '->', ':', '<' ] +} +``` + ## Stack Traces * In the threads window, use `` to expand/collapse. @@ -782,6 +800,22 @@ NOTE: See also [Watches](#watches) above. If the output window is closed, a new one can be opened with `:VimspectorShowOutput Console`. +### Console autocompletion + +The console prompt buffer has its `omnifunc` set to a function that will +calcualte completion for the current command/expression. This is trivailly used +with `` (see `:help ins-completion`), or integrated with your +favourite completion system. The filetype in the buffer is set to +`VimspectorPrompt`. + +For YouCompleteMe, the following config works well: + +```viml +let g:ycm_semantic_triggers = { + \ 'VimspectorPrompt': [ '.', '->', ':', '<' ] +} +``` + ### Log View The Vimspector log file contains a full trace of the communication between diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 302b34d..c1f3640 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -19,6 +19,13 @@ let s:save_cpo = &cpoptions set cpoptions&vim " }}} +function! s:Debug( ... ) abort + py3 < s:latest_completion_request.start_pos + " fix up the text (insert anything that is already present in the line + " that would be erased by the fixed-up earlier start position) + " + " both start_pos and item.start are 1-based + let item.text = s:latest_completion_request.line[ + \ s:latest_completion_request.start_pos + pfxlen - 1 : + \ item.start + pfxlen - 1 ] . item.text + endif + + if item.length > len( a:query ) + " call s:Debug( 'Rejecting %s, length is greater than %s', + " \ item, + " \ len( a:query ) ) + continue + endif + + call add( items, { 'word': item.text, + \ 'abbr': item.label, + \ 'menu': get( item, 'type', '' ), + \ 'icase': 1, + \ } ) + endfor + let s:latest_completion_request = {} + + " call s:Debug( 'Items: %s', items ) + return { 'words': items, 'refresh': 'always' } + endif +endfunction + +function! vimspector#OmniFuncWatch( find_start, query ) abort + return vimspector#CompleteFuncSync( 'Expression: ', a:find_start, a:query ) +endfunction + +function! vimspector#OmniFuncConsole( find_start, query ) abort + return vimspector#CompleteFuncSync( '> ', a:find_start, a:query ) +endfunction + function! vimspector#Install( bang, ... ) abort if !s:enabled return diff --git a/autoload/vimspector/internal/neopopup.vim b/autoload/vimspector/internal/neopopup.vim index 148107c..fe5fe05 100644 --- a/autoload/vimspector/internal/neopopup.vim +++ b/autoload/vimspector/internal/neopopup.vim @@ -26,24 +26,39 @@ 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, [ a:message ] ) + call nvim_buf_set_lines(buf, 0, -1, v:true, 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) + let win = nvim_open_win(buf, 0, s:GetSplashConfig( message ) ) call nvim_win_set_option(win, 'wrap', v:false) + call nvim_win_set_option(win, 'colorcolumn', '') let id = s:next_id let s:next_id += 1 @@ -53,7 +68,9 @@ endfunction function! vimspector#internal#neopopup#UpdateSplash( id, message ) abort let splash = s:db[ a:id ] - call nvim_buf_set_lines(splash.buf, 0, -1, v:true, [ a:message ] ) + let message = s:MessageToList( a:message ) + call nvim_buf_set_lines( splash.buf, 0, -1, v:true, message ) + call nvim_win_set_config( splash.win, s:GetSplashConfig( message ) ) return a:id endfunction diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 5a542c6..3956d7f 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -554,8 +554,7 @@ class DebugSession( object ): # TODO: # - start / length # - sortText - return [ i.get( 'text' ) or i[ 'label' ] - for i in response[ 'body' ][ 'targets' ] ] + return response[ 'body' ][ 'targets' ] def _SetUpUI( self ): @@ -941,13 +940,26 @@ class DebugSession( object ): if 'name' not in launch_config: launch_config[ 'name' ] = 'test' + 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 ) + + self._connection.DoRequest( lambda msg: self._OnLaunchComplete(), { 'command': launch_config[ 'request' ], 'arguments': launch_config - } - ) + }, + failure_handler ) def _OnLaunchComplete( self ): diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 7e1d619..21073f1 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -407,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.0.vsix.gz', + 'target': 'msjsdiag.debugger-for-chrome-4.12.10.vsix.gz', 'format': 'zip.gz', }, 'all': { - 'version': '4.12.0', - 'file_name': 'msjsdiag.debugger-for-chrome-4.12.0.vsix', + 'version': '4.12.10', + 'file_name': 'msjsdiag.debugger-for-chrome-4.12.10.vsix', 'checksum': - '0df2fe96d059a002ebb0936b0003e6569e5a5c35260dc3791e1657d27d82ccf5' + '' }, 'adapters': { 'chrome': { diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 90df09d..71c81e9 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -192,7 +192,8 @@ class OutputView( object ): utils.SetUpPromptBuffer( tab_buffer.buf, name, '> ', - 'vimspector#EvaluateConsole' ) + 'vimspector#EvaluateConsole', + 'vimspector#OmniFuncConsole' ) else: utils.SetUpHiddenBuffer( tab_buffer.buf, name ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 17c8166..2874635 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -135,7 +135,7 @@ def SetUpHiddenBuffer( buf, name ): buf.name = name -def SetUpPromptBuffer( buf, name, prompt, callback ): +def SetUpPromptBuffer( buf, name, prompt, callback, omnifunc ): # This feature is _super_ new, so only enable when available if not Exists( '*prompt_setprompt' ): return SetUpHiddenBuffer( buf, name ) @@ -148,6 +148,7 @@ def SetUpPromptBuffer( buf, name, prompt, callback ): 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, @@ -156,6 +157,12 @@ def SetUpPromptBuffer( buf, name, prompt, callback ): 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 diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index d82090a..5694e46 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -147,7 +147,8 @@ class VariablesView( object ): utils.SetUpPromptBuffer( self._watch.buf, 'vimspector.Watches', 'Expression: ', - 'vimspector#AddWatchPrompt' ) + 'vimspector#AddWatchPrompt', + 'vimspector#OmniFuncWatch' ) with utils.LetCurrentWindow( watches_win ): vim.command( 'nnoremap :call vimspector#ExpandVariable()' ) diff --git a/support/test/bash/test_script b/support/test/bash/test_script new file mode 100644 index 0000000..4e27bd9 --- /dev/null +++ b/support/test/bash/test_script @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +function Test() { + echo $1 +} + +for i in "$@"; do + Test $i +done diff --git a/support/test/chrome/run_server b/support/test/chrome/run_server new file mode 100755 index 0000000..9e5b569 --- /dev/null +++ b/support/test/chrome/run_server @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +php -S localhost:1234 -t www diff --git a/support/test/chrome/www/js/test.js b/support/test/chrome/www/js/test.js index 273804e..9c1c4a1 100644 --- a/support/test/chrome/www/js/test.js +++ b/support/test/chrome/www/js/test.js @@ -6,5 +6,12 @@ $( document ).ready( function() { return msg; }; - alert( 'test: ' + getMessage() ); + var obj = { + test: getMessage(), + toast: function() { return 'egg'; }, + spam: 'ham' + }; + + alert( 'test: ' + obj.test ); + alert( 'toast: ' + obj.toast() ); } ); diff --git a/support/test/node/simple/simple.js b/support/test/node/simple/simple.js index fc6e558..77b8e32 100644 --- a/support/test/node/simple/simple.js +++ b/support/test/node/simple/simple.js @@ -1,3 +1,10 @@ var msg = 'Hello, world!' -console.log( "OK stuff happened" ) +var obj = { + test: 'testing', + toast: function() { + return 'toasty' . this.test; + } +} + +console.log( "OK stuff happened " + obj.toast() ) diff --git a/tests/tabpage.test.vim b/tests/tabpage.test.vim index 6772c66..1a5b1bc 100644 --- a/tests/tabpage.test.vim +++ b/tests/tabpage.test.vim @@ -71,12 +71,8 @@ function! Test_All_Buffers_Deleted_NoHidden() let buffers_after = getbufinfo( opts ) - if assert_equal( len( buffers_before ), len( buffers_after ) ) - call assert_report( 'Expected ' - \ . string( buffers_before ) - \ . ' but found ' - \ . string( buffers_after ) ) - endif + call WaitForAssert( {-> + \ assert_equal( len( buffers_before ), len( buffers_after ) ) } ) set hidden& lcd - @@ -106,12 +102,8 @@ function! Test_All_Buffers_Deleted_Hidden() let buffers_after = getbufinfo( opts ) - if assert_equal( len( buffers_before ), len( buffers_after ) ) - call assert_report( 'Expected ' - \ . string( buffers_before ) - \ . ' but found ' - \ . string( buffers_after ) ) - endif + call WaitForAssert( {-> + \ assert_equal( len( buffers_before ), len( buffers_after ) ) } ) set hidden& lcd - @@ -126,12 +118,8 @@ function! Test_All_Buffers_Deleted_ToggleLog() VimspectorToggleLog let buffers_after = getbufinfo( #{ buflisted: 1 } ) - if assert_equal( len( buffers_before ), len( buffers_after ) ) - call assert_report( 'Expected ' - \ . string( buffers_before ) - \ . ' but found ' - \ . string( buffers_after ) ) - endif + call WaitForAssert( {-> + \ assert_equal( len( buffers_before ), len( buffers_after ) ) } ) call vimspector#test#setup#Reset() set hidden& @@ -158,12 +146,8 @@ function! Test_All_Buffers_Deleted_Installer() \ 120000 ) let buffers_after = getbufinfo( #{ buflisted: 1 } ) - if assert_equal( len( buffers_before ), len( buffers_after ) ) - call assert_report( 'Expected ' - \ . string( buffers_before ) - \ . ' but found ' - \ . string( buffers_after ) ) - endif + call WaitForAssert( {-> + \ assert_equal( len( buffers_before ), len( buffers_after ) ) } ) call vimspector#test#setup#Reset() set hidden&