pmacs3/method.py

1356 lines
48 KiB
Python
Raw Normal View History

2007-03-06 10:05:38 -05:00
import os, commands, popen2, re
import buffer, default, point, regex, util, window
DATATYPES = {
"path": None,
"buffer": None,
"method": None,
"command": None,
"shell": None,
"shellcommand": None,
}
class Argument:
def __init__(self, name, type=type(""), datatype=None, prompt=None, help="",
default=default.none, load_default=False):
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
def coerce_to_type(self, value):
if self.type == type(0):
try:
return int(value)
except:
raise Exception, "expected int; got %s" % (repr(value))
else:
return value
def ask_for_value(self, method, w, **vargs):
assert w.application.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)
w.application.close_mini_buffer()
method.execute(w, **vargs2)
tabber = DATATYPES.get(self.datatype, None)
if d is not None:
p = self.prompt + "(%s) " % (d)
else:
p = self.prompt
w.application.open_mini_buffer(p, return_value, method, tabber)
if starting_value:
w.application.mini_buffer.set_data(starting_value)
class Method:
_is_method = True
def __init__(self):
self.name = self._name()
self.help = self._help()
self.args = self._args()
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 _help(self):
return self.__doc__
def _args(self):
return []
def pre_execute(self, w, **vargs):
pass
def execute(self, w, **vargs):
self.pre_execute(w, **vargs)
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)
def _execute(self, w, **vargs):
raise Exception, "Unimplemented Method: %s %r" % (self.name, vargs)
class GotoChar(Method):
'''Jump to the specified character'''
def _args(self):
return [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'''
def _args(self):
return [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'''
def _args(self):
return [Argument("lineno", type=type(0), prompt="Goto line: ")]
def _execute(self, w, **vargs):
n = vargs["lineno"]
# 0 is not a supported input line number
if n == 0:
return
# negative line numbers are offsets from the end
if n < 0:
n = len(w.buffer.lines) - n
# now let's protect the user from their own mistakes
if n < 0:
n = 0
elif n >= len(w.buffer.lines):
n = len(w.buffer.lines) - 1
else:
n -= 1
# ok so go to the line now
w.goto_line(n)
class ForwardLines(Method):
'''Move forward the specified number of characters'''
def _args(self):
return [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'''
def execute(self, w, **vargs):
self.old_cursor = w.logical_cursor().offset(0, 0)
self.old_window = w
self.direction = 'next'
w.application.open_mini_buffer('I-Search: ',
lambda x: None,
self,
None,
'search')
class ReverseSearch(Method):
'''Interactive search; finds previous occurance of text in buffer'''
def execute(self, w, **vargs):
self.old_cursor = w.logical_cursor().offset(0, 0)
self.old_window = w
self.direction = 'previous'
w.application.open_mini_buffer('I-Search: ',
lambda x: None,
self,
None,
'search')
class Replace(Method):
'''Replace occurances of string X with string Y'''
def _args(self):
return [Argument('before', prompt="Replace: ",
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')
# navigating between buffers
class OpenFile(Method):
'''Open file in a new buffer, or go to file's open buffer'''
def _args(self):
return [Argument('filename', datatype="path", prompt="Open File: ")]
def _execute(self, w, **vargs):
path = vargs['filename']
path = os.path.abspath(os.path.realpath(util.expand_tilde(path)))
a = w.application
b = w.application.get_buffer_by_path(path)
if b is None:
name = os.path.basename(path)
if w.application.has_buffer_name(name):
i = 1
auxname = '%s/%d' % (name, i)
while w.application.has_buffer_name(auxname):
i += 1
auxname = '%s/%d' % (name, i)
name = auxname
b = buffer.FileBuffer(path, name=name)
b.open()
(height, width) = a.get_window_height_width(a.active_slot)
window.Window(b, a, height=height, width=width, slot=a.active_slot)
w.application.add_buffer(b)
SwitchBuffer().execute(w, buffername=b.name())
class OpenAesFile(Method):
'''Open AES encrypted file in a new buffer, or go to file's open buffer'''
def _args(self):
return [Argument('filename', datatype="path", prompt="Open AES File: "),
Argument('password', prompt="Use AES Password: ")]
def _execute(self, w, **vargs):
path = vargs['filename']
password = vargs['password']
path = os.path.abspath(os.path.realpath(util.expand_tilde(path)))
a = w.application
if not w.application.has_buffer_name(path):
b = buffer.AesBuffer(path, password)
b.open()
(height, width) = a.get_window_height_width(a.active_slot)
window.Window(b, a, height=height, width=width, slot=a.active_slot)
w.application.add_buffer(b)
SwitchBuffer().execute(w, buffername=path)
class SwitchBuffer(Method):
'''Switch to a different'''
def _args(self):
return [Argument('buffername', datatype="buffer",
prompt="Switch To Buffer: ",
default=default.last_buffer)]
def pre_execute(self, w, **vargs):
a = w.application
assert len(a.bufferlist.buffers) > 1, "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.application.set_error("buffer %r was not found" % name)
class KillBuffer(Method):
'''Close the current buffer'''
def _args(self):
return [Argument('buffername', datatype="buffer",
prompt="Kill Buffer: ",
default=default.current_buffer)]
def _execute(self, w, **vargs):
name = vargs['buffername']
app = w.application
assert name in app.bufferlist.buffer_names, "Buffer %r does not exist" % name
assert name != '*Scratch*', "Can't kill scratch buffer"
b = app.bufferlist.buffer_names[name]
assert not b.changed(), "Buffer %r has been modified" % (name)
if app.bufferlist.is_buffer_visible(b):
app.bufferlist.set_slot(app.active_slot, app.bufferlist.hidden_buffers[0])
app.bufferlist.remove_buffer(b)
b.close()
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'''
def _args(self):
return [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.application.set_error("got %r (%d)" % (path, len(data)))
if w.application.has_buffer_name(path):
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)
class SaveBuffer(Method):
'''Save the contents of a buffer'''
def _execute(self, w, **vargs):
if w.buffer.changed():
w.buffer.save()
w.application.set_error("Wrote %s" % (w.buffer.path))
else:
w.application.set_error("(No changes need to be saved)")
class RelexBuffer(Method):
'''Relex the buffer; this resets syntax highlighting'''
def _execute(self, w, **vargs):
if w.mode.lexer is not None:
w.mode.highlighter.invalidate_tokens()
w.application.set_error("Buffer relexed.")
else:
w.application.set_error("No lexer for buffer.")
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
# exit/exit2
class Exit(Method):
'''Exit the program, unless there are unsaved changes'''
def _execute(self, w, **vargs):
for b in w.application.bufferlist.buffers:
if b.changed():
s = "Buffer %s was modified" % b.name()
w.application.set_error(s)
return
w.application.exit()
class Exit2(Method):
'''Exit the program, discarding unsaved changes'''
def _execute(self, w, **vargs):
w.application.exit()
# 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, window, **vargs):
window.insert_string(self.string)
# 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.application.set_error("Region killed by %s" % self.name)
class Copy(Method):
'''Copy the contents of the current line'''
def _execute(self, w, **vargs):
w.copy_line()
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.application.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.application.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.application.set_error("Kill ring contains %r" % s)
else:
w.application.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.application.set_error("Removed %r from Kill ring" % s)
else:
w.application.set_error("Kill ring is empty")
# delete
class DeleteLeft(Method):
'''Delete the character to the left of the cursor'''
def _execute(self, w, **vargs):
cursor = w.logical_cursor()
line = w.buffer.lines[cursor.y]
if cursor.x >= 4 and line[0:cursor.x].isspace():
w.kill(point.Point(cursor.x-4, cursor.y),
point.Point(cursor.x, cursor.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]
if len(line[cursor.x:]) >= 4 and line[:cursor.x + 4].isspace():
w.kill(point.Point(cursor.x, cursor.y),
point.Point(cursor.x + 4, 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.kill_left_word()
class DeleteRightWord(Method):
'''Delete the from under cursor right to the end of the word'''
def _execute(self, w, **vargs):
w.kill_right_word()
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)
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)
# random stuff
class DumpTokens(Method):
'''Dump all lexical tokens (syntax highlighting debugging)'''
def _execute(self, w, **vargs):
lines = ['Tokens from %s:' % (w.buffer.name())]
for t in w.mode.highlighter.tokens:
lines.append(repr(t))
output = "\n".join(lines)
w.application.data_buffer("token-dump", output, switch_to=True)
class MetaX(Method):
'''Invoke commands by name'''
def _args(self):
return [Argument('method', datatype="method", prompt="M-x ")]
def _execute(self, w, **vargs):
name = vargs['method']
if name in w.application.methods:
w.application.methods[name].execute(w)
else:
w.application.set_error('no method named %r found' % name)
class ToggleMargins(Method):
'''Show or hide column margins'''
def _execute(self, w, **vargs):
a = w.application
a.margins_visible = not a.margins_visible
class CenterView(Method):
'''Move view to center on cursor'''
def _execute(self, window, **vargs):
window.center_view()
class SetMark(Method):
'''Set the mark to the current cursor location'''
def _execute(self, window, **vargs):
window.set_mark()
class SwitchMark(Method):
'''Switch the mark and the cursor locations'''
def _execute(self, window, **vargs):
window.switch_mark()
# insertion methods
class InsertNewline(Method):
'''Insert newline into buffer at the cursor'''
def _execute(self, window, **vargs):
window.insert_string('\n')
class InsertSpace(Method):
'''Insert space into buffer at the cursor'''
def _execute(self, window, **vargs):
window.insert_string(' ')
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)
if i is None:
window.insert_string(' ')
else:
j = window.buffer.count_leading_whitespace(cursor.y)
if i != j:
KillWhitespace().execute(window)
window.insert(point.Point(0, cursor.y), ' ' * i)
else:
window.goto(point.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)
if i > 0:
window.kill(point.Point(0, cursor.y),
point.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
it = InsertTab()
for i in range(0, len(window.buffer.lines)):
window.goto_line(i)
it.execute(window)
window.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:
p1 = cursor
p2 = window.mark
elif window.mark < cursor:
p1 = window.mark
p2 = cursor
else:
window.input_line = "Empty kill region"
return
for y in range(p1.y, p2.y):
window.buffer.insert_string(point.Point(0, y), "#")
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
for y in range(p1.y, p2.y):
if w.buffer.lines[y].startswith("#"):
w.buffer.delete_string(point.Point(0, y), point.Point(1, y))
# wrapping/justifying/etc
class WrapLine(Method):
'''Wrap the current line at 80 characters by word'''
limit = 80
def _execute(self, window, **vargs):
cursor = window.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(' ')
else:
break
if move_old_cursor:
move_old_cursor = False
old_cursor.x -= j + 1
old_cursor.y += 1
window.goto(point.Point(j, i))
window.right_delete()
window.insert_string('\n')
i += 1
l = len(window.buffer.lines[old_cursor.y])
if l > old_cursor.x:
window.goto(old_cursor)
else:
window.goto(point.Point(l, old_cursor.y))
class WrapParagraph(Method):
limit = 80
wrapper = WrapLine
def _execute(self, w, **vargs):
old_cursor = w.logical_cursor().copy()
i = old_cursor.y
while i < len(w.buffer.lines) - 1:
if i < len(w.buffer.lines) and \
regex.whitespace.match(w.buffer.lines[i + 1]):
break
EndOfLine().execute(w)
InsertSpace().execute(w)
DeleteRightWhitespace().execute(w)
w.goto(old_cursor)
self.wrapper().execute(w)
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]
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)
window.insert_string(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]
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
window.buffer.delete_string(point.Point(i, cursor.y), cursor)
# undo/redo
class Undo(Method):
'''Undo last action'''
def _execute(self, window, **vargs):
try:
window.buffer.undo()
except Exception, e:
window.application.set_error("%s" % (e))
class Redo(Method):
'''Redo last undone action'''
def _execute(self, window, **vargs):
try:
window.buffer.redo()
except Exception, e:
window.application.set_error("%s" % (e))
# window navigation methods
class StartOfLine(Method):
'''Move the cursor to the start of the current line'''
def _execute(self, window, **vargs):
window.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()
class Forward(Method):
'''Move the cursor right one character'''
def _execute(self, window, **vargs):
window.forward()
class Backward(Method):
'''Move the cursor left one character'''
def _execute(self, window, **vargs):
window.backward()
class NextLine(Method):
'''Move the cursor down one line'''
def _execute(self, window, **vargs):
window.next_line()
class PreviousLine(Method):
'''Move the cursor up one line'''
def _execute(self, window, **vargs):
window.previous_line()
class PageUp(Method):
'''Move the cursor up one page'''
def _execute(self, window, **vargs):
window.page_up()
class PageDown(Method):
'''Move the cursor down one page'''
def _execute(self, window, **vargs):
window.page_down()
class GotoBeginning(Method):
'''Move the cursor to the beginning of the buffer'''
def _execute(self, window, **vargs):
window.goto_beginning()
class GotoEnd(Method):
'''Move the cursor to the end of the buffer'''
def _execute(self, window, **vargs):
window.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()
class LeftWord(Method):
'''Move the cursor to the start of the word to the left'''
def _execute(self, window, **vargs):
window.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):
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][4:]
w.buffer.delete_string(point.Point(0, p1.y), point.Point(0, p2.y))
w.buffer.insert_string(point.Point(0, p1.y), '\n'.join(lines) + '\n')
class IndentBlock(Method):
'''Add 4 spaces 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]
for i in range(0, len(lines)):
lines[i] = ' ' + lines[i]
w.buffer.delete_string(point.Point(0, p1.y), point.Point(0, p2.y))
w.buffer.insert_string(point.Point(0, p1.y), '\n'.join(lines) + '\n')
class CodeComplete(Method):
'''Complete based on tokenized strings'''
def execute(self, w, **vargs):
cursor = w.logical_cursor()
if len(w.buffer.lines[cursor.y]) == 0:
return
elif cursor.x == 0:
p1 = w.find_left_word(cursor.offset(1, 0))
p2 = w.find_right_word()
else:
p1 = w.find_left_word()
p2 = w.find_right_word(cursor.offset(-1, 0))
word = w.buffer.get_substring(p1, p2)
seen = {}
sofar = None
tokens = w.mode.highlighter.get_tokens()
for token in tokens:
s = token.string
if s == word:
continue
elif s.startswith(word):
seen[s] = True
if sofar is None:
sofar = s
else:
l = min(len(s), len(sofar))
i = len(word)
while i < l:
if s[i] == sofar[i]:
i += 1
else:
break
sofar = s[:i]
seen_keys = seen.keys()
num_seen = len(seen_keys)
if word == sofar:
#w.application.set_error('No completion possible: %r' % word)
pass
elif sofar:
w.buffer.delete_string(p1, p2)
w.buffer.insert_string(p1, sofar)
if num_seen == 1:
w.application.set_error('Unique!')
else:
w.application.set_error('Ambiguous: %r' % seen_keys)
else:
#w.application.set_error('No completion found: %r' % word)
pass
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*'):
a.add_buffer(buffer.ConsoleBuffer())
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, 'console')
class ShellCmd(Method):
'''Run a command in a shell and put the output in a new buffer'''
def _args(self):
return [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'''
def _args(self):
return [Argument("path", type=type(""), prompt="Filename: ", datatype='path')]
def _execute(self, w, **vargs):
cmd = ("/usr/bin/diff", '-u', '-', vargs['path'])
pipe = popen2.Popen3(cmd, capturestderr=True)
pid = pipe.pid
indata = w.buffer.make_string()
pipe.tochild.write(indata)
pipe.tochild.close()
outdata = pipe.fromchild.read()
errdata = pipe.childerr.read()
status = pipe.wait() >> 8
if status == 0:
w.application.set_error("No difference found")
elif status == 1:
w.application.data_buffer("*Diff*", outdata, switch_to=True, modename='diff')
w.application.set_error("Differences were found")
else:
w.application.data_buffer("*Diff*", errdata, switch_to=True)
w.application.set_error("There was an error: %d exited with status %s" % (pid, status))
class SvnDiff(Method):
'''diff the current file with the version in SVN'''
def _execute(self, w, **vargs):
if not hasattr(w.buffer, 'path'):
w.application.set_error("Buffer has no corresponding file")
return
cmd = "svn diff %r" % w.buffer.path
(status, data) = commands.getstatusoutput(cmd)
if status == 0:
if data:
w.application.data_buffer("*Diff*", data, switch_to=True, modename='diff')
w.application.set_error("Differences were found")
else:
w.application.set_error("No difference found")
else:
w.application.set_error("There was an error (%s)" % (status))
class SvnBlame(Method):
'''show blame output for the current version in SVN'''
line_re = re.compile('^ *(\d+) *([a-zA-Z0-9_]+) *([-0-9]+) *([:0-9]+) *(-\d{4}) *\(([^\)]+)\) (.*)$')
def _execute(self, w, **vargs):
if not hasattr(w.buffer, 'path'):
w.application.set_error("Buffer has no corresponding file")
return
cmd = ("/usr/bin/svn", 'blame', '-v', w.buffer.path)
pipe = popen2.Popen3(cmd)
lines = []
for line in pipe.fromchild:
m = self.line_re.match(line)
if not m:
raise Exception, line
(rev, user, date, t, tz, vdate, content) = m.groups()
lines.append("%-4s %-10s %10s %s\n" % (rev, user, date, content))
data = ''.join(lines)
status = pipe.wait() >> 8
if status == 0:
w.application.data_buffer("*Blame*", data, switch_to=True, modename='blame')
else:
w.application.set_error("There was an error (%s)" % (status))
class CvsStatus(Method):
regex1 = re.compile('^File: (.+?) +?\tStatus: (.*?)$')
regex2 = re.compile('^ Working revision:\t([0-9\.]+)$')
regex3 = re.compile('^ Repository revision:\t([0-9\.]+)\t(.*)$')
regex4 = re.compile('^ Sticky Tag:\t\t\((.*)\)$')
regex5 = re.compile('^ Sticky Date:\t\t\((.*)\)$')
regex6 = re.compile('^ Sticky Options:\t\((.*)\)$')
def _execute(self, w, **vargs):
if not hasattr(w.buffer, 'path'):
w.application.set_error("Buffer has no corresponding file")
return
cwd = os.getcwd() + os.path.sep
path = w.buffer.path
if path.startswith(cwd):
path = path[len(cwd):]
cmd = "cvs status %r" % path
(status, data) = commands.getstatusoutput(cmd)
status = status >> 8
if status != 0:
w.application.set_error("Problems with CVS status: %d" % status)
return
lines = data.split('\n')
if lines[0].startswith('cvs status: nothing known about '):
w.application.set_error('File is not under CVS control')
return
m = self.regex1.match(lines[1])
assert m, "regex1 %r" % lines[1]
ffile = m.group(1)
fstatus = m.group(2)
m = self.regex2.match(lines[3])
assert m, "regex2 %r" % lines[3]
wrev = m.group(1)
m = self.regex3.match(lines[4])
assert m, "regex3 %r" % lines[4]
rrev = m.group(1)
rpath = m.group(2)
m = self.regex4.match(lines[5])
assert m, "regex4 %r" % lines[5]
stag = m.group(1)
m = self.regex5.match(lines[6])
assert m, "regex5 %r" % lines[6]
sdate = m.group(1)
m = self.regex6.match(lines[7])
assert m, "regex6 %r" % lines[7]
soptions = m.group(1)
w.application.set_error('%s %s %s/%s [%s|%s|%s]' % \
(ffile, fstatus, wrev, rrev, stag, sdate, soptions))
2007-03-06 10:05:38 -05:00
class CvsDiff(Method):
'''diff the current file with the version in CVS'''
def _execute(self, w, **vargs):
if not hasattr(w.buffer, 'path'):
w.application.set_error("Buffer has no corresponding file")
return
cwd = os.getcwd() + os.path.sep
path = w.buffer.path
if path.startswith(cwd):
path = path[len(cwd):]
cmd = "cvs diff -u %r" % path
(status, data) = commands.getstatusoutput(cmd)
status = status >> 8
if status == 0:
w.application.set_error("No difference found")
else:
w.application.data_buffer("*Diff*", data, switch_to=True, modename='diff')
w.application.set_error("Differences were found")
class CvsDiff2(Method):
'''diff the current file with the version in CVS'''
rev_regex = re.compile('^[0-9]+\.[0-9]+$')
def _args(self):
return [Argument("revision", type=type(""), prompt="Old Revision: ")]
def _execute(self, w, **vargs):
if not hasattr(w.buffer, 'path'):
w.application.set_error("Buffer has no corresponding file")
return
rev = vargs['revision']
if not self.rev_regex.match(rev):
w.application.set_error("Could not parse revision: %r" % rev)
return
cwd = os.getcwd() + os.path.sep
path = w.buffer.path
if path.startswith(cwd):
path = path[len(cwd):]
cmd = "cvs diff -r %s -u %r" % (rev, path)
(status, data) = commands.getstatusoutput(cmd)
status = status >> 8
if status == 0:
w.application.set_error("No difference found")
else:
w.application.data_buffer("*Diff*", data, switch_to=True, modename='diff')
w.application.set_error("Differences were found")
class CvsDiff3(Method):
'''diff the current file with the version in CVS'''
rev_regex = re.compile('^[0-9]+\.[0-9]+$')
def _args(self):
return [Argument("revision1", type=type(""), prompt="Old Revision: "),
Argument("revision2", type=type(""), prompt="New Revision: ")]
def _execute(self, w, **vargs):
if not hasattr(w.buffer, 'path'):
w.application.set_error("Buffer has no corresponding file")
return
rev1 = vargs['revision1']
if not self.rev_regex.match(rev1):
w.application.set_error("Could not parse revision1: %r" % rev)
return
rev2 = vargs['revision2']
if not self.rev_regex.match(rev2):
w.application.set_error("Could not parse revision2: %r" % rev)
return
cwd = os.getcwd() + os.path.sep
path = w.buffer.path
if path.startswith(cwd):
path = path[len(cwd):]
cmd = "cvs diff -r %s -r %s -u %r" % (rev1, rev2, path)
(status, data) = commands.getstatusoutput(cmd)
status = status >> 8
2007-03-06 10:05:38 -05:00
if status == 0:
w.application.set_error("No difference found")
else:
w.application.data_buffer("*Diff*", data, switch_to=True, modename='diff')
w.application.set_error("Differences were found")
class CvsBlame(Method):
'''show blame output for the current version in SVN'''
line_re = re.compile('^([0-9.]+) +\(*([a-zA-Z0-9_]+) +([-0-9A-Za-z]+)\): (.*)$')
def _execute(self, w, **vargs):
if not hasattr(w.buffer, 'path'):
w.application.set_error("Buffer has no corresponding file")
return
cwd = os.getcwd() + os.path.sep
path = w.buffer.path
if path.startswith(cwd):
path = path[len(cwd):]
cmd = ("/usr/bin/cvs", 'annotate', path)
pipe = popen2.Popen3(cmd, capturestderr=True)
tokens = []
max_rev = 0
max_user = 0
for line in pipe.fromchild:
m = self.line_re.match(line)
if not m:
raise Exception, line
(rev, user, date, content) = m.groups()
max_rev = max(max_rev, len(rev))
max_user = max(max_user, len(user))
tokens.append((rev, user, date, content))
lines = []
fmt = "%%-%ds %%-%ds %%9s %%s\n" % (max_rev, max_user)
for (rev, user, date, content) in tokens:
lines.append(fmt % (rev, user, date, content))
data = ''.join(lines)
status = pipe.wait() >> 8
if status == 0:
w.application.data_buffer("*Blame*", data, switch_to=True, modename='blame')
else:
w.application.set_error("There was an error (%s)" % (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'''
def _args(self):
return [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'''
def _args(self):
return [Argument('mode', datatype='mode', prompt="Enter new mode: ")]
def _execute(self, w, **vargs):
mode_name = vargs['mode']
m = w.application.modes[mode_name](w)
w.set_mode(m)
w.application.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):
w.application.close_mini_buffer()
w.application.set_error('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()
w.relocate_cursor()
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.application.set_error('Window has been unsplit back to one window!')
class SomethingCrazy(Method):
def _execute(self, w, **vargs):
pass
class CloseTag(Method):
tags = {'(': ')',
'{': '}',
'[': ']'}
mytag = ')'
def _execute(self, w, **vargs):
# if w.mode doesn't do tag matching, just insert the character return
if not w.mode.tag_matching:
w.insert_string(self.mytag)
return
# 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.
buffer = w.buffer
#cursor = w.physical_cursor()
tag_stack = []
w.insert_string(self.mytag)
cursor = w.physical_cursor()
assert cursor.x > 0, "my assumptions made an ass out of u and me"
# find the token for the current region, to see if it is a parenthesis
# token or not. if not (i.e. part of a comment or string) then return.
regions = w.mode.get_regions()
assert len(regions[cursor.y]) > 0, "no regions found; strange"
i = 0
found = False
while i < len(regions[cursor.y]):
r = regions[cursor.y][i]
if r[0] == cursor.x - 1 and r[1] == cursor.x and r[3] == self.mytag:
found = True
break
elif r[0] <= cursor.x - 1and r[1] >= cursor.x:
# in this case, we weren't adding a closing tag, but something
# else (a parenthesis in a comment or string, for instance)
return
i += 1
assert found, "fascinizing: %d %s" % \
(cursor.x, repr(regions[cursor.y][-3:]))
# note that the first region we parse should be the region we just added
# (and which we already verified existed), namely the region at index i.
j = cursor.y
while j >= 0:
if j == cursor.y:
index = i
else:
index = len(regions[j]) - 1
while index >= 0:
region = regions[j][index]
s = region[3]
if s in self.tags:
if tag_stack[-1] == self.tags[s]:
tag_stack.pop(-1)
else:
msg = "tag mismatch: %r vs. %r" % (s, tag_stack[-1])
raise Exception, msg
elif s == ')' or s == ']' or s == '}':
tag_stack.append(s)
if len(tag_stack) == 0:
break
else:
index -= 1
if len(tag_stack) == 0:
break
else:
j -= 1
assert len(tag_stack) == 0, "no match for %r found" % (tag_stack[0])
p = w.logical_point(point.Point(region[0], j))
w.set_active_point(p, msg='match found on line %(y)d, at character %(x)d')
class CloseParen(CloseTag):
mytag = ')'
class CloseBrace(CloseTag):
mytag = '}'
class CloseBracket(CloseTag):
mytag = ']'