Skip to content

Commit 14d5d1f

Browse files
deleting of lines super-paste feature
1 parent 38878ea commit 14d5d1f

File tree

4 files changed

+100
-26
lines changed

4 files changed

+100
-26
lines changed

bpython/curtsiesfrontend/repl.py

Lines changed: 79 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -461,8 +461,11 @@ def process_control_event(self, e):
461461
ctrl_char = compress_paste_event(e)
462462
if ctrl_char is not None:
463463
return self.process_event(ctrl_char)
464+
simple_events = just_simple_events(e.events)
465+
source = bad_empty_lines_removed(''.join(simple_events))
466+
464467
with self.in_paste_mode():
465-
for ee in e.events:
468+
for ee in source:
466469
if self.stdin.has_focus:
467470
self.stdin.process_event(ee)
468471
else:
@@ -712,7 +715,9 @@ def send_session_to_external_editor(self, filename=None):
712715
for line in self.getstdout().split('\n'))
713716
text = self.send_to_external_editor(for_editor)
714717
lines = text.split('\n')
715-
self.history = [line for line in lines if line[:4] != '### ']
718+
from_editor = [line for line in lines if line[:4] != '### ']
719+
source = bad_empty_lines_removed('\n'.join(from_editor))
720+
self.history = source.split('\n')
716721
self.reevaluate(insert_into_history=True)
717722
self.current_line = lines[-1][4:]
718723
self.cursor_offset = len(self.current_line)
@@ -822,7 +827,7 @@ def push(self, line, insert_into_history=True):
822827
code_to_run = '\n'.join(self.buffer)
823828

824829
logger.debug('running %r in interpreter', self.buffer)
825-
c, code_will_parse = self.buffer_finished_will_parse()
830+
c, code_will_parse = code_finished_will_parse('\n'.join(self.buffer))
826831
self.saved_predicted_parse_error = not code_will_parse
827832
if c:
828833
logger.debug('finished - buffer cleared')
@@ -834,21 +839,6 @@ def push(self, line, insert_into_history=True):
834839
self.coderunner.load_code(code_to_run)
835840
self.run_code_and_maybe_finish()
836841

837-
def buffer_finished_will_parse(self):
838-
"""Returns a tuple of whether the buffer could be complete and whether it will parse
839-
840-
True, True means code block is finished and no predicted parse error
841-
True, False means code block is finished because a parse error is predicted
842-
False, True means code block is unfinished
843-
False, False isn't possible - an predicted error makes code block done"""
844-
try:
845-
finished = bool(code.compile_command('\n'.join(self.buffer)))
846-
code_will_parse = True
847-
except (ValueError, SyntaxError, OverflowError):
848-
finished = True
849-
code_will_parse = False
850-
return finished, code_will_parse
851-
852842
def run_code_and_maybe_finish(self, for_code=None):
853843
r = self.coderunner.run_code(for_code=for_code)
854844
if r:
@@ -861,7 +851,6 @@ def run_code_and_maybe_finish(self, for_code=None):
861851
if err:
862852
indent = 0
863853

864-
865854
#TODO This should be printed ABOVE the error that just happened instead
866855
# or maybe just thrown away and not shown
867856
if self.current_stdouterr_line:
@@ -1418,6 +1407,77 @@ def compress_paste_event(paste_event):
14181407
else:
14191408
return None
14201409

