Skip to content

Commit 12197cb

Browse files
committed
Move most of the vi-specific logic out of Reader
1 parent 86defc0 commit 12197cb

File tree

3 files changed

+211
-201
lines changed

3 files changed

+211
-201
lines changed

Lib/_pyrepl/reader.py

Lines changed: 0 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,6 @@ def make_default_syntax_table() -> dict[str, int]:
5858
return st
5959

6060

61-
def _is_vi_word_char(c: str) -> bool:
62-
return c.isalnum() or c == '_'
63-
64-
6561
def make_default_commands() -> dict[CommandName, type[Command]]:
6662
result: dict[CommandName, type[Command]] = {}
6763
all_commands = itertools.chain(vars(commands).values(), vars(vi_commands).values())
@@ -543,190 +539,6 @@ def eow(self, p: int | None = None) -> int:
543539
p += 1
544540
return p
545541

546-
def vi_eow(self, p: int | None = None) -> int:
547-
"""Return the 0-based index of the last character of the word
548-
following p most immediately (vi 'e' semantics).
549-
550-
Vi has three character classes: word chars (alnum + _), punctuation
551-
(non-word, non-whitespace), and whitespace. 'e' moves to the end
552-
of the current or next word/punctuation sequence."""
553-
if p is None:
554-
p = self.pos
555-
b = self.buffer
556-
557-
if not b:
558-
return 0
559-
560-
# Helper to check if at end of current sequence
561-
def at_sequence_end(pos: int) -> bool:
562-
if pos >= len(b) - 1:
563-
return True
564-
curr_is_word = _is_vi_word_char(b[pos])
565-
next_is_word = _is_vi_word_char(b[pos + 1])
566-
curr_is_space = b[pos].isspace()
567-
next_is_space = b[pos + 1].isspace()
568-
if curr_is_word:
569-
return not next_is_word
570-
elif not curr_is_space:
571-
# Punctuation - at end if next is word or whitespace
572-
return next_is_word or next_is_space
573-
return True
574-
575-
# If already at end of a word/punctuation, move forward
576-
if p < len(b) and at_sequence_end(p):
577-
p += 1
578-
579-
# Skip whitespace
580-
while p < len(b) and b[p].isspace():
581-
p += 1
582-
583-
if p >= len(b):
584-
return len(b) - 1
585-
586-
# Move to end of current word or punctuation sequence
587-
if _is_vi_word_char(b[p]):
588-
while p + 1 < len(b) and _is_vi_word_char(b[p + 1]):
589-
p += 1
590-
else:
591-
# Punctuation sequence
592-
while p + 1 < len(b) and not _is_vi_word_char(b[p + 1]) and not b[p + 1].isspace():
593-
p += 1
594-
595-
return min(p, len(b) - 1)
596-
597-
def vi_forward_word(self, p: int | None = None) -> int:
598-
"""Return the 0-based index of the first character of the next word
599-
(vi 'w' semantics).
600-
601-
Vi has three character classes: word chars (alnum + _), punctuation
602-
(non-word, non-whitespace), and whitespace. 'w' moves to the start
603-
of the next word or punctuation sequence."""
604-
if p is None:
605-
p = self.pos
606-
b = self.buffer
607-
608-
if not b or p >= len(b):
609-
return max(0, len(b) - 1) if b else 0
610-
611-
# Skip current word or punctuation sequence
612-
if _is_vi_word_char(b[p]):
613-
# On a word char - skip word chars
614-
while p < len(b) and _is_vi_word_char(b[p]):
615-
p += 1
616-
elif not b[p].isspace():
617-
# On punctuation - skip punctuation
618-
while p < len(b) and not _is_vi_word_char(b[p]) and not b[p].isspace():
619-
p += 1
620-
621-
# Skip whitespace to find next word or punctuation
622-
while p < len(b) and b[p].isspace():
623-
p += 1
624-
625-
# Clamp to valid buffer range
626-
return min(p, len(b) - 1) if b else 0
627-
628-
def vi_forward_word_ws(self, p: int | None = None) -> int:
629-
"""Return the 0-based index of the first character of the next WORD
630-
(vi 'W' semantics).
631-
632-
Treats white space as the only separator."""
633-
if p is None:
634-
p = self.pos
635-
b = self.buffer
636-
637-
if not b or p >= len(b):
638-
return max(0, len(b) - 1) if b else 0
639-
640-
# Skip all non-whitespace (the current WORD)
641-
while p < len(b) and not b[p].isspace():
642-
p += 1
643-
644-
# Skip whitespace to find next WORD
645-
while p < len(b) and b[p].isspace():
646-
p += 1
647-
648-
# Clamp to valid buffer range
649-
return min(p, len(b) - 1) if b else 0
650-
651-
def vi_bow(self, p: int | None = None) -> int:
652-
"""Return the 0-based index of the beginning of the word preceding p
653-
(vi 'b' semantics).
654-
655-
Vi has three character classes: word chars (alnum + _), punctuation
656-
(non-word, non-whitespace), and whitespace. 'b' moves to the start
657-
of the current or previous word/punctuation sequence."""
658-
if p is None:
659-
p = self.pos
660-
b = self.buffer
661-
662-
if not b or p <= 0:
663-
return 0
664-
665-
p -= 1
666-
667-
# Skip whitespace going backward
668-
while p >= 0 and b[p].isspace():
669-
p -= 1
670-
671-
if p < 0:
672-
return 0
673-
674-
# Now skip the word or punctuation sequence we landed in
675-
if _is_vi_word_char(b[p]):
676-
while p > 0 and _is_vi_word_char(b[p - 1]):
677-
p -= 1
678-
else:
679-
# Punctuation sequence
680-
while p > 0 and not _is_vi_word_char(b[p - 1]) and not b[p - 1].isspace():
681-
p -= 1
682-
683-
return p
684-
685-
def vi_bow_ws(self, p: int | None = None) -> int:
686-
"""Return the 0-based index of the beginning of the WORD preceding p
687-
(vi 'B' semantics).
688-
689-
Treats white space as the only separator."""
690-
if p is None:
691-
p = self.pos
692-
b = self.buffer
693-
694-
if not b or p <= 0:
695-
return 0
696-
697-
p -= 1
698-
699-
# Skip whitespace going backward
700-
while p >= 0 and b[p].isspace():
701-
p -= 1
702-
703-
if p < 0:
704-
return 0
705-
706-
# Now skip the WORD we landed in
707-
while p > 0 and not b[p - 1].isspace():
708-
p -= 1
709-
710-
return p
711-
712-
def find_char_forward(self, char: str, p: int | None = None) -> int | None:
713-
"""Find next occurrence of char after p. Returns index or None."""
714-
if p is None:
715-
p = self.pos
716-
for i in range(p + 1, len(self.buffer)):
717-
if self.buffer[i] == char:
718-
return i
719-
return None
720-
721-
def find_char_backward(self, char: str, p: int | None = None) -> int | None:
722-
"""Find previous occurrence of char before p. Returns index or None."""
723-
if p is None:
724-
p = self.pos
725-
for i in range(p - 1, -1, -1):
726-
if self.buffer[i] == char:
727-
return i
728-
return None
729-
730542
def bol(self, p: int | None = None) -> int:
731543
"""Return the 0-based index of the line break preceding p most
732544
immediately.

Lib/_pyrepl/vi_commands.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@
66
from . import input as _input
77
from .types import ViFindDirection
88
from .trace import trace
9+
from .vi_motions import (
10+
vi_eow,
11+
vi_forward_word as _vi_forward_word,
12+
vi_forward_word_ws as _vi_forward_word_ws,
13+
vi_bow,
14+
vi_bow_ws,
15+
find_char_forward,
16+
find_char_backward,
17+
)
918

1019

1120
class ViKillCommand(KillCommand):
@@ -26,33 +35,33 @@ class end_of_word(MotionCommand):
2635
def do(self) -> None:
2736
r = self.reader
2837
for _ in range(r.get_arg()):
29-
r.pos = r.vi_eow()
38+
r.pos = vi_eow(r.buffer, r.pos)
3039

3140

3241
class vi_forward_word(MotionCommand):
3342
def do(self) -> None:
3443
r = self.reader
3544
for _ in range(r.get_arg()):
36-
r.pos = r.vi_forward_word()
45+
r.pos = _vi_forward_word(r.buffer, r.pos)
3746

3847
class vi_forward_word_ws(MotionCommand):
3948
def do(self) -> None:
4049
r = self.reader
4150
for _ in range(r.get_arg()):
42-
r.pos = r.vi_forward_word_ws()
51+
r.pos = _vi_forward_word_ws(r.buffer, r.pos)
4352

4453
class vi_backward_word(MotionCommand):
4554
def do(self) -> None:
4655
r = self.reader
4756
for _ in range(r.get_arg()):
48-
r.pos = r.vi_bow()
57+
r.pos = vi_bow(r.buffer, r.pos)
4958

5059

5160
class vi_backward_word_ws(MotionCommand):
5261
def do(self) -> None:
5362
r = self.reader
5463
for _ in range(r.get_arg()):
55-
r.pos = r.vi_bow_ws()
64+
r.pos = vi_bow_ws(r.buffer, r.pos)
5665

5766

5867

@@ -127,7 +136,7 @@ class vi_delete_word(ViKillCommand):
127136
def do(self) -> None:
128137
r = self.reader
129138
for _ in range(r.get_arg()):
130-
end = r.vi_forward_word()
139+
end = _vi_forward_word(r.buffer, r.pos)
131140
if end > r.pos:
132141
self.kill_range(r.pos, end)
133142

@@ -264,14 +273,14 @@ def _execute_find(self, char: str, direction: ViFindDirection | None, inclusive:
264273
r = self.reader
265274
for _ in range(r.get_arg()):
266275
if direction == ViFindDirection.FORWARD:
267-
new_pos = r.find_char_forward(char)
276+
new_pos = find_char_forward(r.buffer, r.pos, char)
268277
if new_pos is not None:
269278
if not inclusive:
270279
new_pos -= 1
271280
if new_pos > r.pos:
272281
r.pos = new_pos
273282
else:
274-
new_pos = r.find_char_backward(char)
283+
new_pos = find_char_backward(r.buffer, r.pos, char)
275284
if new_pos is not None:
276285
if not inclusive:
277286
new_pos += 1
@@ -304,14 +313,14 @@ def do(self) -> None:
304313

305314
for _ in range(r.get_arg()):
306315
if direction == ViFindDirection.FORWARD:
307-
new_pos = r.find_char_forward(char)
316+
new_pos = find_char_forward(r.buffer, r.pos, char)
308317
if new_pos is not None:
309318
if not inclusive:
310319
new_pos -= 1
311320
if new_pos > r.pos:
312321
r.pos = new_pos
313322
else:
314-
new_pos = r.find_char_backward(char)
323+
new_pos = find_char_backward(r.buffer, r.pos, char)
315324
if new_pos is not None:
316325
if not inclusive:
317326
new_pos += 1
@@ -334,14 +343,14 @@ def do(self) -> None:
334343

335344
for _ in range(r.get_arg()):
336345
if direction == ViFindDirection.FORWARD:
337-
new_pos = r.find_char_forward(char)
346+
new_pos = find_char_forward(r.buffer, r.pos, char)
338347
if new_pos is not None:
339348
if not inclusive:
340349
new_pos -= 1
341350
if new_pos > r.pos:
342351
r.pos = new_pos
343352
else:
344-
new_pos = r.find_char_backward(char)
353+
new_pos = find_char_backward(r.buffer, r.pos, char)
345354
if new_pos is not None:
346355
if not inclusive:
347356
new_pos += 1
@@ -358,7 +367,7 @@ class vi_change_word(ViKillCommand):
358367
def do(self) -> None:
359368
r = self.reader
360369
for _ in range(r.get_arg()):
361-
end = r.vi_eow() + 1 # +1 to include last char
370+
end = vi_eow(r.buffer, r.pos) + 1 # +1 to include last char
362371
if end > r.pos:
363372
self.kill_range(r.pos, end)
364373
r.enter_insert_mode()

0 commit comments

Comments
 (0)