Skip to content

Commit 15d3861

Browse files
bpo-37903: IDLE: Shell sidebar with prompts (GH-22682)
The first followup will change shell indents to spaces. More are expected. Co-authored-by: Terry Jan Reedy <[email protected]>
1 parent 103d5e4 commit 15d3861

14 files changed

+887
-131
lines changed

Lib/idlelib/colorizer.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ def LoadTagDefs(self):
133133
# non-modal alternative.
134134
"hit": idleConf.GetHighlight(theme, "hit"),
135135
}
136-
137136
if DEBUG: print('tagdefs', self.tagdefs)
138137

139138
def insert(self, index, chars, tags=None):

Lib/idlelib/editor.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ class EditorWindow:
6060
from idlelib.sidebar import LineNumbers
6161
from idlelib.format import FormatParagraph, FormatRegion, Indents, Rstrip
6262
from idlelib.parenmatch import ParenMatch
63-
from idlelib.squeezer import Squeezer
6463
from idlelib.zoomheight import ZoomHeight
6564

6665
filesystemencoding = sys.getfilesystemencoding() # for file names
6766
help_url = None
6867

6968
allow_code_context = True
7069
allow_line_numbers = True
70+
user_input_insert_tags = None
7171

7272
def __init__(self, flist=None, filename=None, key=None, root=None):
7373
# Delay import: runscript imports pyshell imports EditorWindow.
@@ -784,9 +784,7 @@ def _addcolorizer(self):
784784
self.color = self.ColorDelegator()
785785
# can add more colorizers here...
786786
if self.color:
787-
self.per.removefilter(self.undo)
788-
self.per.insertfilter(self.color)
789-
self.per.insertfilter(self.undo)
787+
self.per.insertfilterafter(filter=self.color, after=self.undo)
790788

791789
def _rmcolorizer(self):
792790
if not self.color:
@@ -1303,8 +1301,6 @@ def smart_backspace_event(self, event):
13031301
# Debug prompt is multilined....
13041302
ncharsdeleted = 0
13051303
while 1:
1306-
if chars == self.prompt_last_line: # '' unless PyShell
1307-
break
13081304
chars = chars[:-1]
13091305
ncharsdeleted = ncharsdeleted + 1
13101306
have = len(chars.expandtabs(tabwidth))
@@ -1313,7 +1309,8 @@ def smart_backspace_event(self, event):
13131309
text.undo_block_start()
13141310
text.delete("insert-%dc" % ncharsdeleted, "insert")
13151311
if have < want:
1316-
text.insert("insert", ' ' * (want - have))
1312+
text.insert("insert", ' ' * (want - have),
1313+
self.user_input_insert_tags)
13171314
text.undo_block_stop()
13181315
return "break"
13191316

@@ -1346,7 +1343,7 @@ def smart_indent_event(self, event):
13461343
effective = len(prefix.expandtabs(self.tabwidth))
13471344
n = self.indentwidth
13481345
pad = ' ' * (n - effective % n)
1349-
text.insert("insert", pad)
1346+
text.insert("insert", pad, self.user_input_insert_tags)
13501347
text.see("insert")
13511348
return "break"
13521349
finally:
@@ -1377,13 +1374,14 @@ def newline_and_indent_event(self, event):
13771374
if i == n:
13781375
# The cursor is in or at leading indentation in a continuation
13791376
# line; just inject an empty line at the start.
1380-
text.insert("insert linestart", '\n')
1377+
text.insert("insert linestart", '\n',
1378+
self.user_input_insert_tags)
13811379
return "break"
13821380
indent = line[:i]
13831381

13841382
# Strip whitespace before insert point unless it's in the prompt.
13851383
i = 0
1386-
while line and line[-1] in " \t" and line != self.prompt_last_line:
1384+
while line and line[-1] in " \t":
13871385
line = line[:-1]
13881386
i += 1
13891387
if i:
@@ -1394,7 +1392,7 @@ def newline_and_indent_event(self, event):
13941392
text.delete("insert")
13951393

13961394
# Insert new line.
1397-
text.insert("insert", '\n')
1395+
text.insert("insert", '\n', self.user_input_insert_tags)
13981396

13991397
# Adjust indentation for continuations and block open/close.
14001398
# First need to find the last statement.
@@ -1430,7 +1428,7 @@ def newline_and_indent_event(self, event):
14301428
elif c == pyparse.C_STRING_NEXT_LINES:
14311429
# Inside a string which started before this line;
14321430
# just mimic the current indent.
1433-
text.insert("insert", indent)
1431+
text.insert("insert", indent, self.user_input_insert_tags)
14341432
elif c == pyparse.C_BRACKET:
14351433
# Line up with the first (if any) element of the
14361434
# last open bracket structure; else indent one
@@ -1444,7 +1442,8 @@ def newline_and_indent_event(self, event):
14441442
# beyond leftmost =; else to beyond first chunk of
14451443
# non-whitespace on initial line.
14461444
if y.get_num_lines_in_stmt() > 1:
1447-
text.insert("insert", indent)
1445+
text.insert("insert", indent,
1446+
self.user_input_insert_tags)
14481447
else:
14491448
self.reindent_to(y.compute_backslash_indent())
14501449
else:
@@ -1455,7 +1454,7 @@ def newline_and_indent_event(self, event):
14551454
# indentation of initial line of closest preceding
14561455
# interesting statement.
14571456
indent = y.get_base_indent_string()
1458-
text.insert("insert", indent)
1457+
text.insert("insert", indent, self.user_input_insert_tags)
14591458
if y.is_block_opener():
14601459
self.smart_indent_event(event)
14611460
elif indent and y.is_block_closer():
@@ -1502,7 +1501,8 @@ def reindent_to(self, column):
15021501
if text.compare("insert linestart", "!=", "insert"):
15031502
text.delete("insert linestart", "insert")
15041503
if column:
1505-
text.insert("insert", self._make_blanks(column))
1504+
text.insert("insert", self._make_blanks(column),
1505+
self.user_input_insert_tags)
15061506
text.undo_block_stop()
15071507

