mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
850 lines
24 KiB
Python
850 lines
24 KiB
Python
import itertools
|
|
import typing as tp
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
|
|
import xonsh.parsers.completion_context as ctx
|
|
from xonsh.pytest.tools import ON_WINDOWS
|
|
from xonsh.parsers.completion_context import (
|
|
CommandArg,
|
|
CommandContext,
|
|
CompletionContextParser,
|
|
PythonContext,
|
|
)
|
|
|
|
DEBUG = False
|
|
MISSING = object()
|
|
X = "\x00" # cursor marker
|
|
PARSER: tp.Optional[CompletionContextParser] = None
|
|
|
|
|
|
@pytest.fixture(scope="module", autouse=True)
|
|
def parser():
|
|
global PARSER
|
|
PARSER = CompletionContextParser(debug=DEBUG)
|
|
patcher = None
|
|
if ON_WINDOWS:
|
|
# on-windows has an option for interactive sessions. Overriding the lazyObject
|
|
patcher = mock.patch.object(
|
|
ctx,
|
|
"LINE_CONT_REPLACEMENT_DIFF",
|
|
("\\\n", "", -2),
|
|
)
|
|
patcher.start()
|
|
yield
|
|
PARSER = None
|
|
if ON_WINDOWS and patcher:
|
|
patcher.stop()
|
|
|
|
|
|
def parse(command, inner_index):
|
|
return PARSER.parse(command, inner_index)
|
|
|
|
|
|
def assert_match(
|
|
commandline, command_context=MISSING, python_context=MISSING, is_main_command=False
|
|
):
|
|
if X in commandline:
|
|
index = commandline.index(X)
|
|
commandline = commandline.replace(X, "")
|
|
else:
|
|
index = len(commandline)
|
|
context = parse(commandline, index)
|
|
if context is None:
|
|
raise SyntaxError(
|
|
"Failed to parse the commandline - set DEBUG = True in this file to see the error"
|
|
)
|
|
if is_main_command and python_context is MISSING:
|
|
python_context = PythonContext(commandline, index)
|
|
if command_context is not MISSING:
|
|
assert context.command == command_context
|
|
if python_context is not MISSING:
|
|
assert context.python == python_context
|
|
|
|
|
|
COMMAND_EXAMPLES = (
|
|
(f"comm{X}", CommandContext(args=(), arg_index=0, prefix="comm")),
|
|
(f" comm{X}", CommandContext(args=(), arg_index=0, prefix="comm")),
|
|
(f"comm{X}and", CommandContext(args=(), arg_index=0, prefix="comm", suffix="and")),
|
|
(f"command {X}", CommandContext(args=(CommandArg("command"),), arg_index=1)),
|
|
(f"{X} command", CommandContext(args=(CommandArg("command"),), arg_index=0)),
|
|
(f" command {X}", CommandContext(args=(CommandArg("command"),), arg_index=1)),
|
|
(
|
|
f"command --{X}",
|
|
CommandContext(args=(CommandArg("command"),), arg_index=1, prefix="--"),
|
|
),
|
|
(
|
|
f"command a {X}",
|
|
CommandContext(args=(CommandArg("command"), CommandArg("a")), arg_index=2),
|
|
),
|
|
(
|
|
f"command a b{X}",
|
|
CommandContext(
|
|
args=(CommandArg("command"), CommandArg("a")), arg_index=2, prefix="b"
|
|
),
|
|
),
|
|
(
|
|
f"command a b{X}",
|
|
CommandContext(
|
|
args=(CommandArg("command"), CommandArg("a")), arg_index=2, prefix="b"
|
|
),
|
|
),
|
|
(
|
|
f"command {X} a",
|
|
CommandContext(args=(CommandArg("command"), CommandArg("a")), arg_index=1),
|
|
),
|
|
(
|
|
f"command a {X} b",
|
|
CommandContext(
|
|
args=(CommandArg("command"), CommandArg("a"), CommandArg("b")), arg_index=2
|
|
),
|
|
),
|
|
(
|
|
f"command -{X} b",
|
|
CommandContext(
|
|
args=(CommandArg("command"), CommandArg("b")), arg_index=1, prefix="-"
|
|
),
|
|
),
|
|
(
|
|
f"command a {X}b",
|
|
CommandContext(
|
|
args=(CommandArg("command"), CommandArg("a")), arg_index=2, suffix="b"
|
|
),
|
|
),
|
|
(
|
|
f"command a{X}b",
|
|
CommandContext(
|
|
args=(CommandArg("command"),), arg_index=1, prefix="a", suffix="b"
|
|
),
|
|
),
|
|
(
|
|
f"'comm and' a{X}b",
|
|
CommandContext(
|
|
args=(CommandArg("comm and", opening_quote="'", closing_quote="'"),),
|
|
arg_index=1,
|
|
prefix="a",
|
|
suffix="b",
|
|
),
|
|
),
|
|
)
|
|
|
|
EMPTY_COMMAND_EXAMPLES = (
|
|
(f"{X}", CommandContext((), 0)),
|
|
(f" {X}", CommandContext((), 0)),
|
|
(f"{X} ", CommandContext((), 0)),
|
|
(f" {X} ", CommandContext((), 0)),
|
|
)
|
|
|
|
STRING_ARGS_EXAMPLES = (
|
|
(
|
|
f"'comm an{X}d'",
|
|
CommandContext(
|
|
args=(),
|
|
arg_index=0,
|
|
prefix="comm an",
|
|
suffix="d",
|
|
opening_quote="'",
|
|
closing_quote="'",
|
|
),
|
|
),
|
|
(
|
|
f"'comm and{X}'",
|
|
CommandContext(
|
|
args=(),
|
|
arg_index=0,
|
|
prefix="comm and",
|
|
suffix="",
|
|
opening_quote="'",
|
|
closing_quote="'",
|
|
),
|
|
),
|
|
(
|
|
f"'comm {X}'",
|
|
CommandContext(
|
|
args=(),
|
|
arg_index=0,
|
|
prefix="comm ",
|
|
suffix="",
|
|
opening_quote="'",
|
|
closing_quote="'",
|
|
),
|
|
),
|
|
(
|
|
f'"comm an{X}d"',
|
|
CommandContext(
|
|
args=(),
|
|
arg_index=0,
|
|
prefix="comm an",
|
|
suffix="d",
|
|
opening_quote='"',
|
|
closing_quote='"',
|
|
),
|
|
),
|
|
(
|
|
f"'''comm an{X}d'''",
|
|
CommandContext(
|
|
args=(),
|
|
arg_index=0,
|
|
prefix="comm an",
|
|
suffix="d",
|
|
opening_quote="'''",
|
|
closing_quote="'''",
|
|
),
|
|
),
|
|
(
|
|
f"fr'comm an{X}d'",
|
|
CommandContext(
|
|
args=(),
|
|
arg_index=0,
|
|
prefix="comm an",
|
|
suffix="d",
|
|
opening_quote="fr'",
|
|
closing_quote="'",
|
|
),
|
|
),
|
|
(
|
|
f"'()+{X}'",
|
|
CommandContext(
|
|
args=(), arg_index=0, prefix="()+", opening_quote="'", closing_quote="'"
|
|
),
|
|
),
|
|
(
|
|
f"'comm and'{X}",
|
|
CommandContext(
|
|
args=(),
|
|
arg_index=0,
|
|
prefix="comm and",
|
|
opening_quote="'",
|
|
closing_quote="'",
|
|
is_after_closing_quote=True,
|
|
),
|
|
),
|
|
(
|
|
f"'''comm and'''{X}",
|
|
CommandContext(
|
|
args=(),
|
|
arg_index=0,
|
|
prefix="comm and",
|
|
opening_quote="'''",
|
|
closing_quote="'''",
|
|
is_after_closing_quote=True,
|
|
),
|
|
),
|
|
)
|
|
|
|
COMMAND_EXAMPLES += STRING_ARGS_EXAMPLES
|
|
COMMAND_EXAMPLES += EMPTY_COMMAND_EXAMPLES
|
|
|
|
|
|
@pytest.mark.parametrize("commandline, context", COMMAND_EXAMPLES)
|
|
def test_command(commandline, context):
|
|
assert_match(commandline, context, is_main_command=True)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"commandline, context",
|
|
tuple(
|
|
(commandline, context)
|
|
for commandline, context in STRING_ARGS_EXAMPLES
|
|
if commandline.endswith("'") or commandline.endswith('"')
|
|
),
|
|
)
|
|
def test_partial_string_arg(commandline, context):
|
|
partial_commandline = commandline.rstrip("\"'")
|
|
partial_context = context._replace(closing_quote="")
|
|
assert_match(partial_commandline, partial_context, is_main_command=True)
|
|
|
|
|
|
CONT = "\\" "\n"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"commandline, context",
|
|
(
|
|
# line continuations:
|
|
(
|
|
f"echo {CONT}a {X}",
|
|
CommandContext(args=(CommandArg("echo"), CommandArg("a")), arg_index=2),
|
|
),
|
|
(
|
|
f"echo {CONT}{X}a {CONT} b",
|
|
CommandContext(
|
|
args=(CommandArg("echo"), CommandArg("b")), arg_index=1, suffix="a"
|
|
),
|
|
),
|
|
(
|
|
f"echo a{CONT}{X}b",
|
|
CommandContext(
|
|
args=(CommandArg("echo"),), arg_index=1, prefix="a", suffix="b"
|
|
),
|
|
),
|
|
(
|
|
f"echo a{X}{CONT}b",
|
|
CommandContext(
|
|
args=(CommandArg("echo"),), arg_index=1, prefix="a", suffix="b"
|
|
),
|
|
),
|
|
(
|
|
f"echo ${CONT}(a) {CONT} {X}b",
|
|
CommandContext(
|
|
args=(CommandArg("echo"), CommandArg("$(a)")), arg_index=2, suffix="b"
|
|
),
|
|
),
|
|
# line continuations in strings:
|
|
(
|
|
f"echo 'a{CONT}{X}b'",
|
|
CommandContext(
|
|
args=(CommandArg("echo"),),
|
|
arg_index=1,
|
|
prefix="a",
|
|
suffix="b",
|
|
opening_quote="'",
|
|
closing_quote="'",
|
|
),
|
|
),
|
|
(
|
|
f"echo '''a{CONT}{X}b'''",
|
|
CommandContext(
|
|
args=(CommandArg("echo"),),
|
|
arg_index=1,
|
|
prefix="a",
|
|
suffix="b",
|
|
opening_quote="'''",
|
|
closing_quote="'''",
|
|
),
|
|
),
|
|
(
|
|
f"echo 'a{CONT}{X}b",
|
|
CommandContext(
|
|
args=(CommandArg("echo"),),
|
|
arg_index=1,
|
|
prefix="a",
|
|
suffix="b",
|
|
opening_quote="'",
|
|
),
|
|
),
|
|
(
|
|
f"echo '''a{CONT}{X}b",
|
|
CommandContext(
|
|
args=(CommandArg("echo"),),
|
|
arg_index=1,
|
|
prefix="a",
|
|
suffix="b",
|
|
opening_quote="'''",
|
|
),
|
|
),
|
|
(
|
|
f"echo ''{CONT}'a{X}b",
|
|
CommandContext(
|
|
args=(CommandArg("echo"),),
|
|
arg_index=1,
|
|
prefix="a",
|
|
suffix="b",
|
|
opening_quote="'''",
|
|
),
|
|
),
|
|
(
|
|
f"echo '''a{CONT}{X} b",
|
|
CommandContext(
|
|
args=(CommandArg("echo"),),
|
|
arg_index=1,
|
|
prefix="a",
|
|
suffix=" b",
|
|
opening_quote="'''",
|
|
),
|
|
),
|
|
# triple-quoted strings:
|
|
(
|
|
f"echo '''a\nb{X}\nc'''",
|
|
CommandContext(
|
|
args=(CommandArg("echo"),),
|
|
arg_index=1,
|
|
prefix="a\nb",
|
|
suffix="\nc",
|
|
opening_quote="'''",
|
|
closing_quote="'''",
|
|
),
|
|
),
|
|
(
|
|
f"echo '''a\n b{X} \n c'''",
|
|
CommandContext(
|
|
args=(CommandArg("echo"),),
|
|
arg_index=1,
|
|
prefix="a\n b",
|
|
suffix=" \n c",
|
|
opening_quote="'''",
|
|
closing_quote="'''",
|
|
),
|
|
),
|
|
# partial triple-quoted strings:
|
|
(
|
|
f"echo '''a\nb{X}\nc",
|
|
CommandContext(
|
|
args=(CommandArg("echo"),),
|
|
arg_index=1,
|
|
prefix="a\nb",
|
|
suffix="\nc",
|
|
opening_quote="'''",
|
|
),
|
|
),
|
|
(
|
|
f"echo '''a\n b{X} \n c",
|
|
CommandContext(
|
|
args=(CommandArg("echo"),),
|
|
arg_index=1,
|
|
prefix="a\n b",
|
|
suffix=" \n c",
|
|
opening_quote="'''",
|
|
),
|
|
),
|
|
),
|
|
)
|
|
def test_multiline_command(commandline, context):
|
|
assert_match(commandline, context, is_main_command=True)
|
|
|
|
|
|
NESTING_EXAMPLES = (
|
|
# nesting, prefix
|
|
(f"echo $({X})", "$("),
|
|
(f"echo @$({X})", "@$("),
|
|
(f"echo $(echo $({X}))", "$("),
|
|
(f"echo @(x + $({X}))", "$("),
|
|
(f"!({X})", "!("),
|
|
(f"$[{X}]", "$["),
|
|
(f"![{X}]", "!["),
|
|
)
|
|
|
|
NESTED_SIMPLE_CMD_EXAMPLES = [
|
|
(
|
|
nesting,
|
|
f"simple {X}",
|
|
CommandContext(
|
|
args=(CommandArg("simple"),), arg_index=1, subcmd_opening=prefix
|
|
),
|
|
)
|
|
for nesting, prefix in NESTING_EXAMPLES[1:]
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"nesting, commandline, context",
|
|
list(
|
|
itertools.chain(
|
|
(
|
|
# complex subcommand in a simple nested expression
|
|
(
|
|
NESTING_EXAMPLES[0][0],
|
|
commandline,
|
|
context._replace(subcmd_opening=NESTING_EXAMPLES[0][1]),
|
|
)
|
|
for commandline, context in COMMAND_EXAMPLES
|
|
),
|
|
NESTED_SIMPLE_CMD_EXAMPLES,
|
|
)
|
|
),
|
|
)
|
|
def test_nested_command(commandline, context, nesting):
|
|
nested_commandline = nesting.replace(X, commandline)
|
|
assert_match(nested_commandline, command_context=context, python_context=None)
|
|
|
|
|
|
NESTING_MALFORMATIONS = (
|
|
lambda s: s[:-1], # remove the last closing brace ')' / ']'
|
|
lambda s: s + s[-1], # add an extra closing brace ')' / ']'
|
|
lambda s: s[-1] + s,
|
|
lambda s: s + "$(",
|
|
lambda s: "$(" + s,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("nesting, commandline, context", NESTED_SIMPLE_CMD_EXAMPLES)
|
|
@pytest.mark.parametrize("malformation", NESTING_MALFORMATIONS)
|
|
def test_malformed_subcmd(nesting, commandline, context, malformation):
|
|
nested_commandline = nesting.replace(X, commandline)
|
|
nested_commandline = malformation(nested_commandline)
|
|
assert_match(nested_commandline, command_context=context, python_context=None)
|
|
|
|
|
|
MALFORMED_SUBCOMMANDS_NESTINGS = (
|
|
# nesting, subcmd_opening
|
|
(f"echo $(a $({X}", "$("),
|
|
(f"echo $(a $(b; {X}", ""),
|
|
(f"$(echo $(a $({X}", "$("),
|
|
(f"echo $[a $({X}]", "$("),
|
|
(f"echo $(a $[{X})", "$["),
|
|
(f"echo @(x = $({X}", "$("),
|
|
(f"echo @(a; x = $({X}", "$("),
|
|
(f"echo @(x = $(a; {X}", ""),
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("nesting, subcmd_opening", MALFORMED_SUBCOMMANDS_NESTINGS)
|
|
@pytest.mark.parametrize("commandline, context", COMMAND_EXAMPLES[:5])
|
|
def test_multiple_malformed_subcmds(nesting, subcmd_opening, commandline, context):
|
|
nested_commandline = nesting.replace(X, commandline)
|
|
nested_context = context._replace(subcmd_opening=subcmd_opening)
|
|
assert_match(nested_commandline, nested_context, python_context=None)
|
|
|
|
|
|
def test_other_subcommand_arg():
|
|
command = "echo $(pwd) "
|
|
assert_match(
|
|
command,
|
|
CommandContext((CommandArg("echo"), CommandArg("$(pwd)")), arg_index=2),
|
|
is_main_command=True,
|
|
)
|
|
|
|
|
|
def test_combined_subcommand_arg():
|
|
command = f"echo file=$(pwd{X})/x"
|
|
|
|
# index inside the subproc
|
|
assert_match(
|
|
command,
|
|
CommandContext((), arg_index=0, prefix="pwd", subcmd_opening="$("),
|
|
python_context=None,
|
|
)
|
|
|
|
# index at the end of the command
|
|
assert_match(
|
|
command.replace(X, ""),
|
|
CommandContext((CommandArg("echo"),), arg_index=1, prefix="file=$(pwd)/x"),
|
|
is_main_command=True,
|
|
)
|
|
|
|
|
|
SUBCMD_BORDER_EXAMPLES = (
|
|
(f"{X}$(echo)", CommandContext((), 0, suffix="$(echo)")),
|
|
(f"${X}(echo)", CommandContext((), 0, prefix="$", suffix="(echo)")),
|
|
(f"$(echo){X}", CommandContext((), 0, prefix="$(echo)")),
|
|
(f"${X}( echo)", CommandContext((), 0, prefix="$", suffix="( echo)")),
|
|
(f"$(echo ){X}", CommandContext((), 0, prefix="$(echo )")),
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("commandline, context", SUBCMD_BORDER_EXAMPLES)
|
|
def test_cursor_in_subcmd_borders(commandline, context):
|
|
assert_match(commandline, context, is_main_command=True)
|
|
|
|
|
|
MULTIPLE_COMMAND_KEYWORDS = (
|
|
"; ",
|
|
"\n",
|
|
" and ",
|
|
"&& ",
|
|
" or ",
|
|
"|| ",
|
|
"| ",
|
|
)
|
|
|
|
MULTIPLE_CMD_SIMPLE_EXAMPLES = [
|
|
(
|
|
keyword,
|
|
("echo hi", f"simple {X}"),
|
|
CommandContext(args=(CommandArg("simple"),), arg_index=1),
|
|
)
|
|
for keyword in MULTIPLE_COMMAND_KEYWORDS
|
|
]
|
|
|
|
EXTENSIVE_COMMAND_PAIRS = tuple(
|
|
itertools.chain(
|
|
zip(COMMAND_EXAMPLES, COMMAND_EXAMPLES[::-1]),
|
|
zip(COMMAND_EXAMPLES, EMPTY_COMMAND_EXAMPLES),
|
|
zip(EMPTY_COMMAND_EXAMPLES, COMMAND_EXAMPLES),
|
|
zip(EMPTY_COMMAND_EXAMPLES, EMPTY_COMMAND_EXAMPLES),
|
|
)
|
|
)
|
|
|
|
MULTIPLE_COMMAND_EXTENSIVE_EXAMPLES = tuple(
|
|
itertools.chain(
|
|
(
|
|
# cursor in first command
|
|
((first, second.replace(X, "")), first_context)
|
|
for (first, first_context), (
|
|
second,
|
|
second_context,
|
|
) in EXTENSIVE_COMMAND_PAIRS
|
|
),
|
|
(
|
|
# cursor in second command
|
|
((first.replace(X, ""), second), second_context)
|
|
for (first, first_context), (
|
|
second,
|
|
second_context,
|
|
) in EXTENSIVE_COMMAND_PAIRS
|
|
),
|
|
(
|
|
# cursor in middle command
|
|
((first.replace(X, ""), second, third.replace(X, "")), second_context)
|
|
for (first, _1), (second, second_context), (third, _3) in zip(
|
|
COMMAND_EXAMPLES[:3], COMMAND_EXAMPLES[3:6], COMMAND_EXAMPLES[6:9]
|
|
)
|
|
),
|
|
(
|
|
# cursor in third command
|
|
((first.replace(X, ""), second.replace(X, ""), third), third_context)
|
|
for (first, _1), (second, _2), (third, third_context) in zip(
|
|
COMMAND_EXAMPLES[:3], COMMAND_EXAMPLES[3:6], COMMAND_EXAMPLES[6:9]
|
|
)
|
|
),
|
|
)
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"keyword, commands, context",
|
|
tuple(
|
|
itertools.chain(
|
|
(
|
|
(MULTIPLE_COMMAND_KEYWORDS[0], commands, context)
|
|
for commands, context in MULTIPLE_COMMAND_EXTENSIVE_EXAMPLES
|
|
),
|
|
MULTIPLE_CMD_SIMPLE_EXAMPLES,
|
|
)
|
|
),
|
|
)
|
|
def test_multiple_commands(keyword, commands, context):
|
|
joined_command = keyword.join(commands)
|
|
|
|
cursor_command = next(command for command in commands if X in command)
|
|
if cursor_command is commands[0]:
|
|
relative_index = cursor_command.index(X)
|
|
else:
|
|
absolute_index = joined_command.index(X)
|
|
relative_index = (
|
|
absolute_index
|
|
- joined_command.rindex(keyword, 0, absolute_index)
|
|
- len(keyword)
|
|
)
|
|
if keyword.endswith(" "):
|
|
# the last space is part of the command
|
|
relative_index += 1
|
|
cursor_command = " " + cursor_command
|
|
|
|
assert_match(joined_command, context, is_main_command=True)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"commandline",
|
|
(
|
|
f"{X};",
|
|
f"; {X}",
|
|
f"{X};;",
|
|
f"; {X};",
|
|
f";; {X}",
|
|
f";;; {X}",
|
|
),
|
|
)
|
|
def test_multiple_empty_commands(commandline):
|
|
assert_match(commandline, CommandContext((), 0), is_main_command=True)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"nesting, keyword, commands, context",
|
|
tuple(
|
|
(
|
|
nesting,
|
|
keyword,
|
|
commands,
|
|
context,
|
|
) # no subcmd_opening in nested multi-commands
|
|
for nesting, prefix in NESTING_EXAMPLES
|
|
for keyword, commands, context in MULTIPLE_CMD_SIMPLE_EXAMPLES
|
|
if keyword != "\n" # the lexer ignores newlines inside subcommands
|
|
),
|
|
)
|
|
def test_nested_multiple_commands(nesting, keyword, commands, context):
|
|
joined_command = keyword.join(commands)
|
|
nested_joined = nesting.replace(X, joined_command)
|
|
assert_match(nested_joined, context, python_context=None)
|
|
|
|
|
|
def test_multiple_nested_commands():
|
|
assert_match(
|
|
f"echo hi; echo $(ls{X})",
|
|
CommandContext((), 0, prefix="ls", subcmd_opening="$("),
|
|
python_context=None,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"commandline, context",
|
|
tuple(
|
|
(commandline, context)
|
|
for commandline, context in STRING_ARGS_EXAMPLES
|
|
if commandline.endswith("'") or commandline.endswith('"')
|
|
),
|
|
)
|
|
def test_multiple_partial_string_arg(commandline, context):
|
|
partial_commandline = commandline.rstrip("\"'")
|
|
partial_context = context._replace(closing_quote="")
|
|
assert_match("echo;" + partial_commandline, partial_context)
|
|
assert_match("echo $[a ;" + partial_commandline, partial_context)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"nesting, keyword, commands, context",
|
|
tuple(
|
|
(nesting, keyword, commands, context)
|
|
for nesting, prefix in NESTING_EXAMPLES[:1]
|
|
for keyword, commands, context in MULTIPLE_CMD_SIMPLE_EXAMPLES[:1]
|
|
),
|
|
)
|
|
@pytest.mark.parametrize("malformation", NESTING_MALFORMATIONS)
|
|
def test_malformed_subcmd_1(malformation, nesting, keyword, commands, context):
|
|
joined_command = keyword.join(commands)
|
|
nested_joined = nesting.replace(X, joined_command)
|
|
malformed_commandline = malformation(nested_joined)
|
|
assert_match(malformed_commandline, context, python_context=None)
|
|
|
|
|
|
MULTIPLE_COMMAND_BORDER_EXAMPLES = tuple(
|
|
itertools.chain(
|
|
itertools.chain(
|
|
*(
|
|
(
|
|
(
|
|
f"ls{ws1}{X}{kwd}{ws2}echo",
|
|
CommandContext((CommandArg("ls"),), 1)
|
|
if ws1
|
|
else CommandContext((), 0, prefix="ls"),
|
|
),
|
|
)
|
|
for ws1, ws2, kwd in itertools.product(
|
|
("", " "), ("", " "), ("&&", ";")
|
|
)
|
|
)
|
|
),
|
|
# all keywords are treated as a normal arg if the cursor is at the edge
|
|
(
|
|
(
|
|
f"ls {X}and echo",
|
|
CommandContext((CommandArg("ls"), CommandArg("echo")), 1, suffix="and"),
|
|
),
|
|
(
|
|
f"ls and{X} echo",
|
|
CommandContext((CommandArg("ls"), CommandArg("echo")), 1, prefix="and"),
|
|
),
|
|
(
|
|
f"ls ||{X} echo",
|
|
CommandContext((CommandArg("ls"), CommandArg("echo")), 1, prefix="||"),
|
|
),
|
|
),
|
|
# if the cursor is inside the keyword, it's treated as a normal arg
|
|
(
|
|
(
|
|
f"ls a{X}nd echo",
|
|
CommandContext(
|
|
(CommandArg("ls"), CommandArg("echo")), 1, prefix="a", suffix="nd"
|
|
),
|
|
),
|
|
(
|
|
f"ls &{X}& echo",
|
|
CommandContext(
|
|
(CommandArg("ls"), CommandArg("echo")), 1, prefix="&", suffix="&"
|
|
),
|
|
),
|
|
),
|
|
)
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"commandline, context",
|
|
tuple(
|
|
itertools.chain(
|
|
MULTIPLE_COMMAND_BORDER_EXAMPLES,
|
|
(
|
|
# ensure these rules work with more than one command
|
|
(f"cat | {commandline}", context)
|
|
for commandline, context in MULTIPLE_COMMAND_BORDER_EXAMPLES
|
|
),
|
|
)
|
|
),
|
|
)
|
|
def test_cursor_in_multiple_keyword_borders(commandline, context):
|
|
assert_match(commandline, context)
|
|
|
|
|
|
PYTHON_EXAMPLES = (
|
|
# commandline, context
|
|
(f"x = {X}", PythonContext("x = ", 4)),
|
|
(f"a {X}= x; b = y", PythonContext("a = x; b = y", 2)),
|
|
(f"a {X}= x\nb = $(ls)", PythonContext("a = x\nb = $(ls)", 2)),
|
|
)
|
|
|
|
PYTHON_NESTING_EXAMPLES = (
|
|
# nesting, prefix
|
|
f"echo @({X})",
|
|
f"echo $(echo @({X}))",
|
|
f"echo @(x + @({X}))", # invalid syntax, but can still be in a partial command
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"nesting, commandline, context",
|
|
list(
|
|
itertools.chain(
|
|
(
|
|
# complex subcommand in a simple nested expression
|
|
(nesting, commandline, context._replace(is_sub_expression=True))
|
|
for nesting in PYTHON_NESTING_EXAMPLES[:1]
|
|
for commandline, context in PYTHON_EXAMPLES
|
|
),
|
|
(
|
|
# simple subcommand in a complex nested expression
|
|
(nesting, commandline, context._replace(is_sub_expression=True))
|
|
for nesting in PYTHON_NESTING_EXAMPLES
|
|
for commandline, context in PYTHON_EXAMPLES[:1]
|
|
),
|
|
)
|
|
),
|
|
)
|
|
def test_nested_python(commandline, context, nesting):
|
|
nested_commandline = nesting.replace(X, commandline)
|
|
assert_match(nested_commandline, command_context=None, python_context=context)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"commandline, context",
|
|
[
|
|
(
|
|
commandline.replace("$", "@"),
|
|
context._replace(
|
|
prefix=context.prefix.replace("$", "@"),
|
|
suffix=context.suffix.replace("$", "@"),
|
|
),
|
|
)
|
|
for commandline, context in SUBCMD_BORDER_EXAMPLES
|
|
],
|
|
)
|
|
def test_cursor_in_sub_python_borders(commandline, context):
|
|
assert_match(commandline, context, is_main_command=True)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"code",
|
|
(
|
|
f"""
|
|
x = 3
|
|
x.{X}""",
|
|
f"""
|
|
x = 3;
|
|
y = 4;
|
|
x.{X}""",
|
|
f"""
|
|
def func({X}):
|
|
return 100
|
|
""",
|
|
f"""
|
|
class A:
|
|
def a():
|
|
return "a{X}"
|
|
pass
|
|
exit()
|
|
""",
|
|
),
|
|
)
|
|
def test_multiline_python(code):
|
|
assert_match(code, is_main_command=True)
|