parent
11412d350b
commit
e23578d82c
|
@ -209,6 +209,8 @@ class Application(object):
|
||||||
curses.cbreak()
|
curses.cbreak()
|
||||||
curses.noecho()
|
curses.noecho()
|
||||||
curses.nonl()
|
curses.nonl()
|
||||||
|
# for non-blocking junk
|
||||||
|
curses.halfdelay(1)
|
||||||
curses.def_prog_mode()
|
curses.def_prog_mode()
|
||||||
|
|
||||||
def _load_config_defaults(self):
|
def _load_config_defaults(self):
|
||||||
|
|
128
buffer.py
128
buffer.py
|
@ -1,9 +1,12 @@
|
||||||
import codecs, datetime, grp, md5, os, pwd, re, sets, shutil, stat, string
|
import codecs, datetime, grp, md5, os, pwd, re, sets, shutil, stat, string
|
||||||
import aes, dirutil, regex, highlight, lex
|
import fcntl, select, pty, threading
|
||||||
|
import aes, dirutil, regex, highlight, lex, term
|
||||||
from point import Point
|
from point import Point
|
||||||
from subprocess import Popen, PIPE, STDOUT
|
from subprocess import Popen, PIPE, STDOUT
|
||||||
|
from keyinput import MAP
|
||||||
|
|
||||||
# undo/redo stack constants
|
# undo/redo stack constants
|
||||||
|
ACT_NONE = -1
|
||||||
ACT_NORM = 0
|
ACT_NORM = 0
|
||||||
ACT_UNDO = 1
|
ACT_UNDO = 1
|
||||||
ACT_REDO = 2
|
ACT_REDO = 2
|
||||||
|
@ -103,7 +106,9 @@ class Buffer(object):
|
||||||
while len(stack) > self.stack_limit:
|
while len(stack) > self.stack_limit:
|
||||||
stack.pop(0)
|
stack.pop(0)
|
||||||
def add_to_stack(self, move, act):
|
def add_to_stack(self, move, act):
|
||||||
if act == ACT_NORM:
|
if act == ACT_NONE:
|
||||||
|
pass
|
||||||
|
elif act == ACT_NORM:
|
||||||
self.redo_stack = []
|
self.redo_stack = []
|
||||||
self.undo_stack.append(move)
|
self.undo_stack.append(move)
|
||||||
self._stack_trim(self.undo_stack)
|
self._stack_trim(self.undo_stack)
|
||||||
|
@ -528,6 +533,125 @@ class IpythonBuffer(InterpreterBuffer):
|
||||||
class BinaryDataException(Exception):
|
class BinaryDataException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class PipeBuffer(Buffer):
|
||||||
|
btype = 'pipe'
|
||||||
|
terms = {
|
||||||
|
'dumb': term.Dumb,
|
||||||
|
'xterm': term.XTerm,
|
||||||
|
}
|
||||||
|
modename = 'colortext'
|
||||||
|
def __init__(self, cmd, args, name=None, term='dumb'):
|
||||||
|
Buffer.__init__(self)
|
||||||
|
self.cmd = cmd
|
||||||
|
if name:
|
||||||
|
self._name = name
|
||||||
|
else:
|
||||||
|
self._name = '*Pipe*'
|
||||||
|
|
||||||
|
self.term = self.terms[term]()
|
||||||
|
|
||||||
|
self._pid, self._pty = pty.fork()
|
||||||
|
if self._pid == 0:
|
||||||
|
# child process
|
||||||
|
os.execve(cmd, [cmd] + args, {'TERM': self.term.name})
|
||||||
|
|
||||||
|
self._lock = threading.Lock()
|
||||||
|
self._towrite = ''
|
||||||
|
self._done = False
|
||||||
|
self._set_nonblock(self._pty)
|
||||||
|
self._thread = threading.Thread(target=self.pipe_read)
|
||||||
|
self._thread.setDaemon(True)
|
||||||
|
self._thread.start()
|
||||||
|
|
||||||
|
def _set_nonblock(self, fd):
|
||||||
|
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
|
||||||
|
fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NDELAY)
|
||||||
|
|
||||||
|
def _filter_output(self, output):
|
||||||
|
output2 = []
|
||||||
|
i = 0
|
||||||
|
escaped = []
|
||||||
|
for c in output:
|
||||||
|
if escaped:
|
||||||
|
escaped.append(c)
|
||||||
|
if c == 'm':
|
||||||
|
escaped = []
|
||||||
|
elif c == '\x1b':
|
||||||
|
escaped.append(c)
|
||||||
|
elif c == '\n':
|
||||||
|
output2.append(c)
|
||||||
|
i = 0
|
||||||
|
elif c == '\t':
|
||||||
|
j = i % 8
|
||||||
|
output2.append(' ' * (8 - j))
|
||||||
|
i += 8 - j
|
||||||
|
elif c == '\a':
|
||||||
|
pass
|
||||||
|
elif c == '\b':
|
||||||
|
if i > 0:
|
||||||
|
output2.pop(-1)
|
||||||
|
i -= 1
|
||||||
|
else:
|
||||||
|
output2.append(c)
|
||||||
|
i += 1
|
||||||
|
return ''.join(output2)
|
||||||
|
|
||||||
|
def pipe_read(self):
|
||||||
|
fd = self._pty
|
||||||
|
while not self._done:
|
||||||
|
if self._towrite:
|
||||||
|
ifd, ofd, efd = select.select([fd], [fd], [fd], 0.1)
|
||||||
|
else:
|
||||||
|
ifd, ofd, efd = select.select([fd], [], [fd], 0.1)
|
||||||
|
if ifd:
|
||||||
|
data = os.read(ifd[0], 1024)
|
||||||
|
end = self.get_buffer_end()
|
||||||
|
#data = self._filter_output(data)
|
||||||
|
data = self.term.filter(data)
|
||||||
|
self.insert_string(end, data, force=True, act=ACT_NONE)
|
||||||
|
if ofd:
|
||||||
|
self._lock.acquire()
|
||||||
|
n = os.write(ofd[0], self._towrite)
|
||||||
|
self._towrite = self._towrite[n:]
|
||||||
|
self._lock.release()
|
||||||
|
if efd:
|
||||||
|
raise Exception, "exception is ready: %s" % repr(efd)
|
||||||
|
os.close(fd)
|
||||||
|
|
||||||
|
def pipe_write(self, s):
|
||||||
|
self._lock.acquire()
|
||||||
|
self._towrite += s
|
||||||
|
self._lock.release()
|
||||||
|
|
||||||
|
def insert_string(self, p, s, act=ACT_NORM, force=False):
|
||||||
|
lines = s.split("\n")
|
||||||
|
self.insert_lines(p, lines, act, force)
|
||||||
|
|
||||||
|
def insert_lines(self, p, lines, act=ACT_NORM, force=False):
|
||||||
|
llen = len(lines)
|
||||||
|
assert llen > 0
|
||||||
|
if not force and self.readonly():
|
||||||
|
raise ReadOnlyError("buffer is read-only")
|
||||||
|
p2 = p.vadd(len(lines[-1]), llen - 1)
|
||||||
|
if llen > 1:
|
||||||
|
self.lines.insert(p.y + 1, [])
|
||||||
|
self.lines[p.y + 1] = lines[-1] + self.lines[p.y][p.x:]
|
||||||
|
self.lines[p.y] = self.lines[p.y][:p.x] + lines[0]
|
||||||
|
for i in range(1, llen - 1):
|
||||||
|
self.lines.insert(p.y + i, lines[i])
|
||||||
|
else:
|
||||||
|
self.lines[p.y] = self.lines[p.y][:p.x] + lines[-1] + self.lines[p.y][p.x:]
|
||||||
|
self._region_add(p, p2, lines, act)
|
||||||
|
self.modified = True
|
||||||
|
|
||||||
|
def name(self): return self._name
|
||||||
|
def changed(self): return False
|
||||||
|
def readonly(self): return True
|
||||||
|
def undo(self, move, act): raise Exception, "invalid"
|
||||||
|
def redo(self, move, act): raise Exception, "invalid"
|
||||||
|
def reload(self): raise Exception, "invalid"
|
||||||
|
def close(self): raise Exception, "invalid"
|
||||||
|
|
||||||
class FileBuffer(Buffer):
|
class FileBuffer(Buffer):
|
||||||
btype = 'file'
|
btype = 'file'
|
||||||
def __init__(self, path, name=None):
|
def __init__(self, path, name=None):
|
||||||
|
|
|
@ -5,18 +5,7 @@ from point import Point
|
||||||
from method import Method
|
from method import Method
|
||||||
from subprocess import Popen, PIPE, STDOUT
|
from subprocess import Popen, PIPE, STDOUT
|
||||||
|
|
||||||
LIMIT = 79
|
|
||||||
|
|
||||||
class ShellExec(Method):
|
class ShellExec(Method):
|
||||||
sequences = {
|
|
||||||
'\x1b[01;30m': "[green:default]",
|
|
||||||
'\x1b[01;31m': "[green:default]",
|
|
||||||
'\x1b[01;32m': "[green:default]",
|
|
||||||
'\x1b[01;34m': "[blue:default]",
|
|
||||||
'\x1b[01;36m': "[cyan:default]",
|
|
||||||
'\x1b[0m': "[default:default]",
|
|
||||||
}
|
|
||||||
sequences = {}
|
|
||||||
def _execute(self, w, **vargs):
|
def _execute(self, w, **vargs):
|
||||||
a = w.application
|
a = w.application
|
||||||
if a.completion_window_is_open():
|
if a.completion_window_is_open():
|
||||||
|
@ -33,78 +22,7 @@ class ShellExec(Method):
|
||||||
b = a.bufferlist.get_buffer_by_name('*Shell*')
|
b = a.bufferlist.get_buffer_by_name('*Shell*')
|
||||||
if a.window().buffer is not b:
|
if a.window().buffer is not b:
|
||||||
a.switch_buffer(b)
|
a.switch_buffer(b)
|
||||||
p = a.get_mini_buffer_prompt()
|
b.pipe_write(s + "\n")
|
||||||
b.insert_string(b.get_buffer_end(), p + s + '\n', force=True)
|
|
||||||
|
|
||||||
a.set_mini_buffer_prompt('>>> ')
|
|
||||||
ok = True
|
|
||||||
|
|
||||||
args = ['sh', '-c', s]
|
|
||||||
env = {'COLUMNS': '80', 'LINES': '24', 'TERM': os.environ['TERM']}
|
|
||||||
p = Popen(args, bufsize=1024, stderr=STDOUT, stdout=PIPE,
|
|
||||||
close_fds=True, env=env)
|
|
||||||
output = p.stdout.read()
|
|
||||||
output2 = []
|
|
||||||
i = 0
|
|
||||||
escaped = []
|
|
||||||
for c in output:
|
|
||||||
if escaped:
|
|
||||||
escaped.append(c)
|
|
||||||
if c == 'm':
|
|
||||||
#seq = ''.join(escaped)
|
|
||||||
#if seq in self.sequences:
|
|
||||||
# output2.append(self.sequences[seq])
|
|
||||||
escaped = []
|
|
||||||
elif c == '\x1b':
|
|
||||||
escaped.append(c)
|
|
||||||
elif c == '\n':
|
|
||||||
output2.append(c)
|
|
||||||
i = 0
|
|
||||||
elif c == '\t':
|
|
||||||
j = i % 8
|
|
||||||
output2.append(' ' * (8 - j))
|
|
||||||
i += 8 - j
|
|
||||||
elif c == '\a':
|
|
||||||
pass
|
|
||||||
elif c == '\b':
|
|
||||||
if i > 0:
|
|
||||||
output2.pop(-1)
|
|
||||||
i -= 1
|
|
||||||
else:
|
|
||||||
output2.append(c)
|
|
||||||
i += 1
|
|
||||||
output = ''.join(output2)
|
|
||||||
|
|
||||||
limit = 1000
|
|
||||||
for w2 in b.windows:
|
|
||||||
limit = min(w.width, limit)
|
|
||||||
if limit == 1000:
|
|
||||||
limit = LIMIT
|
|
||||||
|
|
||||||
if output:
|
|
||||||
newlines = []
|
|
||||||
for line in output.split('\n'):
|
|
||||||
line = line.replace('\x1b[01;32m', "[green:default]")
|
|
||||||
line = line.replace('\x1b[01;34m', "[blue:default]")
|
|
||||||
line = line.replace('\x1b[01;36m', "[cyan:default]")
|
|
||||||
line = line.replace('\x1b[0m', "[default:default]")
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
while i + limit < len(line):
|
|
||||||
j = limit
|
|
||||||
while j > 0 and line[i + j] != ' ':
|
|
||||||
j -= 1
|
|
||||||
if j == 0:
|
|
||||||
newlines.append(line[i:i + limit])
|
|
||||||
i += limit
|
|
||||||
else:
|
|
||||||
newlines.append(line[i:i + j])
|
|
||||||
i += j + 1
|
|
||||||
newlines.append(line[i:])
|
|
||||||
newlines[-1] = ''
|
|
||||||
b.insert_lines(b.get_buffer_end(), newlines, force=True)
|
|
||||||
for w2 in b.windows:
|
|
||||||
w2.goto_end(force=True)
|
|
||||||
|
|
||||||
class ShellCancel(Method):
|
class ShellCancel(Method):
|
||||||
def execute(self, w, **vargs):
|
def execute(self, w, **vargs):
|
||||||
|
@ -117,7 +35,6 @@ class ShellClear(Method):
|
||||||
if not a.has_buffer_name('*Shell*'):
|
if not a.has_buffer_name('*Shell*'):
|
||||||
raise Exception, "No shell found!"
|
raise Exception, "No shell found!"
|
||||||
b = a.bufferlist.get_buffer_by_name('*Shell*')
|
b = a.bufferlist.get_buffer_by_name('*Shell*')
|
||||||
b.clear()
|
|
||||||
|
|
||||||
class ShellHistoryPrev(Method):
|
class ShellHistoryPrev(Method):
|
||||||
def execute(self, w, **vargs):
|
def execute(self, w, **vargs):
|
||||||
|
@ -207,7 +124,7 @@ class OpenShell(Method):
|
||||||
def execute(self, w, **vargs):
|
def execute(self, w, **vargs):
|
||||||
a = w.application
|
a = w.application
|
||||||
if not a.has_buffer_name('*Shell*'):
|
if not a.has_buffer_name('*Shell*'):
|
||||||
b = buffer.ShellBuffer()
|
b = buffer.PipeBuffer('/bin/bash', [], name="*Shell*", term='xterm')
|
||||||
a.add_buffer(b)
|
a.add_buffer(b)
|
||||||
window.Window(b, a)
|
window.Window(b, a)
|
||||||
b = a.bufferlist.get_buffer_by_name('*Shell*')
|
b = a.bufferlist.get_buffer_by_name('*Shell*')
|
||||||
|
@ -216,24 +133,15 @@ class OpenShell(Method):
|
||||||
f = lambda x: None
|
f = lambda x: None
|
||||||
w.application.open_mini_buffer('>>> ', f, self, None, 'shellmini')
|
w.application.open_mini_buffer('>>> ', f, self, None, 'shellmini')
|
||||||
|
|
||||||
class ShellMiniGrammar(Grammar):
|
|
||||||
rules = [
|
|
||||||
PatternRule(r'word', r"'.*'"),
|
|
||||||
PatternRule(r'word', r'"(:\\.|[^"\\])*"'),
|
|
||||||
PatternRule(r'word', r'(?:[^ \n"\'\\]|\\.)+'),
|
|
||||||
]
|
|
||||||
|
|
||||||
class ShellMini(mode.Fundamental):
|
class ShellMini(mode.Fundamental):
|
||||||
modename = 'ShellMini'
|
modename = 'ShellMini'
|
||||||
grammar = ShellMiniGrammar
|
actions = [ShellExec, ShellClear, ShellCancel,
|
||||||
actions = [ShellExec, ShellClear, ShellCancel, ShellHistoryPrev,
|
ShellHistoryPrev, ShellHistoryNext,
|
||||||
ShellHistoryNext, ShellTab,
|
ShellTab,
|
||||||
ShellPageUp, ShellPageDown, ShellGotoBeginning, ShellGotoEnd,
|
ShellPageUp, ShellPageDown, ShellGotoBeginning, ShellGotoEnd,
|
||||||
OpenShell]
|
OpenShell]
|
||||||
def __init__(self, w):
|
def __init__(self, w):
|
||||||
mode.Fundamental.__init__(self, w)
|
mode.Fundamental.__init__(self, w)
|
||||||
self.globals = dict(w.application.globals())
|
|
||||||
self.locals = dict(w.application.locals())
|
|
||||||
self.saved_input = ""
|
self.saved_input = ""
|
||||||
self.history = ['']
|
self.history = ['']
|
||||||
self.hindex = 0
|
self.hindex = 0
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
class Dumb:
|
||||||
|
name = 'dumb'
|
||||||
|
def insert(self, s):
|
||||||
|
assert self.i <= len(self.outc)
|
||||||
|
if self.i == len(self.outc):
|
||||||
|
self.outc.append(s)
|
||||||
|
else:
|
||||||
|
self.outc[self.i] = s
|
||||||
|
self.i += 1
|
||||||
|
|
||||||
|
def do_backspace(self):
|
||||||
|
self.i = max(0, self.i - 1)
|
||||||
|
def do_tab(self):
|
||||||
|
self.insert(' ')
|
||||||
|
def do_newline(self):
|
||||||
|
self.outs += ''.join(self.outc) + '\n'
|
||||||
|
self.i = 0
|
||||||
|
self.outc = []
|
||||||
|
def do_careturn(self):
|
||||||
|
self.i = 0
|
||||||
|
def do_esc(self, c):
|
||||||
|
pass
|
||||||
|
def do_delete(self):
|
||||||
|
if self.i < len(self.outc):
|
||||||
|
del self.outc[self.i]
|
||||||
|
|
||||||
|
def handle_ctl(self, c):
|
||||||
|
n = ord(c)
|
||||||
|
if n == 8:
|
||||||
|
self.do_backspace()
|
||||||
|
elif n == 9:
|
||||||
|
self.do_tab()
|
||||||
|
elif n == 10:
|
||||||
|
self.do_newline()
|
||||||
|
elif n == 13:
|
||||||
|
self.do_careturn()
|
||||||
|
elif n == 27:
|
||||||
|
self.do_esc(c)
|
||||||
|
elif n == 127:
|
||||||
|
self.do_delete()
|
||||||
|
def handle_print(self, c):
|
||||||
|
self.insert(c)
|
||||||
|
def handle_8bit(self, c):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle(self, c):
|
||||||
|
n = ord(c)
|
||||||
|
assert n >= 0 and n < 256
|
||||||
|
if n <= 27 or n == 127:
|
||||||
|
self.handle_ctl(c)
|
||||||
|
elif n < 127:
|
||||||
|
self.handle_print(c)
|
||||||
|
else:
|
||||||
|
self.handle_8bit(c)
|
||||||
|
|
||||||
|
def filter(self, s):
|
||||||
|
self.i = 0
|
||||||
|
self.outc = []
|
||||||
|
self.outs = ""
|
||||||
|
for c in s:
|
||||||
|
self.handle(c)
|
||||||
|
return self.outs + ''.join(self.outc)
|
||||||
|
|
||||||
|
class XTerm(Dumb):
|
||||||
|
name = 'xterm'
|
||||||
|
ansi_colors = {
|
||||||
|
'\033[30m': '[B:d]',
|
||||||
|
'\033[30;0m': '[B:d]',
|
||||||
|
'\033[30;1m': '[B:d:*]',
|
||||||
|
'\033[31m': '[r:d]',
|
||||||
|
'\033[31;0m': '[r:d]',
|
||||||
|
'\033[31;1m': '[r:d:*]',
|
||||||
|
'\033[32m': '[g:d]',
|
||||||
|
'\033[32;0m': '[g:d]',
|
||||||
|
'\033[32;1m': '[g:d:*]',
|
||||||
|
'\033[33m': '[y:d]',
|
||||||
|
'\033[33;0m': '[y:d]',
|
||||||
|
'\033[33;1m': '[y:d:*]',
|
||||||
|
'\033[34m': '[b:d]',
|
||||||
|
'\033[34;0m': '[b:d]',
|
||||||
|
'\033[34;1m': '[b:d:*]',
|
||||||
|
'\033[35m': '[m:d]',
|
||||||
|
'\033[35;0m': '[m:d]',
|
||||||
|
'\033[35;1m': '[m:d:*]',
|
||||||
|
'\033[36m': '[c:d]',
|
||||||
|
'\033[36;0m': '[c:d]',
|
||||||
|
'\033[36;1m': '[c:d:*]',
|
||||||
|
'\033[37m': '[w:d]',
|
||||||
|
'\033[37;0m': '[w:d]',
|
||||||
|
'\033[37;1m': '[w:d:*]',
|
||||||
|
'\033[39m': '[d:d]',
|
||||||
|
'\033[39;0m': '[d:d]',
|
||||||
|
'\033[39;1m': '[d:d:*]',
|
||||||
|
}
|
||||||
|
def filter(self, s):
|
||||||
|
self.meta = []
|
||||||
|
return Dumb.filter(self, s)
|
||||||
|
|
||||||
|
def do_esc(self, c):
|
||||||
|
self.meta.append(c)
|
||||||
|
|
||||||
|
def handle(self, c):
|
||||||
|
if self.meta:
|
||||||
|
self.meta.append(c)
|
||||||
|
if c == 'm':
|
||||||
|
s = ''.join(self.meta)
|
||||||
|
if s in self.ansi_colors:
|
||||||
|
#self.insert(self.ansi_colors[s])
|
||||||
|
pass
|
||||||
|
self.meta = []
|
||||||
|
else:
|
||||||
|
Dumb.handle(self, c)
|
38
window.py
38
window.py
|
@ -239,10 +239,42 @@ class Window(object):
|
||||||
counter += 1
|
counter += 1
|
||||||
self.first = Point(x - (x % self.width), y)
|
self.first = Point(x - (x % self.width), y)
|
||||||
self.redraw()
|
self.redraw()
|
||||||
|
def lower_view(self):
|
||||||
|
(x, y) = self.logical_cursor().xy()
|
||||||
|
counter = 0
|
||||||
|
while counter < self.height - 1:
|
||||||
|
if x > self.width:
|
||||||
|
x -= self.width
|
||||||
|
elif y > 0:
|
||||||
|
y -= 1
|
||||||
|
x = len(self.buffer.lines[y])
|
||||||
|
else:
|
||||||
|
(x, y) = (0, 0)
|
||||||
|
break
|
||||||
|
counter += 1
|
||||||
|
self.first = Point(x - (x % self.width), y)
|
||||||
|
self.redraw()
|
||||||
|
def upper_view(self):
|
||||||
|
(x, y) = self.logical_cursor().xy()
|
||||||
|
counter = 0
|
||||||
|
while counter < 2:
|
||||||
|
if x > self.width:
|
||||||
|
x -= self.width
|
||||||
|
elif y > 0:
|
||||||
|
y -= 1
|
||||||
|
x = len(self.buffer.lines[y])
|
||||||
|
else:
|
||||||
|
(x, y) = (0, 0)
|
||||||
|
break
|
||||||
|
counter += 1
|
||||||
|
self.first = Point(x - (x % self.width), y)
|
||||||
|
self.redraw()
|
||||||
def assure_visible_cursor(self):
|
def assure_visible_cursor(self):
|
||||||
if not self.cursor_is_visible():
|
p = self.logical_cursor()
|
||||||
#raise Exception, "%s < %s" % (self.last, self.logical_cursor())
|
if self.first > p:
|
||||||
self.center_view()
|
self.upper_view()
|
||||||
|
elif p > self.last:
|
||||||
|
self.lower_view()
|
||||||
|
|
||||||
# moving in buffer
|
# moving in buffer
|
||||||
def forward(self):
|
def forward(self):
|
||||||
|
|
Loading…
Reference in New Issue