buffer refactor

--HG--
branch : pmacs2
This commit is contained in:
moculus 2008-10-29 04:02:50 +00:00
parent 79f120d089
commit 93ca1881bf
8 changed files with 266 additions and 257 deletions

View File

@ -3,7 +3,8 @@ import curses, curses.ascii, getpass, os, re, string, sys, termios, time
import traceback import traceback
from subprocess import Popen, PIPE, STDOUT from subprocess import Popen, PIPE, STDOUT
import buffer, bufferlist, color, completer, keyinput, method, minibuffer, mode import buffer, buffer.about, buffer.color
import bufferlist, color, completer, keyinput, method, minibuffer, mode
import util, window import util, window
from point import Point from point import Point
@ -134,7 +135,7 @@ class Application(object):
'latex', 'insertmini', 'conf', 'haskell', 'erlang', 'latex', 'insertmini', 'conf', 'haskell', 'erlang',
'iperl', 'iperlmini', 'ipython', 'ipythonmini', 'awk', 'iperl', 'iperlmini', 'ipython', 'ipythonmini', 'awk',
'bds', #XYZ 'bds', #XYZ
'shell', 'shellmini', 'fstab', 'yacc', 'shell', 'shellmini', 'fstab', 'yacc', 'pipe',
) )
for name in names: for name in names:
exec("import mode.%s; mode.%s.install(self)" % (name, name)) exec("import mode.%s; mode.%s.install(self)" % (name, name))
@ -161,8 +162,7 @@ class Application(object):
# initialize our buffers # initialize our buffers
# note that the first buffer in buffers will be initially visible # note that the first buffer in buffers will be initially visible
buffers.append(buffer.AboutBuffer()) buffers.append(buffer.about.AboutBuffer())
buffers.append(buffer.ScratchBuffer())
buffers.append(buffer.ConsoleBuffer()) buffers.append(buffer.ConsoleBuffer())
if self.rcerror: if self.rcerror:
@ -478,7 +478,7 @@ class Application(object):
if self.has_buffer_name(name): if self.has_buffer_name(name):
b = self.bufferlist.buffer_names[name] b = self.bufferlist.buffer_names[name]
self.remove_buffer(b) self.remove_buffer(b)
b = buffer.ColorDataBuffer(name, data) b = buffer.color.ColorDataBuffer(name, data)
if modename is not None: if modename is not None:
b.modename = modename b.modename = modename
window.Window(b, self, height=0, width=0) window.Window(b, self, height=0, width=0)

View File

