def reset(self): sys.ps1 = ps1 sys.ps2 = ps2 self.printer.clear() self._reset_needed = False self.context.clear() self.context = self.original_context.copy() self.history = ConsoleHistory() self.printer.print_new(banner) if not self.done_first_run: self.done_first_run = True self.browser_sniff()
def __init__(self, locals=None, banner=None, completer=None, use_rlcompleter=True, start_script=None): gtk.TextView.__init__(self) if not locals: locals = __main__.__dict__ code.InteractiveInterpreter.__init__(self, locals) #self.locals['__console__'] = self self.banner = banner self.completer = completer if not self.completer and use_rlcompleter: try: import rlcompleter self.completer = rlcompleter.Completer() except ImportError: pass self.start_script = start_script # for debugging #start_script="import gtk\n" + \ #"win = gtk.Window()\n" + \ #"label = gtk.Label('Hello there!')\n" + \ #"win.add(label)\n" + \ #"win.show_all()\n" self.run_on_raw_input = start_script self.connect("key-press-event", self.on_key_press_event) self.set_wrap_mode(gtk.WRAP_CHAR) self.modify_font(pango.FontDescription("Monospace")) self.buffer = self.get_buffer() self.buffer.connect("insert-text", self.on_buf_insert) self.buffer.connect("delete-range", self.on_buf_delete) self.buffer.connect("mark-set", self.on_buf_mark_set) self.do_insert = False self.do_delete = False self.cursor = self.buffer.create_mark("cursor", self.buffer.get_start_iter(), False) insert = self.buffer.get_insert() self.cursor.set_visible(True) insert.set_visible(False) self.ps = '' self.ps1 = ">>> " self.ps2 = "... " self.in_raw_input = False self.tab_pressed = 0 self.history = ConsoleHistory() self.__start() self.raw_input(self.ps1)
class ConsoleTextBox(TextBox): def __new__(cls, printer, context, root): return TextBox.__new__(cls) def __init__(self, printer, context, root): self._input_data = [] self.original_context = context self.printer = printer self.prompt = root.prompt self.root = root self.done_first_run = False self._sync = ManualResetEvent(False) self.ff3 = False self.FontSize = 15 self.Margin = Thickness(0) self.FontFamily = FontFamily("Consolas, Monaco, Lucida Console, Global Monospace") self.AcceptsReturn = True self.BorderThickness = Thickness(0) self.VerticalScrollBarVisibility = ScrollBarVisibility.Auto self.MinWidth = 300 def reset(): "Clear the console, its history and the execution context." self._reset_needed = True return 'resetting' def input(prompt='Input:'): 'input([prompt]) -> value\n\nEquivalent to eval(raw_input(prompt)).' return eval(self.context['raw_input'](prompt), self.context, self.context) self.original_context['reset'] = reset self.original_context['gohome'] = gohome self.original_context['exit'] = 'There is no escape...' self.original_context['raw_input'] = self.raw_input self.original_context['input'] = input # for debugging only! self.original_context['root'] = root self.context = {} self.history = None self._reset_needed = False self._thread = None self._thread_reset = None self._raw_input_text = '' self._temp_context = None self.engine = Python.CreateEngine() self.scope = self.engine.CreateScope() self._original_caret = None if hasattr(self, 'CaretBrush'): self._original_caret = self.CaretBrush self._disabled = SolidColorBrush(Colors.White) self.KeyDown += self.handle_key self.TextChanged += self.text_changed def reset(self): sys.ps1 = ps1 sys.ps2 = ps2 self.printer.clear() self._reset_needed = False self.context.clear() self.context = self.original_context.copy() self.history = ConsoleHistory() self.printer.print_new(banner) if not self.done_first_run: self.done_first_run = True self.browser_sniff() def browser_sniff(self): useragent = HtmlPage.BrowserInformation.UserAgent match = re.search(FF3_RE, useragent) if match is not None: browser = match.groups()[0] self.printer.print_new(FF3_MESSAGE % browser) self.ff3 = True def OnKeyDown(self, event): # needed so that we get KeyDown # for del and backspace events etc pass def text_changed(self, sender, event): # replace any tabs that are pasted in if '\t' in self.Text: self.Text = self.Text.replace('\t', ' ') self.SelectionStart = len(self.Text) def write(self, data): self.printer.write(data) def is_complete(self, text, pos): if len(text.splitlines()) > 1 and pos < len(text.rstrip()): return False if text.endswith('\\'): return False source = self.engine.CreateScriptSourceFromString(text, '<stdin>', SourceCodeKind.InteractiveCode) result = source.GetCodeProperties() if result == ScriptCodeParseResult.IncompleteToken: return False elif result == ScriptCodeParseResult.IncompleteStatement: if not text.rstrip(' ').endswith('\n'): return False return True def on_first_line(self, text): first_line_end = text.find('\n') if first_line_end == -1: return True return self.SelectionStart <= first_line_end def on_last_line(self, text): last_line_end = text.rfind('\n') if last_line_end == -1: return True return self.SelectionStart > last_line_end def handle_key(self, sender, event): # Mac Safari uses '\r' for newlines in Silverlight TextBox?? contents = self.Text.replace('\r\n', '\n').replace('\r', '\n') key = event.Key start = self.SelectionStart end = start + self.SelectionLength modifiers = Keyboard.Modifiers control = (modifiers & ModifierKeys.Control) or (modifiers & ModifierKeys.Apple) if key == Key.C and control: event.Handled = True self.keyboard_interrupt() return if self._thread is not None: # ignore key events (we have already checked for Ctrl-C) event.Handled = True return if key != Key.Enter: if key == Key.Up: if self.on_first_line(contents): event.Handled = True previous = self.history.back(contents) if previous is not None: self.Text = previous self.SelectionStart = len(previous) return elif key == Key.Down: if self.on_last_line(contents): event.Handled = True next = self.history.forward(contents) if next is not None: self.Text = next self.SelectionStart = len(next) return elif key == Key.Tab: event.Handled = True self.Text = self.Text[:start] + ' ' + self.Text[end:] self.SelectionStart = start + 4 return if key == key.Add and self.ff3: if not (modifiers & ModifierKeys.Shift): event.Handled = True self.Text = self.Text[:start] + '=' + self.Text[end:] self.SelectionStart = start + 1 return TextBox.OnKeyDown(self, event) return event.Handled = True if empty_or_comment_only(contents): # needed or we get a SyntaxError self.Text = '' self.printer.print_lines(contents) self.printer.scroll() return # manual handling unfortunately # means things like trailing comments break # these functions; so not ideal stripped = contents.rstrip() if stripped == 'gohome': gohome() elif stripped == 'reset': contents = 'reset()' elif stripped == 'import this': # import hook so that importing *worked* # would be a better solution... self.printer.print_lines(stripped) self.Text = '' self.history.append(contents) import this self.context['this'] = this self.printer.set_prompt() self.printer.scroll() return if not self.is_complete(contents, start): self.do_indent(start) else: self.execute(contents) def execute(self, contents): self.printer.print_lines(contents) self.Text = '' self.history.append(contents) self._sync.Reset() started = ManualResetEvent(False) if self._temp_context is not None: self.context.update(self._temp_context) def _execute(): context = self.context started.Set() try: code = compile(contents + '\n', '<stdin>', 'single', PyCF_DONT_IMPLY_DEDENT) exec code in context except: if reset_event.WaitOne(1): # don't print exception messages if thread has been terminated return exc_type, value, tb = sys.exc_info() if value is None: # String exceptions # workaround for IronPython bug exc_type = Exception value = Exception('StringException') tblist = traceback.extract_tb(tb) message = traceback.format_list(tblist) del message[:1] if message: # we don't print the 'Traceback...' part for SyntaxError message.insert(0, "Traceback (most recent call last):\n") message.extend(traceback.format_exception_only(exc_type, value)) self.printer.print_new(''.join(message)) # access through closure not on self as we may be an orphaned # thread - with a new reset_event on self result = reset_event.WaitOne(0) if not reset_event.WaitOne(0): self.completed() self._sync.Set() self._thread_reset = reset_event = ManualResetEvent(False) self._thread = Thread(ThreadStart(_execute)) self._thread.IsBackground = True self._thread.Name = "executing" self._thread.Start() self.prompt.Visibility = Visibility.Collapsed if hasattr(self, 'CaretBrush'): self.CaretBrush = self._disabled started.WaitOne() @invoke def completed(self, reset_temp=True): if reset_temp: self._temp_context = None self._thread = None self.prompt.Visibility = Visibility.Visible self._thread_reset = None if hasattr(self, 'CaretBrush'): self.CaretBrush = self._original_caret if self._reset_needed: self.reset() else: self.printer.set_prompt() self.printer.scroll() def do_indent(self, start): to_the_left = self.Text[:start + 1] lines = to_the_left.splitlines() initial_indent = ' ' for line in lines: # we do this incase the user is using one or two space # indent instead of four if line.startswith(' '): initial_indent = get_indent(line) break # there *must* be something here because an empty textbox # would already have been caught by empty_or_comment_only last_line = lines[-1] new_indent = current_indent = get_indent(last_line) if last_line.rstrip().endswith(':'): new_indent = current_indent + initial_indent elif is_terminator(last_line): new_indent = ' ' * (len(current_indent) - len(initial_indent)) new_start = self.SelectionStart new_pos = new_start + len(new_indent) self.Text = self.Text[:new_start] + '\n' + new_indent + self.Text[new_start:] self.SelectionStart = new_pos + 1 def keyboard_interrupt(self): # Aborting threads doesn't work on Silverlight :-( #self._thread.Abort() reset_temp = True if self._thread_reset is not None: reset_temp = False # signal to background thread not to complete self._thread_reset.Set() context = self.context self._temp_context = context.copy() # This will hopefully cause the existing thread to error out context.clear() context['raw_input'] = context['input'] = blow_up self._thread_reset = None self._thread = None if self.Text.strip(): self.history.append(self.Text) self.printer.print_new('KeyboardInterrupt') self.Text = '' self.completed(reset_temp) @invoke def setup_input_box(self, prompt): self.Visibility = Visibility.Collapsed self.root.rawInputPrompt.Text = prompt + ' ' self.root.rawInputField.Text = '' self.root.rawInputField.Width = self.Width - 60 self.root.rawInput.Visibility = Visibility.Visible self.root.rawInputField.KeyDown += self.check_for_enter self.root.rawInputField.Focus() @invoke def remove_input_box(self): self.Visibility = Visibility.Visible self.root.rawInput.Visibility = Visibility.Collapsed self.root.rawInputField.KeyDown -= self.check_for_enter self.printer.print_new(self.root.rawInputPrompt.Text + self._raw_input_text) self.Focus() def check_for_enter(self, sender, event): if event.Key == Key.Enter: event.Handled = True self._raw_input_text = self.root.rawInputField.Text self.input_event.Set() def raw_input(self, prompt='Input:'): if not isinstance(prompt, str): prompt = str(prompt) self.input_event = ManualResetEvent(False) self.setup_input_box(prompt) self.input_event.WaitOne() self.remove_input_box() return self._raw_input_text def _handle_lines(self, lines): try: self._input_data = [] for line in lines: self.handle_line(line) if self._input_data: self.handle_line('') self.Dispatcher.BeginInvoke(self.Focus) except Exception, e: _debug('Handle lines', e)
class ConsoleTextBox(TextBox): def __new__(cls, printer, context, root): return TextBox.__new__(cls) def __init__(self, printer, context, root): self._input_data = [] self.original_context = context self.printer = printer self.prompt = root.prompt self.root = root self.done_first_run = False self._sync = ManualResetEvent(False) self.ff3 = False self.FontSize = 15 self.Margin = Thickness(0) self.FontFamily = FontFamily( "Consolas, Monaco, Lucida Console, Global Monospace") self.AcceptsReturn = True self.BorderThickness = Thickness(0) self.VerticalScrollBarVisibility = ScrollBarVisibility.Auto self.MinWidth = 300 def reset(): "Clear the console, its history and the execution context." self._reset_needed = True return 'resetting' def input(prompt='Input:'): 'input([prompt]) -> value\n\nEquivalent to eval(raw_input(prompt)).' return eval(self.context['raw_input'](prompt), self.context, self.context) self.original_context['reset'] = reset self.original_context['gohome'] = gohome self.original_context['exit'] = 'There is no escape...' self.original_context['raw_input'] = self.raw_input self.original_context['input'] = input # for debugging only! self.original_context['root'] = root self.context = {} self.history = None self._reset_needed = False self._thread = None self._thread_reset = None self._raw_input_text = '' self._temp_context = None self.engine = Python.CreateEngine() self.scope = self.engine.CreateScope() self._original_caret = None if hasattr(self, 'CaretBrush'): self._original_caret = self.CaretBrush self._disabled = SolidColorBrush(Colors.White) self.KeyDown += self.handle_key self.TextChanged += self.text_changed def reset(self): sys.ps1 = ps1 sys.ps2 = ps2 self.printer.clear() self._reset_needed = False self.context.clear() self.context = self.original_context.copy() self.history = ConsoleHistory() self.printer.print_new(banner) if not self.done_first_run: self.done_first_run = True self.browser_sniff() def browser_sniff(self): useragent = HtmlPage.BrowserInformation.UserAgent match = re.search(FF3_RE, useragent) if match is not None: browser = match.groups()[0] self.printer.print_new(FF3_MESSAGE % browser) self.ff3 = True def OnKeyDown(self, event): # needed so that we get KeyDown # for del and backspace events etc pass def text_changed(self, sender, event): # replace any tabs that are pasted in if '\t' in self.Text: self.Text = self.Text.replace('\t', ' ') self.SelectionStart = len(self.Text) def write(self, data): self.printer.write(data) def is_complete(self, text, pos): if len(text.splitlines()) > 1 and pos < len(text.rstrip()): return False if text.endswith('\\'): return False source = self.engine.CreateScriptSourceFromString( text, 'stdin', SourceCodeKind.InteractiveCode) try: result = source.GetCodeProperties() except TypeError: # happens when text is 'lambda' for some reason return True if result == ScriptCodeParseResult.IncompleteToken: return False elif result == ScriptCodeParseResult.IncompleteStatement: if not text.rstrip(' ').endswith('\n'): return False return True def on_first_line(self, text): first_line_end = text.find('\n') if first_line_end == -1: return True return self.SelectionStart <= first_line_end def on_last_line(self, text): last_line_end = text.rfind('\n') if last_line_end == -1: return True return self.SelectionStart > last_line_end def handle_key(self, sender, event): # Mac Safari uses '\r' for newlines in Silverlight TextBox?? contents = self.Text.replace('\r\n', '\n').replace('\r', '\n') key = event.Key start = self.SelectionStart end = start + self.SelectionLength modifiers = Keyboard.Modifiers control = (modifiers & ModifierKeys.Control) or (modifiers & ModifierKeys.Apple) if key == Key.C and control: event.Handled = True self.keyboard_interrupt() return if self._thread is not None: # ignore key events (we have already checked for Ctrl-C) event.Handled = True return if key != Key.Enter: if key == Key.Up: if self.on_first_line(contents): event.Handled = True previous = self.history.back(contents) if previous is not None: self.Text = previous self.SelectionStart = len(previous) return elif key == Key.Down: if self.on_last_line(contents): event.Handled = True next = self.history.forward(contents) if next is not None: self.Text = next self.SelectionStart = len(next) return elif key == Key.Tab: event.Handled = True self.Text = self.Text[:start] + ' ' + self.Text[end:] self.SelectionStart = start + 4 return if key == key.Add and self.ff3: if not (modifiers & ModifierKeys.Shift): event.Handled = True self.Text = self.Text[:start] + '=' + self.Text[end:] self.SelectionStart = start + 1 return TextBox.OnKeyDown(self, event) return event.Handled = True if empty_or_comment_only(contents): # needed or we get a SyntaxError self.Text = '' self.printer.print_lines(contents) self.printer.scroll() return # manual handling unfortunately # means things like trailing comments break # these functions; so not ideal stripped = contents.rstrip() if stripped == 'gohome': gohome() elif stripped == 'reset': contents = 'reset()' elif stripped == 'import this': # import hook so that importing *worked* # would be a better solution... self.printer.print_lines(stripped) self.Text = '' self.history.append(contents) import this self.context['this'] = this self.printer.set_prompt() self.printer.scroll() return if not self.is_complete(contents, start): self.do_indent(start) else: self.execute(contents) def execute(self, contents): self.printer.print_lines(contents) self.Text = '' self.history.append(contents) self._sync.Reset() started = ManualResetEvent(False) if self._temp_context is not None: self.context.update(self._temp_context) def _execute(): context = self.context started.Set() try: code = compile(contents + '\n', '<stdin>', 'single', PyCF_DONT_IMPLY_DEDENT) exec code in context except: if reset_event.WaitOne(1): # don't print exception messages if thread has been terminated return exc_type, value, tb = sys.exc_info() if value is None: # String exceptions # workaround for IronPython bug exc_type = Exception value = Exception('StringException') tblist = traceback.extract_tb(tb) message = traceback.format_list(tblist) del message[:1] if message: # we don't print the 'Traceback...' part for SyntaxError message.insert(0, "Traceback (most recent call last):\n") message.extend(traceback.format_exception_only( exc_type, value)) self.printer.print_new(''.join(message)) # access through closure not on self as we may be an orphaned # thread - with a new reset_event on self result = reset_event.WaitOne(0) if not reset_event.WaitOne(0): self.completed() self._sync.Set() self._thread_reset = reset_event = ManualResetEvent(False) self._thread = Thread(ThreadStart(_execute)) self._thread.IsBackground = True self._thread.Name = "executing" self._thread.Start() self.prompt.Visibility = Visibility.Collapsed if hasattr(self, 'CaretBrush'): self.CaretBrush = self._disabled started.WaitOne() @invoke def completed(self, reset_temp=True): if reset_temp: self._temp_context = None self._thread = None self.prompt.Visibility = Visibility.Visible self._thread_reset = None if hasattr(self, 'CaretBrush'): self.CaretBrush = self._original_caret if self._reset_needed: self.reset() else: self.printer.set_prompt() self.printer.scroll() def do_indent(self, start): to_the_left = self.Text[:start + 1] lines = to_the_left.splitlines() initial_indent = ' ' for line in lines: # we do this incase the user is using one or two space # indent instead of four if line.startswith(' '): initial_indent = get_indent(line) break # there *must* be something here because an empty textbox # would already have been caught by empty_or_comment_only last_line = lines[-1] new_indent = current_indent = get_indent(last_line) if last_line.rstrip().endswith(':'): new_indent = current_indent + initial_indent elif is_terminator(last_line): new_indent = ' ' * (len(current_indent) - len(initial_indent)) new_start = self.SelectionStart new_pos = new_start + len(new_indent) self.Text = self.Text[:new_start] + '\n' + new_indent + self.Text[ new_start:] self.SelectionStart = new_pos + 1 def keyboard_interrupt(self): # Aborting threads doesn't work on Silverlight :-( #self._thread.Abort() reset_temp = True if self._thread_reset is not None: reset_temp = False # signal to background thread not to complete self._thread_reset.Set() context = self.context self._temp_context = context.copy() # This will hopefully cause the existing thread to error out context.clear() context['raw_input'] = context['input'] = blow_up self._thread_reset = None self._thread = None if self.Text.strip(): self.history.append(self.Text) self.printer.print_new('KeyboardInterrupt') self.Text = '' self.completed(reset_temp) @invoke def setup_input_box(self, prompt): self.Visibility = Visibility.Collapsed self.root.rawInputPrompt.Text = prompt + ' ' self.root.rawInputField.Text = '' self.root.rawInputField.Width = self.Width - 60 self.root.rawInput.Visibility = Visibility.Visible self.root.rawInputField.KeyDown += self.check_for_enter self.root.rawInputField.Focus() @invoke def remove_input_box(self): self.Visibility = Visibility.Visible self.root.rawInput.Visibility = Visibility.Collapsed self.root.rawInputField.KeyDown -= self.check_for_enter self.printer.print_new(self.root.rawInputPrompt.Text + self._raw_input_text) self.Focus() def check_for_enter(self, sender, event): if event.Key == Key.Enter: event.Handled = True self._raw_input_text = self.root.rawInputField.Text self.input_event.Set() def raw_input(self, prompt='Input:'): if not isinstance(prompt, str): prompt = str(prompt) self.input_event = ManualResetEvent(False) self.setup_input_box(prompt) self.input_event.WaitOne() self.remove_input_box() return self._raw_input_text def _handle_lines(self, lines): try: self._input_data = [] for line in lines: self.handle_line(line) if self._input_data: self.handle_line('') self.Dispatcher.BeginInvoke(lambda: self.Focus()) except Exception, e: _debug('Handle lines', e)
class PythonConsole(gtk.TextView, code.InteractiveConsole): nonword_re = re.compile("[^\w\._]") def __init__(self, locals=None, banner=None, completer=None, use_rlcompleter=True, start_script=None): gtk.TextView.__init__(self) if not locals: locals = __main__.__dict__ code.InteractiveInterpreter.__init__(self, locals) #self.locals['__console__'] = self self.banner = banner self.completer = completer if not self.completer and use_rlcompleter: try: import rlcompleter self.completer = rlcompleter.Completer() except ImportError: pass self.start_script = start_script # for debugging #start_script="import gtk\n" + \ #"win = gtk.Window()\n" + \ #"label = gtk.Label('Hello there!')\n" + \ #"win.add(label)\n" + \ #"win.show_all()\n" self.run_on_raw_input = start_script self.connect("key-press-event", self.on_key_press_event) self.set_wrap_mode(gtk.WRAP_CHAR) self.modify_font(pango.FontDescription("Monospace")) self.buffer = self.get_buffer() self.buffer.connect("insert-text", self.on_buf_insert) self.buffer.connect("delete-range", self.on_buf_delete) self.buffer.connect("mark-set", self.on_buf_mark_set) self.do_insert = False self.do_delete = False self.cursor = self.buffer.create_mark("cursor", self.buffer.get_start_iter(), False) insert = self.buffer.get_insert() self.cursor.set_visible(True) insert.set_visible(False) self.ps = '' self.ps1 = ">>> " self.ps2 = "... " self.in_raw_input = False self.tab_pressed = 0 self.history = ConsoleHistory() self.__start() self.raw_input(self.ps1) def clear(self, start_script=None): if start_script is None: start_script = self.start_script else: self.start_script = start_script self.__start() self.run_on_raw_input = start_script def __commit(self): end = self.__get_cursor() if not end.ends_line(): end.forward_to_line_end() text = self.__get_line() self.__move_cursor_to(end) self.freeze_undo() self.__insert(end, "\n") self.in_raw_input = False self.history.commit(text) self.do_raw_input(text) self.thaw_undo() def complete(self, text): return None def __complete(self): text = self.__get_text(self.__get_start(), self.__get_cursor()) start = '' word = text nonwords = self.nonword_re.findall(text) if nonwords: last = text.rfind(nonwords[-1]) + len(nonwords[-1]) start = text[:last] word = text[last:] completions = self.complete(word) if completions: prefix = condensation.core.Util.commonprefix(completions) if prefix != word: start_iter = self.__get_start() start_iter.forward_chars(len(start)) end_iter = start_iter.copy() end_iter.forward_chars(len(word)) self.__delete(start_iter, end_iter) self.__insert(end_iter, prefix) elif self.tab_pressed > 1: self.freeze_undo() self.__print_completions(completions) self.thaw_undo() self.tab_pressed = 0 def complete_attr(self, start, end): try: obj = eval(start, self.locals) strings = dir(obj) if end: completions = {} for s in strings: if s.startswith(end): completions[s] = None completions = completions.keys() else: completions = strings completions.sort() return [start + "." + s for s in completions] except: return None def __delete(self, start, end): self.do_delete = True self.buffer.delete(start, end) self.do_delete = False def __delete_at_cursor(self, howmany): iter = self.__get_cursor() end = self.__get_cursor() if not end.ends_line(): end.forward_to_line_end() line_len = end.get_line_offset() erase_to = iter.get_line_offset() + howmany if erase_to > line_len: erase_to = line_len elif erase_to < len(self.ps): erase_to = len(self.ps) end.set_line_offset(erase_to) self.__delete(iter, end) def do_raw_input(self, text): if self.cmd_buffer: cmd = self.cmd_buffer + "\n" + text else: cmd = text saved = sys.stdout sys.stdout = self if code.InteractiveInterpreter.runsource(self, cmd): self.cmd_buffer = cmd ps = self.ps2 else: self.cmd_buffer = '' ps = self.ps1 self.raw_input(ps) sys.stdout = saved def execfile(self, filename): saved = sys.stdout sys.stdout = self try: execfile(filename, self.locals) except SystemExit: raise except: self.showtraceback() sys.stdout = saved def flush(self): """ Dummy method for stream implementation. """ pass def freeze_undo(self): try: self.begin_not_undoable_action() except: pass def __get_cursor(self): return self.buffer.get_iter_at_mark(self.cursor) def __get_end(self): iter = self.__get_cursor() if not iter.ends_line(): iter.forward_to_line_end() return iter def __get_line(self): start = self.__get_start() end = self.__get_end() return self.buffer.get_text(start, end, False) def __get_start(self): iter = self.__get_cursor() iter.set_line_offset(len(self.ps)) return iter def __get_text(self, start, end): return self.buffer.get_text(start, end, False) def __get_width(self): if not (self.flags() & gtk.REALIZED): return 80 layout = pango.Layout(self.get_pango_context()) letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" layout.set_text(letters) pix_width = layout.get_pixel_size()[0] return self.allocation.width * len(letters) / pix_width def __history(self, dir): text = self.__get_line() new_text = self.history.get(dir, text) if not new_text is None: self.__replace_line(new_text) self.__move_cursor(0) self.scroll_to_mark(self.cursor, 0.2) def __insert(self, iter, text): self.do_insert = True self.buffer.insert(iter, text) self.do_insert = False def on_key_press_event(self, widget, event): if not self.in_raw_input: return False tab_pressed = self.tab_pressed self.tab_pressed = 0 handled = True state = event.state & (gtk.gdk.SHIFT_MASK | gtk.gdk.CONTROL_MASK | gtk.gdk.MOD1_MASK) keyval = event.keyval if not state: if keyval == gtk.keysyms.Return: self.__commit() elif keyval == gtk.keysyms.Up: self.__history(-1) elif keyval == gtk.keysyms.Down: self.__history(1) elif keyval == gtk.keysyms.Left: self.__move_cursor(-1) elif keyval == gtk.keysyms.Right: self.__move_cursor(1) elif keyval == gtk.keysyms.Home: self.__move_cursor(-10000) elif keyval == gtk.keysyms.End: self.__move_cursor(10000) elif keyval == gtk.keysyms.Tab: self.tab_pressed = tab_pressed + 1 self.__complete() else: handled = False elif state == gtk.gdk.CONTROL_MASK: if keyval == gtk.keysyms.u: start = self.__get_start() end = self.__get_cursor() self.__delete(start, end) else: handled = False else: handled = False return handled def thaw_undo(self): try: self.end_not_undoable_action() except: pass def raw_input(self, ps=None): if ps: self.ps = ps else: self.ps = '' iter = self.buffer.get_iter_at_mark(self.buffer.get_insert()) if ps: self.freeze_undo() self.buffer.insert(iter, self.ps) self.thaw_undo() self.__move_cursor_to(iter) self.scroll_to_mark(self.cursor, 0.2) self.in_raw_input = True if self.run_on_raw_input: run_now = self.run_on_raw_input self.run_on_raw_input = None self.buffer.insert_at_cursor(run_now + '\n') def on_buf_mark_set(self, buffer, iter, mark): if not mark is buffer.get_insert(): return start = self.__get_start() end = self.__get_end() if iter.compare(self.__get_start()) >= 0 and \ iter.compare(self.__get_end()) <= 0: buffer.move_mark_by_name("cursor", iter) self.scroll_to_mark(self.cursor, 0.2) def on_buf_insert(self, buf, iter, text, len): if not self.in_raw_input or self.do_insert or not len: return buf.stop_emission("insert-text") lines = text.splitlines() need_eol = False for l in lines: if need_eol: self.__commit() iter = self.__get_cursor() else: cursor = self.__get_cursor() if iter.compare(self.__get_start()) < 0: iter = cursor elif iter.compare(self.__get_end()) > 0: iter = cursor else: self.__move_cursor_to(iter) need_eol = True self.__insert(iter, l) self.__move_cursor(0) def on_buf_delete(self, buf, start, end): if not self.in_raw_input or self.do_delete: return buf.stop_emission("delete-range") start.order(end) line_start = self.__get_start() line_end = self.__get_end() if start.compare(line_end) > 0: return if end.compare(line_start) < 0: return self.__move_cursor(0) if start.compare(line_start) < 0: start = line_start if end.compare(line_end) > 0: end = line_end self.__delete(start, end) def __move_cursor_to(self, iter): self.buffer.place_cursor(iter) self.buffer.move_mark_by_name("cursor", iter) def __move_cursor(self, howmany): iter = self.__get_cursor() end = self.__get_cursor() if not end.ends_line(): end.forward_to_line_end() line_len = end.get_line_offset() move_to = iter.get_line_offset() + howmany move_to = min(max(move_to, len(self.ps)), line_len) iter.set_line_offset(move_to) self.__move_cursor_to(iter) def __print_completions(self, completions): line_start = self.__get_text(self.__get_start(), self.__get_cursor()) line_end = self.__get_text(self.__get_cursor(), self.__get_end()) iter = self.buffer.get_end_iter() self.__move_cursor_to(iter) self.__insert(iter, "\n") width = max(self.__get_width(), 4) max_width = max([len(s) for s in completions]) n_columns = max(int(width / (max_width + 1)), 1) col_width = int(width / n_columns) total = len(completions) col_length = total / n_columns if total % n_columns: col_length = col_length + 1 col_length = max(col_length, 1) if col_length == 1: n_columns = total col_width = width / total for i in range(col_length): for j in range(n_columns): ind = i + j*col_length if ind < total: if j == n_columns - 1: n_spaces = 0 else: n_spaces = col_width - len(completions[ind]) self.__insert(iter, completions[ind] + " " * n_spaces) self.__insert(iter, "\n") self.__insert(iter, "%s%s%s" % (self.ps, line_start, line_end)) iter.set_line_offset(len(self.ps) + len(line_start)) self.__move_cursor_to(iter) self.scroll_to_mark(self.cursor, 0.2) def __replace_line(self, new_text): start = self.__get_start() end = self.__get_end() self.__delete(start, end) self.__insert(end, new_text) def write(self,whatever): self.buffer.insert_at_cursor(whatever) def __start(self): self.cmd_buffer = "" self.freeze_undo() self.thaw_undo() self.buffer.set_text("") if self.banner: iter = self.buffer.get_start_iter() self.buffer.insert(iter, self.banner) if not iter.starts_line(): self.buffer.insert(iter, "\n") def runcode(self, code): saved = sys.stdout sys.stdout = self try: eval(code, self.locals) except SystemExit: raise except: self.showtraceback() sys.stdout = saved def complete(self, text): if self.completer: completions = set() i = 0 try: while 1: s = self.completer.complete(text, i) if s: completions.add(s) i = i + 1 else: completions = list(completions) completions.sort() return completions except NameError: return None dot = text.rfind(".") if dot >= 0: return self.complete_attr(text[:dot], text[dot+1:]) completions = {} strings = keyword.kwlist if self.locals: strings.extend(self.locals.keys()) try: strings.extend(eval("globals()", self.locals).keys()) except: pass try: exec "import __builtin__" in self.locals strings.extend(eval("dir(__builtin__)", self.locals)) except: pass for s in strings: if s.startswith(text): completions[s] = None completions = completions.keys() completions.sort() return completions def tell(self): return 0 def seek(self, pos): pass def truncate(self): raise IOError, "Illegal seek"