Be less clever again about Where to check for changes.
- Remove duplicate lines from last and current text. Still, this needs to traverse the whole buffer. - Removed functionality from VimState that had nothing to do with 'State'.
This commit is contained in:
parent
2bddcdb76c
commit
36a1bc1c17
@ -1,6 +1,9 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
|
|
||||||
|
# TODO: Currently Caches whole buffer. Is this really needed?
|
||||||
|
# TODO: Currently searches whole buffer. Is this really needed?
|
||||||
|
|
||||||
import edit_distance
|
import edit_distance
|
||||||
from debug import debug
|
from debug import debug
|
||||||
|
|
||||||
@ -62,6 +65,134 @@ def echom(mes, *args):
|
|||||||
mes = mes % args
|
mes = mes % args
|
||||||
vim.command('echom %s' % vim_string(mes))
|
vim.command('echom %s' % vim_string(mes))
|
||||||
|
|
||||||
|
# TODO: this function should be moved
|
||||||
|
def select_span(r):
|
||||||
|
_unmap_select_mode_mapping()
|
||||||
|
|
||||||
|
delta = r.end - r.start
|
||||||
|
lineno, col = r.start.line, r.start.col
|
||||||
|
|
||||||
|
set_vim_cursor(lineno + 1, col)
|
||||||
|
|
||||||
|
# Case 1: Zero Length Tabstops
|
||||||
|
if delta.line == delta.col == 0:
|
||||||
|
if col == 0 or vim.eval("mode()") != 'i' and \
|
||||||
|
col < len(as_unicode(vim.current.buffer[lineno])):
|
||||||
|
feedkeys(r"\<Esc>i")
|
||||||
|
else:
|
||||||
|
feedkeys(r"\<Esc>a")
|
||||||
|
else:
|
||||||
|
# Case 2a: Non zero length and inclusive selection
|
||||||
|
# TODO: check with exclusive selection
|
||||||
|
# If a tabstop immediately starts with a newline, the selection
|
||||||
|
# must start after the last character in the current line. But if
|
||||||
|
# we are in insert mode and <Esc> out of it, we cannot go past the
|
||||||
|
# last character with move_one_right and therefore cannot
|
||||||
|
# visual-select this newline. We have to hack around this by adding
|
||||||
|
# an extra space which we can select. Note that this problem could
|
||||||
|
# be circumvent by selecting the tab backwards (that is starting
|
||||||
|
# at the end); one would not need to modify the line for this. This creates other
|
||||||
|
# trouble though
|
||||||
|
if col >= len(as_unicode(vim.current.buffer[lineno])):
|
||||||
|
vim.current.buffer[lineno] += " "
|
||||||
|
|
||||||
|
if delta.line:
|
||||||
|
move_lines = "%ij" % delta.line
|
||||||
|
else:
|
||||||
|
move_lines = ""
|
||||||
|
# Depending on the current mode and position, we
|
||||||
|
# might need to move escape out of the mode and this
|
||||||
|
# will move our cursor one left
|
||||||
|
if col != 0 and vim.eval("mode()") == 'i':
|
||||||
|
move_one_right = "l"
|
||||||
|
else:
|
||||||
|
move_one_right = ""
|
||||||
|
|
||||||
|
# After moving to the correct line, we go back to column 0
|
||||||
|
# and select right from there. Note that the we have to select
|
||||||
|
# one column less since vim's visual selection is including the
|
||||||
|
# ending while Python slicing is excluding the ending.
|
||||||
|
if r.end.col == 0:
|
||||||
|
# Selecting should end at beginning of line -> Select the
|
||||||
|
# previous line till its end
|
||||||
|
do_select = "k$"
|
||||||
|
elif r.end.col > 1:
|
||||||
|
do_select = "0%il" % (r.end.col-1)
|
||||||
|
else:
|
||||||
|
do_select = "0"
|
||||||
|
|
||||||
|
move_cmd = LangMapTranslator().translate(
|
||||||
|
r"\<Esc>%sv%s%s\<c-g>" % (move_one_right, move_lines, do_select)
|
||||||
|
)
|
||||||
|
|
||||||
|
feedkeys(move_cmd)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: this function should be moved
|
||||||
|
def _unmap_select_mode_mapping():
|
||||||
|
"""This function unmaps select mode mappings if so wished by the user.
|
||||||
|
Removes select mode mappings that can actually be typed by the user
|
||||||
|
(ie, ignores things like <Plug>).
|
||||||
|
"""
|
||||||
|
if int(vim.eval("g:UltiSnipsRemoveSelectModeMappings")):
|
||||||
|
ignores = vim.eval("g:UltiSnipsMappingsToIgnore") + ['UltiSnips']
|
||||||
|
|
||||||
|
for option in ("<buffer>", ""):
|
||||||
|
# Put all smaps into a var, and then read the var
|
||||||
|
vim.command(r"redir => _tmp_smaps | silent smap %s " % option +
|
||||||
|
"| redir END")
|
||||||
|
|
||||||
|
# Check if any mappings where found
|
||||||
|
all_maps = list(filter(len, vim.eval(r"_tmp_smaps").splitlines()))
|
||||||
|
if (len(all_maps) == 1 and all_maps[0][0] not in " sv"):
|
||||||
|
# "No maps found". String could be localized. Hopefully
|
||||||
|
# it doesn't start with any of these letters in any
|
||||||
|
# language
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Only keep mappings that should not be ignored
|
||||||
|
maps = [m for m in all_maps if
|
||||||
|
not any(i in m for i in ignores) and len(m.strip())]
|
||||||
|
|
||||||
|
for m in maps:
|
||||||
|
# The first three chars are the modes, that might be listed.
|
||||||
|
# We are not interested in them here.
|
||||||
|
trig = m[3:].split()[0]
|
||||||
|
|
||||||
|
# The bar separates commands
|
||||||
|
if trig[-1] == "|":
|
||||||
|
trig = trig[:-1] + "<Bar>"
|
||||||
|
|
||||||
|
# Special ones
|
||||||
|
if trig[0] == "<":
|
||||||
|
add = False
|
||||||
|
# Only allow these
|
||||||
|
for valid in ["Tab", "NL", "CR", "C-Tab", "BS"]:
|
||||||
|
if trig == "<%s>" % valid:
|
||||||
|
add = True
|
||||||
|
if not add:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# UltiSnips remaps <BS>. Keep this around.
|
||||||
|
if trig == "<BS>":
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Actually unmap it
|
||||||
|
try:
|
||||||
|
cmd = ("silent! sunmap %s %s") % (option, trig)
|
||||||
|
vim.command(cmd)
|
||||||
|
except:
|
||||||
|
# Bug 908139: ignore unmaps that fail because of
|
||||||
|
# unprintable characters. This is not ideal because we
|
||||||
|
# will not be able to unmap lhs with any unprintable
|
||||||
|
# character. If the lhs stats with a printable
|
||||||
|
# character this will leak to the user when he tries to
|
||||||
|
# type this character as a first in a selected tabstop.
|
||||||
|
# This case should be rare enough to not bother us
|
||||||
|
# though.
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class _SnippetDictionary(object):
|
class _SnippetDictionary(object):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self._added = []
|
self._added = []
|
||||||
@ -499,68 +630,6 @@ class VimState(object):
|
|||||||
self._lline = self._cline
|
self._lline = self._cline
|
||||||
self._cline = as_unicode(vim.current.buffer[line])
|
self._cline = as_unicode(vim.current.buffer[line])
|
||||||
|
|
||||||
def select_span(self, r):
|
|
||||||
self._unmap_select_mode_mapping()
|
|
||||||
|
|
||||||
delta = r.end - r.start
|
|
||||||
lineno, col = r.start.line, r.start.col
|
|
||||||
|
|
||||||
set_vim_cursor(lineno + 1, col)
|
|
||||||
|
|
||||||
# Case 1: Zero Length Tabstops
|
|
||||||
if delta.line == delta.col == 0:
|
|
||||||
if col == 0 or vim.eval("mode()") != 'i' and \
|
|
||||||
col < len(as_unicode(vim.current.buffer[lineno])):
|
|
||||||
feedkeys(r"\<Esc>i")
|
|
||||||
else:
|
|
||||||
feedkeys(r"\<Esc>a")
|
|
||||||
else:
|
|
||||||
# Case 2a: Non zero length and inclusive selection
|
|
||||||
# TODO: check with exclusive selection
|
|
||||||
# If a tabstop immediately starts with a newline, the selection
|
|
||||||
# must start after the last character in the current line. But if
|
|
||||||
# we are in insert mode and <Esc> out of it, we cannot go past the
|
|
||||||
# last character with move_one_right and therefore cannot
|
|
||||||
# visual-select this newline. We have to hack around this by adding
|
|
||||||
# an extra space which we can select. Note that this problem could
|
|
||||||
# be circumvent by selecting the tab backwards (that is starting
|
|
||||||
# at the end); one would not need to modify the line for this. This creates other
|
|
||||||
# trouble though
|
|
||||||
if col >= len(as_unicode(vim.current.buffer[lineno])):
|
|
||||||
vim.current.buffer[lineno] += " "
|
|
||||||
|
|
||||||
if delta.line:
|
|
||||||
move_lines = "%ij" % delta.line
|
|
||||||
else:
|
|
||||||
move_lines = ""
|
|
||||||
# Depending on the current mode and position, we
|
|
||||||
# might need to move escape out of the mode and this
|
|
||||||
# will move our cursor one left
|
|
||||||
if col != 0 and vim.eval("mode()") == 'i':
|
|
||||||
move_one_right = "l"
|
|
||||||
else:
|
|
||||||
move_one_right = ""
|
|
||||||
|
|
||||||
# After moving to the correct line, we go back to column 0
|
|
||||||
# and select right from there. Note that the we have to select
|
|
||||||
# one column less since vim's visual selection is including the
|
|
||||||
# ending while Python slicing is excluding the ending.
|
|
||||||
if r.end.col == 0:
|
|
||||||
# Selecting should end at beginning of line -> Select the
|
|
||||||
# previous line till its end
|
|
||||||
do_select = "k$"
|
|
||||||
elif r.end.col > 1:
|
|
||||||
do_select = "0%il" % (r.end.col-1)
|
|
||||||
else:
|
|
||||||
do_select = "0"
|
|
||||||
|
|
||||||
move_cmd = LangMapTranslator().translate(
|
|
||||||
r"\<Esc>%sv%s%s\<c-g>" % (move_one_right, move_lines, do_select)
|
|
||||||
)
|
|
||||||
|
|
||||||
feedkeys(move_cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def buf_changed(self):
|
def buf_changed(self):
|
||||||
return self._text_changed
|
return self._text_changed
|
||||||
buf_changed = property(buf_changed)
|
buf_changed = property(buf_changed)
|
||||||
@ -590,68 +659,6 @@ class VimState(object):
|
|||||||
###########################
|
###########################
|
||||||
# Private functions below #
|
# Private functions below #
|
||||||
###########################
|
###########################
|
||||||
def _unmap_select_mode_mapping(self):
|
|
||||||
"""This function unmaps select mode mappings if so wished by the user.
|
|
||||||
Removes select mode mappings that can actually be typed by the user
|
|
||||||
(ie, ignores things like <Plug>).
|
|
||||||
"""
|
|
||||||
if int(vim.eval("g:UltiSnipsRemoveSelectModeMappings")):
|
|
||||||
ignores = vim.eval("g:UltiSnipsMappingsToIgnore") + ['UltiSnips']
|
|
||||||
|
|
||||||
for option in ("<buffer>", ""):
|
|
||||||
# Put all smaps into a var, and then read the var
|
|
||||||
vim.command(r"redir => _tmp_smaps | silent smap %s " % option +
|
|
||||||
"| redir END")
|
|
||||||
|
|
||||||
# Check if any mappings where found
|
|
||||||
all_maps = list(filter(len, vim.eval(r"_tmp_smaps").splitlines()))
|
|
||||||
if (len(all_maps) == 1 and all_maps[0][0] not in " sv"):
|
|
||||||
# "No maps found". String could be localized. Hopefully
|
|
||||||
# it doesn't start with any of these letters in any
|
|
||||||
# language
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Only keep mappings that should not be ignored
|
|
||||||
maps = [m for m in all_maps if
|
|
||||||
not any(i in m for i in ignores) and len(m.strip())]
|
|
||||||
|
|
||||||
for m in maps:
|
|
||||||
# The first three chars are the modes, that might be listed.
|
|
||||||
# We are not interested in them here.
|
|
||||||
trig = m[3:].split()[0]
|
|
||||||
|
|
||||||
# The bar separates commands
|
|
||||||
if trig[-1] == "|":
|
|
||||||
trig = trig[:-1] + "<Bar>"
|
|
||||||
|
|
||||||
# Special ones
|
|
||||||
if trig[0] == "<":
|
|
||||||
add = False
|
|
||||||
# Only allow these
|
|
||||||
for valid in ["Tab", "NL", "CR", "C-Tab", "BS"]:
|
|
||||||
if trig == "<%s>" % valid:
|
|
||||||
add = True
|
|
||||||
if not add:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# UltiSnips remaps <BS>. Keep this around.
|
|
||||||
if trig == "<BS>":
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Actually unmap it
|
|
||||||
try:
|
|
||||||
cmd = ("silent! sunmap %s %s") % (option, trig)
|
|
||||||
vim.command(cmd)
|
|
||||||
except:
|
|
||||||
# Bug 908139: ignore unmaps that fail because of
|
|
||||||
# unprintable characters. This is not ideal because we
|
|
||||||
# will not be able to unmap lhs with any unprintable
|
|
||||||
# character. If the lhs stats with a printable
|
|
||||||
# character this will leak to the user when he tries to
|
|
||||||
# type this character as a first in a selected tabstop.
|
|
||||||
# This case should be rare enough to not bother us
|
|
||||||
# though.
|
|
||||||
pass
|
|
||||||
|
|
||||||
class SnippetManager(object):
|
class SnippetManager(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -797,64 +804,30 @@ class SnippetManager(object):
|
|||||||
ct = map(as_unicode, vim.current.buffer)
|
ct = map(as_unicode, vim.current.buffer)
|
||||||
lt = map(as_unicode, self._lvb[:])
|
lt = map(as_unicode, self._lvb[:])
|
||||||
|
|
||||||
cache = 10 # TODO: increase
|
lt_span = [0, len(lt)]
|
||||||
c0 = min(self._vstate.pos.line, self._vstate.ppos.line)
|
ct_span = [0, len(ct)]
|
||||||
c1 = max(self._vstate.pos.line, self._vstate.ppos.line)
|
initial_line = 0
|
||||||
|
|
||||||
def is_prefix(a, b):
|
# Cut down on lines searched for changes. Start from behind and
|
||||||
"""x: wieviele man vorne von a abtrennen muss, y: wieviele dnan gleich sind"""
|
# remove all equal lines. Then do the same from the front.
|
||||||
for y in range(len(b)-1, 0, -1):
|
while (lt[lt_span[1]-1] == ct[ct_span[1]-1] and
|
||||||
for x in range(0, len(a)-1):
|
(lt_span[0] < lt_span[1]) and
|
||||||
if a[x:x+y] == b[:y]:
|
(ct_span[0] < ct_span[1])):
|
||||||
return x, y
|
ct_span[1] -= 1
|
||||||
return None, None
|
lt_span[1] -= 1
|
||||||
|
while (lt[lt_span[0]] == ct[ct_span[0]] and
|
||||||
|
(lt_span[0] < lt_span[1]) and
|
||||||
|
(ct_span[0] < ct_span[1])):
|
||||||
|
ct_span[0] += 1
|
||||||
|
lt_span[0] += 1
|
||||||
|
initial_line += 1
|
||||||
|
ct_span[0] = max(0, ct_span[0] - 1)
|
||||||
|
lt_span[0] = max(0, lt_span[0] - 1)
|
||||||
|
initial_line = max(0, initial_line - 1)
|
||||||
|
|
||||||
def is_suffix(a, b):
|
lt = '\n'.join(lt[lt_span[0]:lt_span[1]])
|
||||||
return is_prefix(a[::-1], b[::-1])
|
ct = '\n'.join(ct[ct_span[0]:ct_span[1]])
|
||||||
|
self._csnippets[0].edited(edit_distance.edit_script(lt, ct, initial_line))
|
||||||
sl = max(0, c0 - cache)
|
|
||||||
el = c1 + cache
|
|
||||||
ctb = ct[sl:el+1]
|
|
||||||
ltb = lt[sl:el+1]
|
|
||||||
|
|
||||||
assert(sl == 0) # TODO
|
|
||||||
|
|
||||||
xs, ys = is_prefix(ctb, ltb) # TODO: stupid name for function
|
|
||||||
debug("xs: %r, ys: %r" % (xs, ys))
|
|
||||||
xe, ye = is_suffix(ctb, ltb)
|
|
||||||
debug("xe: %r, ye: %r" % (xe, ye))
|
|
||||||
lt_span = (0, sys.maxint)
|
|
||||||
ct_span = (0, sys.maxint)
|
|
||||||
if xs is not None and xe is not None:
|
|
||||||
assert(xs == 0 and xe == 0) # TODO
|
|
||||||
fdl_front = ys
|
|
||||||
fdl_back = len(vim.current.buffer) - ye - 1
|
|
||||||
if fdl_back < fdl_front:
|
|
||||||
overlap = fdl_front - fdl_back
|
|
||||||
debug("overlap: %r" % (overlap))
|
|
||||||
ys -= overlap
|
|
||||||
lt_span = (ys + xs - 1, len(ltb) - ye - xe)
|
|
||||||
ct_span = (ys - 1, fdl_back + 1)
|
|
||||||
|
|
||||||
|
|
||||||
debug("all lt: %r" % (lt))#[sl:el+1]))
|
|
||||||
debug("all ct: %r" % (ct))#[sl:el+1]))
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
# start_line = min(self._vstate.pos.line, self._vstate.ppos.line)
|
|
||||||
# ct = as_unicode('\n').join(map(as_unicode, vim.current.buffer[start_line:]))
|
|
||||||
# lt = as_unicode('\n').join(self._lvb[start_line:])
|
|
||||||
lt = lt[lt_span[0]:lt_span[1]]
|
|
||||||
ct = ct[ct_span[0]:ct_span[1]]
|
|
||||||
debug("lt: %r" % (lt))
|
|
||||||
debug("ct: %r" % (ct))
|
|
||||||
debug("lt_span: %r, ct_span: %r" % (lt_span, ct_span))
|
|
||||||
|
|
||||||
lt = '\n'.join(lt)
|
|
||||||
ct = '\n'.join(ct)
|
|
||||||
rv = edit_distance.edit_script(lt, ct, lt_span[0])
|
|
||||||
debug("rv: %r" % (rv,))
|
|
||||||
self._csnippets[0].edited(rv)
|
|
||||||
|
|
||||||
self._check_if_still_inside_snippet()
|
self._check_if_still_inside_snippet()
|
||||||
if self._csnippets:
|
if self._csnippets:
|
||||||
@ -929,7 +902,7 @@ class SnippetManager(object):
|
|||||||
if self._cs:
|
if self._cs:
|
||||||
self._ctab = self._cs.select_next_tab(backwards)
|
self._ctab = self._cs.select_next_tab(backwards)
|
||||||
if self._ctab:
|
if self._ctab:
|
||||||
self._vstate.select_span(self._ctab.span)
|
select_span(self._ctab.span)
|
||||||
jumped = True
|
jumped = True
|
||||||
if self._ctab.no == 0:
|
if self._ctab.no == 0:
|
||||||
self._current_snippet_is_done()
|
self._current_snippet_is_done()
|
||||||
|
@ -6,8 +6,6 @@ import sys
|
|||||||
|
|
||||||
# TODO: check test cases here. They are not up to date
|
# TODO: check test cases here. They are not up to date
|
||||||
|
|
||||||
from .debug import debug
|
|
||||||
|
|
||||||
def edit_script(a, b, sline = 0):
|
def edit_script(a, b, sline = 0):
|
||||||
d = defaultdict(list)
|
d = defaultdict(list)
|
||||||
seen = defaultdict(lambda: sys.maxint)
|
seen = defaultdict(lambda: sys.maxint)
|
||||||
|
6
test.py
6
test.py
@ -24,6 +24,8 @@
|
|||||||
# The testsuite will use ``screen`` to inject commands into the Vim under test,
|
# The testsuite will use ``screen`` to inject commands into the Vim under test,
|
||||||
# and will compare the resulting output to expected results.
|
# and will compare the resulting output to expected results.
|
||||||
#
|
#
|
||||||
|
#
|
||||||
|
# TODO: visual line selection -> replace with more, less and == amount of lines
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
@ -819,7 +821,6 @@ class TabStop_Shell_TextInNextLine(_VimTest):
|
|||||||
wanted = "hi hallo\nWeiterand more"
|
wanted = "hi hallo\nWeiterand more"
|
||||||
class TabStop_Shell_InDefValue_Leave(_VimTest):
|
class TabStop_Shell_InDefValue_Leave(_VimTest):
|
||||||
skip_on_windows = True
|
skip_on_windows = True
|
||||||
sleeptime = 0.09 # Do this very slowly
|
|
||||||
snippets = ("test", "Hallo ${1:now `echo fromecho`} end")
|
snippets = ("test", "Hallo ${1:now `echo fromecho`} end")
|
||||||
keys = "test" + EX + JF + "and more"
|
keys = "test" + EX + JF + "and more"
|
||||||
wanted = "Hallo now fromecho endand more"
|
wanted = "Hallo now fromecho endand more"
|
||||||
@ -841,7 +842,6 @@ class TabStop_Shell_TestEscapedCharsAndShellVars_Overwrite(_VimTest):
|
|||||||
|
|
||||||
class TabStop_Shell_ShebangPython(_VimTest):
|
class TabStop_Shell_ShebangPython(_VimTest):
|
||||||
skip_on_windows = True
|
skip_on_windows = True
|
||||||
sleeptime = 0.09 # Do this very slowly
|
|
||||||
snippets = ("test", """Hallo ${1:now `#!/usr/bin/env python
|
snippets = ("test", """Hallo ${1:now `#!/usr/bin/env python
|
||||||
print "Hallo Welt"
|
print "Hallo Welt"
|
||||||
`} end""")
|
`} end""")
|
||||||
@ -1432,7 +1432,6 @@ class Transformation_OptionReplaceGlobalMatchInReplace_ECR(_VimTest):
|
|||||||
keys = "test" + EX + "a, nice, building"
|
keys = "test" + EX + "a, nice, building"
|
||||||
wanted = "a, nice, building a, nice, building"
|
wanted = "a, nice, building a, nice, building"
|
||||||
class TransformationUsingBackspaceToDeleteDefaultValueInFirstTab_ECR(_VimTest):
|
class TransformationUsingBackspaceToDeleteDefaultValueInFirstTab_ECR(_VimTest):
|
||||||
sleeptime = 0.09 # Do this very slowly
|
|
||||||
snippets = ("test", "snip ${1/.+/(?0:m1)/} ${2/.+/(?0:m2)/} "
|
snippets = ("test", "snip ${1/.+/(?0:m1)/} ${2/.+/(?0:m2)/} "
|
||||||
"${1:default} ${2:def}")
|
"${1:default} ${2:def}")
|
||||||
keys = "test" + EX + BS + JF + "hi"
|
keys = "test" + EX + BS + JF + "hi"
|
||||||
@ -1787,7 +1786,6 @@ class Multiple_ManySnippetsOneTrigger_ECR(_VimTest):
|
|||||||
("test", "Case28", "This is Case 28"),
|
("test", "Case28", "This is Case 28"),
|
||||||
("test", "Case29", "This is Case 29"),
|
("test", "Case29", "This is Case 29"),
|
||||||
) #}}}
|
) #}}}
|
||||||
sleeptime = 0.09 # Do this very slowly
|
|
||||||
keys = "test" + EX + " " + ESC + ESC + "ahi"
|
keys = "test" + EX + " " + ESC + ESC + "ahi"
|
||||||
wanted = "testhi"
|
wanted = "testhi"
|
||||||
# End: Selecting Between Same Triggers #}}}
|
# End: Selecting Between Same Triggers #}}}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user