From 52970849b586381794591168f00b2fdf657303ec 2008-08-17 02:55:49 From: Fernando Perez Date: 2008-08-17 02:55:49 Subject: [PATCH] Merge Vivan De Smedt's branch on editor synchronization. --- diff --git a/IPython/Debugger.py b/IPython/Debugger.py index 3044e0f..576395c 100644 --- a/IPython/Debugger.py +++ b/IPython/Debugger.py @@ -333,6 +333,12 @@ class Pdb(OldPdb): #frame, lineno = frame_lineno print >>Term.cout, self.format_stack_entry(frame_lineno, '', context) + # vds: >> + frame, lineno = frame_lineno + filename = frame.f_code.co_filename + __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0) + # vds: << + def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3): import linecache, repr @@ -496,6 +502,12 @@ class Pdb(OldPdb): last = first + 10 self.print_list_lines(self.curframe.f_code.co_filename, first, last) + # vds: >> + lineno = first + filename = self.curframe.f_code.co_filename + __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0) + # vds: << + do_l = do_list def do_pdef(self, arg): diff --git a/IPython/Extensions/ipy_synchronize_with.py b/IPython/Extensions/ipy_synchronize_with.py new file mode 100644 index 0000000..d6222b6 --- /dev/null +++ b/IPython/Extensions/ipy_synchronize_with.py @@ -0,0 +1,242 @@ +import IPython.ipapi +ip = IPython.ipapi.get() + +import win32api +import win32ui +import win32console +import dde +import os +import scitedirector + +# test to write. + +def set_hook(synchronize_with_editor): + """Set the synchronize with editor hook with a callable object. + + The callable object will be called with the following arguments when + IPython wants to synchronize with you favorite editor: + + - ip: a running IPython instance. + + - filename: the path of the file the editor is supposed to display. + + - lineno : the line number of the line the editor is supposed to + highlight. + + - columnno : the column number of the character the editor is supposed + to highlight. + """ + ip.set_hook("synchronize_with_editor", synchronize_with_editor) + + +def find_filename(filename): + """Return the filename to synchronize with based on """ + filename = os.path.splitext(filename) + if filename[1] == ".pyc": + filename = (filename[0], ".py") + filename = "".join(filename) + + if not os.path.isabs(filename): + filename = os.path.join(os.getcwd(), filename) + + if os.path.isfile(filename): + return filename + + return "" + + +def run_command(path, command, arguments, asynchronous = True): + """Run a shell command and return the exit code of the command""" + # This is a thin wrapper around os.system that: + # - Let you run command asynchronously. + # - Accept spaces in command path. + # - Dont throw exception if the command don't exist. + line = '' + if asynchronous: + line += 'start ' + + try: + line += win32api.GetShortPathName(os.path.join(path, command) + ".exe") + " " + except: + print 'could not find: "%s"' % (os.path.join(path, command) + ".exe") + return -1 + + line += arguments + r = os.system(line) + return r + + +def sleep(milliseconds): + """Wait some milliseconds.""" + # This is used to make sure the editor did its job before we reset the focus on the console. + win32api.Sleep(milliseconds) + + +def restore_console_focus(): + """Restore the focus to the IPython console.""" + h = win32console.GetConsoleWindow() + console_window = win32ui.CreateWindowFromHandle(h) + console_window.SetForegroundWindow() + + +# This is the most simple example of hook: +class GVimHook: + def __init__(self, path, wakeup_duration): + self.path = path + self.wakeup_duration = wakeup_duration + + def __call__(self, ip, filename, lineno, columnno): + filename = find_filename(filename) + + if not filename: + return + + run_command(self.path, 'gvim', '--remote-silent +%d "%s"' % (lineno, filename)) + + sleep(self.wakeup_duration) + + restore_console_focus() + + +def gvim(path = r"C:\Program Files\vim\vim71", wakeup_duration = 100): + synchronize_with_editor = GVimHook(path, wakeup_duration) + set_hook(synchronize_with_editor) + + +class EmacsHook: + def __init__(self, path, wakeup_duration, start_duration): + self.path = path + self.wakeup_duration = wakeup_duration + self.start_duration = start_duration + + def __call__(self, ip, filename, lineno, columnno): + filename = find_filename(filename) + + if not filename: + return + + r = run_command(self.path, "emacsclient", '-n +%d:%d "%s" 2>nul' % (lineno, columnno, filename), False) + if r != 0: + run_command(self.path, 'runemacs', '--quick -f server-start +%d:%d "%s"' % (lineno, columnno, filename)) + sleep(self.start_duration) + else: + sleep(self.wakeup_duration) + + restore_console_focus() + + +def emacs(path = r"C:\Program Files\emacs\bin", wakeup_duration = 100, start_duration = 2000): + synchronize_with_editor = EmacsHook(path, wakeup_duration, start_duration) + set_hook(synchronize_with_editor) + + +class SciteHook: + def __init__(self, path, wakeup_duration, start_duration): + self.path = path + self.wakeup_duration = wakeup_duration + self.start_duration = start_duration + + def __call__(self, ip, filename, lineno, columnno): + filename = find_filename(filename) + + if not filename: + return + + scites = scitedirector.findWindows() + if not scites: + run_command(self.path, "scite", '"-open:%s" -goto:%d' % (filename.replace("\\", "/"), lineno)) + + sleep(self.start_duration) + restore_console_focus() + else: + scite = scites[0] + scitedirector.sendCommand(scite, 'open:%s' % filename.replace("\\", "/")) + scitedirector.sendCommand(scite, "goto:%d" % lineno) + + +def scite(path = r"C:\Program Files\SciTE Source Code Editor", wakeup_duration = 100, start_duration = 500): + synchronize_with_editor = SciteHook(path, wakeup_duration, start_duration) + set_hook(synchronize_with_editor) + + +class NodePadPlusPlusHook: + def __init__(self, path, wakeup_duration): + self.path = path + self.wakeup_duration = wakeup_duration + + def __call__(self, ip, filename, lineno, columnno): + filename = find_filename(filename) + + if not filename: + return + + run_command(self.path, "notepad++", '"%s" -n%d' % (filename, lineno)) + + sleep(self.wakeup_duration) + + restore_console_focus() + + +def notepadplusplus(path = r"C:\Program Files\Notepad++", wakeup_duration = 100): + synchronize_with_editor = NodePadPlusPlusHook(path, wakeup_duration) + set_hook(synchronize_with_editor) + + +class PsPadHook: + def __init__(self, path, wakeup_duration): + self.path = path + self.wakeup_duration = wakeup_duration + + def __call__(self, ip, filename, lineno, columnno): + filename = find_filename(filename) + + if not filename: + return + + run_command(self.path, "pspad", '"%s" -%d' % (filename, lineno)) + + sleep(self.wakeup_duration) + + restore_console_focus() + + +def pspad(path = r"C:\Program Files\PSPad editor", wakeup_duration = 100): + synchronize_with_editor = PsPadHook(path, wakeup_duration) + set_hook(synchronize_with_editor) + + +# This is an example of DDE hook: +class UltraEditHook: + def __init__(self, path, wakeup_duration, start_duration): + self.path = path + self.wakeup_duration = wakeup_duration + self.start_duration = start_duration + + def __call__(self, ip, filename, lineno, columnno): + filename = find_filename(filename) + + if not filename: + return + + server = dde.CreateServer() + server.Create("myddeserver") + conversation = dde.CreateConversation(server) + try: + conversation.ConnectTo("uedit32", "System") + conversation.Exec(r'[open("%s/%d"])' % (filename, lineno)) + + sleep(self.wakeup_duration) + except: + run_command(self.path, 'uedit32', '"%s/%d"' % (filename, lineno)) + + sleep(self.start_duration) + + server.Shutdown() + + restore_console_focus() + + +def ultraedit(path = r"C:\Program Files\IDM Computer Solutions\UltraEdit-32", wakeup_duration = 10, start_duration = 2000): + synchronize_with_editor = UltraEditHook(path, wakeup_duration, start_duration) + set_hook(synchronize_with_editor) + \ No newline at end of file diff --git a/IPython/Extensions/scitedirector.py b/IPython/Extensions/scitedirector.py new file mode 100644 index 0000000..97dc6cd --- /dev/null +++ b/IPython/Extensions/scitedirector.py @@ -0,0 +1,26 @@ +import win32api +import win32gui +import win32con + +import struct +import array + +def findWindows(): + ret = [] + sdi = win32api.RegisterWindowMessage("SciTEDirectorInterface") + w = win32gui.GetWindow(win32gui.GetDesktopWindow(), win32con.GW_CHILD) + while w: + res = win32gui.SendMessage(w, sdi, 0, 0) + if res == sdi: + ret.append(w) + w = win32gui.GetWindow(w, win32con.GW_HWNDNEXT) + + return ret + +def sendCommand(w, message): + CopyDataStruct = "IIP" + char_buffer = array.array('c', message) + char_buffer_address = char_buffer.buffer_info()[0] + char_buffer_size = char_buffer.buffer_info()[1] + cds = struct.pack(CopyDataStruct, 0, char_buffer_size, char_buffer_address) + win32gui.SendMessage(w, win32con.WM_COPYDATA, 0, cds) diff --git a/IPython/hooks.py b/IPython/hooks.py index 777f308..6a67264 100644 --- a/IPython/hooks.py +++ b/IPython/hooks.py @@ -53,10 +53,12 @@ from pprint import PrettyPrinter # List here all the default hooks. For now it's just the editor functions # but over time we'll move here all the public API for user-accessible things. -__all__ = ['editor', 'fix_error_editor', 'result_display', +# vds: >> +__all__ = ['editor', 'fix_error_editor', 'synchronize_with_editor', 'result_display', 'input_prefilter', 'shutdown_hook', 'late_startup_hook', 'generate_prompt', 'generate_output_prompt','shell_hook', 'show_in_pager','pre_prompt_hook', 'pre_runcode_hook'] +# vds: << pformat = PrettyPrinter().pformat @@ -107,6 +109,10 @@ def fix_error_editor(self,filename,linenum,column,msg): finally: t.close() +# vds: >> +def synchronize_with_editor(self, filename, linenum, column): + pass +# vds: << class CommandChainDispatcher: """ Dispatch calls to a chain of commands until some func can handle it diff --git a/IPython/ultraTB.py b/IPython/ultraTB.py index eb2e579..51a0eee 100644 --- a/IPython/ultraTB.py +++ b/IPython/ultraTB.py @@ -490,6 +490,11 @@ class ListTB(TBTools): Colors.Normal, s)) else: list.append('%s\n' % str(stype)) + + # vds:>> + __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0) + # vds:<< + return list def _some_str(self, value): @@ -801,6 +806,15 @@ class VerboseTB(TBTools): for name in names: value = text_repr(getattr(evalue, name)) exception.append('\n%s%s = %s' % (indent, name, value)) + + # vds: >> + if records: + frame, file, lnum, func, lines, index = records[-1] + #print "file:", str(file), "linenb", str(lnum) + file = abspath(file) + __IPYTHON__.hooks.synchronize_with_editor(file, lnum, 0) + # vds: << + # return all our info assembled as a single string return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )