Index: IPython/iplib.py
===================================================================
--- IPython/iplib.py	(revision 1034)
+++ IPython/iplib.py	(working copy)
@@ -1154,7 +1154,13 @@
     def init_readline(self):
         """Command history completion/saving/reloading."""
         try:
-            import readline
+            if sys.platform=="win32":
+                try:
+                    import IPython.winreadline as readline
+                except ImportError:
+                    import readline
+            else:
+                import readline
         except ImportError:
             self.has_readline = 0
             self.readline = None
Index: IPython/winreadline/PyReadline.py
===================================================================
--- IPython/winreadline/PyReadline.py	(revision 0)
+++ IPython/winreadline/PyReadline.py	(revision 0)
@@ -0,0 +1,1122 @@
+''' an attempt to implement readline for Python in Python using ctypes'''
+
+import string
+import math
+import sys
+from glob import glob
+import os
+import re
+import traceback
+import operator
+
+import win32con as c32
+
+import Console
+from Console import log
+from keysyms import key_text_to_keyinfo
+
+def quote_char(c):
+  if ' ' <= c <= '~':
+    return c
+  else:
+    return repr(c)[1:-1]
+  
+class Readline:
+  def __init__(self):
+    self.startup_hook = None
+    self.pre_input_hook = None
+    self.completer = None
+    self.completer_delims = " \t\n\"\\'`@$><=;|&{("
+    self.history_length = -1
+    self.history = [] # strings for previous commands
+    self.history_cursor = 0
+    self.undo_stack = [] # each entry is a tuple with cursor_position and line_text
+    self.line_buffer = []
+    self.line_cursor = 0
+    self.console = Console.Console()
+    self.size = self.console.size()
+    self.prompt_color = None
+    self.command_color = None
+    self.key_dispatch = {}
+    self.previous_func = None
+    self.first_prompt = True
+    self.next_meta = False # True to force meta on next character
+    self.tabstop = 4
+
+    self.emacs_editing_mode(None)
+    self.begidx = 0
+    self.endidx = 0
+
+    # variables you can control with parse_and_bind
+    self.show_all_if_ambiguous = 'off'
+    self.mark_directories = 'on'
+    self.bell_style = 'none'
+
+  def _bell(self):
+    '''ring the bell if requested.'''
+    if self.bell_style == 'none':
+      self.console.bell()
+      
+  def _quoted_text(self):
+    quoted = [ quote_char(c) for c in self.line_buffer ]
+    self.line_char_width = [ len(c) for c in quoted ]
+    return ''.join(quoted)
+
+  def _line_text(self):
+    return ''.join(self.line_buffer)
+
+  def _set_line(self, text, cursor=None):
+    self.line_buffer = [ c for c in str(text) ]
+    if cursor is None:
+      self.line_cursor = len(self.line_buffer)
+    else:
+      self.line_cursor = cursor
+
+  def _reset_line(self):
+    self.line_buffer = []
+    self.line_cursor = 0
+    self.undo_stack = []
+
+  def _clear_after(self):
+    c = self.console
+    x, y = c.pos()
+    w, h = c.size()
+    c.rectangle((x, y, w, y+1))
+    c.rectangle((0, y+1, w, min(y+3,h)))
+
+  def _set_cursor(self):
+    c = self.console
+    xc, yc = self.prompt_end_pos
+    w, h = c.size()
+    xc += reduce(operator.add, self.line_char_width[0:self.line_cursor], 0)
+    while(xc > w):
+      xc -= w
+      yc += 1
+    c.pos(xc, yc)
+
+  def _print_prompt(self):
+    c = self.console
+    log('prompt="%s"' % repr(self.prompt))
+    x, y = c.pos()
+    n = c.write_scrolling(self.prompt, self.prompt_color)
+    self.prompt_begin_pos = (x, y - n)
+    self.prompt_end_pos = c.pos()
+    self.size = c.size()
+
+  def _update_prompt_pos(self, n):
+    if n != 0:
+      bx, by = self.prompt_begin_pos
+      ex, ey = self.prompt_end_pos
+      self.prompt_begin_pos = (bx, by - n)
+      self.prompt_end_pos = (ex, ey - n)
+      
+  def readline(self, prompt=''):
+    '''Try to act like GNU readline.'''
+
+    # handle startup_hook
+    if self.first_prompt:
+      self.first_prompt = False
+      if self.startup_hook:
+        try:
+          self.startup_hook()
+        except:
+          print 'startup hook failed'
+          traceback.print_exc()
+          
+    c = self.console
+    self._reset_line()
+    self.prompt = prompt
+    self._print_prompt()
+
+    if self.pre_input_hook:
+      try:
+        self.pre_input_hook()
+      except:
+        print 'pre_input_hook failed'
+        traceback.print_exc()
+        self.pre_input_hook = None
+
+    while 1:
+      c.pos(*self.prompt_end_pos)
+      ltext = self._quoted_text()
+      n = c.write_scrolling(ltext, self.command_color)
+      self._update_prompt_pos(n)
+      self._clear_after()
+      self._set_cursor()
+
+      event = c.getkeypress()
+      if self.next_meta:
+        self.next_meta = False
+        control, meta, shift, code = event.keyinfo
+        event.keyinfo = (control, True, shift, code)
+        
+      try:
+        dispatch_func = self.key_dispatch[event.keyinfo]
+      except KeyError:
+        c.bell()
+        continue
+      r = None
+      if dispatch_func:
+        r = dispatch_func(event)
+        ltext = self._line_text()
+        if self.undo_stack and ltext == self.undo_stack[-1][1]:
+          self.undo_stack[-1][0] = self.line_cursor
+        else:
+          self.undo_stack.append([self.line_cursor, ltext])
+
+      self.previous_func = dispatch_func
+      if r:
+        break
+      
+    c.write('\r\n')
+
+    rtext = self._line_text()
+    self.add_history(rtext)
+      
+    log('returning(%s)' % rtext)
+    return rtext + '\n'
+
+  def parse_and_bind(self, string):
+    '''Parse and execute single line of a readline init file.'''
+    try:
+      log('parse_and_bind("%s")' % string)
+      if string.startswith('#'):
+        return
+      if string.startswith('set'):
+        m = re.compile(r'set\s+([-a-zA-Z0-9]+)\s+(.+)\s*$').match(string)
+        if m:
+          var_name = m.group(1)
+          val = m.group(2)
+          try:
+            setattr(self, var_name.replace('-','_'), val)
+          except AttributeError:
+            log('unknown var="%s" val="%s"' % (var_name, val))
+        else:
+          log('bad set "%s"' % string)
+        return
+      log('before')
+      m = re.compile(r'\s*(.+)\s*:\s*([-a-zA-Z]+)\s*$').match(string)
+      log('here')
+      if m:
+        key = m.group(1)
+        func_name = m.group(2)
+        py_name = func_name.replace('-', '_')
+        try:
+          func = getattr(self, py_name)
+        except AttributeError:
+          log('unknown func key="%s" func="%s"' % (key, func_name))
+          print 'unknown function to bind: "%s"' % func_name
+        self._bind_key(key, func)
+    except:
+      log('error')
+      traceback.print_exc()
+      raise
+    log('return')
+    
+  def get_line_buffer(self):
+    '''Return the current contents of the line buffer.'''
+    return "".join(self.line_buffer)
+
+  def insert_text(self, string):
+    '''Insert text into the command line.'''
+    for c in string:
+      self.line_buffer.insert(self.line_cursor, c)
+      self.line_cursor += 1
+
+  def read_init_file(self, filename=None): 
+    '''Parse a readline initialization file. The default filename is the last filename used.'''
+    log('read_init_file("%s")' % filename)
+
+  def read_history_file(self, filename='~/.history'): 
+    '''Load a readline history file. The default filename is ~/.history.'''
+    try:
+      for line in open(filename, 'rt'):
+        self.add_history(line.rstrip())
+    except IOError:
+      self.history = []
+      self.history_cursor = 0
+      raise IOError
+
+  def write_history_file(self, filename='~/.history'): 
+    '''Save a readline history file. The default filename is ~/.history.'''
+    fp = open(filename, 'wb')
+    for line in self.history:
+      fp.write(line)
+      fp.write('\n')
+    fp.close()
+
+  def get_history_length(self, ):
+    '''Return the desired length of the history file.
+
+    Negative values imply unlimited history file size.'''
+    return self.history_length
+
+  def set_history_length(self, length): 
+    '''Set the number of lines to save in the history file.
+
+    write_history_file() uses this value to truncate the history file
+    when saving. Negative values imply unlimited history file size.
+    '''
+    self.history_length = length
+
+  def set_startup_hook(self, function=None): 
+    '''Set or remove the startup_hook function.
+
+    If function is specified, it will be used as the new startup_hook
+    function; if omitted or None, any hook function already installed is
+    removed. The startup_hook function is called with no arguments just
+    before readline prints the first prompt.
+
+    '''
+    self.startup_hook = function
+
+  def set_pre_input_hook(self, function=None):
+    '''Set or remove the pre_input_hook function.
+
+    If function is specified, it will be used as the new pre_input_hook
+    function; if omitted or None, any hook function already installed is
+    removed. The pre_input_hook function is called with no arguments
+    after the first prompt has been printed and just before readline
+    starts reading input characters.
+
+    '''
+    self.pre_input_hook = function
+
+  def set_completer(self, function=None): 
+    '''Set or remove the completer function.
+
+    If function is specified, it will be used as the new completer
+    function; if omitted or None, any completer function already
+    installed is removed. The completer function is called as
+    function(text, state), for state in 0, 1, 2, ..., until it returns a
+    non-string value. It should return the next possible completion
+    starting with text.
+    '''
+    log('set_completer')
+    self.completer = function
+
+  def get_completer(self): 
+    '''Get the completer function. 
+    ''' 
+
+    log('get_completer') 
+    return self.completer 
+
+  def get_begidx(self):
+    '''Get the beginning index of the readline tab-completion scope.'''
+    return self.begidx
+
+  def get_endidx(self):
+    '''Get the ending index of the readline tab-completion scope.'''
+    return self.endidx
+
+  def set_completer_delims(self, string):
+    '''Set the readline word delimiters for tab-completion.'''
+    self.completer_delims = string
+
+  def get_completer_delims(self):
+    '''Get the readline word delimiters for tab-completion.'''
+    return self.completer_delims
+
+  def add_history(self, line):
+    '''Append a line to the history buffer, as if it was the last line typed.'''
+    if not line:
+      pass
+    elif len(self.history) > 0 and self.history[-1] == line:
+      pass
+    else:
+      self.history.append(line)
+      if self.history_length > 0 and len(self.history) > self.history_length:
+        self.history = self.history[-self.history_length:]
+    self.history_cursor = len(self.history)
+
+  ### Methods below here are bindable functions
+  
+  def beginning_of_line(self, e): # (C-a)
+    '''Move to the start of the current line. '''
+    self.line_cursor = 0
+
+  def end_of_line(self, e): # (C-e)
+    '''Move to the end of the line. '''
+    self.line_cursor = len(self.line_buffer)
+
+  def forward_char(self, e): # (C-f)
+    '''Move forward a character. '''
+    if self.line_cursor < len(self.line_buffer):
+      self.line_cursor += 1
+    else:
+      self._bell()
+
+  def backward_char(self, e): # (C-b)
+    '''Move back a character. '''
+    if self.line_cursor > 0:
+      self.line_cursor -= 1
+    else:
+      self._bell()
+
+  def forward_word(self, e): # (M-f)
+    '''Move forward to the end of the next word. Words are composed of
+    letters and digits.'''
+    L = len(self.line_buffer)
+    while self.line_cursor < L:
+      self.line_cursor += 1
+      if self.line_cursor == L:
+        break
+      if self.line_buffer[self.line_cursor] not in string.letters + string.digits:
+        break
+      
+  def backward_word(self, e): # (M-b)
+    '''Move back to the start of the current or previous word. Words are
+    composed of letters and digits.'''
+    while self.line_cursor > 0:
+      self.line_cursor -= 1
+      if self.line_buffer[self.line_cursor] not in string.letters + string.digits:
+        break
+
+  def clear_screen(self, e): # (C-l)
+    '''Clear the screen and redraw the current line, leaving the current
+    line at the top of the screen.'''
+    self.console.page()
+
+  def redraw_current_line(self, e): # ()
+    '''Refresh the current line. By default, this is unbound.'''
+    pass
+
+  def accept_line(self, e): # (Newline or Return)
+    '''Accept the line regardless of where the cursor is. If this line
+    is non-empty, it may be added to the history list for future recall
+    with add_history(). If this line is a modified history line, the
+    history line is restored to its original state.'''
+    return True
+
+  def previous_history(self, e): # (C-p)
+    '''Move back through the history list, fetching the previous command. '''
+    if self.history_cursor > 0:
+      self.history_cursor -= 1
+      line = self.history[self.history_cursor]
+      self._set_line(line)
+    else:
+      self._bell()
+      
+  def next_history(self, e): # (C-n)
+    '''Move forward through the history list, fetching the next command. '''
+    if self.history_cursor < len(self.history) - 1:
+      self.history_cursor += 1
+      line = self.history[self.history_cursor]
+      self._set_line(line)
+    elif self.undo_stack:
+      cursor, text = self.undo_stack[-1]
+      self._set_line(text, cursor)
+    else:
+      self._bell()
+
+  def beginning_of_history(self, e): # (M-<)
+    '''Move to the first line in the history.'''
+    self.history_cursor = 0
+    if len(self.history) > 0:
+      self._set_line(self.history[0])
+    else:
+      self._bell()
+
+  def end_of_history(self, e): # (M->)
+    '''Move to the end of the input history, i.e., the line currently
+    being entered.'''
+    if self.undo_stack:
+      cursor, text = self.undo_stack[-1]
+      self._set_line(text, cursor)
+    else:
+      self._bell()
+
+  def _i_search(self, direction, init_event):
+    c = self.console
+    line = self._line_text()
+    query = ''
+    hc_start = self.history_cursor + direction
+    hc = hc_start
+    while 1:
+      x, y = self.prompt_end_pos
+      c.pos(0, y)
+      if direction < 0:
+        prompt = 'reverse-i-search'
+      else:
+        prompt = 'forward-i-search'
+        
+      scroll = c.write_scrolling("%s`%s': %s" % (prompt, query, line))
+      self._update_prompt_pos(scroll)
+      self._clear_after()
+
+      event = c.getkeypress()
+      if event.keysym == 'BackSpace':
+        if len(query) > 0:
+          query = query[:-1]
+          hc = hc_start
+        else:
+          c.bell()
+      elif event.char in string.letters + string.digits + string.punctuation + ' ':
+        query += event.char
+        hc = hc_start
+      elif event.keyinfo == init_event.keyinfo:
+        hc += direction
+      else:
+        if event.keysym != 'Return':
+          c.bell()
+        break
+
+      while (direction < 0 and hc >= 0) or (direction > 0 and hc < len(self.history)):
+        if self.history[hc].find(query) >= 0:
+          break
+        hc += direction
+      else:
+        c.bell()
+        continue
+      line = self.history[hc]
+      
+    px, py = self.prompt_begin_pos
+    c.pos(0, py)
+    self._set_line(line)
+    self._print_prompt()
+  
+  def reverse_search_history(self, e): # (C-r)
+    '''Search backward starting at the current line and moving up
+    through the history as necessary. This is an incremental search.'''
+    self._i_search(-1, e)
+
+  def forward_search_history(self, e): # (C-s)
+    '''Search forward starting at the current line and moving down
+    through the the history as necessary. This is an incremental search.'''
+    self._i_search(1, e)
+
+  def _non_i_search(self, direction):
+    c = self.console
+    line = self._line_text()
+    query = ''
+    while 1:
+      c.pos(*self.prompt_end_pos)
+      scroll = c.write_scrolling(":%s" % query)
+      self._update_prompt_pos(scroll)
+      self._clear_after()
+
+      event = c.getkeypress()
+      if event.keysym == 'BackSpace':
+        if len(query) > 0:
+          query = query[:-1]
+        else:
+          break
+      elif event.char in string.letters + string.digits + string.punctuation + ' ':
+        query += event.char
+      elif event.keysym == 'Return':
+        break
+      else:
+        c.bell()
+
+    if query:
+      hc = self.history_cursor - 1
+      while (direction < 0 and hc >= 0) or (direction > 0 and hc < len(self.history)):
+        if self.history[hc].find(query) >= 0:
+          self._set_line(self.history[hc])
+          self.history_cursor = hc
+          return
+        hc += direction
+      else:
+        c.bell()
+      
+
+  def non_incremental_reverse_search_history(self, e): # (M-p)
+    '''Search backward starting at the current line and moving up
+    through the history as necessary using a non-incremental search for
+    a string supplied by the user.'''
+    self._non_i_search(-1)
+
+  def non_incremental_forward_search_history(self, e): # (M-n)
+    '''Search forward starting at the current line and moving down
+    through the the history as necessary using a non-incremental search
+    for a string supplied by the user.'''
+    self._non_i_search(1)
+
+  def _search(self, direction):
+    c = self.console
+
+    if (self.previous_func != self.history_search_forward and
+        self.previous_func != self.history_search_backward):
+      self.query = ''.join(self.line_buffer[0:self.line_cursor])
+    hc = self.history_cursor + direction
+    while (direction < 0 and hc >= 0) or (direction > 0 and hc < len(self.history)):
+      h = self.history[hc]
+      if not self.query:
+        self._set_line(h)
+        self.history_cursor = hc
+        return
+      elif h.startswith(self.query) and h != self._line_text:
+        self._set_line(h, len(self.query))
+        self.history_cursor = hc
+        return
+      hc += direction
+    else:
+      self._set_line(self.query)
+      c.bell()
+   
+  def history_search_forward(self, e): # ()
+    '''Search forward through the history for the string of characters
+    between the start of the current line and the point. This is a
+    non-incremental search. By default, this command is unbound.'''
+    self._search(1)
+
+  def history_search_backward(self, e): # ()
+    '''Search backward through the history for the string of characters
+    between the start of the current line and the point. This is a
+    non-incremental search. By default, this command is unbound.'''
+    self._search(-1)
+
+  def yank_nth_arg(self, e): # (M-C-y)
+    '''Insert the first argument to the previous command (usually the
+    second word on the previous line) at point. With an argument n,
+    insert the nth word from the previous command (the words in the
+    previous command begin with word 0). A negative argument inserts the
+    nth word from the end of the previous command.'''
+    pass
+
+  def yank_last_arg(self, e): # (M-. or M-_)
+    '''Insert last argument to the previous command (the last word of
+    the previous history entry). With an argument, behave exactly like
+    yank-nth-arg. Successive calls to yank-last-arg move back through
+    the history list, inserting the last argument of each line in turn.'''
+    pass
+
+  def delete_char(self, e): # (C-d)
+    '''Delete the character at point. If point is at the beginning of
+    the line, there are no characters in the line, and the last
+    character typed was not bound to delete-char, then return EOF.'''
+    if len(self.line_buffer) == 0:
+      if self.previous_func != self.delete_char:
+        raise EOFError
+      self._bell()
+    if self.line_cursor < len(self.line_buffer):
+      del self.line_buffer[self.line_cursor]
+    else:
+      self._bell()
+
+  def backward_delete_char(self, e): # (Rubout)
+    '''Delete the character behind the cursor. A numeric argument means
+    to kill the characters instead of deleting them.'''
+    if self.line_cursor > 0:
+      del self.line_buffer[self.line_cursor-1]
+      self.line_cursor -= 1
+
+  def forward_backward_delete_char(self, e): # ()
+    '''Delete the character under the cursor, unless the cursor is at
+    the end of the line, in which case the character behind the cursor
+    is deleted. By default, this is not bound to a key.'''
+    pass
+
+  def quoted_insert(self, e): # (C-q or C-v)
+    '''Add the next character typed to the line verbatim. This is how to
+    insert key sequences like C-q, for example.'''
+    e = self.console.getkeypress()
+    self.line_buffer.insert(self.line_cursor, e.char)
+    self.line_cursor += 1
+
+  def tab_insert(self, e): # (M-TAB)
+    '''Insert a tab character. '''
+    ws = ' ' * (self.tabstop - (self.line_cursor%self.tabstop))
+    self.insert_text(ws)
+
+  def self_insert(self, e): # (a, b, A, 1, !, ...)
+    '''Insert yourself. '''
+    self.line_buffer.insert(self.line_cursor, e.char)
+    self.line_cursor += 1
+
+  def transpose_chars(self, e): # (C-t)
+    '''Drag the character before the cursor forward over the character
+    at the cursor, moving the cursor forward as well. If the insertion
+    point is at the end of the line, then this transposes the last two
+    characters of the line. Negative arguments have no effect.'''
+    pass
+
+  def transpose_words(self, e): # (M-t)
+    '''Drag the word before point past the word after point, moving
+    point past that word as well. If the insertion point is at the end
+    of the line, this transposes the last two words on the line.'''
+    pass
+
+  def upcase_word(self, e): # (M-u)
+    '''Uppercase the current (or following) word. With a negative
+    argument, uppercase the previous word, but do not move the cursor.'''
+    pass
+
+  def downcase_word(self, e): # (M-l)
+    '''Lowercase the current (or following) word. With a negative
+    argument, lowercase the previous word, but do not move the cursor.'''
+    pass
+
+  def capitalize_word(self, e): # (M-c)
+    '''Capitalize the current (or following) word. With a negative
+    argument, capitalize the previous word, but do not move the cursor.'''
+    pass
+
+  def overwrite_mode(self, e): # ()
+    '''Toggle overwrite mode. With an explicit positive numeric
+    argument, switches to overwrite mode. With an explicit non-positive
+    numeric argument, switches to insert mode. This command affects only
+    emacs mode; vi mode does overwrite differently. Each call to
+    readline() starts in insert mode. In overwrite mode, characters
+    bound to self-insert replace the text at point rather than pushing
+    the text to the right. Characters bound to backward-delete-char
+    replace the character before point with a space.'''
+    pass
+
+  def kill_line(self, e): # (C-k)
+    '''Kill the text from point to the end of the line. '''
+    self.line_buffer[self.line_cursor:] = []
+
+  def backward_kill_line(self, e): # (C-x Rubout)
+    '''Kill backward to the beginning of the line. '''
+    self.line_buffer[:self.line_cursor] = []
+    self.line_cursor = 0
+
+  def unix_line_discard(self, e): # (C-u)
+    '''Kill backward from the cursor to the beginning of the current line. '''
+    # how is this different from backward_kill_line?
+    self.line_buffer[:self.line_cursor] = []
+    self.line_cursor = 0
+
+  def kill_whole_line(self, e): # ()
+    '''Kill all characters on the current line, no matter where point
+    is. By default, this is unbound.'''
+    pass
+
+  def kill_word(self, e): # (M-d)
+    '''Kill from point to the end of the current word, or if between
+    words, to the end of the next word. Word boundaries are the same as
+    forward-word.'''
+    begin = self.line_cursor
+    self.forward_word(e)
+    self.line_buffer[begin:self.line_cursor] = []
+    self.line_cursor = begin
+
+  def backward_kill_word(self, e): # (M-DEL)
+    '''Kill the word behind point. Word boundaries are the same as
+    backward-word. '''
+    begin = self.line_cursor
+    self.backward_word(e)
+    self.line_buffer[self.line_cursor:begin] = []
+
+  def unix_word_rubout(self, e): # (C-w)
+    '''Kill the word behind point, using white space as a word
+    boundary. The killed text is saved on the kill-ring.'''
+    begin = self.line_cursor
+    while self.line_cursor > 0:
+      self.line_cursor -= 1
+      if self.line_buffer[self.line_cursor] == ' ':
+        break
+    self.line_buffer[self.line_cursor:begin] = []
+    
+  def delete_horizontal_space(self, e): # ()
+    '''Delete all spaces and tabs around point. By default, this is unbound. '''
+    pass
+
+  def kill_region(self, e): # ()
+    '''Kill the text in the current region. By default, this command is unbound. '''
+    pass
+
+  def copy_region_as_kill(self, e): # ()
+    '''Copy the text in the region to the kill buffer, so it can be
+    yanked right away. By default, this command is unbound.'''
+    pass
+
+  def copy_backward_word(self, e): # ()
+    '''Copy the word before point to the kill buffer. The word
+    boundaries are the same as backward-word. By default, this command
+    is unbound.'''
+    pass
+
+  def copy_forward_word(self, e): # ()
+    '''Copy the word following point to the kill buffer. The word
+    boundaries are the same as forward-word. By default, this command is
+    unbound.'''
+    pass
+
+  def yank(self, e): # (C-y)
+    '''Yank the top of the kill ring into the buffer at point. '''
+    pass
+
+  def yank_pop(self, e): # (M-y)
+    '''Rotate the kill-ring, and yank the new top. You can only do this
+    if the prior command is yank or yank-pop.'''
+    pass
+
+
+  def digit_argument(self, e): # (M-0, M-1, ... M--)
+    '''Add this digit to the argument already accumulating, or start a
+    new argument. M-- starts a negative argument.'''
+    pass
+
+  def universal_argument(self, e): # ()
+    '''This is another way to specify an argument. If this command is
+    followed by one or more digits, optionally with a leading minus
+    sign, those digits define the argument. If the command is followed
+    by digits, executing universal-argument again ends the numeric
+    argument, but is otherwise ignored. As a special case, if this
+    command is immediately followed by a character that is neither a
+    digit or minus sign, the argument count for the next command is
+    multiplied by four. The argument count is initially one, so
+    executing this function the first time makes the argument count
+    four, a second time makes the argument count sixteen, and so on. By
+    default, this is not bound to a key.'''
+    pass
+
+
+  def _get_completions(self):
+    '''Return a list of possible completions for the string ending at the point.
+
+    Also set begidx and endidx in the process.'''
+    completions = []
+    self.begidx = self.line_cursor
+    self.endidx = self.line_cursor
+    if self.completer:
+      # get the string to complete
+      while self.begidx > 0:
+        self.begidx -= 1
+        if self.line_buffer[self.begidx] in self.completer_delims:
+          self.begidx += 1
+          break
+      text = ''.join(self.line_buffer[self.begidx:self.endidx])
+      log('complete text="%s"' % text)
+      i = 0
+      while 1:
+        try:
+          r = self.completer(text, i)
+        except:
+          break
+        i += 1
+        if r and r not in completions:
+          completions.append(r)
+        else:
+          break
+      log('text completions=%s' % completions)
+    if not completions:
+      # get the filename to complete
+      while self.begidx > 0:
+        self.begidx -= 1
+        if self.line_buffer[self.begidx] in ' \t\n':
+          self.begidx += 1
+          break
+      text = ''.join(self.line_buffer[self.begidx:self.endidx])
+      log('file complete text="%s"' % text)
+      completions = glob(os.path.expanduser(text) + '*')
+      if self.mark_directories == 'on':
+        mc = []
+        for f in completions:
+          if os.path.isdir(f):
+            mc.append(f + os.sep)
+          else:
+            mc.append(f)
+        completions = mc
+      log('fnames=%s' % completions)
+    return completions
+
+  def _display_completions(self, completions):
+    if not completions:
+      return
+    self.console.write('\n')
+    wmax = max(map(len, completions))
+    w, h = self.console.size()
+    cols = max(1, int((w-1) / (wmax+1)))
+    rows = int(math.ceil(float(len(completions)) / cols))
+    for row in range(rows):
+      s = ''
+      for col in range(cols):
+        i = col*rows + row
+        if i < len(completions):
+          self.console.write(completions[i].ljust(wmax+1))
+      self.console.write('\n')
+    self._print_prompt()
+    
+  def complete(self, e): # (TAB)
+    '''Attempt to perform completion on the text before point. The
+    actual completion performed is application-specific. The default is
+    filename completion.'''
+    completions = self._get_completions()
+    if completions:
+      cprefix = commonprefix(completions)
+      rep = [ c for c in cprefix ]
+      self.line_buffer[self.begidx:self.endidx] = rep
+      self.line_cursor += len(rep) - (self.endidx - self.begidx)
+      if len(completions) > 1:
+        if self.show_all_if_ambiguous == 'on':
+          self._display_completions(completions)
+        else:
+          self._bell()
+    else:
+      self._bell()
+
+  def possible_completions(self, e): # (M-?)
+    '''List the possible completions of the text before point. '''
+    completions = self._get_completions()
+    self._display_completions(completions)
+
+  def insert_completions(self, e): # (M-*)
+    '''Insert all completions of the text before point that would have
+    been generated by possible-completions.'''
+    completions = self._get_completions()
+    b = self.begidx
+    e = self.endidx
+    for comp in completions:
+      rep = [ c for c in comp ]
+      rep.append(' ')
+      self.line_buffer[b:e] = rep
+      b += len(rep)
+      e = b
+    self.line_cursor = b      
+
+  def menu_complete(self, e): # ()
+    '''Similar to complete, but replaces the word to be completed with a
+    single match from the list of possible completions. Repeated
+    execution of menu-complete steps through the list of possible
+    completions, inserting each match in turn. At the end of the list of
+    completions, the bell is rung (subject to the setting of bell-style)
+    and the original text is restored. An argument of n moves n
+    positions forward in the list of matches; a negative argument may be
+    used to move backward through the list. This command is intended to
+    be bound to TAB, but is unbound by default.'''
+    pass
+
+  def delete_char_or_list(self, e): # ()
+    '''Deletes the character under the cursor if not at the beginning or
+    end of the line (like delete-char). If at the end of the line,
+    behaves identically to possible-completions. This command is unbound
+    by default.'''
+    pass
+
+  def start_kbd_macro(self, e): # (C-x ()
+    '''Begin saving the characters typed into the current keyboard macro. '''
+    pass
+
+  def end_kbd_macro(self, e): # (C-x ))
+    '''Stop saving the characters typed into the current keyboard macro
+    and save the definition.'''
+    pass
+
+  def call_last_kbd_macro(self, e): # (C-x e)
+    '''Re-execute the last keyboard macro defined, by making the
+    characters in the macro appear as if typed at the keyboard.'''
+    pass
+
+  def re_read_init_file(self, e): # (C-x C-r)
+    '''Read in the contents of the inputrc file, and incorporate any
+    bindings or variable assignments found there.'''
+    pass
+
+  def abort(self, e): # (C-g)
+    '''Abort the current editing command and ring the terminals bell
+       (subject to the setting of bell-style).'''
+    self._bell()
+
+  def do_uppercase_version(self, e): # (M-a, M-b, M-x, ...)
+    '''If the metafied character x is lowercase, run the command that is
+    bound to the corresponding uppercase character.'''
+    pass
+
+  def prefix_meta(self, e): # (ESC)
+    '''Metafy the next character typed. This is for keyboards without a
+    meta key. Typing ESC f is equivalent to typing M-f. '''
+    self.next_meta = True
+
+  def undo(self, e): # (C-_ or C-x C-u)
+    '''Incremental undo, separately remembered for each line.'''
+    log(self.undo_stack)
+    if len(self.undo_stack) >= 2:
+      self.undo_stack.pop()
+      cursor, text = self.undo_stack.pop()
+    else:
+      cursor = 0
+      text = ''
+      self.undo_stack = []
+    self._set_line(text, cursor)
+
+  def revert_line(self, e): # (M-r)
+    '''Undo all changes made to this line. This is like executing the
+    undo command enough times to get back to the beginning.'''
+    pass
+
+  def tilde_expand(self, e): # (M-~)
+    '''Perform tilde expansion on the current word.'''
+    pass
+
+  def set_mark(self, e): # (C-@)
+    '''Set the mark to the point. If a numeric argument is supplied, the
+    mark is set to that position.'''
+    pass
+
+  def exchange_point_and_mark(self, e): # (C-x C-x)
+    '''Swap the point with the mark. The current cursor position is set
+    to the saved position, and the old cursor position is saved as the
+    mark.'''
+    pass
+
+  def character_search(self, e): # (C-])
+    '''A character is read and point is moved to the next occurrence of
+    that character. A negative count searches for previous occurrences.'''
+    pass
+
+  def character_search_backward(self, e): # (M-C-])
+    '''A character is read and point is moved to the previous occurrence
+    of that character. A negative count searches for subsequent
+    occurrences.'''
+    pass
+
+  def insert_comment(self, e): # (M-#)
+    '''Without a numeric argument, the value of the comment-begin
+    variable is inserted at the beginning of the current line. If a
+    numeric argument is supplied, this command acts as a toggle: if the
+    characters at the beginning of the line do not match the value of
+    comment-begin, the value is inserted, otherwise the characters in
+    comment-begin are deleted from the beginning of the line. In either
+    case, the line is accepted as if a newline had been typed.'''
+    pass
+
+  def dump_functions(self, e): # ()
+    '''Print all of the functions and their key bindings to the Readline
+    output stream. If a numeric argument is supplied, the output is
+    formatted in such a way that it can be made part of an inputrc
+    file. This command is unbound by default.'''
+    pass
+
+  def dump_variables(self, e): # ()
+    '''Print all of the settable variables and their values to the
+    Readline output stream. If a numeric argument is supplied, the
+    output is formatted in such a way that it can be made part of an
+    inputrc file. This command is unbound by default.'''
+    pass
+
+  def dump_macros(self, e): # ()
+    '''Print all of the Readline key sequences bound to macros and the
+    strings they output. If a numeric argument is supplied, the output
+    is formatted in such a way that it can be made part of an inputrc
+    file. This command is unbound by default.'''
+    pass
+
+  def _bind_key(self, key, func):
+    '''setup the mapping from key to call the function.'''
+    keyinfo = key_text_to_keyinfo(key)
+    self.key_dispatch[keyinfo] = func
+    
+  def emacs_editing_mode(self, e): # (C-e)
+    '''When in vi command mode, this causes a switch to emacs editing
+    mode.'''
+    # make ' ' to ~ self insert
+    for c in range(ord(' '), 127):
+      self._bind_key('"%s"' % chr(c), self.self_insert)
+    # I often accidentally hold the shift or control while typing space
+    self._bind_key('Shift-space', self.self_insert)
+    self._bind_key('Control-space', self.self_insert)
+    self._bind_key('Return', self.accept_line)
+    self._bind_key('Left', self.backward_char)
+    self._bind_key('Control-b', self.backward_char)
+    self._bind_key('Right', self.forward_char)
+    self._bind_key('Control-f', self.forward_char)
+    self._bind_key('BackSpace', self.backward_delete_char)
+    self._bind_key('Home', self.beginning_of_line)
+    self._bind_key('End', self.end_of_line)
+    self._bind_key('Delete', self.delete_char)
+    self._bind_key('Control-d', self.delete_char)
+    self._bind_key('Clear', self.clear_screen)
+    self._bind_key('Alt-f', self.forward_word)
+    self._bind_key('Alt-b', self.backward_word)
+    self._bind_key('Control-l', self.clear_screen)
+    self._bind_key('Control-p', self.previous_history)
+    self._bind_key('Up', self.history_search_backward)
+    self._bind_key('Control-n', self.next_history)
+    self._bind_key('Down', self.history_search_forward)
+    self._bind_key('Control-a', self.beginning_of_line)
+    self._bind_key('Control-e', self.end_of_line)
+    self._bind_key('Alt-<', self.beginning_of_history)
+    self._bind_key('Alt->', self.end_of_history)
+    self._bind_key('Control-r', self.reverse_search_history)
+    self._bind_key('Control-s', self.forward_search_history)
+    self._bind_key('Alt-p', self.non_incremental_reverse_search_history)
+    self._bind_key('Alt-n', self.non_incremental_forward_search_history)
+    self._bind_key('Control-z', self.undo)
+    self._bind_key('Control-_', self.undo)
+    self._bind_key('Escape', self.prefix_meta)
+    self._bind_key('Meta-d', self.kill_word)
+    self._bind_key('Meta-Delete', self.backward_kill_word)
+    self._bind_key('Control-w', self.unix_word_rubout)
+    self._bind_key('Control-v', self.quoted_insert)
+    
+    # Add keybindings for numpad
+    # first the number keys
+    self._bind_key('NUMPAD0', self.self_insert)
+    self._bind_key('NUMPAD1', self.self_insert)
+    self._bind_key('NUMPAD2', self.self_insert)
+    self._bind_key('NUMPAD3', self.self_insert)
+    self._bind_key('NUMPAD4', self.self_insert)
+    self._bind_key('NUMPAD5', self.self_insert)
+    self._bind_key('NUMPAD6', self.self_insert)
+    self._bind_key('NUMPAD7', self.self_insert)
+    self._bind_key('NUMPAD8', self.self_insert)
+    self._bind_key('NUMPAD9', self.self_insert)
+    # then the others: / * - + 
+    self._bind_key('Divide', self.self_insert)
+    self._bind_key('Multiply', self.self_insert)
+    self._bind_key('Add', self.self_insert)
+    self._bind_key('Subtract', self.self_insert)
+    # the decimal separator: '.' on US keyboards, ',' on DE one's
+    self._bind_key('VK_DECIMAL', self.self_insert)
+    
+    
+  def vi_editing_mode(self, e): # (M-C-j)
+    '''When in emacs editing mode, this causes a switch to vi editing
+    mode.'''
+    pass
+
+def CTRL(c):
+  '''make a control character'''
+  assert '@' <= c <= '_'
+  return chr(ord(c) - ord('@'))
+  
+# make it case insensitive
+def commonprefix(m):
+  "Given a list of pathnames, returns the longest common leading component"
+  if not m: return ''
+  prefix = m[0]
+  for item in m:
+    for i in range(len(prefix)):
+      if prefix[:i+1].lower() != item[:i+1].lower():
+        prefix = prefix[:i]
+        if i == 0: return ''
+        break
+  return prefix
+
+# create a Readline object to contain the state
+rl = Readline()
+
+def GetOutputFile():
+  '''Return the console object used by readline so that it can be used for printing in color.'''
+  return rl.console
+
+# make these available so this looks like the python readline module
+parse_and_bind = rl.parse_and_bind
+get_line_buffer = rl.get_line_buffer
+insert_text = rl.insert_text
+read_init_file = rl.read_init_file
+read_history_file = rl.read_history_file
+write_history_file = rl.write_history_file
+get_history_length = rl.get_history_length
+set_history_length = rl.set_history_length
+set_startup_hook = rl.set_startup_hook
+set_pre_input_hook = rl.set_pre_input_hook
+set_completer = rl.set_completer
+get_completer = rl.get_completer
+get_begidx = rl.get_begidx
+get_endidx = rl.get_endidx
+set_completer_delims = rl.set_completer_delims
+get_completer_delims = rl.get_completer_delims
+add_history = rl.add_history
+
+if __name__ == '__main__':
+  res = [ rl.readline('In[%d] ' % i) for i in range(3) ]
+  print res
+else:
+  #import wingdbstub
+  Console.install_readline(rl.readline)
+    

Property changes on: IPython\winreadline\PyReadline.py
___________________________________________________________________
Name: svn:keywords
   + Id Author Revision Date

Index: IPython/winreadline/__init__.py
===================================================================
--- IPython/winreadline/__init__.py	(revision 0)
+++ IPython/winreadline/__init__.py	(revision 0)
@@ -0,0 +1,19 @@
+from PyReadline import *
+
+__all__ = [ 'parse_and_bind',
+            'get_line_buffer',
+            'insert_text',
+            'read_init_file',
+            'read_history_file',
+            'write_history_file',
+            'get_history_length',
+            'set_history_length',
+            'set_startup_hook',
+            'set_pre_input_hook',
+            'set_completer',
+            'get_completer',
+            'get_begidx',
+            'get_endidx',
+            'set_completer_delims',
+            'get_completer_delims',
+            'add_history' ]

Property changes on: IPython\winreadline\__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id Author Revision Date

Index: IPython/winreadline/Console.py
===================================================================
--- IPython/winreadline/Console.py	(revision 0)
+++ IPython/winreadline/Console.py	(revision 0)
@@ -0,0 +1,712 @@
+'''Cursor control and color for the Windows console.
+
+This was modeled after the C extension of the same name by Fredrik Lundh. 
+'''
+
+# primitive debug printing that won't interfere with the screen
+if 0:
+  fp = open('debug.txt', 'w')
+  def log(s):
+    print >>fp, s
+    fp.flush()
+else:
+  def log(s):
+    pass
+
+import sys
+import traceback
+import re
+
+try:
+  # I developed this with ctypes 0.6
+  from ctypes import *
+  from _ctypes import call_function
+except ImportError:
+  print 'you need the ctypes module to run this code'
+  print 'http://starship.python.net/crew/theller/ctypes/'
+  raise
+
+# my code
+from keysyms import make_keysym, make_keyinfo
+
+# some constants we need
+STD_INPUT_HANDLE = -10
+STD_OUTPUT_HANDLE = -11
+ENABLE_WINDOW_INPUT = 0x0008
+ENABLE_MOUSE_INPUT = 0x0010
+ENABLE_PROCESSED_INPUT = 0x0001
+WHITE = 0x7
+BLACK = 0
+MENU_EVENT = 0x0008
+KEY_EVENT = 0x0001
+MOUSE_MOVED = 0x0001
+MOUSE_EVENT = 0x0002
+WINDOW_BUFFER_SIZE_EVENT = 0x0004
+FOCUS_EVENT = 0x0010
+MENU_EVENT = 0x0008
+VK_SHIFT = 0x10
+VK_CONTROL = 0x11
+VK_MENU = 0x12
+GENERIC_READ = int(0x80000000L)
+GENERIC_WRITE = 0x40000000
+
+# Windows structures we'll need later
+class COORD(Structure):
+  _fields_ = [("X", c_short),
+              ("Y", c_short)]
+
+class SMALL_RECT(Structure):
+  _fields_ = [("Left", c_short),
+              ("Top", c_short),
+              ("Right", c_short),
+              ("Bottom", c_short)]
+  
+class CONSOLE_SCREEN_BUFFER_INFO(Structure):
+  _fields_ = [("dwSize", COORD),
+              ("dwCursorPosition", COORD),
+              ("wAttributes", c_short),
+              ("srWindow", SMALL_RECT),
+              ("dwMaximumWindowSize", COORD)]
+
+class CHAR_UNION(Union):
+  _fields_ = [("UnicodeChar", c_short),
+              ("AsciiChar", c_char)]
+
+class CHAR_INFO(Structure):
+  _fields_ = [("Char", CHAR_UNION),
+              ("Attributes", c_short)]
+
+class KEY_EVENT_RECORD(Structure):
+  _fields_ = [("bKeyDown", c_byte),
+              ("pad2", c_byte),
+              ('pad1', c_short),
+              ("wRepeatCount", c_short),
+              ("wVirtualKeyCode", c_short),
+              ("wVirtualScanCode", c_short),
+              ("uChar", CHAR_UNION),
+              ("dwControlKeyState", c_int)]
+
+class MOUSE_EVENT_RECORD(Structure):
+  _fields_ = [("dwMousePosition", COORD),
+              ("dwButtonState", c_int),
+              ("dwControlKeyState", c_int),
+              ("dwEventFlags", c_int)]
+
+class WINDOW_BUFFER_SIZE_RECORD(Structure):
+  _fields_ = [("dwSize", COORD)]
+
+class MENU_EVENT_RECORD(Structure):
+  _fields_ = [("dwCommandId", c_uint)]
+
+class FOCUS_EVENT_RECORD(Structure):
+  _fields_ = [("bSetFocus", c_byte)]
+
+class INPUT_UNION(Union):
+  _fields_ = [("KeyEvent", KEY_EVENT_RECORD),
+              ("MouseEvent", MOUSE_EVENT_RECORD),
+              ("WindowBufferSizeEvent", WINDOW_BUFFER_SIZE_RECORD),
+              ("MenuEvent", MENU_EVENT_RECORD),
+              ("FocusEvent", FOCUS_EVENT_RECORD)]
+
+class INPUT_RECORD(Structure):
+  _fields_ = [("EventType", c_short),
+              ("Event", INPUT_UNION)]
+
+class CONSOLE_CURSOR_INFO(Structure):
+  _fields_ = [("dwSize", c_int),
+              ("bVisible", c_byte)]
+
+# I didn't want to have to individually import these so I made a list, they are
+# added to the Console class later in this file.
+
+funcs = [
+  'AllocConsole',
+  'CreateConsoleScreenBuffer',
+  'FillConsoleOutputAttribute',
+  'FillConsoleOutputCharacterA',
+  'FreeConsole',
+  'GetConsoleCursorInfo',
+  'GetConsoleMode',
+  'GetConsoleScreenBufferInfo',
+  'GetConsoleTitleA',
+  'GetProcAddress',
+  'GetStdHandle',
+  'PeekConsoleInputA',
+  'ReadConsoleInputA',
+  'ScrollConsoleScreenBufferA',
+  'SetConsoleActiveScreenBuffer',
+  'SetConsoleCursorInfo',
+  'SetConsoleCursorPosition',
+  'SetConsoleMode',
+  'SetConsoleScreenBufferSize',
+  'SetConsoleTextAttribute',
+  'SetConsoleTitleA',
+  'SetConsoleWindowInfo',
+  'WriteConsoleA',
+  'WriteConsoleOutputCharacterA',
+  ]
+
+# I don't want events for these keys, they are just a bother for my application
+key_modifiers = { VK_SHIFT:1,
+                  VK_CONTROL:1,
+                  VK_MENU:1, # alt key
+                  0x5b:1, # windows key
+                  }
+
+class Console(object):
+  '''Console driver for Windows.
+
+  '''
+  
+  def __init__(self, newbuffer=0):
+    '''Initialize the Console object.
+
+    newbuffer=1 will allocate a new buffer so the old content will be restored
+    on exit.
+    '''
+    #Do I need the following line? It causes a console to be created whenever
+    #readline is imported into a pythonw application which seems wrong. Things
+    #seem to work without it...
+    #self.AllocConsole()
+
+    if newbuffer:
+      self.hout = self.CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
+                                                 0, None, 1, None)
+      self.SetConsoleActiveScreenBuffer(self.hout)
+    else:
+      self.hout = self.GetStdHandle(STD_OUTPUT_HANDLE)
+
+    self.hin = self.GetStdHandle(STD_INPUT_HANDLE)
+    self.inmode = c_int(0)
+    self.GetConsoleMode(self.hin, byref(self.inmode))
+    self.SetConsoleMode(self.hin, 0xf)
+    info = CONSOLE_SCREEN_BUFFER_INFO()
+    self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+    self.attr = info.wAttributes # remember the initial colors
+    background = self.attr & 0xf0
+    for escape in self.escape_to_color:
+      if self.escape_to_color[escape] is not None:
+        self.escape_to_color[escape] |= background
+    log('initial attr=%x' % self.attr)
+    self.softspace = 0 # this is for using it as a file-like object
+    self.serial = 0
+
+    self.pythondll = CDLL('python%s%s' % (sys.version[0], sys.version[2]))
+    self.inputHookPtr = c_int.from_address(addressof(self.pythondll.PyOS_InputHook)).value
+    setattr(Console, 'PyMem_Malloc', self.pythondll.PyMem_Malloc)
+
+  def __del__(self):
+    '''Cleanup the console when finished.'''
+    # I don't think this ever gets called
+    self.SetConsoleTextAttribute(self.hout, self.saveattr)
+    self.SetConsoleMode(self.hin, self.inmode)
+    self.FreeConsole()
+
+  def fixcoord(self, x, y):
+    '''Return a long with x and y packed inside, also handle negative x and y.'''
+    if x < 0 or y < 0:
+      info = CONSOLE_SCREEN_BUFFER_INFO()
+      self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+      if x < 0:
+        x = info.srWindow.Right - x
+        y = info.srWindow.Bottom + y
+
+    # this is a hack! ctypes won't pass structures but COORD is just like a
+    # long, so this works.
+    return c_int(y << 16 | x)
+  
+  def pos(self, x=None, y=None):
+    '''Move or query the window cursor.'''
+    if x is None:
+      info = CONSOLE_SCREEN_BUFFER_INFO()
+      self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+      return (info.dwCursorPosition.X, info.dwCursorPosition.Y)
+    else:
+      return self.SetConsoleCursorPosition(self.hout, self.fixcoord(x, y))
+
+  def home(self):
+    '''Move to home.'''
+    self.pos(0,0)
+
+# Map ANSI color escape sequences into Windows Console Attributes
+
+  terminal_escape = re.compile('(\001?\033\\[[0-9;]+m\002?)')
+  escape_parts = re.compile('\001?\033\\[([0-9;]+)m\002?')
+  escape_to_color = { '0;30': 0x0,             #black
+                      '0;31': 0x4,             #red
+                      '0;32': 0x2,             #green
+                      '0;33': 0x4+0x2,         #brown?
+                      '0;34': 0x1,             #blue
+                      '0;35': 0x1+0x4,         #purple
+                      '0;36': 0x2+0x4,         #cyan
+                      '0;37': 0x1+0x2+0x4,     #grey
+                      '1;30': 0x1+0x2+0x4,     #dark gray
+                      '1;31': 0x4+0x8,         #red
+                      '1;32': 0x2+0x8,         #light green
+                      '1;33': 0x4+0x2+0x8,     #yellow
+                      '1;34': 0x1+0x8,         #light blue
+                      '1;35': 0x1+0x4+0x8,     #light purple
+                      '1;36': 0x1+0x2+0x8,     #light cyan
+                      '1;37': 0x1+0x2+0x4+0x8, #white
+                      '0': None,
+                      }
+
+  # This pattern should match all characters that change the cursor position differently
+  # than a normal character.
+  motion_char_re = re.compile('([\n\r\t\010\007])')
+  
+  def write_scrolling(self, text, attr=None):
+    '''write text at current cursor position while watching for scrolling.
+
+    If the window scrolls because you are at the bottom of the screen
+    buffer, all positions that you are storing will be shifted by the
+    scroll amount. For example, I remember the cursor position of the
+    prompt so that I can redraw the line but if the window scrolls,
+    the remembered position is off.
+
+    This variant of write tries to keep track of the cursor position
+    so that it will know when the screen buffer is scrolled. It
+    returns the number of lines that the buffer scrolled.
+
+    '''
+    x, y = self.pos()
+    w, h = self.size()
+    scroll = 0 # the result
+
+    # split the string into ordinary characters and funny characters
+    chunks = self.motion_char_re.split(text)
+    for chunk in chunks:
+      log('C:'+chunk)
+      n = self.write_color(chunk, attr)
+      if len(chunk) == 1: # the funny characters will be alone
+        if chunk[0] == '\n': # newline
+          x = 0
+          y += 1
+        elif chunk[0] == '\r': # carriage return
+          x = 0
+        elif chunk[0] == '\t': # tab
+          x = 8*(int(x/8)+1)
+          if x > w: # newline
+            x -= w
+            y += 1
+        elif chunk[0] == '\007': # bell
+          pass
+        elif chunk[0] == '\010':
+          x -= 1
+          if x < 0:
+            y -= 1 # backed up 1 line
+        else: # ordinary character
+          x += 1
+        if x == w: # wrap
+          x = 0
+          y += 1
+        if y == h: # scroll
+          scroll += 1
+          y = h - 1
+      else: # chunk of ordinary characters
+        x += n
+        l = int(x / w) # lines we advanced
+        x = x % w # new x value
+        y += l
+        if y >= h: # scroll
+          scroll += y - h + 1
+          y = h - 1
+    return scroll
+    
+  def write_color(self, text, attr=None):
+    '''write text at current cursor position and interpret color escapes.
+
+    return the number of characters written.
+    '''
+    log('write_color("%s", %s)' % (text, attr))
+    chunks = self.terminal_escape.split(text)
+    log('chunks=%s' % repr(chunks))
+    junk = c_int(0)
+    n = 0 # count the characters we actually write, omitting the escapes
+    for chunk in chunks:
+      m = self.escape_parts.match(chunk)
+      if m:
+        attr = self.escape_to_color[m.group(1)]
+        continue
+      n += len(chunk)
+      log('attr=%s' % attr)
+      if attr is None:
+        attr = self.attr
+      self.SetConsoleTextAttribute(self.hout, attr)
+      self.WriteConsoleA(self.hout, chunk, len(chunk), byref(junk), None)
+    return n
+
+  def write_plain(self, text, attr=None):
+    '''write text at current cursor position.'''
+    log('write("%s", %s)' %(text,attr))
+    if attr is None:
+      attr = self.attr
+    n = c_int(0)
+    self.SetConsoleTextAttribute(self.hout, attr)
+    self.WriteConsoleA(self.hout, text, len(text), byref(n), None)
+    return len(text)
+
+  # make this class look like a file object
+  def write(self, text):
+    log('write("%s")' % text)
+    return self.write_color(text)
+  
+  #write = write_scrolling
+
+  def isatty(self):
+    return True
+
+  def flush(self):
+    pass
+  
+  def page(self, attr=None, fill=' '):
+    '''Fill the entire screen.'''
+    if attr is None:
+      attr = self.attr
+    if len(fill) != 1:
+      raise ValueError
+    info = CONSOLE_SCREEN_BUFFER_INFO()
+    self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+    if info.dwCursorPosition.X != 0 or info.dwCursorPosition.Y != 0:
+      self.SetConsoleCursorPosition(self.hout, self.fixcoord(0, 0))
+
+    w = info.dwSize.X
+    n = c_int(0)
+    for y in range(info.dwSize.Y):
+      self.FillConsoleOutputAttribute(self.hout, attr, w, self.fixcoord(0, y), byref(n))
+      self.FillConsoleOutputCharacterA(self.hout, ord(fill[0]), w, self.fixcoord(0, y), byref(n))
+
+    self.attr = attr
+
+  def text(self, x, y, text, attr=None):
+    '''Write text at the given position.'''
+    if attr is None:
+      attr = self.attr
+
+    pos = self.fixcoord(x, y)
+    n = c_int(0)
+    self.WriteConsoleOutputCharacterA(self.hout, text, len(text), pos, byref(n))
+    self.FillConsoleOutputAttribute(self.hout, attr, n, pos, byref(n))
+
+  def rectangle(self, rect, attr=None, fill=' '):
+    '''Fill Rectangle.'''
+    x0, y0, x1, y1 = rect
+    n = c_int(0)
+    if attr is None:
+      attr = self.attr
+    for y in range(y0, y1):
+      pos = self.fixcoord(x0, y)
+      self.FillConsoleOutputAttribute(self.hout, attr, x1-x0, pos, byref(n))
+      self.FillConsoleOutputCharacterA(self.hout, ord(fill[0]), x1-x0, pos, byref(n))
+
+  def scroll(self, rect, dx, dy, attr=None, fill=' '):
+    '''Scroll a rectangle.'''
+    if attr is None:
+      attr = self.attr
+      
+    x0, y0, x1, y1 = rect
+    source = SMALL_RECT(x0, y0, x1-1, y1-1)
+    dest = self.fixcoord(x0+dx, y0+dy)
+    style = CHAR_INFO()
+    style.Char.AsciiChar = fill[0]
+    style.Attributes = attr
+
+    return self.ScrollConsoleScreenBufferA(self.hout, byref(source), byref(source),
+                                           dest, byref(style))
+
+  def scroll_window(self, lines):
+    '''Scroll the window by the indicated number of lines.'''
+    info = CONSOLE_SCREEN_BUFFER_INFO()
+    self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+    rect = info.srWindow
+    log('sw: rtop=%d rbot=%d' % (rect.Top, rect.Bottom))
+    top = rect.Top + lines
+    bot = rect.Bottom + lines
+    h = bot - top
+    maxbot = info.dwSize.Y-1
+    log('sw: lines=%d mb=%d top=%d bot=%d' % (lines,maxbot,top,bot))
+    if top < 0:
+      top = 0
+      bot = h
+    if bot > maxbot:
+      bot = maxbot
+      top = bot - h
+
+    nrect = SMALL_RECT()
+    nrect.Top = top
+    nrect.Bottom = bot
+    nrect.Left = rect.Left
+    nrect.Right = rect.Right
+    log('sn: top=%d bot=%d' % (top,bot))
+    r=self.SetConsoleWindowInfo(self.hout, True, byref(nrect))
+    log('r=%d' % r)
+    
+  def get(self):
+    '''Get next event from queue.'''
+    inputHookFunc = c_int.from_address(self.inputHookPtr).value
+
+    Cevent = INPUT_RECORD()
+    count = c_int(0)
+    while 1:
+      if inputHookFunc:
+        call_function(inputHookFunc, ())
+      status = self.ReadConsoleInputA(self.hin, byref(Cevent), 1, byref(count))
+      if status and count.value == 1:
+        e = event(self, Cevent)
+        return e
+
+  def getkeypress(self):
+    '''Return next key press event from the queue, ignoring others.'''
+    while 1:
+      e = self.get()
+      if e.type == 'KeyPress' and e.keycode not in key_modifiers:
+        log(e)
+        if e.keysym == 'Next':
+          self.scroll_window(12)
+        elif e.keysym == 'Prior':
+          self.scroll_window(-12)
+        else:
+          return e
+      
+  def getchar(self):
+    '''Get next character from queue.'''
+
+    Cevent = INPUT_RECORD()
+    count = c_int(0)
+    while 1:
+      status = self.ReadConsoleInputA(self.hin, byref(Cevent), 1, byref(count))
+      if (status and count.value==1 and Cevent.EventType == 1 and
+          Cevent.Event.KeyEvent.bKeyDown):
+        sym = keysym(Cevent.Event.KeyEvent.wVirtualKeyCode)
+        if len(sym) == 0:
+          sym = Cevent.Event.KeyEvent.uChar.AsciiChar
+        return sym
+
+  def peek(self):
+    '''Check event queue.'''
+    Cevent = INPUT_RECORD()
+    count = c_int(0)
+    status = PeekConsoleInput(self.hin, byref(Cevent), 1, byref(count))
+    if status and count == 1:
+      return event(self, Cevent)
+    
+  def title(self, txt=None):
+    '''Set/get title.'''
+    if txt:
+      self.SetConsoleTitleA(txt)
+    else:
+      buffer = c_buffer(200)
+      n = self.GetConsoleTitleA(buffer, 200)
+      if n > 0:
+        return buffer.value[:n]
+
+  def size(self, width=None, height=None):
+    '''Set/get window size.'''
+    info = CONSOLE_SCREEN_BUFFER_INFO()
+    status = self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+    if not status:
+      return None
+    if width is not None and height is not None:
+      wmin = info.srWindow.Right - info.srWindow.Left + 1
+      hmin = info.srWindow.Bottom - info.srWindow.Top + 1
+      #print wmin, hmin
+      width = max(width, wmin)
+      height = max(height, hmin)
+      #print width, height
+      self.SetConsoleScreenBufferSize(self.hout, self.fixcoord(width, height))
+    else:
+      return (info.dwSize.X, info.dwSize.Y)
+
+  def cursor(self, visible):
+    '''Set cursor on or off.'''
+    info = CONSOLE_CURSOR_INFO()
+    if self.GetConsoleCursorInfo(self.hout, byref(info)):
+      info.bVisible = visible
+      self.SetConsoleCursorInfo(self.hout, byref(info))
+
+  def bell(self):
+    self.write('\007')
+    
+  def next_serial(self):
+    '''Get next event serial number.'''
+    self.serial += 1
+    return self.serial
+      
+# add the functions from the dll to the class
+for func in funcs:
+  setattr(Console, func, getattr(windll.kernel32, func))
+
+class event(object):
+  '''Represent events from the console.'''
+  def __init__(self, console, input):
+    '''Initialize an event from the Windows input structure.'''
+    self.type = '??'
+    self.serial = console.next_serial()
+    self.width = 0
+    self.height = 0
+    self.x = 0
+    self.y = 0
+    self.char = ''
+    self.keycode = 0
+    self.keysym = '??'
+    self.keyinfo = '' # a tuple with (control, meta, shift, keycode) for dispatch
+    self.width = None
+
+    if input.EventType == KEY_EVENT:
+      if input.Event.KeyEvent.bKeyDown:
+        self.type = "KeyPress"
+      else:
+        self.type = "KeyRelease"
+      self.char = input.Event.KeyEvent.uChar.AsciiChar
+      self.keycode = input.Event.KeyEvent.wVirtualKeyCode
+      self.state = input.Event.KeyEvent.dwControlKeyState
+      self.keysym = make_keysym(self.keycode)
+      self.keyinfo = make_keyinfo(self.keycode, self.state)
+    elif input.EventType == MOUSE_EVENT:
+      if input.Event.MouseEvent.dwEventFlags & MOUSE_MOVED:
+        self.type = "Motion"
+      else:
+        self.type = "Button"
+      self.x = input.Event.MouseEvent.dwMousePosition.X
+      self.y = input.Event.MouseEvent.dwMousePosition.Y
+      self.state = input.Event.MouseEvent.dwButtonState
+    elif input.EventType == WINDOW_BUFFER_SIZE_EVENT:
+      self.type = "Configure"
+      self.width = input.Event.WindowBufferSizeEvent.dwSize.X
+      self.height = input.Event.WindowBufferSizeEvent.dwSize.Y
+    elif input.EventType == FOCUS_EVENT:
+      if input.Event.FocusEvent.bSetFocus:
+        self.type = "FocusIn"
+      else:
+        self.type = "FocusOut"
+    elif input.EventType == MENU_EVENT:
+      self.type = "Menu"
+      self.state = input.Event.MenuEvent.dwCommandId
+      
+  def __repr__(self):
+    '''Display an event for debugging.'''
+    if self.type in ['KeyPress', 'KeyRelease']:
+      s = "%s char='%s'%d keysym='%s' keycode=%d:%x state=%x keyinfo=%s" % \
+          (self.type, self.char, ord(self.char), self.keysym, self.keycode, self.keycode,
+           self.state, self.keyinfo)
+    elif self.type in ['Motion', 'Button']:
+      s = '%s x=%d y=%d state=%x' % (self.type, self.x, self.y, self.state)
+    elif self.type == 'Configure':
+      s = '%s w=%d h=%d' % (self.type, self.width, self.height)
+    elif self.type in ['FocusIn', 'FocusOut']:
+      s = self.type
+    elif self.type == 'Menu':
+      s = '%s state=%x' % (self.type, self.state)
+    else:
+      s = 'unknown event type'
+    return s
+
+def getconsole(buffer=1):
+    """Get a console handle.
+
+    If buffer is non-zero, a new console buffer is allocated and
+    installed.  Otherwise, this returns a handle to the current
+    console buffer"""
+
+    c = Console(buffer)
+
+    return c
+
+# The following code uses ctypes to allow a Python callable to
+# substitute for GNU readline within the Python interpreter. Calling
+# raw_input or other functions that do input, inside your callable
+# might be a bad idea, then again, it might work.
+
+# The Python callable can raise EOFError or KeyboardInterrupt and
+# these will be translated into the appropriate outputs from readline
+# so that they will then be translated back!
+
+# If the Python callable raises any other exception, a traceback will
+# be printed and readline will appear to return an empty line.
+
+# I use ctypes to create a C-callable from a Python wrapper that
+# handles the exceptions and gets the result into the right form.
+
+# the type for our C-callable wrapper
+HOOKFUNC22 = CFUNCTYPE(c_char_p, c_char_p)
+HOOKFUNC23 = CFUNCTYPE(c_char_p, c_void_p, c_void_p, c_char_p)
+
+readline_hook = None # the python hook goes here
+readline_ref = None  # this holds a reference to the c-callable to keep it alive
+
+def hook_wrapper_23(stdin, stdout, prompt):
+  '''Wrap a Python readline so it behaves like GNU readline.'''
+  try:
+    # call the Python hook
+    res = readline_hook(prompt)
+    # make sure it returned the right sort of thing
+    if res and not isinstance(res, str):
+      raise TypeError, 'readline must return a string.'
+  except KeyboardInterrupt:
+    # GNU readline returns 0 on keyboard interrupt
+    return 0
+  except EOFError:
+    # It returns an empty string on EOF
+    res = ''
+  except:
+    print >>sys.stderr, 'Readline internal error'
+    traceback.print_exc()
+    res = '\n'
+  # we have to make a copy because the caller expects to free the result
+  n = len(res)
+  p = Console.PyMem_Malloc(n+1)
+  cdll.msvcrt.strncpy(p, res, n+1)
+  return p
+  
+def hook_wrapper(prompt):
+  '''Wrap a Python readline so it behaves like GNU readline.'''
+  try:
+    # call the Python hook
+    res = readline_hook(prompt)
+    # make sure it returned the right sort of thing
+    if res and not isinstance(res, str):
+      raise TypeError, 'readline must return a string.'
+  except KeyboardInterrupt:
+    # GNU readline returns 0 on keyboard interrupt
+    return 0
+  except EOFError:
+    # It returns an empty string on EOF
+    res = ''
+  except:
+    print >>sys.stderr, 'Readline internal error'
+    traceback.print_exc()
+    res = '\n'
+  # we have to make a copy because the caller expects to free the result
+  p = cdll.msvcrt._strdup(res)
+  return p
+
+def install_readline(hook):
+  '''Set up things for the interpreter to call our function like GNU readline.'''
+  global readline_hook, readline_ref
+  # save the hook so the wrapper can call it
+  readline_hook = hook
+  # get the address of PyOS_ReadlineFunctionPointer so we can update it
+  PyOS_RFP = c_int.from_address(Console.GetProcAddress(sys.dllhandle,
+                                                       "PyOS_ReadlineFunctionPointer"))
+  # save a reference to the generated C-callable so it doesn't go away
+  if sys.version < '2.3':
+    readline_ref = HOOKFUNC22(hook_wrapper)
+  else:
+    readline_ref = HOOKFUNC23(hook_wrapper_23)
+  # get the address of the function
+  func_start = c_int.from_address(addressof(readline_ref)).value
+  # write the function address into PyOS_ReadlineFunctionPointer
+  PyOS_RFP.value = func_start
+
+if __name__ == '__main__':
+  import time, sys
+  c = Console(0)
+  sys.stdout = c
+  sys.stderr = c
+  c.page()
+  c.pos(5, 10)
+  c.write('hi there')
+  print 'some printed output'
+  for i in range(10):
+    c.getkeypress()
+  del c

