Fix #25 - pre-process GDB's MI output

This commit is contained in:
IdanArye 2016-02-17 00:32:15 +02:00
parent fa84d5c0a5
commit 3ef3f45016
4 changed files with 130 additions and 31 deletions

View File

@ -1,21 +1,13 @@
"Read and return all new lines from a Vebugger pipe object. "Read bytes from pipe to buffer
function! s:readNewLinesFromPipe(pipeObject) function! s:fillBufferFromPipe(pipeObject)
"read let l:text = a:pipeObject.pipe.read(1024, 0)
let l:text=a:pipeObject.pipe.read(1000,0) let l:totalBytesRead = 0
while 0 < len(l:text) while 0 < len(l:text)
let l:totalBytesRead += len(l:text)
let a:pipeObject.buffer .= l:text let a:pipeObject.buffer .= l:text
let l:text=a:pipeObject.pipe.read(1000,0) let l:text = a:pipeObject.pipe.read(1024, 0)
endwhile endwhile
return l:totalBytesRead
"parse
let l:lastNewline=strridx(a:pipeObject.buffer,"\n")
if 0<=l:lastNewline
let l:outLines=split(strpart(a:pipeObject.buffer,0,l:lastNewline),'\r\n\|\n\|\r')
let a:pipeObject.buffer=strpart(a:pipeObject.buffer,l:lastNewline+1)
return l:outLines
endif
return []
endfunction endfunction
let s:f_debugger={} let s:f_debugger={}
@ -43,10 +35,13 @@ endfunction
function! s:f_debugger.invokeReading() dict function! s:f_debugger.invokeReading() dict
let l:newLines = {} let l:newLines = {}
for l:k in keys(self.pipes) for l:k in keys(self.pipes)
let l:nl=s:readNewLinesFromPipe(self.pipes[l:k]) let l:pipe = self.pipes[l:k]
if 0 < s:fillBufferFromPipe(l:pipe)
let l:nl = l:pipe.bufferer()
if 0 < len(l:nl) if 0 < len(l:nl)
let l:newLines[l:k] = l:nl let l:newLines[l:k] = l:nl
endif endif
endif
endfor endfor
for l:k in keys(l:newLines) for l:k in keys(l:newLines)
for l:line in l:newLines[l:k] for l:line in l:newLines[l:k]
@ -252,6 +247,11 @@ function! vebugger#createDebugger(command)
let l:debugger.pipes = { let l:debugger.pipes = {
\ 'out': {'pipe':(l:debugger.shell.stdout), 'buffer': ''}, \ 'out': {'pipe':(l:debugger.shell.stdout), 'buffer': ''},
\ 'err': {'pipe':(l:debugger.shell.stderr), 'buffer': '', 'annotation': "err:\t\t"}} \ 'err': {'pipe':(l:debugger.shell.stderr), 'buffer': '', 'annotation': "err:\t\t"}}
for l:pipe in values(l:debugger.pipes)
"let l:pipe.buffer = ''
"let l:pipe.readIntoBuffer = function('vebugger#readIntoBuffer')
"let l:pipe.bufferer = function('vebugger#readNewLinesFromPipe')
endfor
let l:debugger.readResultTemplate={} let l:debugger.readResultTemplate={}
let l:debugger.state={} let l:debugger.state={}

View File

