term term term TERM

--HG--
branch : pmacs2
This commit is contained in:
moculus 2008-10-18 02:03:27 +00:00
parent 11412d350b
commit e23578d82c
5 changed files with 280 additions and 102 deletions

View File

@ -209,6 +209,8 @@ class Application(object):
curses.cbreak()
curses.noecho()
curses.nonl()
# for non-blocking junk
curses.halfdelay(1)
curses.def_prog_mode()
def _load_config_defaults(self):

128
buffer.py
View File

@ -1,9 +1,12 @@
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 subprocess import Popen, PIPE, STDOUT
from keyinput import MAP
# undo/redo stack constants
ACT_NONE = -1
ACT_NORM = 0
ACT_UNDO = 1
ACT_REDO = 2
@ -103,7 +106,9 @@ class Buffer(object):
while len(stack) > self.stack_limit:
stack.pop(0)
def add_to_stack(self, move, act):
if act == ACT_NORM:
if act == ACT_NONE:
pass
elif act == ACT_NORM:
self.redo_stack = []
self.undo_stack.append(move)
self._stack_trim(self.undo_stack)
@ -528,6 +533,125 @@ class IpythonBuffer(InterpreterBuffer):
class BinaryDataException(Exception):
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):
btype = 'file'
def __init__(self, path, name=None):

View File

@ -5,18 +5,7 @@ from point import Point
from method import Method
from subprocess import Popen, PIPE, STDOUT
LIMIT = 79
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):
a = w.application
if a.completion_window_is_open():
@ -33,78 +22,7 @@ class ShellExec(Method):
b = a.bufferlist.get_buffer_by_name('*Shell*')
if a.window().buffer is not b:
a.switch_buffer(b)
p = a.get_mini_buffer_prompt()
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)
b.pipe_write(s + "\n")
class ShellCancel(Method):
def execute(self, w, **vargs):
@ -117,7 +35,6 @@ class ShellClear(Method):
if not a.has_buffer_name('*Shell*'):
raise Exception, "No shell found!"
b = a.bufferlist.get_buffer_by_name('*Shell*')
b.clear()
class ShellHistoryPrev(Method):
def execute(self, w, **vargs):
@ -207,7 +124,7 @@ class OpenShell(Method):
def execute(self, w, **vargs):
a = w.application
if not a.has_buffer_name('*Shell*'):
b = buffer.ShellBuffer()
b = buffer.PipeBuffer('/bin/bash', [], name="*Shell*", term='xterm')
a.add_buffer(b)
window.Window(b, a)
b = a.bufferlist.get_buffer_by_name('*Shell*')
@ -216,24 +133,15 @@ class OpenShell(Method):
f = lambda x: None
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):
modename = 'ShellMini'
grammar = ShellMiniGrammar
actions = [ShellExec, ShellClear, ShellCancel, ShellHistoryPrev,
ShellHistoryNext, ShellTab,
actions = [ShellExec, ShellClear, ShellCancel,
ShellHistoryPrev, ShellHistoryNext,
ShellTab,
ShellPageUp, ShellPageDown, ShellGotoBeginning, ShellGotoEnd,
OpenShell]
def __init__(self, w):
mode.Fundamental.__init__(self, w)
self.globals = dict(w.application.globals())
self.locals = dict(w.application.locals())
self.saved_input = ""
self.history = ['']
self.hindex = 0

112
term.py Normal file
View File

@ -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)

View File

@ -239,10 +239,42 @@ class Window(object):
counter += 1
self.first = Point(x - (x % self.width), y)
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):
if not self.cursor_is_visible():
#raise Exception, "%s < %s" % (self.last, self.logical_cursor())
self.center_view()
p = self.logical_cursor()
if self.first > p:
self.upper_view()
elif p > self.last:
self.lower_view()
# moving in buffer
def forward(self):