Property changes on: IPython\winreadline\Console.py
___________________________________________________________________
Name: svn:keywords
   + Id Author Revision Date

Index: IPython/winreadline/keysyms.py
===================================================================
--- IPython/winreadline/keysyms.py	(revision 0)
+++ IPython/winreadline/keysyms.py	(revision 0)
@@ -0,0 +1,173 @@
+import win32con as c32
+from ctypes import windll
+
+# table for translating virtual keys to X windows key symbols
+code2sym_map = {c32.VK_CANCEL: 'Cancel',
+                c32.VK_BACK: 'BackSpace',
+                c32.VK_TAB: 'Tab',
+                c32.VK_CLEAR: 'Clear',
+                c32.VK_RETURN: 'Return',
+                c32.VK_SHIFT:'Shift_L',
+                c32.VK_CONTROL: 'Control_L',
+                c32.VK_MENU: 'Alt_L',
+                c32.VK_PAUSE: 'Pause',
+                c32.VK_CAPITAL: 'Caps_Lock',
+                c32.VK_ESCAPE: 'Escape',
+                c32.VK_SPACE: 'space',
+                c32.VK_PRIOR: 'Prior',
+                c32.VK_NEXT: 'Next',
+                c32.VK_END: 'End',
+                c32.VK_HOME: 'Home',
+                c32.VK_LEFT: 'Left',
+                c32.VK_UP: 'Up',
+                c32.VK_RIGHT: 'Right',
+                c32.VK_DOWN: 'Down',
+                c32.VK_SELECT: 'Select',
+                c32.VK_PRINT: 'Print',
+                c32.VK_EXECUTE: 'Execute',
+                c32.VK_SNAPSHOT: 'Snapshot',
+                c32.VK_INSERT: 'Insert',
+                c32.VK_DELETE: 'Delete',
+                c32.VK_HELP: 'Help',
+                c32.VK_F1: 'F1',
+                c32.VK_F2: 'F2',
+                c32.VK_F3: 'F3',
+                c32.VK_F4: 'F4',
+                c32.VK_F5: 'F5',
+                c32.VK_F6: 'F6',
+                c32.VK_F7: 'F7',
+                c32.VK_F8: 'F8',
+                c32.VK_F9: 'F9',
+                c32.VK_F10: 'F10',
+                c32.VK_F11: 'F11',
+                c32.VK_F12: 'F12',
+                c32.VK_F13: 'F13',
+                c32.VK_F14: 'F14',
+                c32.VK_F15: 'F15',
+                c32.VK_F16: 'F16',
+                c32.VK_F17: 'F17',
+                c32.VK_F18: 'F18',
+                c32.VK_F19: 'F19',
+                c32.VK_F20: 'F20',
+                c32.VK_F21: 'F21',
+                c32.VK_F22: 'F22',
+                c32.VK_F23: 'F23',
+                c32.VK_F24: 'F24',
+                c32.VK_NUMLOCK: 'Num_Lock,',
+                c32.VK_SCROLL: 'Scroll_Lock',
+                c32.VK_APPS: 'VK_APPS',
+                c32.VK_PROCESSKEY: 'VK_PROCESSKEY',
+                c32.VK_ATTN: 'VK_ATTN',
+                c32.VK_CRSEL: 'VK_CRSEL',
+                c32.VK_EXSEL: 'VK_EXSEL',
+                c32.VK_EREOF: 'VK_EREOF',
+                c32.VK_PLAY: 'VK_PLAY',
+                c32.VK_ZOOM: 'VK_ZOOM',
+                c32.VK_NONAME: 'VK_NONAME',
+                c32.VK_PA1: 'VK_PA1',
+                c32.VK_OEM_CLEAR: 'VK_OEM_CLEAR',
+                c32.VK_NUMPAD0: 'NUMPAD0',
+                c32.VK_NUMPAD1: 'NUMPAD1',
+                c32.VK_NUMPAD2: 'NUMPAD2',
+                c32.VK_NUMPAD3: 'NUMPAD3',
+                c32.VK_NUMPAD4: 'NUMPAD4',
+                c32.VK_NUMPAD5: 'NUMPAD5',
+                c32.VK_NUMPAD6: 'NUMPAD6',
+                c32.VK_NUMPAD7: 'NUMPAD7',
+                c32.VK_NUMPAD8: 'NUMPAD8',
+                c32.VK_NUMPAD9: 'NUMPAD9',
+                c32.VK_DIVIDE: 'Divide',
+                c32.VK_MULTIPLY: 'Multiply',
+                c32.VK_ADD: 'Add',
+                c32.VK_SUBTRACT: 'Subtract',
+                c32.VK_DECIMAL: 'VK_DECIMAL'
+                }
+
+# function to handle the mapping
+def make_keysym(keycode):
+  try:
+    sym = code2sym_map[keycode]
+  except KeyError:
+    sym = ''
+  return sym
+
+sym2code_map = {}
+for code,sym in code2sym_map.iteritems():
+  sym2code_map[sym.lower()] = code
+  
+def key_text_to_keyinfo(keytext):
+  '''Convert a GNU readline style textual description of a key to keycode with modifiers'''
+  if keytext.startswith('"'): # "
+    return keyseq_to_keyinfo(keytext[1:-1])
+  else:
+    return keyname_to_keyinfo(keytext)
+
+VkKeyScan = windll.user32.VkKeyScanA
+
+def char_to_keyinfo(char, control=False, meta=False, shift=False):
+  vk = VkKeyScan(ord(char))
+  if vk & 0xffff == 0xffff:
+    print 'VkKeyScan("%s") = %x' % (char, vk)
+    raise ValueError, 'bad key'
+  if vk & 0x100:
+    shift = True
+  if vk & 0x200:
+    control = True
+  if vk & 0x400:
+    meta = True
+  return (control, meta, shift, vk & 0xff)
+
+def keyname_to_keyinfo(keyname):
+  control = False
+  meta = False
+  shift = False
+
+  while 1:
+    lkeyname = keyname.lower()
+    if lkeyname.startswith('control-'):
+      control = True
+      keyname = keyname[8:]
+    elif lkeyname.startswith('meta-'):
+      meta = True
+      keyname = keyname[5:]
+    elif lkeyname.startswith('alt-'):
+      meta = True
+      keyname = keyname[4:]
+    elif lkeyname.startswith('shift-'):
+      shift = True
+      keyname = keyname[6:]
+    else:
+      if len(keyname) > 1:
+        return (control, meta, shift, sym2code_map[keyname.lower()])
+      else:
+        return char_to_keyinfo(keyname, control, meta, shift)
+  
+def keyseq_to_keyinfo(keyseq):
+  res = []
+  control = False
+  meta = False
+  shift = False
+
+  while 1:
+    if keyseq.startswith('\\C-'):
+      control = True
+      keyseq = keyseq[3:]
+    elif keyseq.startswith('\\M-'):
+      meta = True
+      keyseq = keyseq[3:]
+    elif keyseq.startswith('\\e'):
+      res.append(char_to_keyinfo('\033', control, meta, shift))
+      control = meta = shift = False
+      keyseq = keyseq[2:]
+    elif len(keyseq) >= 1:
+      res.append(char_to_keyinfo(keyseq[0], control, meta, shift))
+      control = meta = shift = False
+      keyseq = keyseq[1:]
+    else:
+      return res[0]
+
+def make_keyinfo(keycode, state):
+  control = (state & (4+8)) != 0
+  meta = (state & (1+2)) != 0
+  shift = (state & 0x10) != 0
+  return (control, meta, shift, keycode)

