Fix line-based editing behavior for cut and duplicate commands
- Cut to temp buffer: - Respect selections by expanding to full line boundaries - Normalize end iterator to include full line + newline - Ensure consistent newline handling for last line - Preserve cursor position after delete - Prevent line merging when accumulating cut buffer - Duplicate line: - Simplify logic using line-based iter APIs - Fix incorrect selection handling and off-by-one issues - Ensure full-line duplication for both selection and cursor cases - Correct cursor/selection restoration after duplication
This commit is contained in:
@@ -14,31 +14,44 @@ from .helpers import clear_temp_cut_buffer_delayed, set_temp_cut_buffer_delayed
|
||||
|
||||
class Handler:
|
||||
@staticmethod
|
||||
def execute(
|
||||
view: GtkSource.View,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
def execute(view: GtkSource.View, *args, **kwargs):
|
||||
logger.debug("Command: Cut to Temp Buffer")
|
||||
|
||||
clear_temp_cut_buffer_delayed(view)
|
||||
|
||||
buffer = view.get_buffer()
|
||||
itr = buffer.get_iter_at_mark(buffer.get_insert())
|
||||
|
||||
start_itr = itr.copy()
|
||||
start_itr.set_line_offset(0)
|
||||
if buffer.get_has_selection():
|
||||
start_itr, end_itr = buffer.get_selection_bounds()
|
||||
|
||||
end_itr = start_itr.copy()
|
||||
if not end_itr.forward_line():
|
||||
end_itr = buffer.get_end_iter()
|
||||
start_itr.set_line_offset(0)
|
||||
|
||||
if not end_itr.ends_line():
|
||||
end_itr.forward_to_line_end()
|
||||
|
||||
if not end_itr.is_end():
|
||||
end_itr.forward_char()
|
||||
else:
|
||||
itr = buffer.get_iter_at_mark(buffer.get_insert())
|
||||
|
||||
start_itr = itr.copy()
|
||||
start_itr.set_line_offset(0)
|
||||
|
||||
end_itr = start_itr.copy()
|
||||
if not end_itr.forward_line():
|
||||
end_itr = buffer.get_end_iter()
|
||||
|
||||
if not hasattr(view, "_cut_buffer"):
|
||||
view._cut_buffer = ""
|
||||
|
||||
line_str = buffer.get_text(start_itr, end_itr, True)
|
||||
view._cut_buffer += line_str
|
||||
text = buffer.get_text(start_itr, end_itr, True)
|
||||
|
||||
if not text.endswith("\n"):
|
||||
text += "\n"
|
||||
|
||||
view._cut_buffer += text
|
||||
|
||||
buffer.delete(start_itr, end_itr)
|
||||
buffer.place_cursor(start_itr)
|
||||
|
||||
set_temp_cut_buffer_delayed(view)
|
||||
@@ -11,45 +11,45 @@ from gi.repository import GtkSource
|
||||
|
||||
|
||||
|
||||
def execute(
|
||||
view: GtkSource.View,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
def execute(view: GtkSource.View, *args, **kwargs):
|
||||
logger.debug("Command: Duplicate Line")
|
||||
|
||||
buffer = view.get_buffer()
|
||||
|
||||
if not buffer.get_has_selection():
|
||||
had_selection = False
|
||||
itr = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
start_itr = itr.copy()
|
||||
end_itr = itr.copy()
|
||||
start_line = itr.get_line() + 1
|
||||
start_char = itr.get_line_offset()
|
||||
if buffer.get_has_selection():
|
||||
start_itr, \
|
||||
end_itr = buffer.get_selection_bounds()
|
||||
start_line = start_itr.get_line()
|
||||
end_line = end_itr.get_line()
|
||||
scol = start_itr.get_line_offset()
|
||||
ecol = end_itr.get_line_offset()
|
||||
else:
|
||||
had_selection = True
|
||||
start_itr, end_itr = buffer.get_selection_bounds()
|
||||
sline = start_itr.get_line()
|
||||
eline = end_itr.get_line()
|
||||
start_line = eline + 1
|
||||
start_char = start_itr.get_line_offset()
|
||||
end_char = end_itr.get_line_offset()
|
||||
range_line_size = eline - sline
|
||||
itr = buffer.get_iter_at_mark(buffer.get_insert())
|
||||
start_line = end_line = itr.get_line()
|
||||
col = itr.get_line_offset()
|
||||
|
||||
start_itr.backward_visible_line()
|
||||
start_itr.forward_line()
|
||||
end_itr.forward_line()
|
||||
end_itr.backward_char()
|
||||
start_itr = buffer.get_iter_at_line(start_line)
|
||||
end_itr = buffer.get_iter_at_line(end_line)
|
||||
|
||||
line_str = buffer.get_slice(start_itr, end_itr, True)
|
||||
end_itr.forward_char()
|
||||
buffer.insert(end_itr, f"{line_str}\n", -1)
|
||||
if not end_itr.ends_line():
|
||||
end_itr.forward_to_line_end()
|
||||
|
||||
if not had_selection:
|
||||
new_itr = buffer.get_iter_at_line_offset(start_line, start_char)
|
||||
buffer.place_cursor(new_itr)
|
||||
if not end_itr.is_end():
|
||||
end_itr.forward_char()
|
||||
|
||||
text = buffer.get_text(start_itr, end_itr, True)
|
||||
insert_itr = buffer.get_iter_at_line(end_line)
|
||||
|
||||
insert_itr.forward_to_line_end()
|
||||
if not insert_itr.is_end():
|
||||
insert_itr.forward_char()
|
||||
|
||||
buffer.insert(insert_itr, text)
|
||||
|
||||
if buffer.get_has_selection():
|
||||
new_start = buffer.get_iter_at_line_offset(end_line + 1, scol)
|
||||
new_end = buffer.get_iter_at_line_offset(end_line + 1 + (end_line - start_line), ecol)
|
||||
buffer.select_range(new_start, new_end)
|
||||
else:
|
||||
new_itr = buffer.get_iter_at_line_offset(start_line, start_char)
|
||||
new_end_itr = buffer.get_iter_at_line_offset((start_line + range_line_size), end_char)
|
||||
buffer.select_range(new_itr, new_end_itr)
|
||||
new_start = buffer.get_iter_at_line_offset(end_line + 1, col)
|
||||
buffer.place_cursor(new_start)
|
||||
|
||||
@@ -61,7 +61,20 @@ class SourceFile(GtkSource.File):
|
||||
location: Gtk.TextIter,
|
||||
text: str, length: int
|
||||
):
|
||||
...
|
||||
event = Event_Factory.create_event(
|
||||
"text_insert",
|
||||
file = self,
|
||||
buffer = self.buffer,
|
||||
location = location,
|
||||
text = text,
|
||||
length = length
|
||||
)
|
||||
|
||||
# Note: 'idle_add' needed b/c markers don't get thir positions
|
||||
# updated relative to the initial insert.
|
||||
# If not used, seg faults galor during multi insert.
|
||||
# GLib.idle_add(self.emit, event)
|
||||
self.emit(event)
|
||||
|
||||
def _after_insert_text(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user