summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd2/parsing.py44
-rw-r--r--tests/test_shlexparsing.py20
2 files changed, 58 insertions, 6 deletions
diff --git a/cmd2/parsing.py b/cmd2/parsing.py
index 5bb8d654..dece2b5e 100644
--- a/cmd2/parsing.py
+++ b/cmd2/parsing.py
@@ -31,7 +31,10 @@ class Statement(str):
self.outputTo = None
class CommandParser():
- """Parse raw text into command components."""
+ """Parse raw text into command components.
+
+ Shortcuts is a list of tuples with each tuple containing the shortcut and the expansion.
+ """
def __init__(
self,
quotes=['"', "'"],
@@ -39,14 +42,18 @@ class CommandParser():
redirection_chars=['|', '<', '>'],
terminators=[';'],
multilineCommands = [],
+ aliases = {},
+ shortcuts = [],
):
self.quotes = quotes
self.allow_redirection = allow_redirection
self.redirection_chars = redirection_chars
self.terminators = terminators
self.multilineCommands = multilineCommands
+ self.aliases = aliases
+ self.shortcuts = shortcuts
- def parseString(self, rawinput):
+ def parseString(self, line):
# strip C-style comments
# shlex will handle the python/shell style comments for us
def replacer(match):
@@ -61,7 +68,22 @@ class CommandParser():
r'/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
re.DOTALL | re.MULTILINE
)
- rawinput = re.sub(pattern, replacer, rawinput)
+ line = re.sub(pattern, replacer, line)
+ rawinput = line
+
+ # expand shortcuts, have to do this first because
+ # a shortcut can expand into multiple tokens, ie '!ls' becomes
+ # 'shell ls'
+ for (shortcut, expansion) in self.shortcuts:
+ if rawinput.startswith(shortcut):
+ # If the next character after the shortcut isn't a space, then insert one
+ shortcut_len = len(shortcut)
+ if len(rawinput) == shortcut_len or rawinput[shortcut_len] != ' ':
+ expansion += ' '
+
+ # Expand the shortcut
+ rawinput = rawinput.replace(shortcut, expansion, 1)
+ break
s = shlex.shlex(rawinput, posix=False)
s.whitespace_split = True
@@ -140,11 +162,27 @@ class CommandParser():
suffix = None
(command, args) = self._command_and_args(tokens)
+ # expand aliases
+ # make a copy of aliases so we can edit it
+ tmp_aliases = list(self.aliases.keys())
+ keep_expanding = len(tmp_aliases) > 0
+
+ while keep_expanding:
+ for cur_alias in tmp_aliases:
+ keep_expanding = False
+ if command == cur_alias:
+ command = self.aliases[cur_alias]
+ tmp_aliases.remove(cur_alias)
+ keep_expanding = len(tmp_aliases) > 0
+ break
+
+ # set multiline
if command in self.multilineCommands:
multilineCommand = command
else:
multilineCommand = None
+ # build Statement object
result = Statement(args)
result.raw = rawinput
result.command = command
diff --git a/tests/test_shlexparsing.py b/tests/test_shlexparsing.py
index 0029ca07..b8d4b208 100644
--- a/tests/test_shlexparsing.py
+++ b/tests/test_shlexparsing.py
@@ -12,11 +12,10 @@ Todo List
Notes:
-- Shortcuts may have to be discarded, or handled in a different way than they
- are with pyparsing.
- valid comment styles:
- C-style -> /* comment */
- Python/Shell style -> # comment
+- we now ignore self.identchars, which breaks backwards compatibility with the cmd in the standard library
Functions in cmd2.py to be modified:
- _complete_statement()
@@ -42,7 +41,9 @@ def parser():
allow_redirection=True,
redirection_chars=['|', '<', '>'],
terminators = [';'],
- multilineCommands = ['multiline']
+ multilineCommands = ['multiline'],
+ aliases = {'helpalias': 'help', '42': 'theanswer'},
+ shortcuts = [('?', 'help'), ('!', 'shell')]
)
return parser
@@ -274,3 +275,16 @@ def test_empty_statement_raises_exception():
with pytest.raises(cmd2.cmd2.EmptyStatement):
app._complete_statement(' ')
+
+@pytest.mark.parametrize('line,command,args', [
+ ('helpalias', 'help', ''),
+ ('helpalias mycommand', 'help', 'mycommand'),
+ ('42', 'theanswer', ''),
+ ('42 arg1 arg2', 'theanswer', 'arg1 arg2'),
+ ('!ls', 'shell', 'ls'),
+ ('!ls -al /tmp', 'shell', 'ls -al /tmp'),
+])
+def test_alias_and_shortcut_expansion(parser, line, command, args):
+ statement = parser.parseString(line)
+ assert statement.command == command
+ assert statement.args == args