15081508
# Guess indentwidth from text content.

Lib/idlelib/history.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ def fetch(self, reverse):
7474
else:
7575
if self.text.get("iomark", "end-1c") != prefix:
7676
self.text.delete("iomark", "end-1c")
77-
self.text.insert("iomark", prefix)
77+
self.text.insert("iomark", prefix, "stdin")
7878
pointer = prefix = None
7979
break
8080
item = self.history[pointer]
8181
if item[:nprefix] == prefix and len(item) > nprefix:
8282
self.text.delete("iomark", "end-1c")
83-
self.text.insert("iomark", item)
83+
self.text.insert("iomark", item, "stdin")
8484
break
8585
self.text.see("insert")
8686
self.text.tag_remove("sel", "1.0", "end")

Lib/idlelib/idle_test/test_editor.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ def test_indent_and_newline_event(self):
167167
'2.end'),
168168
)
169169

170-
w.prompt_last_line = ''
171170
for test in tests:
172171
with self.subTest(label=test.label):
173172
insert(text, test.text)
@@ -182,13 +181,6 @@ def test_indent_and_newline_event(self):
182181
# Deletes selected text before adding new line.
183182
eq(get('1.0', 'end'), ' def f1(self, a,\n \n return a + b\n')
184183

185-
# Preserves the whitespace in shell prompt.
186-
w.prompt_last_line = '>>> '
187-
insert(text, '>>> \t\ta =')
188-
text.mark_set('insert', '1.5')
189-
nl(None)
190-
eq(get('1.0', 'end'), '>>> \na =\n')
191-
192184

193185
class RMenuTest(unittest.TestCase):
194186

Lib/idlelib/idle_test/test_pyshell.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,89 @@ def test_init(self):
6060
## self.assertIsInstance(ps, pyshell.PyShell)
6161

6262

63+
class PyShellRemoveLastNewlineAndSurroundingWhitespaceTest(unittest.TestCase):
64+
regexp = pyshell.PyShell._last_newline_re
65+
66+
def all_removed(self, text):
67+
self.assertEqual('', self.regexp.sub('', text))
68+
69+
def none_removed(self, text):
70+
self.assertEqual(text, self.regexp.sub('', text))
71+
72+
def check_result(self, text, expected):
73+
self.assertEqual(expected, self.regexp.sub('', text))
74+
75+
def test_empty(self):
76+
self.all_removed('')
77+
78+
def test_newline(self):
79+
self.all_removed('\n')
80+
81+
def test_whitespace_no_newline(self):
82+
self.all_removed(' ')
83+
self.all_removed(' ')
84+
self.all_removed(' ')
85+
self.all_removed(' ' * 20)
86+
self.all_removed('\t')
87+
self.all_removed('\t\t')
88+
self.all_removed('\t\t\t')
89+
self.all_removed('\t' * 20)
90+
self.all_removed('\t ')
91+
self.all_removed(' \t')
92+
self.all_removed(' \t \t ')
93+
self.all_removed('\t \t \t')
94+
95+
def test_newline_with_whitespace(self):
96+
self.all_removed(' \n')
97+
self.all_removed('\t\n')
98+
self.all_removed(' \t\n')
99+
self.all_removed('\t \n')
100+
self.all_removed('\n ')
101+
self.all_removed('\n\t')
102+
self.all_removed('\n \t')
103+
self.all_removed('\n\t ')
104+
self.all_removed(' \n ')
105+
self.all_removed('\t\n ')
106+
self.all_removed(' \n\t')
107+
self.all_removed('\t\n\t')
108+
self.all_removed('\t \t \t\n')
109+
self.all_removed(' \t \t \n')
110+
self.all_removed('\n\t \t \t')
111+
self.all_removed('\n \t \t ')
112+
113+
def test_multiple_newlines(self):
114+
self.check_result('\n\n', '\n')
115+
self.check_result('\n' * 5, '\n' * 4)
116+
self.check_result('\n' * 5 + '\t', '\n' * 4)
117+
self.check_result('\n' * 20, '\n' * 19)
118+
self.check_result('\n' * 20 + ' ', '\n' * 19)
119+
self.check_result(' \n \n ', ' \n')
120+
self.check_result(' \n\n ', ' \n')
121+
self.check_result(' \n\n', ' \n')
122+
self.check_result('\t\n\n', '\t\n')
123+
self.check_result('\n\n ', '\n')
124+
self.check_result('\n\n\t', '\n')
125+
self.check_result(' \n \n ', ' \n')
126+
self.check_result('\t\n\t\n\t', '\t\n')
127+
128+
def test_non_whitespace(self):
129+
self.none_removed('a')
130+
self.check_result('a\n', 'a')
131+
self.check_result('a\n ', 'a')
132+
self.check_result('a \n ', 'a')
133+
self.check_result('a \n\t', 'a')
134+
self.none_removed('-')
135+
self.check_result('-\n', '-')
136+
self.none_removed('.')
137+
self.check_result('.\n', '.')
138+
139+
def test_unsupported_whitespace(self):
140+
self.none_removed('\v')
141+
self.none_removed('\n\v')
142+
self.check_result('\v\n', '\v')
143+
self.none_removed(' \n\v')
144+
self.check_result('\v\n ', '\v')
145+
146+
63147
if __name__ == '__main__':
64148
unittest.main(verbosity=2)

0 commit comments

Comments
 (0)