2007-03-06 10:05:38 -05:00
|
|
|
#!/usr/bin/env python
|
2009-07-23 10:04:12 -04:00
|
|
|
import curses
|
|
|
|
from getpass import getpass
|
2009-06-09 23:37:43 -04:00
|
|
|
import locale
|
2009-07-23 10:04:12 -04:00
|
|
|
import os
|
|
|
|
import string
|
2008-04-04 16:12:31 -04:00
|
|
|
from subprocess import Popen, PIPE, STDOUT
|
2009-07-23 10:04:12 -04:00
|
|
|
import sys
|
|
|
|
import termios
|
|
|
|
import time
|
|
|
|
import traceback
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2009-07-23 10:04:12 -04:00
|
|
|
import buffer
|
|
|
|
import buffer.about
|
|
|
|
import buffer.colors
|
|
|
|
import buffer.console
|
|
|
|
import buffer.data
|
|
|
|
import buffer.fs
|
|
|
|
import buffer.aes
|
|
|
|
|
|
|
|
from bufferlist import BufferList
|
|
|
|
import color
|
|
|
|
import completer
|
|
|
|
import keyinput
|
|
|
|
import method
|
2009-07-06 22:48:34 -04:00
|
|
|
from minibuffer import MiniBuffer, MiniBufferError
|
2009-07-23 10:04:12 -04:00
|
|
|
import mode
|
|
|
|
import util
|
|
|
|
from window import Window
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2009-11-11 00:54:08 -05:00
|
|
|
class PmcError(Exception): pass
|
|
|
|
class RunError(PmcError): pass
|
|
|
|
class FileError(PmcError): pass
|
|
|
|
class DrawError(PmcError): pass
|
|
|
|
|
2007-06-13 22:18:41 -04:00
|
|
|
class Application(object):
|
2009-02-23 22:35:02 -05:00
|
|
|
def __init__(self, stdscr, buffers=[], **kwargs):
|
2007-03-06 10:05:38 -05:00
|
|
|
# initalize curses primitives
|
|
|
|
self.stdscr = stdscr
|
2008-11-08 10:30:04 -05:00
|
|
|
self.y, self.x = self.stdscr.getmaxyx()
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2009-06-09 11:00:41 -04:00
|
|
|
# we have to do this early so we can log errors
|
|
|
|
self.log = buffer.LogBuffer()
|
|
|
|
|
2007-03-06 10:05:38 -05:00
|
|
|
# initialize some basic stuff
|
2008-03-20 21:58:30 -04:00
|
|
|
self.config = {}
|
2007-03-06 10:05:38 -05:00
|
|
|
self.highlighted_ranges = []
|
2009-03-19 00:23:09 -04:00
|
|
|
self.highlight_mark = False
|
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
|
2008-11-08 10:30:04 -05:00
|
|
|
self.need_draw = True
|
2007-07-03 12:53:14 -04:00
|
|
|
self.input = keyinput.Handler()
|
2009-08-20 00:29:04 -04:00
|
|
|
|
|
|
|
# white is for delimiters, operators, numbers
|
|
|
|
default = ('default', 'default')
|
|
|
|
|
|
|
|
# magenta is for keywords/builtins, translation, globs
|
2009-11-11 23:45:03 -05:00
|
|
|
#lo_magenta = ('magenta202', 'default')
|
2009-08-20 00:29:04 -04:00
|
|
|
hi_magenta = ('magenta505', 'default')
|
|
|
|
|
|
|
|
# red is for comments, pods, endblocks
|
2009-11-11 23:45:03 -05:00
|
|
|
#lo_red = ('red300', 'default')
|
2009-08-20 00:29:04 -04:00
|
|
|
hi_red = ('red511', 'default')
|
|
|
|
|
|
|
|
# orange are for arrays and hashes
|
2009-11-11 23:45:03 -05:00
|
|
|
#hi_orange = ('yellow531', 'default')
|
|
|
|
#lo_orange = ('yellow520', 'default')
|
2009-08-20 00:29:04 -04:00
|
|
|
|
|
|
|
# yellow is for scalars and prototypes
|
2009-11-11 23:45:03 -05:00
|
|
|
#hi_yellow = ('yellow551', 'default')
|
|
|
|
#lo_yellow = ('yellow330', 'default')
|
2009-08-20 00:29:04 -04:00
|
|
|
|
|
|
|
# green is for strings and hash keys
|
|
|
|
lo_green = ('green030', 'default')
|
|
|
|
hi_green = ('green050', 'default')
|
|
|
|
|
|
|
|
# cyan is for quotes, evals, regexes, subs
|
2009-11-11 23:45:03 -05:00
|
|
|
#lo_cyan = ('cyan033', 'default')
|
2009-08-20 00:29:04 -04:00
|
|
|
hi_cyan = ('cyan155', 'default')
|
|
|
|
|
|
|
|
# blue is unused
|
2009-11-11 23:45:03 -05:00
|
|
|
#lo_blue = ('blue113', 'default')
|
2009-08-20 00:29:04 -04:00
|
|
|
hi_blue = ('blue225', 'default')
|
2008-11-08 10:30:04 -05:00
|
|
|
|
2008-03-16 02:16:41 -04:00
|
|
|
# let's prepopulate some default token colors
|
2009-04-05 13:05:42 -04:00
|
|
|
self.cached_colors = {}
|
2008-03-16 02:16:41 -04:00
|
|
|
self.token_colors = {
|
2009-08-20 00:29:04 -04:00
|
|
|
'spaces': default,
|
|
|
|
'eol': default,
|
|
|
|
'comment': hi_red,
|
|
|
|
'comment.start': hi_red,
|
|
|
|
'comment.data': hi_red,
|
|
|
|
'comment.null': hi_red,
|
|
|
|
'comment.end': hi_red,
|
|
|
|
'continuation': hi_red,
|
|
|
|
'escaped': hi_magenta,
|
|
|
|
'string.start': lo_green,
|
|
|
|
'string.data': hi_green,
|
|
|
|
'string.null': hi_green,
|
|
|
|
'string.hex': hi_magenta,
|
|
|
|
'string.octal': hi_magenta,
|
|
|
|
'string.escaped': hi_magenta,
|
|
|
|
'string.end': lo_green,
|
|
|
|
'char': hi_green,
|
|
|
|
#'integer': ('white533', 'default'),
|
|
|
|
#'float': ('white533', 'default'),
|
|
|
|
#'number': ('white533', 'default'),
|
|
|
|
'integer': default,
|
|
|
|
'float': default,
|
|
|
|
'number': default,
|
|
|
|
'label': hi_magenta,
|
|
|
|
'keyword': hi_cyan,
|
|
|
|
'reserved': hi_cyan,
|
|
|
|
'function': hi_blue,
|
|
|
|
'builtin': hi_magenta,
|
|
|
|
'method': hi_cyan,
|
|
|
|
'bareword': default,
|
|
|
|
'delimiter': default,
|
|
|
|
'operator': default,
|
2008-03-16 02:16:41 -04:00
|
|
|
}
|
2008-03-16 10:53:35 -04:00
|
|
|
self.default_color = ('default', 'default',)
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2008-11-08 12:44:59 -05:00
|
|
|
# initialize configuration defaults
|
2008-10-15 17:17:16 -04:00
|
|
|
self._load_config_defaults()
|
|
|
|
|
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:
|
|
|
|
color.default_color = False
|
2009-01-15 19:07:53 -05:00
|
|
|
color.init()
|
2007-03-06 10:05:38 -05:00
|
|
|
|
|
|
|
# initialize our modes
|
2007-10-18 17:07:35 -04:00
|
|
|
# the first dict stores our modes by (lowercase) name, and the other
|
|
|
|
# dicts all store various ways to "auto-detecting" the correct mode,
|
|
|
|
# in the precedence with which they are used.
|
|
|
|
self.modes = {}
|
|
|
|
self.mode_paths = {}
|
|
|
|
self.mode_basenames = {}
|
|
|
|
self.mode_extensions = {}
|
|
|
|
self.mode_detection = {}
|
|
|
|
|
2007-03-06 10:05:38 -05:00
|
|
|
# initialize our methods
|
|
|
|
self.methods = {}
|
2008-11-12 10:52:01 -05:00
|
|
|
names = (
|
|
|
|
'method', 'method.svn', 'method.cvs', 'method.search',
|
|
|
|
'method.buffers', 'method.move', 'method.shell',
|
|
|
|
'method.introspect', 'method.help', 'method.numbers',
|
2010-08-16 23:45:33 -04:00
|
|
|
'method.spell', 'method.hg', 'method.utf8', 'method.tags',
|
2010-10-15 11:44:34 -04:00
|
|
|
'method.git',
|
2008-11-12 10:52:01 -05:00
|
|
|
)
|
2008-03-17 03:16:15 -04:00
|
|
|
for name in names:
|
2011-11-08 22:32:03 -05:00
|
|
|
self.load_pmacs_methods(name)
|
|
|
|
#exec("import %s" % name)
|
|
|
|
#mod = eval(name)
|
|
|
|
#for mname in dir(mod):
|
|
|
|
# if mname.startswith('_'):
|
|
|
|
# continue
|
|
|
|
# cls = eval("%s.%s" % (name, mname))
|
|
|
|
# if hasattr(cls, '_is_method') and cls._is_method:
|
|
|
|
# self.methods[cls._name()] = cls()
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2008-04-17 10:55:02 -04:00
|
|
|
# ok, now let's load all the "standard" modes
|
|
|
|
mode.install(self)
|
2009-07-24 00:15:36 -04:00
|
|
|
names = (
|
|
|
|
'blame', 'c', 'console', 'consolemini', 'css', 'diff', 'dir',
|
2011-12-15 23:51:11 -05:00
|
|
|
'elisp', 'hex', 'html', 'java', 'javap', 'javascript', 'lisp', 'make',
|
2009-07-24 00:15:36 -04:00
|
|
|
'mini', 'mutt', 'nasm', 'ocaml', 'perl', 'python', 'replace', 'rst',
|
|
|
|
'scheme', 'search', 'sh', 'sql', 'tt', 'text', 'text2', 'which',
|
|
|
|
'xml', 'cheetah', 'colortext', 'latex', 'insertmini', 'conf',
|
|
|
|
'haskell', 'erlang', 'iperl', 'iperlmini', 'ipython', 'ipythonmini',
|
|
|
|
'awk', 'shell', 'shellmini', 'fstab', 'yacc', 'pipe', 'mbox',
|
2009-11-11 17:54:12 -05:00
|
|
|
'error', 'lua', 'lily', 'forth', 'ebnf', 'colortest', 'go',
|
2012-09-27 21:47:42 -04:00
|
|
|
'inform6', 'scala', 'markdown', 'roy',
|
2008-05-15 03:20:41 -04:00
|
|
|
)
|
2008-04-17 10:55:02 -04:00
|
|
|
for name in names:
|
|
|
|
exec("import mode.%s; mode.%s.install(self)" % (name, name))
|
|
|
|
|
2007-03-06 10:05:38 -05:00
|
|
|
# create all the insert methods for the character ranges we like
|
2009-06-13 21:46:38 -04:00
|
|
|
for c in string.ascii_letters + string.digits + string.punctuation:
|
2008-11-08 19:06:22 -05:00
|
|
|
obj = method.InsertString(c)
|
|
|
|
self.methods[obj.name] = obj
|
|
|
|
obj = method.OverwriteChar(c)
|
|
|
|
self.methods[obj.name] = obj
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2008-11-08 11:18:48 -05:00
|
|
|
# buffer list stuff
|
2008-11-08 13:32:32 -05:00
|
|
|
height = self.y - 1
|
|
|
|
width = self.x
|
2009-07-23 10:04:12 -04:00
|
|
|
self.bufferlist = BufferList(height, width)
|
2008-05-03 22:41:58 -04:00
|
|
|
self.active_slot = 0
|
|
|
|
self.complete_slot = None
|
2008-04-19 19:52:38 -04:00
|
|
|
|
2007-10-18 11:28:55 -04:00
|
|
|
# run user custom code here
|
2007-10-18 11:48:39 -04:00
|
|
|
self.rcerror = None
|
2007-10-18 11:28:55 -04:00
|
|
|
self.loadrc()
|
|
|
|
|
2007-03-06 10:05:38 -05:00
|
|
|
# initialize our buffers
|
2008-10-29 13:17:16 -04:00
|
|
|
# note that only the first buffer will be initially visible
|
2009-11-11 00:54:08 -05:00
|
|
|
buffers_ = list(buffers)
|
|
|
|
buffers_.append(buffer.about.AboutBuffer())
|
|
|
|
buffers_.append(self.log)
|
2007-10-19 03:01:34 -04:00
|
|
|
if self.rcerror:
|
2009-11-11 00:54:08 -05:00
|
|
|
buffers_.insert(0, buffer.data.DataBuffer('*RcError*', self.rcerror))
|
2007-10-19 03:01:34 -04:00
|
|
|
|
2007-03-06 10:05:38 -05:00
|
|
|
# build windows for our buffers
|
2009-11-11 00:54:08 -05:00
|
|
|
for b in buffers_:
|
2008-10-29 13:17:16 -04:00
|
|
|
if b.modename:
|
2009-07-23 10:04:12 -04:00
|
|
|
Window(b, self)
|
2007-06-24 09:51:43 -04:00
|
|
|
else:
|
2009-07-23 10:04:12 -04:00
|
|
|
Window(b, self, mode_name=kwargs.get('init_mode'))
|
2007-03-06 10:05:38 -05:00
|
|
|
self.bufferlist.add_buffer(b)
|
2009-11-11 00:54:08 -05:00
|
|
|
self.bufferlist.set_slot(self.active_slot, buffers_[0])
|
2007-03-06 10:05:38 -05:00
|
|
|
|
|
|
|
# see if the user has requested that we go to a particular line
|
2009-02-23 22:35:02 -05:00
|
|
|
jump_to_line = kwargs.get('jump_to_line')
|
2008-11-08 11:18:48 -05:00
|
|
|
if not self.rcerror and jump_to_line:
|
2007-06-04 23:05:33 -04:00
|
|
|
w = self.bufferlist.slots[0].window
|
2008-03-18 11:03:11 -04:00
|
|
|
self.methods['goto-line'].execute(w, lineno=jump_to_line)
|
2007-03-06 10:05:38 -05:00
|
|
|
|
|
|
|
# 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 = {}
|
2009-07-06 22:48:34 -04:00
|
|
|
self.arg_history = {'default': []}
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2010-08-16 23:45:33 -04:00
|
|
|
# this is used to maintain state about various things (ctags, version
|
|
|
|
# control, etc) which is shared across several different buffers.
|
|
|
|
# this is so it will be correctly inherited by new buffers, and also
|
|
|
|
# so that when buffers are closed this state won't be lost.
|
|
|
|
self.state = {
|
|
|
|
'tags': {},
|
|
|
|
'vc': {},
|
|
|
|
}
|
|
|
|
|
2007-03-06 10:05:38 -05:00
|
|
|
# initialize tab handlers
|
2009-02-24 11:02:53 -05:00
|
|
|
completer.set_completer('path', completer.FileCompleter(self))
|
|
|
|
completer.set_completer('buffer', completer.BufferCompleter(self))
|
|
|
|
completer.set_completer('command', completer.CommandCompleter(self))
|
|
|
|
completer.set_completer('shell', completer.ShellCompleter(self))
|
|
|
|
completer.set_completer('config', completer.ConfigCompleter(self))
|
|
|
|
completer.set_completer('method', completer.MethodCompleter(self))
|
|
|
|
completer.set_completer('register', completer.RegisterCompleter(self))
|
|
|
|
completer.set_completer('mode', completer.ModeCompleter(self))
|
|
|
|
completer.set_completer('token', completer.TokenCompleter(self))
|
2007-03-06 10:05:38 -05:00
|
|
|
|
|
|
|
# set up curses
|
|
|
|
self.win = curses.newwin(self.y, self.x, 0, 0)
|
2007-07-02 20:05:58 -04:00
|
|
|
self.win.leaveok(0)
|
2007-03-06 10:05:38 -05:00
|
|
|
curses.meta(1)
|
2007-08-12 18:07:54 -04:00
|
|
|
curses.cbreak()
|
2007-08-12 18:04:00 -04:00
|
|
|
curses.noecho()
|
|
|
|
curses.nonl()
|
2008-10-17 22:03:27 -04:00
|
|
|
curses.halfdelay(1)
|
2008-04-04 16:12:31 -04:00
|
|
|
curses.def_prog_mode()
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2011-11-08 22:32:03 -05:00
|
|
|
def load_pmacs_methods(self, name):
|
|
|
|
exec("import %s" % name)
|
|
|
|
mod = eval(name)
|
|
|
|
for mname in dir(mod):
|
|
|
|
if mname.startswith('_'):
|
|
|
|
continue
|
|
|
|
cls = eval("%s.%s" % (name, mname))
|
|
|
|
if hasattr(cls, '_is_method') and cls._is_method:
|
|
|
|
self.methods[cls._name()] = cls()
|
|
|
|
|
2008-10-15 17:17:16 -04:00
|
|
|
def _load_config_defaults(self):
|
2009-07-26 22:50:12 -04:00
|
|
|
self.config['ignore_suffix'] = ['~', '-', 'CVS', '.svn', '.git', '.hg']
|
2008-12-05 00:41:02 -05:00
|
|
|
self.config['error_timeout'] = -1
|
|
|
|
self.config['max_error_len'] = 192
|
|
|
|
self.config['max_num_kills'] = 64
|
2009-06-13 21:46:38 -04:00
|
|
|
self.config['word_letters'] = string.ascii_letters + string.digits
|
2008-12-05 00:41:02 -05:00
|
|
|
self.config['default_color'] = ('default', 'default',)
|
2009-07-26 22:50:12 -04:00
|
|
|
self.config['margin'] = 79
|
2008-12-05 00:41:02 -05:00
|
|
|
self.config['margin_color'] = 'blue'
|
|
|
|
self.config['use_last_replace'] = False
|
2008-10-15 17:17:16 -04:00
|
|
|
|
2008-05-03 22:41:58 -04:00
|
|
|
def completion_window_is_open(self):
|
|
|
|
n = self.complete_slot
|
|
|
|
if n is None:
|
|
|
|
pass
|
|
|
|
elif n >= len(self.bufferlist.slots):
|
|
|
|
self.complete_slot = None
|
|
|
|
elif self.bufferlist.slots[n].window is None:
|
|
|
|
self.complete_slot = None
|
|
|
|
elif not hasattr(self.bufferlist.slots[n].window.buffer, '_completion'):
|
|
|
|
self.complete_slot = None
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def get_completion_window(self):
|
|
|
|
return self.bufferlist.slots[self.complete_slot].window
|
|
|
|
|
|
|
|
def open_completion_buffer(self, s, candidates):
|
|
|
|
opened = False
|
|
|
|
previous = None
|
|
|
|
if len(self.bufferlist.slots) == 1:
|
|
|
|
self.add_slot()
|
|
|
|
opened = True
|
|
|
|
|
|
|
|
n = len(self.bufferlist.slots) - 1
|
|
|
|
if self.active_slot == n:
|
|
|
|
n -= 1
|
|
|
|
|
|
|
|
if not opened:
|
|
|
|
previous = self.bufferlist.slots[n].window.buffer
|
|
|
|
|
2008-05-05 09:21:00 -04:00
|
|
|
lines = []
|
2008-05-05 13:47:12 -04:00
|
|
|
clen = len(candidates)
|
|
|
|
if clen > self.bufferlist.slots[n].height:
|
|
|
|
maxlen = 0
|
|
|
|
for c in candidates:
|
|
|
|
maxlen = max(maxlen, len(c))
|
|
|
|
|
|
|
|
# NOTE: this is not an optimal packing, but it's fast and easy to
|
|
|
|
# understand. i encourage someone else to write something better.
|
2008-05-30 14:13:26 -04:00
|
|
|
numcols = max(self.bufferlist.slots[n].width // (maxlen + 2), 1)
|
2008-05-05 13:47:12 -04:00
|
|
|
numrows = clen - ((clen // numcols) * (numcols - 1))
|
2009-11-12 00:01:05 -05:00
|
|
|
for i in xrange(0, numrows):
|
2008-05-05 13:47:12 -04:00
|
|
|
names = []
|
|
|
|
index = i * numcols
|
2009-11-12 00:01:05 -05:00
|
|
|
for j in xrange(0, numcols):
|
2008-05-05 13:47:12 -04:00
|
|
|
if index + j < clen:
|
|
|
|
names.append('%-*s' % (maxlen, candidates[index + j]))
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
lines.append(' '.join(names))
|
2008-05-03 22:41:58 -04:00
|
|
|
else:
|
|
|
|
lines = list(candidates)
|
|
|
|
data = '\n'.join(lines)
|
|
|
|
|
2010-04-19 22:58:27 -04:00
|
|
|
b = self.data_buffer("*Completions*", data, switch_to=False)
|
|
|
|
|
2008-05-03 22:41:58 -04:00
|
|
|
b._completion = s
|
|
|
|
b._opened = opened
|
|
|
|
b._previous = previous
|
|
|
|
self.bufferlist.set_slot(n, b)
|
|
|
|
self.complete_slot = n
|
|
|
|
|
|
|
|
def close_completion_buffer(self):
|
|
|
|
w = self.get_completion_window()
|
|
|
|
opened = w.buffer._opened
|
|
|
|
if opened:
|
|
|
|
self.bufferlist.remove_slot(self.complete_slot)
|
|
|
|
else:
|
|
|
|
self.bufferlist.set_slot(self.complete_slot, w.buffer._previous)
|
2008-05-07 14:15:36 -04:00
|
|
|
self.close_buffer(w.buffer)
|
2008-05-03 22:41:58 -04:00
|
|
|
self.complete_slot = None
|
|
|
|
|
2008-11-08 11:18:48 -05:00
|
|
|
def set_completer(self, datatype, comp):
|
2009-02-24 11:02:53 -05:00
|
|
|
completer.set_completer(datatype, comp)
|
2008-04-25 10:20:40 -04:00
|
|
|
|
2007-10-19 03:01:34 -04:00
|
|
|
# this sets up a mode, as well as optionally adding information on when to
|
|
|
|
# auto-load the mode
|
2008-11-08 12:44:59 -05:00
|
|
|
def setmode(self, name, cls, paths=[], bases=[], exts=[], detection=[]):
|
2007-10-18 12:05:21 -04:00
|
|
|
self.modes[name] = cls
|
2009-04-09 01:01:58 -04:00
|
|
|
for p in paths: self.mode_paths[p] = name
|
|
|
|
for b in bases: self.mode_basenames[b] = name
|
|
|
|
for e in exts: self.mode_extensions[e] = name
|
|
|
|
for d in detection: self.mode_detection[d] = name
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2009-04-09 01:01:58 -04:00
|
|
|
# we are evil
|
|
|
|
def eval(self, s): return eval(s)
|
|
|
|
def globals(self): return globals()
|
|
|
|
def locals(self): return locals()
|
|
|
|
|
|
|
|
# slots
|
2007-03-06 10:05:38 -05:00
|
|
|
def add_slot(self):
|
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):
|
2008-12-05 00:41:02 -05:00
|
|
|
slots = self.bufferlist.slots
|
|
|
|
assert len(slots) > 1, "oh no you didn't!"
|
|
|
|
assert n >= 0 and n < len(slots), "invalid slot %r %r" % (n, len(slots))
|
2007-06-04 03:29:37 -04:00
|
|
|
self.bufferlist.remove_slot(n)
|
2007-06-14 08:41:55 -04:00
|
|
|
if self.active_slot > n:
|
2008-11-12 01:40:31 -05:00
|
|
|
self.active_slot = max(0, self.active_slot - 1)
|
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):
|
2008-12-05 00:41:02 -05:00
|
|
|
slots = self.bufferlist.slots
|
2009-11-11 00:54:08 -05:00
|
|
|
assert i >= 0 and i < len(slots), "invalid slot: %d" % i
|
2008-12-05 00:41:02 -05:00
|
|
|
slot = slots[i]
|
2007-03-06 10:05:38 -05:00
|
|
|
return (slot.height, slot.width)
|
|
|
|
|
2007-07-19 12:38:17 -04:00
|
|
|
# files and stuff
|
2008-05-03 22:41:58 -04:00
|
|
|
def close_buffer(self, b):
|
2008-11-08 12:44:59 -05:00
|
|
|
blist = self.bufferlist
|
|
|
|
blist.remove_buffer(b)
|
2008-05-03 22:41:58 -04:00
|
|
|
b.close()
|
2008-11-08 12:44:59 -05:00
|
|
|
active_slot = blist.slots[self.active_slot]
|
2009-11-12 00:01:05 -05:00
|
|
|
for i in xrange(0, len(blist.slots)):
|
2008-11-08 12:44:59 -05:00
|
|
|
if blist.slots[i].is_empty():
|
|
|
|
if blist.hidden_buffers:
|
|
|
|
blist.set_slot(i, blist.hidden_buffers[0])
|
2009-03-25 21:28:38 -04:00
|
|
|
elif active_slot.window:
|
2008-11-08 12:44:59 -05:00
|
|
|
blist.set_slot(i, active_slot.window.buffer)
|
2009-03-25 21:28:38 -04:00
|
|
|
else:
|
|
|
|
blist.set_slot(i, None)
|
2009-01-28 16:28:33 -05:00
|
|
|
assert blist.slots[i].window is not None
|
2009-03-03 21:10:32 -05:00
|
|
|
def close_buffer_by_name(self, name):
|
|
|
|
if self.has_buffer_name(name):
|
|
|
|
self.close_buffer(self.get_buffer_by_name(name))
|
2009-07-09 16:23:13 -04:00
|
|
|
|
|
|
|
def make_name(self, name):
|
2009-07-24 00:07:03 -04:00
|
|
|
return util.make_name(name, self.has_buffer_name)
|
|
|
|
#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
|
|
|
|
#return name
|
2008-05-03 22:41:58 -04:00
|
|
|
|
2007-10-12 16:26:17 -04:00
|
|
|
def open_path(self, path, binary=False, cipher=None, password=None):
|
2009-07-23 10:04:12 -04:00
|
|
|
path = util.literal_path(path)
|
2007-07-19 12:38:17 -04:00
|
|
|
b = self.get_buffer_by_path(path)
|
|
|
|
if b is None:
|
2009-07-09 16:23:13 -04:00
|
|
|
name = self.make_name(os.path.basename(path))
|
2007-07-19 12:38:17 -04:00
|
|
|
|
|
|
|
mode_name = None
|
|
|
|
if cipher is None:
|
|
|
|
if not os.path.exists(path) or os.path.isfile(path):
|
2007-10-12 16:26:17 -04:00
|
|
|
if binary:
|
2007-10-21 20:50:11 -04:00
|
|
|
b = buffer.Binary32Buffer(path, name=name)
|
2007-10-12 16:26:17 -04:00
|
|
|
else:
|
2007-10-21 20:50:11 -04:00
|
|
|
b = buffer.FileBuffer(path, name=name)
|
2007-07-19 12:38:17 -04:00
|
|
|
elif os.path.isdir(path):
|
2008-10-29 10:58:06 -04:00
|
|
|
b = buffer.fs.DirBuffer(path, name=name)
|
2007-07-19 14:28:21 -04:00
|
|
|
mode_name = 'dir'
|
2007-07-19 12:38:17 -04:00
|
|
|
else:
|
2009-11-11 00:54:08 -05:00
|
|
|
raise FileError("not a file or dir: %r" % path)
|
2007-07-19 12:38:17 -04:00
|
|
|
elif cipher == 'aes':
|
|
|
|
if not password:
|
2009-11-11 00:54:08 -05:00
|
|
|
raise RunError("password is required")
|
2007-07-19 12:38:17 -04:00
|
|
|
if not os.path.exists(path) or os.path.isfile(path):
|
2009-05-21 10:24:29 -04:00
|
|
|
b = buffer.aes.AesBuffer(path, password, name=name)
|
2007-07-19 12:38:17 -04:00
|
|
|
else:
|
2009-11-11 00:54:08 -05:00
|
|
|
raise FileError("not a file or dir: %r" % path)
|
2008-04-11 16:20:24 -04:00
|
|
|
try:
|
|
|
|
b.open()
|
2010-09-30 18:18:57 -04:00
|
|
|
#except buffer.BinaryDataException:
|
|
|
|
except:
|
2008-04-11 16:20:24 -04:00
|
|
|
if binary:
|
|
|
|
raise
|
|
|
|
else:
|
|
|
|
binary = True
|
|
|
|
b = buffer.Binary32Buffer(path, name=name)
|
|
|
|
b.open()
|
|
|
|
if mode_name is None:
|
|
|
|
mode_name = 'hex'
|
2009-07-23 10:04:12 -04:00
|
|
|
Window(b, self, height=0, width=0, mode_name=mode_name)
|
2007-07-19 12:38:17 -04:00
|
|
|
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
|
2008-11-08 12:44:59 -05:00
|
|
|
def open_mini_buffer(self, prompt, cb, method=None, tabber=None,
|
2009-07-06 22:48:34 -04:00
|
|
|
modename=None, startvalue=None, queue='default'):
|
2009-07-12 14:55:01 -04:00
|
|
|
parentw = self.bufferlist.slots[self.active_slot].window
|
2007-03-06 10:05:38 -05:00
|
|
|
if self.mini_buffer_is_open():
|
|
|
|
self.close_mini_buffer()
|
|
|
|
self.mini_prompt = prompt
|
2009-07-12 14:55:01 -04:00
|
|
|
self.mini_buffer = MiniBuffer(cb, self, method, tabber, modename, queue,
|
|
|
|
parentw)
|
2007-06-18 14:50:48 -04:00
|
|
|
try:
|
2008-04-19 19:52:38 -04:00
|
|
|
w = self.x - 1 - len(self.mini_prompt) - 1
|
2009-07-23 10:04:12 -04:00
|
|
|
Window(self.mini_buffer, self, height=1, width=w)
|
2008-05-30 14:13:26 -04:00
|
|
|
if startvalue:
|
|
|
|
self.mini_buffer.set_data(startvalue)
|
2009-07-06 22:48:34 -04:00
|
|
|
self.arg_history.setdefault(queue, [])
|
|
|
|
self.arg_history[queue].append(startvalue or '')
|
|
|
|
self.mini_buffer.hindex = len(self.arg_history[queue]) - 1
|
|
|
|
|
2007-06-18 14:50:48 -04:00
|
|
|
self.mini_active = True
|
2009-07-06 22:48:34 -04:00
|
|
|
except MiniBufferError:
|
2007-06-18 14:50:48 -04:00
|
|
|
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):
|
2007-06-18 14:50:48 -04:00
|
|
|
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 = ""
|
2007-06-18 14:50:48 -04:00
|
|
|
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):
|
2008-11-08 12:44:59 -05:00
|
|
|
blist = self.bufferlist
|
|
|
|
assert 0 <= self.active_slot and self.active_slot < len(blist.slots)
|
|
|
|
self.active_slot = (self.active_slot + 1) % len(blist.slots)
|
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:
|
2008-11-08 12:44:59 -05:00
|
|
|
slot = self.active_slot
|
|
|
|
assert 0 <= slot and slot < len(self.bufferlist.slots), \
|
|
|
|
"0 <= %d < %d" % (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
|
2008-11-08 12:44:59 -05:00
|
|
|
def new_file_buffer(self, path, data, switch_to=True):
|
|
|
|
assert not self.has_buffer_name(path), '%r is already open' % path
|
|
|
|
# touch the file
|
2007-03-06 10:05:38 -05:00
|
|
|
f = open(path, 'w')
|
|
|
|
f.write(data)
|
|
|
|
f.close()
|
2008-11-08 12:44:59 -05:00
|
|
|
# create the buffer
|
2007-10-21 20:50:11 -04:00
|
|
|
b = buffer.FileBuffer(path)
|
2008-04-11 16:20:24 -04:00
|
|
|
try:
|
|
|
|
b.open()
|
|
|
|
except buffer.BinaryDataException:
|
|
|
|
b = buffer.Binary32Buffer(path)
|
|
|
|
b.open()
|
|
|
|
b.modename = 'hex'
|
2009-07-23 10:04:12 -04:00
|
|
|
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)
|
2008-12-03 00:15:22 -05:00
|
|
|
return b
|
2007-03-06 10:05:38 -05:00
|
|
|
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)
|
2008-10-29 10:58:06 -04:00
|
|
|
b = buffer.data.DataBuffer(name, data)
|
2007-03-06 10:05:38 -05:00
|
|
|
if modename is not None:
|
|
|
|
b.modename = modename
|
2009-07-23 10:04:12 -04:00
|
|
|
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)
|
2008-05-03 22:41:58 -04:00
|
|
|
return b
|
2009-07-24 00:07:03 -04:00
|
|
|
|
2009-03-16 14:22:23 -04:00
|
|
|
# NOTE: make sure that data has escaped \, [, and ] properly, or else you
|
|
|
|
# will get undesirable results
|
2008-03-29 10:07:22 -04:00
|
|
|
def color_data_buffer(self, name, data, switch_to=True, modename='colortext'):
|
|
|
|
if self.has_buffer_name(name):
|
|
|
|
b = self.bufferlist.buffer_names[name]
|
|
|
|
self.remove_buffer(b)
|
2009-06-10 16:07:03 -04:00
|
|
|
b = buffer.colors.ColorDataBuffer(name, data)
|
2008-03-29 10:07:22 -04:00
|
|
|
if modename is not None:
|
|
|
|
b.modename = modename
|
2009-07-23 10:04:12 -04:00
|
|
|
Window(b, self, height=0, width=0)
|
2008-03-29 10:07:22 -04:00
|
|
|
self.add_buffer(b)
|
|
|
|
if switch_to:
|
|
|
|
self.switch_buffer(b)
|
2007-03-06 10:05:38 -05:00
|
|
|
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 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)
|
|
|
|
self.bufferlist.set_slot(self.active_slot, b)
|
|
|
|
|
|
|
|
def add_window_to_buffer(self, b, slotname):
|
|
|
|
if not b.has_window(slotname):
|
|
|
|
slot = self.bufferlist.slots[slotname]
|
2009-07-23 10:04:12 -04:00
|
|
|
Window(b, self, height=slot.height, width=slot.width)
|
2007-03-06 10:05:38 -05:00
|
|
|
|
|
|
|
# error string handling
|
2009-04-13 00:22:59 -04:00
|
|
|
def set_msg(self, s):
|
2007-03-06 10:05:38 -05:00
|
|
|
self.error_string = s
|
|
|
|
self.error_timestamp = time.time()
|
2009-04-13 00:22:59 -04:00
|
|
|
def set_error(self, s):
|
|
|
|
self.set_msg(s)
|
2009-06-11 23:08:57 -04:00
|
|
|
self.log.append_lines([s, u""], act=buffer.ACT_NONE, force=True)
|
2007-03-06 10:05:38 -05:00
|
|
|
def clear_error(self):
|
2009-06-11 23:08:57 -04:00
|
|
|
self.error_string = u""
|
2007-03-06 10:05:38 -05:00
|
|
|
self.error_timestamp = None
|
2008-12-04 12:16:41 -05:00
|
|
|
def try_manual_resize(self):
|
|
|
|
y, x = self.stdscr.getmaxyx()
|
|
|
|
if y != self.y or x != self.x:
|
|
|
|
self.y, self.x = y, x
|
|
|
|
self.resize_slots()
|
2007-03-06 10:05:38 -05:00
|
|
|
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
|
2008-04-19 19:52:38 -04:00
|
|
|
y_sum = self.y - 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 \
|
2007-06-28 00:33:25 -04:00
|
|
|
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)
|
2008-12-04 12:16:41 -05:00
|
|
|
maxnum = self.config.get('max_num_kills')
|
2008-11-08 12:44:59 -05:00
|
|
|
if maxnum and len(self.kill_ring) > maxnum:
|
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()
|
|
|
|
|
2010-09-11 10:29:41 -04:00
|
|
|
# build application-centric paths
|
|
|
|
def getpath(self, *parts):
|
|
|
|
return os.path.join(os.getenv('HOME'), '.pmc', *parts)
|
|
|
|
|
|
|
|
# create directories under the application path
|
|
|
|
def mkdirs(self, *parts):
|
2010-09-23 23:35:19 -04:00
|
|
|
path = os.path.join(self.getpath(), *parts)
|
|
|
|
if not os.path.exists(path):
|
|
|
|
os.makedirs(path)
|
|
|
|
#for part in parts:
|
|
|
|
# path = os.path.join(path, part)
|
|
|
|
# if not os.path.exists(path):
|
|
|
|
# os.mkdir(path)
|
2010-09-11 10:29:41 -04:00
|
|
|
|
2007-10-18 11:28:55 -04:00
|
|
|
# load user configuration NOW
|
|
|
|
def loadrc(self):
|
|
|
|
path = os.path.join(os.getenv('HOME'), '.pmc', 'conf')
|
2009-07-24 00:07:03 -04:00
|
|
|
if not os.path.exists(path):
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
f = open(path, 'r')
|
|
|
|
exec(f)
|
|
|
|
f.close()
|
2009-11-11 00:54:08 -05:00
|
|
|
except:
|
2009-07-24 00:07:03 -04:00
|
|
|
s = traceback.format_exc()
|
|
|
|
self.rcerror = 'There was an error during startup:\n\n' + s
|
2009-03-19 00:23:09 -04:00
|
|
|
|
|
|
|
# after actions get handled, do some stuff
|
|
|
|
def post_action_hook(self, act):
|
|
|
|
self.last_action = act.name
|
|
|
|
if self.highlight_mark:
|
|
|
|
if act.name == 'set-mark' or act.metadata.get('is_move'):
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
self.highlight_mark = False
|
2009-06-11 23:08:57 -04:00
|
|
|
|
2009-07-24 00:07:03 -04:00
|
|
|
# UTF-8 aware way to write to the screen
|
2009-06-11 23:08:57 -04:00
|
|
|
def addstr(self, y, x, s, attr=curses.A_NORMAL):
|
|
|
|
self.win.addstr(y, x, s.encode('utf-8'), attr)
|
|
|
|
|
2007-07-18 23:06:21 -04:00
|
|
|
# the mighty run-loop!
|
2007-03-06 10:05:38 -05:00
|
|
|
def run(self):
|
|
|
|
self.done = False
|
2007-08-12 18:18:23 -04:00
|
|
|
self.draw()
|
2008-09-23 09:58:23 -04:00
|
|
|
if os.getenv('PMC_EARLY_OUT'):
|
|
|
|
return
|
2007-03-06 10:05:38 -05:00
|
|
|
while not self.done:
|
|
|
|
i = self.win.getch()
|
2008-05-29 16:35:21 -04:00
|
|
|
|
2009-04-09 01:01:58 -04:00
|
|
|
# if we get a resize event, wait for things to stabilize
|
2007-03-06 10:05:38 -05:00
|
|
|
if i == curses.KEY_RESIZE:
|
2009-04-09 01:01:58 -04:00
|
|
|
while i == curses.KEY_RESIZE: i = self.win.getch()
|
2007-03-06 10:05:38 -05:00
|
|
|
self.resize_event()
|
2008-11-08 10:30:04 -05:00
|
|
|
self.need_draw = True
|
2009-04-09 01:01:58 -04:00
|
|
|
|
|
|
|
# add the keycodes to our input handler
|
2007-03-06 10:05:38 -05:00
|
|
|
try:
|
|
|
|
self.input.parse(i)
|
|
|
|
except Exception, e:
|
2008-12-04 12:16:41 -05:00
|
|
|
self.set_error(str(e))
|
2008-11-08 10:30:04 -05:00
|
|
|
|
2009-04-09 01:01:58 -04:00
|
|
|
# if the mode has parsed keycodes into a key, we (possibly) handle
|
|
|
|
# some actions, and refresh the screen
|
2008-11-08 10:30:04 -05:00
|
|
|
while self.input.tokens:
|
2009-04-09 01:01:58 -04:00
|
|
|
self.need_draw = True
|
2007-03-06 10:05:38 -05:00
|
|
|
t = self.input.tokens.pop(0)
|
|
|
|
self.active_window().mode.handle_token(t)
|
2008-05-29 16:35:21 -04:00
|
|
|
|
2008-11-08 10:30:04 -05:00
|
|
|
if self.need_draw:
|
2008-12-04 12:16:41 -05:00
|
|
|
self.draw()
|
2008-11-08 10:30:04 -05:00
|
|
|
self.need_draw = False
|
2007-07-18 23:06:21 -04:00
|
|
|
|
2009-03-19 00:23:09 -04:00
|
|
|
# clean up, clean up
|
2009-03-05 01:44:30 -05:00
|
|
|
for b in self.bufferlist.buffers:
|
|
|
|
b.close()
|
|
|
|
|
2007-07-18 23:06:21 -04:00
|
|
|
# clear the error line; it might look confusing to the user
|
|
|
|
try:
|
2009-06-11 23:08:57 -04:00
|
|
|
self.addstr(self.y-1, 0, ' ' * self.x)
|
2007-07-18 23:06:21 -04:00
|
|
|
except:
|
|
|
|
pass
|
|
|
|
self.win.refresh()
|
2007-03-06 10:05:38 -05:00
|
|
|
return
|
|
|
|
|
|
|
|
# highlighting
|
2008-05-03 16:22:12 -04:00
|
|
|
def add_highlighted_range(self, hr):
|
|
|
|
self.highlighted_ranges.append(hr)
|
2008-05-03 17:05:24 -04:00
|
|
|
def clear_highlighted_ranges(self, name=None):
|
|
|
|
if name is None:
|
|
|
|
self.highlighted_ranges = []
|
|
|
|
else:
|
|
|
|
i = 0
|
|
|
|
while i < len(self.highlighted_ranges):
|
|
|
|
if self.highlighted_ranges[i].name == name:
|
|
|
|
del self.highlighted_ranges[i]
|
|
|
|
else:
|
|
|
|
i += 1
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2008-11-13 23:44:56 -05:00
|
|
|
# running external programs
|
2009-03-16 10:42:17 -04:00
|
|
|
def run_pipe(self, args, b, name='*Output*', switch=True, modename=None):
|
2010-09-27 01:05:13 -04:00
|
|
|
#pipe = Popen(args=args, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
|
2009-10-16 17:20:17 -04:00
|
|
|
data = b.make_string().encode('utf-8')
|
2010-09-27 01:05:13 -04:00
|
|
|
return self.run_pipe2(args, data, name, switch, modename)
|
|
|
|
def run_pipe2(self, args, data, name='*Output*', switch=True, modename=None):
|
|
|
|
pipe = Popen(args=args, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
|
2010-06-01 14:36:07 -04:00
|
|
|
try:
|
|
|
|
pipe.stdin.write(data)
|
|
|
|
pipe.stdin.close()
|
|
|
|
except IOError:
|
|
|
|
# sometimes the program will stop reading from stdin before we're
|
|
|
|
# done. this is fine and we should ignore the IOError.
|
|
|
|
pass
|
2008-11-13 23:44:56 -05:00
|
|
|
output = pipe.stdout.read()
|
|
|
|
status = pipe.wait()
|
|
|
|
if callable(switch):
|
|
|
|
switch_to = switch(status)
|
|
|
|
else:
|
|
|
|
switch_to = bool(switch)
|
2010-09-27 01:05:13 -04:00
|
|
|
self.data_buffer(name, output.decode('utf-8'), switch_to=switch_to,
|
|
|
|
modename=modename)
|
2008-11-13 23:44:56 -05:00
|
|
|
return status
|
2008-04-04 16:12:31 -04:00
|
|
|
def run_external(self, *args):
|
|
|
|
curses.reset_shell_mode()
|
2008-04-16 10:44:24 -04:00
|
|
|
try:
|
|
|
|
pipe = Popen(args)
|
|
|
|
pipe.wait()
|
2008-05-20 17:40:50 -04:00
|
|
|
except OSError, e:
|
2008-04-16 10:44:24 -04:00
|
|
|
self.set_error("%s: %s" % (args[0], e))
|
2008-04-04 16:12:31 -04:00
|
|
|
curses.reset_prog_mode()
|
|
|
|
self.win.redrawwin()
|
|
|
|
self.draw()
|
|
|
|
|
2007-03-06 10:05:38 -05:00
|
|
|
# full screen drawer
|
2008-12-04 12:16:41 -05:00
|
|
|
def draw(self):
|
2007-07-28 23:08:42 -04:00
|
|
|
try:
|
2008-04-28 02:37:49 -04:00
|
|
|
n = len(self.get_minibuffer_lines())
|
|
|
|
assert n > 0
|
|
|
|
if n != self.bufferlist.mini_height:
|
|
|
|
self.bufferlist.resize_mini(n)
|
2007-09-26 11:56:07 -04:00
|
|
|
self.draw_slots()
|
2008-04-28 02:37:49 -04:00
|
|
|
self.draw_minibuffer()
|
2007-07-28 23:08:42 -04:00
|
|
|
self.draw_cursor()
|
2008-04-06 22:31:13 -04:00
|
|
|
self.win.refresh()
|
2009-11-11 00:54:08 -05:00
|
|
|
except:
|
|
|
|
raise #XYZ
|
2007-07-28 23:08:42 -04:00
|
|
|
# ok, so there was a problem...
|
|
|
|
# let's see if the screen changed sizes and if so, resize our slots
|
|
|
|
self.resize_event()
|
2008-12-04 12:16:41 -05:00
|
|
|
|
|
|
|
# clear the error message if appropriate
|
|
|
|
tout = self.config.get('error_timeout', 0)
|
|
|
|
tstamp = self.error_timestamp
|
|
|
|
if tstamp and tout and time.time() - tstamp > tout:
|
2007-08-12 18:18:23 -04:00
|
|
|
self.clear_error()
|
2008-12-04 12:16:41 -05:00
|
|
|
|
|
|
|
self.try_manual_resize()
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2009-04-11 00:39:34 -04:00
|
|
|
# NOTE: this is taken from the cursor code
|
|
|
|
def map_point(self, slot, p):
|
2010-04-27 01:14:41 -04:00
|
|
|
w = slot.window
|
2009-04-11 00:39:34 -04:00
|
|
|
swidth = slot.width - w.mode.lmargin - w.mode.rmargin
|
2010-04-27 01:14:41 -04:00
|
|
|
blen = len(w.buffer.lines)
|
|
|
|
count = w.mode.header
|
|
|
|
x, y = w.first.xy()
|
|
|
|
vy, vx = None, None
|
|
|
|
|
2009-04-09 01:01:58 -04:00
|
|
|
while count < slot.height:
|
2010-04-27 01:14:41 -04:00
|
|
|
line = w.buffer.lines[y]
|
2010-07-03 23:08:15 -04:00
|
|
|
l = len(line)
|
2010-04-27 01:14:41 -04:00
|
|
|
|
2009-04-11 00:39:34 -04:00
|
|
|
if p.y == y and p.x >= x and p.x <= x + swidth:
|
|
|
|
vy, vx = slot.y_offset + count, p.x - x + w.mode.lmargin
|
2010-04-27 01:14:41 -04:00
|
|
|
if vx == swidth and p.x < l:
|
2009-04-11 00:39:34 -04:00
|
|
|
vx = 0
|
|
|
|
vy += 1
|
|
|
|
break
|
|
|
|
|
2010-04-27 01:14:41 -04:00
|
|
|
if y >= blen or x + swidth >= l:
|
2009-04-09 01:01:58 -04:00
|
|
|
x = 0
|
|
|
|
y += 1
|
|
|
|
else:
|
2009-04-11 00:39:34 -04:00
|
|
|
x += swidth
|
2009-04-09 01:01:58 -04:00
|
|
|
count += 1
|
2009-04-11 00:39:34 -04:00
|
|
|
return vx, vy
|
2009-04-09 01:01:58 -04:00
|
|
|
|
2007-06-28 00:33:25 -04:00
|
|
|
def draw_cursor(self):
|
|
|
|
if self.mini_active:
|
|
|
|
b = self.mini_buffer
|
|
|
|
w = b.windows[0]
|
2007-07-08 19:16:53 -04:00
|
|
|
p = w.logical_cursor()
|
2008-04-28 02:37:49 -04:00
|
|
|
x = p.x + len(self.mini_prompt)
|
|
|
|
y = p.y
|
|
|
|
if y >= len(b.lines):
|
2007-06-28 00:33:25 -04:00
|
|
|
return
|
2008-04-28 02:37:49 -04:00
|
|
|
|
|
|
|
lines = self.get_minibuffer_lines()
|
|
|
|
while x > self.x - 1:
|
|
|
|
y += 1
|
|
|
|
x -= self.x - 1
|
|
|
|
vy, vx = self.y - len(lines) + y, x
|
2007-06-28 00:33:25 -04:00
|
|
|
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()
|
2008-04-07 03:16:34 -04:00
|
|
|
|
2009-04-11 00:39:34 -04:00
|
|
|
vx, vy = self.map_point(slot, p)
|
2007-07-02 20:05:58 -04:00
|
|
|
if vy is None or vx is None:
|
|
|
|
return
|
2007-07-08 19:16:53 -04:00
|
|
|
try:
|
|
|
|
self.win.move(vy, vx)
|
|
|
|
except:
|
2009-11-11 00:54:08 -05:00
|
|
|
raise DrawError("(%r=%r) no (%r)" % ((vx, vy), p, (self.x, self.y)))
|
2007-06-28 00:33:25 -04:00
|
|
|
|
2007-03-06 10:05:38 -05:00
|
|
|
# sub-drawing methods
|
|
|
|
def draw_slots(self):
|
|
|
|
self.win.erase()
|
2009-11-12 00:01:05 -05:00
|
|
|
for i in xrange(0, len(self.bufferlist.slots)):
|
2009-11-11 00:54:08 -05:00
|
|
|
#slot = self.bufferlist.slots[i]
|
2007-03-06 10:05:38 -05:00
|
|
|
self.draw_slot(i)
|
|
|
|
self.draw_status_bar(i)
|
2007-07-28 23:08:42 -04:00
|
|
|
|
2007-07-05 16:18:09 -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)
|
2007-11-04 18:23:06 -05:00
|
|
|
char = chr(junk & 255)
|
2007-07-05 16:18:09 -04:00
|
|
|
attr = color.build(fg, bg)
|
2007-07-08 19:16:53 -04:00
|
|
|
try:
|
2010-06-04 00:10:27 -04:00
|
|
|
#self.addstr(sy, sx, char, attr)
|
|
|
|
self.win.addstr(sy, sx, char, attr)
|
2009-11-11 00:54:08 -05:00
|
|
|
except curses.error:
|
|
|
|
raise DrawError("(%d, %d, %r, %r) v. (%d, %d)" %
|
|
|
|
(sy, sx, fg, bg, self.y, self.x))
|
2007-07-08 19:16:53 -04:00
|
|
|
|
2007-07-05 16:18:09 -04:00
|
|
|
def highlight_chars(self, sy, sx1, sx2, fg='default', bg='default'):
|
2007-07-08 19:16:53 -04:00
|
|
|
assert sx2 < self.x, "%d < %d" % (sx2, self.x)
|
2009-11-12 00:01:05 -05:00
|
|
|
for x in xrange(sx1, sx2):
|
2007-07-05 16:18:09 -04:00
|
|
|
self.highlight_char(sy, x, fg, bg)
|
2007-06-14 06:10:20 -04:00
|
|
|
|
2009-03-19 00:23:09 -04:00
|
|
|
def highlight_simple_range(self, slot, y1, x1, x2, fg, bg):
|
|
|
|
count = slot.window.mode.header
|
|
|
|
tx1, tx2 = slot.window.mode.lmargin, slot.width - 1
|
|
|
|
x, y = slot.window.first.xy()
|
|
|
|
while count < slot.height:
|
|
|
|
if y1 == y and x1 < slot.width + x:
|
|
|
|
sy = slot.y_offset + count
|
|
|
|
sx1 = x1 - x + slot.window.mode.lmargin
|
|
|
|
sx2 = x2 - x + slot.window.mode.lmargin
|
|
|
|
if x1 <= x:
|
|
|
|
if x2 < slot.width + x:
|
|
|
|
self.highlight_chars(sy, tx1, sx2, fg, bg)
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
self.highlight_chars(sy, tx1, tx2, fg, bg)
|
|
|
|
else:
|
|
|
|
if x2 < slot.width + x:
|
|
|
|
self.highlight_chars(sy, sx1, sx2, fg, bg)
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
self.highlight_chars(sy, sx1, tx2, fg, bg)
|
|
|
|
|
|
|
|
if x + slot.width > len(slot.window.buffer.lines[y]):
|
|
|
|
x = 0
|
|
|
|
y += 1
|
|
|
|
else:
|
|
|
|
x += slot.width - 1
|
|
|
|
count += 1
|
|
|
|
|
|
|
|
def highlight_complex_range(self, slot, p1, p2, fg, bg):
|
|
|
|
count = slot.window.mode.header
|
|
|
|
tx1, tx2 = slot.window.mode.lmargin, slot.width - 1
|
|
|
|
x, y = slot.window.first.xy()
|
|
|
|
while count < slot.height:
|
|
|
|
if p1.y <= y:
|
|
|
|
sy = slot.y_offset + count
|
|
|
|
if p1.y == y:
|
|
|
|
if p1.x > slot.width + x:
|
|
|
|
pass
|
|
|
|
elif p1.x > x:
|
|
|
|
self.highlight_chars(sy, p1.x - x + tx1, tx2, fg, bg)
|
|
|
|
else:
|
|
|
|
self.highlight_chars(sy, tx1, tx2, fg, bg)
|
|
|
|
elif p2.y > y:
|
|
|
|
self.highlight_chars(sy, tx1, tx2, fg, bg)
|
|
|
|
elif p2.y == y and p2.x >= x and p2.x < slot.width + x:
|
|
|
|
if slot.width > p2.x - x:
|
|
|
|
self.highlight_chars(sy, tx1, p2.x - x + tx1, fg, bg)
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
self.highlight_chars(sy, tx1, tx2, fg, bg)
|
|
|
|
|
|
|
|
if x + slot.width > len(slot.window.buffer.lines[y]):
|
|
|
|
x = 0
|
|
|
|
y += 1
|
|
|
|
else:
|
|
|
|
x += slot.width - 1
|
|
|
|
count += 1
|
|
|
|
|
|
|
|
def highlight_range(self, slot, p1, p2, fg, bg):
|
|
|
|
if p1.y == p2.y:
|
|
|
|
return self.highlight_simple_range(slot, p1.y, p1.x, p2.x, fg, bg)
|
|
|
|
else:
|
|
|
|
return self.highlight_complex_range(slot, p1, p2, fg, bg)
|
|
|
|
|
2007-06-04 03:29:37 -04:00
|
|
|
def draw_slot(self, i):
|
2008-04-20 23:10:25 -04:00
|
|
|
assert self.active_slot < len(self.bufferlist.slots), \
|
|
|
|
"strange: %d < %d" % (self.active_slot, len(self.bufferlist.slots))
|
|
|
|
assert i < len(self.bufferlist.slots), \
|
|
|
|
"puzzling: %d < %d" % (i, len(self.bufferlist.slots))
|
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
|
|
|
|
|
2009-02-02 09:44:32 -05:00
|
|
|
# draw the header
|
2009-02-05 10:47:20 -05:00
|
|
|
if w.mode.header:
|
|
|
|
rstrs = w.mode.get_header()
|
|
|
|
assert len(rstrs) >= w.mode.header
|
2009-11-12 00:01:05 -05:00
|
|
|
for j in xrange(0, w.mode.header):
|
2009-03-06 11:39:47 -05:00
|
|
|
k = 0
|
|
|
|
for rstr in rstrs[j]:
|
2010-07-27 15:26:42 -04:00
|
|
|
#rstr.draw(self.win, slot.y_offset + j, slot.x_offset + k, slot.width)
|
|
|
|
rstr.draw(self.win, slot.y_offset + j, slot.x_offset + k)
|
2010-04-27 01:14:41 -04:00
|
|
|
#k += len(rstr.string)
|
|
|
|
k += rstr.width(w)
|
2009-01-28 16:28:33 -05:00
|
|
|
|
2009-02-02 09:44:32 -05:00
|
|
|
# draw the actual slot
|
2008-04-20 23:10:25 -04:00
|
|
|
self._draw_slot(i)
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2007-06-14 06:10:20 -04:00
|
|
|
# highlighted regions
|
2008-05-03 16:22:12 -04:00
|
|
|
for hr in self.highlighted_ranges:
|
|
|
|
(high_w, p1, p2, fg, bg) = hr
|
2007-07-03 12:53:14 -04:00
|
|
|
if w is high_w and p2 >= w.first and p1 <= w.last:
|
2009-03-19 00:23:09 -04:00
|
|
|
self.highlight_range(slot, p1, p2, fg, bg)
|
|
|
|
|
2009-05-03 23:12:47 -04:00
|
|
|
if (self.active_slot == i and not self.highlighted_ranges and self.highlight_mark):
|
2009-03-19 00:23:09 -04:00
|
|
|
fg, bg = 'black', 'cyan'
|
|
|
|
cursor = w.logical_cursor()
|
|
|
|
mark = w.mark
|
|
|
|
if mark is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
if mark <= cursor:
|
|
|
|
p1, p2 = mark, cursor
|
|
|
|
else:
|
|
|
|
p1, p2 = cursor, mark
|
|
|
|
|
|
|
|
if p1 < w.first: p1 = w.first
|
|
|
|
if p2 > w.last: p2 = w.last
|
|
|
|
self.highlight_range(slot, p1, p2, fg, bg)
|
2007-06-14 06:10:20 -04:00
|
|
|
|
2007-07-03 12:53:14 -04:00
|
|
|
if w.margins_visible:
|
2008-11-12 00:00:40 -05:00
|
|
|
shade = util.get_margin_color(w, 'blue')
|
|
|
|
limit = util.get_margin_limit(w, 80)
|
|
|
|
if limit < self.x:
|
2009-11-12 00:01:05 -05:00
|
|
|
for j in xrange(0, slot.height):
|
2010-06-04 00:10:27 -04:00
|
|
|
#char = chr(self.win.inch(j + slot.y_offset, limit) & 255)
|
|
|
|
#attr = color.build('default', shade, 'bold')
|
|
|
|
#self.addstr(j + slot.y_offset, limit + w.mode.lmargin, char, attr)
|
|
|
|
self.highlight_char(j + slot.y_offset,
|
|
|
|
limit + w.mode.lmargin,
|
|
|
|
fg='default', bg=shade)
|
2008-04-07 03:16:34 -04:00
|
|
|
|
2008-04-20 23:10:25 -04:00
|
|
|
def _draw_slot(self, i):
|
2007-06-16 10:41:27 -04:00
|
|
|
slot = self.bufferlist.slots[i]
|
|
|
|
w = slot.window
|
2008-04-20 23:10:25 -04:00
|
|
|
x, y = w.first.xy()
|
2008-04-25 10:20:40 -04:00
|
|
|
lm, rm = w.mode.lmargin, w.mode.rmargin
|
2009-02-02 09:44:32 -05:00
|
|
|
count = w.mode.header
|
2009-02-03 14:16:38 -05:00
|
|
|
swidth = slot.width - lm - rm
|
2009-03-17 15:24:10 -04:00
|
|
|
lit = w.mode.name in w.buffer.highlights
|
2008-04-20 23:10:25 -04:00
|
|
|
ended = False
|
2009-02-13 22:14:38 -05:00
|
|
|
|
|
|
|
# figure out which "physical line" is the first to be shown. note that
|
|
|
|
# the cursor shouldn't be in the last column unless it's the end of a
|
|
|
|
# line.
|
|
|
|
k = x // swidth
|
2009-04-11 14:43:46 -04:00
|
|
|
|
2007-06-16 10:41:27 -04:00
|
|
|
while count < slot.height:
|
2008-04-20 23:10:25 -04:00
|
|
|
if lit:
|
2009-02-03 14:16:38 -05:00
|
|
|
rlines = w.render_line_lit(y, swidth)
|
2007-06-16 10:41:27 -04:00
|
|
|
else:
|
2009-02-03 14:16:38 -05:00
|
|
|
rlines = w.render_line_raw(y, swidth)
|
2009-11-12 00:01:05 -05:00
|
|
|
for j in xrange(k, len(rlines)):
|
2009-07-24 00:07:03 -04:00
|
|
|
y2 = slot.y_offset + count
|
2008-04-20 23:10:25 -04:00
|
|
|
if lm:
|
2010-04-27 01:14:41 -04:00
|
|
|
i = 0
|
2008-04-20 23:10:25 -04:00
|
|
|
lcont = j > 0
|
2010-04-27 01:14:41 -04:00
|
|
|
rstrs = w.mode.get_lmargin(w, y, x, ended, lcont)
|
2009-07-24 00:07:03 -04:00
|
|
|
for rstr in rstrs:
|
2010-04-27 01:14:41 -04:00
|
|
|
rstr.draw(self.win, y2, i)
|
|
|
|
i += rstr.width(w)
|
|
|
|
i = lm
|
2008-04-20 23:10:25 -04:00
|
|
|
for rstr in rlines[j]:
|
2010-04-27 01:14:41 -04:00
|
|
|
rstr.draw(self.win, y2, i)
|
|
|
|
i += rstr.width(w)
|
2008-04-20 23:10:25 -04:00
|
|
|
if rm:
|
2010-04-27 01:14:41 -04:00
|
|
|
i = slot.width - rm
|
2008-04-20 23:10:25 -04:00
|
|
|
rcont = j < len(rlines) - 1
|
2010-04-27 01:14:41 -04:00
|
|
|
rstrs = w.mode.get_rmargin(w, y, x, ended, rcont)
|
2009-07-24 00:07:03 -04:00
|
|
|
for rstr in rstrs:
|
2010-04-27 01:14:41 -04:00
|
|
|
rstr.draw(self.win, y2, i)
|
|
|
|
i += rstr.width(w)
|
2007-06-16 10:41:27 -04:00
|
|
|
count += 1
|
2008-04-25 10:20:40 -04:00
|
|
|
if count >= slot.height:
|
|
|
|
break
|
|
|
|
k = 0
|
2008-04-20 23:10:25 -04:00
|
|
|
y += 1
|
|
|
|
ended = ended or y >= len(w.buffer.lines)
|
2007-06-16 10:41:27 -04:00
|
|
|
|
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
|
2008-05-09 15:01:59 -04:00
|
|
|
status = slot.window.mode.get_status_bar()
|
2008-04-19 19:52:38 -04:00
|
|
|
status = status.ljust(slot.width)[:slot.width]
|
2009-06-11 23:08:57 -04:00
|
|
|
self.addstr(slot.height + slot.y_offset, 0, status, curses.A_REVERSE)
|
2007-03-06 10:05:38 -05:00
|
|
|
|
|
|
|
# input bar drawing
|
2008-04-28 02:37:49 -04:00
|
|
|
def draw_minibuffer(self):
|
|
|
|
lines = self.get_minibuffer_lines()
|
2008-12-04 12:16:41 -05:00
|
|
|
attr = color.build('default', 'default')
|
|
|
|
if self.error_string:
|
|
|
|
attr = color.build('default', 'default')
|
2009-11-12 00:01:05 -05:00
|
|
|
for i in xrange(0, len(lines)):
|
2008-12-04 12:16:41 -05:00
|
|
|
line = lines[i]
|
2008-04-23 17:21:50 -04:00
|
|
|
try:
|
2009-06-11 23:08:57 -04:00
|
|
|
self.addstr(self.y - len(lines) + i, 0, line, attr)
|
2008-04-23 17:21:50 -04:00
|
|
|
except:
|
2009-06-11 23:08:57 -04:00
|
|
|
raise
|
2008-12-04 12:16:41 -05:00
|
|
|
if self.error_string or not self.mini_buffer_is_open():
|
|
|
|
return
|
|
|
|
pattr = color.build('cyan', 'default', 'bold')
|
|
|
|
plines = self.get_minibuffer_x_lines(self.mini_prompt)
|
2009-11-12 00:01:05 -05:00
|
|
|
for i in xrange(0, len(plines)):
|
2008-12-04 12:16:41 -05:00
|
|
|
pline = plines[i]
|
|
|
|
try:
|
2009-06-11 23:08:57 -04:00
|
|
|
self.addstr(self.y - len(lines) + i, 0, pline, pattr)
|
2008-12-04 12:16:41 -05:00
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def get_minibuffer_x_lines(self, s):
|
|
|
|
i = 0
|
|
|
|
lines = []
|
|
|
|
while i < len(s):
|
|
|
|
lines.append(s[i:i + self.x - 1])
|
|
|
|
i += self.x - 1
|
|
|
|
return lines
|
2007-09-26 11:56:07 -04:00
|
|
|
|
2008-04-28 02:37:49 -04:00
|
|
|
def get_minibuffer_lines(self):
|
2008-12-04 12:16:41 -05:00
|
|
|
lines2 = []
|
2010-08-17 23:31:30 -04:00
|
|
|
if self.error_string:
|
2008-11-08 12:44:59 -05:00
|
|
|
maxlen = self.config['max_error_len']
|
|
|
|
if len(self.error_string) < maxlen:
|
2008-04-28 02:46:14 -04:00
|
|
|
s = self.error_string
|
|
|
|
else:
|
2008-11-08 12:44:59 -05:00
|
|
|
s = self.error_string[:maxlen] + '...'
|
2008-04-28 02:37:49 -04:00
|
|
|
elif self.mini_buffer_is_open():
|
|
|
|
s = self.mini_prompt + self.mini_buffer.lines[0]
|
|
|
|
lines2 = self.mini_buffer.lines[1:]
|
|
|
|
else:
|
|
|
|
return [' ' * (self.x - 1)]
|
2008-12-04 12:16:41 -05:00
|
|
|
lines = self.get_minibuffer_x_lines(s)
|
2008-04-28 02:37:49 -04:00
|
|
|
lines.extend(lines2)
|
|
|
|
return lines
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2008-04-16 00:44:32 -04:00
|
|
|
def open_aes_file(path, name=None, binary=False):
|
2007-07-20 10:51:36 -04:00
|
|
|
if os.path.isfile(path) or not os.path.exists(path):
|
2009-07-23 10:04:12 -04:00
|
|
|
p = getpass("Please enter the AES password: ")
|
2009-05-21 10:24:29 -04:00
|
|
|
return buffer.aes.AesBuffer(path, p, name)
|
2007-07-19 14:37:39 -04:00
|
|
|
else:
|
|
|
|
raise Exception, "can't open %r; unsupported file type" % path
|
2009-07-24 00:07:03 -04:00
|
|
|
|
2008-04-16 00:44:32 -04:00
|
|
|
def open_plain_file(path, name=None, binary=False):
|
2007-07-20 10:51:36 -04:00
|
|
|
if os.path.isfile(path) or not os.path.exists(path):
|
2007-10-12 16:26:17 -04:00
|
|
|
if binary:
|
2008-04-16 00:44:32 -04:00
|
|
|
return buffer.Binary32Buffer(path, name)
|
2007-10-12 16:26:17 -04:00
|
|
|
else:
|
2008-04-16 00:44:32 -04:00
|
|
|
return buffer.FileBuffer(path, name)
|
2007-07-19 14:37:39 -04:00
|
|
|
elif os.path.isdir(path):
|
2008-10-29 10:58:06 -04:00
|
|
|
return buffer.fs.DirBuffer(path, name)
|
2007-07-19 14:37:39 -04:00
|
|
|
else:
|
|
|
|
raise Exception, "can't open %r; unsupported file type" % path
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2009-02-23 22:35:02 -05:00
|
|
|
def run_app(stdscr, buffers, **kwargs):
|
2008-11-08 10:30:04 -05:00
|
|
|
curses.def_shell_mode()
|
2009-02-23 22:35:02 -05:00
|
|
|
a = Application(stdscr, buffers, **kwargs)
|
2009-03-05 03:08:33 -05:00
|
|
|
metax = a.methods['meta-x']
|
|
|
|
for cmd in kwargs.get('init_cmds', []):
|
|
|
|
metax.execute(a.active_window(), method=cmd)
|
2008-11-08 10:30:04 -05:00
|
|
|
a.run()
|
2008-11-08 23:13:12 -05:00
|
|
|
return 0
|
2008-11-08 10:30:04 -05:00
|
|
|
|
2007-03-06 10:05:38 -05:00
|
|
|
if __name__ == "__main__":
|
2009-07-24 00:07:03 -04:00
|
|
|
ciphers = {'none': open_plain_file,
|
|
|
|
'aes': open_aes_file}
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2009-06-09 23:37:43 -04:00
|
|
|
locale.setlocale(locale.LC_ALL, '')
|
|
|
|
|
2008-03-30 04:20:37 -04:00
|
|
|
# preprocess args
|
|
|
|
argv = list(sys.argv[1:])
|
|
|
|
goto_line = None
|
|
|
|
i = 0
|
|
|
|
while i < len(argv):
|
|
|
|
if argv[i] == '-nw':
|
|
|
|
del argv[i]
|
|
|
|
elif argv[i].startswith('+'):
|
|
|
|
goto_line = int(argv.pop(i))
|
|
|
|
else:
|
|
|
|
i += 1
|
|
|
|
|
2009-07-24 00:07:03 -04:00
|
|
|
# set up the option parser, and set some defaults
|
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')
|
2009-06-08 23:13:04 -04:00
|
|
|
parser.set_defaults(pipe=False)
|
2007-10-12 16:26:17 -04:00
|
|
|
parser.set_defaults(binary=False)
|
2009-02-23 22:49:50 -05:00
|
|
|
parser.set_defaults(cmds=[])
|
|
|
|
|
|
|
|
def exec_cb(option, opt, value, parser):
|
|
|
|
parser.values.cmds.append(value)
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2007-10-12 16:26:17 -04:00
|
|
|
parser.add_option('-b', '--binary', dest='binary', action='store_true',
|
|
|
|
help='open file(s) in hex binary mode')
|
2009-06-08 23:13:04 -04:00
|
|
|
parser.add_option('-p', '--pipe', dest='pipe', action='store_true',
|
|
|
|
help='read data from STDIN into buffer')
|
2007-03-06 10:05:38 -05:00
|
|
|
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('-m', '--mode', dest='mode', metavar='MODE',
|
|
|
|
help='open arguments in MODE')
|
2009-02-23 22:49:50 -05:00
|
|
|
parser.add_option('-x', '--exec', action='callback', callback=exec_cb,
|
|
|
|
type='string', metavar='CMD', help='run CMD after launching')
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2008-03-30 04:20:37 -04:00
|
|
|
(opts, args) = parser.parse_args(argv)
|
2007-03-06 10:05:38 -05:00
|
|
|
|
|
|
|
# if debugging, disable error handling to produce backtraces
|
|
|
|
if opts.debug:
|
2007-10-21 20:55:29 -04:00
|
|
|
mode.DEBUG = True
|
2007-03-06 10:05:38 -05:00
|
|
|
|
2009-07-23 16:26:59 -04:00
|
|
|
# override $TERM if we need to
|
2009-07-24 00:41:02 -04:00
|
|
|
if os.getenv('PMC_TERM'):
|
|
|
|
os.putenv('TERM', os.getenv('PMC_TERM'))
|
2009-07-23 16:26:59 -04:00
|
|
|
|
2009-08-20 22:22:14 -04:00
|
|
|
# load an optional init file
|
|
|
|
try:
|
|
|
|
exec(open(os.path.join(os.getenv('HOME'), '.pmc', 'init'), 'r'))
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
2008-04-11 16:20:24 -04:00
|
|
|
# if -b but no -m, then use -m hex
|
|
|
|
if opts.binary and not opts.mode:
|
|
|
|
opts.mode = 'hex'
|
|
|
|
|
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)
|
2008-03-30 04:20:37 -04:00
|
|
|
if goto_line:
|
|
|
|
opts.goto = goto_line
|
2007-06-04 23:05:33 -04:00
|
|
|
|
2007-03-06 10:05:38 -05:00
|
|
|
# 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 = []
|
2008-05-04 02:40:30 -04:00
|
|
|
names = set()
|
|
|
|
paths = set()
|
2008-04-06 22:31:13 -04:00
|
|
|
|
2009-06-08 23:13:04 -04:00
|
|
|
# if we used a pipe to read some data, that will be our first buffer
|
|
|
|
if opts.pipe:
|
|
|
|
data = sys.stdin.read()
|
2009-06-09 01:50:43 -04:00
|
|
|
# since we just read until EOF from stdin, we need to reset STDIN using
|
|
|
|
# the TTY.
|
|
|
|
tty = open('/dev/tty', 'r')
|
|
|
|
os.dup2(tty.fileno(), sys.stdin.fileno())
|
|
|
|
|
|
|
|
# ok, so now create the actual buffer
|
2009-06-08 23:13:04 -04:00
|
|
|
b = buffer.data.DataBuffer('*Pipe*', data)
|
|
|
|
b.open()
|
|
|
|
buffers.append(b)
|
|
|
|
|
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
|
2009-07-24 00:07:03 -04:00
|
|
|
|
|
|
|
# find a free name that doesn't conflict with any others
|
|
|
|
name = util.make_name(os.path.basename(path), lambda x: x in names)
|
2007-10-12 19:32:17 -04:00
|
|
|
|
|
|
|
try:
|
2008-04-16 00:44:32 -04:00
|
|
|
b = f(path, name, opts.binary)
|
2007-10-12 19:32:17 -04:00
|
|
|
b.open()
|
2007-10-21 20:50:11 -04:00
|
|
|
except buffer.BinaryDataException, e:
|
2008-04-11 16:20:24 -04:00
|
|
|
if not opts.mode:
|
|
|
|
opts.mode = 'hex'
|
2008-04-16 00:44:32 -04:00
|
|
|
b = f(path, name, True)
|
2007-10-12 19:32:17 -04:00
|
|
|
b.open()
|
|
|
|
|
2007-03-06 10:05:38 -05:00
|
|
|
buffers.append(b)
|
|
|
|
paths.add(path)
|
|
|
|
names.add(name)
|
|
|
|
|
2008-11-08 10:30:04 -05:00
|
|
|
# save terminal state so we can restore it when the program exits
|
|
|
|
attr = termios.tcgetattr(sys.stdin)
|
|
|
|
keyinput.disable_control_chars()
|
|
|
|
|
2007-03-06 10:05:38 -05:00
|
|
|
# ok, now run our app
|
2008-11-08 10:30:04 -05:00
|
|
|
try:
|
2009-10-01 20:06:17 -04:00
|
|
|
curses.wrapper(run_app, buffers, jump_to_line=opts.goto,
|
|
|
|
init_mode=opts.mode, init_cmds=opts.cmds)
|
2008-11-08 23:13:12 -05:00
|
|
|
err = 0
|
2008-11-08 10:30:04 -05:00
|
|
|
except:
|
2009-07-13 20:42:34 -04:00
|
|
|
# restore terminal state before printing an error
|
|
|
|
termios.tcsetattr(sys.stdin, termios.TCSANOW, attr)
|
2008-11-08 10:30:04 -05:00
|
|
|
traceback.print_exc()
|
2008-11-08 23:13:12 -05:00
|
|
|
err = 1
|
2008-11-08 10:30:04 -05:00
|
|
|
|
2009-07-13 20:42:34 -04:00
|
|
|
# restore terminal state before exiting
|
2008-11-08 10:30:04 -05:00
|
|
|
termios.tcsetattr(sys.stdin, termios.TCSANOW, attr)
|
2008-11-08 23:13:12 -05:00
|
|
|
sys.exit(err)
|