@ -1,6 +1,7 @@
function! vebugger#gdb#start(binaryFile,args) function! vebugger#gdb#start(binaryFile,args)
let l:debugger=vebugger#std#startDebugger(shellescape(vebugger#util#getToolFullPath('gdb',get(a:args,'version'),'gdb')) let l:debugger=vebugger#std#startDebugger(shellescape(vebugger#util#getToolFullPath('gdb',get(a:args,'version'),'gdb'))
\.' -i mi --silent '.fnameescape(a:binaryFile)) \.' -i mi --silent '.fnameescape(a:binaryFile))
let l:debugger.pipes.out.bufferer = function('vebugger#gdb#readMILinesFromPipe')
let l:debugger.state.gdb={} let l:debugger.state.gdb={}
@ -57,6 +58,61 @@ function! s:findFolderFromStackTrace(src,nameFromStackTrace)
return l:path return l:path
endfunction endfunction
function! vebugger#gdb#readMILinesFromPipe() dict
if !has_key(self, 'tildeBuffer')
let self.tildeBuffer = ''
endif
let l:result = []
while 1
let l:newLinePos = stridx(self.buffer, "\n")
if l:newLinePos < 0
break
endif
let l:firstChar = self.buffer[0]
if l:firstChar == '~'
while self.buffer[0] == '~'
let l:newLinePos = stridx(self.buffer, "\n")
let l:line = self.buffer[0 : l:newLinePos]
let l:line = l:line[2 : strridx(l:line, '"') - 1]
let self.buffer = strpart(self.buffer, l:newLinePos + 1)
"let self.tildeBuffer .= l:line
let l:line = substitute(l:line, '\\n', "\n", "g")
let l:line = substitute(l:line, '\\t', "\t", "g")
let l:line = substitute(l:line, '\\"', '"', "g")
let self.tildeBuffer .= l:line
endwhile
let l:lastTildeNewLine = strridx(self.tildeBuffer, "\n")
if 0 <= l:lastTildeNewLine
let l:newTildeLines = split(self.tildeBuffer[: l:lastTildeNewLine - 1], '\n')
call map(l:newTildeLines, '"~".v:val')
let self.tildeBuffer = strpart(self.tildeBuffer, l:lastTildeNewLine + 1)
call extend(l:result, l:newTildeLines)
endif
continue
else
if !empty(self.tildeBuffer)
call extend(l:result, split(self.tildeBuffer, '\n'))
let self.tildeBuffer = ''
endif
endif
let l:line = self.buffer[0 : l:newLinePos]
if l:firstChar == '&'
let l:line = l:line[2 : strridx(l:line, '"') - 1]
let l:line = substitute(l:line, '\\n', "\n", "g")
let l:line = substitute(l:line, '\\t', "\t", "g")
let l:line = substitute(l:line, '\\"', '"', "g")
let l:line = '&'.l:line
endif
let l:line = substitute(l:line, '\v[\n\r]+$', '', "")
call add(l:result, l:line)
let self.buffer = strpart(self.buffer, l:newLinePos + 1)
endwhile
return l:result
endfunction
function! vebugger#gdb#_readProgramOutput(pipeName,line,readResult,debugger) function! vebugger#gdb#_readProgramOutput(pipeName,line,readResult,debugger)
if 'err'==a:pipeName if 'err'==a:pipeName
\&&a:line!~'\v^[=~*&^]' \&&a:line!~'\v^[=~*&^]'
@ -67,7 +123,6 @@ endfunction
function! vebugger#gdb#_readWhere(pipeName,line,readResult,debugger) function! vebugger#gdb#_readWhere(pipeName,line,readResult,debugger)
if 'out'==a:pipeName if 'out'==a:pipeName
"let l:matches=matchlist(a:line,'\v^\~"#(\d+)\s+(.+)\s+\(.*\)\s+at\s+([^:]+):(\d+)')
let l:matches=matchlist(a:line,'\v^\*stopped.*fullname\=\"([^"]+)\",line\=\"(\d+)"') let l:matches=matchlist(a:line,'\v^\*stopped.*fullname\=\"([^"]+)\",line\=\"(\d+)"')
if 2<len(l:matches) if 2<len(l:matches)
let l:file=l:matches[1] let l:file=l:matches[1]
@ -80,7 +135,7 @@ function! vebugger#gdb#_readWhere(pipeName,line,readResult,debugger)
endfunction endfunction
function! vebugger#gdb#_readFinish(pipeName,line,readResult,debugger) function! vebugger#gdb#_readFinish(pipeName,line,readResult,debugger)
if a:line=~'\c\V\^~"[Inferior \.\*exited normally]' if a:line=~'\c\V*stopped\.\*exited\[ -]normally'
let a:readResult.std.programFinish={'finish':1} let a:readResult.std.programFinish={'finish':1}
endif endif
endfunction endfunction
@ -129,7 +184,7 @@ endfunction
function! vebugger#gdb#_readEvaluatedExpressions(pipeName,line,readResult,debugger) dict function! vebugger#gdb#_readEvaluatedExpressions(pipeName,line,readResult,debugger) dict
if 'out' == a:pipeName if 'out' == a:pipeName
if has_key(self, 'nextExpressionToBePrinted') if has_key(self, 'nextExpressionToBePrinted')
let l:matches=matchlist(a:line,'\v^\~"\$(\d+) \= (.*)"$') let l:matches=matchlist(a:line,'\v^\~\$(\d+) \= (.*)$')
if 2<len(l:matches) if 2<len(l:matches)
let l:expression=l:matches[1] let l:expression=l:matches[1]
let l:value=l:matches[2] let l:value=l:matches[2]
@ -139,7 +194,7 @@ function! vebugger#gdb#_readEvaluatedExpressions(pipeName,line,readResult,debugg
endif endif
call remove(self,'nextExpressionToBePrinted') call remove(self,'nextExpressionToBePrinted')
else else
let l:matches=matchlist(a:line,'\v^\&"print (.{-})(\\r)?(\\n)?"$') let l:matches=matchlist(a:line,'\v^\&print (.+)$')
if 1<len(l:matches) if 1<len(l:matches)
let self.nextExpressionToBePrinted=s:unescapeString(l:matches[1]) let self.nextExpressionToBePrinted=s:unescapeString(l:matches[1])
endif endif

