Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions prompt_toolkit/buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ def reset(self, initial_document=None, append_to_history=False):
# using multiple cursors.)
self.multiple_cursor_positions = []

self.substitute_selection_ranges = []

# When doing consecutive up/down movements, prefer to stay at this column.
self.preferred_column = None

Expand Down Expand Up @@ -1133,6 +1135,8 @@ def apply_search(self, search_state, include_current_position=True, count=1):
self.working_index = working_index
self.cursor_position = cursor_position

return search_result is not None

def exit_selection(self):
self.selection_state = None

Expand Down
96 changes: 96 additions & 0 deletions prompt_toolkit/key_binding/bindings/vi.py
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,30 @@ def get_search_state(cli): return cli.search_state
selection_mode = ViSelectionMode()
handle = create_handle_decorator(registry, filter & ViMode())

@handle('s', filter=selection_mode)
def _(event):
"""
Search text (in order to replace it in the selection).
"""
buff = event.current_buffer

# Store selection ranges.
ranges = []

for from_to in buff.document.selection_ranges():
ranges.append(list(from_to))

buff.substitute_selection_ranges = ranges

event.current_buffer.exit_selection()
event.current_buffer.cursor_position = ranges[0][0]
# Set the ViState.
get_search_state(event.cli).direction = IncrementalSearchDirection.FORWARD
event.cli.vi_state.input_mode = InputMode.INSERT

# Focus search buffer.
event.cli.push_focus(search_buffer_name)

@handle('/', filter=navigation_mode|selection_mode)
@handle(Keys.ControlS, filter=~has_focus)
def _(event):
Expand Down Expand Up @@ -1833,6 +1857,78 @@ def _(event):
event.cli.pop_focus()
event.cli.buffers[search_buffer_name].reset()

@handle('/', filter=has_focus)
def _(event):
"""
Replace the searched text.
"""
ranges = event.cli.buffers.previous(event.cli).substitute_selection_ranges
if len(ranges) > 0:
positions = []

text_length = len(event.cli.buffers[search_buffer_name].text)

# Apply the search:
input_buffer = event.cli.buffers.previous(event.cli)
search_buffer = event.cli.buffers[search_buffer_name]

# Update search state.
if search_buffer.text:
get_search_state(event.cli).text = search_buffer.text

# Apply search.
input_buffer.apply_search(get_search_state(event.cli))

# Add query to history of search line.
search_buffer.append_to_history()
search_buffer.reset()

# Focus previous document again.
event.cli.vi_state.input_mode = InputMode.NAVIGATION
event.cli.pop_focus()

done0 = False
i0 = 0
while (not done0):
from_, to = ranges[i0]
done1 = False
while (not done1):
done2 = False
while (not done2):
last_cursor_position = event.current_buffer.cursor_position
if event.current_buffer.cursor_position < from_:
if not event.current_buffer.apply_search(
get_search_state(event.cli), include_current_position=False,
count=1):
done2 = True
if event.current_buffer.cursor_position <= last_cursor_position:
done2 = True
else:
done2 = True
if from_ <= event.current_buffer.cursor_position <= to - text_length + 1:
positions.append(event.current_buffer.cursor_position)
event.current_buffer.delete(count=text_length)
for i1 in range(i0, len(ranges)):
if i1 > i0:
ranges[i1][0] -= text_length
ranges[i1][1] -= text_length
to -= text_length
if not event.current_buffer.apply_search(
get_search_state(event.cli), include_current_position=True,
count=1):
done1 = True
else:
done1 = True
i0 += 1
if i0 == len(ranges):
done0 = True

event.cli.buffers[search_buffer_name].reset()
ranges = []
if len(positions) > 0:
event.current_buffer.multiple_cursor_positions = positions
event.current_buffer.cursor_position = positions[0]
event.cli.vi_state.input_mode = InputMode.INSERT_MULTIPLE

def load_extra_vi_page_navigation_bindings(registry, filter=None):
"""
Expand Down