Property changes on: IPython\winreadline\keysyms.py
___________________________________________________________________
Name: svn:keywords
   + Id Author Revision Date

Index: IPython/winreadline/__init__.py
===================================================================
--- IPython/winreadline/__init__.py	(revision 0)
+++ IPython/winreadline/__init__.py	(revision 0)
@@ -0,0 +1,19 @@
+from PyReadline import *
+
+__all__ = [ 'parse_and_bind',
+            'get_line_buffer',
+            'insert_text',
+            'read_init_file',
+            'read_history_file',
+            'write_history_file',
+            'get_history_length',
+            'set_history_length',
+            'set_startup_hook',
+            'set_pre_input_hook',
+            'set_completer',
+            'get_completer',
+            'get_begidx',
+            'get_endidx',
+            'set_completer_delims',
+            'get_completer_delims',
+            'add_history' ]

Property changes on: IPython\winreadline\__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id Author Revision Date

Index: IPython/winreadline/Console.py
===================================================================
--- IPython/winreadline/Console.py	(revision 0)
+++ IPython/winreadline/Console.py	(revision 0)
@@ -0,0 +1,712 @@
+'''Cursor control and color for the Windows console.
+
+This was modeled after the C extension of the same name by Fredrik Lundh. 
+'''
+
+# primitive debug printing that won't interfere with the screen
+if 0:
+  fp = open('debug.txt', 'w')
+  def log(s):
+    print >>fp, s
+    fp.flush()
+else:
+  def log(s):
+    pass
+
+import sys
+import traceback
+import re
+
+try:
+  # I developed this with ctypes 0.6
+  from ctypes import *
+  from _ctypes import call_function
+except ImportError:
+  print 'you need the ctypes module to run this code'
+  print 'http://starship.python.net/crew/theller/ctypes/'
+  raise
+
+# my code
+from keysyms import make_keysym, make_keyinfo
+
+# some constants we need
+STD_INPUT_HANDLE = -10
+STD_OUTPUT_HANDLE = -11
+ENABLE_WINDOW_INPUT = 0x0008
+ENABLE_MOUSE_INPUT = 0x0010
+ENABLE_PROCESSED_INPUT = 0x0001
+WHITE = 0x7
+BLACK = 0
+MENU_EVENT = 0x0008
+KEY_EVENT = 0x0001
+MOUSE_MOVED = 0x0001
+MOUSE_EVENT = 0x0002
+WINDOW_BUFFER_SIZE_EVENT = 0x0004
+FOCUS_EVENT = 0x0010
+MENU_EVENT = 0x0008
+VK_SHIFT = 0x10
+VK_CONTROL = 0x11
+VK_MENU = 0x12
+GENERIC_READ = int(0x80000000L)
+GENERIC_WRITE = 0x40000000
+
+# Windows structures we'll need later
+class COORD(Structure):
+  _fields_ = [("X", c_short),
+              ("Y", c_short)]
+
+class SMALL_RECT(Structure):
+  _fields_ = [("Left", c_short),
+              ("Top", c_short),
+              ("Right", c_short),
+              ("Bottom", c_short)]
+  
+class CONSOLE_SCREEN_BUFFER_INFO(Structure):
+  _fields_ = [("dwSize", COORD),
+              ("dwCursorPosition", COORD),
+              ("wAttributes", c_short),
+              ("srWindow", SMALL_RECT),
+              ("dwMaximumWindowSize", COORD)]
+
+class CHAR_UNION(Union):
+  _fields_ = [("UnicodeChar", c_short),
+              ("AsciiChar", c_char)]
+
+class CHAR_INFO(Structure):
+  _fields_ = [("Char", CHAR_UNION),
+              ("Attributes", c_short)]
+
+class KEY_EVENT_RECORD(Structure):
+  _fields_ = [("bKeyDown", c_byte),
+              ("pad2", c_byte),
+              ('pad1', c_short),
+              ("wRepeatCount", c_short),
+              ("wVirtualKeyCode", c_short),
+              ("wVirtualScanCode", c_short),
+              ("uChar", CHAR_UNION),
+              ("dwControlKeyState", c_int)]
+
+class MOUSE_EVENT_RECORD(Structure):
+  _fields_ = [("dwMousePosition", COORD),
+              ("dwButtonState", c_int),
+              ("dwControlKeyState", c_int),
+              ("dwEventFlags", c_int)]
+
+class WINDOW_BUFFER_SIZE_RECORD(Structure):
+  _fields_ = [("dwSize", COORD)]
+
+class MENU_EVENT_RECORD(Structure):
+  _fields_ = [("dwCommandId", c_uint)]
+
+class FOCUS_EVENT_RECORD(Structure):
+  _fields_ = [("bSetFocus", c_byte)]
+
+class INPUT_UNION(Union):
+  _fields_ = [("KeyEvent", KEY_EVENT_RECORD),
+              ("MouseEvent", MOUSE_EVENT_RECORD),
+              ("WindowBufferSizeEvent", WINDOW_BUFFER_SIZE_RECORD),
+              ("MenuEvent", MENU_EVENT_RECORD),
+              ("FocusEvent", FOCUS_EVENT_RECORD)]
+
+class INPUT_RECORD(Structure):
+  _fields_ = [("EventType", c_short),
+              ("Event", INPUT_UNION)]
+
+class CONSOLE_CURSOR_INFO(Structure):
+  _fields_ = [("dwSize", c_int),
+              ("bVisible", c_byte)]
+
+# I didn't want to have to individually import these so I made a list, they are
+# added to the Console class later in this file.
+
+funcs = [
+  'AllocConsole',
+  'CreateConsoleScreenBuffer',
+  'FillConsoleOutputAttribute',
+  'FillConsoleOutputCharacterA',
+  'FreeConsole',
+  'GetConsoleCursorInfo',
+  'GetConsoleMode',
+  'GetConsoleScreenBufferInfo',
+  'GetConsoleTitleA',
+  'GetProcAddress',
+  'GetStdHandle',
+  'PeekConsoleInputA',
+  'ReadConsoleInputA',
+  'ScrollConsoleScreenBufferA',
+  'SetConsoleActiveScreenBuffer',
+  'SetConsoleCursorInfo',
+  'SetConsoleCursorPosition',
+  'SetConsoleMode',
+  'SetConsoleScreenBufferSize',
+  'SetConsoleTextAttribute',
+  'SetConsoleTitleA',
+  'SetConsoleWindowInfo',
+  'WriteConsoleA',
+  'WriteConsoleOutputCharacterA',
+  ]
+
+# I don't want events for these keys, they are just a bother for my application
+key_modifiers = { VK_SHIFT:1,
+                  VK_CONTROL:1,
+                  VK_MENU:1, # alt key
+                  0x5b:1, # windows key
+                  }
+
+class Console(object):
+  '''Console driver for Windows.
+
+  '''
+  
+  def __init__(self, newbuffer=0):
+    '''Initialize the Console object.
+
+    newbuffer=1 will allocate a new buffer so the old content will be restored
+    on exit.
+    '''
+    #Do I need the following line? It causes a console to be created whenever
+    #readline is imported into a pythonw application which seems wrong. Things
+    #seem to work without it...
+    #self.AllocConsole()
+
+    if newbuffer:
+      self.hout = self.CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
+                                                 0, None, 1, None)
+      self.SetConsoleActiveScreenBuffer(self.hout)
+    else:
+      self.hout = self.GetStdHandle(STD_OUTPUT_HANDLE)
+
+    self.hin = self.GetStdHandle(STD_INPUT_HANDLE)
+    self.inmode = c_int(0)
+    self.GetConsoleMode(self.hin, byref(self.inmode))
+    self.SetConsoleMode(self.hin, 0xf)
+    info = CONSOLE_SCREEN_BUFFER_INFO()
+    self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+    self.attr = info.wAttributes # remember the initial colors
+    background = self.attr & 0xf0
+    for escape in self.escape_to_color:
+      if self.escape_to_color[escape] is not None:
+        self.escape_to_color[escape] |= background
+    log('initial attr=%x' % self.attr)
+    self.softspace = 0 # this is for using it as a file-like object
+    self.serial = 0
+
+    self.pythondll = CDLL('python%s%s' % (sys.version[0], sys.version[2]))
+    self.inputHookPtr = c_int.from_address(addressof(self.pythondll.PyOS_InputHook)).value
+    setattr(Console, 'PyMem_Malloc', self.pythondll.PyMem_Malloc)
+
+  def __del__(self):
+    '''Cleanup the console when finished.'''
+    # I don't think this ever gets called
+    self.SetConsoleTextAttribute(self.hout, self.saveattr)
+    self.SetConsoleMode(self.hin, self.inmode)
+    self.FreeConsole()
+
+  def fixcoord(self, x, y):
+    '''Return a long with x and y packed inside, also handle negative x and y.'''
+    if x < 0 or y < 0:
+      info = CONSOLE_SCREEN_BUFFER_INFO()
+      self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+      if x < 0:
+        x = info.srWindow.Right - x
+        y = info.srWindow.Bottom + y
+
+    # this is a hack! ctypes won't pass structures but COORD is just like a
+    # long, so this works.
+    return c_int(y << 16 | x)
+  
+  def pos(self, x=None, y=None):
+    '''Move or query the window cursor.'''
+    if x is None:
+      info = CONSOLE_SCREEN_BUFFER_INFO()
+      self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+      return (info.dwCursorPosition.X, info.dwCursorPosition.Y)
+    else:
+      return self.SetConsoleCursorPosition(self.hout, self.fixcoord(x, y))
+
+  def home(self):
+    '''Move to home.'''
+    self.pos(0,0)
+
+# Map ANSI color escape sequences into Windows Console Attributes
+
+  terminal_escape = re.compile('(\001?\033\\[[0-9;]+m\002?)')
+  escape_parts = re.compile('\001?\033\\[([0-9;]+)m\002?')
+  escape_to_color = { '0;30': 0x0,             #black
+                      '0;31': 0x4,             #red
+                      '0;32': 0x2,             #green
+                      '0;33': 0x4+0x2,         #brown?
+                      '0;34': 0x1,             #blue
+                      '0;35': 0x1+0x4,         #purple
+                      '0;36': 0x2+0x4,         #cyan
+                      '0;37': 0x1+0x2+0x4,     #grey
+                      '1;30': 0x1+0x2+0x4,     #dark gray
+                      '1;31': 0x4+0x8,         #red
+                      '1;32': 0x2+0x8,         #light green
+                      '1;33': 0x4+0x2+0x8,     #yellow
+                      '1;34': 0x1+0x8,         #light blue
+                      '1;35': 0x1+0x4+0x8,     #light purple
+                      '1;36': 0x1+0x2+0x8,     #light cyan
+                      '1;37': 0x1+0x2+0x4+0x8, #white
+                      '0': None,
+                      }
+
+  # This pattern should match all characters that change the cursor position differently
+  # than a normal character.
+  motion_char_re = re.compile('([\n\r\t\010\007])')
+  
+  def write_scrolling(self, text, attr=None):
+    '''write text at current cursor position while watching for scrolling.
+
+    If the window scrolls because you are at the bottom of the screen
+    buffer, all positions that you are storing will be shifted by the
+    scroll amount. For example, I remember the cursor position of the
+    prompt so that I can redraw the line but if the window scrolls,
+    the remembered position is off.
+
+    This variant of write tries to keep track of the cursor position
+    so that it will know when the screen buffer is scrolled. It
+    returns the number of lines that the buffer scrolled.
+
+    '''
+    x, y = self.pos()
+    w, h = self.size()
+    scroll = 0 # the result
+
+    # split the string into ordinary characters and funny characters
+    chunks = self.motion_char_re.split(text)
+    for chunk in chunks:
+      log('C:'+chunk)
+      n = self.write_color(chunk, attr)
+      if len(chunk) == 1: # the funny characters will be alone
+        if chunk[0] == '\n': # newline
+          x = 0
+          y += 1
+        elif chunk[0] == '\r': # carriage return
+          x = 0
+        elif chunk[0] == '\t': # tab
+          x = 8*(int(x/8)+1)
+          if x > w: # newline
+            x -= w
+            y += 1
+        elif chunk[0] == '\007': # bell
+          pass
+        elif chunk[0] == '\010':
+          x -= 1
+          if x < 0:
+            y -= 1 # backed up 1 line
+        else: # ordinary character
+          x += 1
+        if x == w: # wrap
+          x = 0
+          y += 1
+        if y == h: # scroll
+          scroll += 1
+          y = h - 1
+      else: # chunk of ordinary characters
+        x += n
+        l = int(x / w) # lines we advanced
+        x = x % w # new x value
+        y += l
+        if y >= h: # scroll
+          scroll += y - h + 1
+          y = h - 1
+    return scroll
+    
+  def write_color(self, text, attr=None):
+    '''write text at current cursor position and interpret color escapes.
+
+    return the number of characters written.
+    '''
+    log('write_color("%s", %s)' % (text, attr))
+    chunks = self.terminal_escape.split(text)
+    log('chunks=%s' % repr(chunks))
+    junk = c_int(0)
+    n = 0 # count the characters we actually write, omitting the escapes
+    for chunk in chunks:
+      m = self.escape_parts.match(chunk)
+      if m:
+        attr = self.escape_to_color[m.group(1)]
+        continue
+      n += len(chunk)
+      log('attr=%s' % attr)
+      if attr is None:
+        attr = self.attr
+      self.SetConsoleTextAttribute(self.hout, attr)
+      self.WriteConsoleA(self.hout, chunk, len(chunk), byref(junk), None)
+    return n
+
+  def write_plain(self, text, attr=None):
+    '''write text at current cursor position.'''
+    log('write("%s", %s)' %(text,attr))
+    if attr is None:
+      attr = self.attr
+    n = c_int(0)
+    self.SetConsoleTextAttribute(self.hout, attr)
+    self.WriteConsoleA(self.hout, text, len(text), byref(n), None)
+    return len(text)
+
+  # make this class look like a file object
+  def write(self, text):
+    log('write("%s")' % text)
+    return self.write_color(text)
+  
+  #write = write_scrolling
+
+  def isatty(self):
+    return True
+
+  def flush(self):
+    pass
+  
+  def page(self, attr=None, fill=' '):
+    '''Fill the entire screen.'''
+    if attr is None:
+      attr = self.attr
+    if len(fill) != 1:
+      raise ValueError
+    info = CONSOLE_SCREEN_BUFFER_INFO()
+    self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+    if info.dwCursorPosition.X != 0 or info.dwCursorPosition.Y != 0:
+      self.SetConsoleCursorPosition(self.hout, self.fixcoord(0, 0))
+
+    w = info.dwSize.X
+    n = c_int(0)
+    for y in range(info.dwSize.Y):
+      self.FillConsoleOutputAttribute(self.hout, attr, w, self.fixcoord(0, y), byref(n))
+      self.FillConsoleOutputCharacterA(self.hout, ord(fill[0]), w, self.fixcoord(0, y), byref(n))
+
+    self.attr = attr
+
+  def text(self, x, y, text, attr=None):
+    '''Write text at the given position.'''
+    if attr is None:
+      attr = self.attr
+
+    pos = self.fixcoord(x, y)
+    n = c_int(0)
+    self.WriteConsoleOutputCharacterA(self.hout, text, len(text), pos, byref(n))
+    self.FillConsoleOutputAttribute(self.hout, attr, n, pos, byref(n))
+
+  def rectangle(self, rect, attr=None, fill=' '):
+    '''Fill Rectangle.'''
+    x0, y0, x1, y1 = rect
+    n = c_int(0)
+    if attr is None:
+      attr = self.attr
+    for y in range(y0, y1):
+      pos = self.fixcoord(x0, y)
+      self.FillConsoleOutputAttribute(self.hout, attr, x1-x0, pos, byref(n))
+      self.FillConsoleOutputCharacterA(self.hout, ord(fill[0]), x1-x0, pos, byref(n))
+
+  def scroll(self, rect, dx, dy, attr=None, fill=' '):
+    '''Scroll a rectangle.'''
+    if attr is None:
+      attr = self.attr
+      
+    x0, y0, x1, y1 = rect
+    source = SMALL_RECT(x0, y0, x1-1, y1-1)
+    dest = self.fixcoord(x0+dx, y0+dy)
+    style = CHAR_INFO()
+    style.Char.AsciiChar = fill[0]
+    style.Attributes = attr
+
+    return self.ScrollConsoleScreenBufferA(self.hout, byref(source), byref(source),
+                                           dest, byref(style))
+
+  def scroll_window(self, lines):
+    '''Scroll the window by the indicated number of lines.'''
+    info = CONSOLE_SCREEN_BUFFER_INFO()
+    self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+    rect = info.srWindow
+    log('sw: rtop=%d rbot=%d' % (rect.Top, rect.Bottom))
+    top = rect.Top + lines
+    bot = rect.Bottom + lines
+    h = bot - top
+    maxbot = info.dwSize.Y-1
+    log('sw: lines=%d mb=%d top=%d bot=%d' % (lines,maxbot,top,bot))
+    if top < 0:
+      top = 0
+      bot = h
+    if bot > maxbot:
+      bot = maxbot
+      top = bot - h
+
+    nrect = SMALL_RECT()
+    nrect.Top = top
+    nrect.Bottom = bot
+    nrect.Left = rect.Left
+    nrect.Right = rect.Right
+    log('sn: top=%d bot=%d' % (top,bot))
+    r=self.SetConsoleWindowInfo(self.hout, True, byref(nrect))
+    log('r=%d' % r)
+    
+  def get(self):
+    '''Get next event from queue.'''
+    inputHookFunc = c_int.from_address(self.inputHookPtr).value
+
+    Cevent = INPUT_RECORD()
+    count = c_int(0)
+    while 1:
+      if inputHookFunc:
+        call_function(inputHookFunc, ())
+      status = self.ReadConsoleInputA(self.hin, byref(Cevent), 1, byref(count))
+      if status and count.value == 1:
+        e = event(self, Cevent)
+        return e
+
+  def getkeypress(self):
+    '''Return next key press event from the queue, ignoring others.'''
+    while 1:
+      e = self.get()
+      if e.type == 'KeyPress' and e.keycode not in key_modifiers:
+        log(e)
+        if e.keysym == 'Next':
+          self.scroll_window(12)
+        elif e.keysym == 'Prior':
+          self.scroll_window(-12)
+        else:
+          return e
+      
+  def getchar(self):
+    '''Get next character from queue.'''
+
+    Cevent = INPUT_RECORD()
+    count = c_int(0)
+    while 1:
+      status = self.ReadConsoleInputA(self.hin, byref(Cevent), 1, byref(count))
+      if (status and count.value==1 and Cevent.EventType == 1 and
+          Cevent.Event.KeyEvent.bKeyDown):
+        sym = keysym(Cevent.Event.KeyEvent.wVirtualKeyCode)
+        if len(sym) == 0:
+          sym = Cevent.Event.KeyEvent.uChar.AsciiChar
+        return sym
+
+  def peek(self):
+    '''Check event queue.'''
+    Cevent = INPUT_RECORD()
+    count = c_int(0)
+    status = PeekConsoleInput(self.hin, byref(Cevent), 1, byref(count))
+    if status and count == 1:
+      return event(self, Cevent)
+    
+  def title(self, txt=None):
+    '''Set/get title.'''
+    if txt:
+      self.SetConsoleTitleA(txt)
+    else:
+      buffer = c_buffer(200)
+      n = self.GetConsoleTitleA(buffer, 200)
+      if n > 0:
+        return buffer.value[:n]
+
+  def size(self, width=None, height=None):
+    '''Set/get window size.'''
+    info = CONSOLE_SCREEN_BUFFER_INFO()
+    status = self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+    if not status:
+      return None
+    if width is not None and height is not None:
+      wmin = info.srWindow.Right - info.srWindow.Left + 1
+      hmin = info.srWindow.Bottom - info.srWindow.Top + 1
+      #print wmin, hmin
+      width = max(width, wmin)
+      height = max(height, hmin)
+      #print width, height
+      self.SetConsoleScreenBufferSize(self.hout, self.fixcoord(width, height))
+    else:
+      return (info.dwSize.X, info.dwSize.Y)
+
+  def cursor(self, visible):
+    '''Set cursor on or off.'''
+    info = CONSOLE_CURSOR_INFO()
+    if self.GetConsoleCursorInfo(self.hout, byref(info)):
+      info.bVisible = visible
+      self.SetConsoleCursorInfo(self.hout, byref(info))
+
+  def bell(self):
+    self.write('\007')
+    
+  def next_serial(self):
+    '''Get next event serial number.'''
+    self.serial += 1
+    return self.serial
+      
+# add the functions from the dll to the class
+for func in funcs:
+  setattr(Console, func, getattr(windll.kernel32, func))
+
+class event(object):
+  '''Represent events from the console.'''
+  def __init__(self, console, input):
+    '''Initialize an event from the Windows input structure.'''
+    self.type = '??'
+    self.serial = console.next_serial()
+    self.width = 0
+    self.height = 0
+    self.x = 0
+    self.y = 0
+    self.char = ''
+    self.keycode = 0
+    self.keysym = '??'
+    self.keyinfo = '' # a tuple with (control, meta, shift, keycode) for dispatch
+    self.width = None
+
+    if input.EventType == KEY_EVENT:
+      if input.Event.KeyEvent.bKeyDown:
+        self.type = "KeyPress"
+      else:
+        self.type = "KeyRelease"
+      self.char = input.Event.KeyEvent.uChar.AsciiChar
+      self.keycode = input.Event.KeyEvent.wVirtualKeyCode
+      self.state = input.Event.KeyEvent.dwControlKeyState
+      self.keysym = make_keysym(self.keycode)
+      self.keyinfo = make_keyinfo(self.keycode, self.state)
+    elif input.EventType == MOUSE_EVENT:
+      if input.Event.MouseEvent.dwEventFlags & MOUSE_MOVED:
+        self.type = "Motion"
+      else:
+        self.type = "Button"
+      self.x = input.Event.MouseEvent.dwMousePosition.X
+      self.y = input.Event.MouseEvent.dwMousePosition.Y
+      self.state = input.Event.MouseEvent.dwButtonState
+    elif input.EventType == WINDOW_BUFFER_SIZE_EVENT:
+      self.type = "Configure"
+      self.width = input.Event.WindowBufferSizeEvent.dwSize.X
+      self.height = input.Event.WindowBufferSizeEvent.dwSize.Y
+    elif input.EventType == FOCUS_EVENT:
+      if input.Event.FocusEvent.bSetFocus:
+        self.type = "FocusIn"
+      else:
+        self.type = "FocusOut"
+    elif input.EventType == MENU_EVENT:
+      self.type = "Menu"
+      self.state = input.Event.MenuEvent.dwCommandId
+      
+  def __repr__(self):
+    '''Display an event for debugging.'''
+    if self.type in ['KeyPress', 'KeyRelease']:
+      s = "%s char='%s'%d keysym='%s' keycode=%d:%x state=%x keyinfo=%s" % \
+          (self.type, self.char, ord(self.char), self.keysym, self.keycode, self.keycode,
+           self.state, self.keyinfo)
+    elif self.type in ['Motion', 'Button']:
+      s = '%s x=%d y=%d state=%x' % (self.type, self.x, self.y, self.state)
+    elif self.type == 'Configure':
+      s = '%s w=%d h=%d' % (self.type, self.width, self.height)
+    elif self.type in ['FocusIn', 'FocusOut']:
+      s = self.type
+    elif self.type == 'Menu':
+      s = '%s state=%x' % (self.type, self.state)
+    else:
+      s = 'unknown event type'
+    return s
+
+def getconsole(buffer=1):
+    """Get a console handle.
+
+    If buffer is non-zero, a new console buffer is allocated and
+    installed.  Otherwise, this returns a handle to the current
+    console buffer"""
+
+    c = Console(buffer)
+
+    return c
+
+# The following code uses ctypes to allow a Python callable to
+# substitute for GNU readline within the Python interpreter. Calling
+# raw_input or other functions that do input, inside your callable
+# might be a bad idea, then again, it might work.
+
+# The Python callable can raise EOFError or KeyboardInterrupt and
+# these will be translated into the appropriate outputs from readline
+# so that they will then be translated back!
+
+# If the Python callable raises any other exception, a traceback will
+# be printed and readline will appear to return an empty line.
+
+# I use ctypes to create a C-callable from a Python wrapper that
+# handles the exceptions and gets the result into the right form.
+
+# the type for our C-callable wrapper
+HOOKFUNC22 = CFUNCTYPE(c_char_p, c_char_p)
+HOOKFUNC23 = CFUNCTYPE(c_char_p, c_void_p, c_void_p, c_char_p)
+
+readline_hook = None # the python hook goes here
+readline_ref = None  # this holds a reference to the c-callable to keep it alive
+
+def hook_wrapper_23(stdin, stdout, prompt):
+  '''Wrap a Python readline so it behaves like GNU readline.'''
+  try:
+    # call the Python hook
+    res = readline_hook(prompt)
+    # make sure it returned the right sort of thing
+    if res and not isinstance(res, str):
+      raise TypeError, 'readline must return a string.'
+  except KeyboardInterrupt:
+    # GNU readline returns 0 on keyboard interrupt
+    return 0
+  except EOFError:
+    # It returns an empty string on EOF
+    res = ''
+  except:
+    print >>sys.stderr, 'Readline internal error'
+    traceback.print_exc()
+    res = '\n'
+  # we have to make a copy because the caller expects to free the result
+  n = len(res)
+  p = Console.PyMem_Malloc(n+1)
+  cdll.msvcrt.strncpy(p, res, n+1)
+  return p
+  
+def hook_wrapper(prompt):
+  '''Wrap a Python readline so it behaves like GNU readline.'''
+  try:
+    # call the Python hook
+    res = readline_hook(prompt)
+    # make sure it returned the right sort of thing
+    if res and not isinstance(res, str):
+      raise TypeError, 'readline must return a string.'
+  except KeyboardInterrupt:
+    # GNU readline returns 0 on keyboard interrupt
+    return 0
+  except EOFError:
+    # It returns an empty string on EOF
+    res = ''
+  except:
+    print >>sys.stderr, 'Readline internal error'
+    traceback.print_exc()
+    res = '\n'
+  # we have to make a copy because the caller expects to free the result
+  p = cdll.msvcrt._strdup(res)
+  return p
+
+def install_readline(hook):
+  '''Set up things for the interpreter to call our function like GNU readline.'''
+  global readline_hook, readline_ref
+  # save the hook so the wrapper can call it
+  readline_hook = hook
+  # get the address of PyOS_ReadlineFunctionPointer so we can update it
+  PyOS_RFP = c_int.from_address(Console.GetProcAddress(sys.dllhandle,
+                                                       "PyOS_ReadlineFunctionPointer"))
+  # save a reference to the generated C-callable so it doesn't go away
+  if sys.version < '2.3':
+    readline_ref = HOOKFUNC22(hook_wrapper)
+  else:
+    readline_ref = HOOKFUNC23(hook_wrapper_23)
+  # get the address of the function
+  func_start = c_int.from_address(addressof(readline_ref)).value
+  # write the function address into PyOS_ReadlineFunctionPointer
+  PyOS_RFP.value = func_start
+
+if __name__ == '__main__':
+  import time, sys
+  c = Console(0)
+  sys.stdout = c
+  sys.stderr = c
+  c.page()
+  c.pos(5, 10)
+  c.write('hi there')
+  print 'some printed output'
+  for i in range(10):
+    c.getkeypress()
+  del c

