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.
function! s:readNewLinesFromPipe(pipeObject)
"read
let l:text=a:pipeObject.pipe.read(1000,0)
while 0<len(l:text)
let a:pipeObject.buffer.=l:text
let l:text=a:pipeObject.pipe.read(1000,0)
"Read bytes from pipe to buffer
function! s:fillBufferFromPipe(pipeObject)
let l:text = a:pipeObject.pipe.read(1024, 0)
let l:totalBytesRead = 0
while 0 < len(l:text)
let l:totalBytesRead += len(l:text)
let a:pipeObject.buffer .= l:text
let l:text = a:pipeObject.pipe.read(1024, 0)
endwhile
"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 []
return l:totalBytesRead
endfunction
let s:f_debugger={}
@ -41,16 +33,19 @@ endfunction
"Check for new lines from the debugger's interactive shell and handle them
function! s:f_debugger.invokeReading() dict
let l:newLines={}
let l:newLines = {}
for l:k in keys(self.pipes)
let l:nl=s:readNewLinesFromPipe(self.pipes[l:k])
if 0<len(l:nl)
let l:newLines[l:k]=l:nl
let l:pipe = self.pipes[l:k]
if 0 < s:fillBufferFromPipe(l:pipe)
let l:nl = l:pipe.bufferer()
if 0 < len(l:nl)
let l:newLines[l:k] = l:nl
endif
endif
endfor
for l:k in keys(l:newLines)
for l:line in l:newLines[l:k]
call self.handleLine(l:k,l:line)
call self.handleLine(l:k, l:line)
endfor
endfor
@ -249,9 +244,14 @@ function! vebugger#createDebugger(command)
let l:debugger.outBuffer=''
let l:debugger.errBuffer=''
let l:debugger.pipes={
\'out':{'pipe':(l:debugger.shell.stdout),'buffer':''},
\'err':{'pipe':(l:debugger.shell.stderr),'buffer':'','annotation':"err:\t\t"}}
let l:debugger.pipes = {
\ 'out': {'pipe':(l:debugger.shell.stdout), 'buffer': ''},
\ '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.state={}

View File

@ -1,6 +1,7 @@
function! vebugger#gdb#start(binaryFile,args)
let l:debugger=vebugger#std#startDebugger(shellescape(vebugger#util#getToolFullPath('gdb',get(a:args,'version'),'gdb'))
\.' -i mi --silent '.fnameescape(a:binaryFile))
let l:debugger.pipes.out.bufferer = function('vebugger#gdb#readMILinesFromPipe')
let l:debugger.state.gdb={}
@ -57,6 +58,61 @@ function! s:findFolderFromStackTrace(src,nameFromStackTrace)
return l:path
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)
if 'err'==a:pipeName
\&&a:line!~'\v^[=~*&^]'
@ -67,7 +123,6 @@ endfunction
function! vebugger#gdb#_readWhere(pipeName,line,readResult,debugger)
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+)"')
if 2<len(l:matches)
let l:file=l:matches[1]
@ -80,7 +135,7 @@ function! vebugger#gdb#_readWhere(pipeName,line,readResult,debugger)
endfunction
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}
endif
endfunction
@ -127,9 +182,9 @@ function! vebugger#gdb#_executeStatements(writeAction,debugger)
endfunction
function! vebugger#gdb#_readEvaluatedExpressions(pipeName,line,readResult,debugger) dict
if 'out'==a:pipeName
if has_key(self,'nextExpressionToBePrinted')
let l:matches=matchlist(a:line,'\v^\~"\$(\d+) \= (.*)"$')
if 'out' == a:pipeName
if has_key(self, 'nextExpressionToBePrinted')
let l:matches=matchlist(a:line,'\v^\~\$(\d+) \= (.*)$')
if 2<len(l:matches)
let l:expression=l:matches[1]
let l:value=l:matches[2]
@ -139,7 +194,7 @@ function! vebugger#gdb#_readEvaluatedExpressions(pipeName,line,readResult,debugg
endif
call remove(self,'nextExpressionToBePrinted')
else
let l:matches=matchlist(a:line,'\v^\&"print (.{-})(\\r)?(\\n)?"$')
let l:matches=matchlist(a:line,'\v^\&print (.+)$')
if 1<len(l:matches)
let self.nextExpressionToBePrinted=s:unescapeString(l:matches[1])
endif

View File

@ -1,5 +1,12 @@
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
function! vebugger#std#setStandardState(debugger)
let a:debugger.state.std={
@ -55,6 +62,7 @@ endfunction
"Performs the standard initialization of the debugger object
function! vebugger#std#standardInit(debugger)
call vebugger#std#setStandardBufferers(a:debugger)
call vebugger#std#setStandardState(a:debugger)
call vebugger#std#setStandardReadResultTemplate(a:debugger)
call vebugger#std#setStandardWriteactionsTemplate(a:debugger)
@ -73,6 +81,19 @@ function! vebugger#std#startDebugger(command)
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
"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

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
debugger frontend implementation can also define it's own namespace for
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')
<