Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Version 1.0: Initial upload

  • Loading branch information...
commit 5e304ed3ad53961dbb6b1e09224ea89b3ef1f63b 0 parents
@brookhong brookhong authored committed
Showing with 1,512 additions and 0 deletions.
  1. +129 −0 README
  2. +1,193 −0 plugin/dbgpavim.py
  3. +190 −0 plugin/dbgpavim.vim
129 README
@@ -0,0 +1,129 @@
+This is a mirror of http://www.vim.org/scripts/script.php?script_id=4059
+
+This is a plugin to enable php debug in VIM with Xdebug, which originates from http://www.vim.org/scripts/script.php?script_id=1152.
+But most of the code, especially the debugger backend has been rewritten.
+
+Tested with --
+* XDebug 2.2 - PHP 5.4 - GVIM 7.3 - Python 2.7 @ Windows 7
+* XDebug 2.0 - PHP 5.2 - VIM 7.3 - Python 2.7@ Linux
+* XDebug 2.0 - PHP 5.2 - VIM 7.3 - Python 2.3@ Linux
+* XDebug 2.2 - PHP 5.2 - VIM 7.3 - Python 2.7@ Linux
+
+The source code is hosted at https://github.com/brookhong/DBGPavim.
+Some screenshots (under windows) are here at http://sharing-from-brook.16002.n6.nabble.com/Debug-php-in-VIM-with-Xdebug-and-DBGPavim-td4930670.html.
+
+## The enhancements are --
+
+### Non blocking debugger backend.
+So that VIM users do not need to wait for connection from apache server. No timeout things, users press F5 to start debugger backend, and uses his/her VIM normally. Debug backend won't stop users to interact with VIM. Users can press F6 to stop debugger backend anytime.
+
+### Catch all connections from apache server.
+This is very important for a large website, especially for thoes pages who contain AJAX requests. In that case, one reload of a page may trigger dozens of http request, each of them goes to a different URL. The new debugger backend will catch all connections from apache server. Users can debug all of them without missing anyone.
+
+### Break only at breakpoints
+
+ let g:debuggerBreakAtEntry = 0
+
+ The setting will cause debugger backend to break only at breakpoints. Default value is 1, which means it works like before, the debugger backend breaks at entry.
+
+### New commands and function keys
+
+In normal mode
+
+ <F5> => start debugger backend
+ <F6> => stop debugger backend
+ <F8> => toggle debuggerBreakAtEntry, when g:debuggerBreakAtEntry=0, debugger backend breaks only at breakpoints.
+
+ :Bl => to list all breakpoints
+ :Bp => toggle breakpoint on current line
+
+In debuggin mode
+
+ <F1> => toggle help window
+ <F2> => step into
+ <F3> => step over
+ <F4> => step out
+ <F5> => start debugging / run
+ <F6> => stop debugging
+ <F7> => evalute expression and display result. cursor is automatically move to watch window. type line and just press enter.
+ <F9> => toggle layout
+ <F11> => shows all variables
+ <F12> => shows variable on current cursor
+
+ :Pg => to print value of complex variables like $this->savings[3]
+ :Up => goto upper level of stack
+ :Dn => goto lower level of stack
+
+In Watch window
+
+ If you press Enter key at a line which ends with --
+
+ (object) => to get value of an object.
+ (array) => to get value of an array.
+
+ If you press Enter key at a line of output from command :Bl, that breakpoint will be located.
+
+In Stack window
+
+ If you press Enter key at a line, stack level will be set.
+
+### Windows Support
+
+### Status line for debugger backend
+
+ After user press <F5> to start debugger backend, a string like "PHP-bae-LISN" will show up at the right side of status line.
+
+ The status string looks like --
+
+ PHP-<bae|bap>-<LISN|PENDn|CONN|CLSD>
+
+ bae => means Break At Entry
+ bap => means Break only At breakPoints
+
+ LISN => means the debugger backend is listening.
+ PENDn => means there are n connections waiting for debugging.
+ CONN => means debug session has been established, and being debugged.
+ CLSD => means the debugger backend has stopped.
+
+### New layout of windows
+
+## Usage
+
+* Make sure your vim has python(at least 2.3) supported, in vim with command
+
+ :version
+
+ In case of your VIM don't support python, download VIM source package from http://www.vim.org/download.php, then build your own VIM with commands --
+
+ ./configure --prefix=/opt/vim --enable-pythoninterp --with-python-config-dir=/usr/lib/python2.4/config
+ make
+ make install
+
+* Install xdebug for php, and edit php.ini
+
+ zend_extension=path_to_xdebug.so
+ xdebug.remote_enable=1
+
+* Edit your ~/.vimrc
+
+ let g:debuggerPort = 9009
+ let g:debuggerBreakAtEntry = 0
+
+* Edit your apche configure file
+
+ In your VirtualHost section, set debugger port same as the one in your vimrc
+
+ php_value xdebug.remote_port 9009
+
+* Save debugger.py and debugger.vim to your ~/.vim/plugin
+
+* Open your php file, use :Bp to set breakpoints
+
+* Now, press F5 to start debugger backend
+
+* Back to your browser, add XDEBUG_SESSION_START=1 to your URL, for example, http://localhost/index.php?XDEBUG_SESSION_START=1. If you would like to debug from CLI, start your php script like
+
+ php -dxdebug.remote_autostart=1 -dxdebug.remote_port=9009 test.php
+
+If you are tied of adding XDEBUG_SESSION_START=1 in query string, there is a XDEBUG_SESSION helper at http://userscripts.org/scripts/review/132695, a user script for Google Chrome. It also works for Firefox with help of GreaseMonkey.
+
1,193 plugin/dbgpavim.py
@@ -0,0 +1,1193 @@
+# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
+# -*- c--oding: ko_KR.UTF-8 -*-
+# remote PHP debugger : remote debugger interface to DBGp protocol
+#
+# Copyright (c) 2012 Brook Hong
+#
+# The MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify,
+# merge, publish, distribute, sublicense, and/or sell copies of the
+# Software, and to permit persons to whom the Software is furnished
+# to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+#
+# Authors:
+# Brook Hong <hzgmaxwell <at> hotmail.com>
+# The plugin was originally writen by --
+# Seung Woo Shin <segv <at> sayclub.com>
+# I rewrote it with a new debugger engine, please diff this file to find code change.
+
+import os
+import sys
+import vim
+import socket
+import base64
+import traceback
+import xml.dom.minidom
+
+import string
+import time
+from threading import Thread,Lock
+
+class VimWindow:
+ """ wrapper class of window of vim """
+ def __init__(self, name = 'DEBUG_WINDOW'):
+ """ initialize """
+ self.name = name
+ self.method = "new"
+ self.buffer = None
+ self.firstwrite = 1
+ def isprepared(self):
+ """ check window is OK """
+ if self.buffer == None or len(dir(self.buffer)) == 0 or self.getwinnr() == -1:
+ return 0
+ return 1
+ def prepare(self):
+ """ check window is OK, if not then create """
+ if not self.isprepared():
+ self.create(self.method)
+ def before_create(self):
+ vim.command("1wincmd w")
+ def on_create(self):
+ pass
+ def getwinnr(self):
+ return int(vim.eval("bufwinnr('"+self.name+"')"))
+ def focus(self):
+ winnr = self.getwinnr()
+ vim.command(str(winnr) + 'wincmd w')
+ def getWidth(self):
+ return int(vim.eval("winwidth(bufwinnr('"+self.name+"'))"))
+ def getHeight(self):
+ return int(vim.eval("winheight(bufwinnr('"+self.name+"'))"))
+ def xml_on_element(self, node):
+ line = str(node.nodeName)
+ if node.hasAttributes():
+ for (n,v) in node.attributes.items():
+ line += str(' %s=%s' % (n,v))
+ return line
+ def xml_on_attribute(self, node):
+ return str(node.nodeName)
+ def xml_on_entity(self, node):
+ return 'entity node'
+ def xml_on_comment(self, node):
+ return 'comment node'
+ def xml_on_document(self, node):
+ return '#document'
+ def xml_on_document_type(self, node):
+ return 'document type node'
+ def xml_on_notation(self, node):
+ return 'notation node'
+ def xml_on_text(self, node):
+ return node.data
+ def xml_on_processing_instruction(self, node):
+ return 'processing instruction'
+ def xml_on_cdata_section(self, node):
+ return node.data
+
+ def write(self, msg):
+ """ append last """
+ self.prepare()
+ if self.firstwrite == 1:
+ self.firstwrite = 0
+ self.buffer[:] = str(msg).split('\n')
+ else:
+ self.buffer.append(str(msg).split('\n'))
+ self.command('normal G')
+ #self.window.cursor = (len(self.buffer), 1)
+ def create(self, method = 'new'):
+ """ create window """
+ self.method = method
+ self.before_create()
+ vim.command('silent ' + method + ' ' + self.name)
+ #if self.name != 'LOG___WINDOW':
+ vim.command("setlocal buftype=nofile")
+ self.buffer = vim.current.buffer
+ self.width = int( vim.eval("winwidth(0)") )
+ self.height = int( vim.eval("winheight(0)") )
+ self.on_create()
+ def destroy(self):
+ """ destroy window """
+ if self.buffer == None or len(dir(self.buffer)) == 0:
+ return
+ #if self.name == 'LOG___WINDOW':
+ # self.command('hide')
+ #else:
+ self.command('bdelete ' + self.name)
+ self.firstwrite = 1
+ def clean(self):
+ """ clean all datas in buffer """
+ self.prepare()
+ self.buffer[:] = []
+ self.firstwrite = 1
+ def command(self, cmd):
+ """ go to my window & execute command """
+ self.prepare()
+ winnr = self.getwinnr()
+ if winnr != int(vim.eval("winnr()")):
+ vim.command(str(winnr) + 'wincmd w')
+ vim.command(cmd)
+
+ def _xml_stringfy(self, node, level = 0, encoding = None):
+ if node.nodeType == node.ELEMENT_NODE:
+ line = self.xml_on_element(node)
+
+ elif node.nodeType == node.ATTRIBUTE_NODE:
+ line = self.xml_on_attribute(node)
+
+ elif node.nodeType == node.ENTITY_NODE:
+ line = self.xml_on_entity(node)
+
+ elif node.nodeType == node.COMMENT_NODE:
+ line = self.xml_on_comment(node)
+
+ elif node.nodeType == node.DOCUMENT_NODE:
+ line = self.xml_on_document(node)
+
+ elif node.nodeType == node.DOCUMENT_TYPE_NODE:
+ line = self.xml_on_document_type(node)
+
+ elif node.nodeType == node.NOTATION_NODE:
+ line = self.xml_on_notation(node)
+
+ elif node.nodeType == node.PROCESSING_INSTRUCTION_NODE:
+ line = self.xml_on_processing_instruction(node)
+
+ elif node.nodeType == node.CDATA_SECTION_NODE:
+ line = self.xml_on_cdata_section(node)
+
+ elif node.nodeType == node.TEXT_NODE:
+ line = self.xml_on_text(node)
+
+ else:
+ line = 'unknown node type'
+
+ if node.hasChildNodes():
+ #print ''.ljust(level*4) + '{{{' + str(level+1)
+ #print ''.ljust(level*4) + line
+ return self.fixup_childs(line, node, level)
+ else:
+ return self.fixup_single(line, node, level)
+
+ return line
+
+ def fixup_childs(self, line, node, level):
+ line = ''.ljust(level*4) + line + '\n'
+ line += self.xml_stringfy_childs(node, level+1)
+ return line
+ def fixup_single(self, line, node, level):
+ return ''.ljust(level*4) + line + '\n'
+
+ def xml_stringfy(self, xml):
+ return self._xml_stringfy(xml)
+ def xml_stringfy_childs(self, node, level = 0):
+ line = ''
+ for cnode in node.childNodes:
+ line = str(line)
+ line += str(self._xml_stringfy(cnode, level))
+ return line
+
+ def write_xml(self, xml):
+ self.write(self.xml_stringfy(xml))
+ def write_xml_childs(self, xml):
+ self.write(self.xml_stringfy_childs(xml))
+
+class StackWindow(VimWindow):
+ def __init__(self, name = 'STACK_WINDOW'):
+ VimWindow.__init__(self, name)
+ def xml_on_element(self, node):
+ if node.nodeName != 'stack':
+ return VimWindow.xml_on_element(self, node)
+ else:
+ if node.getAttribute('where') != '{main}':
+ fmark = '()'
+ else:
+ fmark = ''
+ if sys.platform == 'win32':
+ fn = node.getAttribute('filename')[8:]
+ else:
+ fn = node.getAttribute('filename')[7:]
+ return str('%-2s %-15s %s:%s' % ( \
+ node.getAttribute('level'), \
+ node.getAttribute('where')+fmark, \
+ fn, \
+ node.getAttribute('lineno')))
+ def on_create(self):
+ self.command('highlight CurStack term=reverse ctermfg=White ctermbg=Red gui=reverse')
+ self.highlight_stack(0)
+ def highlight_stack(self, no):
+ self.command('syntax clear')
+ self.command('syntax region CurStack start="^' +str(no)+ ' " end="$"')
+
+class TraceWindow(VimWindow):
+ def __init__(self, name = 'TRACE_WINDOW'):
+ VimWindow.__init__(self, name)
+ def xml_on_element(self, node):
+ if node.nodeName != 'error':
+ return VimWindow.xml_on_element(self, node)
+ else:
+ desc = ''
+ if node.hasAttribute('code'):
+ desc = ' : '+error_msg[int(node.getAttribute('code'))]
+ return VimWindow.xml_on_element(self, node) + desc
+ def on_create(self):
+ self.command('set nowrap fdm=marker fmr={{{,}}} fdl=0')
+
+class WatchWindow(VimWindow):
+ def __init__(self, name = 'WATCH_WINDOW'):
+ VimWindow.__init__(self, name)
+ def fixup_single(self, line, node, level):
+ return ''.ljust(level*1) + line + '\n'
+ def fixup_childs(self, line, node, level):
+ global z
+ if len(node.childNodes) == 1 and \
+ (node.firstChild.nodeType == node.TEXT_NODE or \
+ node.firstChild.nodeType == node.CDATA_SECTION_NODE):
+ line = str(''.ljust(level*1) + line)
+ encoding = node.getAttribute('encoding')
+ if encoding == 'base64':
+ line += "'" + base64.decodestring(str(node.firstChild.data)) + "';\n"
+ elif encoding == '':
+ line += str(node.firstChild.data) + ';\n'
+ else:
+ line += '(e:'+encoding+') ' + str(node.firstChild.data) + ';\n'
+ else:
+ if level == 0:
+ line = ''.ljust(level*1) + str(line) + ';' + '\n'
+ line += self.xml_stringfy_childs(node, level+1)
+ line += '/*}}}1*/\n'
+ else:
+ line = (''.ljust(level*1) + str(line) + ';').ljust(self.width-20) + ''.ljust(level*1) + '/*{{{' + str(level+1) + '*/' + '\n'
+ line += str(self.xml_stringfy_childs(node, level+1))
+ line += (''.ljust(level*1) + ''.ljust(level*1)).ljust(self.width-20) + ''.ljust(level*1) + '/*}}}' + str(level+1) + '*/\n'
+ return line
+ def xml_on_element(self, node):
+ if node.nodeName == 'property':
+ self.type = node.getAttribute('type')
+
+ name = node.getAttribute('name')
+ fullname = node.getAttribute('fullname')
+ if name == '':
+ name = 'EVAL_RESULT'
+ if fullname == '':
+ fullname = 'EVAL_RESULT'
+
+ if self.type == 'uninitialized':
+ return str(('%-20s' % name) + " = /* uninitialized */'';")
+ else:
+ return str('%-20s' % fullname) + ' = (' + self.type + ') '
+ elif node.nodeName == 'response':
+ return "$command = '" + node.getAttribute('command') + "'"
+ else:
+ return VimWindow.xml_on_element(self, node)
+
+ def xml_on_text(self, node):
+ if self.type == 'string':
+ return "'" + str(node.data) + "'"
+ else:
+ return str(node.data)
+ def xml_on_cdata_section(self, node):
+ if self.type == 'string':
+ return "'" + str(node.data) + "'"
+ else:
+ return str(node.data)
+ def on_create(self):
+ self.write('<?')
+ self.command('inoremap <buffer> <cr> <esc>:python debugger.debugSession.watch_execute()<cr>')
+ self.command('set noai nocin')
+ self.command('set nowrap fdm=manual fmr={{{,}}} ft=php fdl=1')
+ def input(self, mode, arg = ''):
+ self.prepare()
+ line = self.buffer[-1]
+ if line[:len(mode)+1] == '/*{{{1*/ => '+mode+':':
+ self.buffer[-1] = line + arg
+ else:
+ self.buffer.append('/*{{{1*/ => '+mode+': '+arg)
+ self.command('normal G')
+ def get_command(self):
+ line = self.buffer[-1]
+ if line[0:17] == '/*{{{1*/ => exec:':
+ print "exec does not supported by xdebug now."
+ return ('none', '')
+ #return ('exec', line[17:].strip(' '))
+ elif line[0:17] == '/*{{{1*/ => eval:':
+ return ('eval', line[17:].strip(' '))
+ elif line[0:25] == '/*{{{1*/ => property_get:':
+ return ('property_get', line[25:].strip(' '))
+ elif line[0:24] == '/*{{{1*/ => context_get:':
+ return ('context_get', line[24:].strip(' '))
+ else:
+ return ('none', '')
+
+class HelpWindow(VimWindow):
+ def __init__(self, name = 'HELP__WINDOW'):
+ VimWindow.__init__(self, name)
+ def before_create(self):
+ pass
+ def on_create(self):
+ self.write( \
+ '[ Function Keys ] | [ Command Mode ] \n' + \
+ ' <F1> toggle help window | :Bp toggle breakpoint \n' + \
+ ' <F2> step into | :Up stack up \n' + \
+ ' <F3> step over | :Dn stack down \n' + \
+ ' <F4> step out | :Bl list breakpoints \n' + \
+ ' <F5> run | :Pg property get \n' + \
+ ' <F6> quit debugging | <F9> toggle layout \n' + \
+ ' <F7> eval | <F11> get all context \n' + \
+ ' <F8> toggle debuggerBreakAtEntry | <F12> get property at cursor\n' + \
+ ' \n' + \
+ ' For more instructions and latest version, \n' + \
+ ' pleae refer to https://github.com/brookhong/DBGPavim \n' + \
+ '')
+ self.command('1')
+
+class DebugUI:
+ """ DEBUGUI class """
+ (NORMAL, DEBUG) = (0,1)
+ def __init__(self, stackwinHeight, watchwinWidth):
+ """ initialize object """
+ self.watchwin = WatchWindow()
+ self.stackwin = StackWindow()
+ self.stackwinHeight = stackwinHeight
+ self.watchwinWidth = watchwinWidth
+ self.tracewin = None
+ self.helpwin = None
+ self.mode = DebugUI.NORMAL
+ self.file = None
+ self.line = None
+ self.winbuf = {}
+ self.cursign = None
+ if sys.platform == 'win32':
+ self.sessfile = "./debugger_vim_saved_session." + str(os.getpid())
+ else:
+ self.sessfile = "/tmp/debugger_vim_saved_session." + str(os.getpid())
+
+ def trace(self):
+ if self.tracewin:
+ self.tracewin.destroy()
+ self.tracewin = None
+ else:
+ self.tracewin = TraceWindow()
+ self.tracewin.create('belowright new')
+
+ def debug_mode(self):
+ """ change mode to debug """
+ if self.mode == DebugUI.DEBUG:
+ return
+ self.mode = DebugUI.DEBUG
+ # save session
+ vim.command('mksession! ' + self.sessfile)
+ for i in range(1, len(vim.windows)+1):
+ vim.command(str(i)+'wincmd w')
+ self.winbuf[i] = vim.eval('bufnr("%")') # save buffer number, mksession does not do job perfectly
+ # when buffer is not saved at all.
+
+ vim.command('silent topleft new') # create srcview window (winnr=1)
+ for i in range(2, len(vim.windows)+1):
+ vim.command(str(i)+'wincmd w')
+ vim.command('hide')
+ self.create()
+ vim.command('1wincmd w') # goto srcview window(nr=1, top-left)
+ self.cursign = '1'
+
+ self.set_highlight()
+
+ vim.command('call CreateFunctionKeys()')
+ def normal_mode(self):
+ """ restore mode to normal """
+ if self.mode == DebugUI.NORMAL:
+ return
+
+ vim.command('call ClearFunctionKeys()')
+ vim.command('sign unplace 1')
+ vim.command('sign unplace 2')
+
+ # destory all created windows
+ self.destroy()
+
+ # restore session
+ vim.command('silent tabonly')
+ vim.command('source ' + self.sessfile)
+ os.system('rm -f ' + self.sessfile)
+
+ self.set_highlight()
+
+
+ self.winbuf.clear()
+ self.file = None
+ self.line = None
+ self.mode = DebugUI.NORMAL
+ self.cursign = None
+ def create(self):
+ """ create windows """
+ self.stackwin.create('botright '+str(self.stackwinHeight)+' new')
+ self.watchwin.create('vertical belowright '+str(self.watchwinWidth)+' new')
+ def reLayout(self):
+ if self.stackwin.getHeight() != self.stackwinHeight or self.watchwin.getWidth() != self.watchwinWidth:
+ self.stackwin.command("resize "+str(self.stackwinHeight))
+ self.watchwin.command("vertical resize "+str(self.watchwinWidth))
+ else:
+ vim.command("wincmd _")
+ vim.command("wincmd |")
+
+ def set_highlight(self):
+ """ set vim highlight of debugger sign """
+ vim.command("highlight DbgCurrent term=reverse ctermfg=White ctermbg=Red gui=reverse")
+ vim.command("highlight DbgBreakPt term=reverse ctermfg=White ctermbg=Green gui=reverse")
+
+ def help(self):
+ if self.helpwin:
+ self.helpwin.destroy()
+ self.helpwin = None
+ else:
+ self.helpwin = HelpWindow('HELP__WINDOW')
+ self.stackwin.focus()
+ self.helpwin.create('vertical new')
+
+ def destroy(self):
+ """ destroy windows """
+ self.watchwin.destroy()
+ self.stackwin.destroy()
+ if self.tracewin:
+ self.tracewin.destroy()
+ def go_srcview(self):
+ vim.command('1wincmd w')
+ def next_sign(self):
+ if self.cursign == '1':
+ return '2'
+ else:
+ return '1'
+ def set_srcview(self, file, line):
+ """ set srcview windows to file:line and replace current sign """
+
+ if file == self.file and self.line == line:
+ return
+
+ if file != self.file:
+ self.file = file
+ self.go_srcview()
+ vim.command('silent edit ' + file)
+
+ if self.mode == DebugUI.DEBUG:
+ if line == 0:
+ line = 1
+ nextsign = self.next_sign()
+ vim.command('sign place ' + nextsign + ' name=current line='+str(line)+' file='+file)
+ vim.command('sign unplace ' + self.cursign)
+ vim.command('sign jump ' + nextsign + ' file='+file)
+ self.cursign = nextsign
+ else:
+ vim.command(': ' + str(line))
+
+ self.line = line
+
+class DbgSession:
+ def __init__(self, sock):
+ self.latestRes = None
+ self.msgid = 0
+ self.sock = sock
+ self.bptsetlst = {}
+ self.bptsetids = {}
+ def jump(self, fn, line):
+ vim.command("e +"+str(line)+" "+str(fn))
+ def handle_response_breakpoint_set(self, res):
+ """handle <response command=breakpoint_set> tag
+ <responsponse command="breakpoint_set" id="110180001" transaction_id="1"/>"""
+ if res.firstChild.hasAttribute('id'):
+ tid = int(res.firstChild.getAttribute('transaction_id'))
+ bno = self.bptsetlst[tid]
+ del self.bptsetlst[tid]
+ self.bptsetids[bno] = res.firstChild.getAttribute('id')
+ def getbid(self, bno):
+ """ get Debug Server's breakpoint numbered with bno """
+ if bno in self.bptsetids:
+ return self.bptsetids[bno]
+ return None
+ def recv_data(self,len):
+ c = self.sock.recv(len)
+ if c == '':
+ # LINUX come here
+ raise EOFError, 'Socket Closed'
+ return c
+ def recv_length(self):
+ #print '* recv len'
+ length = ''
+ while 1:
+ c = self.recv_data(1)
+ #print ' GET(',c, ':', ord(c), ') : length=', len(c)
+ if c == '\0':
+ return int(length)
+ if c.isdigit():
+ length = length + c
+ def recv_null(self):
+ while 1:
+ c = self.recv_data(1)
+ if c == '\0':
+ return
+ def recv_body(self, to_recv):
+ body = ''
+ while to_recv > 0:
+ buf = self.recv_data(to_recv)
+ to_recv -= len(buf)
+ body = body + buf
+ return body
+ def recv_msg(self):
+ length = self.recv_length()
+ body = self.recv_body(length)
+ self.recv_null()
+ return body
+ def send_msg(self, cmd):
+ self.sock.send(cmd + '\0')
+ def handle_recvd_msg(self, res):
+ resDom = xml.dom.minidom.parseString(res)
+ if resDom.firstChild.tagName == "response":
+ if resDom.firstChild.getAttribute('command') == "breakpoint_set":
+ self.handle_response_breakpoint_set(resDom)
+ if resDom.firstChild.getAttribute('command') == "stop":
+ self.close()
+ return resDom
+ def send_command(self, cmd, arg1 = '', arg2 = ''):
+ self.msgid = self.msgid + 1
+ line = cmd + ' -i ' + str(self.msgid)
+ if arg1 != '':
+ line = line + ' ' + arg1
+ if arg2 != '':
+ line = line + ' -- ' + base64.encodestring(arg2)[0:-1]
+ self.send_msg(line)
+ return self.msgid
+ def ack_command(self, count=10000):
+ while count>0:
+ count = count - 1
+ self.latestRes = self.recv_msg()
+ resDom = self.handle_recvd_msg(self.latestRes)
+ try:
+ if int(resDom.firstChild.getAttribute('transaction_id')) == int(self.msgid):
+ return resDom
+ except:
+ pass
+ def command(self, cmd, arg1 = '', arg2 = ''):
+ self.send_command(cmd, arg1, arg2)
+ return self.ack_command()
+ def close(self):
+ if self.sock:
+ self.sock.close()
+ self.sock = None
+ def init(self):
+ if self.latestRes != None:
+ return
+ self.ack_command(1)
+ flag = 0
+ for bno in debugger.breakpt.list():
+ msgid = self.send_command('breakpoint_set', \
+ '-t line -f ' + debugger.breakpt.getfile(bno) + ' -n ' + str(debugger.breakpt.getline(bno)) + ' -s enabled', \
+ debugger.breakpt.getexp(bno))
+ self.bptsetlst[msgid] = bno
+ flag = 1
+ if flag:
+ self.ack_command()
+
+class DbgSessionWithUI(DbgSession):
+ def __init__(self, sock):
+ self.status = None
+ self.ui = debugger.ui
+
+ self.msgid = 0
+ self.stacks = []
+ self.curstack = 0
+ self.laststack = 0
+ DbgSession.__init__(self,sock)
+ def copyFromParent(self, ss):
+ self.latestRes = ss.latestRes
+ self.msgid = ss.msgid
+ self.sock = ss.sock
+ self.bptsetlst = ss.bptsetlst
+ self.bptsetids = ss.bptsetids
+ def init(self):
+ self.command('feature_set', '-n max_children -v ' + debugger.max_children)
+ self.command('feature_set', '-n max_data -v ' + debugger.max_data)
+ self.command('feature_set', '-n max_depth -v ' + debugger.max_depth)
+ def start(self):
+ self.sock.settimeout(30)
+ debugger.updateStatusLine()
+ self.ui.debug_mode()
+
+ if self.latestRes != None:
+ self.handle_recvd_msg(self.latestRes)
+ self.init()
+ self.command('stack_get')
+ else:
+ DbgSession.init(self)
+ self.init()
+ self.command('step_into')
+ self.command('property_get', "-d %d -n $_SERVER['REQUEST_URI']" % (self.laststack))
+ self.ui.go_srcview()
+ def send_msg(self, cmd):
+ """ send message """
+ self.sock.send(cmd + '\0')
+ # log message
+ if self.ui.tracewin:
+ self.ui.tracewin.write(str(self.msgid) + ' : send =====> ' + cmd)
+ def handle_recvd_msg(self, txt):
+ # log messages {{{
+ if self.ui.tracewin:
+ self.ui.tracewin.write( str(self.msgid) + ' : recv <===== {{{ ' + txt)
+ self.ui.tracewin.write('}}}')
+ res = xml.dom.minidom.parseString(txt)
+ """ call appropraite message handler member function, handle_XXX() """
+ fc = res.firstChild
+ try:
+ handler = getattr(self, 'handle_' + fc.tagName)
+ handler(res)
+ except AttributeError:
+ print 'Debugger.handle_'+fc.tagName+'() not found, please see the LOG___WINDOW'
+ self.ui.go_srcview()
+ return res
+ def handle_response(self, res):
+ """ call appropraite response message handler member function, handle_response_XXX() """
+ if res.firstChild.hasAttribute('reason') and res.firstChild.getAttribute('reason') == 'error':
+ self.handle_response_error(res)
+ return
+ errors = res.getElementsByTagName('error')
+ if len(errors)>0:
+ self.handle_response_error(res)
+ return
+
+ command = res.firstChild.getAttribute('command')
+ try:
+ handler = getattr(self, 'handle_response_' + command)
+ except AttributeError:
+ print 'Debugger.handle_response_'+command+'() not found, please see the LOG___WINDOW'
+ return
+ handler(res)
+ return
+ def handle_response_stop(self, res):
+ debugger.handle_exception()
+
+ def handle_init(self, res):
+ """handle <init> tag
+ <init appid="7035" fileuri="file:///home/segv/htdocs/index.php" language="PHP" protocol_version="1.0">
+ <engine version="2.0.0beta1">
+ Xdebug
+ </engine>
+ <author>
+ Derick Rethans
+ </author>
+ <url>
+ http://xdebug.org
+ </url>
+ <copyright>
+ Copyright (c) 2002-2004 by Derick Rethans
+ </copyright>
+ </init>"""
+
+ if sys.platform == 'win32':
+ file = res.firstChild.getAttribute('fileuri')[8:]
+ else:
+ file = res.firstChild.getAttribute('fileuri')[7:]
+ self.ui.set_srcview(file, 1)
+
+ def handle_response_error(self, res):
+ """ handle <error> tag """
+ if self.ui.tracewin:
+ self.ui.tracewin.write_xml_childs(res)
+ errors = res.getElementsByTagName('error')
+ for error in errors:
+ code = int(error.getAttribute('code'))
+ if code == 5:
+ self.command('run')
+ break
+
+ def handle_response_stack_get(self, res):
+ """handle <response command=stack_get> tag
+ <response command="stack_get" transaction_id="1 ">
+ <stack filename="file:///home/segv/htdocs/index.php" level="0" lineno="41" where="{main}"/>
+ </response>"""
+
+ stacks = res.getElementsByTagName('stack')
+ if len(stacks)>0:
+ self.curstack = 0
+ self.laststack = len(stacks) - 1
+
+ self.stacks = []
+ for s in stacks:
+ if sys.platform == 'win32':
+ fn = s.getAttribute('filename')[8:]
+ else:
+ fn = s.getAttribute('filename')[7:]
+ self.stacks.append( {'file': fn, \
+ 'line': int(s.getAttribute('lineno')), \
+ 'where': s.getAttribute('where'), \
+ 'level': int(s.getAttribute('level'))
+ } )
+
+ self.ui.stackwin.clean()
+ self.ui.stackwin.highlight_stack(self.curstack)
+
+ self.ui.stackwin.write_xml_childs(res.firstChild) #str(res.toprettyxml()))
+ self.ui.set_srcview( self.stacks[self.curstack]['file'], self.stacks[self.curstack]['line'] )
+
+
+ def handle_response_step_out(self, res):
+ """handle <response command=step_out> tag
+ <response command="step_out" reason="ok" status="break" transaction_id="1 "/>"""
+ if res.firstChild.hasAttribute('reason') and res.firstChild.getAttribute('reason') == 'ok':
+ if res.firstChild.hasAttribute('status'):
+ self.status = res.firstChild.getAttribute('status')
+ return
+ else:
+ print res.toprettyxml()
+ def handle_response_step_over(self, res):
+ """handle <response command=step_over> tag
+ <response command="step_over" reason="ok" status="break" transaction_id="1 "/>"""
+ if res.firstChild.hasAttribute('reason') and res.firstChild.getAttribute('reason') == 'ok':
+ if res.firstChild.hasAttribute('status'):
+ self.status = res.firstChild.getAttribute('status')
+ return
+ else:
+ print res.toprettyxml()
+ def handle_response_step_into(self, res):
+ """handle <response command=step_into> tag
+ <response command="step_into" reason="ok" status="break" transaction_id="1 "/>"""
+ if res.firstChild.hasAttribute('reason') and res.firstChild.getAttribute('reason') == 'ok':
+ if res.firstChild.hasAttribute('status'):
+ self.status = res.firstChild.getAttribute('status')
+ return
+ else:
+ print res.toprettyxml()
+ def handle_response_run(self, res):
+ """handle <response command=run> tag
+ <response command="step_over" reason="ok" status="break" transaction_id="1 "/>"""
+ if res.firstChild.hasAttribute('status'):
+ self.status = res.firstChild.getAttribute('status')
+ return
+ def handle_response_eval(self, res):
+ """handle <response command=eval> tag """
+ self.ui.watchwin.write_xml_childs(res)
+ def handle_response_property_get(self, res):
+ """handle <response command=property_get> tag """
+ self.ui.watchwin.write_xml_childs(res)
+ def handle_response_context_get(self, res):
+ """handle <response command=context_get> tag """
+ self.ui.watchwin.write_xml_childs(res)
+ def handle_response_feature_set(self, res):
+ """handle <response command=feature_set> tag """
+ self.ui.watchwin.write_xml_childs(res)
+ def handle_response_default(self, res):
+ """handle <response command=context_get> tag """
+ print res.toprettyxml()
+
+ def go(self, stack):
+ if stack >= 0 and stack <= self.laststack:
+ self.curstack = stack
+ self.ui.stackwin.highlight_stack(self.curstack)
+ self.ui.set_srcview(self.stacks[self.curstack]['file'], self.stacks[self.curstack]['line'])
+
+ def jump(self, fn, line):
+ self.ui.set_srcview(fn, line)
+
+ def up(self):
+ if self.curstack > 0:
+ self.curstack -= 1
+ self.ui.stackwin.highlight_stack(self.curstack)
+ self.ui.set_srcview(self.stacks[self.curstack]['file'], self.stacks[self.curstack]['line'])
+
+ def down(self):
+ if self.curstack < self.laststack:
+ self.curstack += 1
+ self.ui.stackwin.highlight_stack(self.curstack)
+ self.ui.set_srcview(self.stacks[self.curstack]['file'], self.stacks[self.curstack]['line'])
+
+ def watch_input(self, mode, arg = ''):
+ self.ui.watchwin.input(mode, arg)
+
+ def property_get(self, name = ''):
+ if name == '':
+ name = vim.eval('expand("<cword>")')
+ self.ui.watchwin.write('--> property_get: '+name)
+ self.command('property_get', '-d %d -n %s' % (self.curstack, name))
+
+ def watch_execute(self):
+ """ execute command in watch window """
+ (cmd, expr) = self.ui.watchwin.get_command()
+ if cmd == 'exec':
+ self.command('exec', '', expr)
+ print cmd, '--', expr
+ elif cmd == 'eval':
+ self.command('eval', '', expr)
+ print cmd, '--', expr
+ elif cmd == 'property_get':
+ self.command('property_get', '-d %d -n %s' % (self.curstack, expr))
+ print cmd, '-n ', expr
+ elif cmd == 'context_get':
+ self.command('context_get', ('-d %d' % self.curstack))
+ print cmd
+ else:
+ print "no commands", cmd, expr
+
+class DbgSilentClient(Thread):
+ def __init__(self, ss):
+ self.session = ss
+ Thread.__init__(self)
+ def run(self):
+ self.session.init()
+ self.session.sock.settimeout(None)
+
+ resDom = self.session.command("run")
+ status = "stopping"
+ if resDom.firstChild.hasAttribute('status'):
+ status = resDom.firstChild.getAttribute('status')
+ if status == "stopping":
+ self.session.command("stop")
+ elif status == "break":
+ debugger.debugListener.newSession(self.session)
+
+class DbgListener(Thread):
+ (INIT,LISTEN,CLOSED) = (0,1,2)
+ """ DBGp Procotol class """
+ def __init__(self, port):
+ self.port = port
+ self.session_queue = []
+ self._status = self.INIT
+ self.lock = Lock()
+ Thread.__init__(self)
+ def start(self):
+ Thread.start(self)
+ time.sleep(0.1)
+ debugger.updateStatusLine()
+ def pendingCount(self):
+ self.lock.acquire()
+ c = len(self.session_queue)
+ self.lock.release()
+ return c
+ def newSession(self, ss):
+ if not isinstance(ss, DbgSessionWithUI):
+ s = DbgSessionWithUI(None)
+ s.copyFromParent(ss)
+ ss = s
+ self.lock.acquire()
+ self.session_queue.append(ss)
+ c = str(len(self.session_queue))
+ self.lock.release()
+ debugger.updateStatusLine()
+ print c+" pending connection(s) to be debug, press <F5> to continue."
+ def nextSession(self):
+ session = None
+ self.lock.acquire()
+ if len(self.session_queue) > 0:
+ session = self.session_queue.pop(0)
+ self.lock.release()
+ debugger.updateStatusLine()
+ print ""
+ return session
+ def stop(self):
+ self.lock.acquire()
+ if self._status == self.LISTEN:
+ client = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
+ client.connect ( ( '127.0.0.1', self.port ) )
+ client.close()
+ for s in self.session_queue:
+ s.sock.close()
+ self._status = self.CLOSED
+ self.lock.release()
+ debugger.updateStatusLine()
+ def status(self):
+ self.lock.acquire()
+ s = self._status
+ self.lock.release()
+ return s
+ def run(self):
+ global debugger
+ self.lock.acquire()
+ serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ try:
+ serv.bind(('', self.port))
+ except socket.error, e:
+ print "Can not bind to port "+str(self.port)+', Socket Error '+str(e[0])
+ self.lock.release()
+ return
+ print ""
+ serv.listen(5)
+ self._status = self.LISTEN
+ self.lock.release()
+ while 1:
+ (sock, address) = serv.accept()
+ s = self.status()
+ if s == self.LISTEN:
+ if debugger.break_at_entry:
+ self.newSession(DbgSessionWithUI(sock))
+ else:
+ client = DbgSilentClient(DbgSession(sock))
+ client.start()
+ else:
+ break
+ serv.close()
+
+class BreakPoint:
+ """ Breakpoint class """
+ def __init__(self):
+ """ initalize """
+ self.dictionaries = {}
+ self.startbno = 10000
+ self.maxbno = self.startbno
+ def clear(self):
+ """ clear of breakpoint number """
+ self.dictionaries.clear()
+ self.maxbno = self.startbno
+ def add(self, file, line, exp = ''):
+ """ add break point at file:line """
+ self.maxbno = self.maxbno + 1
+ self.dictionaries[self.maxbno] = { 'file':file, 'line':line, 'exp':exp }
+ return self.maxbno
+ def remove(self, bno):
+ """ remove break point numbered with bno """
+ del self.dictionaries[bno]
+ def find(self, file, line):
+ """ find break point and return bno(breakpoint number) """
+ for bno in self.dictionaries.keys():
+ if self.dictionaries[bno]['file'] == file and self.dictionaries[bno]['line'] == line:
+ return bno
+ return None
+ def getfile(self, bno):
+ """ get file name of breakpoint numbered with bno """
+ return self.dictionaries[bno]['file']
+ def getline(self, bno):
+ """ get line number of breakpoint numbered with bno """
+ return self.dictionaries[bno]['line']
+ def getexp(self, bno):
+ """ get expression of breakpoint numbered with bno """
+ return self.dictionaries[bno]['exp']
+ def list(self):
+ """ return list of breakpoint number """
+ return self.dictionaries.keys()
+
+class Debugger:
+ """ Main Debugger class """
+ def __init__(self):
+ """ initialize Debugger """
+ self.loadSettings()
+ self.debugListener = DbgListener(self.port)
+ self.debugSession = DbgSession(None)
+ vim.command('sign unplace *')
+
+ self.normal_statusline = vim.eval('&statusline')
+ self.statusline="%<%f\ %h%m%r\ %=%-10.(%l,%c%V%)\ %P\ %=%{'PHP-'}%{(g:debuggerBreakAtEntry==1)?'bae':'bap'}"
+ self.breakpt = BreakPoint()
+ self.ui = DebugUI(12, 70)
+
+ def updateStatusLine(self):
+ status = self.debugListener.status()
+ if status == DbgListener.INIT or status == DbgListener.CLOSED:
+ sl = self.normal_statusline
+ else:
+ c = self.debugListener.pendingCount()
+ if c > 0:
+ sl = self.statusline+"%{'-PEND"+str(c)+"'}"
+ elif self.debugSession.sock != None:
+ sl = self.statusline+"%{'-CONN'}"
+ else:
+ sl = self.statusline+"%{'-LISN'}"
+ vim.command("let &statusline=\""+sl+"\"")
+
+ def loadSettings(self):
+ self.port = int(vim.eval('debuggerPort'))
+ self.max_children = vim.eval('debuggerMaxChildren')
+ self.max_data = vim.eval('debuggerMaxData')
+ self.max_depth = vim.eval('debuggerMaxDepth')
+ self.break_at_entry = int(vim.eval('debuggerBreakAtEntry'))
+ def setMaxChildren(self):
+ self.max_children = vim.eval('debuggerMaxChildren')
+ if self.debugSession.sock != None:
+ self.debugSession.command('feature_set', '-n max_children -v ' + self.max_children)
+ def setMaxDepth(self):
+ self.max_depth = vim.eval('debuggerMaxDepth')
+ if self.debugSession.sock != None:
+ self.debugSession.command('feature_set', '-n max_depth -v ' + self.max_depth)
+ def setMaxData(self):
+ self.max_data = vim.eval('debuggerMaxData')
+ if self.debugSession.sock != None:
+ self.debugSession.command('feature_set', '-n max_data -v ' + self.max_data)
+ def handle_exception(self):
+ if self.ui.tracewin:
+ self.ui.tracewin.write(sys.exc_info())
+ self.ui.tracewin.write("".join(traceback.format_tb( sys.exc_info()[2])))
+ errno = sys.exc_info()[0]
+
+ session = self.debugListener.nextSession()
+ if errno == socket.timeout:
+ if session != None:
+ print "socket timeout, switch to another session."
+ ss = DbgSession(self.debugSession.sock)
+ ss.latestRes = self.debugSession.latestRes
+ client = DbgSilentClient(ss)
+ client.start()
+ self.debugSession = session
+ self.debugSession.start()
+ else:
+ print "socket timeout, try again or press F6 to stop debugging."
+ else: #errno == socket.error:
+ self.debugSession.close()
+ if session != None:
+ self.debugSession = session
+ self.debugSession.start()
+ else:
+ self.ui.normal_mode()
+ self.updateStatusLine()
+ def command(self, msg, arg1 = '', arg2 = ''):
+ try:
+ if self.debugSession.sock == None:
+ print 'No debug session started.'
+ else:
+ self.debugSession.command(msg, arg1, arg2)
+ if self.debugSession.status != 'stopping':
+ self.debugSession.command('stack_get')
+ except:
+ self.handle_exception()
+ def watch_input(self, cmd, arg = ''):
+ try:
+ if self.debugSession.sock == None:
+ print 'No debug session started.'
+ else:
+ if arg == '<cword>':
+ arg = vim.eval('expand("<cword>")')
+ if arg == 'this':
+ arg = '$this'
+ self.debugSession.watch_input(cmd, arg)
+ except:
+ self.handle_exception()
+ def property(self, name = ''):
+ try:
+ if self.debugSession.sock == None:
+ print 'No debug session started.'
+ else:
+ string.replace(name,'"','\'')
+ if string.find(name,' ') != -1:
+ name = "\"" + name +"\""
+ elif name == 'this':
+ name = '$this'
+ self.debugSession.property_get(name)
+ except:
+ self.handle_exception()
+ def up(self):
+ try:
+ if self.debugSession.sock == None:
+ print 'No debug session started.'
+ else:
+ self.debugSession.up()
+ except:
+ self.handle_exception()
+
+ def down(self):
+ try:
+ if self.debugSession.sock == None:
+ print 'No debug session started.'
+ else:
+ self.debugSession.down()
+ except:
+ self.handle_exception()
+
+ def run(self):
+ """ start debugger or continue """
+ try:
+ status = self.debugListener.status()
+ if status == DbgListener.INIT or status == DbgListener.CLOSED:
+ self.loadSettings()
+ self.debugListener = DbgListener(self.port)
+ self.debugListener.start()
+ elif self.debugSession.sock != None:
+ self.debugSession.command('run')
+ if self.debugSession.status == 'stopping':
+ self.debugSession.command("stop")
+ elif self.debugSession.status != 'stopped':
+ self.debugSession.command("stack_get")
+ else:
+ session = self.debugListener.nextSession()
+ if session != None:
+ self.debugSession = session
+ self.debugSession.start()
+ except:
+ self.handle_exception()
+
+ def list(self):
+ self.ui.watchwin.write('--> breakpoints list: ')
+ for bno in self.breakpt.list():
+ self.ui.watchwin.write(str(bno)+' ' + self.breakpt.getfile(bno) + ':' + str(self.breakpt.getline(bno)))
+
+ def mark(self, exp = ''):
+ (row, rol) = vim.current.window.cursor
+ file = vim.current.buffer.name
+
+ bno = self.breakpt.find(file, row)
+ if bno != None:
+ self.breakpt.remove(bno)
+ vim.command('sign unplace ' + str(bno))
+ id = self.debugSession.getbid(bno)
+ if self.debugSession.sock != None and id != None:
+ self.debugSession.send_command('breakpoint_remove', '-d ' + str(id))
+ self.debugSession.ack_command()
+ else:
+ bno = self.breakpt.add(file, row, exp)
+ vim.command('sign place ' + str(bno) + ' name=breakpt line=' + str(row) + ' file=' + file)
+ if self.debugSession.sock != None:
+ msgid = self.debugSession.send_command('breakpoint_set', \
+ '-t line -f ' + self.breakpt.getfile(bno) + ' -n ' + str(self.breakpt.getline(bno)), \
+ self.breakpt.getexp(bno))
+ self.debugSession.bptsetlst[msgid] = bno
+ self.debugSession.ack_command()
+
+ def quit(self):
+ self.ui.normal_mode()
+ self.debugSession.close()
+ self.debugListener.stop()
+
+def debugger_init():
+ global debugger
+ debugger = Debugger()
+
+error_msg = { \
+ # 000 Command parsing errors
+ 0 : """no error""", \
+ 1 : """parse error in command""", \
+ 2 : """duplicate arguments in command""", \
+ 3 : """invalid options (ie, missing a required option)""", \
+ 4 : """Unimplemented command""", \
+ 5 : """Command not available (Is used for async commands. For instance if the engine is in state "run" than only "break" and "status" are available). """, \
+ # 100 : File related errors
+ 100 : """can not open file (as a reply to a "source" command if the requested source file can't be opened)""", \
+ 101 : """stream redirect failed """, \
+ # 200 Breakpoint, or code flow errors
+ 200 : """breakpoint could not be set (for some reason the breakpoint could not be set due to problems registering it)""", \
+ 201 : """breakpoint type not supported (for example I don't support 'watch' yet and thus return this error)""", \
+ 202 : """invalid breakpoint (the IDE tried to set a breakpoint on a line that does not exist in the file (ie "line 0" or lines past the end of the file)""", \
+ 203 : """no code on breakpoint line (the IDE tried to set a breakpoint on a line which does not have any executable code. The debugger engine is NOT required to """ + \
+ """return this type if it is impossible to determine if there is code on a given location. (For example, in the PHP debugger backend this will only be """ + \
+ """returned in some special cases where the current scope falls into the scope of the breakpoint to be set)).""", \
+ 204 : """Invalid breakpoint state (using an unsupported breakpoint state was attempted)""", \
+ 205 : """No such breakpoint (used in breakpoint_get etc. to show that there is no breakpoint with the given ID)""", \
+ 206 : """Error evaluating code (use from eval() (or perhaps property_get for a full name get))""", \
+ 207 : """Invalid expression (the expression used for a non-eval() was invalid) """, \
+ # 300 Data errors
+ 300 : """Can not get property (when the requested property to get did not exist, this is NOT used for an existing but uninitialized property, which just gets the """ + \
+ """type "uninitialised" (See: PreferredTypeNames)).""", \
+ 301 : """Stack depth invalid (the -d stack depth parameter did not exist (ie, there were less stack elements than the number requested) or the parameter was < 0)""", \
+ 302 : """Context invalid (an non existing context was requested) """, \
+ # 900 Protocol errors
+ 900 : """Encoding not supported""", \
+ 998 : """An internal exception in the debugger occurred""", \
+ 999 : """Unknown error """ \
+}
+
190 plugin/dbgpavim.vim
@@ -0,0 +1,190 @@
+" vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
+" DBGPavim: a remote debugger interface to the DBGp protocol
+"
+" Script Info and Documentation {{{
+"=============================================================================
+" Copyright: Copyright (C) 2012 Brook Hong
+" License: The MIT License
+"
+" Permission is hereby granted, free of charge, to any person obtaining
+" a copy of this software and associated documentation files
+" (the "Software"), to deal in the Software without restriction,
+" including without limitation the rights to use, copy, modify,
+" merge, publish, distribute, sublicense, and/or sell copies of the
+" Software, and to permit persons to whom the Software is furnished
+" to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included
+" in all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+" IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+" CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+" TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+" SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+" Name Of File: dbgpavim.vim, dbgpavim.py
+" Description: remote debugger interface to DBGp protocol
+" The DBGPavim originates from http://www.vim.org/scripts/script.php?script_id=1152, with a new enhanced debugger engine.
+"
+" This file should reside in the plugins directory along
+" with dbgpavim.py and be automatically sourced.
+"
+" By default, the script expects the debugging engine to connect
+" on port 9000. You can change this with the g:debuggerPort
+" variable by putting the following line your vimrc:
+"
+" let g:debuggerPort = 10001
+"
+" where 10001 is the new port number you want the server to
+" connect to.
+"
+" There are three maximum limits you can set referring to the
+" properties (variables) returned by the debugging engine.
+"
+" g:debuggerMaxChildren (default 1024): The max number of array or
+" object children to initially retrieve per variable.
+" For example:
+"
+" let g:debuggerMaxChildren = 64
+"
+" g:debuggerMaxData (default 1024 bytes): The max amount of
+" variable data to retrieve.
+" For example:
+"
+" let g:debuggerMaxData = 2048
+"
+" g:debuggerMaxDepth (default 1): The maximum depth that the
+" debugger engine may return when sending arrays, hashs or
+" object structures to the IDE.
+" For example:
+"
+" let g:debuggerMaxDepth = 10
+"
+" g:debuggerBreakAtEntry (default 1): Whether to break at entry,
+" if set it 0, the debugger engine will break only at
+" breakpoints.
+" For example:
+"
+" let g:debuggerBreakAtEntry = 0
+"
+" To enable debug from CLI
+"
+" php -dxdebug.remote_autostart=1 -dxdebug.remote_port=9000 test.php
+"=============================================================================
+" }}}
+" Do not source this script when python is not compiled in.
+if !has("python")
+ finish
+endif
+
+" Load dbgpavim.py either from the same path where dbgpavim.vim is
+let s:dbgpavim_py = expand("<sfile>:p:h")."/dbgpavim.py"
+if filereadable(s:dbgpavim_py)
+ exec 'pyfile '.s:dbgpavim_py
+else
+ call confirm('dbgpavim.vim: Unable to find '.s:dbgpavim_py.'. Place it in either your home vim directory or in the Vim runtime directory.', 'OK')
+endif
+
+map <silent> <F5> :python debugger.run()<cr>
+map <silent> <F6> :python debugger.quit()<cr>
+map <silent> <F8> :call Bae()<cr>
+map <silent> + :call ResizeWindow("+")<cr>
+map <silent> - :call ResizeWindow("-")<cr>
+command! -nargs=? Bp python debugger.mark('<args>')
+command! -nargs=0 Bl python debugger.list()
+command! -nargs=1 Dmc let g:debuggerMaxChildren=<args>|python debugger.setMaxChildren()
+command! -nargs=1 Dme let g:debuggerMaxDepth=<args>|python debugger.setMaxDepth()
+command! -nargs=1 Dma let g:debuggerMaxData=<args>|python debugger.setMaxData()
+function! CreateFunctionKeys()
+ map <silent> <F1> :python debugger.ui.help()<cr>
+ map <silent> <F2> :python debugger.command('step_into')<cr>
+ map <silent> <F3> :python debugger.command('step_over')<cr>
+ map <silent> <F4> :python debugger.command('step_out')<cr>
+ map <silent> <F7> :python debugger.watch_input("eval")<cr>A
+ map <silent> <F9> :python debugger.ui.reLayout()<cr>
+ map <silent> <F11> :python debugger.watch_input("context_get")<cr>A<cr>
+ map <silent> <F12> :python debugger.watch_input("property_get", '<cword>')<cr>A<cr>
+
+ command! -nargs=0 Up python debugger.up()
+ command! -nargs=0 Dn python debugger.down()
+ command! -nargs=? Pg python debugger.property("<args>")
+ command! -nargs=0 Dt python debugger.ui.trace()
+endfunction
+function! ClearFunctionKeys()
+ try
+ unmap <F1>
+ unmap <F2>
+ unmap <F3>
+ unmap <F4>
+ unmap <F7>
+ unmap <F9>
+ unmap <F11>
+ unmap <F12>
+
+ delcommand Up
+ delcommand Dn
+ delcommand Pg
+ catch /.*/
+ echo "Exception from" v:throwpoint
+ endtry
+endfunction
+function! ResizeWindow(flag)
+ let l:width = winwidth("%")
+ if l:width == &columns
+ execute 'resize '.a:flag.'5'
+ else
+ execute 'vertical resize '.a:flag.'5'
+ endif
+endfunction
+function! Bae()
+ let g:debuggerBreakAtEntry = (g:debuggerBreakAtEntry == 1) ? 0 : 1
+ execute 'python debugger.break_at_entry = '.g:debuggerBreakAtEntry
+endfunction
+function! WatchWindowOnEnter()
+ let l:line = getline(".")
+ if l:line =~ "^\\s*\\$.* = (object)\\|(array)"
+ execute "Pg ".substitute(line,"\\s*\\(\\S.*\\S\\)\\s*=.*","\\1","g")
+ execute "normal \<c-w>p"
+ elseif l:line =~ "^\\d\\+ .*:\\d\\+$"
+ let fn = substitute(l:line,"^\\d\\+ \\(.*\\):\\d\\+$","\\1","")
+ let ln = substitute(l:line,"^\\d\\+ .*:\\(\\d\\+\\)$","\\1","")
+ execute 'python debugger.debugSession.jump("'.l:fn.'",'.l:ln.')'
+ endif
+endfunction
+function! StackWindowOnEnter()
+ let l:stackNo = substitute(getline("."),"\\(\\d\\+\\)\\s\\+.*","\\1","g")
+ if l:stackNo =~ "^\\d\\+$"
+ execute 'python debugger.debugSession.go('.l:stackNo.')'
+ execute "normal \<c-w>p"
+ endif
+endfunction
+
+hi DbgCurrent term=reverse ctermfg=White ctermbg=Red gui=reverse
+hi DbgBreakPt term=reverse ctermfg=White ctermbg=Green gui=reverse
+sign define current text=-> texthl=DbgCurrent linehl=DbgCurrent
+sign define breakpt text=B> texthl=DbgBreakPt linehl=DbgBreakPt
+
+if !exists('g:debuggerPort')
+ let g:debuggerPort = 9000
+endif
+if !exists('g:debuggerMaxChildren')
+ let g:debuggerMaxChildren = 1024
+endif
+if !exists('g:debuggerMaxData')
+ let g:debuggerMaxData = 1024
+endif
+if !exists('g:debuggerMaxDepth')
+ let g:debuggerMaxDepth = 1
+endif
+if !exists('g:debuggerBreakAtEntry')
+ let g:debuggerBreakAtEntry = 1
+endif
+python debugger_init()
+set laststatus=2
+
+autocmd BufEnter WATCH_WINDOW map <silent> <buffer> <Enter> :call WatchWindowOnEnter()<CR>
+autocmd BufEnter STACK_WINDOW map <silent> <buffer> <Enter> :call StackWindowOnEnter()<CR>
+autocmd BufLeave HELP__WINDOW :python debugger.ui.helpwin=None
+autocmd VimLeavePre * python debugger.quit()
Please sign in to comment.
Something went wrong with that request. Please try again.