Property changes on: IPython\winreadline\Console.py
___________________________________________________________________
Name: svn:keywords
   + Id Author Revision Date

Index: IPython/winreadline/keysyms.py
===================================================================
--- IPython/winreadline/keysyms.py	(revision 0)
+++ IPython/winreadline/keysyms.py	(revision 0)
@@ -0,0 +1,173 @@
+import win32con as c32
+from ctypes import windll
+
+# table for translating virtual keys to X windows key symbols
+code2sym_map = {c32.VK_CANCEL: 'Cancel',
+                c32.VK_BACK: 'BackSpace',
+                c32.VK_TAB: 'Tab',
+                c32.VK_CLEAR: 'Clear',
+                c32.VK_RETURN: 'Return',
+                c32.VK_SHIFT:'Shift_L',
+                c32.VK_CONTROL: 'Control_L',
+                c32.VK_MENU: 'Alt_L',
+                c32.VK_PAUSE: 'Pause',
+                c32.VK_CAPITAL: 'Caps_Lock',
+                c32.VK_ESCAPE: 'Escape',
+                c32.VK_SPACE: 'space',
+                c32.VK_PRIOR: 'Prior',
+                c32.VK_NEXT: 'Next',
+                c32.VK_END: 'End',
+                c32.VK_HOME: 'Home',
+                c32.VK_LEFT: 'Left',
+                c32.VK_UP: 'Up',
+                c32.VK_RIGHT: 'Right',
+                c32.VK_DOWN: 'Down',
+                c32.VK_SELECT: 'Select',
+                c32.VK_PRINT: 'Print',
+                c32.VK_EXECUTE: 'Execute',
+                c32.VK_SNAPSHOT: 'Snapshot',
+                c32.VK_INSERT: 'Insert',
+                c32.VK_DELETE: 'Delete',
+                c32.VK_HELP: 'Help',
+                c32.VK_F1: 'F1',
+                c32.VK_F2: 'F2',
+                c32.VK_F3: 'F3',
+                c32.VK_F4: 'F4',
+                c32.VK_F5: 'F5',
+                c32.VK_F6: 'F6',
+                c32.VK_F7: 'F7',
+                c32.VK_F8: 'F8',
+                c32.VK_F9: 'F9',
+                c32.VK_F10: 'F10',
+                c32.VK_F11: 'F11',
+                c32.VK_F12: 'F12',
+                c32.VK_F13: 'F13',
+                c32.VK_F14: 'F14',
+                c32.VK_F15: 'F15',
+                c32.VK_F16: 'F16',
+                c32.VK_F17: 'F17',
+                c32.VK_F18: 'F18',
+                c32.VK_F19: 'F19',
+                c32.VK_F20: 'F20',
+                c32.VK_F21: 'F21',
+                c32.VK_F22: 'F22',
+                c32.VK_F23: 'F23',
+                c32.VK_F24: 'F24',
+                c32.VK_NUMLOCK: 'Num_Lock,',
+                c32.VK_SCROLL: 'Scroll_Lock',
+                c32.VK_APPS: 'VK_APPS',
+                c32.VK_PROCESSKEY: 'VK_PROCESSKEY',
+                c32.VK_ATTN: 'VK_ATTN',
+                c32.VK_CRSEL: 'VK_CRSEL',
+                c32.VK_EXSEL: 'VK_EXSEL',
+                c32.VK_EREOF: 'VK_EREOF',
+                c32.VK_PLAY: 'VK_PLAY',
+                c32.VK_ZOOM: 'VK_ZOOM',
+                c32.VK_NONAME: 'VK_NONAME',
+                c32.VK_PA1: 'VK_PA1',
+                c32.VK_OEM_CLEAR: 'VK_OEM_CLEAR',
+                c32.VK_NUMPAD0: 'NUMPAD0',
+                c32.VK_NUMPAD1: 'NUMPAD1',
+                c32.VK_NUMPAD2: 'NUMPAD2',
+                c32.VK_NUMPAD3: 'NUMPAD3',
+                c32.VK_NUMPAD4: 'NUMPAD4',
+                c32.VK_NUMPAD5: 'NUMPAD5',
+                c32.VK_NUMPAD6: 'NUMPAD6',
+                c32.VK_NUMPAD7: 'NUMPAD7',
+                c32.VK_NUMPAD8: 'NUMPAD8',
+                c32.VK_NUMPAD9: 'NUMPAD9',
+                c32.VK_DIVIDE: 'Divide',
+                c32.VK_MULTIPLY: 'Multiply',
+                c32.VK_ADD: 'Add',
+                c32.VK_SUBTRACT: 'Subtract',
+                c32.VK_DECIMAL: 'VK_DECIMAL'
+                }
+
+# function to handle the mapping
+def make_keysym(keycode):
+  try:
+    sym = code2sym_map[keycode]
+  except KeyError:
+    sym = ''
+  return sym
+
+sym2code_map = {}
+for code,sym in code2sym_map.iteritems():
+  sym2code_map[sym.lower()] = code
+  
+def key_text_to_keyinfo(keytext):
+  '''Convert a GNU readline style textual description of a key to keycode with modifiers'''
+  if keytext.startswith('"'): # "
+    return keyseq_to_keyinfo(keytext[1:-1])
+  else:
+    return keyname_to_keyinfo(keytext)
+
+VkKeyScan = windll.user32.VkKeyScanA
+
+def char_to_keyinfo(char, control=False, meta=False, shift=False):
+  vk = VkKeyScan(ord(char))
+  if vk & 0xffff == 0xffff:
+    print 'VkKeyScan("%s") = %x' % (char, vk)
+    raise ValueError, 'bad key'
+  if vk & 0x100:
+    shift = True
+  if vk & 0x200:
+    control = True
+  if vk & 0x400:
+    meta = True
+  return (control, meta, shift, vk & 0xff)
+
+def keyname_to_keyinfo(keyname):
+  control = False
+  meta = False
+  shift = False
+
+  while 1:
+    lkeyname = keyname.lower()
+    if lkeyname.startswith('control-'):
+      control = True
+      keyname = keyname[8:]
+    elif lkeyname.startswith('meta-'):
+      meta = True
+      keyname = keyname[5:]
+    elif lkeyname.startswith('alt-'):
+      meta = True
+      keyname = keyname[4:]
+    elif lkeyname.startswith('shift-'):
+      shift = True
+      keyname = keyname[6:]
+    else:
+      if len(keyname) > 1:
+        return (control, meta, shift, sym2code_map[keyname.lower()])
+      else:
+        return char_to_keyinfo(keyname, control, meta, shift)
+  
+def keyseq_to_keyinfo(keyseq):
+  res = []
+  control = False
+  meta = False
+  shift = False
+
+  while 1:
+    if keyseq.startswith('\\C-'):
+      control = True
+      keyseq = keyseq[3:]
+    elif keyseq.startswith('\\M-'):
+      meta = True
+      keyseq = keyseq[3:]
+    elif keyseq.startswith('\\e'):
+      res.append(char_to_keyinfo('\033', control, meta, shift))
+      control = meta = shift = False
+      keyseq = keyseq[2:]
+    elif len(keyseq) >= 1:
+      res.append(char_to_keyinfo(keyseq[0], control, meta, shift))
+      control = meta = shift = False
+      keyseq = keyseq[1:]
+    else:
+      return res[0]
+
+def make_keyinfo(keycode, state):
+  control = (state & (4+8)) != 0
+  meta = (state & (1+2)) != 0
+  shift = (state & 0x10) != 0
+  return (control, meta, shift, keycode)

