# coding=utf8

from functools import wraps
import traceback
import re
import sys

from UltiSnips import vim_helper


def wrap(func):
    """Decorator that will catch any Exception that 'func' throws and displays
    it in a new Vim scratch buffer."""

    @wraps(func)
    def wrapper(self, *args, **kwds):
        try:
            return func(self, *args, **kwds)
        except Exception as e:  # pylint: disable=bare-except
            msg = """An error occured. This is either a bug in UltiSnips or a bug in a
snippet definition. If you think this is a bug, please report it to
https://github.com/SirVer/ultisnips/issues/new
Please read and follow:
https://github.com/SirVer/ultisnips/blob/master/CONTRIBUTING.md#reproducing-bugs

Following is the full stack trace:
"""

            msg += traceback.format_exc()
            if hasattr(e, "snippet_info"):
                msg += "\nSnippet, caused error:\n"
                msg += re.sub("^(?=\S)", "  ", e.snippet_info, flags=re.MULTILINE)
            # snippet_code comes from _python_code.py, it's set manually for
            # providing error message with stacktrace of failed python code
            # inside of the snippet.
            if hasattr(e, "snippet_code"):
                _, _, tb = sys.exc_info()
                tb_top = traceback.extract_tb(tb)[-1]
                msg += "\nExecuted snippet code:\n"
                lines = e.snippet_code.split("\n")
                for number, line in enumerate(lines, 1):
                    msg += str(number).rjust(3)
                    prefix = "   " if line else ""
                    if tb_top[1] == number:
                        prefix = " > "
                    msg += prefix + line + "\n"

            # Vim sends no WinLeave msg here.
            if hasattr(self, "_leaving_buffer"):
                self._leaving_buffer()  # pylint:disable=protected-access
            vim_helper.new_scratch_buffer(msg)

    return wrapper
