Skip to content

Commit 7b78234

Browse files
committed
Merged in thomasballinger/bpython (pull request #29)
more bpython-curtsies code
2 parents b4bef26 + c816865 commit 7b78234

File tree

5 files changed

+99
-81
lines changed

5 files changed

+99
-81
lines changed

bpython/curtsies.py

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import absolute_import
22

33
import sys
4-
import os
4+
import code
55
from optparse import Option
66

77
import curtsies
@@ -20,22 +20,44 @@ def main(args=None, locals_=None, banner=None):
2020
config, options, exec_args = bpargs.parse(args, (
2121
'scroll options', None, [
2222
Option('--log', '-L', action='store_true',
23-
help=_("log debug messages to scroll.log")),
23+
help=_("log debug messages to bpython-curtsies.log")),
2424
Option('--type', '-t', action='store_true',
2525
help=_("enter lines of file as though interactively typed")),
2626
]))
2727
if options.log:
2828
import logging
2929
logging.basicConfig(filename='scroll.log', level=logging.DEBUG)
3030

31-
# do parsing before doing any frontend stuff
31+
interp = None
32+
paste = None
33+
if exec_args:
34+
assert options, "don't pass in exec_args without options"
35+
exit_value = 0
36+
if options.type:
37+
paste = curtsies.events.PasteEvent()
38+
sourcecode = open(exec_args[0]).read()
39+
paste.events.extend(sourcecode)
40+
else:
41+
try:
42+
interp = code.InteractiveInterpreter(locals=locals_)
43+
bpargs.exec_code(interp, exec_args)
44+
except SystemExit, e:
45+
exit_value = e.args
46+
if not options.interactive:
47+
raise SystemExit(exit_value)
48+
else:
49+
sys.path.insert(0, '') # expected for interactive sessions (python does it)
50+
51+
mainloop(config, locals_, banner, interp, paste)
52+
53+
def mainloop(config, locals_, banner, interp=None, paste=None):
3254
with Terminal(paste_mode=True) as tc:
3355
with Window(tc, keep_last_line=True, hide_cursor=False) as term:
34-
#TODO why need to make repl first
3556
with Repl(config=config,
3657
locals_=locals_,
3758
stuff_a_refresh_request=tc.stuff_a_refresh_request,
38-
banner=banner) as repl:
59+
banner=banner,
60+
interp=interp) as repl:
3961
rows, columns = tc.get_screen_size()
4062
repl.width = columns
4163
repl.height = rows
@@ -44,8 +66,7 @@ def process_event(e):
4466
try:
4567
repl.process_event(e)
4668
except SystemExitFromCodeThread:
47-
#Get rid of nasty constant
48-
array, cursor_pos = repl.paint(about_to_exit=2)
69+
array, cursor_pos = repl.paint(about_to_exit=True, user_quit=True)
4970
term.render_to_terminal(array, cursor_pos)
5071
raise
5172
except SystemExit:
@@ -59,34 +80,12 @@ def process_event(e):
5980
# Could this be calculated in the repl, avoiding this
6081
# two-way communication?
6182

62-
exit_value = 0
63-
if exec_args:
64-
assert options, "don't pass in exec_args without options"
65-
if options.type:
66-
repl.process_event(tc.get_event()) #first event will always be a window size set
67-
paste = curtsies.events.PasteEvent()
68-
old_argv, sys.argv = sys.argv, exec_args
69-
paste.events.extend(open(exec_args[0]).read())
70-
sys.path.insert(0, os.path.abspath(os.path.dirname(exec_args[0])))
71-
process_event(paste)
72-
else:
73-
try:
74-
# THIS IS NORMAL PYTHON
75-
#TODO replace this so that stdout is properly harvested for display and rewind works
76-
bpargs.exec_code(repl.interp, exec_args)
77-
except SystemExit, e:
78-
exit_value = e.args
79-
if not options.interactive:
80-
#TODO treat this properly: no prompt should ever display, but stdout should!
81-
array, cursor_pos = repl.paint(about_to_exit=True)
82-
term.render_to_terminal(array, cursor_pos)
83-
raise SystemExit(exit_value)
84-
else:
85-
sys.path.insert(0, '') # expected for interactive sessions (python does it)
83+
if paste:
84+
repl.process_event(tc.get_event()) #first event will always be a window size set
85+
process_event(paste)
8686

8787
while True:
8888
process_event(tc.get_event())
8989

90-
9190
if __name__ == '__main__':
9291
sys.exit(main())

bpython/curtsiesfrontend/coderunner.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ def __init__(self, coderunner, please):
115115
def write(self, *args, **kwargs):
116116
self.please(*args, **kwargs)
117117
return self.coderunner.refresh_and_get_value()
118+
def writelines(self, l):
119+
for s in l:
120+
self.write(s)
121+
def flush(self):
122+
pass
123+
def isatty(self):
124+
return True
118125

119126
def test_simple():
120127
orig_stdout = sys.stdout

bpython/curtsiesfrontend/manual_readline.py

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,52 +8,48 @@
88
import re
99
char_sequences = {}
1010

