diff --git a/application.py b/application.py index 3dfbe42..4c47715 100755 --- a/application.py +++ b/application.py @@ -87,7 +87,7 @@ class Application(object): # 'nasm': mode_nasm.Nasm, 'perl': mode_perl.Perl, 'python': mode_python.Python, -# 'replace': mode_replace.Replace, + 'replace': mode_replace.Replace, 'search': mode_search.Search, # 'sh': mode_sh.Sh, # 'text': mode_text.Text, @@ -211,7 +211,7 @@ class Application(object): assert n >= 0 and n < len(self.bufferlist.slots), \ "invalid slot: %r (%r)" % (n, len(self.bufferlist.slots)) self.bufferlist.remove_slot(n) - if self.active_slot > slotname: + if self.active_slot > n: self.active_slot = max(0, self.active_slot - 1) #XYZ def single_slot(self): while len(self.bufferlist.slots) > 1: diff --git a/bufferlist.py b/bufferlist.py index 17412d3..ee8bcb0 100644 --- a/bufferlist.py +++ b/bufferlist.py @@ -67,16 +67,13 @@ class BufferList: # manipulate slots def add_slot(self): - assert len(self.slots) == 0, "fuck" self.slots.append(Slot(self.height, self.width, 0)) self.fit_slots() return len(self.slots) - 1 def empty_slot(self, i): - assert len(self.slots) == 0, "fuck" assert i > -1 and i < len(self.slots), "slot %d does not exist" % i return self.slots[i].is_empty() def unset_slot(self, i): - assert len(self.slots) == 1, "fuck" assert i > -1 and i < len(self.slots), "slot %d does not exist" % i old_w = self.slots[i].unset() if old_w is not None: @@ -87,7 +84,6 @@ class BufferList: old_b.remove_window(old_w) def set_slot(self, i, b): - assert len(self.slots) == 1, "fuck" assert i > -1 and i < len(self.slots), "slot %d does not exist" % i assert b in self.buffers, "buffer %s does not exist" % (b.name()) slot = self.slots[i] @@ -103,7 +99,6 @@ class BufferList: slot.set(w) def remove_slot(self, i): - assert False, "fuck" assert i > -1 and i < len(self.slots), "slot %d does not exist" % i self.unset_slot(i) del self.slots[i] diff --git a/highlight2.py b/highlight2.py index b8f007a..f4b702e 100644 --- a/highlight2.py +++ b/highlight2.py @@ -160,7 +160,6 @@ class Highlighter: def update_del(self, lines, y1, x1, y2, x2): assert y1 >= 0 assert y1 <= y2 - assert y2 < len(lines) # first let's delete any token who falls in the range of the change (or, # in the case of child tokens, whose parent is being deleted). diff --git a/method.py b/method.py index 777e6d9..e880fcd 100644 --- a/method.py +++ b/method.py @@ -2,6 +2,8 @@ import os, commands, popen2, re import buffer2, default, regex, util, window2 from point2 import Point +WHITESPACE = [' ', '\n'] + DATATYPES = { "path": None, "buffer": None, @@ -220,7 +222,7 @@ class OpenAesFile(Method): if not w.application.has_buffer_name(path): b = buffer2.AesBuffer(path, password) b.open() - window.Window(b, a, height=0, width=0) + window2.Window(b, a, height=0, width=0) w.application.add_buffer(b) SwitchBuffer().execute(w, buffername=path) class SwitchBuffer(Method): @@ -261,6 +263,10 @@ class KillBuffer(Method): b.close() class ForceKillBuffer(KillBuffer): force=True + def _args(self): + return [Argument('buffername', datatype="buffer", + prompt="Force Kill Buffer: ", + default=default.current_buffer)] class ListBuffers(Method): '''List all open buffers in a new buffer''' def _execute(self, w, **vargs): @@ -284,8 +290,11 @@ class SaveBufferAs(Method): w.application.set_error("buffer for %r is already open" % path) return w.application.file_buffer(path, data, switch_to=True) - w.application.methods['kill-buffer'].execute(w, buffername=curr_buffer_name) - #w.application.set_error('Wrote %r' % path) + if curr_buffer_name != '*Scratch*': + w.application.methods['kill-buffer'].execute(w, buffername=curr_buffer_name) + else: + curr_buffer.set_data('') + w.application.set_error('Wrote %r' % path) class SaveBuffer(Method): '''Save the contents of a buffer''' def _execute(self, w, **vargs): @@ -421,30 +430,28 @@ class DeleteRightWord(Method): class DeleteLeftWhitespace(Method): '''Delete all contiguous of whitespace left of the cursor''' def _execute(self, w, **vargs): - cursor = w.logical_cursor() - cursor_offset = w.get_cursor_offset() - left_offset = cursor_offset - 1 - s = w.buffer.make_string() - while left_offset >= 0 and (s[left_offset] == ' ' or - s[left_offset] == '\n'): - left_offset -= 1 - left_offset += 1 - if left_offset < cursor_offset - 1: - left_point = w.buffer.get_offset_point(left_offset) - w.kill(left_point, cursor) + c = w.logical_cursor() + p = c + l = w.point_left(p) + if l is None: + return + while l is not None and w.point_char(l) in WHITESPACE: + p = l + l = w.point_left(p) + if p < c: + w.kill(p, c) class DeleteRightWhitespace(Method): '''Delete all contiguous of whitespace under and right of the cursor''' def _execute(self, w, **vargs): - cursor = w.logical_cursor() - cursor_offset = w.get_cursor_offset() - right_offset = cursor_offset - s = w.buffer.make_string() - while right_offset < len(s) and (s[right_offset] == ' ' or - s[right_offset] == '\n'): - right_offset += 1 - if right_offset > cursor_offset: - right_point = w.buffer.get_offset_point(right_offset) - w.kill(cursor, right_point) + c = w.logical_cursor() + p = c + while w.point_char(p) in WHITESPACE: + r = w.point_right(p) + if r is None: + break + p = r + if p > c: + w.kill(c, p) # random stuff class DumpTokens(Method): @@ -487,77 +494,78 @@ class ToggleMargins(Method): a.margins_visible = not a.margins_visible class CenterView(Method): '''Move view to center on cursor''' - def _execute(self, window, **vargs): - window.center_view() + def _execute(self, w, **vargs): + w.center_view() class SetMark(Method): '''Set the mark to the current cursor location''' - def _execute(self, window, **vargs): - window.set_mark() + def _execute(self, w, **vargs): + w.set_mark() class SwitchMark(Method): '''Switch the mark and the cursor locations''' - def _execute(self, window, **vargs): - window.switch_mark() + def _execute(self, w, **vargs): + w.switch_mark() # insertion methods class InsertNewline(Method): '''Insert newline into buffer at the cursor''' - def _execute(self, window, **vargs): - window.insert_string_at_cursor('\n') + def _execute(self, w, **vargs): + w.insert_string_at_cursor('\n') class InsertSpace(Method): '''Insert space into buffer at the cursor''' - def _execute(self, window, **vargs): - window.insert_string_at_cursor(' ') + def _execute(self, w, **vargs): + w.insert_string_at_cursor(' ') class InsertTab(Method): '''Insert tab into buffer, or tabbify line, depending on mode''' - def _execute(self, window, **vargs): - cursor = window.logical_cursor() - #i = window.mode.get_indentation_level(cursor.y) + #XYZ + def _execute(self, w, **vargs): + cursor = w.logical_cursor() + #i = w.mode.get_indentation_level(cursor.y) i = None if i is None: - window.insert_string_at_cursor(' ') + w.insert_string_at_cursor(' ') else: - j = window.buffer.count_leading_whitespace(cursor.y) + j = w.buffer.count_leading_whitespace(cursor.y) if i != j: - KillWhitespace().execute(window) - window.insert(Point(0, cursor.y), ' ' * i) + KillWhitespace().execute(w) + w.insert(Point(0, cursor.y), ' ' * i) else: - window.goto(Point(j, cursor.y)) + w.goto(Point(j, cursor.y)) class KillWhitespace(Method): '''Delete leading whitespace on current line''' - def _execute(self, window, **vargs): - cursor = window.logical_cursor() - i = window.buffer.count_leading_whitespace(cursor.y) + def _execute(self, w, **vargs): + cursor = w.logical_cursor() + i = w.buffer.count_leading_whitespace(cursor.y) if i > 0: - window.kill(Point(0, cursor.y), + w.kill(Point(0, cursor.y), Point(i, cursor.y)) # tabification class TabBuffer(Method): '''Tabbify every line in the current buffer''' - def _execute(self, window, **vargs): - y = window.logical_cursor().y + def _execute(self, w, **vargs): + y = w.logical_cursor().y it = InsertTab() - for i in range(0, len(window.buffer.lines)): - window.goto_line(i) - it.execute(window) - window.goto_line(y) + for i in range(0, len(w.buffer.lines)): + w.goto_line(i) + it.execute(w) + w.goto_line(y) # commenting class CommentRegion(Method): '''Prepend a comment to every line in the current buffer''' - def _execute(self, window, **vargs): - cursor = window.logical_cursor() - if cursor < window.mark: + def _execute(self, w, **vargs): + cursor = w.logical_cursor() + if cursor < w.mark: p1 = cursor - p2 = window.mark - elif window.mark < cursor: - p1 = window.mark + p2 = w.mark + elif w.mark < cursor: + p1 = w.mark p2 = cursor else: - window.input_line = "Empty kill region" + w.input_line = "Empty kill region" return for y in range(p1.y, p2.y): - window.buffer.insert_string_at_cursor(Point(0, y), "#") + w.buffer.insert_string_at_cursor(Point(0, y), "#") class UncommentRegion(Method): '''Remove a comment from every line in the current buffer''' def _execute(self, w, **vargs): @@ -579,33 +587,33 @@ class UncommentRegion(Method): class WrapLine(Method): '''Wrap the current line at 80 characters by word''' limit = 80 - def _execute(self, window, **vargs): - cursor = window.logical_cursor() + def _execute(self, w, **vargs): + cursor = w.logical_cursor() old_cursor = cursor.copy() i = cursor.y move_old_cursor = old_cursor.x > self.limit - while len(window.buffer.lines[i]) > self.limit: - if ' ' in window.buffer.lines[i][:self.limit]: - j = window.buffer.lines[i][:self.limit].rindex(' ') - elif ' ' in window.buffer.lines[i][self.limit:]: - j = window.buffer.lines[i][self.limit:].index(' ') + while len(w.buffer.lines[i]) > self.limit: + if ' ' in w.buffer.lines[i][:self.limit]: + j = w.buffer.lines[i][:self.limit].rindex(' ') + elif ' ' in w.buffer.lines[i][self.limit:]: + j = w.buffer.lines[i][self.limit:].index(' ') else: break if move_old_cursor: move_old_cursor = False old_cursor.x -= j + 1 old_cursor.y += 1 - window.goto(Point(j, i)) - window.right_delete() - window.insert_string_at_cursor('\n') + w.goto(Point(j, i)) + w.right_delete() + w.insert_string_at_cursor('\n') i += 1 - l = len(window.buffer.lines[old_cursor.y]) + l = len(w.buffer.lines[old_cursor.y]) if l > old_cursor.x: - window.goto(old_cursor) + w.goto(old_cursor) else: - window.goto(Point(l, old_cursor.y)) + w.goto(Point(l, old_cursor.y)) class WrapParagraph(Method): limit = 80 wrapper = WrapLine @@ -624,11 +632,11 @@ class WrapParagraph(Method): class JustifyRight(Method): '''Justify text with the previous line right from the cursor by whitespace''' - def _execute(self, window, **vargs): - DeleteLeftWhitespace().execute(window) - cursor = window.logical_cursor() - prev_line = window.buffer.lines[cursor.y-1] - this_line = window.buffer.lines[cursor.y] + def _execute(self, w, **vargs): + DeleteLeftWhitespace().execute(w) + cursor = w.logical_cursor() + prev_line = w.buffer.lines[cursor.y-1] + this_line = w.buffer.lines[cursor.y] if cursor.y <= 0: return if cursor.x >= len(prev_line): @@ -643,14 +651,14 @@ class JustifyRight(Method): if i >= len(prev_line): return s = ' ' * (i - cursor.x) - window.insert_string_at_cursor(s) + w.insert_string_at_cursor(s) class JustifyLeft(Method): '''Justify text with the previous line left from the cursor by whitespace''' - def _execute(self, window, **vargs): - DeleteRightWhitespace().execute(window) - cursor = window.logical_cursor() - prev_line = window.buffer.lines[cursor.y-1] - this_line = window.buffer.lines[cursor.y] + def _execute(self, w, **vargs): + DeleteRightWhitespace().execute(w) + cursor = w.logical_cursor() + prev_line = w.buffer.lines[cursor.y-1] + this_line = w.buffer.lines[cursor.y] if cursor.y <= 0: return if cursor.x <= 0: @@ -674,73 +682,73 @@ class JustifyLeft(Method): return if this_line[i] != ' ': return - window.buffer.delete_string(Point(i, cursor.y), cursor) + w.buffer.delete_string(Point(i, cursor.y), cursor) # undo/redo class Undo(Method): '''Undo last action''' - def _execute(self, window, **vargs): + def _execute(self, w, **vargs): try: - window.buffer.undo() + w.buffer.undo() except Exception, e: - window.application.set_error("%s" % (e)) + w.application.set_error("%s" % (e)) class Redo(Method): '''Redo last undone action''' - def _execute(self, window, **vargs): + def _execute(self, w, **vargs): try: - window.buffer.redo() + w.buffer.redo() except Exception, e: - window.application.set_error("%s" % (e)) + w.application.set_error("%s" % (e)) -# window navigation methods +# w navigation methods class StartOfLine(Method): '''Move the cursor to the start of the current line''' - def _execute(self, window, **vargs): - window.start_of_line() + def _execute(self, w, **vargs): + w.start_of_line() class EndOfLine(Method): '''Move the cursor to the end of the current line''' - def _execute(self, window, **vargs): - window.end_of_line() + def _execute(self, w, **vargs): + w.end_of_line() class Forward(Method): '''Move the cursor right one character''' - def _execute(self, window, **vargs): - window.forward() + def _execute(self, w, **vargs): + w.forward() class Backward(Method): '''Move the cursor left one character''' - def _execute(self, window, **vargs): - window.backward() + def _execute(self, w, **vargs): + w.backward() class NextLine(Method): '''Move the cursor down one line''' - def _execute(self, window, **vargs): - window.next_line() + def _execute(self, w, **vargs): + w.next_line() class PreviousLine(Method): '''Move the cursor up one line''' - def _execute(self, window, **vargs): - window.previous_line() + def _execute(self, w, **vargs): + w.previous_line() class PageUp(Method): '''Move the cursor up one page''' - def _execute(self, window, **vargs): - window.page_up() + def _execute(self, w, **vargs): + w.page_up() class PageDown(Method): '''Move the cursor down one page''' - def _execute(self, window, **vargs): - window.page_down() + def _execute(self, w, **vargs): + w.page_down() class GotoBeginning(Method): '''Move the cursor to the beginning of the buffer''' - def _execute(self, window, **vargs): - window.goto_beginning() + def _execute(self, w, **vargs): + w.goto_beginning() class GotoEnd(Method): '''Move the cursor to the end of the buffer''' - def _execute(self, window, **vargs): - window.goto_end() + def _execute(self, w, **vargs): + w.goto_end() class RightWord(Method): '''Move the cursor to the start of the word to the right''' - def _execute(self, window, **vargs): - window.right_word() + def _execute(self, w, **vargs): + w.right_word() class LeftWord(Method): '''Move the cursor to the start of the word to the left''' - def _execute(self, window, **vargs): - window.left_word() + def _execute(self, w, **vargs): + w.left_word() class NextSection(Method): '''Move the cursor to the next section''' def _execute(self, w, **vargs): @@ -862,7 +870,7 @@ class OpenConsole(Method): if not a.has_buffer_name('*Console*'): a.add_buffer(buffer2.ConsoleBuffer()) b = a.bufferlist.get_buffer_by_name('*Console*') - if a.window().buffer is not b: + if a.w().buffer is not b: a.switch_buffer(b) f = lambda x: None w.application.open_mini_buffer('>>> ', f, self, None, 'console') diff --git a/mode_replace.py b/mode_replace.py index c7c6494..20cc240 100644 --- a/mode_replace.py +++ b/mode_replace.py @@ -11,13 +11,14 @@ class Replace(mode2.Fundamental): self.actions = {} self.bindings = {} - default_actions = ((ReplaceAll(), ('a', '!',)), - (ReplaceOne(), ('y', 'SPACE',)), - (SkipReplace(), ('n', 'DELETE',)), - (CancelReplace(), ('q', 'RETURN', 'C-]', 'C-n', - 'C-p', 'C-a', 'C-e', 'C-f', - 'C-b'))) - + default_actions = ( + (ReplaceAll(), ('a', '!',)), + (ReplaceOne(), ('y', 'SPACE',)), + (SkipReplace(), ('n', 'DELETE',)), + (CancelReplace(), ('q', 'RETURN', 'C-]', 'C-n', 'C-p', 'C-a', 'C-e', + 'C-f', 'C-b')), + ) + # add the replace actions for pair in default_actions: (action, sequences) = pair @@ -31,51 +32,55 @@ class Replace(mode2.Fundamental): return "Replace" class ReplaceOne(method.Method): - def execute(self, window, **vargs): - m = window.buffer.method + def execute(self, w, **vargs): + m = w.buffer.method old_window = m.old_window _replace(m, old_window) - _find_next(window, True) - _finish(m, window, old_window) + _find_next(w, True) + _finish(m, w, old_window) class SkipReplace(method.Method): - def execute(self, window, **vargs): - m = window.buffer.method + def execute(self, w, **vargs): + m = w.buffer.method old_window = m.old_window - _find_next(window, True) - _finish(m, window, old_window) + _find_next(w, True) + _finish(m, w, old_window) class ReplaceAll(method.Method): - def execute(self, window, **vargs): - m = window.buffer.method + def execute(self, w, **vargs): + m = w.buffer.method old_window = m.old_window while m.p1 is not None: _replace(m, old_window) - _find_next(window, True) - _end(window) - window.application.set_error("Replace ended") + _find_next(w, True) + _end(w) + w.application.set_error("Replace ended") class CancelReplace(method.Method): - def execute(self, window, **vargs): - _end(window) - window.application.set_error("Replace cancelled") + def execute(self, w, **vargs): + _end(w) + w.application.set_error("Replace cancelled") -def _set_prompt(m, window): - i = m.old_window.buffer.get_point_offset(m.p1) - s = m.old_window.buffer.make_string() - count = s[i:].count(m.before) +def _set_prompt(m, w): + (x, y) = m.p1.xy() + count = 0 + while y < len(w.buffer.lines): + count += w.buffer.lines[y][x:].count(m.before) + y += 1 + x = 0 if count > 1: - window.application.mini_prompt = 'Replace %r with %r [ynaq] (%d occurances)?' % (m.before, m.after, count) + p = 'Replace %r with %r [ynaq] (%d occurances)?' % (m.before, m.after, count) else: - window.application.mini_prompt = 'Replace %r with %r [ynaq] (1 occurance)?' % (m.before, m.after) + p = 'Replace %r with %r [ynaq] (1 occurance)?' % (m.before, m.after) + w.application.mini_prompt = p def _replace(m, old_window): - old_window.buffer.delete_string(m.p1, m.p2) + old_window.buffer.delete(m.p1, m.p2) if m.after: old_window.buffer.insert_string(m.p1, m.after) -def _find_next(window, move=False): - m = window.buffer.method +def _find_next(w, move=False): + m = w.buffer.method old_window = m.old_window b = old_window.buffer s = m.before @@ -111,16 +116,15 @@ def _find_next(window, move=False): m.p1 = None m.p2 = None -def _finish(m, window, old_window): +def _finish(m, w, old_window): if m.p1 is None: - _end(window) - window.application.set_error("Replace ended") + _end(w) + w.application.set_error("Replace ended") else: _set_prompt(m, old_window) -def _end(window): - window.application.close_mini_buffer() - #window.application.highlighted_range = [] - window.application.clear_highlighted_ranges() - window.buffer.method.old_cursor = None - window.buffer.method.old_window = None +def _end(w): + w.application.close_mini_buffer() + w.application.clear_highlighted_ranges() + w.buffer.method.old_cursor = None + w.buffer.method.old_window = None diff --git a/window2.py b/window2.py index 1846a52..bcdca12 100644 --- a/window2.py +++ b/window2.py @@ -4,12 +4,11 @@ from point2 import Point WORD_LETTERS = list(string.letters + string.digits) -# note about the cursor: the cursor position will insert in front of -# the character it highlights. to this end, it needs to be able to -# highlight behind the last character on a line. thus, the x -# coordinate of the (logical) cursor can equal the length of lines[y], -# even though lines[y][x] throws an index error. both buffer and -# window need to be aware of this possibility for points. +# note about the cursor: the cursor position will insert in front of the +# character it highlights. to this end, it needs to be able to highlight behind +# the last character on a line. thus, the x coordinate of the (logical) cursor +# can equal the length of lines[y], even though lines[y][x] throws an index +# error. both buffer and window need to be aware of this possibility for points. class Window(object): def __init__(self, b, a, height=24, width=80, mode_name=None): @@ -85,9 +84,31 @@ class Window(object): if not self.point_is_visible(p): self.application.set_error(msg % {'x': p.x, 'y': p.y}) + # point left + def point_left(self, p): + if p.y == 0 and p.x == 0: + return None + elif p.x == 0: + return Point(len(self.buffer.lines[p.y - 1]), p.y - 1) + else: + return Point(p.x - 1, p.y) + + # point right + def point_right(self, p): + if p.y == len(self.buffer.lines)-1 and p.x == len(self.buffer.lines[-1]): + return None + elif p.x == len(self.buffer.lines[p.y]): + return Point(0, p.y + 1) + else: + return Point(p.x + 1, p.y) + # cursors def logical_cursor(self): - x = min(self.cursor.x, len(self.buffer.lines[self.cursor.y])) + if len(self.buffer.lines) > self.cursor.y: + l = len(self.buffer.lines[self.cursor.y]) + else: + l = 0 + x = min(self.cursor.x, l) return Point(x, self.cursor.y) # last visible point @@ -454,17 +475,20 @@ class Window(object): return self.application.pop_kill() # querying - def highlighted_char(self): + def cursor_char(self): self.point_char(self.logical_cursor()) def point_char(self, p): return self.xy_char(p.x, p.y) def xy_char(self, x, y): + #assert 0 <= y and y < len(self.buffer.lines) + #assert 0 <= x and x <= len(self.buffer.lines[y]) if x == len(self.buffer.lines[y]): return "\n" else: return self.buffer.lines[y][x] # undo/redo + # XYZ def undo(self): # TODO: put something here to move the cursor to the area of the undo self.buffer.undo()