@ -354,21 +354,6 @@ class Buffer(object):
# should not happen # should not happen
raise Exception, "iiiijjjj" raise Exception, "iiiijjjj"
# scratch is a singleton
scratch = None
class ScratchBuffer(Buffer):
btype = 'scratch'
def __new__(cls, *args, **kwargs):
global scratch
if scratch is None:
scratch = object.__new__(ScratchBuffer, *args, **kwargs)
return scratch
def name(self):
return "*Scratch*"
def close(self):
global scratch
scratch = None
class DataBuffer(Buffer): class DataBuffer(Buffer):
btype = 'data' btype = 'data'
def __init__(self, name, data): def __init__(self, name, data):
@ -533,122 +518,6 @@ 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:
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.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):
@ -946,120 +815,3 @@ class PathListBuffer(DirBuffer):
return name return name
def name(self): def name(self):
return self._name return self._name
# NOTE: this highlighter will not reprocess the data given. it is intended to
# be used with read-only buffers like DataBuffer and ColorBuffer
class ColorHighlighter(highlight.Highlighter):
color_re = re.compile(r'\[([a-zA-Z0-9\*:]+)\]')
color_map = {
'B': 'black',
'r': 'red',
'g': 'green',
'y': 'yellow',
'b': 'blue',
'm': 'magenta',
'c': 'cyan',
'w': 'white',
'd': 'default',
'*': 'bold',
}
def __init__(self):
self.tokens = []
def append_token(self, y, x, s, color):
s2 = s.replace('\\[', '[')
s2 = s2.replace('\\]', ']')
s2 = s2.replace('\\\\', '\\')
t = lex.Token('color_data', None, y, x, s2, color)
self.tokens[y].append(t)
return len(s) - len(s2)
def delete_token(self, y, i):
pass
def relex(self, lines, y1, x1, y2, x2, token=None):
pass
def relex_del(self, lines, y1, x1, y2, x2):
pass
def highlight(self, lines):
if self.tokens:
return
self.tokens = [[] for l in lines]
#self.tokens = [None] * len(lines)
for y in range(0, len(lines)):
self.tokens[y] = []
line = lines[y]
c = ['default', 'default']
i = 0
offset = 0
while i < len(line):
m = self.color_re.search(line, i)
if m:
(j, k) = (m.start(), m.end())
if j > i:
offset += self.append_token(y, i - offset, line[i:j], c)
fields = m.group(1).split(':')
c = [self.color_map.get(x, x) for x in fields]
offset += k - j
i = k
else:
offset += self.append_token(y, i - offset, line[i:], c)
break
class ColorDataBuffer(DataBuffer):
btype = 'colordata'
modename = 'colortext'
color_re = re.compile(r'\[([a-z:]+)\]')
def _highlight(self, data):
data2 = ColorHighlighter.color_re.sub('', data)
data2 = data2.replace('\\[', '[')
data2 = data2.replace('\\]', ']')
data2 = data2.replace('\\\\', '\\')
Buffer.set_data(self, data2, force=True)
lines = data.split(self.nl)
self.highlights = {'Colortext': ColorHighlighter()}
self.highlights['Colortext'].highlight(lines)
self.modified = False
def __init__(self, name, data):
DataBuffer.__init__(self, name, '')
self._highlight(data)
def set_data(self, data, force=True):
self._highlight(data)
ABOUT_DATA = '''
[r:d:*]===============================================================================
[y:d:*]************ ********** ****** ****** **** ******** *********
[y:d:*]************** ****************** ************* ********** ***********
[y:d:*]******* ***** ****** ***** **** **** ****** **** **** ***** ***
[y:d:*] *** *** *** *** *** **** **** **** *******
[y:d:*] *** *** *** *** *** **** **** **** ******
[y:d:*] ***** ***** ***** ***** **** **** ****** **** **** **** ****
[y:d:*] ************ ***** ***** **** ************* ********** **********
[y:d:*] ********** ***** ***** **** ****** **** ******** ********
[y:d:*] ***
[y:d:*] *** [c:d:*]pmacs[d:d:*] is a python-based text editor by [c:d:*]Erik Osheim[d:d:*], [b:d:*](c) 2005-2008
[y:d:*] *** [c:d:*]pmacs[d:d:*] is available to you under the [c:d:*]GNU General Public License v2
[y:d:*]*****
[y:d:*]***** [d:d:*]to view available commands use [c:d:*]C-c M-h [b:d:*](show-bindings-buffer)
[y:d:*]***** [d:d:*]open files with [c:d:*]C-x C-f[d:d:*]; save with [c:d:*]C-x C-s[d:d:*]; quit with [c:d:*]C-x C-c
[r:d:*]===============================================================================
'''
class AboutBuffer(ColorDataBuffer):
def __init__(self):
ColorDataBuffer.__init__(self, '*About*', ABOUT_DATA)
class ShellBuffer(Buffer):
btype = 'shell'
modename = 'shell'
def __init__(self):
Buffer.__init__(self)
self.clear()
def clear(self):
lines = ['=== Shell Console\n',
"=== Bash your brains out!\n"]
self.set_data(''.join(lines), force=True)
def name(self):
return '*Shell*'
def changed(self):
return False
def readonly(self):
return True

25
buffer/about.py Normal file
View File

@ -0,0 +1,25 @@
from buffer.color import ColorDataBuffer
_data = '''
[r:d:*]===============================================================================
[y:d:*]************ ********** ****** ****** **** ******** *********
[y:d:*]************** ****************** ************* ********** ***********
[y:d:*]******* ***** ****** ***** **** **** ****** **** **** ***** ***
[y:d:*] *** *** *** *** *** **** **** **** *******
[y:d:*] *** *** *** *** *** **** **** **** ******
[y:d:*] ***** ***** ***** ***** **** **** ****** **** **** **** ****
[y:d:*] ************ ***** ***** **** ************* ********** **********
[y:d:*] ********** ***** ***** **** ****** **** ******** ********
[y:d:*] ***
[y:d:*] *** [c:d:*]pmacs[d:d:*] is a python-based text editor by [c:d:*]Erik Osheim[d:d:*], [b:d:*](c) 2005-2008
[y:d:*] *** [c:d:*]pmacs[d:d:*] is available to you under the [c:d:*]GNU General Public License v2
[y:d:*]*****
[y:d:*]***** [d:d:*]to view available commands use [c:d:*]C-c M-h [b:d:*](show-bindings-buffer)
[y:d:*]***** [d:d:*]open files with [c:d:*]C-x C-f[d:d:*]; save with [c:d:*]C-x C-s[d:d:*]; quit with [c:d:*]C-x C-c
[r:d:*]===============================================================================
'''
class AboutBuffer(ColorDataBuffer):
def __init__(self):
ColorDataBuffer.__init__(self, '*About*', _data)

80
buffer/color.py Normal file
View File

