Author: andar
Revision: 5089
Log:
Add tab completion support
Diff:
Modified: trunk/deluge/ui/console/main.py
===================================================================
--- trunk/deluge/ui/console/main.py 2009-04-18 18:53:16 UTC (rev 5088)
+++ trunk/deluge/ui/console/main.py 2009-04-18 22:27:54 UTC (rev 5089)
@@ -161,7 +161,7 @@
# We want to do an interactive session, so start up the curses screen
and
# pass it the function that handles commands
colors.init_colors()
- self.screen = screen.Screen(stdscr, self.do_command)
+ self.screen = screen.Screen(stdscr, self.do_command,
self.tab_completer)
self.statusbars = StatusBars()
self.screen.topbar = "{{status}}Deluge " + deluge.common.get_version()
+ " Console"
@@ -176,8 +176,20 @@
reactor.run()
def start(self):
- pass
+ # 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"]))
+ 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 update(self):
pass
@@ -227,3 +239,87 @@
raise
except Exception, e:
self.write("{{error}}" + str(e))
+
+ def tab_completer(self, line, cursor, second_hit):
+ """
+ Called when the user hits 'tab' and will autocomplete or show options.
+
+ :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.
+ if " " not in line:
+ if len(line) == 0:
+ # We only print these out if it's a second_hit
+ if second_hit:
+ # There is nothing in line so just print out all possible
commands
+ # and return.
+ self.write(" ")
+ for cmd in self._commands:
+ self.write(cmd)
+ return ("", 0)
+ # Iterate through the commands looking for ones that startwith the
+ # line.
+ possible_matches = []
+ for cmd in self._commands:
+ if cmd.startswith(line):
+ possible_matches.append(cmd)
+
+ line_prefix = ""
+
+ else:
+ # This isn't a command so treat it as a torrent_id or torrent name
+ name = line.split(" ")[-1]
+ if len(name) == 0:
+ # There is nothing in the string, so just display all possible
options
+ if second_hit:
+ self.write(" ")
+ # Display all torrent_ids and torrent names
+ for torrent_id, name in self.torrents:
+ self.write(torrent_id)
+ self.write(name)
+ return (line, cursor)
+
+ # Find all possible matches
+ possible_matches = []
+ for torrent_id, torrent_name in self.torrents:
+ if torrent_id.startswith(name):
+ possible_matches.append(torrent_id)
+ elif torrent_name.startswith(name):
+ possible_matches.append(torrent_name)
+
+ # Set the line prefix that should be prepended to any input line
match
+ line_prefix = " ".join(line.split(" ")[:-1]) + " "
+
+ # 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:
+ new_line = line_prefix + possible_matches[0] + " "
+ return (new_line, len(new_line))
+ else:
+ if second_hit:
+ # Only print these out if it's a second_hit
+ self.write(" ")
+ for cmd in possible_matches:
+ self.write(cmd)
+ return (line, cursor)
+
+ def on_torrent_added_event(self, torrent_id):
+ def on_torrent_status(status):
+ self.torrents.append(torrent_id, status["name"])
+ client.get_torrent_status(torrent_id,
["name"]).addCallback(on_torrent_status)
+
+ def on_torrent_removed_event(self, torrent_id):
+ for index, (tid, name) in enumerate(self.torrents):
+ if torrent_id == tid:
+ del self.torrents[index]
Modified: trunk/deluge/ui/console/screen.py
===================================================================
--- trunk/deluge/ui/console/screen.py 2009-04-18 18:53:16 UTC (rev 5088)
+++ trunk/deluge/ui/console/screen.py 2009-04-18 22:27:54 UTC (rev 5089)
@@ -44,16 +44,21 @@
INPUT_HISTORY_SIZE = 500
class Screen(CursesStdIO):
- def __init__(self, stdscr, command_parser):
+ def __init__(self, stdscr, command_parser, tab_completer=None):
"""
A curses screen designed to run as a reader in a twisted reactor.
:param command_parser: a function that will be passed a string when the
user hits enter
+ :param tab_completer: a function that is sent the `:prop:input` string
when
+ the user hits tab. It's intended purpose is to modify the input
string.
+ It should return a 2-tuple (input string, input cursor).
+
"""
log.debug("Screen init!")
# Function to be called with commands
self.command_parser = command_parser
+ self.tab_completer = tab_completer
self.stdscr = stdscr
# Make the input calls non-blocking
self.stdscr.nodelay(1)
@@ -67,6 +72,9 @@
self.input_history = []
self.input_history_index = 0
+ # Keep track of double-tabs
+ self.tab_count = 0
+
# Strings for the 2 status bars
self.topbar = ""
self.bottombar = ""
@@ -78,13 +86,7 @@
# The offset to display lines
self.display_lines_offset = 0
- # Create some color pairs, should probably be moved to colors.py
- # Regular text
- #curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK)
- # Status bar
- #curses.init_pair(2, curses.COLOR_YELLOW, curses.COLOR_BLUE)
-
-
+ # Refresh the screen to display everything right away
self.refresh()
def connectionLost(self, reason):
@@ -117,7 +119,7 @@
self.lines.extend(text.splitlines())
while len(self.lines) > LINES_BUFFER_SIZE:
# Remove the oldest line if the max buffer size has been reached
- self.lines.remove(0)
+ del self.lines[0]
self.refresh()
@@ -197,7 +199,7 @@
if len(self.input_history) == INPUT_HISTORY_SIZE:
# Remove the oldest input history if the max history size
# is reached.
- self.input_history.remove(0)
+ del self.input_history[0]
self.input_history.append(self.input)
self.input_history_index = len(self.input_history)
self.input = ""
@@ -205,6 +207,22 @@
self.input_cursor = 0
self.stdscr.refresh()
+ # Run the tab completer function
+ elif c == 9:
+ # Keep track of tab hit count to know when it's double-hit
+ self.tab_count += 1
+ if self.tab_count > 1:
+ second_hit = True
+ self.tab_count = 0
+ else:
+ second_hit = False
+
+ if self.tab_completer:
+ # We only call the tab completer function if we're at the end
of
+ # the input string on the cursor is on a space
+ if self.input_cursor == len(self.input) or
self.input[self.input_cursor] == " ":
+ self.input, self.input_cursor =
self.tab_completer(self.input, self.input_cursor, second_hit)
+
# We use the UP and DOWN keys to cycle through input history
elif c == curses.KEY_UP:
if self.input_history_index - 1 >= 0:
@@ -254,6 +272,10 @@
# Move the cursor forward
self.input_cursor += 1
+ # We remove the tab count if the key wasn't a tab
+ if c != 9:
+ self.tab_count = 0
+
# Update the input string on the screen
self.add_string(self.rows - 1, self.input)
self.stdscr.move(self.rows - 1, self.input_cursor)
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"deluge-commit" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/deluge-commit?hl=en
-~----------~----~----~----~------~----~------~--~---