1410+
def just_simple_events(event_list):
1411+
simple_events = []
1412+
for e in event_list:
1413+
if e in (u"<Ctrl-j>", u"<Ctrl-m>", u"<PADENTER>", u"\n", u"\r"): # '\n' necessary for pastes
1414+
simple_events.append(u'\n')
1415+
elif isinstance(e, events.Event):
1416+
pass # ignore events
1417+
elif e == '<SPACE>':
1418+
simple_events.append(' ')
1419+
else:
1420+
simple_events.append(e)
1421+
return simple_events
1422+
1423+
def code_finished_will_parse(s):
1424+
"""Returns a tuple of whether the buffer could be complete and whether it will parse
1425+
1426+
True, True means code block is finished and no predicted parse error
1427+
True, False means code block is finished because a parse error is predicted
1428+
False, True means code block is unfinished
1429+
False, False isn't possible - an predicted error makes code block done"""
1430+
try:
1431+
finished = bool(code.compile_command(s))
1432+
code_will_parse = True
1433+
except (ValueError, SyntaxError, OverflowError):
1434+
finished = True
1435+
code_will_parse = False
1436+
return finished, code_will_parse
1437+
1438+
def bad_empty_lines_removed(s):
1439+
"""Removes empty lines that would cause unfinished input to be evaluated"""
1440+
# If there's a syntax error followed by an empty line, remove the empty line
1441+
lines = s.split('\n')
1442+
#TODO this should be our interpreter object making this decision so it
1443+
# can be compiler directive (__future__ statement) -aware
1444+
#TODO specifically catch IndentationErrors instead of any syntax errors
1445+
1446+
current_block = []
1447+
complete_blocks = []
1448+
for i, line in enumerate(s.split('\n')):
1449+
current_block.append(line)
1450+
could_be_finished, valid = code_finished_will_parse('\n'.join(current_block))
1451+
if could_be_finished and valid:
1452+
complete_blocks.append(current_block)
1453+
current_block = []
1454+
continue
1455+
elif could_be_finished and not valid:
1456+
if complete_blocks:
1457+
complete_blocks[-1].extend(current_block)
1458+
current_block = complete_blocks.pop()
1459+
if len(current_block) < 2:
1460+
return s #TODO return partial result instead of giving up
1461+
last_line = current_block.pop(len(current_block) - 2)
1462+
assert not last_line, last_line
1463+
new_finished, new_valid = code_finished_will_parse('\n'.join(current_block))
1464+
if new_valid and new_finished:
1465+
complete_blocks.append(current_block)
1466+
current_block = []
1467+
elif new_valid:
1468+
continue
1469+
else:
1470+
return s #TODO return partial result instead of giving up
1471+
1472+
else:
1473+
return s #TODO return partial result instead of giving up
1474+
else:
1475+
continue
1476+
return '\n'.join(['\n'.join(block)
1477+
for block in complete_blocks + [current_block]
1478+
if block])
1479+
1480+
14211481
#TODO this needs some work to function again and be useful for embedding
14221482
def simple_repl():
14231483
refreshes = []

bpython/test/test_curtsies_painting.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# coding: utf8
2+
from __future__ import unicode_literals
23
import sys
34
import os
45
from contextlib import contextmanager

bpython/test/test_curtsies_repl.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,19 @@ class TestCurtsiesRepl(unittest.TestCase):
3333
def setUp(self):
3434
self.repl = create_repl()
3535

36-
def test_buffer_finished_will_parse(self):
36+
def test_code_finished_will_parse(self):
3737
self.repl.buffer = ['1 + 1']
38-
self.assertTrue(self.repl.buffer_finished_will_parse(), (True, True))
38+
self.assertTrue(curtsiesrepl.code_finished_will_parse('\n'.join(self.repl.buffer)), (True, True))
3939
self.repl.buffer = ['def foo(x):']
40-
self.assertTrue(self.repl.buffer_finished_will_parse(), (False, True))
40+
self.assertTrue(curtsiesrepl.code_finished_will_parse('\n'.join(self.repl.buffer)), (False, True))
4141
self.repl.buffer = ['def foo(x)']
42-
self.assertTrue(self.repl.buffer_finished_will_parse(), (True, False))
42+
self.assertTrue(curtsiesrepl.code_finished_will_parse('\n'.join(self.repl.buffer)), (True, False))
4343
self.repl.buffer = ['def foo(x):', 'return 1']
44-
self.assertTrue(self.repl.buffer_finished_will_parse(), (True, False))
44+
self.assertTrue(curtsiesrepl.code_finished_will_parse('\n'.join(self.repl.buffer)), (True, False))
4545
self.repl.buffer = ['def foo(x):', ' return 1']
46-
self.assertTrue(self.repl.buffer_finished_will_parse(), (True, True))
46+
self.assertTrue(curtsiesrepl.code_finished_will_parse('\n'.join(self.repl.buffer)), (True, True))
4747
self.repl.buffer = ['def foo(x):', ' return 1', '']
48-
self.assertTrue(self.repl.buffer_finished_will_parse(), (True, True))
48+
self.assertTrue(curtsiesrepl.code_finished_will_parse('\n'.join(self.repl.buffer)), (True, True))
4949

5050
def test_external_communication(self):
5151
self.assertEqual(type(self.repl.help_text()), type(b''))

bpython/test/test_interpreter.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import unittest
22

33
from bpython.curtsiesfrontend import interpreter
4+
from bpython.curtsiesfrontend.repl import bad_empty_lines_removed
45
from curtsies.fmtfuncs import *
56

67
class TestInterpreter(unittest.TestCase):
@@ -40,3 +41,15 @@ def g():
4041

4142
self.assertEquals(str(plain('').join(a)), str(expected))
4243
self.assertEquals(plain('').join(a), expected)
44+
45+
class TestPreprocessing(unittest.TestCase):
46+
def test_bad_empty_lines_removed(self):
47+
self.assertEqual(bad_empty_lines_removed("def foo():\n"
48+
" return 1\n"
49+
"\n"
50+
" pass\n"),
51+
"def foo():\n"
52+
" return 1\n"
53+
" pass\n")
54+
55+

0 commit comments

Comments
 (0)