@ -0,0 +1,80 @@
import re
import lex
from buffer import Buffer, DataBuffer
from highlight import Highlighter
# NOTE: this highlighter will not reprocess the data given. it is intended to
# be used with read-only buffers like DataBuffer and ColorBuffer
class ColorHighlighter(Highlighter):
color_re = re.compile(r'\[([a-zA-Z0-9\*:]+)\]')
color_map = {
'B': 'black',
'r': 'red',
'g': 'green',
'y': 'yellow',
'b': 'blue',
'm': 'magenta',
'c': 'cyan',
'w': 'white',
'd': 'default',
'*': 'bold',
}
def __init__(self):
self.tokens = []
def append_token(self, y, x, s, color):
s2 = s.replace('\\[', '[')
s2 = s2.replace('\\]', ']')
s2 = s2.replace('\\\\', '\\')
t = lex.Token('color_data', None, y, x, s2, color)
self.tokens[y].append(t)
return len(s) - len(s2)
def delete_token(self, y, i):
pass
def relex(self, lines, y1, x1, y2, x2, token=None):
pass
def relex_del(self, lines, y1, x1, y2, x2):
pass
def highlight(self, lines):
if self.tokens:
return
self.tokens = [[] for l in lines]
#self.tokens = [None] * len(lines)
for y in range(0, len(lines)):
self.tokens[y] = []
line = lines[y]
c = ['default', 'default']
i = 0
offset = 0
while i < len(line):
m = self.color_re.search(line, i)
if m:
(j, k) = (m.start(), m.end())
if j > i:
offset += self.append_token(y, i - offset, line[i:j], c)
fields = m.group(1).split(':')
c = [self.color_map.get(x, x) for x in fields]
offset += k - j
i = k
else:
offset += self.append_token(y, i - offset, line[i:], c)
break
class ColorDataBuffer(DataBuffer):
btype = 'colordata'
modename = 'colortext'
color_re = re.compile(r'\[([a-z:]+)\]')
def _highlight(self, data):
data2 = ColorHighlighter.color_re.sub('', data)
data2 = data2.replace('\\[', '[')
data2 = data2.replace('\\]', ']')
data2 = data2.replace('\\\\', '\\')
Buffer.set_data(self, data2, force=True)
lines = data.split(self.nl)
self.highlights = {'Colortext': ColorHighlighter()}
self.highlights['Colortext'].highlight(lines)
self.modified = False
def __init__(self, name, data):
DataBuffer.__init__(self, name, '')
self._highlight(data)
def set_data(self, data, force=True):
self._highlight(data)

123
buffer/pipe.py Normal file
View File

@ -0,0 +1,123 @@
import fcntl, os, select, pty, threading
from buffer import Buffer, ACT_NORM, ACT_NONE
import term
class PipeBuffer(Buffer):
btype = 'pipe'
terms = {
'dumb': term.Dumb,
'xterm': term.XTerm,
}
modename = 'pipe'
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:
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
try:
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.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)
except OSError, e:
# read error means we're done
pass
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"

View File

@ -1,7 +1,8 @@
import os, commands, re, sets, tempfile import os, commands, re, sets, tempfile
from subprocess import Popen, PIPE, STDOUT from subprocess import Popen, PIPE, STDOUT
import buffer, default, dirutil, regex, util, window import buffer, buffer.about
import default, dirutil, regex, util, window
from point import Point from point import Point
from method import DATATYPES, Method, Argument from method import DATATYPES, Method, Argument
@ -147,7 +148,7 @@ class AboutPmacs(Method):
def _execute(self, w, **vargs): def _execute(self, w, **vargs):
a = w.application a = w.application
if not a.has_buffer_name('*About*'): if not a.has_buffer_name('*About*'):
b = buffer.AboutBuffer() b = buffer.about.AboutBuffer()
a.add_buffer(b) a.add_buffer(b)
window.Window(b, a) window.Window(b, a)
b = a.bufferlist.get_buffer_by_name('*About*') b = a.bufferlist.get_buffer_by_name('*About*')

27
mode/pipe.py Normal file
View File

@ -0,0 +1,27 @@
import keyinput
from mode import Fundamental
from method import Method
class PipeInsertChr(Method):
_is_method = False
def __init__(self, i):
self.name = "pipe-insert-chr-%s" % i
self.args = []
self.help = "Insert chr(%d) into the current pipe-buffer." % i
self.string = chr(i)
def _execute(self, w, **vargs):
w.buffer.pipe_write(self.string)
class Pipe(Fundamental):
modename = 'pipe'
def __init__(self, w):
Fundamental.__init__(self, w)
_codes = [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14] + \
[16, 18, 20, 23, 25] + list(range(32, 128))
for i in _codes:
sym = keyinput.MAP.get(i, chr(i))
obj = PipeInsertChr(i)
w.application.methods[obj.name] = obj
self.add_binding(obj.name, sym)
install = Pipe.install

View File

@ -1,5 +1,6 @@
import code, os, re, string, StringIO, sys, traceback import code, os, re, string, StringIO, sys, traceback
import buffer, color, completer, lex, method, mode, window import buffer, buffer.pipe
import color, completer, lex, method, mode, window
from lex import Grammar, PatternRule, RegionRule from lex import Grammar, PatternRule, RegionRule
from point import Point from point import Point
from method import Method from method import Method
@ -124,7 +125,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.PipeBuffer('/bin/bash', [], name="*Shell*", term='xterm') b = buffer.pipe.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*')