"""
When an exception is encountered, save context information about the exception
to a file, so that we can later invoke an editor on the last exception, bringing
it automatically at the correct file:line location.

Install this feature in your sitecustomize.py file by importing this module.
"""

import sys, os
from os.path import join, isabs, realpath

__all__ = ('invoke_editor', 'install_twisted',)



sfn = '.python_last_exception'

def get_filename():
    return join(os.environ.get('HOME', '/tmp'), sfn)

def atexit_handler():
    if not hasattr(sys, 'last_traceback'):
        return
    else:
        save_context(sys.last_traceback)
        
def save_context(tb):
    import traceback

    cwd = os.environ.get('PWD', '')
    fn = get_filename()

    f = open(fn, 'w')
    f.write(cwd + '\n')
    traceback.print_tb(tb, file=f)
    f.close()

def install_twisted():
    "Install a context saver for Twisted code."
    from twisted.python.log import theLogPublisher
    theLogPublisher.addObserver(twisted_handler)

def twisted_handler(edict):
    if 'failure' in edict:
        save_context(edict['failure'].tb)


# You will need to have a function that takes a filename and line number and
# opens the file and highlights it, defined in your Emacs environment.
#
# FIXME: we could modify the code that calls emacs to catch errors and inject
# the defun ourselves if not present.
lisp_defuns = """

  (defun find-file-at-line (filename line traceback)
   \"Opens the given filename and center and highlight at the given
    line number.\"
   (find-file filename)
   (goto-line line)
   (recenter)
   (set-mark (point))
   (next-line)
   )

"""

def invoke_editor():
    import re

    try:
        f = open(get_filename())
    except IOError, e:
        print >> sys.stderr, "error: can't find %s" % get_filename()
        return

    fiter = iter(f)
    cwd = fiter.next().strip()
    tbtxt = list(fiter)

    # Keep the last line that matches...
    mo = None
    for line in reversed(tbtxt):
        mo = re.match('.*File "(.*)", line ([0-9]+)', line) or mo
        if mo:
            break
    else: # Not found
        return

    if mo:
        filename, lineno = mo.group(1, 2)
        lineno = int(lineno)
        if not isabs(filename):
            filename = realpath(join(cwd, filename))

    editor = os.environ.get("SAVERR_EDITOR", "emacs")
    if editor not in EDITORS:
        print >> sys.stderr, "error: unknown editor - %s" % editor
        return

    print filename, lineno
    EDITORS[editor](filename, lineno)
    return 0

def invoke_emacs(filename, lineno):
    import subprocess
    elisp = '(find-file-at-line "%s" %d)' % (filename, lineno)
    r = subprocess.call(('emacsclient', '-n', '-e', elisp), shell=False)


def _vim(process, filename, lineno):
    import subprocess
    subprocess.call("%s \"%s\" +%d" % (process, filename, lineno), shell=True)

def invoke_vim(filename, lineno):
    _vim("vim", filename, lineno)

def invoke_gvim(filename, lineno):
    _vim("gvim", filename, lineno)

EDITORS = {
    'emacs' : invoke_emacs,
    'vim' : invoke_vim,
    'gvim' : invoke_gvim
}



if __name__ == '__main__':
    sys.exit(invoke_editor())
else:
    import atexit
    atexit.register(atexit_handler)


