migrate to snip.* namespace
This commit is contained in:
parent
7ead6fa178
commit
191ebd8e8b
@ -12,6 +12,7 @@ from UltiSnips.compatibility import as_unicode
|
|||||||
from UltiSnips.indent_util import IndentUtil
|
from UltiSnips.indent_util import IndentUtil
|
||||||
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 SnippetUtilForAction, SnippetUtilCursor
|
||||||
from UltiSnips.position import Position
|
from UltiSnips.position import Position
|
||||||
from UltiSnips.buffer_helper import VimBufferHelper
|
from UltiSnips.buffer_helper import VimBufferHelper
|
||||||
|
|
||||||
@ -91,7 +92,9 @@ class SnippetDefinition(object):
|
|||||||
if len(vim.current.buffer) == 1 and vim.current.buffer[0] == "":
|
if len(vim.current.buffer) == 1 and vim.current.buffer[0] == "":
|
||||||
return
|
return
|
||||||
|
|
||||||
return self._eval_code('holder["result"] = ' + self._context_code)
|
return self._eval_code('snip.context = ' + self._context_code, {
|
||||||
|
'context': None
|
||||||
|
}).context
|
||||||
|
|
||||||
def _eval_code(self, code, additional_locals={}):
|
def _eval_code(self, code, additional_locals={}):
|
||||||
code = "\n".join([
|
code = "\n".join([
|
||||||
@ -105,19 +108,20 @@ class SnippetDefinition(object):
|
|||||||
holder = {'result': False}
|
holder = {'result': False}
|
||||||
|
|
||||||
locals = {
|
locals = {
|
||||||
'holder': holder,
|
|
||||||
'window': current.window,
|
'window': current.window,
|
||||||
'buffer': current.buffer,
|
'buffer': current.buffer,
|
||||||
'line': current.window.cursor[0]-1,
|
'line': current.window.cursor[0]-1,
|
||||||
'column': current.window.cursor[1]-1,
|
'column': current.window.cursor[1]-1,
|
||||||
'cursor': (current.window.cursor[0]-1, current.window.cursor[1]-1)
|
'cursor': SnippetUtilCursor(current.window.cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
locals.update(additional_locals)
|
locals.update(additional_locals)
|
||||||
|
|
||||||
exec(code, locals)
|
snip = SnippetUtilForAction(locals)
|
||||||
|
|
||||||
return holder["result"]
|
exec(code, {'snip': snip})
|
||||||
|
|
||||||
|
return snip
|
||||||
|
|
||||||
def _execute_action(
|
def _execute_action(
|
||||||
self,
|
self,
|
||||||
@ -133,22 +137,15 @@ class SnippetDefinition(object):
|
|||||||
cursor_line_before = _vim.buf.line_till_cursor
|
cursor_line_before = _vim.buf.line_till_cursor
|
||||||
|
|
||||||
locals = {
|
locals = {
|
||||||
'new_cursor': None,
|
|
||||||
'context': context,
|
'context': context,
|
||||||
}
|
}
|
||||||
|
|
||||||
locals.update(additional_locals)
|
locals.update(additional_locals)
|
||||||
|
|
||||||
new_cursor, new_context = self._eval_code(
|
snip = self._eval_code(action, locals)
|
||||||
action + "\nholder['result'] = (new_cursor, context)",
|
|
||||||
locals
|
|
||||||
)
|
|
||||||
|
|
||||||
cursor_set_in_action = False
|
if snip.cursor.is_set():
|
||||||
if new_cursor:
|
vim.current.window.cursor = snip.cursor.to_vim_cursor()
|
||||||
if new_cursor != 'keep':
|
|
||||||
vim.current.window.cursor = (new_cursor[0]+1, new_cursor[1])
|
|
||||||
cursor_set_in_action = True
|
|
||||||
else:
|
else:
|
||||||
new_mark_pos = _vim.get_mark_pos(mark_to_use)
|
new_mark_pos = _vim.get_mark_pos(mark_to_use)
|
||||||
|
|
||||||
@ -163,8 +160,8 @@ class SnippetDefinition(object):
|
|||||||
|
|
||||||
if cursor_invalid:
|
if cursor_invalid:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
'line under the cursor was modified, but "new_cursor" ' +
|
'line under the cursor was modified, but "snip.cursor" ' +
|
||||||
'variable is not set; either set set "new_cursor" to ' +
|
'variable is not set; either set set "snip.cursor" to ' +
|
||||||
'new cursor position, or do not modify cursor line'
|
'new cursor position, or do not modify cursor line'
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -173,7 +170,8 @@ class SnippetDefinition(object):
|
|||||||
_vim.delete_mark(mark_to_use)
|
_vim.delete_mark(mark_to_use)
|
||||||
else:
|
else:
|
||||||
_vim.set_mark_from_pos(mark_to_use, mark_pos)
|
_vim.set_mark_from_pos(mark_to_use, mark_pos)
|
||||||
return cursor_set_in_action, new_context
|
|
||||||
|
return snip
|
||||||
|
|
||||||
def has_option(self, opt):
|
def has_option(self, opt):
|
||||||
"""Check if the named option is set."""
|
"""Check if the named option is set."""
|
||||||
@ -315,13 +313,13 @@ class SnippetDefinition(object):
|
|||||||
if 'pre_expand' in self._actions:
|
if 'pre_expand' in self._actions:
|
||||||
locals = {'buffer': buffer, 'visual_content': visual_content}
|
locals = {'buffer': buffer, 'visual_content': visual_content}
|
||||||
|
|
||||||
cursor_set_in_action, new_context = self._execute_action(
|
snip = self._execute_action(
|
||||||
self._actions['pre_expand'], self._context, locals
|
self._actions['pre_expand'], self._context, locals
|
||||||
)
|
)
|
||||||
|
|
||||||
self._context = new_context
|
self._context = snip.context
|
||||||
|
|
||||||
return buffer, cursor_set_in_action
|
return buffer, snip.cursor.is_set()
|
||||||
else:
|
else:
|
||||||
return buffer, False
|
return buffer, False
|
||||||
|
|
||||||
@ -334,13 +332,13 @@ class SnippetDefinition(object):
|
|||||||
'buffer': buffer
|
'buffer': buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor_set_in_action, new_context = self._execute_action(
|
snip = self._execute_action(
|
||||||
self._actions['post_expand'], snippets_stack[0].context, locals
|
self._actions['post_expand'], snippets_stack[0].context, locals
|
||||||
)
|
)
|
||||||
|
|
||||||
snippets_stack[0].context = new_context
|
snippets_stack[0].context = snip.context
|
||||||
|
|
||||||
return buffer, cursor_set_in_action
|
return buffer, snip.cursor.is_set()
|
||||||
else:
|
else:
|
||||||
return buffer, False
|
return buffer, False
|
||||||
|
|
||||||
@ -351,6 +349,7 @@ class SnippetDefinition(object):
|
|||||||
if 'post_jump' in self._actions:
|
if 'post_jump' in self._actions:
|
||||||
start = snippets_stack[0].start
|
start = snippets_stack[0].start
|
||||||
end = snippets_stack[0].end
|
end = snippets_stack[0].end
|
||||||
|
|
||||||
locals = {
|
locals = {
|
||||||
'tabstop': tabstop_number,
|
'tabstop': tabstop_number,
|
||||||
'jump_direction': jump_direction,
|
'jump_direction': jump_direction,
|
||||||
@ -360,13 +359,13 @@ class SnippetDefinition(object):
|
|||||||
'buffer': buffer
|
'buffer': buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor_set_in_action, new_context = self._execute_action(
|
snip = self._execute_action(
|
||||||
self._actions['post_jump'], snippets_stack[0].context, locals
|
self._actions['post_jump'], snippets_stack[0].context, locals
|
||||||
)
|
)
|
||||||
|
|
||||||
snippets_stack[0].context = new_context
|
snippets_stack[0].context = snip.context
|
||||||
|
|
||||||
return buffer, cursor_set_in_action
|
return buffer, snip.cursor.is_set()
|
||||||
else:
|
else:
|
||||||
return buffer, (False, None)
|
return buffer, (False, None)
|
||||||
|
|
||||||
|
@ -30,6 +30,48 @@ class _Tabs(object):
|
|||||||
_VisualContent = namedtuple('_VisualContent', ['mode', 'text'])
|
_VisualContent = namedtuple('_VisualContent', ['mode', 'text'])
|
||||||
|
|
||||||
|
|
||||||
|
class SnippetUtilForAction(dict):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(SnippetUtilForAction, self).__init__(*args, **kwargs)
|
||||||
|
self.__dict__ = self
|
||||||
|
|
||||||
|
|
||||||
|
class SnippetUtilCursor(object):
|
||||||
|
def __init__(self, cursor):
|
||||||
|
self._cursor = [cursor[0] - 1, cursor[1]]
|
||||||
|
self._set = False
|
||||||
|
|
||||||
|
def preserve(self):
|
||||||
|
self._set = True
|
||||||
|
self._cursor = [
|
||||||
|
_vim.buf.cursor[0],
|
||||||
|
_vim.buf.cursor[1],
|
||||||
|
]
|
||||||
|
|
||||||
|
def is_set(self):
|
||||||
|
return self._set
|
||||||
|
|
||||||
|
def set(self, line, column):
|
||||||
|
self.__setitem__(0, line)
|
||||||
|
self.__setitem__(1, column)
|
||||||
|
|
||||||
|
def to_vim_cursor(self):
|
||||||
|
return (self._cursor[0] + 1, self._cursor[1])
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
return self._cursor[index]
|
||||||
|
|
||||||
|
def __setitem__(self, index, value):
|
||||||
|
self._set = True
|
||||||
|
self._cursor[index] = value
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return 2
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str((self._cursor[0], self._cursor[1]))
|
||||||
|
|
||||||
|
|
||||||
class SnippetUtil(object):
|
class SnippetUtil(object):
|
||||||
|
|
||||||
"""Provides easy access to indentation, etc.
|
"""Provides easy access to indentation, etc.
|
||||||
|
@ -49,7 +49,7 @@ class ContextSnippets_UseContext(_VimTest):
|
|||||||
return "< " + ins + " >"
|
return "< " + ins + " >"
|
||||||
endglobal
|
endglobal
|
||||||
|
|
||||||
snippet a "desc" "wrap(buffer[line])" e
|
snippet a "desc" "wrap(snip.buffer[snip.line])" e
|
||||||
{ `!p snip.rv = context` }
|
{ `!p snip.rv = context` }
|
||||||
endsnippet
|
endsnippet
|
||||||
"""}
|
"""}
|
||||||
@ -59,7 +59,7 @@ class ContextSnippets_UseContext(_VimTest):
|
|||||||
|
|
||||||
class ContextSnippets_SnippetPriority(_VimTest):
|
class ContextSnippets_SnippetPriority(_VimTest):
|
||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
snippet i "desc" "re.search('err :=', buffer[line-1])" e
|
snippet i "desc" "re.search('err :=', snip.buffer[snip.line-1])" e
|
||||||
if err != nil {
|
if err != nil {
|
||||||
${1:// pass}
|
${1:// pass}
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ class ContextSnippets_ReportErrorOnIndexOutOfRange(_VimTest):
|
|||||||
skip_if = lambda self: 'Bug in Neovim.' \
|
skip_if = lambda self: 'Bug in Neovim.' \
|
||||||
if self.vim_flavor == 'neovim' else None
|
if self.vim_flavor == 'neovim' else None
|
||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
snippet e "desc" "buffer[123]" e
|
snippet e "desc" "snip.buffer[123]" e
|
||||||
error
|
error
|
||||||
endsnippet
|
endsnippet
|
||||||
"""}
|
"""}
|
||||||
@ -131,10 +131,10 @@ class ContextSnippets_ReportErrorOnIndexOutOfRange(_VimTest):
|
|||||||
|
|
||||||
class ContextSnippets_CursorIsZeroBased(_VimTest):
|
class ContextSnippets_CursorIsZeroBased(_VimTest):
|
||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
snippet e "desc" "cursor" e
|
snippet e "desc" "snip.cursor" e
|
||||||
`!p snip.rv = str(context)`
|
`!p snip.rv = str(context)`
|
||||||
endsnippet
|
endsnippet
|
||||||
"""}
|
"""}
|
||||||
|
|
||||||
keys = "e" + EX
|
keys = "e" + EX
|
||||||
wanted = "(2, 0)"
|
wanted = "(2, 1)"
|
||||||
|
@ -4,7 +4,7 @@ from test.constant import *
|
|||||||
|
|
||||||
class SnippetActions_PreActionModifiesBuffer(_VimTest):
|
class SnippetActions_PreActionModifiesBuffer(_VimTest):
|
||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
pre_expand "buffer[line:line] = ['\n']"
|
pre_expand "snip.buffer[snip.line:snip.line] = ['\n']"
|
||||||
snippet a "desc" "True" e
|
snippet a "desc" "True" e
|
||||||
abc
|
abc
|
||||||
endsnippet
|
endsnippet
|
||||||
@ -15,7 +15,7 @@ class SnippetActions_PreActionModifiesBuffer(_VimTest):
|
|||||||
|
|
||||||
class SnippetActions_PostActionModifiesBuffer(_VimTest):
|
class SnippetActions_PostActionModifiesBuffer(_VimTest):
|
||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
post_expand "buffer[line+1:line+1] = ['\n']"
|
post_expand "snip.buffer[snip.line+1:snip.line+1] = ['\n']"
|
||||||
snippet a "desc" "True" e
|
snippet a "desc" "True" e
|
||||||
abc
|
abc
|
||||||
endsnippet
|
endsnippet
|
||||||
@ -48,7 +48,7 @@ class SnippetActions_ErrorOnModificationSnippetLine(_VimTest):
|
|||||||
|
|
||||||
class SnippetActions_EnsureIndent(_VimTest):
|
class SnippetActions_EnsureIndent(_VimTest):
|
||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
pre_expand "buffer[line] = ' '*4; new_cursor = (cursor[0], 4)"
|
pre_expand "snip.buffer[snip.line] = ' '*4; snip.cursor[1] = 4"
|
||||||
snippet i "desc" "True" e
|
snippet i "desc" "True" e
|
||||||
if:
|
if:
|
||||||
$1
|
$1
|
||||||
@ -65,11 +65,11 @@ class SnippetActions_PostActionCanUseSnippetRange(_VimTest):
|
|||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
global !p
|
global !p
|
||||||
def ensure_newlines(start, end):
|
def ensure_newlines(start, end):
|
||||||
buffer[start[0]:start[0]] = ['\n'] * 2
|
snip.buffer[start[0]:start[0]] = ['\n'] * 2
|
||||||
buffer[end[0]+1:end[0]+1] = ['\n'] * 1
|
snip.buffer[end[0]+1:end[0]+1] = ['\n'] * 1
|
||||||
endglobal
|
endglobal
|
||||||
|
|
||||||
post_expand "ensure_newlines(snippet_start, snippet_end)"
|
post_expand "ensure_newlines(snip.snippet_start, snip.snippet_end)"
|
||||||
snippet i "desc"
|
snippet i "desc"
|
||||||
if
|
if
|
||||||
$1
|
$1
|
||||||
@ -94,10 +94,10 @@ class SnippetActions_CanModifyParentBody(_VimTest):
|
|||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
global !p
|
global !p
|
||||||
def ensure_newlines(start, end):
|
def ensure_newlines(start, end):
|
||||||
buffer[start[0]:start[0]] = ['\n'] * 2
|
snip.buffer[start[0]:start[0]] = ['\n'] * 2
|
||||||
endglobal
|
endglobal
|
||||||
|
|
||||||
post_expand "ensure_newlines(snippet_start, snippet_end)"
|
post_expand "ensure_newlines(snip.snippet_start, snip.snippet_end)"
|
||||||
snippet i "desc"
|
snippet i "desc"
|
||||||
if
|
if
|
||||||
$1
|
$1
|
||||||
@ -127,7 +127,7 @@ class SnippetActions_MoveParentSnippetFromChildInPreAction(_VimTest):
|
|||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
global !p
|
global !p
|
||||||
def insert_import():
|
def insert_import():
|
||||||
buffer[2:2] = ['import smthing', '']
|
snip.buffer[2:2] = ['import smthing', '']
|
||||||
endglobal
|
endglobal
|
||||||
|
|
||||||
pre_expand "insert_import()"
|
pre_expand "insert_import()"
|
||||||
@ -156,13 +156,13 @@ end"""
|
|||||||
class SnippetActions_CanExpandSnippetInDifferentPlace(_VimTest):
|
class SnippetActions_CanExpandSnippetInDifferentPlace(_VimTest):
|
||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
global !p
|
global !p
|
||||||
def expand_after_if():
|
def expand_after_if(snip):
|
||||||
global new_cursor
|
snip.buffer[snip.line] = snip.buffer[snip.line][:snip.column] + \
|
||||||
buffer[line] = buffer[line][:column] + buffer[line][column+1:]
|
snip.buffer[snip.line][snip.column+1:]
|
||||||
new_cursor = (line, buffer[line].index('if ')+3)
|
snip.cursor[1] = snip.buffer[snip.line].index('if ')+3
|
||||||
endglobal
|
endglobal
|
||||||
|
|
||||||
pre_expand "expand_after_if()"
|
pre_expand "expand_after_if(snip)"
|
||||||
snippet n "append not to if" w
|
snippet n "append not to if" w
|
||||||
not $0
|
not $0
|
||||||
endsnippet
|
endsnippet
|
||||||
@ -178,14 +178,13 @@ class SnippetActions_CanExpandSnippetInDifferentPlace(_VimTest):
|
|||||||
class SnippetActions_MoveVisual(_VimTest):
|
class SnippetActions_MoveVisual(_VimTest):
|
||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
global !p
|
global !p
|
||||||
def extract_method():
|
def extract_method(snip):
|
||||||
global new_cursor
|
del snip.buffer[snip.line]
|
||||||
del buffer[line]
|
snip.buffer[len(snip.buffer)-1:len(snip.buffer)-1] = ['']
|
||||||
buffer[len(buffer)-1:len(buffer)-1] = ['']
|
snip.cursor.set(len(snip.buffer)-2, 0)
|
||||||
new_cursor = (len(buffer)-2, 0)
|
|
||||||
endglobal
|
endglobal
|
||||||
|
|
||||||
pre_expand "extract_method()"
|
pre_expand "extract_method(snip)"
|
||||||
snippet n "append not to if" w
|
snippet n "append not to if" w
|
||||||
def $1:
|
def $1:
|
||||||
${VISUAL}
|
${VISUAL}
|
||||||
@ -210,7 +209,7 @@ def b:
|
|||||||
|
|
||||||
class SnippetActions_CanMirrorTabStopsOutsideOfSnippet(_VimTest):
|
class SnippetActions_CanMirrorTabStopsOutsideOfSnippet(_VimTest):
|
||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
post_jump "buffer[2] = 'debug({})'.format(tabstops[1].current_text)"
|
post_jump "snip.buffer[2] = 'debug({})'.format(snip.tabstops[1].current_text)"
|
||||||
snippet i "desc"
|
snippet i "desc"
|
||||||
if $1:
|
if $1:
|
||||||
$2
|
$2
|
||||||
@ -228,14 +227,14 @@ if test(some(complex(cond(a)))):
|
|||||||
class SnippetActions_CanExpandAnonSnippetInJumpAction(_VimTest):
|
class SnippetActions_CanExpandAnonSnippetInJumpAction(_VimTest):
|
||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
global !p
|
global !p
|
||||||
def expand_anon():
|
def expand_anon(snip):
|
||||||
if tabstop == 0:
|
if snip.tabstop == 0:
|
||||||
from UltiSnips import UltiSnips_Manager
|
from UltiSnips import UltiSnips_Manager
|
||||||
UltiSnips_Manager.expand_anon("a($2, $1)")
|
UltiSnips_Manager.expand_anon("a($2, $1)")
|
||||||
return 'keep'
|
snip.cursor.preserve()
|
||||||
endglobal
|
endglobal
|
||||||
|
|
||||||
post_jump "new_cursor = expand_anon()"
|
post_jump "expand_anon(snip)"
|
||||||
snippet i "desc"
|
snippet i "desc"
|
||||||
if ${1:cond}:
|
if ${1:cond}:
|
||||||
$0
|
$0
|
||||||
@ -249,14 +248,14 @@ class SnippetActions_CanExpandAnonSnippetInJumpAction(_VimTest):
|
|||||||
class SnippetActions_CanExpandAnonSnippetInJumpActionWhileSelected(_VimTest):
|
class SnippetActions_CanExpandAnonSnippetInJumpActionWhileSelected(_VimTest):
|
||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
global !p
|
global !p
|
||||||
def expand_anon():
|
def expand_anon(snip):
|
||||||
if tabstop == 0:
|
if snip.tabstop == 0:
|
||||||
from UltiSnips import UltiSnips_Manager
|
from UltiSnips import UltiSnips_Manager
|
||||||
UltiSnips_Manager.expand_anon(" // a($2, $1)")
|
UltiSnips_Manager.expand_anon(" // a($2, $1)")
|
||||||
return 'keep'
|
snip.cursor.preserve()
|
||||||
endglobal
|
endglobal
|
||||||
|
|
||||||
post_jump "new_cursor = expand_anon()"
|
post_jump "expand_anon(snip)"
|
||||||
snippet i "desc"
|
snippet i "desc"
|
||||||
if ${1:cond}:
|
if ${1:cond}:
|
||||||
${2:pass}
|
${2:pass}
|
||||||
@ -269,15 +268,7 @@ class SnippetActions_CanExpandAnonSnippetInJumpActionWhileSelected(_VimTest):
|
|||||||
|
|
||||||
class SnippetActions_CanUseContextFromContextMatch(_VimTest):
|
class SnippetActions_CanUseContextFromContextMatch(_VimTest):
|
||||||
files = { 'us/all.snippets': r"""
|
files = { 'us/all.snippets': r"""
|
||||||
global !p
|
pre_expand "snip.buffer[snip.line:snip.line] = [snip.context]"
|
||||||
def expand_anon():
|
|
||||||
if tabstop == 0:
|
|
||||||
from UltiSnips import UltiSnips_Manager
|
|
||||||
UltiSnips_Manager.expand_anon(" // a($2, $1)")
|
|
||||||
return 'keep'
|
|
||||||
endglobal
|
|
||||||
|
|
||||||
pre_expand "buffer[line:line] = [context]"
|
|
||||||
snippet i "desc" "'some context'" e
|
snippet i "desc" "'some context'" e
|
||||||
body
|
body
|
||||||
endsnippet
|
endsnippet
|
||||||
|
Loading…
x
Reference in New Issue
Block a user