diff --git a/application.py b/application.py index aa8d3c1..2bbeec3 100755 --- a/application.py +++ b/application.py @@ -141,8 +141,9 @@ class Application(object): width = self.x # buffer list stuff - self.bufferlist = bufferlist.BufferList(height, width) - self.active_slot = 0 + self.bufferlist = bufferlist.BufferList(height, width) + self.active_slot = 0 + self.complete_slot = None # run user custom code here self.rcerror = None @@ -199,6 +200,70 @@ class Application(object): curses.nonl() curses.def_prog_mode() + def completion_window_is_open(self): + n = self.complete_slot + if n is None: + pass + elif n >= len(self.bufferlist.slots): + self.complete_slot = None + elif self.bufferlist.slots[n].window is None: + self.complete_slot = None + elif not hasattr(self.bufferlist.slots[n].window.buffer, '_completion'): + self.complete_slot = None + else: + return True + return False + + def get_completion_window(self): + return self.bufferlist.slots[self.complete_slot].window + + def open_completion_buffer(self, s, candidates): + opened = False + previous = None + if len(self.bufferlist.slots) == 1: + self.add_slot() + opened = True + + n = len(self.bufferlist.slots) - 1 + if self.active_slot == n: + n -= 1 + + if not opened: + previous = self.bufferlist.slots[n].window.buffer + + lines = ["This is a completion buffer:", ""] + if len(candidates) > self.bufferlist.slots[n].height: + m = len(candidates) - (len(candidates) // 2) + mlen = 0 + for c in candidates[:m]: + mlen = max(mlen, len(c)) + for i in range(0, m): + if m + i < len(candidates): + c1, c2 = candidates[i * 2], candidates[i * 2 + 1] + lines.append("%-*s %-s" % (mlen, c1, c2)) + else: + lines.append(candidates[i * 2]) + else: + lines = list(candidates) + data = '\n'.join(lines) + + b = self.data_buffer("*Completions*", data, switch_to=False) + b._completion = s + b._opened = opened + b._previous = previous + self.bufferlist.set_slot(n, b) + self.complete_slot = n + + def close_completion_buffer(self): + w = self.get_completion_window() + opened = w.buffer._opened + if opened: + self.bufferlist.remove_slot(self.complete_slot) + else: + self.bufferlist.set_slot(self.complete_slot, w.buffer._previous) + self.close_buffer(w.buffer) + self.complete_slot = None + def set_completer(self, datatype, completer): method.DATATYPES[datatype] = completer @@ -246,6 +311,17 @@ class Application(object): return (slot.height, slot.width) # files and stuff + def close_buffer(self, b): + self.bufferlist.remove_buffer(b) + b.close() + active_slot = self.bufferlist.slots[self.active_slot] + for i in range(0, len(self.bufferlist.slots)): + if self.bufferlist.slots[i].is_empty(): + if self.bufferlist.hidden_buffers: + self.bufferlist.set_slot(i, self.bufferlist.hidden_buffers[0]) + else: + self.bufferlist.set_slot(i, active_slot.window.buffer) + def open_path(self, path, binary=False, cipher=None, password=None): path = os.path.abspath(os.path.realpath(util.expand_tilde(path))) b = self.get_buffer_by_path(path) @@ -369,6 +445,7 @@ class Application(object): self.add_buffer(b) if switch_to: self.switch_buffer(b) + return b def color_data_buffer(self, name, data, switch_to=True, modename='colortext'): if self.has_buffer_name(name): b = self.bufferlist.buffer_names[name] diff --git a/bufferlist.py b/bufferlist.py index 4a4ebf0..a598edb 100644 --- a/bufferlist.py +++ b/bufferlist.py @@ -125,6 +125,11 @@ class BufferList(object): if hasattr(b, 'path') and b.path == path: return b return None + def close_buffer(self, b): + for i in range(0, len(self.slots)): + slot = self.slots[i] + if self.slots[i].window is not None and self.slots[i].window.buffer is b: + self.unset_slot(i) def remove_buffer(self, b): assert b in self.buffers, "buffer %s does not exist" % (b.name()) for i in range(0, len(self.slots)): diff --git a/method/__init__.py b/method/__init__.py index c03ba5b..247dbaa 100644 --- a/method/__init__.py +++ b/method/__init__.py @@ -809,6 +809,8 @@ class Cancel(Method): '''Cancel command in-progress, and return to the main buffer''' def execute(self, w, **vargs): w.application.close_mini_buffer() + if w.application.completion_window_is_open(): + w.application.close_completion_buffer() w.set_error('Cancel') class SplitWindow(Method): diff --git a/method/buffers.py b/method/buffers.py index cb6f498..293ba60 100644 --- a/method/buffers.py +++ b/method/buffers.py @@ -70,10 +70,7 @@ class KillBuffer(Method): def _doit(self): a = self._old_window.application b = self._to_kill - if a.bufferlist.is_buffer_visible(b): - a.bufferlist.set_slot(a.active_slot, a.bufferlist.hidden_buffers[0]) - a.bufferlist.remove_buffer(b) - b.close() + a.close_buffer(b) def _callback(self, v): a = self._old_window.application if v == 'yes': diff --git a/mode/mini.py b/mode/mini.py index 4edf5c4..fa4e9fe 100644 --- a/mode/mini.py +++ b/mode/mini.py @@ -1,25 +1,70 @@ -import method, mode +import string +import buffer, method, mode, window + +class MiniInsertString(method.InsertString): + _is_method = False + def __init__(self, s): + method.InsertString.__init__(self, s) + self.name = "mini-insert-string-%s" % s + def _execute(self, w, **vargs): + try: + app = w.application + w.insert_string_at_cursor(self.string) + if app.completion_window_is_open(): + app.close_completion_buffer() + except buffer.ReadOnlyError: + w.set_error('Buffer is read-only') + +class MiniInsertSpace(method.Method): + def _execute(self, w, **vargs): + try: + app = w.application + w.insert_string_at_cursor(' ') + if app.completion_window_is_open(): + app.close_completion_buffer() + except buffer.ReadOnlyError: + w.set_error('Buffer is read-only') class MiniCallback(method.Method): - def execute(self, window, **vargs): - window.buffer.do_callback() + def execute(self, w, **vargs): + app = w.application + if app.completion_window_is_open(): + app.close_completion_buffer() + w.buffer.do_callback() class MiniTabComplete(method.Method): - def execute(self, window, **vargs): - b = window.buffer + def execute(self, w, **vargs): + app = w.application + b = w.buffer if b.tabber is None: - window.application.set_error("No tab completion") + w.application.set_error("No tab completion") return s1 = b.make_string() - s2, exists, complete = b.tabber.tab_string(s1, window) + s2, exists, complete = b.tabber.tab_string(s1, w) b.set_data(s2) + if app.completion_window_is_open(): + w2 = app.get_completion_window() + if w2.last_is_visible(): + w2.goto_beginning() + else: + w2.page_down() + else: + candidates = sorted(b.tabber.get_candidates(s1)) + if len(candidates) > 1: + app.open_completion_buffer(s2, candidates) + class Mini(mode.Fundamental): modename = 'Mini' - actions = [MiniCallback, MiniTabComplete] + actions = [MiniCallback, MiniTabComplete, MiniInsertSpace] def __init__(self, w): mode.Fundamental.__init__(self, w) self.add_bindings('mini-callback', ('RETURN',)) self.add_bindings('mini-tab-complete', ('TAB',)) + # create all the insert actions for the basic text input + for c in string.letters + string.digits + string.punctuation: + self.add_action_and_bindings(MiniInsertString(c), (c,)) + self.add_bindings('mini-insert-space', ('SPACE',)) + install = Mini.install diff --git a/searchutil.py b/searchutil.py index 09be40f..dd2559f 100644 --- a/searchutil.py +++ b/searchutil.py @@ -73,7 +73,7 @@ def find(r, w, move=False, direction='next', start=None, end=None): break if newc: w.goto(newc[0]) - else: + elif ranges: i = 0 if direction == 'next': i = -1