From d5c005a6923f3375fb0ebeb02eee3dbd8a990e71 Mon Sep 17 00:00:00 2001 From: moculus Date: Mon, 17 Mar 2008 07:16:15 +0000 Subject: [PATCH] more method clean-up; bug fixes for shell junk --HG-- branch : pmacs2 --- application.py | 5 +- method/__init__.py | 667 ++----------------------------------------- method/buffers.py | 125 ++++++++ method/help.py | 132 +++++++++ method/introspect.py | 148 ++++++++++ method/move.py | 118 ++++++++ method/search.py | 55 ++++ method/shell.py | 74 +++++ mode/__init__.py | 6 +- 9 files changed, 687 insertions(+), 643 deletions(-) create mode 100644 method/buffers.py create mode 100644 method/help.py create mode 100644 method/introspect.py create mode 100644 method/move.py create mode 100644 method/search.py create mode 100644 method/shell.py diff --git a/application.py b/application.py index 671aba5..494a152 100755 --- a/application.py +++ b/application.py @@ -118,7 +118,10 @@ class Application(object): # initialize our methods self.methods = {} - for name in ('method', 'method.svn', 'method.cvs'): + names = ('method', 'method.svn', 'method.cvs', 'method.search', + 'method.buffers', 'method.move', 'method.shell', + 'method.introspect', 'method.help') + for name in names: exec("import %s" % name) mod = eval(name) for mname in dir(mod): diff --git a/method/__init__.py b/method/__init__.py index aadecbf..68d3d7b 100644 --- a/method/__init__.py +++ b/method/__init__.py @@ -105,213 +105,6 @@ class Method(object): def _execute(self, w, **vargs): raise Exception, "Unimplemented Method: %s %r" % (self.name, vargs) -class AboutPmacs(Method): - '''print some information about pmacs''' - def _execute(self, w, **vargs): - a = w.application - if not a.has_buffer_name('*About*'): - b = buffer.AboutBuffer() - a.add_buffer(b) - window.Window(b, a) - b = a.bufferlist.get_buffer_by_name('*About*') - if a.window().buffer is not b: - a.switch_buffer(b) - -class GotoChar(Method): - '''Jump to the specified character''' - args = [Argument("charno", type=type(0), prompt="Goto char: ")] - def _execute(self, w, **vargs): - w.goto_char(vargs["charno"]) -class ForwardChars(Method): - '''Move forward the specified number of characters''' - args = [Argument("charno", type=type(0), prompt="Forward chars: ")] - def _execute(self, w, **vargs): - w.forward_chars(vargs["charno"]) - -class GotoLine(Method): - '''Jump to the specified line number''' - args = [Argument("lineno", type=type(0), prompt="Goto line: ")] - def _execute(self, w, **vargs): - n = vargs["lineno"] - if n < 0: - n = len(w.buffer.lines) + n + 1 - if n > len(w.buffer.lines): - n = len(w.buffer.lines) - elif n < 1: - n = 1 - w.goto_line(n) -class ForwardLines(Method): - '''Move forward the specified number of characters''' - args = [Argument("lineno", type=type(0), prompt="Forward lines: ")] - def _execute(self, w, **vargs): - w.forward_lines(vargs["lineno"]) - -# search and replace -class Search(Method): - '''Interactive search; finds next occurance of text in buffer''' - is_literal = True - direction = 'next' - prompt = 'I-Search: ' - def execute(self, w, **vargs): - self.old_cursor = w.logical_cursor() - self.old_window = w - w.application.open_mini_buffer(self.prompt, lambda x: None, self, None, 'search') -class ReverseSearch(Search): - '''Interactive search; finds previous occurance of text in buffer''' - direction = 'previous' -class RegexSearch(Search): - '''Interactive search; finds next occurance of regex in buffer''' - is_literal = False - prompt = 'I-RegexSearch: ' -class RegexReverseSearch(RegexSearch): - '''Interactive search; finds prevoius occurance of regex in buffer''' - direction = 'previous' - -class Replace(Method): - '''Replace occurances of string X with string Y''' - is_literal = True - args = [Argument('before', prompt="Replace String: ", - default=default.last_replace_before, load_default=True), - Argument('after', prompt="Replace With: ", - default=default.last_replace_after, load_default=True)] - def _execute(self, w, **vargs): - a = w.application - a.last_replace_before = self.before = vargs['before'] - a.last_replace_after = self.after = vargs['after'] - self.old_window = w - a.open_mini_buffer('I-Replace: ', lambda x: None, self, None, 'replace') -class RegexReplace(Method): - '''Replace occurances of string X with string Y''' - is_literal = False - args = [Argument('before', prompt="Replace Regex: ", - default=default.last_replace_before, load_default=True), - Argument('after', prompt="Replace With: ", - default=default.last_replace_after, load_default=True)] - def _execute(self, w, **vargs): - w.application.last_replace_before = self.before = vargs['before'] - w.application.last_replace_after = self.after = vargs['after'] - self.old_window = w - f = lambda x: None - w.application.open_mini_buffer('I-RegexReplace: ', f, self, None, 'replace') - -# navigating between buffers -class OpenFile(Method): - '''Open file in a new buffer, or go to file's open buffer''' - args = [Argument('filename', datatype="path", prompt="Open File: ", - default=default.path_dirname, load_default=True)] - def _execute(self, w, **vargs): - b = w.application.open_path(vargs['filename']) - SwitchBuffer().execute(w, buffername=b.name()) -class OpenAesFile(Method): - '''Open AES encrypted file in a new buffer, or go to file's open buffer''' - args = [Argument('filename', datatype="path", prompt="Open AES File: "), - Argument('password', prompt="Use AES Password: ")] - def _execute(self, w, **vargs): - b = w.application.open_path(vargs['filename'], 'aes', vargs['password']) - SwitchBuffer().execute(w, buffername=b.name()) - return - -class ViewBufferParent(Method): - def _execute(self, w, **vargs): - b = w.buffer - if not hasattr(b, 'path'): - w.set_error('Buffer has no path') - elif b.path == '/': - w.set_error("Root directory has no parent") - else: - path = os.path.dirname(b.path) - w.application.methods['open-file'].execute(w, filename=path) - -class SwitchBuffer(Method): - '''Switch to a different''' - args = [Argument('buffername', datatype="buffer", prompt="Switch To Buffer: ", - default=default.last_buffer)] - def _pre_execute(self, w, **vargs): - a = w.application - if len(a.bufferlist.buffers) < 1: - raise Exception, "No other buffers" - def _execute(self, w, **vargs): - name = vargs['buffername'] - buf = None - if w.application.has_buffer_name(name): - b = w.application.bufferlist.get_buffer_by_name(name) - w.application.switch_buffer(b) - else: - w.set_error("buffer %r was not found" % name) -class KillBuffer(Method): - '''Close the current buffer''' - force=False - args = [Argument('buffername', datatype="buffer", prompt="Kill Buffer: ", - default=default.current_buffer)] - def _execute(self, w, **vargs): - name = vargs['buffername'] - a = w.application - assert name in a.bufferlist.buffer_names, "Buffer %r does not exist" % name - assert name != '*Scratch*', "Can't kill scratch buffer" - self._to_kill = a.bufferlist.buffer_names[name] - self._old_window = w - if self.force or not self._to_kill.changed(): - self._doit() - else: - self._prompt = "Buffer has unsaved changes; kill anyway? " - a.open_mini_buffer(self._prompt, self._callback) - 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() - def _callback(self, v): - a = self._old_window.application - if v == 'yes': - self._doit() - a.close_mini_buffer() - elif v == 'no': - a.close_mini_buffer() - else: - a.close_mini_buffer() - a.set_error('Please type "yes" or "no"') - -class ForceKillBuffer(KillBuffer): - force=True - args = [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): - bl = w.application.bufferlist - bnames = [b.name() for b in bl.buffers] - bnames.sort() - data = '\n'.join(bnames) - w.application.data_buffer("*Buffers*", data, switch_to=True) -class SaveBufferAs(Method): - '''Save the contents of a buffer to the specified path''' - args = [Argument('path', datatype="path", prompt="Write file: ", - default=default.current_working_dir, load_default=True)] - def _execute(self, w, **vargs): - curr_buffer = w.buffer - curr_buffer_name = curr_buffer.name() - data = curr_buffer.make_string() - path = os.path.realpath(os.path.expanduser(vargs['path'])) - w.set_error("got %r (%d)" % (path, len(data))) - if w.application.has_buffer_name(path): - w.set_error("buffer for %r is already open" % path) - return - w.application.file_buffer(path, data, switch_to=True) - if curr_buffer_name != '*Scratch*': - w.application.methods['kill-buffer'].execute(w, buffername=curr_buffer_name) - else: - curr_buffer.set_data('') - w.set_error('Wrote %r' % path) -class SaveBuffer(Method): - '''Save the contents of a buffer''' - def _execute(self, w, **vargs): - if w.buffer.changed(): - w.buffer.save() - w.set_error("Wrote %s" % (w.buffer.path)) - else: - w.set_error("(No changes need to be saved)") class RelexBuffer(Method): '''Relex the buffer; this resets syntax highlighting''' def _execute(self, w, **vargs): @@ -321,6 +114,7 @@ class RelexBuffer(Method): else: h.highlight(w.buffer.lines) w.set_error("Buffer relexed.") + class ToggleWindow(Method): '''Move between visible windows''' def _execute(self, w, **vargs): @@ -511,57 +305,6 @@ class DeleteRightSpace(Method): if p > c: w.kill(c, p) -# random stuff -class DumpRegions(Method): - '''debug region highlighting''' - def _execute(self, w, **vargs): - lines = [] - for (w, p1, p2) in w.application.highlighted_ranges: - lines.append("%r %s %s" % (w, p1, p2)) - output = "\n".join(lines) - w.application.data_buffer("region-dump", output, switch_to=True) -class DumpMarkers(Method): - '''Dump all tab markers (tab debugging)''' - def _execute(self, w, **vargs): - lines = [] - if w.mode.tabber: - keys = w.mode.tabber.lines.keys() - keys.sort() - for i in keys: - line = w.mode.tabber.lines[i] - lines.append("LINE %d: %r" % (i, line)) - lines.append(" %s" % repr(w.mode.tabber.record[i])) - else: - lines.append("no tokens") - output = "\n".join(lines) - w.application.data_buffer("marker-dump", output, switch_to=True) -class DumpTokens(Method): - '''Dump all lexical tokens (syntax highlighting debugging)''' - def _execute(self, w, **vargs): - modename = w.mode.name() - lines = [] - if modename in w.buffer.highlights: - tokens = w.buffer.highlights[modename].tokens - for i in range(0, len(tokens)): - lines.append("LINE %d" % i) - group = tokens[i] - for token in group: - fqname = token.fqname() - p1 = Point(token.x, token.y) - if token.parent is None: - pcoord = '' - else: - pcoord = '[%d, %d]' % (token.parent.x, token.parent.y) - if fqname in w.mode.ghist and p1 in w.mode.ghist[fqname]: - g = '[' + w.mode.ghist[fqname][p1].name() + ']' - else: - g = '' - fields = (str(p1), pcoord, token.fqname(), g, token.string) - lines.append(' %-10s %-10s %-20s %-10s %r' % fields) - else: - lines.append("no tokens") - output = "\n".join(lines) - w.application.data_buffer("token-dump", output, switch_to=True) class MetaX(Method): '''Invoke commands by name''' args = [Argument('method', datatype="method", prompt="M-x ")] @@ -598,16 +341,25 @@ class InsertSpace(Method): '''Insert space into buffer at the cursor''' def _execute(self, w, **vargs): w.insert_string_at_cursor(' ') -class GetIndentionLevel(Method): - '''Calculate the indention level for this line''' + +class InsertSquotes(Method): + '''Insert a pair of single-quotes into the buffer''' def _execute(self, w, **vargs): - cursor = w.logical_cursor() - if not w.mode.tabber: - w.set_error('No tabber available') - return - else: - i = w.mode.tabber.get_level(cursor.y) - w.set_error('Indention level: %r' % i) + w.insert_string_at_cursor("''") + w.backward() +class InsertDquotes(Method): + '''Insert a pair of double-quotes into the buffer''' + def _execute(self, w, **vargs): + w.insert_string_at_cursor('""') + w.backward() +class InsertEscapedSquote(Method): + '''Insert an escaped single-quote''' + def _execute(self, w, **vargs): + w.insert_string_at_cursor("\\'") +class InsertEscapedDquote(Method): + '''Insert an escaped double-quote''' + def _execute(self, w, **vargs): + w.insert_string_at_cursor('\\"') class InsertTab(Method): '''Insert tab into buffer, or tabbify line, depending on mode''' @@ -627,6 +379,7 @@ class InsertTab(Method): w.insert_string(Point(0, cursor.y), ' ' * i) else: w.goto(Point(j, cursor.y)) + class KillWhitespace(Method): '''Delete leading whitespace on current line''' def _execute(self, w, **vargs): @@ -645,6 +398,16 @@ class TabBuffer(Method): w.goto_line(i) it.execute(w) w.goto_line(y) +class GetIndentionLevel(Method): + '''Calculate the indention level for this line''' + def _execute(self, w, **vargs): + cursor = w.logical_cursor() + if not w.mode.tabber: + w.set_error('No tabber available') + return + else: + i = w.mode.tabber.get_level(cursor.y) + w.set_error('Indention level: %r' % i) # commenting class CommentRegion(Method): @@ -899,79 +662,6 @@ class Redo(Method): except Exception, e: w.set_error("%s" % (e)) -# w navigation methods -class StartOfLine(Method): - '''Move the cursor to the start of the current 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, w, **vargs): - w.end_of_line() -class Forward(Method): - '''Move the cursor right one character''' - def _execute(self, w, **vargs): - w.forward() -class Backward(Method): - '''Move the cursor left one character''' - def _execute(self, w, **vargs): - w.backward() -class NextLine(Method): - '''Move the cursor down one line''' - def _execute(self, w, **vargs): - w.next_line() -class PreviousLine(Method): - '''Move the cursor up one line''' - def _execute(self, w, **vargs): - w.previous_line() -class PageUp(Method): - '''Move the cursor up one page''' - def _execute(self, w, **vargs): - w.page_up() -class PageDown(Method): - '''Move the cursor down one page''' - def _execute(self, w, **vargs): - w.page_down() -class GotoBeginning(Method): - '''Move the cursor to the beginning of the buffer''' - def _execute(self, w, **vargs): - w.goto_beginning() -class GotoEnd(Method): - '''Move the cursor to the end of the buffer''' - 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, w, **vargs): - w.right_word() -class LeftWord(Method): - '''Move the cursor to the start of the word to the left''' - def _execute(self, w, **vargs): - w.left_word() -class NextSection(Method): - '''Move the cursor to the next section''' - def _execute(self, w, **vargs): - cursor = w.logical_cursor() - i = cursor.y + 1 - seen_null_line = False - while i < len(w.buffer.lines): - if seen_null_line: - w.goto_line(i) - break - seen_null_line = regex.whitespace.match(w.buffer.lines[i]) - i += 1 -class PreviousSection(Method): - '''Move the cursor to the previous section''' - def _execute(self, w, **vargs): - cursor = w.logical_cursor() - i = cursor.y - 1 - seen_null_line = False - while i >= 0: - if seen_null_line: - w.goto_line(i) - break - seen_null_line = regex.whitespace.match(w.buffer.lines[i]) - i -= 1 class UnindentBlock(Method): '''Prepend 4 spaces to each line in region''' def _execute(self, w, **vargs): @@ -1011,32 +701,6 @@ class IndentBlock(Method): w.buffer.delete(Point(0, p1.y), Point(0, p2.y)) w.buffer.insert_string(Point(0, p1.y), '\n'.join(lines) + '\n') -class OpenConsole(Method): - '''Evaluate python expressions (for advanced use and debugging only)''' - def execute(self, w, **vargs): - a = w.application - if not a.has_buffer_name('*Console*'): - b = buffer.ConsoleBuffer() - a.add_buffer(b) - window.Window(b, a) - b = a.bufferlist.get_buffer_by_name('*Console*') - if a.window().buffer is not b: - a.switch_buffer(b) - f = lambda x: None - w.application.open_mini_buffer('>>> ', f, self, None, 'consolemini') - -class ShellCmd(Method): - '''Run a command in a shell and put the output in a new buffer''' - args = [Argument("cmd", type=type(""), prompt="$ ", datatype='shell')] - def _execute(self, w, **vargs): - cmd = "PBUF='%s'; %s" % (w.buffer.name(), vargs['cmd']) - (status, data) = commands.getstatusoutput(cmd) - if status == 0: - mesg = 'ok' - else: - mesg = 'error' - data += "\nprocess exited with status %d (%s)" % (status, mesg) - w.application.data_buffer("*Shell*", data, switch_to=True) class FileDiff(Method): '''diff the buffer's contents with the given file''' args = [Argument("path", type=type(""), prompt="Filename: ", datatype='path')] @@ -1062,109 +726,6 @@ class FileDiff(Method): w.application.data_buffer("*Diff*", errdata, switch_to=True) w.set_error("There was an error: %d exited with status %s" % (pid, status)) -class ShowBindingsBuffer(Method): - '''Dump all keybindings for current mode into a new buffer''' - def _execute(self, w, **vargs): - lines = [] - mode_name = w.mode.name() - - lines.append('Key bindings for mode %r:' % (mode_name)) - lines.append('') - - names_to_sequences = {} - - seq_len = len('BINDINGS') - name_len = len('ACTION') - for seq in w.mode.bindings: - name = w.mode.bindings[seq] - if name.startswith('insert-string-'): - # we aren't going to show all the generic keypress actions - continue - # determine this for formatting - seq_len = max(seq_len, len(seq)) - name_len = max(name_len, len(name)) - - # set up our new data structure - names_to_sequences.setdefault(name, []) - names_to_sequences[name].append(seq) - - # generate the format string (note the 'meta formatting') - format_str = '%%-%ds %%-%ds %%s' % (seq_len, name_len) - - lines.append(format_str % ('BINDINGS', 'ACTIONS', 'HELP')) - - names = names_to_sequences.keys() - names.sort() - for name in names: - sequences = names_to_sequences[name] - sequences.sort() - seq = sequences[0] - help = w.application.methods[name].help - if help is None: - help = '' - - lines.append(format_str % (seq, name, help)) - for seq2 in sequences[1:]: - lines.append(format_str % (seq2, '', '')) - - data = '\n'.join(lines) - w.application.data_buffer("*Bindings-Help*", data, switch_to=True) - -class CmdHelpBuffer(Method): - '''Get help with the specified command''' - args = [Argument('method', datatype="method", prompt="Help for command: ")] - def _execute(self, w, **vargs): - lines = [] - name = vargs['method'] - if name not in w.application.methods: - err = "No command called %r in mode %r" % (name, w.mode.name) - raise Exception, err - - m = w.application.methods[name] - lines.append('HELP FOR %r' % name) - lines.append('') - - # sequences - sequences = [] - for seq in w.mode.bindings: - if w.mode.bindings[seq] == name: - sequences.append(seq) - sequences.sort() - lines.append('Keys bound to this command:') - for seq in sequences: - lines.append(' %s' % (seq)) - lines.append('') - - # arguments - if m.args: - lines.append('Arguments for this command:') - for arg in m.args: - if arg.datatype is None: - if arg.type == type(""): - t = 'str' - elif arg.type == type(0): - t = 'int' - elif arg.type == type(0.0): - t = 'float' - else: - t = 'str' - else: - t = arg.datatype - if arg.help: - lines.append(' %s %r: %s' % (t, arg.name, arg.help)) - else: - lines.append(' %s %r' % (t, arg.name)) - lines.append('') - - # help text - lines.append('Help text for this command:') - h = m.help - if not h: - h = 'No help available' - lines.append(' %s' % h) - data = '\n'.join(lines) - w.application.data_buffer("*Command-Help*", data, switch_to=True) - class SetMode(Method): '''Set the mode of the current buffer''' args = [Argument('mode', datatype='mode', prompt="Enter new mode: ")] @@ -1174,16 +735,6 @@ class SetMode(Method): w.set_mode(m) w.set_error('Set mode to %r' % (mode_name)) -class WhichCommand(Method): - '''Display which command is run for a given key-sequence''' - def _execute(self, w, **vargs): - self.old_window = w - w.application.open_mini_buffer('Enter a key sequence to be explained: ', - lambda x: None, - self, - None, - 'which') - class Cancel(Method): '''Cancel command in-progress, and return to the main buffer''' def execute(self, w, **vargs): @@ -1207,29 +758,6 @@ class UnsplitWindow(Method): w.application.single_slot() w.set_error('Window has been unsplit back to one window!') -class SomethingCrazy(Method): - def _execute(self, w, **vargs): - pass - -class InsertSquotes(Method): - '''Insert a pair of single-quotes into the buffer''' - def _execute(self, w, **vargs): - w.insert_string_at_cursor("''") - w.backward() -class InsertDquotes(Method): - '''Insert a pair of double-quotes into the buffer''' - def _execute(self, w, **vargs): - w.insert_string_at_cursor('""') - w.backward() -class InsertEscapedSquote(Method): - '''Insert an escaped single-quote''' - def _execute(self, w, **vargs): - w.insert_string_at_cursor("\\'") -class InsertEscapedDquote(Method): - '''Insert an escaped double-quote''' - def _execute(self, w, **vargs): - w.insert_string_at_cursor('\\"') - class CloseTag(Method): mytag = ')' def _execute(self, w, **vargs): @@ -1290,15 +818,6 @@ class CloseBrace(CloseTag): class CloseBracket(CloseTag): mytag = ']' -class GetToken(Method): - '''View type and data of the "current" token''' - def _execute(self, w, **vargs): - token = w.get_token() - if token is None: - w.set_error('No Token') - else: - w.set_error('Token: %r (%s)' % (token.string, token.fqname())) - class RegisterSave(Method): MAX_TXT = 30 MAX_REG = 20 @@ -1334,129 +853,3 @@ class RegisterRestore(Method): if len(name) > self.MAX_REG: name = name[0:self.MAX_REG] + '...' w.set_error('Restored %r from register %r' % (text2, name2)) - -class Pipe(Method): - '''Pipe the buffer's contents through the command, and display the output in a new buffer''' - args = [Argument('cmd', datatype="str", prompt="Command: ")] - def _parse(self, w, **vargs): - m = regex.shell_command.match(vargs['cmd']) - if m: - prog = m.group(0) - return (prog, vargs['cmd']) - else: - return (None, None) - - def _execute(self, w, **vargs): - (prog, cmd) = self._parse(w, **vargs) - if prog is None: - return - - pipe = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT) - pid = pipe.pid - - indata = w.buffer.make_string() - pipe.stdin.write(indata) - pipe.stdin.close() - - outdata = pipe.stdout.read() - status = pipe.wait() >> 8 - - bufname = '*%s*' % self.name.title() - w.application.data_buffer(bufname, outdata, switch_to=True) - w.set_error("%s exited with status %d" % (prog, status)) - -class Grep(Pipe): - '''Grep the buffer's contents for instances of a pattern, and display them in a new buffer''' - args = [Argument('pattern', datatype="str", prompt="Pattern: ")] - def _parse(self, w, **vargs): - return ('grep', ('/usr/bin/grep', '-E', '-n', vargs['pattern'])) - -class Exec(Method): - args = [Argument('cmd', datatype="str", prompt="Exec: ")] - def _doit(self, w, path, cmd): - try: - cmd = cmd % path - except: - w.set_error("Malformed command: %r" % cmd) - return - (status, output) = commands.getstatusoutput(cmd) - bufname = '*%s*' % self.name.title() - w.application.data_buffer(bufname, output, switch_to=True) - w.set_error("Shell exited with %d" % status) - def _execute(self, w, **vargs): - if w.buffer.btype == 'dir': - name = dirutil.resolve_name(w) - path = dirutil.resolve_path(w) - self._doit(w, path, vargs['cmd']) - dirutil.find_name(w, name) - elif hasattr(w.buffer, 'path'): - path = w.buffer.path - self._doit(w, path, vargs['cmd']) - else: - w.set_error("Don't know how to exec: %r" % w.buffer) - return - -class TokenComplete(Method): - '''Complete token names based on other tokens in the buffer''' - name_overrides = {} - def _min_completion(self, w, t): - h = w.get_highlighter() - minlen = None - if t.name in self.name_overrides: - ok = name_overrides[t.name] - else: - ok = (t.name,) - - strings = {} - for line in h.tokens: - for t2 in line: - if t2 is t: - continue - elif False and t2.name not in ok: - continue - elif t2.string.startswith(t.string): - strings[t2.string] = 1 - if minlen is None: - minlen = len(t2.string) - else: - minlen = min(minlen, len(t2.string)) - - strings = strings.keys() - if not strings: - return ([], t.string) - - i = len(t.string) - while i < minlen: - c = strings[0][i] - for s in strings: - if s[i] != c: - return (strings, strings[0][:i]) - i += 1 - return (strings, strings[0][:minlen]) - - def _execute(self, w, **vargs): - t = w.get_token2() - - if t is None: - w.set_error("No token to complete!") - return - elif regex.reserved_token_names.match(t.name): - w.set_error("Will not complete reserved token") - return - - (candidates, result) = self._min_completion(w, t) - - if candidates: - p1 = Point(t.x, t.y) - p2 = Point(t.end_x(), t.y) - w.buffer.delete(p1, p2) - w.insert_string(p1, result) - - if not candidates: - w.set_error("No completion: %r" % result) - elif len(candidates) == 1: - w.set_error("Unique completion: %r" % result) - elif result in candidates: - w.set_error("Ambiguous completion: %r" % candidates) - else: - w.set_error("Partial completion: %r" % candidates) diff --git a/method/buffers.py b/method/buffers.py new file mode 100644 index 0000000..980e1e1 --- /dev/null +++ b/method/buffers.py @@ -0,0 +1,125 @@ +import os, commands, re, sets, tempfile +from subprocess import Popen, PIPE, STDOUT + +import buffer, default, dirutil, regex, util, window +from point import Point + +from method import DATATYPES, Method, Argument + +class OpenFile(Method): + '''Open file in a new buffer, or go to file's open buffer''' + args = [Argument('filename', datatype="path", prompt="Open File: ", + default=default.path_dirname, load_default=True)] + def _execute(self, w, **vargs): + b = w.application.open_path(vargs['filename']) + SwitchBuffer().execute(w, buffername=b.name()) +class OpenAesFile(Method): + '''Open AES encrypted file in a new buffer, or go to file's open buffer''' + args = [Argument('filename', datatype="path", prompt="Open AES File: "), + Argument('password', prompt="Use AES Password: ")] + def _execute(self, w, **vargs): + b = w.application.open_path(vargs['filename'], 'aes', vargs['password']) + SwitchBuffer().execute(w, buffername=b.name()) + return + +class ViewBufferParent(Method): + def _execute(self, w, **vargs): + b = w.buffer + if not hasattr(b, 'path'): + w.set_error('Buffer has no path') + elif b.path == '/': + w.set_error("Root directory has no parent") + else: + path = os.path.dirname(b.path) + w.application.methods['open-file'].execute(w, filename=path) + +class SwitchBuffer(Method): + '''Switch to a different''' + args = [Argument('buffername', datatype="buffer", prompt="Switch To Buffer: ", + default=default.last_buffer)] + def _pre_execute(self, w, **vargs): + a = w.application + if len(a.bufferlist.buffers) < 1: + raise Exception, "No other buffers" + def _execute(self, w, **vargs): + name = vargs['buffername'] + buf = None + if w.application.has_buffer_name(name): + b = w.application.bufferlist.get_buffer_by_name(name) + w.application.switch_buffer(b) + else: + w.set_error("buffer %r was not found" % name) +class KillBuffer(Method): + '''Close the current buffer''' + force=False + args = [Argument('buffername', datatype="buffer", prompt="Kill Buffer: ", + default=default.current_buffer)] + def _execute(self, w, **vargs): + name = vargs['buffername'] + a = w.application + assert name in a.bufferlist.buffer_names, "Buffer %r does not exist" % name + assert name != '*Scratch*', "Can't kill scratch buffer" + self._to_kill = a.bufferlist.buffer_names[name] + self._old_window = w + if self.force or not self._to_kill.changed(): + self._doit() + else: + self._prompt = "Buffer has unsaved changes; kill anyway? " + a.open_mini_buffer(self._prompt, self._callback) + 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() + def _callback(self, v): + a = self._old_window.application + if v == 'yes': + self._doit() + a.close_mini_buffer() + elif v == 'no': + a.close_mini_buffer() + else: + a.close_mini_buffer() + a.set_error('Please type "yes" or "no"') + +class ForceKillBuffer(KillBuffer): + force=True + args = [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): + bl = w.application.bufferlist + bnames = [b.name() for b in bl.buffers] + bnames.sort() + data = '\n'.join(bnames) + w.application.data_buffer("*Buffers*", data, switch_to=True) +class SaveBufferAs(Method): + '''Save the contents of a buffer to the specified path''' + args = [Argument('path', datatype="path", prompt="Write file: ", + default=default.current_working_dir, load_default=True)] + def _execute(self, w, **vargs): + curr_buffer = w.buffer + curr_buffer_name = curr_buffer.name() + data = curr_buffer.make_string() + path = os.path.realpath(os.path.expanduser(vargs['path'])) + w.set_error("got %r (%d)" % (path, len(data))) + if w.application.has_buffer_name(path): + w.set_error("buffer for %r is already open" % path) + return + w.application.file_buffer(path, data, switch_to=True) + if curr_buffer_name != '*Scratch*': + w.application.methods['kill-buffer'].execute(w, buffername=curr_buffer_name) + else: + curr_buffer.set_data('') + w.set_error('Wrote %r' % path) +class SaveBuffer(Method): + '''Save the contents of a buffer''' + def _execute(self, w, **vargs): + if w.buffer.changed(): + w.buffer.save() + w.set_error("Wrote %s" % (w.buffer.path)) + else: + w.set_error("(No changes need to be saved)") diff --git a/method/help.py b/method/help.py new file mode 100644 index 0000000..4043560 --- /dev/null +++ b/method/help.py @@ -0,0 +1,132 @@ +import os, commands, re, sets, tempfile +from subprocess import Popen, PIPE, STDOUT + +import buffer, default, dirutil, regex, util, window +from point import Point + +from method import DATATYPES, Method, Argument + +class ShowBindingsBuffer(Method): + '''Dump all keybindings for current mode into a new buffer''' + def _execute(self, w, **vargs): + lines = [] + mode_name = w.mode.name() + + lines.append('Key bindings for mode %r:' % (mode_name)) + lines.append('') + + names_to_sequences = {} + + seq_len = len('BINDINGS') + name_len = len('ACTION') + for seq in w.mode.bindings: + name = w.mode.bindings[seq] + if name.startswith('insert-string-'): + # we aren't going to show all the generic keypress actions + continue + # determine this for formatting + seq_len = max(seq_len, len(seq)) + name_len = max(name_len, len(name)) + + # set up our new data structure + names_to_sequences.setdefault(name, []) + names_to_sequences[name].append(seq) + + # generate the format string (note the 'meta formatting') + format_str = '%%-%ds %%-%ds %%s' % (seq_len, name_len) + + lines.append(format_str % ('BINDINGS', 'ACTIONS', 'HELP')) + + names = names_to_sequences.keys() + names.sort() + for name in names: + sequences = names_to_sequences[name] + sequences.sort() + seq = sequences[0] + help = w.application.methods[name].help + if help is None: + help = '' + + lines.append(format_str % (seq, name, help)) + for seq2 in sequences[1:]: + lines.append(format_str % (seq2, '', '')) + + data = '\n'.join(lines) + w.application.data_buffer("*Bindings-Help*", data, switch_to=True) + +class CmdHelpBuffer(Method): + '''Get help with the specified command''' + args = [Argument('method', datatype="method", prompt="Help for command: ")] + def _execute(self, w, **vargs): + lines = [] + name = vargs['method'] + if name not in w.application.methods: + err = "No command called %r in mode %r" % (name, w.mode.name) + raise Exception, err + + m = w.application.methods[name] + lines.append('HELP FOR %r' % name) + lines.append('') + + # sequences + sequences = [] + for seq in w.mode.bindings: + if w.mode.bindings[seq] == name: + sequences.append(seq) + sequences.sort() + lines.append('Keys bound to this command:') + for seq in sequences: + lines.append(' %s' % (seq)) + lines.append('') + + # arguments + if m.args: + lines.append('Arguments for this command:') + for arg in m.args: + if arg.datatype is None: + if arg.type == type(""): + t = 'str' + elif arg.type == type(0): + t = 'int' + elif arg.type == type(0.0): + t = 'float' + else: + t = 'str' + else: + t = arg.datatype + if arg.help: + lines.append(' %s %r: %s' % (t, arg.name, arg.help)) + else: + lines.append(' %s %r' % (t, arg.name)) + lines.append('') + + # help text + lines.append('Help text for this command:') + h = m.help + if not h: + h = 'No help available' + lines.append(' %s' % h) + data = '\n'.join(lines) + w.application.data_buffer("*Command-Help*", data, switch_to=True) + +class WhichCommand(Method): + '''Display which command is run for a given key-sequence''' + def _execute(self, w, **vargs): + self.old_window = w + w.application.open_mini_buffer('Enter a key sequence to be explained: ', + lambda x: None, + self, + None, + 'which') + +class AboutPmacs(Method): + '''print some information about pmacs''' + def _execute(self, w, **vargs): + a = w.application + if not a.has_buffer_name('*About*'): + b = buffer.AboutBuffer() + a.add_buffer(b) + window.Window(b, a) + b = a.bufferlist.get_buffer_by_name('*About*') + if a.window().buffer is not b: + a.switch_buffer(b) diff --git a/method/introspect.py b/method/introspect.py new file mode 100644 index 0000000..394a7b1 --- /dev/null +++ b/method/introspect.py @@ -0,0 +1,148 @@ +import os, commands, re, sets, tempfile +from subprocess import Popen, PIPE, STDOUT + +import buffer, default, dirutil, regex, util, window +from point import Point + +from method import DATATYPES, Method, Argument + +class DumpRegions(Method): + '''debug region highlighting''' + def _execute(self, w, **vargs): + lines = [] + for (w, p1, p2) in w.application.highlighted_ranges: + lines.append("%r %s %s" % (w, p1, p2)) + output = "\n".join(lines) + w.application.data_buffer("region-dump", output, switch_to=True) + +class DumpMarkers(Method): + '''Dump all tab markers (tab debugging)''' + def _execute(self, w, **vargs): + lines = [] + if w.mode.tabber: + keys = w.mode.tabber.lines.keys() + keys.sort() + for i in keys: + line = w.mode.tabber.lines[i] + lines.append("LINE %d: %r" % (i, line)) + lines.append(" %s" % repr(w.mode.tabber.record[i])) + else: + lines.append("no tokens") + output = "\n".join(lines) + w.application.data_buffer("marker-dump", output, switch_to=True) + +class DumpTokens(Method): + '''Dump all lexical tokens (syntax highlighting debugging)''' + def _execute(self, w, **vargs): + modename = w.mode.name() + lines = [] + if modename in w.buffer.highlights: + tokens = w.buffer.highlights[modename].tokens + for i in range(0, len(tokens)): + lines.append("LINE %d" % i) + group = tokens[i] + for token in group: + fqname = token.fqname() + p1 = Point(token.x, token.y) + if token.parent is None: + pcoord = '' + else: + pcoord = '[%d, %d]' % (token.parent.x, token.parent.y) + if fqname in w.mode.ghist and p1 in w.mode.ghist[fqname]: + g = '[' + w.mode.ghist[fqname][p1].name() + ']' + else: + g = '' + fields = (str(p1), pcoord, token.fqname(), g, token.string) + lines.append(' %-10s %-10s %-20s %-10s %r' % fields) + else: + lines.append("no tokens") + output = "\n".join(lines) + w.application.data_buffer("token-dump", output, switch_to=True) + +class GetToken(Method): + '''View type and data of the "current" token''' + def _execute(self, w, **vargs): + token = w.get_token() + if token is None: + w.set_error('No Token') + else: + w.set_error('Token: %r (%s)' % (token.string, token.fqname())) + +class TokenComplete(Method): + '''Complete token names based on other tokens in the buffer''' + name_overrides = {} + def _min_completion(self, w, t): + h = w.get_highlighter() + minlen = None + if t.name in self.name_overrides: + ok = name_overrides[t.name] + else: + ok = (t.name,) + + strings = {} + for line in h.tokens: + for t2 in line: + if t2 is t: + continue + elif False and t2.name not in ok: + continue + elif t2.string.startswith(t.string): + strings[t2.string] = 1 + if minlen is None: + minlen = len(t2.string) + else: + minlen = min(minlen, len(t2.string)) + + strings = strings.keys() + if not strings: + return ([], t.string) + + i = len(t.string) + while i < minlen: + c = strings[0][i] + for s in strings: + if s[i] != c: + return (strings, strings[0][:i]) + i += 1 + return (strings, strings[0][:minlen]) + + def _execute(self, w, **vargs): + t = w.get_token2() + + if t is None: + w.set_error("No token to complete!") + return + elif regex.reserved_token_names.match(t.name): + w.set_error("Will not complete reserved token") + return + + (candidates, result) = self._min_completion(w, t) + + if candidates: + p1 = Point(t.x, t.y) + p2 = Point(t.end_x(), t.y) + w.buffer.delete(p1, p2) + w.insert_string(p1, result) + + if not candidates: + w.set_error("No completion: %r" % result) + elif len(candidates) == 1: + w.set_error("Unique completion: %r" % result) + elif result in candidates: + w.set_error("Ambiguous completion: %r" % candidates) + else: + w.set_error("Partial completion: %r" % candidates) + +class OpenConsole(Method): + '''Evaluate python expressions (for advanced use and debugging only)''' + def execute(self, w, **vargs): + a = w.application + if not a.has_buffer_name('*Console*'): + b = buffer.ConsoleBuffer() + a.add_buffer(b) + window.Window(b, a) + b = a.bufferlist.get_buffer_by_name('*Console*') + if a.window().buffer is not b: + a.switch_buffer(b) + f = lambda x: None + w.application.open_mini_buffer('>>> ', f, self, None, 'consolemini') diff --git a/method/move.py b/method/move.py new file mode 100644 index 0000000..239143d --- /dev/null +++ b/method/move.py @@ -0,0 +1,118 @@ +import os, commands, re, sets, tempfile +from subprocess import Popen, PIPE, STDOUT + +import buffer, default, dirutil, regex, util, window +from point import Point + +from method import DATATYPES, Method, Argument + +class StartOfLine(Method): + '''Move the cursor to the start of the current 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, w, **vargs): + w.end_of_line() + +class Forward(Method): + '''Move the cursor right one character''' + def _execute(self, w, **vargs): + w.forward() +class Backward(Method): + '''Move the cursor left one character''' + def _execute(self, w, **vargs): + w.backward() + +class NextLine(Method): + '''Move the cursor down one line''' + def _execute(self, w, **vargs): + w.next_line() +class PreviousLine(Method): + '''Move the cursor up one line''' + def _execute(self, w, **vargs): + w.previous_line() + +class PageUp(Method): + '''Move the cursor up one page''' + def _execute(self, w, **vargs): + w.page_up() +class PageDown(Method): + '''Move the cursor down one page''' + def _execute(self, w, **vargs): + w.page_down() + +class GotoBeginning(Method): + '''Move the cursor to the beginning of the buffer''' + def _execute(self, w, **vargs): + w.goto_beginning() +class GotoEnd(Method): + '''Move the cursor to the end of the buffer''' + 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, w, **vargs): + w.right_word() +class LeftWord(Method): + '''Move the cursor to the start of the word to the left''' + def _execute(self, w, **vargs): + w.left_word() + +class NextSection(Method): + '''Move the cursor to the next section''' + def _execute(self, w, **vargs): + cursor = w.logical_cursor() + i = cursor.y + 1 + seen_null_line = False + while i < len(w.buffer.lines): + if seen_null_line: + w.goto_line(i) + break + seen_null_line = regex.whitespace.match(w.buffer.lines[i]) + i += 1 +class PreviousSection(Method): + '''Move the cursor to the previous section''' + def _execute(self, w, **vargs): + cursor = w.logical_cursor() + i = cursor.y - 1 + seen_null_line = False + while i >= 0: + if seen_null_line: + w.goto_line(i) + break + seen_null_line = regex.whitespace.match(w.buffer.lines[i]) + i -= 1 + +class GotoChar(Method): + '''Jump to the specified character''' + args = [Argument("charno", type=type(0), prompt="Goto char: ")] + def _execute(self, w, **vargs): + w.goto_char(vargs["charno"]) + +class ForwardChars(Method): + '''Move forward the specified number of characters''' + args = [Argument("charno", type=type(0), prompt="Forward chars: ")] + def _execute(self, w, **vargs): + w.forward_chars(vargs["charno"]) + +class GotoLine(Method): + '''Jump to the specified line number''' + args = [Argument("lineno", type=type(0), prompt="Goto line: ")] + def _execute(self, w, **vargs): + n = vargs["lineno"] + if n < 0: + n = len(w.buffer.lines) + n + 1 + if n > len(w.buffer.lines): + n = len(w.buffer.lines) + elif n < 1: + n = 1 + w.goto_line(n) + +class ForwardLines(Method): + '''Move forward the specified number of characters''' + args = [Argument("lineno", type=type(0), prompt="Forward lines: ")] + def _execute(self, w, **vargs): + w.forward_lines(vargs["lineno"]) + diff --git a/method/search.py b/method/search.py new file mode 100644 index 0000000..8c06613 --- /dev/null +++ b/method/search.py @@ -0,0 +1,55 @@ +import os, commands, re, sets, tempfile +from subprocess import Popen, PIPE, STDOUT + +import buffer, default, dirutil, regex, util, window +from point import Point + +from method import DATATYPES, Method, Argument + +class Search(Method): + '''Interactive search; finds next occurance of text in buffer''' + is_literal = True + direction = 'next' + prompt = 'I-Search: ' + def execute(self, w, **vargs): + self.old_cursor = w.logical_cursor() + self.old_window = w + w.application.open_mini_buffer(self.prompt, lambda x: None, self, None, 'search') +class ReverseSearch(Search): + '''Interactive search; finds previous occurance of text in buffer''' + direction = 'previous' + +class RegexSearch(Search): + '''Interactive search; finds next occurance of regex in buffer''' + is_literal = False + prompt = 'I-RegexSearch: ' +class RegexReverseSearch(RegexSearch): + '''Interactive search; finds prevoius occurance of regex in buffer''' + direction = 'previous' + +class Replace(Method): + '''Replace occurances of string X with string Y''' + is_literal = True + args = [Argument('before', prompt="Replace String: ", + default=default.last_replace_before, load_default=True), + Argument('after', prompt="Replace With: ", + default=default.last_replace_after, load_default=True)] + def _execute(self, w, **vargs): + a = w.application + a.last_replace_before = self.before = vargs['before'] + a.last_replace_after = self.after = vargs['after'] + self.old_window = w + a.open_mini_buffer('I-Replace: ', lambda x: None, self, None, 'replace') +class RegexReplace(Method): + '''Replace occurances of string X with string Y''' + is_literal = False + args = [Argument('before', prompt="Replace Regex: ", + default=default.last_replace_before, load_default=True), + Argument('after', prompt="Replace With: ", + default=default.last_replace_after, load_default=True)] + def _execute(self, w, **vargs): + w.application.last_replace_before = self.before = vargs['before'] + w.application.last_replace_after = self.after = vargs['after'] + self.old_window = w + f = lambda x: None + w.application.open_mini_buffer('I-RegexReplace: ', f, self, None, 'replace') diff --git a/method/shell.py b/method/shell.py new file mode 100644 index 0000000..59ea13c --- /dev/null +++ b/method/shell.py @@ -0,0 +1,74 @@ +import os, commands, re, sets, tempfile +from subprocess import Popen, PIPE, STDOUT + +import buffer, default, dirutil, regex, util, window +from point import Point + +from method import DATATYPES, Method, Argument + +class Exec(Method): + '''Execute a command in a shell and put the output in a new buffer''' + args = [Argument('cmd', prompt="Exec: ", datatype='shell')] + def _doit(self, w, path, cmd): + if path: + try: + cmd = cmd % {'path': path} + except: + pass + (status, output) = commands.getstatusoutput(cmd) + bufname = '*%s*' % self.name.title() + w.application.data_buffer(bufname, output, switch_to=True) + w.set_error("Shell exited with %d" % status) + def _execute(self, w, **vargs): + if w.buffer.btype == 'dir': + name = dirutil.resolve_name(w) + path = dirutil.resolve_path(w) + self._doit(w, path, vargs['cmd']) + dirutil.find_name(w, name) + elif hasattr(w.buffer, 'path'): + path = w.buffer.path + self._doit(w, path, vargs['cmd']) + else: + self._doit(w, None, vargs['cmd']) + +class Pipe(Method): + '''Pipe the buffer's contents through the command, and display the output in a new buffer''' + args = [Argument('cmd', datatype="str", prompt="Command: ")] + def _parse(self, w, **vargs): + # return 3 things: prog name, cmd, and whether to use the shell + m = regex.shell_command.match(vargs['cmd']) + if m: + prog = m.group(0) + return (prog, vargs['cmd'], True) + else: + return (None, None, False) + + def _execute(self, w, **vargs): + (prog, cmd, shell) = self._parse(w, **vargs) + if prog is None or not cmd: + return + + pipe = Popen(cmd, shell=shell, stdin=PIPE, stdout=PIPE, stderr=STDOUT) + pid = pipe.pid + + indata = w.buffer.make_string() + pipe.stdin.write(indata) + pipe.stdin.close() + + outdata = pipe.stdout.read() + status = pipe.wait() >> 8 + + bufname = '*%s*' % self.name.title() + w.application.data_buffer(bufname, outdata, switch_to=True) + w.set_error("%s exited with status %d" % (prog, status)) + +class Grep(Pipe): + '''Grep the buffer's contents for instances of a pattern, and display them in a new buffer''' + args = [Argument('pattern', datatype="str", prompt="Pattern: ")] + def _parse(self, w, **vargs): + return ('grep', ('grep', '-E', '-n', vargs['pattern']), False) +class Sed(Pipe): + '''Push the buffer's contents through a sed expression''' + args = [Argument('expression', datatype="str", prompt="Expression: ")] + def _parse(self, w, **vargs): + return ('grep', ('sed', '-r', '-e', vargs['expression']), False) diff --git a/mode/__init__.py b/mode/__init__.py index 2383ded..914946d 100644 --- a/mode/__init__.py +++ b/mode/__init__.py @@ -170,7 +170,6 @@ class Fundamental(Handler): self.add_bindings('indent-block', ('C-c >',)) self.add_bindings('unindent-block', ('C-c <',)) self.add_bindings('token-complete', ('M-c', 'C-c c', 'C-c TAB',)) - self.add_bindings('shell-cmd', ('C-c !',)) self.add_bindings('open-aes-file', ('C-c a',)) self.add_bindings('open-console', ('M-e',)) self.add_bindings('show-bindings-buffer', ('C-c M-h','C-c M-?',)) @@ -178,7 +177,7 @@ class Fundamental(Handler): self.add_bindings('cmd-help-buffer', ('M-h',)) self.add_bindings('set-mode', ('C-x m',)) self.add_bindings('cancel', ('C-]',)) - self.add_bindings('exec', ('C-c e',)) + self.add_bindings('exec', ('C-c e', 'C-c !')) self.add_bindings('grep', ('C-c g',)) self.add_bindings('pipe', ('C-c p',)) self.add_bindings('view-buffer-parent', ('C-c .',)) @@ -188,9 +187,6 @@ class Fundamental(Handler): self.add_bindings('insert-escaped-dquote', ('C-c M-"',)) self.add_bindings('get-token', ('C-c t',)) - # unbound actions - self.add_action(method.GetToken()) - # create all the insert actions for the basic text input for c in string.letters + string.digits + string.punctuation: self.add_binding('insert-string-%s' % c, c)