From d4212617154c3ae5bf65e2fc76cafae578b04c20 Mon Sep 17 00:00:00 2001 From: xiaobeibei Date: Tue, 27 Aug 2019 11:47:20 +0800 Subject: [PATCH 01/10] feat: support debug multi goroutine --- autoload/go/debug.vim | 58 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/autoload/go/debug.vim b/autoload/go/debug.vim index bb14cf0af4..f723b2c894 100644 --- a/autoload/go/debug.vim +++ b/autoload/go/debug.vim @@ -455,6 +455,8 @@ function! s:start_cb() abort command! -nargs=0 GoDebugStop call go#debug#Stop() command! -nargs=* GoDebugSet call go#debug#Set() command! -nargs=1 GoDebugPrint call go#debug#Print() + command! -nargs=0 GoDebugGoroutines call go#debug#Goroutines() + command! -nargs=1 GoDebugGoroutine call go#debug#Goroutine() nnoremap (go-debug-breakpoint) :call go#debug#Breakpoint() nnoremap (go-debug-next) :call go#debug#Stack('next') @@ -861,8 +863,17 @@ function! go#debug#Stack(name) abort endif let s:stack_name = l:name try - let res = s:call_jsonrpc('RPCServer.Command', {'name': l:name}) - call s:stack_cb(res) + let l:res = s:call_jsonrpc('RPCServer.Command', {'name': l:name}) + if l:name is# 'next' + for i in range(99999) + if l:res.result.State.NextInProgress == v:true + let l:res = s:call_jsonrpc('RPCServer.Command', {'name': 'continue'}) + else + break + endif + endfor + endif + call s:stack_cb(l:res) catch call go#util#EchoError(v:exception) call s:clearState() @@ -899,6 +910,49 @@ function! s:isActive() return len(s:state['message']) > 0 endfunction +" List All Goroutines Running +function! go#debug#Goroutines(...) abort + try + let l:currentGoroutineId = 0 + try + let l:currentGoroutineId = s:groutineID() + catch + echom "current goroutineId not found..." + endtry + + let l:res = s:call_jsonrpc('RPCServer.ListGoroutines') + let l:goroutines = l:res.result.Goroutines + if len(l:goroutines) == 0 + echom "No Goroutines Running Now..." + return + endif + + for l:idx in range(len(l:goroutines)) + let l:goroutine = l:goroutines[l:idx] + if l:goroutine.id == l:currentGoroutineId + echom "* Goroutine " . l:goroutine.id . " - " . l:goroutine.userCurrentLoc.file . ":" . l:goroutine.userCurrentLoc.line . " " l:goroutine.userCurrentLoc.function.name . " " . " (thread: " . l:goroutine.threadID . ")" + else + echom " Goroutine " . l:goroutine.id . " - " . l:goroutine.userCurrentLoc.file . ":" . l:goroutine.userCurrentLoc.line . " " l:goroutine.userCurrentLoc.function.name . " " . " (thread: " . l:goroutine.threadID . ")" + endif + endfor + + catch + call go#util#EchoError(v:exception) + endtry +endfunction + +" Change Goroutine +function! go#debug#Goroutine(goroutineId) abort + let l:goroutineId = a:goroutineId + try + let l:res = s:call_jsonrpc('RPCServer.Command', {'Name': 'switchGoroutine', 'GoroutineID': str2nr(l:goroutineId)}) + call s:stack_cb(l:res) + echom "Switched Goroutine to:" . a:1 + catch + call go#util#EchoError(v:exception) + endtry +endfunction + " Toggle breakpoint. Returns 0 on success and 1 on failure. function! go#debug#Breakpoint(...) abort let l:filename = fnamemodify(expand('%'), ':p:gs!\\!/!') From ed6bd2ad753d6931bcea2f313993a9fe0d66fa8a Mon Sep 17 00:00:00 2001 From: xiaobeibei Date: Tue, 27 Aug 2019 11:57:34 +0800 Subject: [PATCH 02/10] fix: a log typo --- autoload/go/debug.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/go/debug.vim b/autoload/go/debug.vim index f723b2c894..765a3658b2 100644 --- a/autoload/go/debug.vim +++ b/autoload/go/debug.vim @@ -947,7 +947,7 @@ function! go#debug#Goroutine(goroutineId) abort try let l:res = s:call_jsonrpc('RPCServer.Command', {'Name': 'switchGoroutine', 'GoroutineID': str2nr(l:goroutineId)}) call s:stack_cb(l:res) - echom "Switched Goroutine to:" . a:1 + echom "Switched Goroutine to: " . l:goroutineId catch call go#util#EchoError(v:exception) endtry From 830d7871de49740eac7ddbabebc85b4a71dc211e Mon Sep 17 00:00:00 2001 From: xiaobeibei Date: Wed, 28 Aug 2019 18:11:19 +0800 Subject: [PATCH 03/10] fix: remove 99999 continue limit, user can user ctrl-c if it cost too much time --- autoload/go/debug.vim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/autoload/go/debug.vim b/autoload/go/debug.vim index 765a3658b2..975ed855d5 100644 --- a/autoload/go/debug.vim +++ b/autoload/go/debug.vim @@ -865,13 +865,14 @@ function! go#debug#Stack(name) abort try let l:res = s:call_jsonrpc('RPCServer.Command', {'name': l:name}) if l:name is# 'next' - for i in range(99999) + let l:w = 0 + while l:w < 1 if l:res.result.State.NextInProgress == v:true let l:res = s:call_jsonrpc('RPCServer.Command', {'name': 'continue'}) else break endif - endfor + endwhile endif call s:stack_cb(l:res) catch From f93692b457e85f8e50ddcf53f4933ec6876b5d55 Mon Sep 17 00:00:00 2001 From: xiaobeibei Date: Tue, 3 Sep 2019 11:37:53 +0800 Subject: [PATCH 04/10] fix: change echom to go#util#Echo* --- autoload/go/debug.vim | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/autoload/go/debug.vim b/autoload/go/debug.vim index 975ed855d5..0cb490a804 100644 --- a/autoload/go/debug.vim +++ b/autoload/go/debug.vim @@ -455,8 +455,8 @@ function! s:start_cb() abort command! -nargs=0 GoDebugStop call go#debug#Stop() command! -nargs=* GoDebugSet call go#debug#Set() command! -nargs=1 GoDebugPrint call go#debug#Print() - command! -nargs=0 GoDebugGoroutines call go#debug#Goroutines() - command! -nargs=1 GoDebugGoroutine call go#debug#Goroutine() + command! -nargs=0 GoDebugGoroutines call go#debug#Goroutines() + command! -nargs=1 GoDebugGoroutine call go#debug#Goroutine() nnoremap (go-debug-breakpoint) :call go#debug#Breakpoint() nnoremap (go-debug-next) :call go#debug#Stack('next') @@ -912,28 +912,28 @@ function! s:isActive() endfunction " List All Goroutines Running -function! go#debug#Goroutines(...) abort +function! go#debug#Goroutines() abort try let l:currentGoroutineId = 0 try let l:currentGoroutineId = s:groutineID() catch - echom "current goroutineId not found..." + call go#util#EchoWarning("current goroutineId not found...") endtry let l:res = s:call_jsonrpc('RPCServer.ListGoroutines') let l:goroutines = l:res.result.Goroutines if len(l:goroutines) == 0 - echom "No Goroutines Running Now..." + call go#util#EchoWarning("No Goroutines Running Now...") return endif for l:idx in range(len(l:goroutines)) let l:goroutine = l:goroutines[l:idx] if l:goroutine.id == l:currentGoroutineId - echom "* Goroutine " . l:goroutine.id . " - " . l:goroutine.userCurrentLoc.file . ":" . l:goroutine.userCurrentLoc.line . " " l:goroutine.userCurrentLoc.function.name . " " . " (thread: " . l:goroutine.threadID . ")" + call go#util#EchoInfo("* Goroutine " . l:goroutine.id . " - " . l:goroutine.userCurrentLoc.file . ":" . l:goroutine.userCurrentLoc.line . " " . l:goroutine.userCurrentLoc.function.name . " " . " (thread: " . l:goroutine.threadID . ")") else - echom " Goroutine " . l:goroutine.id . " - " . l:goroutine.userCurrentLoc.file . ":" . l:goroutine.userCurrentLoc.line . " " l:goroutine.userCurrentLoc.function.name . " " . " (thread: " . l:goroutine.threadID . ")" + call go#util#EchoInfo(" Goroutine " . l:goroutine.id . " - " . l:goroutine.userCurrentLoc.file . ":" . l:goroutine.userCurrentLoc.line . " " . l:goroutine.userCurrentLoc.function.name . " " . " (thread: " . l:goroutine.threadID . ")") endif endfor @@ -948,7 +948,7 @@ function! go#debug#Goroutine(goroutineId) abort try let l:res = s:call_jsonrpc('RPCServer.Command', {'Name': 'switchGoroutine', 'GoroutineID': str2nr(l:goroutineId)}) call s:stack_cb(l:res) - echom "Switched Goroutine to: " . l:goroutineId + call go#util#EchoInfo("Switched Goroutine to: " . l:goroutineId) catch call go#util#EchoError(v:exception) endtry From 1de27e4baf2de531da68a8efbe54c3bda7b7cea6 Mon Sep 17 00:00:00 2001 From: xiaobeibei Date: Thu, 5 Sep 2019 12:13:54 +0800 Subject: [PATCH 05/10] feat: remove go#debug#goroutines command, add one window to show/swicth between goroutines --- autoload/go/debug.vim | 117 +++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 35 deletions(-) diff --git a/autoload/go/debug.vim b/autoload/go/debug.vim index 0cb490a804..224da9210b 100644 --- a/autoload/go/debug.vim +++ b/autoload/go/debug.vim @@ -410,6 +410,7 @@ endfunction function! s:start_cb() abort let l:winid = win_getid() + let l:goroutine_win_id=0 silent! only! let winnum = bufwinnr(bufnr('__GODEBUG_STACKTRACE__')) @@ -435,6 +436,15 @@ function! s:start_cb() abort nmap q (go-debug-stop) endif + if has_key(debugwindows, "goroutines") && debugwindows['goroutines'] != '' + exe 'silent ' . debugwindows['goroutines'] + let l:goroutine_win_id = win_getid() + silent file `='__GODEBUG_GOROUTINES__'` + setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline + setlocal filetype=godebugvariables + call append(0, ["# Goroutines"]) + nmap :call go#debug#Goroutine() + endif if has_key(debugwindows, "vars") && debugwindows['vars'] != '' exe 'silent ' . debugwindows['vars'] silent file `='__GODEBUG_VARIABLES__'` @@ -445,6 +455,13 @@ function! s:start_cb() abort nmap q (go-debug-stop) endif + call win_gotoid(l:winid) + exe "resize +12" + if l:goroutine_win_id != 0 + call win_gotoid(l:goroutine_win_id) + exe "resize +6" + endif + silent! delcommand GoDebugStart silent! delcommand GoDebugTest command! -nargs=0 GoDebugContinue call go#debug#Stack('continue') @@ -455,8 +472,6 @@ function! s:start_cb() abort command! -nargs=0 GoDebugStop call go#debug#Stop() command! -nargs=* GoDebugSet call go#debug#Set() command! -nargs=1 GoDebugPrint call go#debug#Print() - command! -nargs=0 GoDebugGoroutines call go#debug#Goroutines() - command! -nargs=1 GoDebugGoroutine call go#debug#Goroutine() nnoremap (go-debug-breakpoint) :call go#debug#Breakpoint() nnoremap (go-debug-next) :call go#debug#Stack('next') @@ -773,6 +788,68 @@ function! go#debug#Print(arg) abort endtry endfunction +function! s:update_goroutines() abort + let l:var_win = bufwinnr(bufnr('__GODEBUG_GOROUTINES__')) + if l:var_win == -1 + return + endif + + let l:cur_win = bufwinnr('') + exe l:var_win 'wincmd w' + + try + setlocal modifiable + silent %delete _ + + let v = [] + let v += ['# Goroutines'] + let l:res = s:call_jsonrpc('RPCServer.State') + let l:currentGoroutineId = 0 + try + let l:currentGoroutineId = l:res["result"]["State"]["currentGoroutine"]["id"] + catch + call go#util#EchoInfo("current goroutineId not found...") + endtry + let l:res = s:call_jsonrpc('RPCServer.ListGoroutines') + let l:goroutines = l:res["result"]["Goroutines"] + if len(l:goroutines) == 0 + call go#util#EchoError("No Goroutines Running Now...") + return + endif + for l:idx in range(len(l:goroutines)) + let l:goroutine = l:goroutines[l:idx] + let l:goroutineType="" + let l:loc=0 + if l:goroutine.startLoc.file != "" + let l:loc=l:goroutine.startLoc + let l:goroutineType = "Start" + endif + if l:goroutine.goStatementLoc.file != "" + let l:loc=l:goroutine.goStatementLoc + let l:goroutineType = "Go" + endif + if l:goroutine.currentLoc.file != "" + let l:loc=l:goroutine.currentLoc + let l:goroutineType = "Runtime" + endif + if l:goroutine.userCurrentLoc.file != "" + let l:loc=l:goroutine.userCurrentLoc + let l:goroutineType = "User" + endif + if l:goroutine.id == l:currentGoroutineId + let l:g = "* Goroutine " . l:goroutine.id . " - " . l:goroutineType . ": " . l:loc.file . ":" . l:loc.line . " " . l:loc.function.name . " " . " (thread: " . l:goroutine.threadID . ")" + else + let l:g = " Goroutine " . l:goroutine.id . " - " . l:goroutineType . ": " . l:loc.file . ":" . l:loc.line . " " . l:loc.function.name . " " . " (thread: " . l:goroutine.threadID . ")" + endif + let v += [l:g] + endfor + + call setline(1, v) + finally + setlocal nomodifiable + exe l:cur_win 'wincmd w' + endtry +endfunction function! s:update_variables() abort " FollowPointers requests pointers to be automatically dereferenced. " MaxVariableRecurse is how far to recurse when evaluating nested types. @@ -834,6 +911,7 @@ function! s:stack_cb(res) abort call s:update_breakpoint(a:res) call s:update_stacktrace() call s:update_variables() + call s:update_goroutines() endfunction " Send a command to change the cursor location to Delve. @@ -911,40 +989,9 @@ function! s:isActive() return len(s:state['message']) > 0 endfunction -" List All Goroutines Running -function! go#debug#Goroutines() abort - try - let l:currentGoroutineId = 0 - try - let l:currentGoroutineId = s:groutineID() - catch - call go#util#EchoWarning("current goroutineId not found...") - endtry - - let l:res = s:call_jsonrpc('RPCServer.ListGoroutines') - let l:goroutines = l:res.result.Goroutines - if len(l:goroutines) == 0 - call go#util#EchoWarning("No Goroutines Running Now...") - return - endif - - for l:idx in range(len(l:goroutines)) - let l:goroutine = l:goroutines[l:idx] - if l:goroutine.id == l:currentGoroutineId - call go#util#EchoInfo("* Goroutine " . l:goroutine.id . " - " . l:goroutine.userCurrentLoc.file . ":" . l:goroutine.userCurrentLoc.line . " " . l:goroutine.userCurrentLoc.function.name . " " . " (thread: " . l:goroutine.threadID . ")") - else - call go#util#EchoInfo(" Goroutine " . l:goroutine.id . " - " . l:goroutine.userCurrentLoc.file . ":" . l:goroutine.userCurrentLoc.line . " " . l:goroutine.userCurrentLoc.function.name . " " . " (thread: " . l:goroutine.threadID . ")") - endif - endfor - - catch - call go#util#EchoError(v:exception) - endtry -endfunction - " Change Goroutine -function! go#debug#Goroutine(goroutineId) abort - let l:goroutineId = a:goroutineId +function! go#debug#Goroutine() abort + let l:goroutineId = split(getline('.'), " ")[1] try let l:res = s:call_jsonrpc('RPCServer.Command', {'Name': 'switchGoroutine', 'GoroutineID': str2nr(l:goroutineId)}) call s:stack_cb(l:res) From d07958af8031e1ca131b05d56c2bb93c44c1f102 Mon Sep 17 00:00:00 2001 From: xiaobeibei Date: Thu, 5 Sep 2019 12:32:01 +0800 Subject: [PATCH 06/10] feat: remove go#debug#goroutines command, add one window to show/swicth between goroutines --- autoload/go/config.vim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/autoload/go/config.vim b/autoload/go/config.vim index 5c03569357..9a25d28718 100644 --- a/autoload/go/config.vim +++ b/autoload/go/config.vim @@ -205,9 +205,10 @@ endfunction function! go#config#DebugWindows() abort return get(g:, 'go_debug_windows', { - \ 'stack': 'leftabove 20vnew', - \ 'out': 'botright 10new', - \ 'vars': 'leftabove 30vnew', + \ 'goroutines': 'split 60vnew', + \ 'out': 'botright 100new', + \ 'vars': 'leftabove 40vnew', + \ 'stack': 'leftabove 40vnew', \ } \ ) From 0c62e810674b3227859fa57c0cca9b59b0033d2c Mon Sep 17 00:00:00 2001 From: Billie Cleek Date: Wed, 11 Sep 2019 10:12:05 -0700 Subject: [PATCH 07/10] handle command response correctly when NextInProgress is set --- autoload/go/debug.vim | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/autoload/go/debug.vim b/autoload/go/debug.vim index 224da9210b..971c0a9ea6 100644 --- a/autoload/go/debug.vim +++ b/autoload/go/debug.vim @@ -941,12 +941,14 @@ function! go#debug#Stack(name) abort endif let s:stack_name = l:name try - let l:res = s:call_jsonrpc('RPCServer.Command', {'name': l:name}) + let l:res = s:call_jsonrpc('RPCServer.Command', {'name': l:name}) + if l:name is# 'next' + let l:res2 = l:res let l:w = 0 while l:w < 1 - if l:res.result.State.NextInProgress == v:true - let l:res = s:call_jsonrpc('RPCServer.Command', {'name': 'continue'}) + if l:res2.result.State.NextInProgress == v:true + let l:res2 = s:call_jsonrpc('RPCServer.Command', {'name': 'continue'}) else break endif From 0341ff3de3515a632b1fd1b1b78abe0d26107344 Mon Sep 17 00:00:00 2001 From: Billie Cleek Date: Wed, 11 Sep 2019 10:13:09 -0700 Subject: [PATCH 08/10] debug: call s:logger directly Call s:logger directly instead of calling it through call() --- autoload/go/debug.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoload/go/debug.vim b/autoload/go/debug.vim index 971c0a9ea6..c06383e859 100644 --- a/autoload/go/debug.vim +++ b/autoload/go/debug.vim @@ -504,7 +504,7 @@ endfunction function! s:err_cb(ch, msg) abort if get(s:state, 'ready', 0) != 0 - call call('s:logger', ['ERR: ', a:ch, a:msg]) + call s:logger('ERR: ', a:ch, a:msg) return endif @@ -513,7 +513,7 @@ endfunction function! s:out_cb(ch, msg) abort if get(s:state, 'ready', 0) != 0 - call call('s:logger', ['OUT: ', a:ch, a:msg]) + call s:logger('OUT: ', a:ch, a:msg) return endif From a52bbb1c28cd84825e521368a9717b9c6d66d7e4 Mon Sep 17 00:00:00 2001 From: Billie Cleek Date: Wed, 11 Sep 2019 10:15:27 -0700 Subject: [PATCH 09/10] debug: refactor goroutine UX Refactor debug window placement so that the windows are sized and place properly simply by creating the splits so that windows don't have to be resized after the splits are created. Factor the showing of goroutines into its own method for readability and consistency with how other windows are handled. Return early without trying to change the goroutine when the user presses enter on a line that doesn't list a goroutine or when the chosen goroutine is already current. Use window management functions instead of normal mode commands. Use 'ID' consistently instead of 'Id'. --- autoload/go/config.vim | 8 +-- autoload/go/debug.vim | 131 +++++++++++++++++++++++------------------ doc/vim-go.txt | 15 +++-- 3 files changed, 88 insertions(+), 66 deletions(-) diff --git a/autoload/go/config.vim b/autoload/go/config.vim index 9a25d28718..096469cb73 100644 --- a/autoload/go/config.vim +++ b/autoload/go/config.vim @@ -205,10 +205,10 @@ endfunction function! go#config#DebugWindows() abort return get(g:, 'go_debug_windows', { - \ 'goroutines': 'split 60vnew', - \ 'out': 'botright 100new', - \ 'vars': 'leftabove 40vnew', - \ 'stack': 'leftabove 40vnew', + \ 'vars': 'leftabove 30vnew', + \ 'stack': 'leftabove 20new', + \ 'goroutines': 'botright 10new', + \ 'out': 'botright 5new', \ } \ ) diff --git a/autoload/go/debug.vim b/autoload/go/debug.vim index c06383e859..721117d42b 100644 --- a/autoload/go/debug.vim +++ b/autoload/go/debug.vim @@ -23,7 +23,7 @@ if !exists('s:start_args') let s:start_args = [] endif -function! s:groutineID() abort +function! s:goroutineID() abort return s:state['currentThread'].goroutineID endfunction @@ -410,7 +410,6 @@ endfunction function! s:start_cb() abort let l:winid = win_getid() - let l:goroutine_win_id=0 silent! only! let winnum = bufwinnr(bufnr('__GODEBUG_STACKTRACE__')) @@ -419,6 +418,16 @@ function! s:start_cb() abort endif let debugwindows = go#config#DebugWindows() + if has_key(debugwindows, "vars") && debugwindows['vars'] != '' + exe 'silent ' . debugwindows['vars'] + silent file `='__GODEBUG_VARIABLES__'` + setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline + setlocal filetype=godebugvariables + call append(0, ["# Local Variables", "", "# Function Arguments"]) + nmap :call expand_var() + nmap q (go-debug-stop) + endif + if has_key(debugwindows, "stack") && debugwindows['stack'] != '' exe 'silent ' . debugwindows['stack'] silent file `='__GODEBUG_STACKTRACE__'` @@ -428,39 +437,23 @@ function! s:start_cb() abort nmap q (go-debug-stop) endif - if has_key(debugwindows, "out") && debugwindows['out'] != '' - exe 'silent ' . debugwindows['out'] - silent file `='__GODEBUG_OUTPUT__'` - setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline - setlocal filetype=godebugoutput - nmap q (go-debug-stop) - endif - if has_key(debugwindows, "goroutines") && debugwindows['goroutines'] != '' exe 'silent ' . debugwindows['goroutines'] - let l:goroutine_win_id = win_getid() silent file `='__GODEBUG_GOROUTINES__'` setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline setlocal filetype=godebugvariables call append(0, ["# Goroutines"]) nmap :call go#debug#Goroutine() endif - if has_key(debugwindows, "vars") && debugwindows['vars'] != '' - exe 'silent ' . debugwindows['vars'] - silent file `='__GODEBUG_VARIABLES__'` + + if has_key(debugwindows, "out") && debugwindows['out'] != '' + exe 'silent ' . debugwindows['out'] + silent file `='__GODEBUG_OUTPUT__'` setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline - setlocal filetype=godebugvariables - call append(0, ["# Local Variables", "", "# Function Arguments"]) - nmap :call expand_var() + setlocal filetype=godebugoutput nmap q (go-debug-stop) endif - call win_gotoid(l:winid) - exe "resize +12" - if l:goroutine_win_id != 0 - call win_gotoid(l:goroutine_win_id) - exe "resize +6" - endif silent! delcommand GoDebugStart silent! delcommand GoDebugTest @@ -489,8 +482,6 @@ function! s:start_cb() abort set ballooneval endif - call win_gotoid(l:winid) - augroup vim-go-debug autocmd! * autocmd FileType go nmap (go-debug-continue) @@ -789,57 +780,78 @@ function! go#debug#Print(arg) abort endfunction function! s:update_goroutines() abort - let l:var_win = bufwinnr(bufnr('__GODEBUG_GOROUTINES__')) - if l:var_win == -1 + try + let l:res = s:call_jsonrpc('RPCServer.State') + let l:currentGoroutineID = 0 + try + let l:currentGoroutineID = l:res["result"]["State"]["currentGoroutine"]["id"] + catch + call go#util#EchoWarning("current goroutine not found...") + endtry + + let l:res = s:call_jsonrpc('RPCServer.ListGoroutines') + call s:show_goroutines(l:currentGoroutineID, l:res) + catch + call go#util#EchoError(v:exception) + endtry + endfunction + +function! s:show_goroutines(currentGoroutineID, res) abort + let l:goroutines_winid = bufwinid('__GODEBUG_GOROUTINES__') + if l:goroutines_winid == -1 return endif - let l:cur_win = bufwinnr('') - exe l:var_win 'wincmd w' + let l:winid = win_getid() + call win_gotoid(l:goroutines_winid) try setlocal modifiable silent %delete _ - let v = [] - let v += ['# Goroutines'] - let l:res = s:call_jsonrpc('RPCServer.State') - let l:currentGoroutineId = 0 - try - let l:currentGoroutineId = l:res["result"]["State"]["currentGoroutine"]["id"] - catch - call go#util#EchoInfo("current goroutineId not found...") - endtry - let l:res = s:call_jsonrpc('RPCServer.ListGoroutines') - let l:goroutines = l:res["result"]["Goroutines"] + let v = ['# Goroutines'] + + if !has_key(a:res, 'result') + call setline(1, v) + return + endif + + let l:goroutines = a:res["result"]["Goroutines"] if len(l:goroutines) == 0 - call go#util#EchoError("No Goroutines Running Now...") + call go#util#EchoWarning("No Goroutines Running Now...") + call setline(1, v) return endif + for l:idx in range(len(l:goroutines)) let l:goroutine = l:goroutines[l:idx] - let l:goroutineType="" - let l:loc=0 + let l:goroutineType = "" + let l:loc = 0 if l:goroutine.startLoc.file != "" - let l:loc=l:goroutine.startLoc + let l:loc = l:goroutine.startLoc let l:goroutineType = "Start" endif if l:goroutine.goStatementLoc.file != "" - let l:loc=l:goroutine.goStatementLoc + let l:loc = l:goroutine.goStatementLoc let l:goroutineType = "Go" endif if l:goroutine.currentLoc.file != "" - let l:loc=l:goroutine.currentLoc + let l:loc = l:goroutine.currentLoc let l:goroutineType = "Runtime" endif if l:goroutine.userCurrentLoc.file != "" let l:loc=l:goroutine.userCurrentLoc let l:goroutineType = "User" endif - if l:goroutine.id == l:currentGoroutineId - let l:g = "* Goroutine " . l:goroutine.id . " - " . l:goroutineType . ": " . l:loc.file . ":" . l:loc.line . " " . l:loc.function.name . " " . " (thread: " . l:goroutine.threadID . ")" + + " The current goroutine can be changed by pressing enter on one of the + " lines listing a non-active goroutine. If the format of either of these + " lines is modified, then make sure that go#debug#Goroutine is also + " changed if needed. + if l:goroutine.id == a:currentGoroutineID + let l:g = printf("* Goroutine %s - %s: %s:%s %s (thread: %s)", l:goroutine.id, l:goroutineType, l:loc.file, l:loc.line, l:loc.function.name, l:goroutine.threadID) else - let l:g = " Goroutine " . l:goroutine.id . " - " . l:goroutineType . ": " . l:loc.file . ":" . l:loc.line . " " . l:loc.function.name . " " . " (thread: " . l:goroutine.threadID . ")" + let l:g = printf(" Goroutine %s - %s: %s:%s %s (thread: %s)", l:goroutine.id, l:goroutineType, l:loc.file, l:loc.line, l:loc.function.name, l:goroutine.threadID) endif let v += [l:g] endfor @@ -847,9 +859,10 @@ function! s:update_goroutines() abort call setline(1, v) finally setlocal nomodifiable - exe l:cur_win 'wincmd w' + call win_gotoid(l:winid) endtry endfunction + function! s:update_variables() abort " FollowPointers requests pointers to be automatically dereferenced. " MaxVariableRecurse is how far to recurse when evaluating nested types. @@ -857,7 +870,7 @@ function! s:update_variables() abort " MaxArrayValues is the maximum number of elements read from an array, a slice or a map. " MaxStructFields is the maximum number of fields read from a struct, -1 will read all fields. let l:cfg = { - \ 'scope': {'GoroutineID': s:groutineID()}, + \ 'scope': {'GoroutineID': s:goroutineID()}, \ 'cfg': {'MaxStringLen': 20, 'MaxArrayValues': 20} \ } @@ -895,7 +908,7 @@ endfunction function! s:update_stacktrace() abort try - let l:res = s:call_jsonrpc('RPCServer.Stacktrace', {'id': s:groutineID(), 'depth': 5}) + let l:res = s:call_jsonrpc('RPCServer.Stacktrace', {'id': s:goroutineID(), 'depth': 5}) call s:show_stacktrace(l:res) catch call go#util#EchoError(v:exception) @@ -908,10 +921,11 @@ function! s:stack_cb(res) abort if empty(a:res) || !has_key(a:res, 'result') return endif + call s:update_breakpoint(a:res) + call s:update_goroutines() call s:update_stacktrace() call s:update_variables() - call s:update_goroutines() endfunction " Send a command to change the cursor location to Delve. @@ -993,11 +1007,16 @@ endfunction " Change Goroutine function! go#debug#Goroutine() abort - let l:goroutineId = split(getline('.'), " ")[1] + let l:goroutineID = substitute(getline('.'), '^ Goroutine \(.\{-1,\}\) - .*', '\1', 'g') + + if l:goroutineID <= 0 + return + endif + try - let l:res = s:call_jsonrpc('RPCServer.Command', {'Name': 'switchGoroutine', 'GoroutineID': str2nr(l:goroutineId)}) + let l:res = s:call_jsonrpc('RPCServer.Command', {'Name': 'switchGoroutine', 'GoroutineID': str2nr(l:goroutineID)}) call s:stack_cb(l:res) - call go#util#EchoInfo("Switched Goroutine to: " . l:goroutineId) + call go#util#EchoInfo("Switched goroutine to: " . l:goroutineID) catch call go#util#EchoError(v:exception) endtry diff --git a/doc/vim-go.txt b/doc/vim-go.txt index 4100b17658..4d903a6979 100644 --- a/doc/vim-go.txt +++ b/doc/vim-go.txt @@ -2170,17 +2170,20 @@ DEBUGGER SETTINGS~ *'g:go_debug_windows'* -Controls the window layout for debugging mode. This is a |dict| with three -possible keys: "stack", "out", and "vars"; the windows will created in that -order with the commands in the value. +Controls the window layout for debugging mode. This is a |dict| with four +possible keys: "vars", "stack", "goroutines", and "out"; each of the new +windows will be created in that that order with the commands in the value. The +current window is made the only window before creating the debug windows. + A window will not be created if a key is missing or empty. Defaults: > let g:go_debug_windows = { - \ 'stack': 'leftabove 20vnew', - \ 'out': 'botright 10new', - \ 'vars': 'leftabove 30vnew', + \ 'vars': 'leftabove 30vnew', + \ 'stack': 'leftabove 20new', + \ 'goroutines': 'botright 10new', + \ 'out': 'botright 5new', \ } < Show only variables on the right-hand side: > From f5c4760126115473ad426ce68305d0301fd64cb4 Mon Sep 17 00:00:00 2001 From: Billie Cleek Date: Wed, 11 Sep 2019 10:21:05 -0700 Subject: [PATCH 10/10] debug: fix tests Now there's a new window in play, the expected buffer number is different. Update the debug tests to be less fragile in the face of changes. --- autoload/go/debug_test.vim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/autoload/go/debug_test.vim b/autoload/go/debug_test.vim index b9aeb2a86f..36ba6b4297 100644 --- a/autoload/go/debug_test.vim +++ b/autoload/go/debug_test.vim @@ -24,14 +24,15 @@ function! Test_GoDebugStart_Errors() abort endif try + let l:tmp = gotest#load_fixture('debug/compilerror/main.go') + let l:expected = [ \ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 0, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': '# debug/compilerror'}, - \ {'lnum': 6, 'bufnr': 7, 'col': 22, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': ' syntax error: unexpected newline, expecting comma or )'}, + \ {'lnum': 6, 'bufnr': bufnr('%'), 'col': 22, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': ' syntax error: unexpected newline, expecting comma or )'}, \ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 0, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exit status 2'} \] call setqflist([], 'r') - let l:tmp = gotest#load_fixture('debug/compilerror/main.go') call assert_false(exists(':GoDebugStop')) let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'