View File

@ -1,5 +1,12 @@
let g:vebugger_breakpoints=[] let g:vebugger_breakpoints=[]
"Initialize the default pipe bufferers
function! vebugger#std#setStandardBufferers(debugger)
for l:pipe in values(a:debugger.pipes)
let l:pipe.bufferer = function('vebugger#std#readNewLinesFromPipe')
endfor
endfunction
"Initialize the std part of the debugger's state "Initialize the std part of the debugger's state
function! vebugger#std#setStandardState(debugger) function! vebugger#std#setStandardState(debugger)
let a:debugger.state.std={ let a:debugger.state.std={
@ -55,6 +62,7 @@ endfunction
"Performs the standard initialization of the debugger object "Performs the standard initialization of the debugger object
function! vebugger#std#standardInit(debugger) function! vebugger#std#standardInit(debugger)
call vebugger#std#setStandardBufferers(a:debugger)
call vebugger#std#setStandardState(a:debugger) call vebugger#std#setStandardState(a:debugger)
call vebugger#std#setStandardReadResultTemplate(a:debugger) call vebugger#std#setStandardReadResultTemplate(a:debugger)
call vebugger#std#setStandardWriteactionsTemplate(a:debugger) call vebugger#std#setStandardWriteactionsTemplate(a:debugger)
@ -73,6 +81,19 @@ function! vebugger#std#startDebugger(command)
endfunction endfunction
"Read and return all new lines from a Vebugger pipe object.
function! vebugger#std#readNewLinesFromPipe() dict
let l:lastNewline = strridx(self.buffer, "\n")
if 0 <= l:lastNewline
let l:outLines = split(strpart(self.buffer, 0, l:lastNewline), '\r\n\|\n\|\r')
let self.buffer = strpart(self.buffer, l:lastNewline + 1)
return l:outLines
endif
return []
endfunction
"Opens the shell buffer for a debugger. The shell buffer displays the output "Opens the shell buffer for a debugger. The shell buffer displays the output
"of the debugged program, and when it's closed the debugger gets terminated. "of the debugged program, and when it's closed the debugger gets terminated.
"Shell buffers should not be used when attaching a debugger to a running "Shell buffers should not be used when attaching a debugger to a running

View File

@ -493,3 +493,26 @@ defines the "std" namespace, and plugins built on top of Vebugger can define a
namespaced named after the plugin to contain their own read results. A namespaced named after the plugin to contain their own read results. A
debugger frontend implementation can also define it's own namespace for debugger frontend implementation can also define it's own namespace for
storing debugger-specific data and configuration. storing debugger-specific data and configuration.
COMPILER OUTPUT BUFFERERS *vebugger-architecture-bufferers*
Read handlers receive lines created by the debugger output bufferer. There is
only one for each debugger, and if you just want to get the debugger output
line-by-line "as is" you can just use the default one and ignore this section.
The debugger bufferer is a dict function(|:func-dict|) that operates on pipe
objects. It will be called when the pipe object's "self.buffer", which
represents everything read from a debugger's output stream, has new data.
The bufferer function should:
* return a list of new lines that will be send to the read handlers, or an
empty list if there are no new lines.
* "advance" the buffer - delete from it's head everything that was handled.
This can easily be done with |strpart()|.
Bufferers should be set individually for each pipe, usually in the debugger
creation function:
Example: >
let l:debugger.pipes.out.bufferer = function('g:myCustomBufferer')
<