def complete_line(line, possible_matches): "Find the common prefix of possible matches, proritizing matching-case elements" if not possible_matches: return line line = line.replace(r"\ ", " ") matches1 = [] matches2 = [] for match in possible_matches: match = format_utils.remove_formatting(match) match = match.replace(r"\ ", " ") m1, m2 = "", "" for i, c in enumerate(line): if m1 and m2: break if not m1 and c != line[i]: m1 = line[:i] if not m2 and c.lower() != line[i].lower(): m2 = line[:i] if not m1: matches1.append(match) elif not m2: matches2.append(match) possible_matches = matches1 + matches2 maxlen = 9001 for match in possible_matches[1:]: for i, c in enumerate(match): try: if c.lower() != possible_matches[0][i].lower(): maxlen = min(maxlen, i) break except IndexError: maxlen = min(maxlen, i) break return possible_matches[0][:maxlen].replace(" ", r"\ ")
def tab_completer(self, line, cursor, hits): """ Called when the user hits 'tab' and will autocomplete or show options. If a command is already supplied in the line, this function will call the complete method of the command. :param line: str, the current input string :param cursor: int, the cursor position in the line :param second_hit: bool, if this is the second time in a row the tab key has been pressed :returns: 2-tuple (string, cursor position) """ # First check to see if there is no space, this will mean that it's a # command that needs to be completed. #We don't want to split by escaped spaces def split(string): return re.split(r"(?<!\\) ", string) if " " not in line: possible_matches = [] # Iterate through the commands looking for ones that startwith the # line. for cmd in self.console._commands: if cmd.startswith(line): possible_matches.append(cmd) line_prefix = "" else: cmd = split(line)[0] if cmd in self.console._commands: # Call the command's complete method to get 'er done possible_matches = self.console._commands[cmd].complete(split(line)[-1]) line_prefix = " ".join(split(line)[:-1]) + " " else: # This is a bogus command return (line, cursor) # No matches, so just return what we got passed if len(possible_matches) == 0: return (line, cursor) # If we only have 1 possible match, then just modify the line and # return it, else we need to print out the matches without modifying # the line. elif len(possible_matches) == 1: #Do not append space after directory names new_line = line_prefix + possible_matches[0] if not new_line.endswith("/") and not new_line.endswith(r"\\"): new_line += " " #We only want to print eventual colors or other control characters, not return them new_line = format_utils.remove_formatting(new_line) return (new_line, len(new_line)) else: if hits == 1: p = " ".join(split(line)[:-1]) try: l_arg = split(line)[-1] except IndexError: l_arg = "" new_line = " ".join( [p, complete_line(l_arg, possible_matches)] ).lstrip() if len(format_utils.remove_formatting(new_line)) > len(line): line = new_line cursor = len(line) elif hits >= 2: max_list = self.console_config["torrents_per_tab_press"] match_count = len(possible_matches) listed = (hits-2) * max_list pages = (match_count-1) // max_list + 1 left = match_count - listed if hits == 2: self.write(" ") if match_count >= 4: self.write("{!green!}Autocompletion matches:") #Only list some of the matching torrents as there can be hundreds of them if self.console_config["third_tab_lists_all"]: if hits == 2 and left > max_list: for i in range(listed, listed + max_list): match = possible_matches[i] self.write(match.replace(r"\ ", " ")) self.write("{!error!}And %i more. Press <tab> to list them" % (left - max_list) ) else: self.tab_count = 0 for match in possible_matches[listed:]: self.write(match.replace(r"\ ", " ")) else: if left > max_list: for i in range(listed, listed + max_list): match = possible_matches[i] self.write(match.replace(r"\ ", " ")) self.write("{!error!}And %i more (%i/%i). Press <tab> to view more" % (left - max_list, hits-1, pages) ) else: self.tab_count = 0 for match in possible_matches[listed:]: self.write(match.replace(r"\ ", " ")) if hits > 2: self.write("{!green!}Finished listing %i torrents (%i/%i)" % (match_count, hits-1, pages) ) #We only want to print eventual colors or other control characters, not return them line = format_utils.remove_formatting(line) cursor = len(line) return (line, cursor)
def __init__(self, stdscr, encoding=None): component.Component.__init__(self, "LegacyUI", 1, depend=["SessionProxy"]) self.batch_write = False # A list of strings to be displayed based on the offset (scroll) self.lines = [] # The offset to display lines self.display_lines_offset = 0 # Holds the user input and is cleared on 'enter' self.input = "" self.input_incomplete = "" self._old_char = 0 self._last_char = 0 self._last_del_char = '' # Keep track of where the cursor is self.input_cursor = 0 # Keep a history of inputs self.input_history = [] self.input_history_index = 0 # Keep track of double- and multi-tabs self.tab_count = 0 # Get a handle to the main console self.console = component.get("ConsoleUI") self.console_config = component.get("AllTorrents").config #To avoid having to truncate the file every time we're writing # or doing it on exit(and therefore relying on an error-less # or in other words clean exit, we're going to have two files # that we swap around based on length config_dir = deluge.configmanager.get_config_dir() self.history_file = [ os.path.join(config_dir, "legacy.hist1"), os.path.join(config_dir, "legacy.hist2") ] self._hf_lines = [0, 0] if self.console_config["save_legacy_history"]: try: lines1 = open(self.history_file[0], 'r').read().splitlines() self._hf_lines[0] = len(lines1) except: lines1 = [] self._hf_lines[0] = 0 try: lines2 = open(self.history_file[1], 'r').read().splitlines() self._hf_lines[1] = len(lines2) except: lines2 = [] self._hf_lines[1] = 0 #The non-full file is the active one if self._hf_lines[0] > self._hf_lines[1]: self.lines = lines1 + lines2 else: self.lines = lines2 + lines1 if len(self.lines) > MAX_HISTFILE_SIZE: self.lines = self.lines[-MAX_HISTFILE_SIZE:] #Instead of having additional input history file, we can # simply scan for lines beginning with ">>> " for i, line in enumerate(self.lines): #if not isinstance(line, unicode): #line = line.encode(self.encoding) #self.lines[i] = line line = format_utils.remove_formatting(line) if line.startswith(">>> "): input = line[4:] if self.console_config["ignore_duplicate_lines"]: if len(self.input_history) > 0: if self.input_history[-1] != input: self.input_history.append(input) else: self.input_history.append(input) self.input_history_index = len(self.input_history) component.start("LegacyUI") # show the cursor curses.curs_set(2) BaseMode.__init__(self, stdscr, encoding) # This gets fired once we have received the torrents list from the core self.started_deferred = defer.Deferred() # Maintain a list of (torrent_id, name) for use in tab completion self.torrents = [] def on_session_state(result): def on_torrents_status(torrents): for torrent_id, status in torrents.items(): self.torrents.append((torrent_id, status["name"])) self.started_deferred.callback(True) client.core.get_torrents_status({"id": result}, ["name"]).addCallback(on_torrents_status) client.core.get_session_state().addCallback(on_session_state) # Register some event handlers to keep the torrent list up-to-date client.register_event_handler("TorrentAddedEvent", self.on_torrent_added_event) client.register_event_handler("TorrentRemovedEvent", self.on_torrent_removed_event)
def tab_completer(self, line, cursor, hits): """ Called when the user hits 'tab' and will autocomplete or show options. If a command is already supplied in the line, this function will call the complete method of the command. :param line: str, the current input string :param cursor: int, the cursor position in the line :param second_hit: bool, if this is the second time in a row the tab key has been pressed :returns: 2-tuple (string, cursor position) """ # First check to see if there is no space, this will mean that it's a # command that needs to be completed. #We don't want to split by escaped spaces def split(string): return re.split(r"(?<!\\) ", string) if " " not in line: possible_matches = [] # Iterate through the commands looking for ones that startwith the # line. for cmd in self.console._commands: if cmd.startswith(line): possible_matches.append(cmd) line_prefix = "" else: cmd = split(line)[0] if cmd in self.console._commands: # Call the command's complete method to get 'er done possible_matches = self.console._commands[cmd].complete( split(line)[-1]) line_prefix = " ".join(split(line)[:-1]) + " " else: # This is a bogus command return (line, cursor) # No matches, so just return what we got passed if len(possible_matches) == 0: return (line, cursor) # If we only have 1 possible match, then just modify the line and # return it, else we need to print out the matches without modifying # the line. elif len(possible_matches) == 1: #Do not append space after directory names new_line = line_prefix + possible_matches[0] if not new_line.endswith("/") and not new_line.endswith(r"\\"): new_line += " " #We only want to print eventual colors or other control characters, not return them new_line = format_utils.remove_formatting(new_line) return (new_line, len(new_line)) else: if hits == 1: p = " ".join(split(line)[:-1]) try: l_arg = split(line)[-1] except IndexError: l_arg = "" new_line = " ".join( [p, complete_line(l_arg, possible_matches)]).lstrip() if len(format_utils.remove_formatting(new_line)) > len(line): line = new_line cursor = len(line) elif hits >= 2: max_list = self.console_config["torrents_per_tab_press"] match_count = len(possible_matches) listed = (hits - 2) * max_list pages = (match_count - 1) // max_list + 1 left = match_count - listed if hits == 2: self.write(" ") if match_count >= 4: self.write("{!green!}Autocompletion matches:") #Only list some of the matching torrents as there can be hundreds of them if self.console_config["third_tab_lists_all"]: if hits == 2 and left > max_list: for i in range(listed, listed + max_list): match = possible_matches[i] self.write(match.replace(r"\ ", " ")) self.write( "{!error!}And %i more. Press <tab> to list them" % (left - max_list)) else: self.tab_count = 0 for match in possible_matches[listed:]: self.write(match.replace(r"\ ", " ")) else: if left > max_list: for i in range(listed, listed + max_list): match = possible_matches[i] self.write(match.replace(r"\ ", " ")) self.write( "{!error!}And %i more (%i/%i). Press <tab> to view more" % (left - max_list, hits - 1, pages)) else: self.tab_count = 0 for match in possible_matches[listed:]: self.write(match.replace(r"\ ", " ")) if hits > 2: self.write( "{!green!}Finished listing %i torrents (%i/%i)" % (match_count, hits - 1, pages)) #We only want to print eventual colors or other control characters, not return them line = format_utils.remove_formatting(line) cursor = len(line) return (line, cursor)
def __init__(self, stdscr, encoding=None): component.Component.__init__(self, "LegacyUI", 1, depend=["SessionProxy"]) self.batch_write = False # A list of strings to be displayed based on the offset (scroll) self.lines = [] # The offset to display lines self.display_lines_offset = 0 # Holds the user input and is cleared on 'enter' self.input = "" self.input_incomplete = "" self._old_char = 0 self._last_char = 0 self._last_del_char = '' # Keep track of where the cursor is self.input_cursor = 0 # Keep a history of inputs self.input_history = [] self.input_history_index = 0 # Keep track of double- and multi-tabs self.tab_count = 0 # Get a handle to the main console self.console = component.get("ConsoleUI") self.console_config = component.get("AllTorrents").config #To avoid having to truncate the file every time we're writing # or doing it on exit(and therefore relying on an error-less # or in other words clean exit, we're going to have two files # that we swap around based on length config_dir = deluge.configmanager.get_config_dir() self.history_file = [ os.path.join(config_dir, "legacy.hist1"), os.path.join(config_dir, "legacy.hist2") ] self._hf_lines = [0, 0] if self.console_config["save_legacy_history"]: try: lines1 = open(self.history_file[0], 'r').read().splitlines() self._hf_lines[0] = len(lines1) except: lines1 = [] self._hf_lines[0] = 0 try: lines2 = open(self.history_file[1], 'r').read().splitlines() self._hf_lines[1] = len(lines2) except: lines2 = [] self._hf_lines[1] = 0 #The non-full file is the active one if self._hf_lines[0] > self._hf_lines[1]: self.lines = lines1 + lines2 else: self.lines = lines2 + lines1 if len(self.lines) > MAX_HISTFILE_SIZE: self.lines = self.lines[-MAX_HISTFILE_SIZE:] #Instead of having additional input history file, we can # simply scan for lines beginning with ">>> " for i, line in enumerate(self.lines): #if not isinstance(line, unicode): #line = line.encode(self.encoding) #self.lines[i] = line line = format_utils.remove_formatting(line) if line.startswith(">>> "): input = line[4:] if self.console_config["ignore_duplicate_lines"]: if len(self.input_history) > 0: if self.input_history[-1] != input: self.input_history.append(input) else: self.input_history.append(input) self.input_history_index = len(self.input_history) component.start("LegacyUI") # show the cursor curses.curs_set(2) BaseMode.__init__(self, stdscr, encoding) # This gets fired once we have received the torrents list from the core self.started_deferred = defer.Deferred() # Maintain a list of (torrent_id, name) for use in tab completion self.torrents = [] def on_session_state(result): def on_torrents_status(torrents): for torrent_id, status in torrents.items(): self.torrents.append((torrent_id, status["name"])) self.started_deferred.callback(True) client.core.get_torrents_status({ "id": result }, ["name"]).addCallback(on_torrents_status) client.core.get_session_state().addCallback(on_session_state) # Register some event handlers to keep the torrent list up-to-date client.register_event_handler("TorrentAddedEvent", self.on_torrent_added_event) client.register_event_handler("TorrentRemovedEvent", self.on_torrent_removed_event)