print errors in regexp triggers
This commit is contained in:
parent
8250d33bca
commit
6cd168c5b4
51
pythonx/UltiSnips/err_to_scratch_buffer.py
Normal file
51
pythonx/UltiSnips/err_to_scratch_buffer.py
Normal file
@ -0,0 +1,51 @@
|
||||
# coding=utf8
|
||||
|
||||
from functools import wraps
|
||||
import traceback
|
||||
import re
|
||||
import sys
|
||||
|
||||
from UltiSnips import _vim
|
||||
|
||||
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.
|
||||
|
||||
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.new_scratch_buffer(msg)
|
||||
return wrapper
|
@ -11,16 +11,19 @@ import textwrap
|
||||
from UltiSnips import _vim
|
||||
from UltiSnips.compatibility import as_unicode
|
||||
from UltiSnips.indent_util import IndentUtil
|
||||
from UltiSnips.position import Position
|
||||
from UltiSnips.text import escape
|
||||
from UltiSnips.text_objects import SnippetInstance
|
||||
from UltiSnips.text_objects._python_code import SnippetUtilCursor, SnippetUtilForAction
|
||||
from UltiSnips.text_objects._python_code import \
|
||||
SnippetUtilCursor, SnippetUtilForAction
|
||||
|
||||
__WHITESPACE_SPLIT = re.compile(r"\s")
|
||||
|
||||
|
||||
def split_at_whitespace(string):
|
||||
"""Like string.split(), but keeps empty words as empty words."""
|
||||
return re.split(__WHITESPACE_SPLIT, string)
|
||||
|
||||
|
||||
def _words_for_line(trigger, before, num_words=None):
|
||||
"""Gets the final 'num_words' words from 'before'.
|
||||
|
||||
@ -131,27 +134,7 @@ class SnippetDefinition(object):
|
||||
try:
|
||||
exec(code, {'snip': snip})
|
||||
except Exception as e:
|
||||
e.snippet_info = textwrap.dedent("""
|
||||
Defined in: {}
|
||||
Trigger: {}
|
||||
Description: {}
|
||||
Context: {}
|
||||
Pre-expand: {}
|
||||
Post-expand: {}
|
||||
""").format(
|
||||
self._location,
|
||||
self._trigger,
|
||||
self._description,
|
||||
self._context_code if self._context_code else '<none>',
|
||||
self._actions['pre_expand'] if 'pre_expand' in self._actions
|
||||
else '<none>',
|
||||
self._actions['post_expand'] if 'post_expand' in self._actions
|
||||
else '<none>',
|
||||
code,
|
||||
)
|
||||
|
||||
e.snippet_code = code
|
||||
|
||||
self._make_debug_exception(e, code)
|
||||
raise
|
||||
|
||||
return snip
|
||||
@ -200,6 +183,28 @@ class SnippetDefinition(object):
|
||||
|
||||
return snip
|
||||
|
||||
def _make_debug_exception(self, e, code=''):
|
||||
e.snippet_info = textwrap.dedent("""
|
||||
Defined in: {}
|
||||
Trigger: {}
|
||||
Description: {}
|
||||
Context: {}
|
||||
Pre-expand: {}
|
||||
Post-expand: {}
|
||||
""").format(
|
||||
self._location,
|
||||
self._trigger,
|
||||
self._description,
|
||||
self._context_code if self._context_code else '<none>',
|
||||
self._actions['pre_expand'] if 'pre_expand' in self._actions
|
||||
else '<none>',
|
||||
self._actions['post_expand'] if 'post_expand' in self._actions
|
||||
else '<none>',
|
||||
code,
|
||||
)
|
||||
|
||||
e.snippet_code = code
|
||||
|
||||
def has_option(self, opt):
|
||||
"""Check if the named option is set."""
|
||||
return opt in self._opts
|
||||
@ -247,7 +252,12 @@ class SnippetDefinition(object):
|
||||
words = _words_for_line(self._trigger, before)
|
||||
|
||||
if 'r' in self._opts:
|
||||
try:
|
||||
match = self._re_match(before)
|
||||
except Exception as e:
|
||||
self._make_debug_exception(e)
|
||||
raise
|
||||
|
||||
elif 'w' in self._opts:
|
||||
words_len = len(self._trigger)
|
||||
words_prefix = words[:-words_len]
|
||||
|
@ -14,6 +14,7 @@ import re
|
||||
from contextlib import contextmanager
|
||||
|
||||
from UltiSnips import _vim
|
||||
from UltiSnips import err_to_scratch_buffer
|
||||
from UltiSnips._diff import diff, guess_edit
|
||||
from UltiSnips.compatibility import as_unicode
|
||||
from UltiSnips.position import Position
|
||||
@ -52,49 +53,6 @@ def _ask_snippets(snippets):
|
||||
return _ask_user(snippets, display)
|
||||
|
||||
|
||||
def err_to_scratch_buffer(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.
|
||||
|
||||
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.
|
||||
self._leaving_buffer() # pylint:disable=protected-access
|
||||
_vim.new_scratch_buffer(msg)
|
||||
return wrapper
|
||||
|
||||
|
||||
# TODO(sirver): This class is still too long. It should only contain public
|
||||
# facing methods, most of the private methods should be moved outside of it.
|
||||
class SnippetManager(object):
|
||||
@ -141,7 +99,7 @@ class SnippetManager(object):
|
||||
|
||||
self._reinit()
|
||||
|
||||
@err_to_scratch_buffer
|
||||
@err_to_scratch_buffer.wrap
|
||||
def jump_forwards(self):
|
||||
"""Jumps to the next tabstop."""
|
||||
_vim.command('let g:ulti_jump_forwards_res = 1')
|
||||
@ -150,7 +108,7 @@ class SnippetManager(object):
|
||||
_vim.command('let g:ulti_jump_forwards_res = 0')
|
||||
return self._handle_failure(self.forward_trigger)
|
||||
|
||||
@err_to_scratch_buffer
|
||||
@err_to_scratch_buffer.wrap
|
||||
def jump_backwards(self):
|
||||
"""Jumps to the previous tabstop."""
|
||||
_vim.command('let g:ulti_jump_backwards_res = 1')
|
||||
@ -159,7 +117,7 @@ class SnippetManager(object):
|
||||
_vim.command('let g:ulti_jump_backwards_res = 0')
|
||||
return self._handle_failure(self.backward_trigger)
|
||||
|
||||
@err_to_scratch_buffer
|
||||
@err_to_scratch_buffer.wrap
|
||||
def expand(self):
|
||||
"""Try to expand a snippet at the current position."""
|
||||
_vim.command('let g:ulti_expand_res = 1')
|
||||
@ -167,7 +125,7 @@ class SnippetManager(object):
|
||||
_vim.command('let g:ulti_expand_res = 0')
|
||||
self._handle_failure(self.expand_trigger)
|
||||
|
||||
@err_to_scratch_buffer
|
||||
@err_to_scratch_buffer.wrap
|
||||
def expand_or_jump(self):
|
||||
"""This function is used for people who wants to have the same trigger
|
||||
for expansion and forward jumping.
|
||||
@ -185,7 +143,7 @@ class SnippetManager(object):
|
||||
_vim.command('let g:ulti_expand_or_jump_res = 0')
|
||||
self._handle_failure(self.expand_trigger)
|
||||
|
||||
@err_to_scratch_buffer
|
||||
@err_to_scratch_buffer.wrap
|
||||
def snippets_in_current_scope(self, searchAll):
|
||||
"""Returns the snippets that could be expanded to Vim as a global
|
||||
variable."""
|
||||
@ -226,7 +184,7 @@ class SnippetManager(object):
|
||||
|
||||
|
||||
|
||||
@err_to_scratch_buffer
|
||||
@err_to_scratch_buffer.wrap
|
||||
def list_snippets(self):
|
||||
"""Shows the snippets that could be expanded to the User and let her
|
||||
select one."""
|
||||
@ -251,7 +209,7 @@ class SnippetManager(object):
|
||||
|
||||
return True
|
||||
|
||||
@err_to_scratch_buffer
|
||||
@err_to_scratch_buffer.wrap
|
||||
def add_snippet(self, trigger, value, description,
|
||||
options, ft='all', priority=0, context=None, actions={}):
|
||||
"""Add a snippet to the list of known snippets of the given 'ft'."""
|
||||
@ -260,7 +218,7 @@ class SnippetManager(object):
|
||||
description, options, {}, 'added',
|
||||
context, actions))
|
||||
|
||||
@err_to_scratch_buffer
|
||||
@err_to_scratch_buffer.wrap
|
||||
def expand_anon(
|
||||
self, value, trigger='', description='', options='',
|
||||
context=None, actions={}
|
||||
@ -317,7 +275,7 @@ class SnippetManager(object):
|
||||
self._buffer_filetypes[_vim.buf.number].insert(idx + 1, ft)
|
||||
idx += 1
|
||||
|
||||
@err_to_scratch_buffer
|
||||
@err_to_scratch_buffer.wrap
|
||||
def _cursor_moved(self):
|
||||
"""Called whenever the cursor moved."""
|
||||
self._should_update_textobjects = False
|
||||
@ -446,7 +404,7 @@ class SnippetManager(object):
|
||||
# are back in our buffer
|
||||
pass
|
||||
|
||||
@err_to_scratch_buffer
|
||||
@err_to_scratch_buffer.wrap
|
||||
def _save_last_visual_selection(self):
|
||||
"""This is called when the expand trigger is pressed in visual mode.
|
||||
Our job is to remember everything between '< and '> and pass it on to.
|
||||
@ -876,7 +834,7 @@ class SnippetManager(object):
|
||||
finally:
|
||||
self._inside_action = old_flag
|
||||
|
||||
@err_to_scratch_buffer
|
||||
@err_to_scratch_buffer.wrap
|
||||
def _track_change(self):
|
||||
self._should_update_textobjects = True
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user