mirror of https://github.com/python/cpython
gh-111201: Improve pyrepl auto indentation (#119606)
- auto-indent when editing multi-line block - ignore comments
This commit is contained in:
parent
94e9585e99
commit
dae0375bd9
|
@ -237,13 +237,24 @@ def _get_first_indentation(buffer: list[str]) -> str | None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _is_last_char_colon(buffer: list[str]) -> bool:
|
def _should_auto_indent(buffer: list[str], pos: int) -> bool:
|
||||||
i = len(buffer)
|
# check if last character before "pos" is a colon, ignoring
|
||||||
while i > 0:
|
# whitespaces and comments.
|
||||||
i -= 1
|
last_char = None
|
||||||
if buffer[i] not in " \t\n": # ignore whitespaces
|
while pos > 0:
|
||||||
return buffer[i] == ":"
|
pos -= 1
|
||||||
return False
|
if last_char is None:
|
||||||
|
if buffer[pos] not in " \t\n": # ignore whitespaces
|
||||||
|
last_char = buffer[pos]
|
||||||
|
else:
|
||||||
|
# even if we found a non-whitespace character before
|
||||||
|
# original pos, we keep going back until newline is reached
|
||||||
|
# to make sure we ignore comments
|
||||||
|
if buffer[pos] == "\n":
|
||||||
|
break
|
||||||
|
if buffer[pos] == "#":
|
||||||
|
last_char = None
|
||||||
|
return last_char == ":"
|
||||||
|
|
||||||
|
|
||||||
class maybe_accept(commands.Command):
|
class maybe_accept(commands.Command):
|
||||||
|
@ -280,7 +291,7 @@ class maybe_accept(commands.Command):
|
||||||
for i in range(prevlinestart, prevlinestart + indent):
|
for i in range(prevlinestart, prevlinestart + indent):
|
||||||
r.insert(r.buffer[i])
|
r.insert(r.buffer[i])
|
||||||
r.update_last_used_indentation()
|
r.update_last_used_indentation()
|
||||||
if _is_last_char_colon(r.buffer):
|
if _should_auto_indent(r.buffer, r.pos):
|
||||||
if r.last_used_indentation is not None:
|
if r.last_used_indentation is not None:
|
||||||
indentation = r.last_used_indentation
|
indentation = r.last_used_indentation
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -312,6 +312,14 @@ class TestCursorPosition(TestCase):
|
||||||
self.assertEqual(reader.pos, 10)
|
self.assertEqual(reader.pos, 10)
|
||||||
self.assertEqual(reader.cxy, (1, 1))
|
self.assertEqual(reader.cxy, (1, 1))
|
||||||
|
|
||||||
|
|
||||||
|
class TestPyReplAutoindent(TestCase):
|
||||||
|
def prepare_reader(self, events):
|
||||||
|
console = FakeConsole(events)
|
||||||
|
config = ReadlineConfig(readline_completer=None)
|
||||||
|
reader = ReadlineAlikeReader(console=console, config=config)
|
||||||
|
return reader
|
||||||
|
|
||||||
def test_auto_indent_default(self):
|
def test_auto_indent_default(self):
|
||||||
# fmt: off
|
# fmt: off
|
||||||
input_code = (
|
input_code = (
|
||||||
|
@ -372,7 +380,6 @@ class TestCursorPosition(TestCase):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
output_code = (
|
output_code = (
|
||||||
"def g():\n"
|
"def g():\n"
|
||||||
" pass\n"
|
" pass\n"
|
||||||
|
@ -385,6 +392,78 @@ class TestCursorPosition(TestCase):
|
||||||
output2 = multiline_input(reader)
|
output2 = multiline_input(reader)
|
||||||
self.assertEqual(output2, output_code)
|
self.assertEqual(output2, output_code)
|
||||||
|
|
||||||
|
def test_auto_indent_multiline(self):
|
||||||
|
# fmt: off
|
||||||
|
events = itertools.chain(
|
||||||
|
code_to_events(
|
||||||
|
"def f():\n"
|
||||||
|
"pass"
|
||||||
|
),
|
||||||
|
[
|
||||||
|
# go to the end of the first line
|
||||||
|
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
|
||||||
|
Event(evt="key", data="\x05", raw=bytearray(b"\x1bO5")),
|
||||||
|
# new line should be autoindented
|
||||||
|
Event(evt="key", data="\n", raw=bytearray(b"\n")),
|
||||||
|
],
|
||||||
|
code_to_events(
|
||||||
|
"pass"
|
||||||
|
),
|
||||||
|
[
|
||||||
|
# go to end of last line
|
||||||
|
Event(evt="key", data="down", raw=bytearray(b"\x1bOB")),
|
||||||
|
Event(evt="key", data="\x05", raw=bytearray(b"\x1bO5")),
|
||||||
|
# double newline to terminate the block
|
||||||
|
Event(evt="key", data="\n", raw=bytearray(b"\n")),
|
||||||
|
Event(evt="key", data="\n", raw=bytearray(b"\n")),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
output_code = (
|
||||||
|
"def f():\n"
|
||||||
|
" pass\n"
|
||||||
|
" pass\n"
|
||||||
|
" "
|
||||||
|
)
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
reader = self.prepare_reader(events)
|
||||||
|
output = multiline_input(reader)
|
||||||
|
self.assertEqual(output, output_code)
|
||||||
|
|
||||||
|
def test_auto_indent_with_comment(self):
|
||||||
|
# fmt: off
|
||||||
|
events = code_to_events(
|
||||||
|
"def f(): # foo\n"
|
||||||
|
"pass\n\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
output_code = (
|
||||||
|
"def f(): # foo\n"
|
||||||
|
" pass\n"
|
||||||
|
" "
|
||||||
|
)
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
reader = self.prepare_reader(events)
|
||||||
|
output = multiline_input(reader)
|
||||||
|
self.assertEqual(output, output_code)
|
||||||
|
|
||||||
|
def test_auto_indent_ignore_comments(self):
|
||||||
|
# fmt: off
|
||||||
|
events = code_to_events(
|
||||||
|
"pass #:\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
output_code = (
|
||||||
|
"pass #:"
|
||||||
|
)
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
reader = self.prepare_reader(events)
|
||||||
|
output = multiline_input(reader)
|
||||||
|
self.assertEqual(output, output_code)
|
||||||
|
|
||||||
|
|
||||||
class TestPyReplOutput(TestCase):
|
class TestPyReplOutput(TestCase):
|
||||||
def prepare_reader(self, events):
|
def prepare_reader(self, events):
|
||||||
|
|
Loading…
Reference in New Issue