mirror of
https://github.com/python/cpython.git
synced 2025-08-30 21:48:47 +00:00
gh-111201: auto-indentation in _pyrepl (#119348)
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
This commit is contained in:
parent
e9875ecb5d
commit
cd516cd1f5
2 changed files with 179 additions and 58 deletions
|
@ -5,19 +5,31 @@ import rlcompleter
|
|||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
|
||||
from .support import FakeConsole, handle_all_events, handle_events_narrow_console
|
||||
from .support import more_lines, multiline_input, code_to_events
|
||||
from .support import (
|
||||
FakeConsole,
|
||||
handle_all_events,
|
||||
handle_events_narrow_console,
|
||||
more_lines,
|
||||
multiline_input,
|
||||
code_to_events,
|
||||
)
|
||||
from _pyrepl.console import Event
|
||||
from _pyrepl.readline import ReadlineAlikeReader, ReadlineConfig
|
||||
from _pyrepl.readline import multiline_input as readline_multiline_input
|
||||
|
||||
|
||||
class TestCursorPosition(TestCase):
|
||||
def prepare_reader(self, events):
|
||||
console = FakeConsole(events)
|
||||
config = ReadlineConfig(readline_completer=None)
|
||||
reader = ReadlineAlikeReader(console=console, config=config)
|
||||
return reader
|
||||
|
||||
def test_up_arrow_simple(self):
|
||||
# fmt: off
|
||||
code = (
|
||||
'def f():\n'
|
||||
' ...\n'
|
||||
"def f():\n"
|
||||
" ...\n"
|
||||
)
|
||||
# fmt: on
|
||||
events = itertools.chain(
|
||||
|
@ -34,8 +46,8 @@ class TestCursorPosition(TestCase):
|
|||
def test_down_arrow_end_of_input(self):
|
||||
# fmt: off
|
||||
code = (
|
||||
'def f():\n'
|
||||
' ...\n'
|
||||
"def f():\n"
|
||||
" ...\n"
|
||||
)
|
||||
# fmt: on
|
||||
events = itertools.chain(
|
||||
|
@ -300,6 +312,79 @@ class TestCursorPosition(TestCase):
|
|||
self.assertEqual(reader.pos, 10)
|
||||
self.assertEqual(reader.cxy, (1, 1))
|
||||
|
||||
def test_auto_indent_default(self):
|
||||
# fmt: off
|
||||
input_code = (
|
||||
"def f():\n"
|
||||
"pass\n\n"
|
||||
)
|
||||
|
||||
output_code = (
|
||||
"def f():\n"
|
||||
" pass\n"
|
||||
" "
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
def test_auto_indent_continuation(self):
|
||||
# auto indenting according to previous user indentation
|
||||
# fmt: off
|
||||
events = itertools.chain(
|
||||
code_to_events("def f():\n"),
|
||||
# add backspace to delete default auto-indent
|
||||
[
|
||||
Event(evt="key", data="backspace", raw=bytearray(b"\x7f")),
|
||||
],
|
||||
code_to_events(
|
||||
" pass\n"
|
||||
"pass\n\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_prev_block(self):
|
||||
# auto indenting according to indentation in different block
|
||||
# fmt: off
|
||||
events = itertools.chain(
|
||||
code_to_events("def f():\n"),
|
||||
# add backspace to delete default auto-indent
|
||||
[
|
||||
Event(evt="key", data="backspace", raw=bytearray(b"\x7f")),
|
||||
],
|
||||
code_to_events(
|
||||
" pass\n"
|
||||
"pass\n\n"
|
||||
),
|
||||
code_to_events(
|
||||
"def g():\n"
|
||||
"pass\n\n"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
output_code = (
|
||||
"def g():\n"
|
||||
" pass\n"
|
||||
" "
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
reader = self.prepare_reader(events)
|
||||
output1 = multiline_input(reader)
|
||||
output2 = multiline_input(reader)
|
||||
self.assertEqual(output2, output_code)
|
||||
|
||||
|
||||
class TestPyReplOutput(TestCase):
|
||||
def prepare_reader(self, events):
|
||||
|
@ -316,14 +401,12 @@ class TestPyReplOutput(TestCase):
|
|||
|
||||
def test_multiline_edit(self):
|
||||
events = itertools.chain(
|
||||
code_to_events("def f():\n ...\n\n"),
|
||||
code_to_events("def f():\n...\n\n"),
|
||||
[
|
||||
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
|
||||
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
|
||||
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
|
||||
Event(evt="key", data="right", raw=bytearray(b"\x1bOC")),
|
||||
Event(evt="key", data="right", raw=bytearray(b"\x1bOC")),
|
||||
Event(evt="key", data="right", raw=bytearray(b"\x1bOC")),
|
||||
Event(evt="key", data="backspace", raw=bytearray(b"\x7f")),
|
||||
Event(evt="key", data="g", raw=bytearray(b"g")),
|
||||
Event(evt="key", data="down", raw=bytearray(b"\x1bOB")),
|
||||
|
@ -334,9 +417,9 @@ class TestPyReplOutput(TestCase):
|
|||
reader = self.prepare_reader(events)
|
||||
|
||||
output = multiline_input(reader)
|
||||
self.assertEqual(output, "def f():\n ...\n ")
|
||||
self.assertEqual(output, "def f():\n ...\n ")
|
||||
output = multiline_input(reader)
|
||||
self.assertEqual(output, "def g():\n ...\n ")
|
||||
self.assertEqual(output, "def g():\n ...\n ")
|
||||
|
||||
def test_history_navigation_with_up_arrow(self):
|
||||
events = itertools.chain(
|
||||
|
@ -485,6 +568,7 @@ class TestPyReplCompleter(TestCase):
|
|||
@property
|
||||
def test_func(self):
|
||||
import warnings
|
||||
|
||||
warnings.warn("warnings\n")
|
||||
return None
|
||||
|
||||
|
@ -508,12 +592,12 @@ class TestPasteEvent(TestCase):
|
|||
def test_paste(self):
|
||||
# fmt: off
|
||||
code = (
|
||||
'def a():\n'
|
||||
' for x in range(10):\n'
|
||||
' if x%2:\n'
|
||||
' print(x)\n'
|
||||
' else:\n'
|
||||
' pass\n'
|
||||
"def a():\n"
|
||||
" for x in range(10):\n"
|
||||
" if x%2:\n"
|
||||
" print(x)\n"
|
||||
" else:\n"
|
||||
" pass\n"
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
@ -534,10 +618,10 @@ class TestPasteEvent(TestCase):
|
|||
def test_paste_mid_newlines(self):
|
||||
# fmt: off
|
||||
code = (
|
||||
'def f():\n'
|
||||
' x = y\n'
|
||||
' \n'
|
||||
' y = z\n'
|
||||
"def f():\n"
|
||||
" x = y\n"
|
||||
" \n"
|
||||
" y = z\n"
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
@ -558,16 +642,16 @@ class TestPasteEvent(TestCase):
|
|||
def test_paste_mid_newlines_not_in_paste_mode(self):
|
||||
# fmt: off
|
||||
code = (
|
||||
'def f():\n'
|
||||
' x = y\n'
|
||||
' \n'
|
||||
' y = z\n\n'
|
||||
"def f():\n"
|
||||
"x = y\n"
|
||||
"\n"
|
||||
"y = z\n\n"
|
||||
)
|
||||
|
||||
expected = (
|
||||
'def f():\n'
|
||||
' x = y\n'
|
||||
' '
|
||||
"def f():\n"
|
||||
" x = y\n"
|
||||
" "
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
@ -579,20 +663,20 @@ class TestPasteEvent(TestCase):
|
|||
def test_paste_not_in_paste_mode(self):
|
||||
# fmt: off
|
||||
input_code = (
|
||||
'def a():\n'
|
||||
' for x in range(10):\n'
|
||||
' if x%2:\n'
|
||||
' print(x)\n'
|
||||
' else:\n'
|
||||
' pass\n\n'
|
||||
"def a():\n"
|
||||
"for x in range(10):\n"
|
||||
"if x%2:\n"
|
||||
"print(x)\n"
|
||||
"else:\n"
|
||||
"pass\n\n"
|
||||
)
|
||||
|
||||
output_code = (
|
||||
'def a():\n'
|
||||
' for x in range(10):\n'
|
||||
' if x%2:\n'
|
||||
' print(x)\n'
|
||||
' else:'
|
||||
"def a():\n"
|
||||
" for x in range(10):\n"
|
||||
" if x%2:\n"
|
||||
" print(x)\n"
|
||||
" else:"
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
@ -605,25 +689,25 @@ class TestPasteEvent(TestCase):
|
|||
"""Test that bracketed paste using \x1b[200~ and \x1b[201~ works."""
|
||||
# fmt: off
|
||||
input_code = (
|
||||
'def a():\n'
|
||||
' for x in range(10):\n'
|
||||
'\n'
|
||||
' if x%2:\n'
|
||||
' print(x)\n'
|
||||
'\n'
|
||||
' else:\n'
|
||||
' pass\n'
|
||||
"def a():\n"
|
||||
" for x in range(10):\n"
|
||||
"\n"
|
||||
" if x%2:\n"
|
||||
" print(x)\n"
|
||||
"\n"
|
||||
" else:\n"
|
||||
" pass\n"
|
||||
)
|
||||
|
||||
output_code = (
|
||||
'def a():\n'
|
||||
' for x in range(10):\n'
|
||||
'\n'
|
||||
' if x%2:\n'
|
||||
' print(x)\n'
|
||||
'\n'
|
||||
' else:\n'
|
||||
' pass\n'
|
||||
"def a():\n"
|
||||
" for x in range(10):\n"
|
||||
"\n"
|
||||
" if x%2:\n"
|
||||
" print(x)\n"
|
||||
"\n"
|
||||
" else:\n"
|
||||
" pass\n"
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue