Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfe49677

Browse files
indent instead of removing empty pasted lines
only indents, doesn't add lines for mutliple commands in one pasteonly works correctly for pastes beginning on an empty line
1 parent81d6044 commitfe49677

File tree

9 files changed

+179
-82
lines changed

9 files changed

+179
-82
lines changed

‎bpython/curtsiesfrontend/interpreter.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,19 @@ def format(self, tbtext, lexer):
153153
else:
154154
cur_line.append((token,text))
155155
assertcur_line== [],cur_line
156+
157+
158+
defcode_finished_will_parse(s,compiler):
159+
"""Returns a tuple of whether the buffer could be complete and whether it will parse
160+
161+
True, True means code block is finished and no predicted parse error
162+
True, False means code block is finished because a parse error is predicted
163+
False, True means code block is unfinished
164+
False, False isn't possible - an predicted error makes code block done"""
165+
try:
166+
finished=bool(compiler(s))
167+
code_will_parse=True
168+
except (ValueError,SyntaxError,OverflowError):
169+
finished=True
170+
code_will_parse=False
171+
returnfinished,code_will_parse
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""Tools for preparing code to be run in the REPL (removing blank lines, etc)"""
2+
importre
3+
4+
frombpython.curtsiesfrontend.interpreterimportcode_finished_will_parse
5+
6+
#TODO specifically catch IndentationErrors instead of any syntax errors
7+
8+
defindent_empty_lines(s,compiler):
9+
"""Indents blank lines that would otherwise cause early compilation
10+
11+
Only really works if starting on a new line"""
12+
lines=s.split('\n')
13+
ends_with_newline=False
14+
iflinesandnotlines[-1]:
15+
ends_with_newline=True
16+
lines.pop()
17+
result_lines= []
18+
19+
forp_line,line,n_lineinzip(['']+lines[:-1],lines,lines[1:]+ ['']):
20+
iflen(line)==0:
21+
p_indent=re.match(r'\s*',p_line).group()
22+
n_indent=re.match(r'\s*',n_line).group()
23+
result_lines.append(min([p_indent,n_indent],key=len)+line)
24+
else:
25+
result_lines.append(line)
26+
27+
return'\n'.join(result_lines)+ ('\n'ifends_with_newlineelse'')
28+

‎bpython/curtsiesfrontend/repl.py‎

Lines changed: 6 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
importcode
21
importcontextlib
32
importerrno
43
importfunctools
@@ -17,7 +16,6 @@
1716
frompygmentsimportformat
1817
frombpython._py3compatimportPythonLexer
1918
frompygments.formattersimportTerminalFormatter
20-
frominterpreterimportInterp
2119

2220
importblessings
2321

@@ -45,6 +43,8 @@
4543
frombpython.curtsiesfrontendimporteventsasbpythonevents
4644
frombpython.curtsiesfrontend.parseimportparseasbpythonparse
4745
frombpython.curtsiesfrontend.parseimportfunc_for_letter,color_for_letter
46+
frombpython.curtsiesfrontend.preprocessimportindent_empty_lines
47+
frombpython.curtsiesfrontend.interpreterimportInterp,code_finished_will_parse
4848

4949
#TODO other autocomplete modes (also fix in other bpython implementations)
5050

@@ -457,7 +457,7 @@ def process_control_event(self, e):
457457
ifctrl_charisnotNone:
458458
returnself.process_event(ctrl_char)
459459
simple_events=just_simple_events(e.events)
460-
source=bad_empty_lines_removed(''.join(simple_events))
460+
source=indent_empty_lines(''.join(simple_events),self.interp.compile)
461461

462462
withself.in_paste_mode():
463463
foreeinsource:
@@ -713,7 +713,7 @@ def send_session_to_external_editor(self, filename=None):
713713
text=self.send_to_external_editor(for_editor)
714714
lines=text.split('\n')
715715
from_editor= [lineforlineinlinesifline[:4]!='### ']
716-
source=bad_empty_lines_removed('\n'.join(from_editor))
716+
source=indent_empty_lines('\n'.join(from_editor),self.interp.compile)
717717
self.history=source.split('\n')
718718
self.reevaluate(insert_into_history=True)
719719
self.current_line=lines[-1][4:]
@@ -828,7 +828,8 @@ def push(self, line, insert_into_history=True):
828828
code_to_run='\n'.join(self.buffer)
829829

830830
logger.debug('running %r in interpreter',self.buffer)
831-
c,code_will_parse=code_finished_will_parse('\n'.join(self.buffer))
831+
c,code_will_parse=code_finished_will_parse('\n'.join(self.buffer),
832+
self.interp.compile)
832833
self.saved_predicted_parse_error=notcode_will_parse
833834
ifc:
834835
logger.debug('finished - buffer cleared')
@@ -1429,63 +1430,6 @@ def just_simple_events(event_list):
14291430
simple_events.append(e)
14301431
returnsimple_events
14311432

1432-
defcode_finished_will_parse(s):
1433-
"""Returns a tuple of whether the buffer could be complete and whether it will parse
1434-
1435-
True, True means code block is finished and no predicted parse error
1436-
True, False means code block is finished because a parse error is predicted
1437-
False, True means code block is unfinished
1438-
False, False isn't possible - an predicted error makes code block done"""
1439-
try:
1440-
finished=bool(code.compile_command(s))
1441-
code_will_parse=True
1442-
except (ValueError,SyntaxError,OverflowError):
1443-
finished=True
1444-
code_will_parse=False
1445-
returnfinished,code_will_parse
1446-
1447-
defbad_empty_lines_removed(s):
1448-
"""Removes empty lines that would cause unfinished input to be evaluated"""
1449-
# If there's a syntax error followed by an empty line, remove the empty line
1450-
lines=s.split('\n')
1451-
#TODO this should be our interpreter object making this decision so it
1452-
# can be compiler directive (__future__ statement) -aware
1453-
#TODO specifically catch IndentationErrors instead of any syntax errors
1454-
1455-
current_block= []
1456-
complete_blocks= []
1457-
fori,lineinenumerate(s.split('\n')):
1458-
current_block.append(line)
1459-
could_be_finished,valid=code_finished_will_parse('\n'.join(current_block))
1460-
ifcould_be_finishedandvalid:
1461-
complete_blocks.append(current_block)
1462-
current_block= []
1463-
continue
1464-
elifcould_be_finishedandnotvalid:
1465-
ifcomplete_blocks:
1466-
complete_blocks[-1].extend(current_block)
1467-
current_block=complete_blocks.pop()
1468-
iflen(current_block)<2:
1469-
returns#TODO return partial result instead of giving up
1470-
last_line=current_block.pop(len(current_block)-2)
1471-
assertnotlast_line,last_line
1472-
new_finished,new_valid=code_finished_will_parse('\n'.join(current_block))
1473-
ifnew_validandnew_finished:
1474-
complete_blocks.append(current_block)
1475-
current_block= []
1476-
elifnew_valid:
1477-
continue
1478-
else:
1479-
returns#TODO return partial result instead of giving up
1480-
1481-
else:
1482-
returns#TODO return partial result instead of giving up
1483-
else:
1484-
continue
1485-
return'\n'.join(['\n'.join(block)
1486-
forblockincomplete_blocks+ [current_block]
1487-
ifblock])
1488-
14891433

