diff --git a/src/command_modules/azure-cli-interactive/HISTORY.rst b/src/command_modules/azure-cli-interactive/HISTORY.rst index fff36ba91bc..f439b5e0452 100644 --- a/src/command_modules/azure-cli-interactive/HISTORY.rst +++ b/src/command_modules/azure-cli-interactive/HISTORY.rst @@ -3,6 +3,11 @@ Release History =============== +0.3.17 +++++++ +* Persist history across different sessions +* Fixed history while in scope + 0.3.16 ++++++ * Fix issue where user is prompted to login when using interactive mode in Cloud Shell. diff --git a/src/command_modules/azure-cli-interactive/azclishell/app.py b/src/command_modules/azure-cli-interactive/azclishell/app.py index 3612a31a110..eddab4f8992 100644 --- a/src/command_modules/azure-cli-interactive/azclishell/app.py +++ b/src/command_modules/azure-cli-interactive/azclishell/app.py @@ -34,7 +34,7 @@ from azclishell.frequency_heuristic import DISPLAY_TIME, frequency_heuristic from azclishell.gather_commands import add_new_lines, GatherCommands from azclishell.key_bindings import InteractiveKeyBindings -from azclishell.layout import create_layout, create_tutorial_layout, set_scope +from azclishell.layout import create_layout, create_tutorial_layout, set_scope, get_scope from azclishell.progress import progress_view from azclishell.telemetry import Telemetry from azclishell.threads import LoadCommandTableThread @@ -88,8 +88,8 @@ def restart_completer(shell_ctx): # pylint: disable=too-many-instance-attributes class AzInteractiveShell(object): - def __init__(self, cli_ctx, style=None, completer=None, styles=None, - lexer=None, history=InMemoryHistory(), + def __init__(self, cli_ctx, style=None, completer=None, + lexer=None, history=None, app=None, input_custom=sys.stdin, output_custom=None, user_feedback=False, intermediate_sleep=.25, final_sleep=4): @@ -142,7 +142,7 @@ def __init__(self, cli_ctx, style=None, completer=None, styles=None, def __call__(self): if self.cli_ctx.data["az_interactive_active"]: - logger.warning("You're in the interactive shell already.\n") + logger.warning("You're in the interactive shell already.") return if self.config.BOOLEAN_STATES[self.config.config.get('DEFAULT', 'firsttime')]: @@ -476,60 +476,59 @@ def _special_cases(self, cmd, outside): continue_flag = False args = parse_quotes(cmd) cmd_stripped = cmd.strip() - if cmd_stripped and cmd.split(' ', 1)[0].lower() == 'az': - self.telemetry.track_ssg('az', cmd) - cmd = ' '.join(cmd.split()[1:]) - if self.default_command: - cmd = self.default_command + " " + cmd - if cmd_stripped == "quit" or cmd_stripped == "exit": + if not cmd_stripped and cmd: + # add scope if there are only spaces + cmd = self.default_command + " " + cmd + elif cmd_stripped == "quit" or cmd_stripped == "exit": break_flag = True elif cmd_stripped == "clear-history": - # clears the history, but only when you restart - outside = True - cmd = 'echo -n "" >' +\ - os.path.join( - self.config.config_dir(), - self.config.get_history()) + continue_flag = True + self.reset_history() elif cmd_stripped == CLEAR_WORD: outside = True cmd = CLEAR_WORD - if cmd_stripped: - if cmd_stripped[0] == SELECT_SYMBOL['outside']: - cmd = cmd_stripped[1:] - outside = True - if cmd.strip() and cmd.split()[0] == 'cd': - self.handle_cd(parse_quotes(cmd)) - continue_flag = True - self.telemetry.track_ssg('outside', '') + elif cmd_stripped[0] == SELECT_SYMBOL['outside']: + cmd = cmd_stripped[1:] + outside = True + if cmd.strip() and cmd.split()[0] == 'cd': + self.handle_cd(parse_quotes(cmd)) + continue_flag = True + self.telemetry.track_ssg('outside', '') - elif cmd_stripped[0] == SELECT_SYMBOL['exit_code']: - meaning = "Success" if self.last_exit == 0 else "Failure" + elif cmd_stripped[0] == SELECT_SYMBOL['exit_code']: + meaning = "Success" if self.last_exit == 0 else "Failure" - print(meaning + ": " + str(self.last_exit), file=self.output) - continue_flag = True - self.telemetry.track_ssg('exit code', '') - elif SELECT_SYMBOL['query'] in cmd_stripped and self.last and self.last.result: - continue_flag = self.handle_jmespath_query(args) - self.telemetry.track_ssg('query', '') + print(meaning + ": " + str(self.last_exit), file=self.output) + continue_flag = True + self.telemetry.track_ssg('exit code', '') + elif SELECT_SYMBOL['query'] in cmd_stripped and self.last and self.last.result: + continue_flag = self.handle_jmespath_query(args) + self.telemetry.track_ssg('query', '') - elif args[0] == '--version' or args[0] == '-v': - try: - continue_flag = True - self.cli_ctx.show_version() - except SystemExit: - pass - elif "|" in cmd or ">" in cmd: + elif args[0] == '--version' or args[0] == '-v': + try: + continue_flag = True + self.cli_ctx.show_version() + except SystemExit: + pass + elif SELECT_SYMBOL['example'] in cmd: + cmd, continue_flag = self.handle_example(cmd, continue_flag) + self.telemetry.track_ssg('tutorial', cmd) + elif SELECT_SYMBOL['scope'] == cmd_stripped[0:2]: + continue_flag, cmd = self.handle_scoping_input(continue_flag, cmd, cmd_stripped) + else: + # not a special character; add scope and remove 'az' + if self.default_command: + cmd = self.default_command + " " + cmd + elif cmd.split(' ', 1)[0].lower() == 'az': + self.telemetry.track_ssg('az', cmd) + cmd = ' '.join(cmd.split()[1:]) + if "|" in cmd or ">" in cmd: # anything I don't parse, send off outside = True cmd = "az " + cmd - elif SELECT_SYMBOL['example'] in cmd: - cmd, continue_flag = self.handle_example(cmd, continue_flag) - self.telemetry.track_ssg('tutorial', cmd) - elif len(cmd_stripped) > 2 and SELECT_SYMBOL['scope'] == cmd_stripped[0:2]: - continue_flag, cmd = self.handle_scoping_input(continue_flag, cmd, cmd_stripped) - return break_flag, continue_flag, outside, cmd def handle_jmespath_query(self, args): @@ -604,8 +603,7 @@ def handle_scoping_input(self, continue_flag, cmd, text): cmd = cmd.replace(SELECT_SYMBOL['scope'], '') self.telemetry.track_ssg('scope command', value) - elif SELECT_SYMBOL['unscope'] == default_split[0] and \ - len(self.default_command.split()) > 0: + elif SELECT_SYMBOL['unscope'] == default_split[0] and self.default_command.split(): value = self.default_command.split()[-1] self.default_command = ' ' + ' '.join(self.default_command.split()[:-1]) @@ -621,6 +619,12 @@ def handle_scoping_input(self, continue_flag, cmd, text): default_split = default_split[1:] return continue_flag, cmd + def reset_history(self): + history_file_path = os.path.join(self.config.config_dir, self.config.get_history()) + os.remove(history_file_path) + self.history = FileHistory(history_file_path) + self.cli.buffers[DEFAULT_BUFFER].history = self.history + def cli_execute(self, cmd): """ sends the command to the CLI to be executed """ @@ -709,9 +713,9 @@ def run(self): # when the user pressed Control D break else: + self.history.append(text) b_flag, c_flag, outside, cmd = self._special_cases(cmd, outside) - if not self.default_command: - self.history.append(text) + if b_flag: break if c_flag: diff --git a/src/command_modules/azure-cli-interactive/azclishell/key_bindings.py b/src/command_modules/azure-cli-interactive/azclishell/key_bindings.py index d6ae29fa5d1..5eb8ef276df 100644 --- a/src/command_modules/azure-cli-interactive/azclishell/key_bindings.py +++ b/src/command_modules/azure-cli-interactive/azclishell/key_bindings.py @@ -66,7 +66,6 @@ def pan_down(event): def config_settings(event): """ opens the configuration """ shell_ctx.telemetry.track_key('F1') - shell_ctx.is_prompting = True config = shell_ctx.config answer = "" diff --git a/src/command_modules/azure-cli-interactive/setup.py b/src/command_modules/azure-cli-interactive/setup.py index 25e348821bf..f4376cb7e7c 100644 --- a/src/command_modules/azure-cli-interactive/setup.py +++ b/src/command_modules/azure-cli-interactive/setup.py @@ -14,7 +14,7 @@ cmdclass = {} # Version is also defined in azclishell.__init__.py. -VERSION = "0.3.16" +VERSION = "0.3.17" # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers CLASSIFIERS = [