diff options
-rw-r--r-- | cmd2/cmd2.py | 46 | ||||
-rw-r--r-- | cmd2/parsing.py | 20 | ||||
-rw-r--r-- | tests/conftest.py | 3 | ||||
-rw-r--r-- | tests/test_cmd2.py | 51 | ||||
-rw-r--r-- | tests/transcripts/from_cmdloop.txt | 18 |
5 files changed, 77 insertions, 61 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 012b6f6b..f30f0c12 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -288,27 +288,36 @@ class EmptyStatement(Exception): class HistoryItem(str): - """Class used to represent an item in the History list. + """Class used to represent an item in the History list""" + listformat = ' {:>4} {}\n' + ex_listformat = ' Ex: {}\n' - Thin wrapper around str class which adds a custom format for printing. It - also keeps track of its index in the list as well as a lowercase - representation of itself for convenience/efficiency. + def __new__(cls, statement: Statement): + """Create a new instance of HistoryItem - """ - listformat = '-------------------------[{}]\n{}\n' + We must override __new__ because we are subclassing `str` which is + immutable and takes a different number of arguments as Statement. + """ + hi = super().__new__(cls, statement.raw) + hi.statement = statement + hi.idx = None + return hi - # noinspection PyUnusedLocal - def __init__(self, instr: str) -> None: - str.__init__(self) - self.lowercase = self.lower() - self.idx = None + @property + def expanded(self) -> str: + """Return the command as run which includes shortcuts and aliases resolved plus any changes made in hooks""" + return self.statement.expanded_command_line - def pr(self) -> str: + def pr(self, verbose: bool) -> str: """Represent a HistoryItem in a pretty fashion suitable for printing. :return: pretty print string version of a HistoryItem """ - return self.listformat.format(self.idx, str(self).rstrip()) + ret_str = self.listformat.format(self.idx, str(self).rstrip()) + if verbose and self != self.expanded: + ret_str += self.ex_listformat.format(self.expanded.rstrip()) + + return ret_str class Cmd(cmd.Cmd): @@ -2002,7 +2011,7 @@ class Cmd(cmd.Cmd): if func: # Since we have a valid command store it in the history if statement.command not in self.exclude_from_history: - self.history.append(statement.raw) + self.history.append(statement) stop = func(statement) @@ -2065,7 +2074,7 @@ class Cmd(cmd.Cmd): """ if self.default_to_shell: if 'shell' not in self.exclude_from_history: - self.history.append(statement.raw) + self.history.append(statement) return self.do_shell(statement.command_and_args) else: @@ -3163,6 +3172,9 @@ class Cmd(cmd.Cmd): setattr(history_parser_group.add_argument('-t', '--transcript', help='output commands and results to a transcript file'), ACTION_ARG_CHOICES, ('path_complete',)) + history_parser_group.add_argument('-v', '--verbose', action='store_true', + help='display history and include expanded commands if they' + ' differ from the typed command.') history_parser_group.add_argument('-c', '--clear', action="store_true", help='clear all history') history_arg_help = ("empty all history items\n" "a one history item by number\n" @@ -3246,7 +3258,7 @@ class Cmd(cmd.Cmd): if args.script: self.poutput(hi) else: - self.poutput(hi.pr()) + self.poutput(hi.pr(args.verbose)) def _generate_transcript(self, history: List[HistoryItem], transcript_file: str) -> None: """Generate a transcript file from a given history of commands.""" @@ -3820,7 +3832,7 @@ class History(list): rangePattern = re.compile(r'^\s*(?P<start>[\d]+)?\s*-\s*(?P<end>[\d]+)?\s*$') - def append(self, new: str) -> None: + def append(self, new: Statement) -> None: """Append a HistoryItem to end of the History list :param new: command line to convert to HistoryItem and add to the end of the History list diff --git a/cmd2/parsing.py b/cmd2/parsing.py index d5c67ae0..f372b0d3 100644 --- a/cmd2/parsing.py +++ b/cmd2/parsing.py @@ -188,6 +188,26 @@ class Statement(str): return rtn @property + def expanded_command_line(self) -> str: + """Contains command_and_args plus any ending terminator, suffix, and redirection chars""" + rtn = self.command_and_args + if self.terminator: + rtn += self.terminator + + if self.suffix: + rtn += ' ' + self.suffix + + if self.pipe_to: + rtn += ' | ' + self.pipe_to + + if self.output: + rtn += ' ' + self.output + if self.output_to: + rtn += ' ' + self.output_to + + return rtn + + @property def argv(self) -> List[str]: """a list of arguments a la sys.argv. diff --git a/tests/conftest.py b/tests/conftest.py index ac6a1896..fdaf2809 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -51,7 +51,7 @@ shortcuts List available shortcuts """ # Help text for the history command -HELP_HISTORY = """Usage: history [-h] [-r | -e | -s | -o FILE | -t TRANSCRIPT | -c] [arg] +HELP_HISTORY = """Usage: history [-h] [-r | -e | -s | -o FILE | -t TRANSCRIPT | -v | -c] [arg] View, run, edit, save, or clear previously entered commands @@ -71,6 +71,7 @@ optional arguments: output commands to a script file -t, --transcript TRANSCRIPT output commands and results to a transcript file + -v, --verbose display history and include expanded commands if they differ from the typed command. -c, --clear clear all history """ diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index 59c6bb60..ecca58cd 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -303,8 +303,12 @@ def test_base_error(base_app): @pytest.fixture def hist(): + from cmd2.parsing import Statement from cmd2.cmd2 import History, HistoryItem - h = History([HistoryItem('first'), HistoryItem('second'), HistoryItem('third'), HistoryItem('fourth')]) + h = History([HistoryItem(Statement('', raw='first')), + HistoryItem(Statement('', raw='second')), + HistoryItem(Statement('', raw='third')), + HistoryItem(Statement('', raw='fourth'))]) return h def test_history_span(hist): @@ -334,24 +338,20 @@ def test_base_history(base_app): run_cmd(base_app, 'shortcuts') out = run_cmd(base_app, 'history') expected = normalize(""" --------------------------[1] -help --------------------------[2] -shortcuts + 1 help + 2 shortcuts """) assert out == expected out = run_cmd(base_app, 'history he') expected = normalize(""" --------------------------[1] -help + 1 help """) assert out == expected out = run_cmd(base_app, 'history sh') expected = normalize(""" --------------------------[2] -shortcuts + 2 shortcuts """) assert out == expected @@ -371,10 +371,8 @@ def test_history_with_string_argument(base_app): run_cmd(base_app, 'help history') out = run_cmd(base_app, 'history help') expected = normalize(""" --------------------------[1] -help --------------------------[3] -help history + 1 help + 3 help history """) assert out == expected @@ -384,8 +382,7 @@ def test_history_with_integer_argument(base_app): run_cmd(base_app, 'shortcuts') out = run_cmd(base_app, 'history 1') expected = normalize(""" --------------------------[1] -help + 1 help """) assert out == expected @@ -396,10 +393,8 @@ def test_history_with_integer_span(base_app): run_cmd(base_app, 'help history') out = run_cmd(base_app, 'history 1..2') expected = normalize(""" --------------------------[1] -help --------------------------[2] -shortcuts + 1 help + 2 shortcuts """) assert out == expected @@ -409,10 +404,8 @@ def test_history_with_span_start(base_app): run_cmd(base_app, 'help history') out = run_cmd(base_app, 'history 2:') expected = normalize(""" --------------------------[2] -shortcuts --------------------------[3] -help history + 2 shortcuts + 3 help history """) assert out == expected @@ -422,10 +415,8 @@ def test_history_with_span_end(base_app): run_cmd(base_app, 'help history') out = run_cmd(base_app, 'history :2') expected = normalize(""" --------------------------[1] -help --------------------------[2] -shortcuts + 1 help + 2 shortcuts """) assert out == expected @@ -435,8 +426,7 @@ def test_history_with_span_index_error(base_app): run_cmd(base_app, '!ls -hal :') out = run_cmd(base_app, 'history "hal :"') expected = normalize(""" --------------------------[3] -!ls -hal : + 3 !ls -hal : """) assert out == expected @@ -956,8 +946,7 @@ def test_exclude_from_history(base_app, monkeypatch): run_cmd(base_app, 'help') # And verify we have a history now ... out = run_cmd(base_app, 'history') - expected = normalize("""-------------------------[1] -help""") + expected = normalize(""" 1 help""") assert out == expected diff --git a/tests/transcripts/from_cmdloop.txt b/tests/transcripts/from_cmdloop.txt index 8c0dd007..871b71f1 100644 --- a/tests/transcripts/from_cmdloop.txt +++ b/tests/transcripts/from_cmdloop.txt @@ -35,18 +35,12 @@ OODNIGHT, GRACIEGAY OODNIGHT, GRACIEGAY OODNIGHT, GRACIEGAY (Cmd) history --------------------------[1] -help --------------------------[2] -help say --------------------------[3] -say goodnight, Gracie --------------------------[4] -say -ps --repeat=5 goodnight, Gracie --------------------------[5] -set maxrepeats 5 --------------------------[6] -say -ps --repeat=5 goodnight, Gracie + 1 help + 2 help say + 3 say goodnight, Gracie + 4 say -ps --repeat=5 goodnight, Gracie + 5 set maxrepeats 5 + 6 say -ps --repeat=5 goodnight, Gracie (Cmd) history -r 4 say -ps --repeat=5 goodnight, Gracie OODNIGHT, GRACIEGAY |