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
-~----------~----~----~----~------~----~------~--~---

Reply via email to