Skip to content

Commit 0a8a1e3

Browse files
committed
Implement D, X, C and s commands
1 parent fa1a578 commit 0a8a1e3

File tree

3 files changed

+86
-1
lines changed

3 files changed

+86
-1
lines changed

Lib/_pyrepl/reader.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,13 @@ def make_default_commands() -> dict[CommandName, type[Command]]:
186186
(r"dd", "vi-delete-line"),
187187
(r"d0", "vi-delete-to-bol"),
188188
(r"d$", "vi-delete-to-eol"),
189+
(r"D", "vi-delete-to-eol"),
190+
(r"X", "vi-delete-char-before"),
189191

190192
# Change commands
191193
(r"cw", "vi-change-word"),
194+
(r"C", "vi-change-to-eol"),
195+
(r"s", "vi-substitute-char"),
192196

193197
# Replace commands
194198
(r"r", "vi-replace-char"),

Lib/_pyrepl/vi_commands.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,21 @@ def do(self) -> None:
137137

138138

139139
class vi_delete_to_eol(KillCommand):
140-
"""Delete from cursor to end of line (d$)."""
140+
"""Delete from cursor to end of line (d$ or D)."""
141141
def do(self) -> None:
142142
r = self.reader
143143
self.kill_range(r.pos, r.eol())
144144

145145

146+
class vi_delete_char_before(KillCommand):
147+
"""Delete character before cursor (X)."""
148+
def do(self) -> None:
149+
r = self.reader
150+
for _ in range(r.get_arg()):
151+
if r.pos > 0:
152+
self.kill_range(r.pos - 1, r.pos)
153+
154+
146155
# ============================================================================
147156
# Find Commands (f/F/t/T)
148157
# ============================================================================
@@ -345,6 +354,24 @@ def do(self) -> None:
345354
r.enter_insert_mode()
346355

347356

357+
class vi_change_to_eol(KillCommand):
358+
"""Change from cursor to end of line (C)."""
359+
def do(self) -> None:
360+
r = self.reader
361+
self.kill_range(r.pos, r.eol())
362+
r.enter_insert_mode()
363+
364+
365+
class vi_substitute_char(KillCommand):
366+
"""Delete character under cursor and enter insert mode (s)."""
367+
def do(self) -> None:
368+
r = self.reader
369+
for _ in range(r.get_arg()):
370+
if r.pos < len(r.buffer):
371+
self.kill_range(r.pos, r.pos + 1)
372+
r.enter_insert_mode()
373+
374+
348375
# ============================================================================
349376
# Replace Commands
350377
# ============================================================================

Lib/test/test_pyrepl/test_reader.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,60 @@ def test_undo_after_replace(self):
12521252
reader, _ = self._run_vi(events)
12531253
self.assertEqual(reader.get_unicode(), "hello")
12541254

1255+
def test_D_delete_to_eol(self):
1256+
events = itertools.chain(
1257+
code_to_events("hello world"),
1258+
[
1259+
Event(evt="key", data="\x1b", raw=bytearray(b"\x1b")), # ESC
1260+
Event(evt="key", data="0", raw=bytearray(b"0")), # BOL
1261+
Event(evt="key", data="w", raw=bytearray(b"w")), # forward word
1262+
Event(evt="key", data="D", raw=bytearray(b"D")), # delete to EOL
1263+
],
1264+
)
1265+
reader, _ = self._run_vi(events)
1266+
self.assertEqual(reader.get_unicode(), "hello ")
1267+
1268+
def test_C_change_to_eol(self):
1269+
events = itertools.chain(
1270+
code_to_events("hello world"),
1271+
[
1272+
Event(evt="key", data="\x1b", raw=bytearray(b"\x1b")), # ESC
1273+
Event(evt="key", data="0", raw=bytearray(b"0")), # BOL
1274+
Event(evt="key", data="w", raw=bytearray(b"w")), # forward word
1275+
Event(evt="key", data="C", raw=bytearray(b"C")), # change to EOL
1276+
],
1277+
code_to_events("there"),
1278+
)
1279+
reader, _ = self._run_vi(events)
1280+
self.assertEqual(reader.get_unicode(), "hello there")
1281+
self.assertEqual(reader.vi_mode, ViMode.INSERT)
1282+
1283+
def test_s_substitute_char(self):
1284+
events = itertools.chain(
1285+
code_to_events("hello"),
1286+
[
1287+
Event(evt="key", data="\x1b", raw=bytearray(b"\x1b")), # ESC
1288+
Event(evt="key", data="0", raw=bytearray(b"0")), # BOL
1289+
Event(evt="key", data="s", raw=bytearray(b"s")), # substitute
1290+
],
1291+
code_to_events("j"),
1292+
)
1293+
reader, _ = self._run_vi(events)
1294+
self.assertEqual(reader.get_unicode(), "jello")
1295+
self.assertEqual(reader.vi_mode, ViMode.INSERT)
1296+
1297+
def test_X_delete_char_before(self):
1298+
events = itertools.chain(
1299+
code_to_events("hello"),
1300+
[
1301+
Event(evt="key", data="\x1b", raw=bytearray(b"\x1b")), # ESC
1302+
Event(evt="key", data="X", raw=bytearray(b"X")), # delete before
1303+
],
1304+
)
1305+
reader, _ = self._run_vi(events)
1306+
self.assertEqual(reader.get_unicode(), "helo")
1307+
self.assertEqual(reader.vi_mode, ViMode.NORMAL)
1308+
12551309

12561310
@force_not_colorized_test_class
12571311
class TestHistoricalReaderBindings(TestCase):

0 commit comments

Comments
 (0)