refactors

--HG--
branch : pmacs2
This commit is contained in:
moculus 2008-11-08 15:30:04 +00:00
parent 4b36c23942
commit ad71553553
27 changed files with 197 additions and 192 deletions

View File

@ -8,37 +8,11 @@ import bufferlist, color, completer, keyinput, method, minibuffer, mode
import util, window
from point import Point
def run(buffers, jump_to_line=None, init_mode=None):
# save terminal state so we can restore it when the program exits
attr = termios.tcgetattr(sys.stdin)
keyinput.disable_control_chars()
retval = 1
try:
retval = curses.wrapper(run_app, buffers, jump_to_line, init_mode)
except:
traceback.print_exc()
# restore terminal state
termios.tcsetattr(sys.stdin, termios.TCSANOW, attr)
return retval
def run_app(stdscr, buffers, jump_to_line=None, init_mode=None):
curses.def_shell_mode()
a = Application(stdscr, buffers, jump_to_line, init_mode)
a.run()
MAX_ERROR_DISPLAY = 128
KILL_RING_LIMIT = 128
WORD_LETTERS = list(string.letters + string.digits)
ERROR_TIMEOUT = -1
#ERROR_TIMEOUT = 2
class Application(object):
def __init__(self, stdscr, buffers=[], jump_to_line=None, init_mode=None):
# initalize curses primitives
self.stdscr = stdscr
(self.y, self.x) = self.stdscr.getmaxyx()
self.y, self.x = self.stdscr.getmaxyx()
# initialize some basic stuff
# a highlighted_range contains three things: (window, start_p, end_p)
@ -51,7 +25,15 @@ class Application(object):
self.mini_prompt = ""
self.error_string = ""
self.error_timestamp = None
self.need_draw = True
self.input = keyinput.Handler()
# some constants
self.error_timeout = -1
self.max_error_len = 192
self.max_num_kills = 64
self.def_word_letters = string.letters + string.digits
# let's prepopulate some default token colors
self.token_colors = {
'comment': ('red', 'default', 'bold'),
@ -544,7 +526,8 @@ class Application(object):
self.kill_ring[-1] = self.kill_ring[-1] + s
else:
self.kill_ring.append(s)
if KILL_RING_LIMIT and len(self.kill_ring) > KILL_RING_LIMIT:
#if KILL_RING_LIMIT and len(self.kill_ring) > KILL_RING_LIMIT:
if self.max_num_kills and len(self.kill_ring) > self.max_num_kills:
self.kill_ring.pop(0)
def pop_kill(self):
return self.kill_ring.pop(-1)
@ -605,16 +588,22 @@ class Application(object):
while i == curses.KEY_RESIZE:
i = self.win.getch()
self.resize_event()
self.need_draw = True
err = ''
try:
self.input.parse(i)
except Exception, e:
err = str(e)
while len(self.input.tokens):
if self.input.tokens:
self.need_draw = True
while self.input.tokens:
t = self.input.tokens.pop(0)
self.active_window().mode.handle_token(t)
self.draw(err)
if self.need_draw:
self.draw(err)
self.need_draw = False
# clear the error line; it might look confusing to the user
try:
@ -667,8 +656,8 @@ class Application(object):
self.resize_event()
if err:
self.set_error(err)
if self.error_timestamp is not None and ERROR_TIMEOUT > 0 and \
time.time() - self.error_timestamp > ERROR_TIMEOUT:
if self.error_timestamp is not None and self.error_timeout > 0 and \
time.time() - self.error_timestamp > self.error_timeout:
self.clear_error()
(y, x) = self.stdscr.getmaxyx()
if y != self.y or x != self.x:
@ -861,10 +850,10 @@ class Application(object):
def get_minibuffer_lines(self):
lines, lines2 = [], []
if self.error_string:
if len(self.error_string) < MAX_ERROR_DISPLAY:
if len(self.error_string) < self.max_error_len:
s = self.error_string
else:
s = self.error_string[:MAX_ERROR_DISPLAY] + '...'
s = self.error_string[:self.max_error_len] + '...'
elif self.mini_buffer_is_open():
s = self.mini_prompt + self.mini_buffer.lines[0]
lines2 = self.mini_buffer.lines[1:]
@ -895,6 +884,11 @@ def open_plain_file(path, name=None, binary=False):
else:
raise Exception, "can't open %r; unsupported file type" % path
def run_app(stdscr, buffers, jump_to_line=None, init_mode=None):
curses.def_shell_mode()
a = Application(stdscr, buffers, jump_to_line, init_mode)
a.run()
if __name__ == "__main__":
ciphers = {
'none': open_plain_file,
@ -986,5 +980,17 @@ if __name__ == "__main__":
paths.add(path)
names.add(name)
# save terminal state so we can restore it when the program exits
attr = termios.tcgetattr(sys.stdin)
keyinput.disable_control_chars()
# ok, now run our app
run(buffers, opts.goto, opts.mode)
retval = 1
try:
retval = curses.wrapper(run_app, buffers, opts.goto, opts.mode)
except:
traceback.print_exc()
# restore terminal state
termios.tcsetattr(sys.stdin, termios.TCSANOW, attr)
sys.exit(retval)

View File

@ -1,4 +1,4 @@
import codecs, datetime, grp, md5, os, pwd, re, sets, shutil, stat, string
import codecs, datetime, grp, md5, os, pwd, re, shutil, stat, string
import fcntl, select, pty, threading
import aes, dirutil, regex, highlight, lex, term
from point import Point

View File

@ -10,9 +10,10 @@ class XTermBuffer(Buffer, XTerm):
modename = 'pipe'
termtype = 'xterm'
#termtype = 'vt100'
def __init__(self, cmd, args, name=None):
def __init__(self, app, cmd, args, name=None):
XTerm.__init__(self)
Buffer.__init__(self)
self.application = app
self._name = name or '*XTerm*'
self._pid, self._pty = pty.fork()
if self._pid == 0:
@ -102,6 +103,7 @@ class XTermBuffer(Buffer, XTerm):
if ifd:
data = os.read(ifd[0], 1024)
self.term_receive(data)
self.application.need_draw = True
if ofd:
self._lock.acquire()
n = os.write(ofd[0], self._towrite)

View File

@ -1,4 +1,4 @@
import glob, os, pwd, sets
import glob, os, pwd
import method, util
def find_common_string(candidates):
@ -78,7 +78,7 @@ class CommandCompleter(Completer):
def get_candidates(self, s, w=None):
path = os.getenv('PATH')
path_dirs = path.split(':')
candidates = sets.Set()
candidates = set()
for d in path_dirs:
if (not os.path.isdir(d) or not os.access(d, os.R_OK)):
continue

View File

@ -1,4 +1,4 @@
import os, commands, re, sets, tempfile
import os, commands, re, tempfile
from subprocess import Popen, PIPE, STDOUT
import buffer, default, dirutil, regex, util, window
@ -216,7 +216,9 @@ class KillRegion(Method):
class Copy(Method):
'''Copy the contents of the current line'''
def _execute(self, w, **vargs):
w.copy_line()
result = w.copy_line()
if result is None:
w.set_error("Empty kill region")
class CopyRegion(Method):
'''Copy the region between the mark and the cursor'''
def _execute(self, w, **vargs):
@ -1017,23 +1019,3 @@ class SetModeTabWidth(Method):
return
app.modes[mode].tabwidth = vargs['width']
w.set_error('Default tab width set to %d' % app.modes[mode].tabwidth)
#class HideRange(Method):
# def _execute(self, w, **vargs):
# cursor = w.logical_cursor()
# if cursor.y < w.mark.y:
# y1, y2 = cursor.y, w.mark.y
# elif w.mark.y < cursor.y:
# y1, y2 = w.mark.y, cursor.y
# else:
# w.set_error("No line range selected")
# w.hide(y1, y2)
# w.set_error("Lines %d through %d hidden" % (y1, y2))
#class UnhideLine(Method):
# def _execute(self, w, **vargs):
# cursor = w.logical_cursor()
# if w.ishidden(cursor.y):
# w.unhide(cursor.y)
# w.set_error("Line %d is no longer hidden" % cursor.y)
# else:
# w.set_error("Line %d is not hidden" % cursor.y)

View File

@ -1,4 +1,4 @@
import os, commands, re, sets, tempfile
import os, commands, re, tempfile
from subprocess import Popen, PIPE, STDOUT
import buffer, default, dirutil, regex, util, window

View File

@ -1,4 +1,4 @@
import os, commands, re, sets, tempfile
import os, commands, re, tempfile
from subprocess import Popen, PIPE, STDOUT
import buffer, default, dirutil, lex, regex, util, window

View File

@ -1,4 +1,4 @@
import os, commands, re, sets, tempfile
import os, commands, re, tempfile
from subprocess import Popen, PIPE, STDOUT
import buffer, buffer.about

View File

@ -1,4 +1,4 @@
import os, commands, re, sets, tempfile
import os, commands, re, tempfile
from subprocess import Popen, PIPE, STDOUT
import buffer, buffer.console

View File

@ -1,4 +1,4 @@
import os, commands, re, sets, tempfile
import os, commands, re, tempfile
from subprocess import Popen, PIPE, STDOUT
import buffer, default, dirutil, regex, util, window

View File

@ -1,4 +1,4 @@
import os, commands, re, sets, tempfile
import os, commands, re, tempfile
from subprocess import Popen, PIPE, STDOUT
import buffer, default, dirutil, regex, util, window

View File

@ -1,4 +1,4 @@
import os, commands, re, sets, tempfile
import os, commands, re, tempfile
from subprocess import Popen, PIPE, STDOUT
import buffer, default, dirutil, regex, term, util, window

View File

@ -1,4 +1,4 @@
import os, commands, re, sets, tempfile
import os, commands, re, tempfile
from subprocess import Popen, PIPE, STDOUT
import buffer, default, dirutil, lex, regex, util, window

View File

@ -1,4 +1,4 @@
import math, os, sets, string
import math, os, string
import color, method
from lex import Lexer
from point import Point
@ -75,19 +75,20 @@ class Handler(object):
class Fundamental(Handler):
'''This is the default mode'''
modename = "Fundamental"
paths = []
basenames = []
extensions = []
detection = []
savetabs = False
tabwidth = 4
tabbercls = None
grammar = None
lexer = None
tabber = None
context = None
colors = {}
modename = "Fundamental"
paths = []
basenames = []
extensions = []
detection = []
savetabs = False
tabwidth = 4
tabbercls = None
grammar = None
lexer = None
tabber = None
context = None
colors = {}
word_letters = None
# config settings installed/modified by the mode
config = {}
@ -124,7 +125,7 @@ class Fundamental(Handler):
for val2 in val:
app.config[key].append(val2)
for (key, val) in cls.sconfig.iteritems():
app.config.setdefault(key, sets.Set())
app.config.setdefault(key, set())
app.config[key].add(val)
for (key, d) in cls.dconfig.iteritems():
app.config.setdefault(key, {})
@ -231,6 +232,10 @@ class Fundamental(Handler):
self.add_bindings('insert-text2', ('C-c M-i',))
self.add_bindings('insert-multiline-text', ('C-c m',))
# used for all word operations
if not self.word_letters:
self.word_letters = w.application.def_word_letters
# create all the insert actions for the basic text input
for c in string.letters + string.digits + string.punctuation:
self.add_binding('insert-string-%s' % c, c)
@ -259,8 +264,6 @@ class Fundamental(Handler):
c, fg, bg = " ", "red", "default"
if cont:
c = "\\"
if w.hiddenindicator(y):
bg = "green"
return [RenderString(s=c, attrs=color.build(fg, bg))]
def get_lmargin(self, w, y, x, ended=False, cont=False):
lm = self.lmargin
@ -342,10 +345,10 @@ class Fundamental(Handler):
self.window.application.last_action = act.name
except ActionError, e:
#XYZ: HACK--should fix
if t not in ('C-]', 'C-g'):
self.window.set_error(str(e))
else:
if t in ('C-]', 'C-g'):
self.window.set_error('Cancelled')
else:
self.window.set_error(str(e))
except Exception, e:
if DEBUG:
raise

View File

@ -1,4 +1,4 @@
import re, sets, string
import re, string
import color, method, minibuffer, mode, searchutil
from point import Point

View File

@ -1,4 +1,4 @@
import commands, os.path, sets, string, sys, traceback
import commands, os.path, string, sys, traceback
import color, completer, default, mode, method, regex, tab
from point import Point
from lex import Grammar, PatternRule, RegionRule, OverridePatternRule

View File

@ -1,4 +1,4 @@
import commands, os.path, sets, string, sys, traceback
import commands, os.path, string, sys, traceback
import color, completer, default, mode, method, regex, tab
from point import Point
from lex import Grammar, PatternRule, RegionRule, OverridePatternRule, NocasePatternRule

View File

@ -1,4 +1,4 @@
import commands, os.path, sets, string, sys, traceback
import commands, os.path, string, sys, traceback
import color, completer, default, mode, method, regex, tab
from point import Point
from lex import Grammar, PatternRule, RegionRule, OverridePatternRule, NocasePatternRule

View File

@ -1,4 +1,4 @@
import commands, os.path, sets, string, sys, traceback
import commands, os.path, string, sys, traceback
import color, completer, default, mode, method, regex, tab
from point import Point
from lex import Grammar, PatternRule, RegionRule, OverridePatternRule

View File

@ -1,4 +1,4 @@
import commands, os.path, sets, string, sys, traceback
import commands, os.path, string, sys, traceback
import color, completer, default, mode, method, regex, tab
from point import Point
from lex import Grammar, PatternRule, RegionRule, NocasePatternRule

View File

@ -1,4 +1,4 @@
import os, re, sets, string, sys
import os, re, string, sys
import buffer, color, commands, completer, context, default, method, mode, regex, tab
from point import Point
from lex import Grammar, PatternRule, ContextPatternRule, RegionRule, \

View File

@ -1,4 +1,4 @@
import commands, os.path, sets, string, sys, traceback
import commands, os.path, string, sys, traceback
import color, completer, context, default, mode, method, regex, tab, method.introspect
from point import Point
from lex import Grammar, PatternRule, RegionRule, OverridePatternRule

View File

@ -1,4 +1,4 @@
import re, sets, string
import re, string
import color, method, minibuffer, mode, searchutil
from point import Point

View File

@ -1,4 +1,4 @@
import commands, os.path, sets, string, sys, traceback
import commands, os.path, string, sys, traceback
import color, completer, default, mode, method, regex, tab
from point import Point
from lex import Grammar, PatternRule, RegionRule, OverridePatternRule

View File

@ -1,4 +1,4 @@
import re, sets, string
import re, string
import color, method, minibuffer, mode, searchutil
from point import Point

View File

@ -123,7 +123,7 @@ class OpenShell(Method):
a = w.application
if not a.has_buffer_name('*Shell*'):
#b = buffer.pipe.PipeBuffer('/bin/bash', [], name="*Shell*", term='xterm')
b = buffer.emul.XTermBuffer('/bin/bash', [], name="*Shell*")
b = buffer.emul.XTermBuffer(a, '/bin/bash', [], name="*Shell*")
a.add_buffer(b)
window.Window(b, a)
b = a.bufferlist.get_buffer_by_name('*Shell*')

194
window.py
View File

@ -3,8 +3,6 @@ import color, highlight, regex
from point import Point
from render import RenderString
WORD_LETTERS = list(string.letters + string.digits)
# note about the cursor: the cursor position will insert in front of the
# character it highlights. to this end, it needs to be able to highlight behind
# the last character on a line. thus, the x coordinate of the (logical) cursor
@ -22,19 +20,16 @@ class Window(object):
margins = ((80, 'blue'),)
margins_visible = False
def __init__(self, b, a, height=24, width=80, mode_name=None):
self.buffer = b
self.application = a
self.height = height
self.width = width
self.first = Point(0, 0)
self.last = None
self.cursor = Point(0, 0)
self.mark = None
self.active_point = None
self.input_line = ""
self.hidden_ranges = {}
self.hidden_lines = {}
self.buffer = b
self.application = a
self.height = height
self.width = width
self.first = Point(0, 0)
self.last = None
self.cursor = Point(0, 0)
self.mark = None
self.active_point = None
self.input_line = ""
if mode_name is not None:
pass
@ -311,14 +306,14 @@ class Window(object):
x = len(self.buffer.lines[y])
else:
x -= 1
while (y, x) >= start and self.xy_char(x, y) not in WORD_LETTERS:
while (y, x) >= start and self.xy_char(x, y) not in self.mode.word_letters:
if x == 0:
y -= 1
x = len(self.buffer.lines[y])
else:
x -= 1
found_word = False
while (y, x) >= start and self.xy_char(x, y) in WORD_LETTERS:
while (y, x) >= start and self.xy_char(x, y) in self.mode.word_letters:
found_word = True
if x == 0:
y -= 1
@ -339,13 +334,13 @@ class Window(object):
else:
(x, y) = p.xy()
end = self.buffer.get_buffer_end()
while (y, x) < end and self.xy_char(x, y) not in WORD_LETTERS:
while (y, x) < end and self.xy_char(x, y) not in self.mode.word_letters:
if x == len(self.buffer.lines[y]):
x = 0
y += 1
else:
x += 1
while (y, x) < end and self.xy_char(x, y) in WORD_LETTERS:
while (y, x) < end and self.xy_char(x, y) in self.mode.word_letters:
if x == len(self.buffer.lines[y]):
x = 0
y += 1
@ -360,27 +355,27 @@ class Window(object):
p = self.find_right_word()
if p is not None:
self.goto(p)
def get_word_bounds_at_point(self, p, wl=WORD_LETTERS):
def get_word_bounds_at_point(self, p):
if len(self.buffer.lines[p.y]) == 0:
return None
elif self.cursor_char() not in wl:
return None
x1 = x2 = p.x
while x1 > 0 and self.xy_char(x1 - 1, p.y) in wl:
while x1 > 0 and self.xy_char(x1 - 1, p.y) in self.mode.word_letters:
x1 -= 1
while x2 < len(self.buffer.lines[p.y]) and self.xy_char(x2, p.y) in wl:
x2 += 1
return (Point(x1, p.y), Point(x2, p.y))
def get_word_at_point(self, p, wl=WORD_LETTERS):
bounds = self.get_word_bounds_at_point(p, wl)
def get_word_at_point(self, p):
bounds = self.get_word_bounds_at_point(p)
if bounds is None:
return None
else:
return self.buffer.get_substring(bounds[0], bounds[1])
def get_word_bounds(self, wl=WORD_LETTERS):
return self.get_word_bounds_at_point(self.logical_cursor(), wl)
def get_word(self, wl=WORD_LETTERS):
return self.get_word_at_point(self.logical_cursor(), wl)
def get_word_bounds(self):
return self.get_word_bounds_at_point(self.logical_cursor())
def get_word(self):
return self.get_word_at_point(self.logical_cursor())
# page up/down
def _pshift_up(self, p, num):
@ -496,69 +491,111 @@ class Window(object):
self.set_mark_point(self.logical_cursor())
self.goto(p)
# deletion
def left_delete(self):
self.buffer.left_delete(self.logical_cursor())
def right_delete(self):
self.buffer.right_delete(self.logical_cursor())
def delete(self, p1, p2):
self.buffer.delete(p1, p2)
# killing
def kill_line(self):
return self.copy_line(kill=True)
def kill_region(self):
return self.copy_region(kill=True)
def kill_left_word(self):
p1 = self.find_left_word()
p2 = self.logical_cursor()
if p1 == p2:
return
return self.kill(p1, p2)
def kill_right_word(self):
p1 = self.logical_cursor()
p2 = self.find_right_word()
if p1 == p2:
return
return self.kill(p1, p2)
def copy_line(self, kill=False):
# finding (used by deletion, killing, and copying)
def get_line(self):
(x, y) = self.logical_cursor().xy()
lines = self.buffer.lines
if y < len(lines) - 1:
return (Point(0, y), Point(0, y + 1))
elif y > 0:
return (Point(len(lines[y - 1]), y - 1), Point(len(lines[y]), y))
else:
return (None, None)
def get_line_left(self):
cursor = self.logical_cursor()
(x, y) = cursor.xy()
lines = self.buffer.lines
if (x > 0) and not regex.whitespace.match(lines[y][:x]):
return (Point(0, y), cursor)
elif y > 0:
return (Point(len(lines[y - 1]), y - 1), cursor)
else:
return (None, None)
def get_line_right(self):
cursor = self.logical_cursor()
(x, y) = cursor.xy()
lines = self.buffer.lines
if (x < len(lines[y]) and not regex.whitespace.match(lines[y][x:])):
limit = Point(len(lines[y]), y)
return (cursor, Point(len(lines[y]), y))
elif y < len(lines) - 1:
limit = Point(0, y + 1)
return (cursor, Point(0, y + 1))
else:
return
if kill:
return self.kill(cursor, limit)
else:
return self.copy(cursor, limit)
def copy_region(self, kill=False):
return (None, None)
def get_region(self):
cursor = self.logical_cursor()
p1, p2 = None, None
if cursor < self.mark:
p1 = cursor
p2 = self.mark
elif self.mark < cursor:
p1 = self.mark
p2 = cursor
return (p1, p2)
def get_left_word(self):
p1 = self.find_left_word()
p2 = self.logical_cursor()
if p1 == p2:
return (None, None)
else:
self.input_line = "Empty kill region"
return
if kill:
return self.kill(p1, p2)
return (p1, p2)
def get_right_word(self):
p1 = self.logical_cursor()
p2 = self.find_right_word()
if p1 == p2:
return (None, None)
else:
return self.copy(p1, p2)
return (p1, p2)
# deletion
def delete(self, p1, p2):
self.buffer.delete(p1, p2)
def delete_line(self):
(p1, p2) = self.get_line_right()
if p1 is not None: self.delete(p1, p2)
def delete_region(self):
(p1, p2) = self.get_region()
if p1 is not None: self.delete(p1, p2)
def delete_left_word(self):
(p1, p2) = self.get_left_word()
if p1 is not None: self.delete(p1, p2)
def delete_right_word(self):
(p1, p2) = self.get_right_word()
if p1 is not None: self.delete(p1, p2)
def left_delete(self):
self.buffer.left_delete(self.logical_cursor())
def right_delete(self):
self.buffer.right_delete(self.logical_cursor())
# killing
def kill(self, p1, p2):
killed = self.buffer.get_substring(p1, p2)
self.buffer.delete(p1, p2)
self.application.push_kill(killed)
return killed
def kill_line(self):
(p1, p2) = self.get_line_right()
if p1 is not None: self.kill(p1, p2)
def kill_region(self):
(p1, p2) = self.get_region()
if p1 is not None: self.kill(p1, p2)
def kill_left_word(self):
(p1, p2) = self.get_left_word()
if p1 is not None: self.kill(p1, p2)
def kill_right_word(self):
(p1, p2) = self.get_right_word()
if p1 is not None: self.kill(p1, p2)
# copying
def copy(self, p1, p2):
copied = self.buffer.get_substring(p1, p2)
self.application.push_kill(copied)
return copied
def copy_line(self):
(p1, p2) = self.get_line_right()
if p1 is not None: return self.copy(p1, p2)
def copy_region(self, kill=False):
(p1, p2) = self.get_region()
if p1 is not None: return self.copy(p1, p2)
# overwriting
def overwrite_char_at_cursor(self, c):
@ -610,31 +647,6 @@ class Window(object):
else:
return self.buffer.lines[y][x]
# hiding stuff
def hide(self, y1, y2):
if y2 == 0:
return
for (py1, py2) in self.hidden_ranges.itervalues():
if ((y1 >= py1 and y1 < py2) or
(y2 >= py1 and y2 < py2) or
(y1 <= py1 and y2 >= py2)):
return
for i in range(y1, y2):
self.hidden_lines[i] = y2
self.hidden_ranges[y2] = (y1, y2)
def ishidden(self, y):
return self.hidden_lines.setdefault(y, False)
def hiddenindicator(self, y):
return y in self.hidden_ranges
def unhide(self, y):
py2 = self.ishidden(y)
if not py2:
return
py1 = self.hidden_ranges[py2][0]
del self.hidden_ranges[py2]
for i in range(py1, py2):
self.hidden_lines[i] = False
# undo/redo
def undo(self):
p = self.buffer.undo()