def __init__( self, edit_text="", multiline=True, align="left", wrap="space", allow_tab=True, edit_pos=None, layout=None, lexer=None, formatter=None, ): if lexer is not None: self.lexer = lexer else: self.lexer = pygments.lexers.get_lexer_by_name("python") if formatter is not None: self.formatter = formatter else: self.formatter = UrwidFormatter() # note: captions not allowed widget.Edit.__init__(self, "", edit_text, multiline, align, wrap, allow_tab, edit_pos, layout) if edit_text: self.colorize() self.enterpressed = False
def __init__(self, inputlines=4, caption='>>> '): # make inner widgets # first the input widgets # the input widgets are a 'captionwidget' for the prompt, next # to the inputwidget. # this means that the prompt is completely uneditable without # trying, and also takes care of indenting past the # prompt self.formatter = UrwidFormatter() self.lexer = pygments.lexers.get_lexer_by_name('python', stripall='True', ensurenl='False') self.errlexer = pygments.lexers.get_lexer_by_name('pytb', stripall='True', ensurenl='False') self.inputbox = PromptPyEdit(multiline=True,lexer = self.lexer, formatter = self.formatter) self.inputwidget = urwid.Filler(self.inputbox, valign='top') # the completion box self.completionbox = TextGrid() # the 'upper' box, which can be switched to completions, help, etc. self.upperbox = UpperBox(self.completionbox) # now the output widgets self.outputbox = OutputBox() # Box widget self.outputwidget = self.outputbox #self.outputwidget = urwid.Filler(self.outputbox, valign='top') # Box widget # now initialize as a pile urwid.Pile.__init__(self, [ ('flow', self.upperbox), self.outputwidget, ('fixed', inputlines, self.inputwidget)] ) self.set_focus(self.inputwidget)
def enable_syntax_coloring(self): debug("INITIALIZING SYNTAX COLORED WIDGET") walker = urwid.SimpleListWalker([]) self.previous_widget = self.window.original_widget listbox = TextBox(walker) self.window.original_widget = listbox self.syntax_colored = True focused_index = self.get_focus_index(self.previous_widget) formatter = UrwidFormatter(style=PYGMENTS_STYLE) def handle_token(token, formatted_line, diff=False): text = token[1] if not text: return if text.find('\n') >= 0: split_line = clear_escape_codes(text) while split_line: n = split_line.find('\n') if n >= 0: last_word = split_line[:n] split_line = split_line[n+1:] formatted_line.append(last_word) if diff: walker.append(DiffLine(list(formatted_line))) else: walker.append(urwid.Text(list(formatted_line))) del formatted_line[:] else: formatted_line.append((token[0], split_line)) break else: token = (token[0], clear_escape_codes(token[1])) formatted_line.append(token) # end of handle_token function def add_diff_lines_to_walker(ret, index, walker, clear_walker=True, cb=None, fname=None): if clear_walker: walker[:] = [ ] wlines = [] # stupid \n ending required... iterator = itertools.count(index) for index in iterator: if index >= len(ret['lines']) or self.quit: break line = clear_escape_codes(ret['lines'][index]) if line.startswith("diff --git"): diff_index = index commit_lines = [ line ] def add_line(): commit_lines.append(clear_escape_codes(ret['lines'][iterator.next()])) return 1 # doh. even though iterator is consuming it, we need this for later index += add_line() # Author index += add_line() # Date index += add_line() # Blah index += 1 # Look upwards for the commit line (find the first line that starts with Author and Date) # and put them in commit_lines author_index = None for windex, wline in enumerate(wlines): if wline.startswith("Author:"): author_index = windex if author_index: commit_lines = wlines[author_index-1:] + commit_lines wlines = wlines[:author_index-1] if wlines: debug("ADDING SYNTAX LINES", wlines, self.fname) add_lines_to_walker(wlines, walker, self.fname, diff=True) if commit_lines: debug("ADDING COMMIT LINES", commit_lines, self.fname) if not clear_walker and author_index: walker.append(urwid.Text("")) add_lines_to_walker(commit_lines, walker, None, skip_colors=True, diff=True) # next fname output self.fname = line.split().pop() debug("SETTING FNAME TO", self.fname) def future_call(index, walker): self.update_pager() self.ret['syntax_lines'] = index add_diff_lines_to_walker(ret, index, walker, clear_walker=False, cb=cb) thread = threading.Thread(target=future_call, args=(index, walker)) time.sleep(0.001) if not self.quit: thread.start() return else: wlines.append(line) if wlines: # When we make it to the way end, put the last file contents in add_lines_to_walker(wlines, walker, self.fname, diff=True) if cb: cb() self.ret['syntax_lines'] = index # This is when we are finally done. (For reals) self.update_pager() def add_lines_to_walker(lines, walker, fname=None, diff=False, skip_colors=False): if len(lines): lexer = None forced = False if not fname and skip_colors: debug("LINES BEFORE LEXER", lines) debug("SKIPPING COLORING", fname, diff) lines = self.escape_ansi_colors([line.rstrip() for line in lines]) self.syntax_lang = "None" walker.extend(lines) return output = "".join(lines) try: forced = True if not fname in _lexer_fname_cache: _lexer_fname_cache[fname] = pygments.lexers.get_lexer_for_filename(fname) lexer = _lexer_fname_cache[fname] except: pass if not lexer: try: lexer = guess_lexer(output) except Exception, e: debug("EXCEPTION", e); lexer = pygments.lexers.TextLexer() if diff and forced: self.syntax_lang = "git diff" debug("LEXER (FORCED) ", lexer) else: score = lexer.__class__.analyse_text(output) self.syntax_lang = lexer.name debug("LEXER (TRIED: %s) and (GUESSED) SCORE" % (fname), lexer, score) if score < 0.3: # COULDNT FIGURE OUT A GOOD SYNTAX HIGHLIGHTER # DISABLE IT lexer = pygments.lexers.get_lexer_by_name('text') self.syntax_lang = "none. (Couldn't auto-detect a syntax)" lines = self.escape_ansi_colors([line.rstrip() for line in lines], self.syntax_colored) walker.extend(lines) return if lexer.__class__ is pygments.lexers.TextLexer: debug("TEXT LEXER! DISABLING") lines = self.escape_ansi_colors(["%s" % line.rstrip() for line in lines], self.syntax_colored) walker.extend(lines) return tokens = lexer.get_tokens(output) # Build the syntax output up line by line, so that it can be highlighted # one line at a time formatted_tokens = list(formatter.formatgenerator(tokens)) formatted_line = [] for token in formatted_tokens: handle_token(token, formatted_line, diff) if formatted_line: walker.append(urwid.Text(list(formatted_line)))
class PythonEdit(widget.Edit): # flow """An editbox that colorizes python code. A FLOW widget.""" def __init__( self, edit_text="", multiline=True, align="left", wrap="space", allow_tab=True, edit_pos=None, layout=None, lexer=None, formatter=None, ): if lexer is not None: self.lexer = lexer else: self.lexer = pygments.lexers.get_lexer_by_name("python") if formatter is not None: self.formatter = formatter else: self.formatter = UrwidFormatter() # note: captions not allowed widget.Edit.__init__(self, "", edit_text, multiline, align, wrap, allow_tab, edit_pos, layout) if edit_text: self.colorize() self.enterpressed = False def handlekey(self, size, key): """Extra magic for a python editor""" # deal with double enter - return it if key == "enter" and self.multiline and not self.enterpressed: self.enterpressed = True return urwid.Edit.keypress(self, size, key) elif key == "enter" and self.multiline and self.enterpressed: return key else: self.enterpressed = False return widget.Edit.keypress(self, size, key) def keypress(self, size, key): """Handles colorpress""" retval = self.handlekey(size, key) self.colorize() return retval @property def markup(self): return self.get_text() def colorize(self): text = self.edit_text assert hasattr(self, "formatter") assert hasattr(self, "lexer") tkns = self.lexer.get_tokens(text) markup = list(self.formatter.formatgenerator(tkns)) widget.Text.set_text(self, markup)
class InterpreterWidget(urwid.Pile): """A widget that looks like an interpreter. Note that this is simply a widget, and has no extra functionality; it exists to organize the inner widgets.""" def __init__(self, inputlines=4, caption='>>> '): # make inner widgets # first the input widgets # the input widgets are a 'captionwidget' for the prompt, next # to the inputwidget. # this means that the prompt is completely uneditable without # trying, and also takes care of indenting past the # prompt self.formatter = UrwidFormatter() self.lexer = pygments.lexers.get_lexer_by_name('python', stripall='True', ensurenl='False') self.errlexer = pygments.lexers.get_lexer_by_name('pytb', stripall='True', ensurenl='False') self.inputbox = PromptPyEdit(multiline=True,lexer = self.lexer, formatter = self.formatter) self.inputwidget = urwid.Filler(self.inputbox, valign='top') # the completion box self.completionbox = TextGrid() # the 'upper' box, which can be switched to completions, help, etc. self.upperbox = UpperBox(self.completionbox) # now the output widgets self.outputbox = OutputBox() # Box widget self.outputwidget = self.outputbox #self.outputwidget = urwid.Filler(self.outputbox, valign='top') # Box widget # now initialize as a pile urwid.Pile.__init__(self, [ ('flow', self.upperbox), self.outputwidget, ('fixed', inputlines, self.inputwidget)] ) self.set_focus(self.inputwidget) def set_input_caption(self, caption): self.inputbox.set_prompt(caption) def highlight(self, txt): tkns = self.lexer.get_tokens(txt) return list(self.formatter.formatgenerator(tkns)) def highlight_err(self, txt): tkns = self.errlexer.get_tokens(txt) return list(self.formatter.formatgenerator(tkns)) @property def input_text(self): return self.inputbox.text @input_text.setter def input_text(self, newtxt): self.inputbox.text = u'' def set_style(self, s): if isinstance(s, basestring): s = pygments.styles.get_style_by_name(s) self.formatter.style = s def add_to_output(self, markup): self.outputbox.add_stdout(markup) return def _get_widget_size(self, widget, selfsize): item_rows = None if len(selfsize)==2: # Box widget item_rows = self.get_item_rows( selfsize, focus=True ) i = self.widget_list.index(widget) return self.get_item_size(selfsize,i,True,item_rows) def _passkey(self, widget, size, key): """Pass the key along to an inner widget.""" tsize = self._get_widget_size(widget, size) key = widget.keypress( tsize, key ) return key def keypress(self, size, key): """We do not want the normal urwid.Pile keypress stuff to happen...""" # let the focus item use the key, if it can... if (self.focus_item.selectable() and self.focus_item is not self.outputwidget): key = self._passkey(self.focus_item, size, key) # try it on the outputbox origkey = key if key in ('up','page up', 'home'): self.outputbox.jumptobottom = False key = None key = self._passkey(self.outputwidget, size, origkey) tsize = self._get_widget_size(self.outputwidget, size) if (origkey in ('down', 'page down', 'end') and self.outputbox.atbottom(tsize)): self.outputbox.jumptobottom = True key = None if key == 'ctrl k': # switch focus widget if self.focus_item is self.outputwidget: self.set_focus(self.inputwidget) else: self.set_focus(self.outputwidget) return return key