import urwid class LineWalker: """ListWalker-compatible class for lazily reading file contents.""" def __init__(self, name): self.file = open(name) self.lines = [] self.focus = 0 def get_focus(self): return self._get_at_pos( self.focus ) def set_focus(self, focus): self.focus = focus def get_next(self, start_from): return self._get_at_pos( start_from + 1 ) def get_prev(self, start_from): return self._get_at_pos( start_from - 1 ) def read_next_line(self): """Read another line from the file.""" next_line = self.file.readline() if not next_line or next_line[-1:] != '\n': # no newline on last line of file self.file = None else: # trim newline characters next_line = next_line[:-1] expanded = next_line.expandtabs() edit = urwid.Edit( "", expanded, allow_tab=True ) edit.set_edit_pos(0) edit.original_text = next_line self.lines.append( edit ) return next_line def _get_at_pos(self, pos): """Return a widget for the line number passed.""" if pos < 0: # line 0 is the start of the file, no more above return None, None if len(self.lines) > pos: # we have that line so return it return self.lines[pos], pos if self.file is None: # file is closed, so there are no more lines return None, None assert pos == len(self.lines), "out of order request?" self.read_next_line() return self.lines[-1], pos def split_focus(self): """Divide the focus edit widget at the cursor location.""" focus = self.lines[self.focus] pos = focus.edit_pos edit = urwid.Edit("",focus.edit_text[pos:], allow_tab=True ) edit.original_text = "" focus.set_edit_text( focus.edit_text[:pos] ) edit.set_edit_pos(0) self.lines.insert( self.focus+1, edit ) def combine_focus_with_prev(self): """Combine the focus edit widget with the one above.""" above, ignore = self.get_prev(self.focus) if above is None: # already at the top return focus = self.lines[self.focus] above.set_edit_pos(len(above.edit_text)) above.set_edit_text( above.edit_text + focus.edit_text ) del self.lines[self.focus] self.focus -= 1 def combine_focus_with_next(self): """Combine the focus edit widget with the one below.""" below, ignore = self.get_next(self.focus) if below is None: # already at bottom return focus = self.lines[self.focus] focus.set_edit_text( focus.edit_text + below.edit_text ) del self.lines[self.focus+1]