import os, commands, re, tempfile from subprocess import Popen, PIPE, STDOUT import buffer, completer, default, dirutil, regex, util, window import buffer.colors from point import Point class MethodError(Exception): pass def arg(n, t=type(''), dt=None, p=None, h='', dv=default.none, ld=False, q='default'): '''convenience function for arguments''' return Argument(n, type=t, datatype=dt, prompt=p, help=h, default=dv, load_default=ld, queue=q) class Argument(object): def __init__(self, name, type=type(""), datatype=None, prompt=None, help='', default=default.none, load_default=False, queue='default'): self.name = name self.type = type self.datatype = datatype if prompt is None: self.prompt = "%s: " % (name) else: self.prompt = prompt self.help = help self.load_default = load_default self.default = default self.queue = queue def coerce_to_type(self, value): if self.type == type(0): try: return int(value, 0) except: raise Exception, "expected int; got %s" % (repr(value)) else: return value def ask_for_value(self, method, w, **vargs): app = w.application assert app.mini_buffer_is_open() is False, "Recursive minibuffer antics" vargs2 = vargs.copy() assert callable(self.default), "default value func must be callable" if self.load_default: d = None starting_value = self.default(w) else: d = self.default(w) starting_value = None def return_value(v): if d is not None and v == "": v = d vargs2[self.name] = self.coerce_to_type(v) app.close_mini_buffer() method.execute(w, **vargs2) tabber = completer.get_completer(self.datatype) if d is not None: p = self.prompt + "(%s) " % (d) else: p = self.prompt app.open_mini_buffer(p, return_value, method=method, tabber=tabber, startvalue=starting_value, queue=self.queue) class Method(object): _is_method = True args = [] help = "" metadata = {} def __init__(self): self.name = self._name() if self.__doc__: self.help = self.__doc__ def _name(cls): s = cls.__name__ s2 = s[0].lower() for c in s[1:]: if c.isupper(): s2 += '-' + c.lower() elif c == '_': s2 += '-' else: s2 += c return s2 _name = classmethod(_name) def _pre_execute(self, w, **vargs): pass def execute(self, w, **vargs): try: self._pre_execute(w, **vargs) except MethodError, e: w.set_error(str(e)) return for arg in self.args: if arg.name not in vargs: self.old_window = w arg.ask_for_value(self, w, **vargs) return self._execute(w, **vargs) w.buffer.undo_id += 1 def _execute(self, w, **vargs): raise Exception, "Unimplemented Method: %s %r" % (self.name, vargs) class RelexBuffer(Method): '''Relex the buffer; this resets syntax highlighting''' def _execute(self, w, **vargs): h = w.get_highlighter() if h is None: w.set_error("No lexer for buffer.") else: h.highlight(w.buffer.lines) w.set_error("Buffer relexed.") class ToggleWindow(Method): '''Move between visible windows''' def _execute(self, w, **vargs): w.application.toggle_window() # complex text maniuplation class TransposeWords(Method): '''Switch the place of the two words nearest the cursor''' pass # you wanna quit right? class Exit(Method): '''Exit the program, unless there are unsaved changes''' def _execute(self, w, **vargs): a = w.application assert a.mini_buffer_is_open() is False, "Recursive minibuffer antics" changed = False for b in w.application.bufferlist.buffers: changed = b.changed() if changed: break if not changed: w.application.exit() return else: self._old_window = w self._prompt = "There are buffers with unsaved changes; exit anyway? " a.open_mini_buffer(self._prompt, self._callback) def _callback(self, v): a = self._old_window.application if v in ('yes', 'y'): a.exit() a.close_mini_buffer() if v in ('no', 'n'): return a.open_mini_buffer(self._prompt, self._callback) a.set_error('Please type "yes" or "no"') # insert text class InsertString(Method): _is_method = False def __init__(self, s): self.name = "insert-string-%s" % s self.args = [] self.help = "Insert %r into the current buffer." % s self.string = s def _execute(self, w, **vargs): try: w.insert_string_at_cursor(self.string) except buffer.ReadOnlyError: w.set_error('Buffer is read-only') class OverwriteChar(Method): _is_method = False def __init__(self, c): self.name = 'overwrite-char-%s' % c self.args = [] self.help = "Overwrite %r into the current buffer." % c self.char = c def _execute(self, w, **vargs): w.overwrite_char_at_cursor(self.char) class InsertText(Method): '''Insert literal text into the buffer''' args = [arg('text', t="string", p="Literal: ", h='Literal text to insert')] def _execute(self, w, **vargs): w.insert_string_at_cursor(vargs['text']) class InsertText2(Method): '''Insert escaped text into the buffer''' args = [arg('text', t="string", p="Text: ", h='Text to insert')] def _execute(self, w, **vargs): text = vargs['text'].replace('\\n', '\n') text = text.replace('\\t', ' ') text = text.replace('\\\\', '\\') w.insert_string_at_cursor(text) class InsertMultilineText(Method): '''Insert multiple lines into the buffer (M-RETURN to end; C-] to cancel)''' def _execute(self, w, **vargs): f = lambda s: w.insert_string_at_cursor(s) w.application.open_mini_buffer('Multi-Insert: ', f, self, None, 'insertmini') # killing/copying/etc. class Kill(Method): '''Kill the contents of the current line''' def _execute(self, w, **vargs): w.kill_line() class KillRegion(Method): '''Kill the region between the mark and the cursor''' def _execute(self, w, **vargs): w.kill_region() w.set_error("Region killed by %s" % self.name) class Copy(Method): '''Copy the contents of the current line''' def _execute(self, w, **vargs): result = w.copy_line() if result is None: w.set_error("Empty kill region") class CopyRegion(Method): '''Copy the region between the mark and the cursor''' def _execute(self, w, **vargs): w.copy_region() w.set_active_point(w.mark) w.set_error("Region copied") class Yank(Method): '''Paste the top item in the kill ring into the buffer''' def _execute(self, w, **vargs): if w.application.has_kill(): w.yank() else: w.set_error("Kill ring is empty") class ShowKill(Method): '''Display the top item in the kill ring''' def _execute(self, w, **vargs): if w.application.has_kill(): s = w.application.get_kill() x = w.application.x if len(s) > x - 40: s = s[:x - 40] + "..." w.set_error("Kill ring contains %r" % s) else: w.set_error("Kill ring is empty") class PopKill(Method): '''Pop the top item in the kill ring off''' def _execute(self, w, **vargs): if w.application.has_kill(): s = w.pop_kill() x = w.application.x if len(s) > x - 40: s = s[:x - 40] + "..." w.set_error("Removed %r from Kill ring" % s) else: w.set_error("Kill ring is empty") # delete class DeleteLeft(Method): '''Delete the character to the left of the cursor''' def _execute(self, w, **vargs): (x, y) = w.logical_cursor().xy() line = w.buffer.lines[y] tabwidth = w.mode.tabwidth if x >= tabwidth and x % tabwidth == 0 and line[0:x].isspace(): w.delete(Point(x - tabwidth, y), Point(x, y)) else: w.left_delete() class DeleteRight(Method): '''Delete the character under the cursor''' def _execute(self, w, **vargs): cursor = w.logical_cursor() line = w.buffer.lines[cursor.y] lvl = w.mode.tabwidth if len(line[cursor.x:]) >= lvl and line[:cursor.x + lvl].isspace(): w.delete(Point(cursor.x, cursor.y), Point(cursor.x + lvl, cursor.y)) else: w.right_delete() class DeleteLeftWord(Method): '''Delete the from the cursor left to the end of the word''' def _execute(self, w, **vargs): w.delete_left_word() class DeleteRightWord(Method): '''Delete the from under cursor right to the end of the word''' def _execute(self, w, **vargs): w.delete_right_word() class DeleteLeftWhitespace(Method): '''Delete all contiguous of whitespace left of the cursor''' def _execute(self, w, **vargs): 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 (' ', '\n'): p = l l = w.point_left(p) if p < c: w.delete(p, c) class DeleteRightWhitespace(Method): '''Delete all contiguous of whitespace under and right of the cursor''' def _execute(self, w, **vargs): c = w.logical_cursor() p = c while w.point_char(p) in (' ', '\n'): r = w.point_right(p) if r is None: break p = r if p > c: w.delete(c, p) class DeleteLeftSpace(Method): '''Delete all contiguous spaces left of the cursor''' def _execute(self, w, **vargs): 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) == ' ': p = l l = w.point_left(p) if p < c: w.delete(p, c) class DeleteRightSpace(Method): '''Delete all contiguous spaces under and right of the cursor''' def _execute(self, w, **vargs): c = w.logical_cursor() p = c while w.point_char(p) == ' ': r = w.point_right(p) if r is None: break p = r if p > c: w.delete(c, p) # errata class LowercaseWord(Method): '''Lowercase all characters in word''' def _execute(self, w, **vargs): (p1, p2) = w.get_word_bounds() word = w.buffer.get_substring(p1, p2) w.delete(p1, p2) w.insert_string(p1, word.lower()) class UppercaseWord(Method): '''Uppercase all characters in word''' def _execute(self, w, **vargs): (p1, p2) = w.get_word_bounds() word = w.buffer.get_substring(p1, p2) w.delete(p1, p2) w.insert_string(p1, word.upper()) class MetaX(Method): '''Call pmacs functions by name (with or without arguments)''' args = [arg('method', dt="method", p="M-x ", h='Method to execute', q='metax')] name_re = re.compile(r'[a-z0-9_-]+') py_empty_re = re.compile(r'^\( *\)$') py_delim_re = re.compile(r', *') py_end_re = re.compile(r' *\)') pythonstyle = { 'arg_re': re.compile(r'("(?:[^\\"]|\\.)"|[^=),]+)(?= *,| *\))'), 'varg_re': re.compile(r'([a-z0-9_]+)=("(?:[^\\"]|\\.)"|[^=),]+)'), } shellstyle = { 'arg_re': re.compile(r'("(?:[^\\"]|\\.)"|[^= ]+)(?= +|$)'), 'varg_re': re.compile(r'([a-z0-9_]+)=("(?:[^\\"]|\\.)"|[^= ]+)'), } def _parse_arg(self, w, style, other, i): m1 = style['arg_re'].match(other, i) m2 = style['varg_re'].match(other, i) if not (m1 or m2): w.set_error("1couldn't parse %r:%d -> %r" % (other, i, other[i:])) return (None, None, 0) elif m1 and m2: w.set_error("3couldn't parse %r:%d -> %r" % (other, i, other[i:])) return (None, None, 0) if m1: name, value = None, m1.group(1) if value.startswith('"'): value = eval(value) return (None, value, m1.end()) elif m2: name, value = m2.group(1), m2.group(2) if value.startswith('"'): value = eval(value) return (name, value, m2.end()) def _execute(self, w, **vargs): s = vargs['method'].strip() m = self.name_re.match(s) assert m, "invalid cmd %r" % s func = m.group(0) args = [] vargs = {} other = s[m.end():].strip() if not other or self.py_empty_re.match(other): # no arguments pass elif other.startswith('('): # python type call i = 1 while other[i] == ' ': i += 1 while i < len(other): name, value, i = self._parse_arg(w, self.pythonstyle, other, i) if not value: return elif name: vargs[name] = value else: args.append(value) if self.py_end_re.match(other, i): break m = self.py_delim_re.match(other, i) if not m: w.set_error("2couldn't parse %r" % s[i:]) return i = m.end() else: # shell type call i = 0 while i < len(other): if other[i] == ' ': i += 1 continue name, value, i = self._parse_arg(w, self.shellstyle, other, i) if not value: return elif name: vargs[name] = value else: args.append(value) meth = w.application.methods.get(func) if meth is None: w.set_error("method %r not found" % func) return try: for (arg, value) in zip(meth.args, args): vargs[arg.name] = value except: w.set_error("4fail") return meth.execute(w, **vargs) class ToggleMargins(Method): '''Show or hide column margins''' def _execute(self, w, **vargs): w.margins_visible = not w.margins_visible class CenterView(Method): '''Move view to center on cursor''' def _execute(self, w, **vargs): w.center_view(force=True) class SetMark(Method): '''Set the mark to the current cursor location''' def _execute(self, w, **vargs): if w.application.last_action == self.name: w.application.highlight_mark = True w.set_error("Highlighting enabled: %r" % w.application.highlight_mark) else: w.set_mark() class SwitchMark(Method): '''Switch the mark and the cursor locations''' def _execute(self, w, **vargs): w.switch_mark() # insertion methods class InsertNewline(Method): '''Insert newline into buffer at the cursor''' def _execute(self, w, **vargs): w.insert_string_at_cursor('\n') class InsertSpace(Method): '''Insert space into buffer at the cursor''' def _execute(self, w, **vargs): w.insert_string_at_cursor(' ') 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 InsertTab(Method): '''Insert tab into buffer, or tabbify line, depending on mode''' def _execute(self, w, **vargs): cursor = w.logical_cursor() if w.mode.tabber: i = w.mode.tabber.get_level(cursor.y) else: i = None if i is None: w.insert_string_at_cursor(' ' * w.mode.tabwidth) else: j = w.buffer.count_leading_whitespace(cursor.y) if i != j: KillWhitespace().execute(w) 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): cursor = w.logical_cursor() i = w.buffer.count_leading_whitespace(cursor.y) if i > 0: w.delete(Point(0, cursor.y), Point(i, cursor.y)) # tabification class TabBuffer(Method): '''Tabbify every line in the current buffer''' def _execute(self, w, **vargs): y = w.logical_cursor().y it = InsertTab() for i in range(0, len(w.buffer.lines)): w.goto_line(i + 1) it.execute(w) w.goto_line(y + 1) 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): '''Prepend a comment to every line in the current buffer''' def _execute(self, w, **vargs): cursor = w.logical_cursor() if cursor < w.mark: p1 = cursor p2 = w.mark elif w.mark < cursor: p1 = w.mark p2 = cursor else: w.input_line = "Empty kill region" return c = w.mode.commentc or '#' lvl = w.buffer.detect_indent_level(p1.y, p2.y) or 0 for y in range(p1.y, p2.y): if len(w.buffer.lines[y]) < lvl: pad = lvl - len(w.buffer.lines[y]) x = lvl - pad else: pad = 0 x = lvl w.buffer.insert_string(Point(x, y), ' ' * pad + c) class UncommentRegion(Method): '''Remove a comment from every line in the current buffer''' def _execute(self, w, **vargs): cursor = w.logical_cursor() if cursor < w.mark: p1 = cursor p2 = w.mark elif w.mark < cursor: p1 = w.mark p2 = cursor else: w.input_line = "Empty kill region" return commentc = w.mode.commentc or '#' commentre = re.compile('^( *)(%s)' % commentc) for y in range(p1.y, p2.y): line = w.buffer.lines[y] m = commentre.match(line) if not m: continue s1, s2 = m.groups() x1, x2 = len(s1), len(s1) + len(s2) w.buffer.delete(Point(x1, y), Point(x2, y)) # wrapping/justifying/etc class WrapLine(Method): '''Wrap a line of text based on a predefined margin''' limit = 80 space_re = re.compile(' +') def _token_len(self, tokens): l = 0 for t in tokens: l += len(t) return l def _find_line_bounds(self, limit, tokens, x, y): if len(tokens[0]) > limit: i = 1 else: i = 0 l = self._token_len(tokens[:i+1]) while i < len(tokens) and l <= limit: i += 1 l = self._token_len(tokens[:i+1]) while i > 1 and tokens and tokens[i-1].isspace(): token = tokens.pop(i-1) l -= len(token) if x > l: x -= len(token) i -= 1 return i, x, y def _clear_preceeding_spaces(self, tokens, x, y): while tokens and self.space_re.match(tokens[0]): if x > 0: x = max(0, x - len(tokens[0])) del tokens[0] return x, y def _wrap_line(self, limit, line, x, y): tokens = re.findall('[^ ]+| +', line) if self._token_len(tokens) <= limit: return None, None, None lines = [] while tokens and self._token_len(tokens) > limit: i, x, y = self._find_line_bounds(limit, tokens, x, y) s = ''.join(tokens[:i]) lines.append(s) if x > len(s): y += 1 x -= len(s) del tokens[:i] x, y = self._clear_preceeding_spaces(tokens, x, y) if tokens: lines.append(''.join(tokens) + ' ') return lines, x, y def _execute(self, w, **vargs): limit = util.get_margin_limit(w, self.limit) cursor = w.logical_cursor() x, y = cursor.xy() lines, x, y = self._wrap_line(limit, w.buffer.lines[y], x, y) if lines is None: return p1 = Point(0, cursor.y) p2 = Point(len(w.buffer.lines[cursor.y]), cursor.y) w.buffer.delete(p1, p2) p3 = Point(0, cursor.y) w.buffer.insert_lines(p3, lines) w.goto(Point(x, y)) class WrapParagraph(Method): '''Wrap contiguous lines of text based on a predefined margin''' limit = 80 valid_re = re.compile('^()( *)([^ ].*)$') empty_re = re.compile('^ ') def _execute(self, w, **vargs): limit = util.get_margin_limit(w, self.limit) # we will store the start of our paragaph in p1, and also the original # cursor position. p1 = oldc = w.logical_cursor() cur_offset = 0 if self.empty_re.match(w.buffer.lines[p1.y]): return m = self.valid_re.match(w.buffer.lines[p1.y]) if not m: # the line was empty return elif not m.group(1) and m.group(2): # the line had leading whitespace return # see if we are starting in the middle of the paragraph; if so, then # let's find the actual begining, and update p1 accordingly. i = p1.y if i > 1 and w.buffer.lines[i] and not w.buffer.lines[i].startswith(' '): while (i > 1 and w.buffer.lines[i - 1] and not self.empty_re.match(w.buffer.lines[i - 1])): i -= 1 p1 = Point(0, i) # get the first line; strip it, and put it in our new lines list. s1 = w.buffer.lines[p1.y][p1.x:] s2 = s1.rstrip() if p1.y <= oldc.y: cur_offset += len(s1) - len(s2) lines = [s2] # ok, so now let's move forward and find the end of the paragraph. i = p1.y + 1 while (i < len(w.buffer.lines) and w.buffer.lines[i] and not self.empty_re.match(w.buffer.lines[i])): s1 = w.buffer.lines[i] s2 = s1.rstrip() if oldc.y == i: # once we've adjusted all our previous lines, adjust our # stored cursor to keep it's X and Y in sync (set Y to the line # the paragraph started on, increase X by the previous lines # plus added spaces minus removed whitespace. x = p1.x + oldc.x + sum([len(x) + 1 for x in lines]) - cur_offset oldc = Point(x, p1.y) elif i < oldc.y: cur_offset += len(s1) - len(s2) lines.append(s2) i += 1 # stringify our paragraph s = " ".join(lines) # ok, so now we need to find the line breaks newlines = [] while s: # if we have less than the limit left, add it and we're done! if len(s) < limit: newlines.append(s) break # look for the rightmost space within our bounds j = s.rfind(' ', 0, limit) # if we failed to find one, look for the leftmost space if j == -1: j = s.find(' ') # if we failed to find any, use the whole rest of the paragraph if j == -1: j = len(s) # add the next chunk we found and adjust the paragraph newlines.append(s[:j]) s = s[j + 1:] # translate our cursor according to the line breaks we just did. (x, y) = oldc.xy() k = 0 while k < len(newlines) - 1 and x > len(newlines[k]): x = x - len(newlines[k]) - 1 y += 1 k += 1 # kill the old paragraph region, insert the new, and goto the new cursor w.delete(p1, Point(len(w.buffer.lines[i-1]), i-1)) w.insert_lines(p1, newlines) w.goto(Point(x, y)) class CountWords(Method): '''Count the number of words in the document''' def _execute(self, w, **vargs): wcount = 0 pcount = 0 inp = False name = w.buffer.name() for line in w.buffer.lines: c = len(line.split()) if c and not inp: inp = True pcount += 1 elif not c and inp: inp = False wcount += c w.set_error("%d words (%d paragraphs) found in %r" % (wcount, pcount, name)) class JustifyRight(Method): '''Justify text with the previous line right from the cursor by whitespace''' def _execute(self, w, **vargs): DeleteLeftSpace().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): return i = cursor.x while prev_line[i] != ' ': i += 1 if i >= len(prev_line): return while prev_line[i] == ' ': i += 1 if i >= len(prev_line): return s = ' ' * (i - cursor.x) w.insert_string_at_cursor(s) class JustifyLeft(Method): '''Justify text with the previous line left from the cursor by whitespace''' 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: return i = cursor.x while i >= len(prev_line): i -= 1 if i <= 0: return if this_line[i] != ' ': return while prev_line[i] != ' ': i -= 1 if i <= 0: return if this_line[i] != ' ': return while prev_line[i] == ' ': i -= 1 if i >= len(prev_line): return if this_line[i] != ' ': return w.buffer.delete(Point(i, cursor.y), cursor) # undo/redo class Undo(Method): '''Undo last action''' def _execute(self, w, **vargs): w.undo() class Redo(Method): '''Redo last undone action''' def _execute(self, w, **vargs): w.redo() class UnindentBlock(Method): '''Prepend a tab of space to each line in region''' def _execute(self, w, **vargs): lvl = w.mode.tabwidth cursor = w.logical_cursor() if cursor < w.mark: p1 = cursor p2 = w.mark elif w.mark < cursor: p1 = w.mark p2 = cursor else: w.input_line = "Empty kill region" return lines = w.buffer.lines[p1.y:p2.y] for i in range(0, len(lines)): if lines[i].startswith(' '): lines[i] = lines[i][lvl:] w.buffer.delete(Point(0, p1.y), Point(0, p2.y)) w.buffer.insert_string(Point(0, p1.y), '\n'.join(lines) + '\n') class IndentBlock(Method): '''Prepend a tab of space to each line in region''' def _execute(self, w, **vargs): cursor = w.logical_cursor() if cursor < w.mark: p1 = cursor p2 = w.mark elif w.mark < cursor: p1 = w.mark p2 = cursor else: w.input_line = "Empty kill region" return lines = w.buffer.lines[p1.y:p2.y] tstr = ' ' * w.mode.tabwidth for i in range(0, len(lines)): lines[i] = tstr + lines[i] w.buffer.delete(Point(0, p1.y), Point(0, p2.y)) w.buffer.insert_string(Point(0, p1.y), '\n'.join(lines) + '\n') class Diff(Method): '''diff the buffer's contents with the given file''' args = [arg("path1", t=type(""), p="Filename: ", dt='path', h="left path to diff"), arg("path2", t=type(""), p="Filename: ", dt='path', h="right path to diff")] def _get_cmd(self, w, **vargs): return ("/usr/bin/diff", '-u', vargs['path1'], vargs['path2']) def _pipe_write(self, pipe, w, **vargs): pass def _execute(self, w, **vargs): cmd = self._get_cmd(w, **vargs) pipe = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) self._pipe_write(pipe, w, **vargs) outdata = pipe.stdout.read() errdata = pipe.stderr.read() status = pipe.wait() if status == 0: w.set_error("No difference found" + str(status)) elif status == 1: w.application.data_buffer("*Diff*", outdata, switch_to=True, modename='diff') w.set_error("Differences were found") else: w.application.data_buffer("*Diff*", errdata, switch_to=True) w.set_error("There was an error: %d exited with status %s" % (pipe.pid, status)) class FileDiff(Diff): '''diff the buffer's contents with the given file''' args = [arg("path", t=type(""), p="Filename: ", dt='path', h="path to diff against current buffer's contents")] def _get_cmd(self, w, **vargs): return ("/usr/bin/diff", '-u', '-', vargs['path']) def _pipe_write(self, pipe, w, **vargs): indata = w.buffer.make_string() pipe.stdin.write(indata) pipe.stdin.close() class SetMode(Method): '''Set the mode of the current buffer''' args = [arg('mode', dt='mode', p="Enter new mode: ")] def _execute(self, w, **vargs): mode_name = vargs['mode'] m = w.application.modes[mode_name](w) w.set_mode(m) w.set_error('Set mode to %r' % (mode_name)) 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_msg('Cancel') class SplitWindow(Method): '''Split the main window horizontally into upper and lower windows''' def execute(self, w, **vargs): a = w.application a.add_slot() if not w.cursor_is_visible(): w.center_view(force=True) n = len(a.bufferlist.slots) a.set_error('Window has been split into %d windows!' % n) class UnsplitWindow(Method): '''Maximize the current window to fill the screen''' def execute(self, w, **vargs): w.application.single_slot() w.set_error('Window has been unsplit back to one window!') class CloseParen(Method): '''Insert ), matching if applicable''' mytag = ')' def _execute(self, w, **vargs): # first, de-reference some variables and actually do the insertion # NOTE: we derence the cursor *before* inserting the character, so it is # expecected that the cursor variable should be the point the new # character is on. (x, y) = w.logical_cursor().xy() w.insert_string_at_cursor(self.mytag) app = w.application buffer = w.buffer highlighter = buffer.highlights[w.mode.name] tokens = highlighter.tokens # REFACTOR: we have methods in window to do this now i = 0 while i < len(tokens[y]): token = tokens[y][i] if token.x == x and token.string == self.mytag: break elif token.x <= x and token.end_x() > x: return i += 1 if i >= len(tokens[y]): return tag_stack = [] while y >= 0: while i >= 0 and i < len(tokens[y]): token = tokens[y][i] n = token.name s = token.string if n in w.mode.closetokens and s in w.mode.closetags: tag_stack.append(s) elif n in w.mode.opentokens and s in w.mode.opentags: if tag_stack[-1] == w.mode.opentags[s]: del tag_stack[-1] else: app.set_error("tag mismatch; got %r expected %r" % (s, w.mode.closetags[tag_stack[-1]])) return if len(tag_stack) == 0: p = Point(token.x, y) s = w.buffer.lines[p.y][:p.x+1] if len(s) > 60: s = "..." + s[-60:] msg = 'matches %r' % s w.set_active_point(p, msg) return i -= 1 y -= 1 i = len(tokens[y]) - 1 class CloseBrace(CloseParen): '''Insert }, matching if applicable''' mytag = '}' class CloseBracket(CloseParen): '''Insert ], matching if applicable''' mytag = ']' class RegisterSave(Method): '''Save the top item of the kill stack into the named register''' MAX_TXT = 30 MAX_REG = 18 args = [arg('name', dt="register", p="Register name: ", h="Register name to use")] def _pre_execute(self, w, **vargs): if not w.has_kill(): raise MethodError("No text on the kill stack") def _execute(self, w, **vargs): name = vargs['name'] text = w.get_kill() w.application.registers[name] = text if len(name) > self.MAX_REG: name = name[:self.MAX_REG] + '...' if len(text) > self.MAX_TXT: text = text[:self.MAX_TXT] + '...' w.set_error('Saved %r into register %r' % (text, name)) class RegisterRestore(Method): '''Push the value of the named register onto the kill stack''' MAX_TXT = 30 MAX_REG = 18 args = [arg('name', dt="register", p="Register name: ", h="Register name to use")] def _execute(self, w, **vargs): name = vargs['name'] if name not in w.application.registers: w.set_error('Register %r does not exist' % name) return app = w.application text = app.registers[name] w.push_kill(text) if len(text) > self.MAX_TXT: text = text[0:self.MAX_TXT] + '...' if len(name) > self.MAX_REG: name = name[0:self.MAX_REG] + '...' w.set_error('Restored %r from register %r' % (text, name)) class GetConfigVariable(Method): '''View the value of a particular config variables''' args = [arg('name', dt='config', p="Config variable: ", h='Config variable name')] def _execute(self, w, **vargs): name = vargs['name'] if name in w.application.config: value = w.application.config[name] w.set_error("param %r set to %r" % (name, value)) else: w.set_error("param %r is not set" % (name,)) class ViewConfigVariables(Method): '''View the value of all config variables''' def _execute(self, w, **vargs): lines = ["APPLICATION CONFIGURATION VARIABLES\n"] for name in w.application.config: lines.append(" %-20s %r\n" % (name, w.application.config[name])) data = ''.join(lines) w.application.data_buffer('*Config*', data, switch_to=True) class SetConfigVariable(Method): '''Set a particular config variable to a value''' args = [arg('name', dt='config', p="Variable name: ", h='Config variable name'), arg('value', t=type(''), p="Variable value: ", h='Config variable value')] def _execute(self, w, **vargs): name = vargs['name'] found = name in w.application.config try: value = eval(vargs['value']) except: value = vargs['value'] w.application.config[name] = value if found: w.set_error("param %r set to %r" % (name, value)) else: w.set_error("previously unset param %r set to %r" % (name, value)) class ToggleHeader(Method): '''Toggle the visibility of the buffer header''' def _execute(self, w, **vargs): if w.mode.showing_header(): w.mode.disable_header() w.set_error('Header hidden') else: w.mode.enable_header() w.set_error('Header visible') # TODO: rename to left-margin class ToggleLineNumbers(Method): '''Toggle the visibility of the left margin''' def _execute(self, w, **vargs): if w.mode.showing_line_numbers(): w.mode.disable_line_numbers() w.set_error('Line numbers hidden') else: w.mode.enable_line_numbers() w.set_error('Line numbers visible') class SetTabWidth(Method): '''Set the tab-width for the current buffer''' args = [arg('width', t=type(0), p="Tab Width: ", h='New tab width for buffer')] def _execute(self, w, **vargs): w.mode.tabwidth = vargs['width'] w.set_error('Tab width set to %d' % w.mode.tabwidth) class SetModeTabWidth(Method): '''Set the default tab-width for the current mode''' args = [arg('mode', dt='mode', p="Mode: ", h=''), arg('width', t=type(0), p="Default Tab Width: ", h='New default tab width for mode')] def _execute(self, w, **vargs): app, mode = w.application, vargs['mode'] if mode not in app.modes: w.set_error('Mode %r not found' % mode) return app.modes[mode].tabwidth = vargs['width'] w.set_error('Default tab width set to %d' % app.modes[mode].tabwidth) class ViewTokenColors(Method): args = [] def _execute(self, w, **vargs): a = w.application keys = sorted([x for x in a.token_colors.keys()]) l = 0 for key in keys: l = max(l, len(key)) lines = ['Color information for %d tokens:\n' % len(keys), '\n'] for key in keys: c = buffer.colors.get_cbuf_code(*a.token_colors[key]) lines.append('%s%-*s %r\n' % (c, l, key, a.token_colors[key])) a.color_data_buffer("*Token-Colors*", ''.join(lines), switch_to=True) class SetTokenColors(Method): args = [arg('name', p='Token Name: ', h=''), arg('colors', p='Colors: ', h='')] def _execute(self, w, **vargs): name = vargs['name'] colors = tuple([x.strip() for x in vargs['colors'].split(',')]) a = w.application if '*' in name: a.cached_colors = {} nstr = name.replace('.', '\\.').replace('*', '.*') r = re.compile(nstr) count = 0 for name2 in a.token_colors: if r.match(name2): a.token_colors[name2] = colors count += 1 msg = 'Color for %d tokens matching %s set to %r' w.set_error(msg % (count, name, colors)) elif name in a.token_colors: a.cached_colors = {} a.token_colors[name] = colors w.set_error('Color for %s set to %r' % (name, colors))