def handle_add_request(logger_console, input_cmd_str, path_data_folder, error_feedback=False): """ take input and add store it :return: """ # local import to load this module only in case of an 'add' commands from fastHistory.parser.inputParser import InputParser one_line_input = InputParser.adjust_multi_line_input(input_cmd_str) # define log class logging.debug("input: '%s'" % one_line_input[1]) # parse tags and store the cmd parser_res = InputParser.parse_input(one_line_input[1]) if parser_res is None: if error_feedback: logger_console.log_on_console_error("wrong input") logger_console.log_on_console_info("syntax : f --add <command> #[<tag> [#<tag> ...]][@<description>]") logger_console.log_on_console_info("examples: f --add ls -la #") logger_console.log_on_console_info(" f --add ls -la #list @show files") logger_console.log_on_console_info("syntax 2: #<command> #[<tag> [#<tag> ...]][@<description>]") logger_console.log_on_console_info("examples: #ls -la #") logger_console.log_on_console_info(" #ls -la #list @show files") else: cmd = parser_res.get_main_str() description = parser_res.get_description_str() tags = parser_res.get_tags(strict=True) data_manager = DataManager(path_data_folder, NAME_DATABASE_FILE, DATABASE_MODE) stored = data_manager.add_new_element(cmd, description, tags) if stored: logging.debug("command added") if one_line_input[0]: logger_console.log_on_console_warn("command has been adjusted") logger_console.log_on_console_warn("multi-line commands are not fully supported") logger_console.log_on_console_info("command: '%s'" % cmd) if tags and len(tags) > 0 and tags[0] != "": str_tags = "" for tag in tags: str_tags += logger_console.tag_colored + tag + " " logger_console.log_on_console_info("tags: %s" % str_tags) if description and len(description) > 0: logger_console.log_on_console_info("description: %s%s" % (logger_console.desc_colored, description)) else: logging.error("store command failed") logger_console.log_on_console_info("store command failed, please check your log file: %s" % os.path.abspath(path_data_folder + NAME_LOG_FILE))
def test_TLDR_search_with_simulated_manual_input(self): """ note: without "thread.has_been_stopped()" check -> >= 3 seconds with "thread.has_been_stopped()" check at fnames level -> 0.9 seconds with "thread.has_been_stopped()" check at line level -> 0.85 seconds :return: """ test_strings = [ "open port listen", "a fi le file ile" ] for test in test_strings: start_time = time.time() fake_manual_input = "" tldr_parser_thread = None tldr_parser = TLDRParser() for c in test: fake_manual_input += c input_data = InputParser.parse_input(test, is_search_mode=True) if tldr_parser_thread: tldr_parser_thread.stop() tldr_parser_thread = TLDRParseThread(tldr_parser, input_data) tldr_parser_thread.start() time.sleep(0.05) while tldr_parser_thread.is_alive(): time.sleep(0.01) execution_time = (time.time() - start_time) logging.info("execution_time: %s -> %s seconds" % (test, execution_time)) self.assertTrue(execution_time < 3.0, msg="execution takes too long: %s sec" % execution_time)
def filter(self, search, n=100): """ get filtered commands array :param n: max number of returned rows :param search: filter text :return: array with [cmd, description, tags array, bool advanced] """ # put all to lower case search = search.lower() # parse input search text input_data = InputParser.parse_input(search, is_search_mode=True) if input_data: self.search_filters = input_data if not input_data.is_advanced(): filtered_data = self.database.get_last_n_filtered_elements( generic_filters=input_data.get_main_words(), n=n) else: filtered_data = self.database.get_last_n_filtered_elements( generic_filters=input_data.get_main_words(), description_filters=input_data.get_description_words( strict=True), tags_filters=input_data.get_tags(strict=True), n=n) if filtered_data: return filtered_data else: return [] else: # the string inserted does not match the regex and a dummy response is returned self.search_filters = self.DUMMY_INPUT_DATA return []
def run_loop_edit_command(self, blocks_shift, bash_parser_thread): """ loop to capture user input keys to interact with the "edit command" page :return: [False, None] if no change has been done, [True, <new_command_str>] otherwise] """ # import this locally to improve performance when the program is loaded from fastHistory.pick.pageEditCommand import PageEditCommand page_command = PageEditCommand( self.drawer, option=self.current_selected_option, search_filters=self.data_manager.get_search_filters(), context_shift=self.context_shift, blocks_shift=blocks_shift) current_command = self.current_selected_option[ DataManager.OPTION.INDEX_CMD] command_t = TextManager( self.current_selected_option[DataManager.OPTION.INDEX_CMD], max_x=self.drawer.get_max_x() - self.EDIT_FIELD_MARGIN) input_error_msg = None while True: if page_command.has_minimum_size(): page_command.clean_page() page_command.draw_page_edit( command_text=command_t.get_text_to_print(), command_cursor_index=command_t.get_cursor_index_to_print(), input_error_msg=input_error_msg, data_from_man_page=bash_parser_thread.get_result()) page_command.refresh_page() # wait for char c = self.drawer.wait_next_char( multi_threading_mode=bash_parser_thread.is_alive()) if c == Keys.KEY_TIMEOUT: continue # save and exit elif c in Keys.KEYS_ENTER: new_command = command_t.get_text() if current_command == new_command: return [False, None] else: is_valid_command = InputParser.is_cmd_str_valid( new_command) if is_valid_command: if self.data_manager.update_command( current_command, new_command): return [True, new_command] else: msg = "database error during saving, please try again" logging.error(msg) input_error_msg = msg else: input_error_msg = "no tags and description are allowed here" # exit without saving elif c == Keys.KEY_TAB or c == Keys.KEY_SHIFT_TAB or c == Keys.KEY_ESC: return [False, None] # -> command elif c == Keys.KEY_RIGHT: if command_t.is_cursor_at_the_end(): self.context_shift.shift_context_right() else: command_t.move_cursor_right() # <- command elif c == Keys.KEY_LEFT: if not self.context_shift.is_context_index_zero(): self.context_shift.shift_context_left() elif not command_t.is_cursor_at_the_beginning(): command_t.move_cursor_left() else: # do nothing, the cursor is already on the position 0 pass # delete a char of the search elif c in Keys.KEYS_DELETE: command_t.delete_char() input_error_msg = None # move cursor to the beginning elif c == Keys.KEY_START or c == Keys.KEY_CTRL_A: command_t.move_cursor_to_start() self.context_shift.reset_context_shifted() # move cursor to the end elif c == Keys.KEY_END or c == Keys.KEY_CTRL_E: command_t.move_cursor_to_end() elif c == Keys.KEY_RESIZE: # this occurs when the console size changes self.drawer.reset() command_t.set_max_x(self.drawer.get_max_x() - self.EDIT_FIELD_MARGIN) elif c == Keys.KEY_CTRL_U: command_t.set_text("") elif type(c) is str: command_t.add_string(c, self.data_manager.get_forbidden_chars()) input_error_msg = None else: logging.error("input not handled: %s" % repr(c))
def run_loop_edit_tags(self, bash_parser_thread): """ loop to capture user input keys to interact with the "add tag" page :return: """ # import this locally to improve performance when the program is loaded from fastHistory.pick.pageEditTags import PageEditTags page_tags = PageEditTags( self.drawer, option=self.current_selected_option, search_filters=self.data_manager.get_search_filters(), context_shift=self.context_shift) current_command = self.current_selected_option[ DataManager.OPTION.INDEX_CMD] new_tags = self.current_selected_option[DataManager.OPTION.INDEX_TAGS] new_tags_str = "" for tag in new_tags: if len(tag) > 0: new_tags_str += InputParser.TAG_SIGN + tag + " " new_tags_t = TextManager(new_tags_str, max_x=self.drawer.get_max_x() - self.EDIT_FIELD_MARGIN) new_tags_t.add_string(InputParser.TAG_SIGN, self.data_manager.get_forbidden_chars()) input_error_msg = None while True: if page_tags.has_minimum_size(): page_tags.clean_page() page_tags.draw_page_edit( tags_text=new_tags_t.get_text_to_print(), tags_cursor_index=new_tags_t.get_cursor_index_to_print(), input_error_msg=input_error_msg, data_from_man_page=bash_parser_thread.get_result()) page_tags.refresh_page() # wait for char c = self.drawer.wait_next_char( multi_threading_mode=bash_parser_thread.is_alive()) if c == Keys.KEY_TIMEOUT: continue elif c in Keys.KEYS_ENTER: new_tags_array = InputParser.parse_tags_str( new_tags_t.get_text()) if new_tags_array is not None: if self.data_manager.update_tags(current_command, new_tags_array): return True else: msg = "database error during saving, please try again" logging.error(msg) input_error_msg = msg else: input_error_msg = self.TEXT_NOT_ALLOWED_STR # exit without saving # TODO fix return if "alt+char" is pressed elif c == Keys.KEY_TAB or c == Keys.KEY_SHIFT_TAB or c == Keys.KEY_ESC: return False # -> command elif c == Keys.KEY_RIGHT: if new_tags_t.is_cursor_at_the_end(): self.context_shift.shift_context_right() else: # move the search cursor one position right (->) new_tags_t.move_cursor_right() # <- command elif c == Keys.KEY_LEFT: if not self.context_shift.is_context_index_zero(): self.context_shift.shift_context_left() elif not new_tags_t.is_cursor_at_the_beginning(): new_tags_t.move_cursor_left() else: # do nothing, the cursor is already on the position 0 pass # delete a char of the search elif c in Keys.KEYS_DELETE: # the delete is allowed if the search text is not empty and if if new_tags_t.delete_char(): self.context_shift.reset_context_shifted() if input_error_msg is not None: new_tags_array = InputParser.parse_tags_str( new_tags_t.get_text()) if new_tags_array is None: input_error_msg = self.TEXT_NOT_ALLOWED_STR else: input_error_msg = None # move cursor to the beginning elif c == Keys.KEY_START or c == Keys.KEY_CTRL_A: new_tags_t.move_cursor_to_start() self.context_shift.reset_context_shifted() # move cursor to the end elif c == Keys.KEY_END or c == Keys.KEY_CTRL_E: new_tags_t.move_cursor_to_end() # this occurs when the console size changes elif c == Keys.KEY_RESIZE: self.drawer.reset() new_tags_t.set_max_x(self.drawer.get_max_x() - self.EDIT_FIELD_MARGIN) elif c == Keys.KEY_CTRL_U: new_tags_t.set_text("") elif type(c) is str: new_tags_t.add_string(c, self.data_manager.get_forbidden_chars()) new_tags_array = InputParser.parse_tags_str( new_tags_t.get_text()) if new_tags_array is None: input_error_msg = self.TEXT_NOT_ALLOWED_STR else: input_error_msg = None else: logging.error("input not handled: %s" % repr(c))
def run_loop_edit_description(self, blocks_shift, bash_parser_thread): """ loop to capture user input keys to interact with the "add description" page :return: """ # import this locally to improve performance when the program is loaded from fastHistory.pick.pageEditDescription import PageEditDescription page_desc = PageEditDescription( self.drawer, option=self.current_selected_option, search_filters=self.data_manager.get_search_filters(), context_shift=self.context_shift, blocks_shift=blocks_shift) current_command = self.current_selected_option[ DataManager.OPTION.INDEX_CMD] description_t = TextManager( InputParser.DESCRIPTION_SIGN + self.current_selected_option[DataManager.OPTION.INDEX_DESC], max_x=self.drawer.get_max_x() - self.EDIT_FIELD_MARGIN) input_error_msg = None while True: if page_desc.has_minimum_size(): page_desc.clean_page() page_desc.draw_page_edit( description_text=description_t.get_text_to_print(), description_cursor_index=description_t. get_cursor_index_to_print(), input_error_msg=input_error_msg, data_from_man_page=bash_parser_thread.get_result()) page_desc.refresh_page() # wait for char c = self.drawer.wait_next_char( multi_threading_mode=bash_parser_thread.is_alive()) if c == Keys.KEY_TIMEOUT: continue # save and exit elif c in Keys.KEYS_ENTER: new_description = InputParser.parse_description( description_t.get_text()) if new_description is not None: if self.data_manager.update_description( current_command, new_description): return True else: msg = "database error during saving, please try again" logging.error(msg) input_error_msg = msg else: input_error_msg = self.TEXT_NOT_ALLOWED_STR # exit without saving elif c == Keys.KEY_TAB or c == Keys.KEY_SHIFT_TAB or c == Keys.KEY_ESC: return False # -> command elif c == Keys.KEY_RIGHT: if description_t.is_cursor_at_the_end(): self.context_shift.shift_context_right() else: description_t.move_cursor_right() # <- command elif c == Keys.KEY_LEFT: if not self.context_shift.is_context_index_zero(): self.context_shift.shift_context_left() elif not description_t.is_cursor_at_the_beginning(): description_t.move_cursor_left() else: # do nothing, the cursor is already on the position 0 pass # delete a char of the search elif c in Keys.KEYS_DELETE: description_t.delete_char() if input_error_msg is not None: new_description = InputParser.parse_description( description_t.get_text()) if new_description is None: input_error_msg = self.TEXT_NOT_ALLOWED_STR else: input_error_msg = None # move cursor to the beginning elif c == Keys.KEY_START or c == Keys.KEY_CTRL_A: description_t.move_cursor_to_start() self.context_shift.reset_context_shifted() # move cursor to the end elif c == Keys.KEY_END or c == Keys.KEY_CTRL_E: description_t.move_cursor_to_end() elif c == "#": # Keys.KEY_RESIZE: # this occurs when the console size changes self.drawer.reset() description_t.set_max_x(self.drawer.get_max_x() - self.EDIT_FIELD_MARGIN) elif c == Keys.KEY_CTRL_U: description_t.set_text("") elif type(c) is str: description_t.add_string( c, self.data_manager.get_forbidden_chars()) new_description = InputParser.parse_description( description_t.get_text()) if new_description is None: input_error_msg = self.TEXT_NOT_ALLOWED_STR else: input_error_msg = None else: logging.error("input not handled: %s" % repr(c))
def run_loop_tldr(self, cached_in_memory_pages): """ :return: """ tldr_parser = TLDRParser(cached_in_memory_pages) tldr_parser_thread = None input_data = InputData(False, "", []) tldr_options_reload_needed = True tldr_options_waiting = True tldr_examples_reload_needed = True tldr_ui_reload = True msg_to_show = None page_tldr_search = PageSelectTLDR(self.drawer) while True: # TODO fix "alt+d" crash if tldr_options_reload_needed: tldr_options_reload_needed = False input_data = InputParser.parse_input( self.search_field.get_text_lower(), is_search_mode=True) if tldr_parser_thread: tldr_parser_thread.stop() tldr_parser_thread = TLDRParseThread(tldr_parser, input_data) tldr_parser_thread.start() tldr_options_waiting = True if tldr_options_waiting and not tldr_parser_thread.is_alive(): tldr_options_waiting = False self.tldr_options = tldr_parser_thread.get_result_tldr_options( ) self.update_tldr_options_to_draw() tldr_examples_reload_needed = True tldr_ui_reload = True if tldr_examples_reload_needed: tldr_examples_reload_needed = False if len(self.tldr_options_draw) > 0: tldr_page_match = self.tldr_options_draw[ self.tldr_options_draw_index] self.tldr_examples = tldr_parser.get_tldr_cmd_examples( tldr_page_match) self.update_tldr_example_to_draw() self.update_tldr_options_to_draw_cmd_availability() # reset example index self.tldr_examples_index = self.tldr_examples.get_first_example_index( ) self.tldr_examples_draw_index = self.tldr_examples_index else: self.tldr_examples = ParsedTLDRExample() self.tldr_examples_index = 0 self.tldr_examples_draw = [] if page_tldr_search.has_minimum_size(): page_tldr_search.draw_page( search_filters=self.search_field, input_data=input_data, tldr_options_draw=self.tldr_options_draw, tldr_options_draw_index=self.tldr_options_draw_index, tldr_examples_draw=self.tldr_examples_draw, example_draw_index=self.tldr_examples_draw_index, example_content_shift=self.example_content_shift, focus_area=self.focus, has_url_more_info=self.tldr_examples.has_url_more_info(), is_waiting=tldr_options_waiting, msg_to_show=msg_to_show) if msg_to_show: msg_to_show = None # wait for char c = self.drawer.wait_next_char( multi_threading_mode=tldr_parser_thread.is_alive()) logging.debug("pressed key: %s" % repr(c)) tldr_ui_reload = True if c == Keys.KEY_TIMEOUT: tldr_ui_reload = False continue elif c in Keys.KEYS_ENTER: selected_example = self.get_selected_example( search_input=input_data) if selected_example: return [True, [True, selected_example]] elif c == Keys.KEY_CTRL_SPACE: example_to_copy = self.get_selected_example( search_input=input_data, copied=True) if example_to_copy: msg_to_show = ConsoleUtils.copy_to_clipboard( example_to_copy, show_data_in_msg=False)[1] elif c == Keys.KEY_CTRL_L: if self.tldr_examples.has_url_more_info(): msg_to_show = ConsoleUtils.copy_to_clipboard( self.tldr_examples.get_url_more_info())[1] elif c == Keys.KEY_CTRL_E: res = ConsoleUtils.copy_to_clipboard( self.tldr_examples.get_tldr_github_page()) msg_to_show = res[1] elif c == Keys.KEY_TAB: self.flip_focus() # go back to main page elif c == Keys.KEY_CTRL_F or c == Keys.KEY_CTRL_D or c == Keys.KEY_ESC: return [False, 0] elif c == Keys.KEY_UP: if self.move_up(): tldr_examples_reload_needed = True elif c == Keys.KEY_DOWN: if self.move_down(): tldr_examples_reload_needed = True # -> command elif c == Keys.KEY_RIGHT: self.move_right() # <- command elif c == Keys.KEY_LEFT: self.move_left() # this occurs when the console size changes elif c == Keys.KEY_RESIZE: self.drawer.reset() self.search_field.set_max_x(self.drawer.get_max_x(), with_margin_x=True) self.update_tldr_options_to_draw() # move cursor to the beginning elif c == Keys.KEY_START or c == Keys.KEY_CTRL_A: self.search_field.move_cursor_to_start() self.example_content_shift.reset_context_shifted() self.focus = PageSelectTLDR.Focus.AREA_FILES # move cursor to the end elif c == Keys.KEY_CTRL_E: self.search_field.move_cursor_to_end() self.example_content_shift.reset_context_shifted() self.focus = PageSelectTLDR.Focus.AREA_FILES elif c == Keys.KEY_CTRL_U: if self.search_field.set_text(""): tldr_options_reload_needed = True # delete a char of the search elif c in Keys.KEYS_DELETE: if self.delete_char(): tldr_options_reload_needed = True # normal search char elif type(c) is str: if self.add_str(c): tldr_options_reload_needed = True else: logging.error("input not handled: %s" % repr(c))