Skip to content

Commit 346ec6f

Browse files
author
Jonathan Plasse
committed
Fix replace for partial selection
1 parent 88a7914 commit 346ec6f

File tree

1 file changed

+29
-14
lines changed

1 file changed

+29
-14
lines changed

src/textual/widgets/_masked_input.py

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ def insert_separators(self, value: str, cursor_position: int) -> tuple[str, int]
200200
cursor_position += 1
201201
return value, cursor_position
202202

203-
def insert_text_at_cursor(self, text: str) -> tuple[str, int] | None:
203+
def insert_text_at_cursor(self, text: str, value: str, cursor_position: int) -> tuple[str, int] | None:
204204
"""Inserts `text` at current cursor position. If not present in `text`, any expected separator is automatically
205205
inserted at the correct position.
206206
@@ -211,8 +211,6 @@ def insert_text_at_cursor(self, text: str) -> tuple[str, int] | None:
211211
A tuple in the form `(value, cursor_position)` with the new control value and current cursor position if
212212
`text` matches the template, None otherwise.
213213
"""
214-
value = self.input.value
215-
cursor_position = self.input.cursor_position
216214
separators = set(
217215
[
218216
char_definition.char
@@ -604,7 +602,7 @@ def insert_text_at_cursor(self, text: str) -> None:
604602
text: New text to insert.
605603
"""
606604

607-
new_value = self._template.insert_text_at_cursor(text)
605+
new_value = self._template.insert_text_at_cursor(text, self.value, self.cursor_position)
608606
if new_value is not None:
609607
self.value, self.cursor_position = new_value
610608
else:
@@ -619,16 +617,33 @@ def replace(self, text: str, start: int, end: int):
619617
end: End index to replace (inclusive).
620618
"""
621619

622-
old_cursor_position = self.cursor_position
623-
self.cursor_position = start
624-
new_value = self._template.insert_text_at_cursor(text)
625-
if new_value is not None:
626-
value, cursor_position = new_value
627-
self.value = value[:cursor_position] + value[end:]
628-
self.cursor_position = cursor_position
629-
else:
630-
self.cursor_position = old_cursor_position
631-
self.restricted()
620+
previous_cursor_position = self.cursor_position
621+
value = self.value
622+
cursor_position = start
623+
for char in text:
624+
new_value_cursor_position = self._template.insert_text_at_cursor(char, value, cursor_position)
625+
if new_value_cursor_position is None:
626+
self.value = value
627+
self.cursor_position = previous_cursor_position
628+
self.restricted()
629+
return
630+
631+
new_value, new_cursor_position = new_value_cursor_position
632+
if new_cursor_position >= end:
633+
self.value = new_value[:end] + value[end:]
634+
self.cursor_position = new_cursor_position
635+
return
636+
637+
value = new_value
638+
cursor_position = new_cursor_position
639+
640+
self.value = value
641+
self.cursor_position = end
642+
while self.cursor_position > cursor_position:
643+
self._template.move_cursor(-1)
644+
self._template.delete_at_position()
645+
646+
self.cursor_position = cursor_position
632647

633648
def clear(self) -> None:
634649
"""Clear the masked input."""

0 commit comments

Comments
 (0)