11-
#TODO fix this - should use value in repl.
12-
# Sadly, this breaks the pure function aspect of backspace!
1311
INDENT = 4
1412

15-
#TODO make an object out of this so instances can have keybindings via config
13+
#TODO Allow user config of keybindings for these actions
1614

1715
def on(seq):
1816
def add_to_char_sequences(func):
1917
char_sequences[seq] = func
2018
return func
2119
return add_to_char_sequences
2220

23-
@on('')
24-
@on('')
25-
@on(chr(2))
21+
@on('\x1b[D')
22+
@on('\x02')
2623
@on('KEY_LEFT')
2724
def left_arrow(cursor_offset, line):
2825
return max(0, cursor_offset - 1), line
2926

30-
@on('')
31-
@on('')
32-
@on(chr(6))
27+
@on('\x1b[C')
28+
@on('\x06')
3329
@on('KEY_RIGHT')
3430
def right_arrow(cursor_offset, line):
3531
return min(len(line), cursor_offset + 1), line
3632

37-
@on('')
33+
@on('\x01')
3834
@on('KEY_HOME')
3935
def beginning_of_line(cursor_offset, line):
4036
return 0, line
4137

42-
@on('')
38+
@on('\x05')
4339
@on('KEY_END')
4440
def end_of_line(cursor_offset, line):
4541
return len(line), line
4642

47-
@on('f')
48-
@on('l')
43+
@on('\x1bf')
44+
@on('\x1bl')
4945
@on('\x1bOC')
5046
def forward_word(cursor_offset, line):
5147
patt = r"\S\s"
5248
match = re.search(patt, line[cursor_offset:]+' ')
5349
delta = match.end() - 1 if match else 0
5450
return (cursor_offset + delta, line)
5551

56-
@on('b')
52+
@on('\x1bb')
5753
@on('\x1bOD')
5854
@on('\x1bB')
5955
def back_word(cursor_offset, line):
@@ -66,14 +62,14 @@ def last_word_pos(string):
6662
index = match and len(string) - match.end() + 1
6763
return index or 0
6864

69-
@on('[3~')
65+
@on('\x1b[3~')
7066
@on('KEY_DC')
7167
def delete(cursor_offset, line):
7268
return (cursor_offset,
7369
line[:cursor_offset] + line[cursor_offset+1:])
7470

75-
@on('')
76-
@on('')
71+
@on('\x08')
72+
@on('\x7f')
7773
@on('KEY_BACKSPACE')
7874
def backspace(cursor_offset, line):
7975
if cursor_offset == 0:
@@ -85,54 +81,54 @@ def backspace(cursor_offset, line):
8581
return (cursor_offset - 1,
8682
line[:cursor_offset - 1] + line[cursor_offset:])
8783

88-
@on('')
84+
@on('\x15')
8985
def delete_from_cursor_back(cursor_offset, line):
9086
return 0, line[cursor_offset:]
9187

92-
@on('')
88+
@on('\x0b')
9389
def delete_from_cursor_forward(cursor_offset, line):
9490
return cursor_offset, line[:cursor_offset]
9591

96-
@on('d') # option-d
92+
@on('\x1bd') # option-d
9793
def delete_rest_of_word(cursor_offset, line):
9894
m = re.search(r'\w\b', line[cursor_offset:])
9995
if not m:
10096
return cursor_offset, line
10197
return cursor_offset, line[:cursor_offset] + line[m.start()+cursor_offset+1:]
10298

103-
@on('')
99+
@on('\x17')
104100
def delete_word_to_cursor(cursor_offset, line):
105101
matches = list(re.finditer(r'\s\S', line[:cursor_offset]))
106102
start = matches[-1].start()+1 if matches else 0
107103
return start, line[:start] + line[cursor_offset:]
108104

109-
@on('y')
105+
@on('\x1by')
110106
def yank_prev_prev_killed_text(cursor_offset, line):
111107
raise NotImplementedError()
112108

113-
@on('')
109+
@on('\x14')
114110
def transpose_character_before_cursor(cursor_offset, line):
115111
return (min(len(line), cursor_offset + 1),
116112
line[:cursor_offset-1] +
117113
(line[cursor_offset] if len(line) > cursor_offset else '') +
118114
line[cursor_offset - 1] +
119115
line[cursor_offset+1:])
120116

121-
@on('t')
117+
@on('\x1bt')
122118
def transpose_word_before_cursor(cursor_offset, line):
123119
raise NotImplementedError()
124120

125121
# bonus functions (not part of readline)
126122

127-
@on('r')
123+
@on('\x1br')
128124
def delete_line(cursor_offset, line):
129125
return 0, ""
130126

131-
@on('u')
127+
@on('\x1bu')
132128
def uppercase_next_word(cursor_offset, line):
133129
raise NotImplementedError()
134130

135-
@on('c')
131+
@on('\x1bc')
136132
def titlecase_next_word(cursor_offset, line):
137133
raise NotImplementedError()
138134

0 commit comments

Comments
 (0)