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,16 +14,24 @@ from .helpers import clear_temp_cut_buffer_delayed, set_temp_cut_buffer_delayed
|
|||||||
|
|
||||||
class Handler:
|
class Handler:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def execute(
|
def execute(view: GtkSource.View, *args, **kwargs):
|
||||||
view: GtkSource.View,
|
|
||||||
*args,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
logger.debug("Command: Cut to Temp Buffer")
|
logger.debug("Command: Cut to Temp Buffer")
|
||||||
|
|
||||||
clear_temp_cut_buffer_delayed(view)
|
clear_temp_cut_buffer_delayed(view)
|
||||||
|
|
||||||
buffer = view.get_buffer()
|
buffer = view.get_buffer()
|
||||||
|
|
||||||
|
if buffer.get_has_selection():
|
||||||
|
start_itr, end_itr = buffer.get_selection_bounds()
|
||||||
|
|
||||||
|
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())
|
itr = buffer.get_iter_at_mark(buffer.get_insert())
|
||||||
|
|
||||||
start_itr = itr.copy()
|
start_itr = itr.copy()
|
||||||
@@ -36,9 +44,14 @@ class Handler:
|
|||||||
if not hasattr(view, "_cut_buffer"):
|
if not hasattr(view, "_cut_buffer"):
|
||||||
view._cut_buffer = ""
|
view._cut_buffer = ""
|
||||||
|
|
||||||
line_str = buffer.get_text(start_itr, end_itr, True)
|
text = buffer.get_text(start_itr, end_itr, True)
|
||||||
view._cut_buffer += line_str
|
|
||||||
|
if not text.endswith("\n"):
|
||||||
|
text += "\n"
|
||||||
|
|
||||||
|
view._cut_buffer += text
|
||||||
|
|
||||||
buffer.delete(start_itr, end_itr)
|
buffer.delete(start_itr, end_itr)
|
||||||
|
buffer.place_cursor(start_itr)
|
||||||
|
|
||||||
set_temp_cut_buffer_delayed(view)
|
set_temp_cut_buffer_delayed(view)
|
||||||
@@ -11,45 +11,45 @@ from gi.repository import GtkSource
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def execute(
|
def execute(view: GtkSource.View, *args, **kwargs):
|
||||||
view: GtkSource.View,
|
|
||||||
*args,
|
|
||||||
**kwargs
|
|
||||||
):
|
|
||||||
logger.debug("Command: Duplicate Line")
|
logger.debug("Command: Duplicate Line")
|
||||||
|
|
||||||
buffer = view.get_buffer()
|
buffer = view.get_buffer()
|
||||||
|
|
||||||
if not buffer.get_has_selection():
|
if buffer.get_has_selection():
|
||||||
had_selection = False
|
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:
|
||||||
itr = buffer.get_iter_at_mark(buffer.get_insert())
|
itr = buffer.get_iter_at_mark(buffer.get_insert())
|
||||||
start_itr = itr.copy()
|
start_line = end_line = itr.get_line()
|
||||||
end_itr = itr.copy()
|
col = itr.get_line_offset()
|
||||||
start_line = itr.get_line() + 1
|
|
||||||
start_char = 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
|
|
||||||
|
|
||||||
start_itr.backward_visible_line()
|
start_itr = buffer.get_iter_at_line(start_line)
|
||||||
start_itr.forward_line()
|
end_itr = buffer.get_iter_at_line(end_line)
|
||||||
end_itr.forward_line()
|
|
||||||
end_itr.backward_char()
|
|
||||||
|
|
||||||
line_str = buffer.get_slice(start_itr, end_itr, True)
|
if not end_itr.ends_line():
|
||||||
|
end_itr.forward_to_line_end()
|
||||||
|
|
||||||
|
if not end_itr.is_end():
|
||||||
end_itr.forward_char()
|
end_itr.forward_char()
|
||||||
buffer.insert(end_itr, f"{line_str}\n", -1)
|
|
||||||
|
|
||||||
if not had_selection:
|
text = buffer.get_text(start_itr, end_itr, True)
|
||||||
new_itr = buffer.get_iter_at_line_offset(start_line, start_char)
|
insert_itr = buffer.get_iter_at_line(end_line)
|
||||||
buffer.place_cursor(new_itr)
|
|
||||||
|
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:
|
else:
|
||||||
new_itr = buffer.get_iter_at_line_offset(start_line, start_char)
|
new_start = buffer.get_iter_at_line_offset(end_line + 1, col)
|
||||||
new_end_itr = buffer.get_iter_at_line_offset((start_line + range_line_size), end_char)
|
buffer.place_cursor(new_start)
|
||||||
buffer.select_range(new_itr, new_end_itr)
|
|
||||||
|
|||||||
@@ -61,7 +61,20 @@ class SourceFile(GtkSource.File):
|
|||||||
location: Gtk.TextIter,
|
location: Gtk.TextIter,
|
||||||
text: str, length: int
|
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(
|
def _after_insert_text(
|
||||||
self,
|
self,
|
||||||
|
|||||||
Reference in New Issue
Block a user