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 import _vim
|
||||||
from UltiSnips.compatibility import as_unicode
|
from UltiSnips.compatibility import as_unicode
|
||||||
from UltiSnips.indent_util import IndentUtil
|
from UltiSnips.indent_util import IndentUtil
|
||||||
from UltiSnips.position import Position
|
|
||||||
from UltiSnips.text import escape
|
from UltiSnips.text import escape
|
||||||
from UltiSnips.text_objects import SnippetInstance
|
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")
|
__WHITESPACE_SPLIT = re.compile(r"\s")
|
||||||
|
|
||||||
|
|
||||||
def split_at_whitespace(string):
|
def split_at_whitespace(string):
|
||||||
"""Like string.split(), but keeps empty words as empty words."""
|
"""Like string.split(), but keeps empty words as empty words."""
|
||||||
return re.split(__WHITESPACE_SPLIT, string)
|
return re.split(__WHITESPACE_SPLIT, string)
|
||||||
|
|
||||||
|
|
||||||
def _words_for_line(trigger, before, num_words=None):
|
def _words_for_line(trigger, before, num_words=None):
|
||||||
"""Gets the final 'num_words' words from 'before'.
|
"""Gets the final 'num_words' words from 'before'.
|
||||||
|
|
||||||
@ -131,27 +134,7 @@ class SnippetDefinition(object):
|
|||||||
try:
|
try:
|
||||||
exec(code, {'snip': snip})
|
exec(code, {'snip': snip})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
e.snippet_info = textwrap.dedent("""
|
self._make_debug_exception(e, code)
|
||||||
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
|
|
||||||
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
return snip
|
return snip
|
||||||
@ -200,6 +183,28 @@ class SnippetDefinition(object):
|
|||||||
|
|
||||||
return snip
|
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):
|
def has_option(self, opt):
|
||||||
"""Check if the named option is set."""
|
"""Check if the named option is set."""
|
||||||
return opt in self._opts
|
return opt in self._opts
|
||||||
@ -247,7 +252,12 @@ class SnippetDefinition(object):
|
|||||||
words = _words_for_line(self._trigger, before)
|
words = _words_for_line(self._trigger, before)
|
||||||
|
|
||||||
if 'r' in self._opts:
|
if 'r' in self._opts:
|
||||||
|
try:
|
||||||
match = self._re_match(before)
|
match = self._re_match(before)
|
||||||
|
except Exception as e:
|
||||||
|
self._make_debug_exception(e)
|
||||||
|
raise
|
||||||
|
|
||||||
elif 'w' in self._opts:
|
elif 'w' in self._opts:
|
||||||
words_len = len(self._trigger)
|
words_len = len(self._trigger)
|
||||||
words_prefix = words[:-words_len]
|
words_prefix = words[:-words_len]
|
||||||
|
@ -14,6 +14,7 @@ import re
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from UltiSnips import _vim
|
from UltiSnips import _vim
|
||||||
|
from UltiSnips import err_to_scratch_buffer
|
||||||
from UltiSnips._diff import diff, guess_edit
|
from UltiSnips._diff import diff, guess_edit
|
||||||
from UltiSnips.compatibility import as_unicode
|
from UltiSnips.compatibility import as_unicode
|
||||||
from UltiSnips.position import Position
|
from UltiSnips.position import Position
|
||||||
@ -52,49 +53,6 @@ def _ask_snippets(snippets):
|
|||||||
return _ask_user(snippets, display)
|
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
|
# 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.
|
# facing methods, most of the private methods should be moved outside of it.
|
||||||
class SnippetManager(object):
|
class SnippetManager(object):
|
||||||
@ -141,7 +99,7 @@ class SnippetManager(object):
|
|||||||
|
|
||||||
self._reinit()
|
self._reinit()
|
||||||
|
|
||||||
@err_to_scratch_buffer
|
@err_to_scratch_buffer.wrap
|
||||||
def jump_forwards(self):
|
def jump_forwards(self):
|
||||||
"""Jumps to the next tabstop."""
|
"""Jumps to the next tabstop."""
|
||||||
_vim.command('let g:ulti_jump_forwards_res = 1')
|
_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')
|
_vim.command('let g:ulti_jump_forwards_res = 0')
|
||||||
return self._handle_failure(self.forward_trigger)
|
return self._handle_failure(self.forward_trigger)
|
||||||
|
|
||||||
@err_to_scratch_buffer
|
@err_to_scratch_buffer.wrap
|
||||||
def jump_backwards(self):
|
def jump_backwards(self):
|
||||||
"""Jumps to the previous tabstop."""
|
"""Jumps to the previous tabstop."""
|
||||||
_vim.command('let g:ulti_jump_backwards_res = 1')
|
_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')
|
_vim.command('let g:ulti_jump_backwards_res = 0')
|
||||||
return self._handle_failure(self.backward_trigger)
|
return self._handle_failure(self.backward_trigger)
|
||||||
|
|
||||||
@err_to_scratch_buffer
|
@err_to_scratch_buffer.wrap
|
||||||
def expand(self):
|
def expand(self):
|
||||||
"""Try to expand a snippet at the current position."""
|
"""Try to expand a snippet at the current position."""
|
||||||
_vim.command('let g:ulti_expand_res = 1')
|
_vim.command('let g:ulti_expand_res = 1')
|
||||||
@ -167,7 +125,7 @@ class SnippetManager(object):
|
|||||||
_vim.command('let g:ulti_expand_res = 0')
|
_vim.command('let g:ulti_expand_res = 0')
|
||||||
self._handle_failure(self.expand_trigger)
|
self._handle_failure(self.expand_trigger)
|
||||||
|
|
||||||
@err_to_scratch_buffer
|
@err_to_scratch_buffer.wrap
|
||||||
def expand_or_jump(self):
|
def expand_or_jump(self):
|
||||||
"""This function is used for people who wants to have the same trigger
|
"""This function is used for people who wants to have the same trigger
|
||||||
for expansion and forward jumping.
|
for expansion and forward jumping.
|
||||||
@ -185,7 +143,7 @@ class SnippetManager(object):
|
|||||||
_vim.command('let g:ulti_expand_or_jump_res = 0')
|
_vim.command('let g:ulti_expand_or_jump_res = 0')
|
||||||
self._handle_failure(self.expand_trigger)
|
self._handle_failure(self.expand_trigger)
|
||||||
|
|
||||||
@err_to_scratch_buffer
|
@err_to_scratch_buffer.wrap
|
||||||
def snippets_in_current_scope(self, searchAll):
|
def snippets_in_current_scope(self, searchAll):
|
||||||
"""Returns the snippets that could be expanded to Vim as a global
|
"""Returns the snippets that could be expanded to Vim as a global
|
||||||
variable."""
|
variable."""
|
||||||
@ -226,7 +184,7 @@ class SnippetManager(object):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@err_to_scratch_buffer
|
@err_to_scratch_buffer.wrap
|
||||||
def list_snippets(self):
|
def list_snippets(self):
|
||||||
"""Shows the snippets that could be expanded to the User and let her
|
"""Shows the snippets that could be expanded to the User and let her
|
||||||
select one."""
|
select one."""
|
||||||
@ -251,7 +209,7 @@ class SnippetManager(object):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@err_to_scratch_buffer
|
@err_to_scratch_buffer.wrap
|
||||||
def add_snippet(self, trigger, value, description,
|
def add_snippet(self, trigger, value, description,
|
||||||
options, ft='all', priority=0, context=None, actions={}):
|
options, ft='all', priority=0, context=None, actions={}):
|
||||||
"""Add a snippet to the list of known snippets of the given 'ft'."""
|
"""Add a snippet to the list of known snippets of the given 'ft'."""
|
||||||
@ -260,7 +218,7 @@ class SnippetManager(object):
|
|||||||
description, options, {}, 'added',
|
description, options, {}, 'added',
|
||||||
context, actions))
|
context, actions))
|
||||||
|
|
||||||
@err_to_scratch_buffer
|
@err_to_scratch_buffer.wrap
|
||||||
def expand_anon(
|
def expand_anon(
|
||||||
self, value, trigger='', description='', options='',
|
self, value, trigger='', description='', options='',
|
||||||
context=None, actions={}
|
context=None, actions={}
|
||||||
@ -317,7 +275,7 @@ class SnippetManager(object):
|
|||||||
self._buffer_filetypes[_vim.buf.number].insert(idx + 1, ft)
|
self._buffer_filetypes[_vim.buf.number].insert(idx + 1, ft)
|
||||||
idx += 1
|
idx += 1
|
||||||
|
|
||||||
@err_to_scratch_buffer
|
@err_to_scratch_buffer.wrap
|
||||||
def _cursor_moved(self):
|
def _cursor_moved(self):
|
||||||
"""Called whenever the cursor moved."""
|
"""Called whenever the cursor moved."""
|
||||||
self._should_update_textobjects = False
|
self._should_update_textobjects = False
|
||||||
@ -446,7 +404,7 @@ class SnippetManager(object):
|
|||||||
# are back in our buffer
|
# are back in our buffer
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@err_to_scratch_buffer
|
@err_to_scratch_buffer.wrap
|
||||||
def _save_last_visual_selection(self):
|
def _save_last_visual_selection(self):
|
||||||
"""This is called when the expand trigger is pressed in visual mode.
|
"""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.
|
Our job is to remember everything between '< and '> and pass it on to.
|
||||||
@ -876,7 +834,7 @@ class SnippetManager(object):
|
|||||||
finally:
|
finally:
|
||||||
self._inside_action = old_flag
|
self._inside_action = old_flag
|
||||||
|
|
||||||
@err_to_scratch_buffer
|
@err_to_scratch_buffer.wrap
|
||||||
def _track_change(self):
|
def _track_change(self):
|
||||||
self._should_update_textobjects = True
|
self._should_update_textobjects = True
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user