pmacs3/application.py

843 lines
30 KiB
Python
Raw Normal View History

2007-03-06 10:05:38 -05:00
#!/usr/bin/env python
2007-06-04 03:29:37 -04:00
import curses, curses.ascii, getpass, os, re, string, sets, sys, termios, time
2007-03-06 10:05:38 -05:00
import traceback
2007-06-04 03:29:37 -04:00
import buffer2, bufferlist, color, completer, keyinput, method, minibuffer
2007-06-04 23:05:33 -04:00
import util, window2
2007-06-04 03:29:37 -04:00
from point2 import Point
2007-03-06 10:05:38 -05:00
# modes
2007-06-23 21:41:18 -04:00
import mode2
2007-07-21 11:40:53 -04:00
import mode.mini, mode.search, mode.replace, mode.which
import mode.console, mode.consolemini
2007-08-02 17:43:04 -04:00
import mode.c, mode.python, mode.perl, mode.nasm, mode.sh, mode.sql, mode.java
2007-07-21 11:40:53 -04:00
import mode.blame, mode.diff, mode.dir
import mode.xml, mode.tt, mode.css, mode.javascript, mode.html
import mode.text, mode.mutt
import mode.bds, mode.life
import mode.rst
2007-03-06 10:05:38 -05:00
def run(buffers, jump_to_line=None, init_mode=None):
# save terminal state so we can restore it when the program exits
attr = termios.tcgetattr(sys.stdin)
keyinput.disable_control_chars()
retval = 1
try:
retval = curses.wrapper(run_app, buffers, jump_to_line, init_mode)
except:
traceback.print_exc()
# restore terminal state
termios.tcsetattr(sys.stdin, termios.TCSANOW, attr)
return retval
def run_app(stdscr, buffers, jump_to_line=None, init_mode=None):
a = Application(stdscr, buffers, jump_to_line, init_mode)
a.run()
KILL_RING_LIMIT = 128
WORD_LETTERS = list(string.letters + string.digits)
ERROR_TIMEOUT = -1
#ERROR_TIMEOUT = 2
#DARK_BACKGROUND = False
DARK_BACKGROUND = True
2007-06-13 22:18:41 -04:00
class Application(object):
2007-03-06 10:05:38 -05:00
def __init__(self, stdscr, buffers=[], jump_to_line=None, init_mode=None):
# initalize curses primitives
self.stdscr = stdscr
2007-07-03 12:53:14 -04:00
(self.y, self.x) = self.stdscr.getmaxyx()
2007-03-06 10:05:38 -05:00
# initialize some basic stuff
# each highlighted_range contains three things: [window, start_p, end_p]
self.highlighted_ranges = []
2007-07-03 12:53:14 -04:00
self.mini_active = False
self.mini_buffer = None
self.mini_prompt = ""
self.error_string = ""
self.error_timestamp = None
self.input = keyinput.Handler()
2007-03-06 10:05:38 -05:00
# initialize our colors
if curses.has_colors():
curses.start_color()
try:
curses.use_default_colors()
color.default_color = True
except:
# guess we weren't on 2.4
color.default_color = False
color.init()
# this is how we can change color settings
if curses.can_change_color():
#curses.init_color(curses.COLOR_BLUE, 750, 400, 0)
pass
else:
self.set_error("Dynamic color not available")
# initialize our modes
2007-06-04 23:05:33 -04:00
self.modes = {
2007-07-21 11:40:53 -04:00
'blame': mode.blame.Blame,
'c': mode.c.C,
'console': mode.console.Console,
'consolemini': mode.consolemini.Console,
'diff': mode.diff.Diff,
'dir': mode.dir.Dir,
2007-06-05 00:49:24 -04:00
'fundamental': mode2.Fundamental,
2007-07-21 11:40:53 -04:00
'mini': mode.mini.Mini,
'nasm': mode.nasm.Nasm,
'perl': mode.perl.Perl,
'python': mode.python.Python,
'replace': mode.replace.Replace,
'search': mode.search.Search,
'sh': mode.sh.Sh,
'text': mode.text.Text,
'which': mode.which.Which,
'xml': mode.xml.XML,
'html': mode.html.HTML,
'css': mode.css.CSS,
'life': mode.life.Life,
'mutt': mode.mutt.Mutt,
'javascript': mode.javascript.Javascript,
'sql': mode.sql.Sql,
'template': mode.tt.Template,
'bds': mode.bds.BDS,
'rst': mode.rst.RST,
2007-08-02 17:43:04 -04:00
'java': mode.java.Java,
2007-06-05 00:49:24 -04:00
}
2007-03-06 10:05:38 -05:00
# these are used in this order to determine which mode to open certain
# kinds of files
self.mode_paths = {
'/etc/profile': 'sh',
2007-03-06 10:05:38 -05:00
}
self.mode_basenames = {
'.bashrc': 'sh',
'.bash_profile': 'sh',
'.profile': 'sh',
'components.xml': 'bds',
2007-03-06 10:05:38 -05:00
}
self.mode_extensions = {
2007-06-05 00:49:24 -04:00
'.py': 'python',
'.pl': 'perl',
'.pm': 'perl',
'.t': 'perl',
2007-06-24 11:05:44 -04:00
'.c': 'c',
'.txt': 'text',
2007-06-24 10:29:05 -04:00
'.s': 'nasm',
'.sh': 'sh',
'.bash': 'sh',
2007-06-18 15:09:00 -04:00
'.xml': 'xml',
'.xml.in': 'xml',
2007-07-18 07:22:12 -04:00
'.html': 'html',
'.htm': 'html',
2007-06-25 10:06:16 -04:00
'.js': 'javascript',
2007-06-25 11:49:04 -04:00
'.sql': 'sql',
2007-07-08 15:01:55 -04:00
'.tt': 'template',
'.css': 'css',
'.rst': 'rst',
2007-08-02 17:43:04 -04:00
'.java': 'java',
2007-03-06 10:05:38 -05:00
}
self.mode_detection = {
2007-06-05 00:49:24 -04:00
'python': 'python',
'perl': 'perl',
'sh': 'sh',
'bash': 'sh',
2007-03-06 10:05:38 -05:00
}
# initialize our methods
self.methods = {}
for name in dir(method):
cls = eval("method.%s" % name)
if hasattr(cls, '_is_method') and cls._is_method:
self.methods[cls._name()] = cls()
# create all the insert methods for the character ranges we like
for c in string.letters + string.digits + string.punctuation:
obj = method.InsertString(c)
self.methods[obj.name] = obj
# window/slot height/width
height = self.y - 2
width = self.x - 1
# initialize our buffers
# note that the first buffer in buffers will be initially visible
2007-06-04 03:29:37 -04:00
buffers.append(buffer2.ScratchBuffer())
buffers.append(buffer2.ConsoleBuffer())
2007-03-06 10:05:38 -05:00
self.bufferlist = bufferlist.BufferList(height, width)
self.active_slot = 0
# build windows for our buffers
for b in buffers:
2007-06-24 09:51:43 -04:00
if b.name() == '*Console*':
window2.Window(b, self, height, width, mode_name='console')
else:
window2.Window(b, self, height, width, mode_name=init_mode)
2007-03-06 10:05:38 -05:00
self.bufferlist.add_buffer(b)
2007-06-04 03:29:37 -04:00
self.bufferlist.set_slot(0, buffers[0])
2007-03-06 10:05:38 -05:00
# see if the user has requested that we go to a particular line
if jump_to_line:
2007-06-04 23:05:33 -04:00
w = self.bufferlist.slots[0].window
2007-03-06 10:05:38 -05:00
method.GotoLine().execute(w, lineno=jump_to_line)
# initialize our kill ring and last action
2007-07-02 20:29:27 -04:00
self.kill_ring = []
self.kill_commands = ['kill', 'kill-region']
self.last_action = None
self.last_search = None
2007-03-06 10:05:38 -05:00
self.last_replace_before = None
2007-07-02 20:29:27 -04:00
self.last_replace_after = None
self.registers = {}
2007-03-06 10:05:38 -05:00
# initialize tab handlers
method.DATATYPES['path'] = completer.FileCompleter()
method.DATATYPES['buffer'] = completer.BufferCompleter(self)
method.DATATYPES['command'] = completer.CommandCompleter()
method.DATATYPES['shell'] = completer.ShellCompleter()
method.DATATYPES['method'] = completer.MethodCompleter()
method.DATATYPES['mode'] = completer.ModeCompleter()
method.DATATYPES['perlfunction'] = completer.PerlFunctionCompleter()
# set up curses
self.win = curses.newwin(self.y, self.x, 0, 0)
self.win.leaveok(0)
2007-03-06 10:05:38 -05:00
curses.meta(1)
curses.halfdelay(1)
def globals(self):
return globals()
def locals(self):
return locals()
def add_slot(self):
2007-06-04 03:29:37 -04:00
# XYZ
2007-06-04 23:05:33 -04:00
b = self.bufferlist.slots[self.active_slot].window.buffer
2007-06-04 03:29:37 -04:00
n = self.bufferlist.add_slot()
self.bufferlist.set_slot(n, b)
def remove_slot(self, n):
2007-03-06 10:05:38 -05:00
assert len(self.bufferlist.slots) > 1, "oh no you didn't!"
2007-06-04 03:29:37 -04:00
assert n >= 0 and n < len(self.bufferlist.slots), \
"invalid slot: %r (%r)" % (n, len(self.bufferlist.slots))
self.bufferlist.remove_slot(n)
if self.active_slot > n:
2007-06-13 22:18:41 -04:00
self.active_slot = max(0, self.active_slot - 1) #XYZ
2007-03-06 10:05:38 -05:00
def single_slot(self):
while len(self.bufferlist.slots) > 1:
if self.active_slot == 0:
self.remove_slot(1)
else:
self.remove_slot(0)
2007-06-04 03:29:37 -04:00
def get_window_height_width(self, i):
assert i >= 0 and i < len(self.bufferlist.slots), \
2007-03-06 10:05:38 -05:00
"invalid slot: %r" % slotname
2007-06-04 03:29:37 -04:00
slot = self.bufferlist.slots[i]
2007-03-06 10:05:38 -05:00
return (slot.height, slot.width)
# files and stuff
def open_path(self, path, cipher=None, password=None):
path = os.path.abspath(os.path.realpath(util.expand_tilde(path)))
b = self.get_buffer_by_path(path)
if b is None:
name = os.path.basename(path)
if self.has_buffer_name(name):
i = 1
auxname = '%s/%d' % (name, i)
while self.has_buffer_name(auxname):
i += 1
auxname = '%s/%d' % (name, i)
name = auxname
mode_name = None
if cipher is None:
if not os.path.exists(path) or os.path.isfile(path):
b = buffer2.FileBuffer(path, name=name)
elif os.path.isdir(path):
b = buffer2.DirBuffer(path, name=name)
mode_name = 'dir'
else:
raise Exception, "not a file or dir: %r" % path
elif cipher == 'aes':
if not password:
raise Exception, "password is required"
if not os.path.exists(path) or os.path.isfile(path):
b = buffer2.AesBuffer(path, password, name=name)
else:
raise Exception, "not a file or dir: %r" % path
b.open()
window2.Window(b, self, height=0, width=0, mode_name=mode_name)
self.add_buffer(b)
return b
2007-03-06 10:05:38 -05:00
# mini buffer handling
def get_mini_buffer(self):
return self.mini_buffer
def mini_buffer_is_open(self):
return self.mini_buffer is not None
def open_mini_buffer(self, prompt, callback, method=None, tabber=None, modename=None):
2007-03-06 10:05:38 -05:00
if self.mini_buffer_is_open():
self.close_mini_buffer()
self.mini_prompt = prompt
2007-06-04 23:05:33 -04:00
self.mini_buffer = minibuffer.MiniBuffer(callback, method, tabber, modename)
try:
window2.Window(self.mini_buffer, self, height=1,
width=self.x-1-len(self.mini_prompt)-1)
self.mini_active = True
except minibuffer.MiniBufferError:
self.mini_buffer = None
self.mini_prompt = ''
2007-03-06 10:05:38 -05:00
def exec_mini_buffer(self):
self.mini_buffer.callback(self.mini_buffer.make_string())
self.close_mini_buffer()
def close_mini_buffer(self):
self.mini_active = False
2007-03-06 10:05:38 -05:00
if self.mini_buffer_is_open():
self.mini_buffer.close()
self.mini_buffer = None
self.mini_prompt = ""
assert not self.mini_active
2007-03-06 10:05:38 -05:00
def get_mini_buffer_prompt(self):
return self.mini_prompt
def set_mini_buffer_prompt(self, p):
self.mini_prompt = p
# window handling
def toggle_window(self):
assert 0 <= self.active_slot and self.active_slot < len(self.bufferlist.slots)
2007-06-13 22:18:41 -04:00
self.active_slot = (self.active_slot + 1) % len(self.bufferlist.slots) #XYZ
2007-03-06 10:05:38 -05:00
def window(self):
2007-06-04 23:05:33 -04:00
return self.bufferlist.slots[self.active_slot].window
2007-03-06 10:05:38 -05:00
def active_window(self):
if self.mini_active:
2007-06-04 23:05:33 -04:00
return self.mini_buffer.windows[0]
2007-03-06 10:05:38 -05:00
else:
assert 0 <= self.active_slot and self.active_slot < len(self.bufferlist.slots), \
"0 <= %d < %d" % (self.active_slot, len(self.bufferlist.slots))
2007-06-04 03:29:37 -04:00
i = self.active_slot
return self.bufferlist.slots[i].window
2007-03-06 10:05:38 -05:00
# buffer handling
def file_buffer(self, path, data, switch_to=True):
assert not self.has_buffer_name(path), 'oh no! %r is already open' % path
assert not os.path.exists(path), 'oh no! %r already exists in fs' % path
f = open(path, 'w')
f.write(data)
f.close()
2007-06-04 03:29:37 -04:00
b = buffer2.FileBuffer(path)
2007-03-06 10:05:38 -05:00
b.open()
2007-06-13 11:44:09 -04:00
window2.Window(b, self, height=0, width=0)
2007-03-06 10:05:38 -05:00
self.add_buffer(b)
if switch_to:
self.switch_buffer(b)
def data_buffer(self, name, data, switch_to=True, modename=None):
if self.has_buffer_name(name):
b = self.bufferlist.buffer_names[name]
self.remove_buffer(b)
2007-06-04 03:29:37 -04:00
b = buffer2.DataBuffer(name, data)
2007-03-06 10:05:38 -05:00
if modename is not None:
b.modename = modename
2007-06-13 11:44:09 -04:00
window2.Window(b, self, height=0, width=0)
2007-03-06 10:05:38 -05:00
self.add_buffer(b)
if switch_to:
self.switch_buffer(b)
def get_buffer_by_path(self, path):
return self.bufferlist.get_buffer_by_path(path)
def has_buffer_name(self, name):
return self.bufferlist.has_buffer_name(name)
def get_buffer_by_name(self, name):
return self.bufferlist.get_buffer_by_name(name)
def has_buffer(self, b):
return self.bufferlist.has_buffer(b)
def add_buffer(self, b):
self.bufferlist.add_buffer(b)
def remove_buffer(self, b):
assert b.name() is not "*Scratch*", "can't kill the scratch"
assert self.bufferlist.has_buffer(b), "can't kill what's not there"
assert len(self.bufferlist.buffers) > 1, "can't kill with no other buffers"
self.bufferlist.remove_buffer(b)
b.close()
if self.bufferlist.empty_slot(self.active_slot):
b2 = self.bufferlist.hidden_buffers[0]
self.bufferlist.set_slot(self.active_slot, b2)
def switch_buffer(self, b):
assert self.has_buffer_name(b.name()), "buffer %s does not exist" % (b.name())
assert 0 <= self.active_slot and self.active_slot < len(self.bufferlist.slots)
2007-06-05 00:49:24 -04:00
#self.add_window_to_buffer(b, self.active_slot)
2007-03-06 10:05:38 -05:00
self.bufferlist.set_slot(self.active_slot, b)
def add_window_to_buffer(self, b, slotname):
2007-06-05 00:49:24 -04:00
# XYZ
2007-03-06 10:05:38 -05:00
if not b.has_window(slotname):
slot = self.bufferlist.slots[slotname]
2007-06-04 03:29:37 -04:00
window2.Window(b, self, height=slot.height, width=slot.width)
2007-03-06 10:05:38 -05:00
# error string handling
def set_error(self, s):
self.error_string = s
self.error_timestamp = time.time()
def clear_error(self):
self.error_string = ""
self.error_timestamp = None
def resize_event(self):
2007-07-28 23:08:42 -04:00
(self.y, self.x) = self.stdscr.getmaxyx()
2007-03-06 10:05:38 -05:00
self.resize_slots()
def resize_slots(self):
n = len(self.bufferlist.slots)
2007-06-04 10:06:04 -04:00
assert n > 0
2007-03-06 10:05:38 -05:00
x = self.x - 1
y_sum = self.y - 1 - n
2007-07-28 23:08:42 -04:00
self.bufferlist.resize(y_sum, x)
2007-03-06 10:05:38 -05:00
# exit
def exit(self):
self.done = True
# kill stack manipulation
def push_kill(self, s):
if s is not None:
if self.last_action in self.kill_commands and \
len(self.kill_ring):
2007-03-06 10:05:38 -05:00
self.kill_ring[-1] = self.kill_ring[-1] + s
else:
self.kill_ring.append(s)
2007-07-02 20:29:27 -04:00
if KILL_RING_LIMIT and len(self.kill_ring) > KILL_RING_LIMIT:
2007-03-06 10:05:38 -05:00
self.kill_ring.pop(0)
def pop_kill(self):
return self.kill_ring.pop(-1)
def has_kill(self, i=-1):
return len(self.kill_ring) >= abs(i)
def get_kill(self, i=-1):
return self.kill_ring[i]
# undo/redo
def undo(self):
try:
2007-06-04 03:29:37 -04:00
self.window().undo()
2007-03-06 10:05:38 -05:00
except Exception, e:
self.set_error("%s" % (e))
def redo(self):
try:
2007-06-04 03:29:37 -04:00
self.window().redo()
2007-03-06 10:05:38 -05:00
except Exception, e:
self.set_error("%s" % (e))
# action creating methods
def make_insert_action(self, c):
return lambda: self.window().insert_string(c)
def make_window_action(self, methodname):
f = getattr(self.window(), methodname)
f()
# we are evil
def eval(self, s):
return eval(s)
# the mighty run-loop!
2007-03-06 10:05:38 -05:00
def run(self):
self.done = False
while not self.done:
i = self.win.getch()
if i == curses.KEY_RESIZE:
while i == curses.KEY_RESIZE:
i = self.win.getch()
self.resize_event()
err = ''
try:
self.input.parse(i)
except Exception, e:
err = str(e)
while len(self.input.tokens):
t = self.input.tokens.pop(0)
self.active_window().mode.handle_token(t)
self.draw()
if err:
self.set_error(err)
2007-07-18 07:22:12 -04:00
if self.error_timestamp is not None and ERROR_TIMEOUT > 0 and \
2007-03-06 10:05:38 -05:00
time.time() - self.error_timestamp > ERROR_TIMEOUT:
self.clear_error()
2007-07-28 23:08:42 -04:00
(y, x) = self.stdscr.getmaxyx()
if y != self.y or x != self.x:
self.resize_event()
# clear the error line; it might look confusing to the user
try:
self.win.addstr(self.y-1, 0, ' ' * self.x)
except:
pass
self.win.refresh()
2007-03-06 10:05:38 -05:00
return
# highlighting
# each highlighted_range contains three things: [window, start_p, end_p]
def add_highlighted_range(self, w, p1, p2, fg='default', bg='default'):
self.highlighted_ranges.append([w, p1, p2, fg, bg])
2007-03-06 10:05:38 -05:00
def clear_highlighted_ranges(self):
self.highlighted_ranges = []
# full screen drawer
def draw(self):
2007-07-28 23:08:42 -04:00
try:
self.draw_slots()
self.draw_input_bar()
self.draw_cursor()
self.win.noutrefresh()
curses.doupdate()
except:
# ok, so there was a problem...
# let's see if the screen changed sizes and if so, resize our slots
self.resize_event()
2007-03-06 10:05:38 -05:00
def draw_cursor(self):
if self.mini_active:
b = self.mini_buffer
w = b.windows[0]
p = w.logical_cursor()
if p.y >= len(b.lines):
return
(vy, vx) = (self.y - 1, min(p.x + len(self.mini_prompt), self.x - 2))
else:
slot = self.bufferlist.slots[self.active_slot]
w = slot.window
if w.active_point is not None and w.point_is_visible(w.active_point):
p = w.active_point
else:
p = w.logical_cursor()
count = 0
(x, y) = w.first.xy()
(vy, vx) = (None, None)
while count < slot.height:
if p.y == y and p.x >= x and p.x <= x + slot.width:
(vy, vx) = (slot.offset + count, p.x - x)
break
if x + slot.width >= len(w.buffer.lines[y]):
x = 0
y += 1
else:
x += slot.width
count += 1
if vy is None or vx is None:
return
try:
self.win.move(vy, vx)
except:
raise Exception, "(%d,%d==%r) was illegal (%d,%d)" % \
(vx, vy, p, self.x, self.y)
2007-03-06 10:05:38 -05:00
# sub-drawing methods
def draw_slots(self):
self.win.erase()
for i in range(0, len(self.bufferlist.slots)):
slot = self.bufferlist.slots[i]
self.draw_slot(i)
self.draw_status_bar(i)
2007-07-28 23:08:42 -04:00
def highlight_char(self, sy, sx, fg='default', bg='default'):
2007-06-14 06:10:20 -04:00
junk = self.win.inch(sy, sx)
char = junk & 255
#attr = color.build(fg, bg, curses.A_REVERSE)
attr = color.build(fg, bg)
try:
self.win.addch(sy, sx, char, attr)
except:
raise Exception, "(%d, %d, %r, %r) v. (%d, %d)" % \
(sy, sx, fg, bg, self.y, self.x)
def highlight_chars(self, sy, sx1, sx2, fg='default', bg='default'):
assert sx2 < self.x, "%d < %d" % (sx2, self.x)
2007-06-14 06:10:20 -04:00
for x in range(sx1, sx2):
self.highlight_char(sy, x, fg, bg)
2007-06-14 06:10:20 -04:00
2007-06-04 03:29:37 -04:00
def draw_slot(self, i):
2007-06-13 22:18:41 -04:00
assert self.active_slot < len(self.bufferlist.slots), "only two"
assert i < len(self.bufferlist.slots), "only three"
2007-06-04 03:29:37 -04:00
slot = self.bufferlist.slots[i]
if slot.window is None:
2007-03-06 10:05:38 -05:00
return
2007-06-04 03:29:37 -04:00
w = slot.window
2007-06-05 00:49:24 -04:00
modename = w.mode.name()
2007-06-04 03:29:37 -04:00
if modename in w.buffer.highlights:
self._draw_slot_lit(i)
else:
self._draw_slot_raw(i)
2007-03-06 10:05:38 -05:00
2007-06-14 06:10:20 -04:00
# highlighted regions
for (high_w, p1, p2, fg, bg) in self.highlighted_ranges:
2007-07-03 12:53:14 -04:00
if w is high_w and p2 >= w.first and p1 <= w.last:
2007-06-14 06:10:20 -04:00
count = 0
(x, y) = w.first.xy()
px = p1.x
while count < slot.height:
if p1.y == y and px >= x and px - x < slot.width:
if slot.width > p2.x - x:
self.highlight_chars(slot.offset + count, px-x, p2.x-x, fg, bg)
2007-06-14 06:10:20 -04:00
break
else:
self.highlight_chars(slot.offset + count, px-x, slot.width, fg, bg)
px += slot.width - px + x
2007-06-17 10:55:36 -04:00
if x + slot.width >= len(w.buffer.lines[y]):
2007-06-14 06:10:20 -04:00
x = 0
y += 1
else:
x += slot.width
count += 1
2007-07-03 12:53:14 -04:00
if w.margins_visible:
for (limit, shade) in w.margins:
#if limit <= self.x:
if limit < self.x:
2007-06-04 14:06:07 -04:00
for j in range(0, slot.height):
char = self.win.inch(j + slot.offset, limit) & 255
2007-03-06 10:05:38 -05:00
attr = color.build('default', shade, 'bold')
2007-06-04 14:06:07 -04:00
self.win.addch(j + slot.offset, limit, char, attr)
2007-06-13 11:44:09 -04:00
def _draw_slot_raw(self, i):
slot = self.bufferlist.slots[i]
w = slot.window
modename = w.mode.name()
redattr = color.build_attr(color.pairs('red', 'default'))
(x, y) = w.first.xy()
lines = w.buffer.lines
count = 0
while count < slot.height:
if y >= len(lines):
self.win.addstr(slot.offset + count, 0, '~', redattr)
count += 1
continue
line = lines[y]
s = line[x:x + slot.width]
2007-07-19 14:37:39 -04:00
try:
self.win.addstr(slot.offset + count, 0, s)
except:
self.set_error("addstr(%r + %r, %r, %r)" % (slot.offset, count, 0, s))
if x + slot.width >= len(line):
x = 0
y += 1
else:
self.win.addch(slot.offset + count, slot.width, '\\', redattr)
x += slot.width
count += 1
def _draw_slot_lit(self, i):
slot = self.bufferlist.slots[i]
w = slot.window
modename = w.mode.name()
redattr = color.build_attr(color.pairs('red', 'default'))
highlighter = w.buffer.highlights[modename]
(x, y) = w.first.xy()
j = 0
count = 0
assert len(w.buffer.lines) == len(highlighter.tokens)
while count < slot.height:
if y < len(w.buffer.lines):
while j < len(highlighter.tokens[y]):
token = highlighter.tokens[y][j]
if token.string.endswith('\n'):
tstring = token.string[:-1]
else:
tstring = token.string
assert token.y == y, '%d == %d' % (token.y, y)
s_offset = max(x - token.x, 0)
x_offset = max(token.x - x, 0)
2007-06-21 17:24:56 -04:00
assert x_offset <= slot.width, '%d <= %d' % (x_offset, slot.width)
s = tstring[s_offset:]
token_done = x_offset + len(s) <= slot.width
token_wrap = x_offset + len(s) > slot.width
2007-07-18 07:22:12 -04:00
attr = color.build(*token.color)
self.win.addstr(slot.offset + count, x_offset, s[:slot.width - x_offset], attr)
if token_wrap:
self.win.addch(slot.offset + count, slot.width, '\\', redattr)
x += slot.width
count += 1
if token_done:
j += 1
if count >= slot.height:
break
# we have finished this logical line of tokens
2007-07-18 07:22:12 -04:00
j = x = 0
y += 1
count += 1
else:
self.win.addstr(slot.offset + count, 0, '~', redattr)
count += 1
2007-03-06 10:05:38 -05:00
def draw_status_bar(self, slotname):
slot = self.bufferlist.slots[slotname]
2007-06-04 03:29:37 -04:00
if slot.window is None:
2007-03-06 10:05:38 -05:00
return
2007-07-18 07:22:12 -04:00
w = slot.window
b = w.buffer
2007-03-06 10:05:38 -05:00
cursor = w.logical_cursor()
2007-07-18 07:22:12 -04:00
first = w.first
last = w.last
2007-03-06 10:05:38 -05:00
if b.readonly():
if b.changed():
modflag = '%*'
else:
modflag = '%%'
else:
if b.changed():
modflag = '**'
else:
modflag = '--'
if w.mark:
mark = w.mark
else:
2007-06-04 03:29:37 -04:00
mark = Point(-1, -1)
2007-03-06 10:05:38 -05:00
name = b.name()
if w.first_is_visible():
perc = "Top"
elif w.last_is_visible():
perc = "Bot"
else:
perc = "%2d%%" % (first.y*100 / len(b.lines))
# XYZ: we should actually use more of the 'state' variables
format = "%s %-18s (%s)--L%d--C%d--%s"
status = format % (modflag, name, w.mode.name(), cursor.y+1, cursor.x+1, perc)
#format = "%s %-18s (%s)--L%d--C%d--%s %s %s %s"
#status = format % (modflag, name, w.mode.name(), cursor.y+1, cursor.x+1, perc, w.first, cursor, w.last)
2007-03-06 10:05:38 -05:00
status = status[:slot.width + 1]
status += "-" * (slot.width - len(status) + 1)
self.win.addnstr(slot.height + slot.offset, 0, status, slot.width + 1,
curses.A_REVERSE)
# input bar drawing
def draw_input_bar(self):
if self.error_string:
self.draw_error()
elif self.mini_buffer_is_open():
self.draw_mini_buffer()
else:
self.draw_nothing()
try:
# fucking python, fucking curses, fucking fuck
self.win.addch(self.y-1, self.x-1, ' ')
except:
pass
def draw_error(self):
l = self.x - 1
s1 = self.error_string
s2 = util.cleanse(util.padtrunc(s1, l))
self.win.addnstr(self.y-1, 0, s2, l)
def draw_mini_buffer(self):
l = self.x - 1
2007-06-04 23:05:33 -04:00
b = self.mini_buffer
s1 = self.mini_prompt + b.lines[0]
2007-03-06 10:05:38 -05:00
s2 = util.padtrunc(s1, l)
self.win.addnstr(self.y-1, 0, s2, l)
def draw_nothing(self):
l = self.x - 1
self.win.addnstr(self.y-1, 0, util.pad('', l), l)
def open_aes_file(path, nl, name=None):
2007-07-20 10:51:36 -04:00
if os.path.isfile(path) or not os.path.exists(path):
2007-07-19 14:37:39 -04:00
p = getpass.getpass("Please enter the AES password: ")
return buffer2.AesBuffer(path, p, nl, name)
else:
raise Exception, "can't open %r; unsupported file type" % path
2007-03-06 10:05:38 -05:00
def open_plain_file(path, nl, name=None):
2007-07-20 10:51:36 -04:00
if os.path.isfile(path) or not os.path.exists(path):
2007-07-19 14:37:39 -04:00
return buffer2.FileBuffer(path, nl, name)
elif os.path.isdir(path):
return buffer2.DirBuffer(path, nl, name)
else:
raise Exception, "can't open %r; unsupported file type" % path
2007-03-06 10:05:38 -05:00
if __name__ == "__main__":
2007-07-06 18:27:52 -04:00
ciphers = { 'none': open_plain_file, 'aes': open_aes_file }
linetypes = { 'win': '\r\n', 'mac': '\r', 'unix': '\n' }
2007-03-06 10:05:38 -05:00
import optparse
parser = optparse.OptionParser()
parser.set_defaults(debug=False)
parser.set_defaults(goto=None)
parser.set_defaults(mode=None)
parser.set_defaults(cipher='none')
parser.set_defaults(linetype='unix')
parser.add_option('-d', '--debug', dest='debug', action='store_true',
help='run in debug mode')
parser.add_option('-e', '--encrypt', dest='cipher', metavar='CIPHER',
help='decrypt and encrypt with CIPHER (default: none)')
parser.add_option('-g', '--goto', dest='goto', metavar='NUM', type='int',
help='jump to line NUM of the first argument')
parser.add_option('-l', '--line-end', dest='linetype', metavar='TYPE',
help='use TYPE (win,mac,unix) line endings (default: unix)')
parser.add_option('-m', '--mode', dest='mode', metavar='MODE',
help='open arguments in MODE')
(opts, args) = parser.parse_args()
# if debugging, disable error handling to produce backtraces
if opts.debug:
2007-06-04 23:05:33 -04:00
mode2.DEBUG = True
2007-03-06 10:05:38 -05:00
# we will support using +19 as the first argument to indicate opening the
# first file on line 19 (same as -g 19 or --goto 19)
2007-06-23 16:12:00 -04:00
if len(args) > 0 and args[0].startswith('+'):
2007-03-06 10:05:38 -05:00
opts.goto = int(args[0][1:])
args = args[1:]
2007-06-04 23:05:33 -04:00
if opts.goto is not None:
opts.goto += 1
2007-03-06 10:05:38 -05:00
# figure out which kind of line types we're using
if opts.linetype not in linetypes:
sys.stderr.write('invalid linetype: %r' % opts.linetype)
sys.exit(1)
nl = linetypes[opts.linetype]
# figure out what kind of file open function to use
if opts.cipher not in ciphers:
sys.stderr.write('invalid cipher: %r' % opts.cipher)
sys.exit(2)
f = ciphers[opts.cipher]
# open each path using our callback to get a buffer, open that buffer, etc.
buffers = []
2007-07-18 07:22:12 -04:00
names = sets.Set()
paths = sets.Set()
2007-07-19 20:22:45 -04:00
if not args:
args = ['.']
2007-03-06 10:05:38 -05:00
for path in args:
path = os.path.abspath(os.path.realpath(util.expand_tilde(path)))
if path in paths:
continue
name = os.path.basename(path)
if name in names:
i = 1
auxname = '%s/%d' % (name, i)
while auxname in names:
i += 1
auxname = '%s/%d' % (name, i)
name = auxname
b = f(path, nl, name)
b.open()
buffers.append(b)
paths.add(path)
names.add(name)
# ok, now run our app
run(buffers, opts.goto, opts.mode)