14901434
#TODO this needs some work to function again and be useful for embedding
14911435
defsimple_repl():

‎bpython/test/fodder/__init__.py‎

Whitespace-only changes.

‎bpython/test/fodder/original.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# careful: whitespace is very important in this file
2+
# also, this code runs - so everything should be a noop
3+
4+
classBlankLineBetweenMethods(object):
5+
defmethod1(self):
6+
pass
7+
8+
defmethod2(self):
9+
pass
10+
11+
defBlankLineInFunction(self):
12+
return7
13+
14+
pass
15+
16+
#StartTest-blank_lines_in_for_loop
17+
foriinrange(2):
18+
pass
19+
20+
pass
21+
#EndTest
22+

‎bpython/test/fodder/processed.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#careful! Whitespace is very important in this file
2+
3+
classBlankLineBetweenMethods(object):
4+
defmethod1(self):
5+
pass
6+
7+
defmethod2(self):
8+
pass
9+
10+
defBlankLineInFunction(self):
11+
return7
12+
13+
pass
14+
15+
#StartTest-blank_lines_in_for_loop
16+
foriinrange(2):
17+
pass
18+
19+
pass
20+
#EndTest

‎bpython/test/test_curtsies_repl.py‎

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# coding: utf8
22
importcode
3+
fromcontextlibimportcontextmanager
4+
fromfunctoolsimportpartial
35
importos
6+
fromStringIOimportStringIO
47
importsys
58
importtempfile
6-
fromcontextlibimportcontextmanager
7-
fromStringIOimportStringIO
89

910
importunittest
1011
try:
@@ -16,6 +17,7 @@ def skip(f):
1617
py3= (sys.version_info[0]==3)
1718

1819
frombpython.curtsiesfrontendimportreplascurtsiesrepl
20+
frombpython.curtsiesfrontendimportinterpreter
1921
frombpythonimportconfig
2022
frombpythonimportargs
2123

@@ -28,24 +30,28 @@ def setup_config(conf):
2830
setattr(config_struct,key,value)
2931
returnconfig_struct
3032

33+
3134
classTestCurtsiesRepl(unittest.TestCase):
3235

3336
defsetUp(self):
3437
self.repl=create_repl()
3538