Property changes on: IPython\winreadline\keysyms.py
___________________________________________________________________
Name: svn:keywords
   + Id Author Revision Date

Index: IPython/winreadline/PyReadline.py
===================================================================
--- IPython/winreadline/PyReadline.py	(revision 0)
+++ IPython/winreadline/PyReadline.py	(revision 0)
@@ -0,0 +1,1122 @@
+''' an attempt to implement readline for Python in Python using ctypes'''
+
+import string
+import math
+import sys
+from glob import glob
+import os
+import re
+import traceback
+import operator
+
+import win32con as c32
+
+import Console
+from Console import log
+from keysyms import key_text_to_keyinfo
+
+def quote_char(c):
+  if ' ' <= c <= '~':
+    return c
+  else:
+    return repr(c)[1:-1]
+  
+class Readline:
+  def __init__(self):
+    self.startup_hook = None
+    self.pre_input_hook = None
+    self.completer = None
+    self.completer_delims = " \t\n\"\\'`@$><=;|&{("
+    self.history_length = -1
+    self.history = [] # strings for previous commands
+    self.history_cursor = 0
+    self.undo_stack = [] # each entry is a tuple with cursor_position and line_text
+    self.line_buffer = []
+    self.line_cursor = 0
+    self.console = Console.Console()
+    self.size = self.console.size()
+    self.prompt_color = None
+    self.command_color = None
+    self.key_dispatch = {}
+    self.previous_func = None
+    self.first_prompt = True
+    self.next_meta = False # True to force meta on next character
+    self.tabstop = 4
+
+    self.emacs_editing_mode(None)
+    self.begidx = 0
+    self.endidx = 0
+
+    # variables you can control with parse_and_bind
+    self.show_all_if_ambiguous = 'off'
+    self.mark_directories = 'on'
+    self.bell_style = 'none'
+
+  def _bell(self):
+    '''ring the bell if requested.'''
+    if self.bell_style == 'none':
+      self.console.bell()
+      
+  def _quoted_text(self):
+    quoted = [ quote_char(c) for c in self.line_buffer ]
+    self.line_char_width = [ len(c) for c in quoted ]
+    return ''.join(quoted)
+
+  def _line_text(self):
+    return ''.join(self.line_buffer)
+
+  def _set_line(self, text, cursor=None):
+    self.line_buffer = [ c for c in str(text) ]
+    if cursor is None:
+      self.line_cursor = len(self.line_buffer)
+    else:
+      self.line_cursor = cursor
+
+  def _reset_line(self):
+    self.line_buffer = []
+    self.line_cursor = 0
+    self.undo_stack = []
+
+  def _clear_after(self):
+    c = self.console
+    x, y = c.pos()
+    w, h = c.size()
+    c.rectangle((x, y, w, y+1))
+    c.rectangle((0, y+1, w, min(y+3,h)))
+
+  def _set_cursor(self):
+    c = self.console
+    xc, yc = self.prompt_end_pos
+    w, h = c.size()
+    xc += reduce(operator.add, self.line_char_width[0:self.line_cursor], 0)
+    while(xc > w):
+      xc -= w
+      yc += 1
+    c.pos(xc, yc)
+
+  def _print_prompt(self):
+    c = self.console
+    log('prompt="%s"' % repr(self.prompt))
+    x, y = c.pos()
+    n = c.write_scrolling(self.prompt, self.prompt_color)
+    self.prompt_begin_pos = (x, y - n)
+    self.prompt_end_pos = c.pos()
+    self.size = c.size()
+
+  def _update_prompt_pos(self, n):
+    if n != 0:
+      bx, by = self.prompt_begin_pos
+      ex, ey = self.prompt_end_pos
+      self.prompt_begin_pos = (bx, by - n)
+      self.prompt_end_pos = (ex, ey - n)
+      
+  def readline(self, prompt=''):
+    '''Try to act like GNU readline.'''
+
+    # handle startup_hook
+    if self.first_prompt:
+      self.first_prompt = False
+      if self.startup_hook:
+        try:
+          self.startup_hook()
+        except:
+          print 'startup hook failed'
+          traceback.print_exc()
+          
+    c = self.console
+    self._reset_line()
+    self.prompt = prompt
+    self._print_prompt()
+
+    if self.pre_input_hook:
+      try:
+        self.pre_input_hook()
+      except:
+        print 'pre_input_hook failed'
+        traceback.print_exc()
+        self.pre_input_hook = None
+
+    while 1:
+      c.pos(*self.prompt_end_pos)
+      ltext = self._quoted_text()
+      n = c.write_scrolling(ltext, self.command_color)
+      self._update_prompt_pos(n)
+      self._clear_after()
+      self._set_cursor()
+
+      event = c.getkeypress()
+      if self.next_meta:
+        self.next_meta = False
+        control, meta, shift, code = event.keyinfo
+        event.keyinfo = (control, True, shift, code)
+        
+      try:
+        dispatch_func = self.key_dispatch[event.keyinfo]
+      except KeyError:
+        c.bell()
+        continue
+      r = None
+      if dispatch_func:
+        r = dispatch_func(event)
+        ltext = self._line_text()
+        if self.undo_stack and ltext == self.undo_stack[-1][1]:
+          self.undo_stack[-1][0] = self.line_cursor
+        else:
+          self.undo_stack.append([self.line_cursor, ltext])
+
+      self.previous_func = dispatch_func
+      if r:
+        break
+      
+    c.write('\r\n')
+
+    rtext = self._line_text()
+    self.add_history(rtext)
+      
+    log('returning(%s)' % rtext)
+    return rtext + '\n'
+
+  def parse_and_bind(self, string):
+    '''Parse and execute single line of a readline init file.'''
+    try:
+      log('parse_and_bind("%s")' % string)
+      if string.startswith('#'):
+        return
+      if string.startswith('set'):
+        m = re.compile(r'set\s+([-a-zA-Z0-9]+)\s+(.+)\s*$').match(string)
+        if m:
+          var_name = m.group(1)
+          val = m.group(2)
+          try:
+            setattr(self, var_name.replace('-','_'), val)
+          except AttributeError:
+            log('unknown var="%s" val="%s"' % (var_name, val))
+        else:
+          log('bad set "%s"' % string)
+        return
+      log('before')
+      m = re.compile(r'\s*(.+)\s*:\s*([-a-zA-Z]+)\s*$').match(string)
+      log('here')
+      if m:
+        key = m.group(1)
+        func_name = m.group(2)
+        py_name = func_name.replace('-', '_')
+        try:
+          func = getattr(self, py_name)
+        except AttributeError:
+          log('unknown func key="%s" func="%s"' % (key, func_name))
+          print 'unknown function to bind: "%s"' % func_name
+        self._bind_key(key, func)
+    except:
+      log('error')
+      traceback.print_exc()
+      raise
+    log('return')
+    
+  def get_line_buffer(self):
+    '''Return the current contents of the line buffer.'''
+    return "".join(self.line_buffer)
+
+  def insert_text(self, string):
+    '''Insert text into the command line.'''
+    for c in string:
+      self.line_buffer.insert(self.line_cursor, c)
+      self.line_cursor += 1
+
+  def read_init_file(self, filename=None): 
+    '''Parse a readline initialization file. The default filename is the last filename used.'''
+    log('read_init_file("%s")' % filename)
+
+  def read_history_file(self, filename='~/.history'): 
+    '''Load a readline history file. The default filename is ~/.history.'''
+    try:
+      for line in open(filename, 'rt'):
+        self.add_history(line.rstrip())
+    except IOError:
+      self.history = []
+      self.history_cursor = 0
+      raise IOError
+
+  def write_history_file(self, filename='~/.history'): 
+    '''Save a readline history file. The default filename is ~/.history.'''
+    fp = open(filename, 'wb')
+    for line in self.history:
+      fp.write(line)
+      fp.write('\n')
+    fp.close()
+
+  def get_history_length(self, ):
+    '''Return the desired length of the history file.
+
+    Negative values imply unlimited history file size.'''
+    return self.history_length
+
+  def set_history_length(self, length): 
+    '''Set the number of lines to save in the history file.
+
+    write_history_file() uses this value to truncate the history file
+    when saving. Negative values imply unlimited history file size.
+    '''
+    self.history_length = length
+
+  def set_startup_hook(self, function=None): 
+    '''Set or remove the startup_hook function.
+
+    If function is specified, it will be used as the new startup_hook
+    function; if omitted or None, any hook function already installed is
+    removed. The startup_hook function is called with no arguments just
+    before readline prints the first prompt.
+
+    '''
+    self.startup_hook = function
+
+  def set_pre_input_hook(self, function=None):
+    '''Set or remove the pre_input_hook function.
+
+    If function is specified, it will be used as the new pre_input_hook
+    function; if omitted or None, any hook function already installed is
+    removed. The pre_input_hook function is called with no arguments
+    after the first prompt has been printed and just before readline
+    starts reading input characters.
+
+    '''
+    self.pre_input_hook = function
+
+  def set_completer(self, function=None): 
+    '''Set or remove the completer function.
+
+    If function is specified, it will be used as the new completer
+    function; if omitted or None, any completer function already
+    installed is removed. The completer function is called as
+    function(text, state), for state in 0, 1, 2, ..., until it returns a
+    non-string value. It should return the next possible completion
+    starting with text.
+    '''
+    log('set_completer')
+    self.completer = function
+
+  def get_completer(self): 
+    '''Get the completer function. 
+    ''' 
+
+    log('get_completer') 
+    return self.completer 
+
+  def get_begidx(self):
+    '''Get the beginning index of the readline tab-completion scope.'''
+    return self.begidx
+
+  def get_endidx(self):
+    '''Get the ending index of the readline tab-completion scope.'''
+    return self.endidx
+
+  def set_completer_delims(self, string):
+    '''Set the readline word delimiters for tab-completion.'''
+    self.completer_delims = string
+
+  def get_completer_delims(self):
+    '''Get the readline word delimiters for tab-completion.'''
+    return self.completer_delims
+
+  def add_history(self, line):
+    '''Append a line to the history buffer, as if it was the last line typed.'''
+    if not line:
+      pass
+    elif len(self.history) > 0 and self.history[-1] == line:
+      pass
+    else:
+      self.history.append(line)
+      if self.history_length > 0 and len(self.history) > self.history_length:
+        self.history = self.history[-self.history_length:]
+    self.history_cursor = len(self.history)
+
+  ### Methods below here are bindable functions
+  
+  def beginning_of_line(self, e): # (C-a)
+    '''Move to the start of the current line. '''
+    self.line_cursor = 0
+
+  def end_of_line(self, e): # (C-e)
+    '''Move to the end of the line. '''
+    self.line_cursor = len(self.line_buffer)
+
+  def forward_char(self, e): # (C-f)
+    '''Move forward a character. '''
+    if self.line_cursor < len(self.line_buffer):
+      self.line_cursor += 1
+    else:
+      self._bell()
+
+  def backward_char(self, e): # (C-b)
+    '''Move back a character. '''
+    if self.line_cursor > 0:
+      self.line_cursor -= 1
+    else:
+      self._bell()
+
+  def forward_word(self, e): # (M-f)
+    '''Move forward to the end of the next word. Words are composed of
+    letters and digits.'''
+    L = len(self.line_buffer)
+    while self.line_cursor < L:
+      self.line_cursor += 1
+      if self.line_cursor == L:
+        break
+      if self.line_buffer[self.line_cursor] not in string.letters + string.digits:
+        break
+      
+  def backward_word(self, e): # (M-b)
+    '''Move back to the start of the current or previous word. Words are
+    composed of letters and digits.'''
+    while self.line_cursor > 0:
+      self.line_cursor -= 1
+      if self.line_buffer[self.line_cursor] not in string.letters + string.digits:
+        break
+
+  def clear_screen(self, e): # (C-l)
+    '''Clear the screen and redraw the current line, leaving the current
+    line at the top of the screen.'''
+    self.console.page()
+
+  def redraw_current_line(self, e): # ()
+    '''Refresh the current line. By default, this is unbound.'''
+    pass
+
+  def accept_line(self, e): # (Newline or Return)
+    '''Accept the line regardless of where the cursor is. If this line
+    is non-empty, it may be added to the history list for future recall
+    with add_history(). If this line is a modified history line, the
+    history line is restored to its original state.'''
+    return True
+
+  def previous_history(self, e): # (C-p)
+    '''Move back through the history list, fetching the previous command. '''
+    if self.history_cursor > 0:
+      self.history_cursor -= 1
+      line = self.history[self.history_cursor]
+      self._set_line(line)
+    else:
+      self._bell()
+      
+  def next_history(self, e): # (C-n)
+    '''Move forward through the history list, fetching the next command. '''
+    if self.history_cursor < len(self.history) - 1:
+      self.history_cursor += 1
+      line = self.history[self.history_cursor]
+      self._set_line(line)
+    elif self.undo_stack:
+      cursor, text = self.undo_stack[-1]
+      self._set_line(text, cursor)
+    else:
+      self._bell()
+
+  def beginning_of_history(self, e): # (M-<)
+    '''Move to the first line in the history.'''
+    self.history_cursor = 0
+    if len(self.history) > 0:
+      self._set_line(self.history[0])
+    else:
+      self._bell()
+
+  def end_of_history(self, e): # (M->)
+    '''Move to the end of the input history, i.e., the line currently
+    being entered.'''
+    if self.undo_stack:
+      cursor, text = self.undo_stack[-1]
+      self._set_line(text, cursor)
+    else:
+      self._bell()
+
+  def _i_search(self, direction, init_event):
+    c = self.console
+    line = self._line_text()
+    query = ''
+    hc_start = self.history_cursor + direction
+    hc = hc_start
+    while 1:
+      x, y = self.prompt_end_pos
+      c.pos(0, y)
+      if direction < 0:
+        prompt = 'reverse-i-search'
+      else:
+        prompt = 'forward-i-search'
+        
+      scroll = c.write_scrolling("%s`%s': %s" % (prompt, query, line))
+      self._update_prompt_pos(scroll)
+      self._clear_after()
+
+      event = c.getkeypress()
+      if event.keysym == 'BackSpace':
+        if len(query) > 0:
+          query = query[:-1]
+          hc = hc_start
+        else:
+          c.bell()
+      elif event.char in string.letters + string.digits + string.punctuation + ' ':
+        query += event.char
+        hc = hc_start
+      elif event.keyinfo == init_event.keyinfo:
+        hc += direction
+      else:
+        if event.keysym != 'Return':
+          c.bell()
+        break
+
+      while (direction < 0 and hc >= 0) or (direction > 0 and hc < len(self.history)):
+        if self.history[hc].find(query) >= 0:
+          break
+        hc += direction
+      else:
+        c.bell()
+        continue
+      line = self.history[hc]
+      
+    px, py = self.prompt_begin_pos
+    c.pos(0, py)
+    self._set_line(line)
+    self._print_prompt()
+  
+  def reverse_search_history(self, e): # (C-r)
+    '''Search backward starting at the current line and moving up
+    through the history as necessary. This is an incremental search.'''
+    self._i_search(-1, e)
+
+  def forward_search_history(self, e): # (C-s)
+    '''Search forward starting at the current line and moving down
+    through the the history as necessary. This is an incremental search.'''
+    self._i_search(1, e)
+
+  def _non_i_search(self, direction):
+    c = self.console
+    line = self._line_text()
+    query = ''
+    while 1:
+      c.pos(*self.prompt_end_pos)
+      scroll = c.write_scrolling(":%s" % query)
+      self._update_prompt_pos(scroll)
+      self._clear_after()
+
+      event = c.getkeypress()
+      if event.keysym == 'BackSpace':
+        if len(query) > 0:
+          query = query[:-1]
+        else:
+          break
+      elif event.char in string.letters + string.digits + string.punctuation + ' ':
+        query += event.char
+      elif event.keysym == 'Return':
+        break
+      else:
+        c.bell()
+
+    if query:
+      hc = self.history_cursor - 1
+      while (direction < 0 and hc >= 0) or (direction > 0 and hc < len(self.history)):
+        if self.history[hc].find(query) >= 0:
+          self._set_line(self.history[hc])
+          self.history_cursor = hc
+          return
+        hc += direction
+      else:
+        c.bell()
+      
+
+  def non_incremental_reverse_search_history(self, e): # (M-p)
+    '''Search backward starting at the current line and moving up
+    through the history as necessary using a non-incremental search for
+    a string supplied by the user.'''
+    self._non_i_search(-1)
+
+  def non_incremental_forward_search_history(self, e): # (M-n)
+    '''Search forward starting at the current line and moving down
+    through the the history as necessary using a non-incremental search
+    for a string supplied by the user.'''
+    self._non_i_search(1)
+
+  def _search(self, direction):
+    c = self.console
+
+    if (self.previous_func != self.history_search_forward and
+        self.previous_func != self.history_search_backward):
+      self.query = ''.join(self.line_buffer[0:self.line_cursor])
+    hc = self.history_cursor + direction
+    while (direction < 0 and hc >= 0) or (direction > 0 and hc < len(self.history)):
+      h = self.history[hc]
+      if not self.query:
+        self._set_line(h)
+        self.history_cursor = hc
+        return
+      elif h.startswith(self.query) and h != self._line_text:
+        self._set_line(h, len(self.query))
+        self.history_cursor = hc
+        return
+      hc += direction
+    else:
+      self._set_line(self.query)
+      c.bell()
+   
+  def history_search_forward(self, e): # ()
+    '''Search forward through the history for the string of characters
+    between the start of the current line and the point. This is a
+    non-incremental search. By default, this command is unbound.'''
+    self._search(1)
+
+  def history_search_backward(self, e): # ()
+    '''Search backward through the history for the string of characters
+    between the start of the current line and the point. This is a
+    non-incremental search. By default, this command is unbound.'''
+    self._search(-1)
+
+  def yank_nth_arg(self, e): # (M-C-y)
+    '''Insert the first argument to the previous command (usually the
+    second word on the previous line) at point. With an argument n,
+    insert the nth word from the previous command (the words in the
+    previous command begin with word 0). A negative argument inserts the
+    nth word from the end of the previous command.'''
+    pass
+
+  def yank_last_arg(self, e): # (M-. or M-_)
+    '''Insert last argument to the previous command (the last word of
+    the previous history entry). With an argument, behave exactly like
+    yank-nth-arg. Successive calls to yank-last-arg move back through
+    the history list, inserting the last argument of each line in turn.'''
+    pass
+
+  def delete_char(self, e): # (C-d)
+    '''Delete the character at point. If point is at the beginning of
+    the line, there are no characters in the line, and the last
+    character typed was not bound to delete-char, then return EOF.'''
+    if len(self.line_buffer) == 0:
+      if self.previous_func != self.delete_char:
+        raise EOFError
+      self._bell()
+    if self.line_cursor < len(self.line_buffer):
+      del self.line_buffer[self.line_cursor]
+    else:
+      self._bell()
+
+  def backward_delete_char(self, e): # (Rubout)
+    '''Delete the character behind the cursor. A numeric argument means
+    to kill the characters instead of deleting them.'''
+    if self.line_cursor > 0:
+      del self.line_buffer[self.line_cursor-1]
+      self.line_cursor -= 1
+
+  def forward_backward_delete_char(self, e): # ()
+    '''Delete the character under the cursor, unless the cursor is at
+    the end of the line, in which case the character behind the cursor
+    is deleted. By default, this is not bound to a key.'''
+    pass
+
+  def quoted_insert(self, e): # (C-q or C-v)
+    '''Add the next character typed to the line verbatim. This is how to
+    insert key sequences like C-q, for example.'''
+    e = self.console.getkeypress()
+    self.line_buffer.insert(self.line_cursor, e.char)
+    self.line_cursor += 1
+
+  def tab_insert(self, e): # (M-TAB)
+    '''Insert a tab character. '''
+    ws = ' ' * (self.tabstop - (self.line_cursor%self.tabstop))
+    self.insert_text(ws)
+
+  def self_insert(self, e): # (a, b, A, 1, !, ...)
+    '''Insert yourself. '''
+    self.line_buffer.insert(self.line_cursor, e.char)
+    self.line_cursor += 1
+
+  def transpose_chars(self, e): # (C-t)
+    '''Drag the character before the cursor forward over the character
+    at the cursor, moving the cursor forward as well. If the insertion
+    point is at the end of the line, then this transposes the last two
+    characters of the line. Negative arguments have no effect.'''
+    pass
+
+  def transpose_words(self, e): # (M-t)
+    '''Drag the word before point past the word after point, moving
+    point past that word as well. If the insertion point is at the end
+    of the line, this transposes the last two words on the line.'''
+    pass
+
+  def upcase_word(self, e): # (M-u)
+    '''Uppercase the current (or following) word. With a negative
+    argument, uppercase the previous word, but do not move the cursor.'''
+    pass
+
+  def downcase_word(self, e): # (M-l)
+    '''Lowercase the current (or following) word. With a negative
+    argument, lowercase the previous word, but do not move the cursor.'''
+    pass
+
+  def capitalize_word(self, e): # (M-c)
+    '''Capitalize the current (or following) word. With a negative
+    argument, capitalize the previous word, but do not move the cursor.'''
+    pass
+
+  def overwrite_mode(self, e): # ()
+    '''Toggle overwrite mode. With an explicit positive numeric
+    argument, switches to overwrite mode. With an explicit non-positive
+    numeric argument, switches to insert mode. This command affects only
+    emacs mode; vi mode does overwrite differently. Each call to
+    readline() starts in insert mode. In overwrite mode, characters
+    bound to self-insert replace the text at point rather than pushing
+    the text to the right. Characters bound to backward-delete-char
+    replace the character before point with a space.'''
+    pass
+
+  def kill_line(self, e): # (C-k)
+    '''Kill the text from point to the end of the line. '''
+    self.line_buffer[self.line_cursor:] = []
+
+  def backward_kill_line(self, e): # (C-x Rubout)
+    '''Kill backward to the beginning of the line. '''
+    self.line_buffer[:self.line_cursor] = []
+    self.line_cursor = 0
+
+  def unix_line_discard(self, e): # (C-u)
+    '''Kill backward from the cursor to the beginning of the current line. '''
+    # how is this different from backward_kill_line?
+    self.line_buffer[:self.line_cursor] = []
+    self.line_cursor = 0
+
+  def kill_whole_line(self, e): # ()
+    '''Kill all characters on the current line, no matter where point
+    is. By default, this is unbound.'''
+    pass
+
+  def kill_word(self, e): # (M-d)
+    '''Kill from point to the end of the current word, or if between
+    words, to the end of the next word. Word boundaries are the same as
+    forward-word.'''
+    begin = self.line_cursor
+    self.forward_word(e)
+    self.line_buffer[begin:self.line_cursor] = []
+    self.line_cursor = begin
+
+  def backward_kill_word(self, e): # (M-DEL)
+    '''Kill the word behind point. Word boundaries are the same as
+    backward-word. '''
+    begin = self.line_cursor
+    self.backward_word(e)
+    self.line_buffer[self.line_cursor:begin] = []
+
+  def unix_word_rubout(self, e): # (C-w)
+    '''Kill the word behind point, using white space as a word
+    boundary. The killed text is saved on the kill-ring.'''
+    begin = self.line_cursor
+    while self.line_cursor > 0:
+      self.line_cursor -= 1
+      if self.line_buffer[self.line_cursor] == ' ':
+        break
+    self.line_buffer[self.line_cursor:begin] = []
+    
+  def delete_horizontal_space(self, e): # ()
+    '''Delete all spaces and tabs around point. By default, this is unbound. '''
+    pass
+
+  def kill_region(self, e): # ()
+    '''Kill the text in the current region. By default, this command is unbound. '''
+    pass
+
+  def copy_region_as_kill(self, e): # ()
+    '''Copy the text in the region to the kill buffer, so it can be
+    yanked right away. By default, this command is unbound.'''
+    pass
+
+  def copy_backward_word(self, e): # ()
+    '''Copy the word before point to the kill buffer. The word
+    boundaries are the same as backward-word. By default, this command
+    is unbound.'''
+    pass
+
+  def copy_forward_word(self, e): # ()
+    '''Copy the word following point to the kill buffer. The word
+    boundaries are the same as forward-word. By default, this command is
+    unbound.'''
+    pass
+
+  def yank(self, e): # (C-y)
+    '''Yank the top of the kill ring into the buffer at point. '''
+    pass
+
+  def yank_pop(self, e): # (M-y)
+    '''Rotate the kill-ring, and yank the new top. You can only do this
+    if the prior command is yank or yank-pop.'''
+    pass
+
+
+  def digit_argument(self, e): # (M-0, M-1, ... M--)
+    '''Add this digit to the argument already accumulating, or start a
+    new argument. M-- starts a negative argument.'''
+    pass
+
+  def universal_argument(self, e): # ()
+    '''This is another way to specify an argument. If this command is
+    followed by one or more digits, optionally with a leading minus
+    sign, those digits define the argument. If the command is followed
+    by digits, executing universal-argument again ends the numeric
+    argument, but is otherwise ignored. As a special case, if this
+    command is immediately followed by a character that is neither a
+    digit or minus sign, the argument count for the next command is
+    multiplied by four. The argument count is initially one, so
+    executing this function the first time makes the argument count
+    four, a second time makes the argument count sixteen, and so on. By
+    default, this is not bound to a key.'''
+    pass
+
+
+  def _get_completions(self):
+    '''Return a list of possible completions for the string ending at the point.
+
+    Also set begidx and endidx in the process.'''
+    completions = []
+    self.begidx = self.line_cursor
+    self.endidx = self.line_cursor
+    if self.completer:
+      # get the string to complete
+      while self.begidx > 0:
+        self.begidx -= 1
+        if self.line_buffer[self.begidx] in self.completer_delims:
+          self.begidx += 1
+          break
+      text = ''.join(self.line_buffer[self.begidx:self.endidx])
+      log('complete text="%s"' % text)
+      i = 0
+      while 1:
+        try:
+          r = self.completer(text, i)
+        except:
+          break
+        i += 1
+        if r and r not in completions:
+          completions.append(r)
+        else:
+          break
+      log('text completions=%s' % completions)
+    if not completions:
+      # get the filename to complete
+      while self.begidx > 0:
+        self.begidx -= 1
+        if self.line_buffer[self.begidx] in ' \t\n':
+          self.begidx += 1
+          break
+      text = ''.join(self.line_buffer[self.begidx:self.endidx])
+      log('file complete text="%s"' % text)
+      completions = glob(os.path.expanduser(text) + '*')
+      if self.mark_directories == 'on':
+        mc = []
+        for f in completions:
+          if os.path.isdir(f):
+            mc.append(f + os.sep)
+          else:
+            mc.append(f)
+        completions = mc
+      log('fnames=%s' % completions)
+    return completions
+
+  def _display_completions(self, completions):
+    if not completions:
+      return
+    self.console.write('\n')
+    wmax = max(map(len, completions))
+    w, h = self.console.size()
+    cols = max(1, int((w-1) / (wmax+1)))
+    rows = int(math.ceil(float(len(completions)) / cols))
+    for row in range(rows):
+      s = ''
+      for col in range(cols):
+        i = col*rows + row
+        if i < len(completions):
+          self.console.write(completions[i].ljust(wmax+1))
+      self.console.write('\n')
+    self._print_prompt()
+    
+  def complete(self, e): # (TAB)
+    '''Attempt to perform completion on the text before point. The
+    actual completion performed is application-specific. The default is
+    filename completion.'''
+    completions = self._get_completions()
+    if completions:
+      cprefix = commonprefix(completions)
+      rep = [ c for c in cprefix ]
+      self.line_buffer[self.begidx:self.endidx] = rep
+      self.line_cursor += len(rep) - (self.endidx - self.begidx)
+      if len(completions) > 1:
+        if self.show_all_if_ambiguous == 'on':
+          self._display_completions(completions)
+        else:
+          self._bell()
+    else:
+      self._bell()
+
+  def possible_completions(self, e): # (M-?)
+    '''List the possible completions of the text before point. '''
+    completions = self._get_completions()
+    self._display_completions(completions)
+
+  def insert_completions(self, e): # (M-*)
+    '''Insert all completions of the text before point that would have
+    been generated by possible-completions.'''
+    completions = self._get_completions()
+    b = self.begidx
+    e = self.endidx
+    for comp in completions:
+      rep = [ c for c in comp ]
+      rep.append(' ')
+      self.line_buffer[b:e] = rep
+      b += len(rep)
+      e = b
+    self.line_cursor = b      
+
+  def menu_complete(self, e): # ()
+    '''Similar to complete, but replaces the word to be completed with a
+    single match from the list of possible completions. Repeated
+    execution of menu-complete steps through the list of possible
+    completions, inserting each match in turn. At the end of the list of
+    completions, the bell is rung (subject to the setting of bell-style)
+    and the original text is restored. An argument of n moves n
+    positions forward in the list of matches; a negative argument may be
+    used to move backward through the list. This command is intended to
+    be bound to TAB, but is unbound by default.'''
+    pass
+
+  def delete_char_or_list(self, e): # ()
+    '''Deletes the character under the cursor if not at the beginning or
+    end of the line (like delete-char). If at the end of the line,
+    behaves identically to possible-completions. This command is unbound
+    by default.'''
+    pass
+
+  def start_kbd_macro(self, e): # (C-x ()
+    '''Begin saving the characters typed into the current keyboard macro. '''
+    pass
+
+  def end_kbd_macro(self, e): # (C-x ))
+    '''Stop saving the characters typed into the current keyboard macro
+    and save the definition.'''
+    pass
+
+  def call_last_kbd_macro(self, e): # (C-x e)
+    '''Re-execute the last keyboard macro defined, by making the
+    characters in the macro appear as if typed at the keyboard.'''
+    pass
+
+  def re_read_init_file(self, e): # (C-x C-r)
+    '''Read in the contents of the inputrc file, and incorporate any
+    bindings or variable assignments found there.'''
+    pass
+
+  def abort(self, e): # (C-g)
+    '''Abort the current editing command and ring the terminals bell
+       (subject to the setting of bell-style).'''
+    self._bell()
+
+  def do_uppercase_version(self, e): # (M-a, M-b, M-x, ...)
+    '''If the metafied character x is lowercase, run the command that is
+    bound to the corresponding uppercase character.'''
+    pass
+
+  def prefix_meta(self, e): # (ESC)
+    '''Metafy the next character typed. This is for keyboards without a
+    meta key. Typing ESC f is equivalent to typing M-f. '''
+    self.next_meta = True
+
+  def undo(self, e): # (C-_ or C-x C-u)
+    '''Incremental undo, separately remembered for each line.'''
+    log(self.undo_stack)
+    if len(self.undo_stack) >= 2:
+      self.undo_stack.pop()
+      cursor, text = self.undo_stack.pop()
+    else:
+      cursor = 0
+      text = ''
+      self.undo_stack = []
+    self._set_line(text, cursor)
+
+  def revert_line(self, e): # (M-r)
+    '''Undo all changes made to this line. This is like executing the
+    undo command enough times to get back to the beginning.'''
+    pass
+
+  def tilde_expand(self, e): # (M-~)
+    '''Perform tilde expansion on the current word.'''
+    pass
+
+  def set_mark(self, e): # (C-@)
+    '''Set the mark to the point. If a numeric argument is supplied, the
+    mark is set to that position.'''
+    pass
+
+  def exchange_point_and_mark(self, e): # (C-x C-x)
+    '''Swap the point with the mark. The current cursor position is set
+    to the saved position, and the old cursor position is saved as the
+    mark.'''
+    pass
+
+  def character_search(self, e): # (C-])
+    '''A character is read and point is moved to the next occurrence of
+    that character. A negative count searches for previous occurrences.'''
+    pass
+
+  def character_search_backward(self, e): # (M-C-])
+    '''A character is read and point is moved to the previous occurrence
+    of that character. A negative count searches for subsequent
+    occurrences.'''
+    pass
+
+  def insert_comment(self, e): # (M-#)
+    '''Without a numeric argument, the value of the comment-begin
+    variable is inserted at the beginning of the current line. If a
+    numeric argument is supplied, this command acts as a toggle: if the
+    characters at the beginning of the line do not match the value of
+    comment-begin, the value is inserted, otherwise the characters in
+    comment-begin are deleted from the beginning of the line. In either
+    case, the line is accepted as if a newline had been typed.'''
+    pass
+
+  def dump_functions(self, e): # ()
+    '''Print all of the functions and their key bindings to the Readline
+    output stream. If a numeric argument is supplied, the output is
+    formatted in such a way that it can be made part of an inputrc
+    file. This command is unbound by default.'''
+    pass
+
+  def dump_variables(self, e): # ()
+    '''Print all of the settable variables and their values to the
+    Readline output stream. If a numeric argument is supplied, the
+    output is formatted in such a way that it can be made part of an
+    inputrc file. This command is unbound by default.'''
+    pass
+
+  def dump_macros(self, e): # ()
+    '''Print all of the Readline key sequences bound to macros and the
+    strings they output. If a numeric argument is supplied, the output
+    is formatted in such a way that it can be made part of an inputrc
+    file. This command is unbound by default.'''
+    pass
+
+  def _bind_key(self, key, func):
+    '''setup the mapping from key to call the function.'''
+    keyinfo = key_text_to_keyinfo(key)
+    self.key_dispatch[keyinfo] = func
+    
+  def emacs_editing_mode(self, e): # (C-e)
+    '''When in vi command mode, this causes a switch to emacs editing
+    mode.'''
+    # make ' ' to ~ self insert
+    for c in range(ord(' '), 127):
+      self._bind_key('"%s"' % chr(c), self.self_insert)
+    # I often accidentally hold the shift or control while typing space
+    self._bind_key('Shift-space', self.self_insert)
+    self._bind_key('Control-space', self.self_insert)
+    self._bind_key('Return', self.accept_line)
+    self._bind_key('Left', self.backward_char)
+    self._bind_key('Control-b', self.backward_char)
+    self._bind_key('Right', self.forward_char)
+    self._bind_key('Control-f', self.forward_char)
+    self._bind_key('BackSpace', self.backward_delete_char)
+    self._bind_key('Home', self.beginning_of_line)
+    self._bind_key('End', self.end_of_line)
+    self._bind_key('Delete', self.delete_char)
+    self._bind_key('Control-d', self.delete_char)
+    self._bind_key('Clear', self.clear_screen)
+    self._bind_key('Alt-f', self.forward_word)
+    self._bind_key('Alt-b', self.backward_word)
+    self._bind_key('Control-l', self.clear_screen)
+    self._bind_key('Control-p', self.previous_history)
+    self._bind_key('Up', self.history_search_backward)
+    self._bind_key('Control-n', self.next_history)
+    self._bind_key('Down', self.history_search_forward)
+    self._bind_key('Control-a', self.beginning_of_line)
+    self._bind_key('Control-e', self.end_of_line)
+    self._bind_key('Alt-<', self.beginning_of_history)
+    self._bind_key('Alt->', self.end_of_history)
+    self._bind_key('Control-r', self.reverse_search_history)
+    self._bind_key('Control-s', self.forward_search_history)
+    self._bind_key('Alt-p', self.non_incremental_reverse_search_history)
+    self._bind_key('Alt-n', self.non_incremental_forward_search_history)
+    self._bind_key('Control-z', self.undo)
+    self._bind_key('Control-_', self.undo)
+    self._bind_key('Escape', self.prefix_meta)
+    self._bind_key('Meta-d', self.kill_word)
+    self._bind_key('Meta-Delete', self.backward_kill_word)
+    self._bind_key('Control-w', self.unix_word_rubout)
+    self._bind_key('Control-v', self.quoted_insert)
+    
+    # Add keybindings for numpad
+    # first the number keys
+    self._bind_key('NUMPAD0', self.self_insert)
+    self._bind_key('NUMPAD1', self.self_insert)
+    self._bind_key('NUMPAD2', self.self_insert)
+    self._bind_key('NUMPAD3', self.self_insert)
+    self._bind_key('NUMPAD4', self.self_insert)
+    self._bind_key('NUMPAD5', self.self_insert)
+    self._bind_key('NUMPAD6', self.self_insert)
+    self._bind_key('NUMPAD7', self.self_insert)
+    self._bind_key('NUMPAD8', self.self_insert)
+    self._bind_key('NUMPAD9', self.self_insert)
+    # then the others: / * - + 
+    self._bind_key('Divide', self.self_insert)
+    self._bind_key('Multiply', self.self_insert)
+    self._bind_key('Add', self.self_insert)
+    self._bind_key('Subtract', self.self_insert)
+    # the decimal separator: '.' on US keyboards, ',' on DE one's
+    self._bind_key('VK_DECIMAL', self.self_insert)
+    
+    
+  def vi_editing_mode(self, e): # (M-C-j)
+    '''When in emacs editing mode, this causes a switch to vi editing
+    mode.'''
+    pass
+
+def CTRL(c):
+  '''make a control character'''
+  assert '@' <= c <= '_'
+  return chr(ord(c) - ord('@'))
+  
+# make it case insensitive
+def commonprefix(m):
+  "Given a list of pathnames, returns the longest common leading component"
+  if not m: return ''
+  prefix = m[0]
+  for item in m:
+    for i in range(len(prefix)):
+      if prefix[:i+1].lower() != item[:i+1].lower():
+        prefix = prefix[:i]
+        if i == 0: return ''
+        break
+  return prefix
+
+# create a Readline object to contain the state
+rl = Readline()
+
+def GetOutputFile():
+  '''Return the console object used by readline so that it can be used for printing in color.'''
+  return rl.console
+
+# make these available so this looks like the python readline module
+parse_and_bind = rl.parse_and_bind
+get_line_buffer = rl.get_line_buffer
+insert_text = rl.insert_text
+read_init_file = rl.read_init_file
+read_history_file = rl.read_history_file
+write_history_file = rl.write_history_file
+get_history_length = rl.get_history_length
+set_history_length = rl.set_history_length
+set_startup_hook = rl.set_startup_hook
+set_pre_input_hook = rl.set_pre_input_hook
+set_completer = rl.set_completer
+get_completer = rl.get_completer
+get_begidx = rl.get_begidx
+get_endidx = rl.get_endidx
+set_completer_delims = rl.set_completer_delims
+get_completer_delims = rl.get_completer_delims
+add_history = rl.add_history
+
+if __name__ == '__main__':
+  res = [ rl.readline('In[%d] ' % i) for i in range(3) ]
+  print res
+else:
+  #import wingdbstub
+  Console.install_readline(rl.readline)
+    

Property changes on: IPython\winreadline\PyReadline.py
___________________________________________________________________
Name: svn:keywords
   + Id Author Revision Date

