mirror of
https://github.com/vim/vim.git
synced 2026-05-28 00:21:37 +02:00
patch 9.2.0143: termdebug: no support for thread and condition in :Break
Problem: termdebug :Break does not support `thread` and `if` arguments
Solution: extend :Break and :Tbreak to accept optional location, thread
{nr}, and if {expr} arguments (Yinzuo Jiang).
closes: #19613
Signed-off-by: Yinzuo Jiang <jiangyinzuo@foxmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
401f9aeddf
commit
5890ea5397
@@ -1,4 +1,4 @@
|
||||
*terminal.txt* For Vim version 9.2. Last change: 2026 Feb 24
|
||||
*terminal.txt* For Vim version 9.2. Last change: 2026 Mar 12
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -1427,11 +1427,25 @@ gdb:
|
||||
`:Arguments` {args} set arguments for the next `:Run`
|
||||
|
||||
*:Break* set a breakpoint at the cursor position
|
||||
:Break {position}
|
||||
:Break [{position}] [thread {nr}] [if {expr}]
|
||||
set a breakpoint at the specified position
|
||||
if {position} is omitted, use the current file and line
|
||||
thread {nr} limits the breakpoint to one thread
|
||||
if {expr} sets a conditional breakpoint
|
||||
Examples: >
|
||||
:Break if argc == 1
|
||||
:Break 42 thread 3 if x > 10
|
||||
:Break main
|
||||
<
|
||||
*:Tbreak* set a temporary breakpoint at the cursor position
|
||||
:Tbreak {position}
|
||||
set a temporary breakpoint at the specified position
|
||||
:Tbreak [{position}] [thread {nr}] [if {expr}]
|
||||
like `:Break`, but the breakpoint is deleted after
|
||||
it is hit once
|
||||
Examples: >
|
||||
:Tbreak if argc == 1
|
||||
:Tbreak 42 thread 3 if x > 10
|
||||
:Tbreak main
|
||||
<
|
||||
*:Clear* delete the breakpoint at the cursor position
|
||||
*:ToggleBreak* set a breakpoint at the cursor position or delete all
|
||||
breakpoints at the cursor position
|
||||
|
||||
+83
-10
@@ -4,7 +4,7 @@ vim9script
|
||||
|
||||
# Author: Bram Moolenaar
|
||||
# Copyright: Vim license applies, see ":help license"
|
||||
# Last Change: 2025 Dec 26
|
||||
# Last Change: 2026 Mar 11
|
||||
# Converted to Vim9: Ubaldo Tiberi <ubaldo.tiberi@gmail.com>
|
||||
|
||||
# WORK IN PROGRESS - The basics works stable, more to come
|
||||
@@ -1592,6 +1592,87 @@ def QuoteArg(x: string): string
|
||||
return printf('"%s"', x ->substitute('[\\"]', '\\&', 'g'))
|
||||
enddef
|
||||
|
||||
def DefaultBreakpointLocation(): string
|
||||
# Use the fname:lnum format, older gdb can't handle --source.
|
||||
var fname = Remote2LocalPath(expand('%:p'))
|
||||
return QuoteArg($"{fname}:{line('.')}")
|
||||
enddef
|
||||
|
||||
def TokenizeBreakpointArguments(args: string): list<dict<any>>
|
||||
var tokens: list<dict<any>> = []
|
||||
var start = -1
|
||||
var escaped = false
|
||||
var in_quotes = false
|
||||
|
||||
var i = 0
|
||||
for ch in args
|
||||
if start < 0 && ch !~ '\s'
|
||||
start = i
|
||||
endif
|
||||
if start >= 0
|
||||
if escaped
|
||||
escaped = false
|
||||
elseif ch == '\'
|
||||
escaped = true
|
||||
elseif ch == '"'
|
||||
in_quotes = !in_quotes
|
||||
elseif !in_quotes && ch =~ '\s'
|
||||
tokens->add({text: args[start : i - 1], start: start, end: i - 1})
|
||||
start = -1
|
||||
endif
|
||||
endif
|
||||
i += 1
|
||||
endfor
|
||||
|
||||
if start >= 0
|
||||
tokens->add({text: args[start :], start: start, end: i - 1})
|
||||
endif
|
||||
return tokens
|
||||
enddef
|
||||
|
||||
def BuildBreakpointCommand(at: string, tbreak=false): string
|
||||
var args = trim(at)
|
||||
var cmd = '-break-insert'
|
||||
if tbreak
|
||||
cmd ..= ' -t'
|
||||
endif
|
||||
|
||||
if empty(args)
|
||||
return $'{cmd} {DefaultBreakpointLocation()}'
|
||||
endif
|
||||
|
||||
var condition = ''
|
||||
var prefix = args
|
||||
for token in TokenizeBreakpointArguments(args)
|
||||
if token.text == 'if' && token.end < strchars(args) - 1
|
||||
condition = trim(args[token.end + 1 :])
|
||||
prefix = token.start > 0 ? trim(args[: token.start - 1]) : ''
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
var prefix_tokens = TokenizeBreakpointArguments(prefix)
|
||||
var location = prefix
|
||||
var thread = ''
|
||||
if len(prefix_tokens) >= 2
|
||||
&& prefix_tokens[-2].text == 'thread'
|
||||
&& prefix_tokens[-1].text =~ '^\d\+$'
|
||||
thread = prefix_tokens[-1].text
|
||||
location = join(prefix_tokens[: -3]->mapnew('v:val.text'), ' ')
|
||||
endif
|
||||
|
||||
if empty(trim(location))
|
||||
location = DefaultBreakpointLocation()
|
||||
endif
|
||||
if !empty(thread)
|
||||
cmd ..= $' -p {thread}'
|
||||
endif
|
||||
if !empty(condition)
|
||||
cmd ..= $' -c {QuoteArg(condition)}'
|
||||
endif
|
||||
return $'{cmd} {trim(location)}'
|
||||
enddef
|
||||
|
||||
# :Until - Execute until past a specified position or current line
|
||||
def Until(at: string)
|
||||
|
||||
@@ -1620,15 +1701,7 @@ def SetBreakpoint(at: string, tbreak=false)
|
||||
sleep 10m
|
||||
endif
|
||||
|
||||
# Use the fname:lnum format, older gdb can't handle --source.
|
||||
var fname = Remote2LocalPath(expand('%:p'))
|
||||
var AT = empty(at) ? QuoteArg($"{fname}:{line('.')}") : at
|
||||
var cmd = ''
|
||||
if tbreak
|
||||
cmd = $'-break-insert -t {AT}'
|
||||
else
|
||||
cmd = $'-break-insert {AT}'
|
||||
endif
|
||||
var cmd = BuildBreakpointCommand(at, tbreak)
|
||||
SendCommand(cmd)
|
||||
if do_continue
|
||||
ContinueCommand()
|
||||
|
||||
@@ -55,6 +55,96 @@ endfunction
|
||||
|
||||
packadd termdebug
|
||||
|
||||
func s:GetTermdebugFunction(name)
|
||||
for line in execute('scriptnames')->split("\n")
|
||||
if line =~# 'termdebug/plugin/termdebug\.vim$'
|
||||
let sid = matchstr(line, '^\s*\zs\d\+')
|
||||
return function('<SNR>' .. sid .. '_' .. a:name)
|
||||
endif
|
||||
endfor
|
||||
throw 'termdebug script not found'
|
||||
endfunc
|
||||
|
||||
func Test_termdebug_break_command_builder()
|
||||
let bin_name = 'XTD_break_cmd'
|
||||
let src_name = bin_name .. '.c'
|
||||
let BuildBreakpointCommand = s:GetTermdebugFunction('BuildBreakpointCommand')
|
||||
call s:generate_files(bin_name)
|
||||
|
||||
execute 'edit ' .. src_name
|
||||
call cursor(22, 1)
|
||||
let here = '"' .. fnamemodify(src_name, ':p') .. ':22"'
|
||||
|
||||
call assert_equal('-break-insert ' .. here, BuildBreakpointCommand('', v:false))
|
||||
call assert_equal('-break-insert -t ' .. here, BuildBreakpointCommand('', v:true))
|
||||
call assert_equal('-break-insert -c "argc == 1" ' .. here,
|
||||
\ BuildBreakpointCommand('if argc == 1', v:false))
|
||||
call assert_equal('-break-insert -p 2 ' .. here,
|
||||
\ BuildBreakpointCommand('thread 2', v:false))
|
||||
call assert_equal('-break-insert -p 2 -c "argc == 1" ' .. here,
|
||||
\ BuildBreakpointCommand('thread 2 if argc == 1', v:false))
|
||||
call assert_equal('-break-insert -p 2 -c "argc == 1" 22',
|
||||
\ BuildBreakpointCommand('22 thread 2 if argc == 1', v:false))
|
||||
call assert_equal('-break-insert -c "argc == 1" 22',
|
||||
\ BuildBreakpointCommand('22 if argc == 1', v:false))
|
||||
call assert_equal('-break-insert -c "é == 1" 断点',
|
||||
\ BuildBreakpointCommand('断点 if é == 1', v:false))
|
||||
call assert_equal('-break-insert -p 2 断点',
|
||||
\ BuildBreakpointCommand('断点 thread 2', v:false))
|
||||
call assert_equal('-break-insert 断点 if',
|
||||
\ BuildBreakpointCommand('断点 if', v:false))
|
||||
call assert_equal('-break-insert 断点 thread 2 if',
|
||||
\ BuildBreakpointCommand('断点 thread 2 if', v:false))
|
||||
call assert_equal('-break-insert foo\ if\ bar',
|
||||
\ BuildBreakpointCommand('foo\ if\ bar', v:false))
|
||||
|
||||
call s:cleanup_files(bin_name)
|
||||
%bw!
|
||||
endfunc
|
||||
|
||||
func Test_termdebug_break_with_default_location_and_condition()
|
||||
let g:test_is_flaky = 1
|
||||
let bin_name = 'XTD_break_if'
|
||||
let src_name = bin_name .. '.c'
|
||||
call s:generate_files(bin_name)
|
||||
|
||||
execute 'edit ' .. src_name
|
||||
execute 'Termdebug ./' .. bin_name
|
||||
call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))})
|
||||
call WaitForAssert({-> assert_equal(3, winnr('$'))})
|
||||
let gdb_buf = winbufnr(1)
|
||||
wincmd b
|
||||
|
||||
call cursor(22, 1)
|
||||
Break if argc == 1
|
||||
call term_wait(gdb_buf)
|
||||
redraw!
|
||||
call WaitForAssert({-> assert_equal([
|
||||
\ {'lnum': 22, 'id': 1014, 'name': 'debugBreakpoint1.0',
|
||||
\ 'priority': 110, 'group': 'TermDebug'}],
|
||||
\ sign_getplaced('', #{group: 'TermDebug'})[0].signs)})
|
||||
|
||||
Run
|
||||
call term_wait(gdb_buf, 400)
|
||||
redraw!
|
||||
call WaitForAssert({-> assert_equal([
|
||||
\ {'lnum': 22, 'id': 12, 'name': 'debugPC', 'priority': 110,
|
||||
\ 'group': 'TermDebug'},
|
||||
\ {'lnum': 22, 'id': 1014, 'name': 'debugBreakpoint1.0',
|
||||
\ 'priority': 110, 'group': 'TermDebug'}],
|
||||
\ sign_getplaced('', #{group: 'TermDebug'})[0].signs)})
|
||||
|
||||
Continue
|
||||
call term_wait(gdb_buf)
|
||||
wincmd t
|
||||
quit!
|
||||
redraw!
|
||||
call WaitForAssert({-> assert_equal(1, winnr('$'))})
|
||||
|
||||
call s:cleanup_files(bin_name)
|
||||
%bw!
|
||||
endfunc
|
||||
|
||||
" should be the first test to run, since it validates the window layout with
|
||||
" win ids
|
||||
func Test_termdebug_basic()
|
||||
|
||||
@@ -734,6 +734,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
143,
|
||||
/**/
|
||||
142,
|
||||
/**/
|
||||
|
||||
Reference in New Issue
Block a user