39+
defcfwp(self,source):
40+
returninterpreter.code_finished_will_parse(source,self.repl.interp.compile)
41+
3642
deftest_code_finished_will_parse(self):
3743
self.repl.buffer= ['1 + 1']
38-
self.assertTrue(curtsiesrepl.code_finished_will_parse('\n'.join(self.repl.buffer)), (True,True))
44+
self.assertTrue(self.cfwp('\n'.join(self.repl.buffer)), (True,True))
3945
self.repl.buffer= ['def foo(x):']
40-
self.assertTrue(curtsiesrepl.code_finished_will_parse('\n'.join(self.repl.buffer)), (False,True))
46+
self.assertTrue(self.cfwp('\n'.join(self.repl.buffer)), (False,True))
4147
self.repl.buffer= ['def foo(x)']
42-
self.assertTrue(curtsiesrepl.code_finished_will_parse('\n'.join(self.repl.buffer)), (True,False))
48+
self.assertTrue(self.cfwp('\n'.join(self.repl.buffer)), (True,False))
4349
self.repl.buffer= ['def foo(x):','return 1']
44-
self.assertTrue(curtsiesrepl.code_finished_will_parse('\n'.join(self.repl.buffer)), (True,False))
50+
self.assertTrue(self.cfwp('\n'.join(self.repl.buffer)), (True,False))
4551
self.repl.buffer= ['def foo(x):',' return 1']
46-
self.assertTrue(curtsiesrepl.code_finished_will_parse('\n'.join(self.repl.buffer)), (True,True))
52+
self.assertTrue(self.cfwp('\n'.join(self.repl.buffer)), (True,True))
4753
self.repl.buffer= ['def foo(x):',' return 1','']
48-
self.assertTrue(curtsiesrepl.code_finished_will_parse('\n'.join(self.repl.buffer)), (True,True))
54+
self.assertTrue(self.cfwp('\n'.join(self.repl.buffer)), (True,True))
4955

5056
deftest_external_communication(self):
5157
self.assertEqual(type(self.repl.help_text()),type(b''))

‎bpython/test/test_interpreter.py‎

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

33
frombpython.curtsiesfrontendimportinterpreter
4-
frombpython.curtsiesfrontend.replimportbad_empty_lines_removed
54
fromcurtsies.fmtfuncsimport*
65

76
classTestInterpreter(unittest.TestCase):
@@ -42,14 +41,3 @@ def g():
4241
self.assertEquals(str(plain('').join(a)),str(expected))
4342
self.assertEquals(plain('').join(a),expected)
4443

45-
classTestPreprocessing(unittest.TestCase):
46-
deftest_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-

‎bpython/test/test_preprocess.py‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
fromcodeimportcompile_commandascompiler
2+
fromfunctoolsimportpartial
3+
importdifflib
4+
importinspect
5+
importre
6+
importunittest
7+
8+
frombpython.curtsiesfrontend.interpreterimportcode_finished_will_parse
9+
frombpython.curtsiesfrontend.preprocessimportindent_empty_lines
10+
11+
frombpython.test.fodderimportoriginalasoriginal,processed
12+
13+
indent_empty=partial(indent_empty_lines,compiler=compiler)
14+
15+
16+
defget_fodder_source(test_name):
17+
pattern=r'#StartTest-%s\n(.*?)#EndTest'% (test_name,)
18+
printrepr(pattern)
19+
orig,xformed= [re.search(pattern,inspect.getsource(module),re.DOTALL)
20+
formodulein [original,processed]]
21+
22+
ifnotorig:
23+
raiseValueError("Can't locate test %s in original fodder file"% (test_name,))
24+
ifnotxformed:
25+
raiseValueError("Can't locate test %s in processed fodder file"% (test_name,))
26+
returnorig.group(1),xformed.group(1)
27+
28+
29+
classTestPreprocessing(unittest.TestCase):
30+
31+
defassertCompiles(self,source):
32+
finished,parsable=code_finished_will_parse(source,compiler)
33+
returnfinishedandparsable
34+
35+
deftest_indent_empty_lines_nops(self):
36+
self.assertEqual(indent_empty('hello'),'hello')
37+
38+
defassertShowWhitespaceEqual(self,a,b):
39+
self.assertEqual(
40+
indent_empty(a),b,
41+
''.join(difflib.context_diff(a.replace(' ','~').splitlines(True),
42+
b.replace(' ','~').splitlines(True),
43+
fromfile='original',
44+
tofile='processed',
45+
n=5)))
46+
47+
defassertDefinitionIndented(self,obj):
48+
name=obj.__name__
49+
obj2=getattr(processed,name)
50+
orig=inspect.getsource(obj)
51+
xformed=inspect.getsource(obj2)
52+
self.assertShowWhitespaceEqual(indent_empty(orig),xformed)
53+
self.assertCompiles(xformed)
54+
55+
defassertLinesIndented(self,test_name):
56+
orig,xformed=get_fodder_source(test_name)
57+
self.assertShowWhitespaceEqual(indent_empty(orig),xformed)
58+
self.assertCompiles(xformed)
59+
60+
defassertIndented(self,obj_or_name):
61+
ifisinstance(obj_or_name,str):
62+
self.assertLinesIndented(obj_or_name)
63+
else:
64+
self.assertDefinitionIndented(obj_or_name)
65+
66+
deftest_empty_line_between_methods(self):
67+
self.assertIndented(original.BlankLineBetweenMethods)
68+
69+
deftest_empty_line_within_class(self):
70+
self.assertIndented(original.BlankLineInFunction)
71+
72+
deftest_blank_lines_in_for_loop(self):
73+
self.assertIndented('blank_lines_in_for_loop')

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2026 Movatter.jp