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.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
View File

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

View File

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

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