Merge pull request #61902 from Paulb23/multi-caret
Add Multi-caret support to TextEdit
This commit is contained in:
commit
f5903215d0
11 changed files with 3812 additions and 2100 deletions
|
@ -5,6 +5,7 @@
|
|||
</brief_description>
|
||||
<description>
|
||||
TextEdit is meant for editing large, multiline text. It also has facilities for editing code, such as syntax highlighting support and multiple levels of undo/redo.
|
||||
[b]Note:[/b] Most viewport, caret and edit methods contain a [code]caret_index[/code] argument for [member caret_multiple] support. The argument should be one of the following: [code]-1[/code] for all carets, [code]0[/code] for the main caret, or greater than [code]0[/code] for secondary carets.
|
||||
[b]Note:[/b] When holding down [kbd]Alt[/kbd], the vertical scroll wheel will scroll 5 times as fast as it would normally do. This also works in the Godot script editor.
|
||||
</description>
|
||||
<tutorials>
|
||||
|
@ -12,18 +13,21 @@
|
|||
<methods>
|
||||
<method name="_backspace" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" />
|
||||
<description>
|
||||
Override this method to define what happens when the user presses the backspace key.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_copy" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" />
|
||||
<description>
|
||||
Override this method to define what happens when the user performs a copy operation.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_cut" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" />
|
||||
<description>
|
||||
Override this method to define what happens when the user performs a cut operation.
|
||||
</description>
|
||||
|
@ -31,23 +35,34 @@
|
|||
<method name="_handle_unicode_input" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<param index="0" name="unicode_char" type="int" />
|
||||
<param index="1" name="caret_index" type="int" />
|
||||
<description>
|
||||
Override this method to define what happens when the user types in the provided key [param unicode_char].
|
||||
</description>
|
||||
</method>
|
||||
<method name="_paste" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" />
|
||||
<description>
|
||||
Override this method to define what happens when the user performs a paste operation.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_paste_primary_clipboard" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" />
|
||||
<description>
|
||||
Override this method to define what happens when the user performs a paste operation with middle mouse button.
|
||||
[b]Note:[/b] This method is only implemented on Linux.
|
||||
</description>
|
||||
</method>
|
||||
<method name="add_caret">
|
||||
<return type="int" />
|
||||
<param index="0" name="line" type="int" />
|
||||
<param index="1" name="col" type="int" />
|
||||
<description>
|
||||
Adds a new caret at the given location. Returns the index of the new caret, or [code]-1[/code] if the location is invalid.
|
||||
</description>
|
||||
</method>
|
||||
<method name="add_gutter">
|
||||
<return type="void" />
|
||||
<param index="0" name="at" type="int" default="-1" />
|
||||
|
@ -55,14 +70,27 @@
|
|||
Register a new gutter to this [TextEdit]. Use [param at] to have a specific gutter order. A value of [code]-1[/code] appends the gutter to the right.
|
||||
</description>
|
||||
</method>
|
||||
<method name="adjust_carets_after_edit">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret" type="int" />
|
||||
<param index="1" name="from_line" type="int" />
|
||||
<param index="2" name="from_col" type="int" />
|
||||
<param index="3" name="to_line" type="int" />
|
||||
<param index="4" name="to_col" type="int" />
|
||||
<description>
|
||||
Reposition the carets affected by the edit. This assumes edits are applied in edit order, see [method get_caret_index_edit_order].
|
||||
</description>
|
||||
</method>
|
||||
<method name="adjust_viewport_to_caret">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Adjust the viewport so the caret is visible.
|
||||
</description>
|
||||
</method>
|
||||
<method name="backspace">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" default="-1" />
|
||||
<description>
|
||||
Called when the user presses the backspace key. Can be overridden with [method _backspace].
|
||||
</description>
|
||||
|
@ -75,6 +103,7 @@
|
|||
</method>
|
||||
<method name="center_viewport_to_caret">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Centers the viewport on the line the editing caret is at. This also resets the [member scroll_horizontal] value to [code]0[/code].
|
||||
</description>
|
||||
|
@ -93,28 +122,38 @@
|
|||
</method>
|
||||
<method name="copy">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" default="-1" />
|
||||
<description>
|
||||
Copies the current text selection. Can be overridden with [method _copy].
|
||||
</description>
|
||||
</method>
|
||||
<method name="cut">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" default="-1" />
|
||||
<description>
|
||||
Cut's the current selection. Can be overridden with [method _cut].
|
||||
</description>
|
||||
</method>
|
||||
<method name="delete_selection">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" default="-1" />
|
||||
<description>
|
||||
Deletes the selected text.
|
||||
</description>
|
||||
</method>
|
||||
<method name="deselect">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" default="-1" />
|
||||
<description>
|
||||
Deselects the current selection.
|
||||
</description>
|
||||
</method>
|
||||
<method name="end_action">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Marks the end of steps in the current action started with [method start_action].
|
||||
</description>
|
||||
</method>
|
||||
<method name="end_complex_operation">
|
||||
<return type="void" />
|
||||
<description>
|
||||
|
@ -123,24 +162,40 @@
|
|||
</method>
|
||||
<method name="get_caret_column" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Returns the column the editing caret is at.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_caret_count" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns the number of carets in this [TextEdit].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_caret_draw_pos" qualifiers="const">
|
||||
<return type="Vector2" />
|
||||
<param index="0" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Returns the caret pixel draw position.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_caret_index_edit_order">
|
||||
<return type="PackedInt32Array" />
|
||||
<description>
|
||||
Returns a list of caret indexes in their edit order, this done from bottom to top. Edit order refers to the way actions such as [method insert_text_at_caret] are applied.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_caret_line" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Returns the line the editing caret is on.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_caret_wrap_index" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Returns the wrap index the editing caret is on.
|
||||
</description>
|
||||
|
@ -381,32 +436,37 @@
|
|||
Returns the scroll position for [param wrap_index] of [param line].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_selected_text" qualifiers="const">
|
||||
<method name="get_selected_text">
|
||||
<return type="String" />
|
||||
<param index="0" name="caret_index" type="int" default="-1" />
|
||||
<description>
|
||||
Returns the text inside the selection.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_selection_column" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Returns the original start column of the selection.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_selection_from_column" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Returns the selection begin column.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_selection_from_line" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Returns the selection begin line.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_selection_line" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Returns the original start line of the selection.
|
||||
</description>
|
||||
|
@ -419,12 +479,14 @@
|
|||
</method>
|
||||
<method name="get_selection_to_column" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Returns the selection end column.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_selection_to_line" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Returns the selection end line.
|
||||
</description>
|
||||
|
@ -476,6 +538,7 @@
|
|||
</method>
|
||||
<method name="get_word_under_caret" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="caret_index" type="int" default="-1" />
|
||||
<description>
|
||||
Returns a [String] text with the word under the caret's location.
|
||||
</description>
|
||||
|
@ -494,6 +557,7 @@
|
|||
</method>
|
||||
<method name="has_selection" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="caret_index" type="int" default="-1" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the user has selected text.
|
||||
</description>
|
||||
|
@ -515,12 +579,14 @@
|
|||
<method name="insert_text_at_caret">
|
||||
<return type="void" />
|
||||
<param index="0" name="text" type="String" />
|
||||
<param index="1" name="caret_index" type="int" default="-1" />
|
||||
<description>
|
||||
Insert the specified text at the caret position.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_caret_visible" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the caret is visible on the screen.
|
||||
</description>
|
||||
|
@ -576,6 +642,7 @@
|
|||
<method name="is_mouse_over_selection" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="edges" type="bool" />
|
||||
<param index="1" name="caret_index" type="int" default="-1" />
|
||||
<description>
|
||||
Returns whether the mouse is over selection. If [param edges] is [code]true[/code], the edges are considered part of the selection.
|
||||
</description>
|
||||
|
@ -601,18 +668,41 @@
|
|||
Merge the gutters from [param from_line] into [param to_line]. Only overwritable gutters will be copied.
|
||||
</description>
|
||||
</method>
|
||||
<method name="paste">
|
||||
<method name="merge_overlapping_carets">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Merges any overlapping carets. Will favour the newest caret, or the caret with a selection.
|
||||
[b]Note:[/b] This is not called when a caret changes position but after certain actions, so it is possible to get into a state where carets overlap.
|
||||
</description>
|
||||
</method>
|
||||
<method name="paste">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" default="-1" />
|
||||
<description>
|
||||
Paste at the current location. Can be overridden with [method _paste].
|
||||
</description>
|
||||
</method>
|
||||
<method name="paste_primary_clipboard">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" default="-1" />
|
||||
<description>
|
||||
Pastes the primary clipboard.
|
||||
</description>
|
||||
</method>
|
||||
<method name="redo">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Perform redo operation.
|
||||
</description>
|
||||
</method>
|
||||
<method name="remove_caret">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret" type="int" />
|
||||
<description>
|
||||
Removes the given caret index.
|
||||
[b]Note:[/b] This can result in adjustment of all other caret indices.
|
||||
</description>
|
||||
</method>
|
||||
<method name="remove_gutter">
|
||||
<return type="void" />
|
||||
<param index="0" name="gutter" type="int" />
|
||||
|
@ -620,6 +710,12 @@
|
|||
Removes the gutter from this [TextEdit].
|
||||
</description>
|
||||
</method>
|
||||
<method name="remove_secondary_carets">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Removes all additional carets.
|
||||
</description>
|
||||
</method>
|
||||
<method name="remove_text">
|
||||
<return type="void" />
|
||||
<param index="0" name="from_line" type="int" />
|
||||
|
@ -666,6 +762,7 @@
|
|||
<param index="1" name="from_column" type="int" />
|
||||
<param index="2" name="to_line" type="int" />
|
||||
<param index="3" name="to_column" type="int" />
|
||||
<param index="4" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Perform selection, from line/column to line/column.
|
||||
If [member selecting_enabled] is [code]false[/code], no selection will occur.
|
||||
|
@ -680,6 +777,7 @@
|
|||
</method>
|
||||
<method name="select_word_under_caret">
|
||||
<return type="void" />
|
||||
<param index="0" name="caret_index" type="int" default="-1" />
|
||||
<description>
|
||||
Selects the word under the caret.
|
||||
</description>
|
||||
|
@ -688,9 +786,11 @@
|
|||
<return type="void" />
|
||||
<param index="0" name="column" type="int" />
|
||||
<param index="1" name="adjust_viewport" type="bool" default="true" />
|
||||
<param index="2" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Moves the caret to the specified [param column] index.
|
||||
If [param adjust_viewport] is [code]true[/code], the viewport will center at the caret position after the move occurs.
|
||||
[b]Note:[/b] If supporting multiple carets this will not check for any overlap. See [method merge_overlapping_carets].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_caret_line">
|
||||
|
@ -699,10 +799,12 @@
|
|||
<param index="1" name="adjust_viewport" type="bool" default="true" />
|
||||
<param index="2" name="can_be_hidden" type="bool" default="true" />
|
||||
<param index="3" name="wrap_index" type="int" default="0" />
|
||||
<param index="4" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Moves the caret to the specified [param line] index.
|
||||
If [param adjust_viewport] is [code]true[/code], the viewport will center at the caret position after the move occurs.
|
||||
If [param can_be_hidden] is [code]true[/code], the specified [code]line[/code] can be hidden.
|
||||
[b]Note:[/b] If supporting multiple carets this will not check for any overlap. See [method merge_overlapping_carets].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_gutter_clickable">
|
||||
|
@ -872,6 +974,7 @@
|
|||
<param index="0" name="mode" type="int" enum="TextEdit.SelectionMode" />
|
||||
<param index="1" name="line" type="int" default="-1" />
|
||||
<param index="2" name="column" type="int" default="-1" />
|
||||
<param index="3" name="caret_index" type="int" default="0" />
|
||||
<description>
|
||||
Sets the current selection mode.
|
||||
</description>
|
||||
|
@ -890,6 +993,14 @@
|
|||
Provide custom tooltip text. The callback method must take the following args: [code]hovered_word: String[/code]
|
||||
</description>
|
||||
</method>
|
||||
<method name="start_action">
|
||||
<return type="void" />
|
||||
<param index="0" name="action" type="int" enum="TextEdit.EditAction" />
|
||||
<description>
|
||||
Starts an action, will end the current action if [code]action[/code] is different.
|
||||
An action will also end after a call to [method end_action], after [member ProjectSettings.gui/timers/text_edit_idle_detect_sec] is triggered or a new undoable step outside the [method start_action] and [method end_action] calls.
|
||||
</description>
|
||||
</method>
|
||||
<method name="swap_lines">
|
||||
<return type="void" />
|
||||
<param index="0" name="from_line" type="int" />
|
||||
|
@ -926,6 +1037,9 @@
|
|||
If [code]true[/code], a right-click moves the caret at the mouse position before displaying the context menu.
|
||||
If [code]false[/code], the context menu disregards mouse location.
|
||||
</member>
|
||||
<member name="caret_multiple" type="bool" setter="set_multiple_carets_enabled" getter="is_multiple_carets_enabled" default="true">
|
||||
Sets if multiple carets are allowed.
|
||||
</member>
|
||||
<member name="caret_type" type="int" setter="set_caret_type" getter="get_caret_type" enum="TextEdit.CaretType" default="0">
|
||||
Set the type of caret to draw.
|
||||
</member>
|
||||
|
@ -1154,6 +1268,18 @@
|
|||
<constant name="MENU_MAX" value="28" enum="MenuItems">
|
||||
Represents the size of the [enum MenuItems] enum.
|
||||
</constant>
|
||||
<constant name="ACTION_NONE" value="0" enum="EditAction">
|
||||
No current action.
|
||||
</constant>
|
||||
<constant name="ACTION_TYPING" value="1" enum="EditAction">
|
||||
A typing action.
|
||||
</constant>
|
||||
<constant name="ACTION_BACKSPACE" value="2" enum="EditAction">
|
||||
A backwards delete action.
|
||||
</constant>
|
||||
<constant name="ACTION_DELETE" value="3" enum="EditAction">
|
||||
A forward delete action.
|
||||
</constant>
|
||||
<constant name="SEARCH_MATCH_CASE" value="1" enum="SearchFlags">
|
||||
Match case when searching.
|
||||
</constant>
|
||||
|
|
|
@ -55,6 +55,7 @@ void GotoLineDialog::ok_pressed() {
|
|||
if (get_line() < 1 || get_line() > text_editor->get_line_count()) {
|
||||
return;
|
||||
}
|
||||
text_editor->remove_secondary_carets();
|
||||
text_editor->unfold_line(get_line() - 1);
|
||||
text_editor->set_caret_line(get_line() - 1);
|
||||
hide();
|
||||
|
@ -149,7 +150,7 @@ bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col)
|
|||
text_editor->unfold_line(pos.y);
|
||||
text_editor->set_caret_line(pos.y, false);
|
||||
text_editor->set_caret_column(pos.x + text.length(), false);
|
||||
text_editor->center_viewport_to_caret();
|
||||
text_editor->center_viewport_to_caret(0);
|
||||
text_editor->select(pos.y, pos.x, pos.y, pos.x + text.length());
|
||||
|
||||
line_col_changed_for_result = true;
|
||||
|
@ -176,11 +177,11 @@ bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col)
|
|||
}
|
||||
|
||||
void FindReplaceBar::_replace() {
|
||||
bool selection_enabled = text_editor->has_selection();
|
||||
bool selection_enabled = text_editor->has_selection(0);
|
||||
Point2i selection_begin, selection_end;
|
||||
if (selection_enabled) {
|
||||
selection_begin = Point2i(text_editor->get_selection_from_line(), text_editor->get_selection_from_column());
|
||||
selection_end = Point2i(text_editor->get_selection_to_line(), text_editor->get_selection_to_column());
|
||||
selection_begin = Point2i(text_editor->get_selection_from_line(0), text_editor->get_selection_from_column(0));
|
||||
selection_end = Point2i(text_editor->get_selection_to_line(0), text_editor->get_selection_to_column(0));
|
||||
}
|
||||
|
||||
String replace_text = get_replace_text();
|
||||
|
@ -188,25 +189,25 @@ void FindReplaceBar::_replace() {
|
|||
|
||||
text_editor->begin_complex_operation();
|
||||
if (selection_enabled && is_selection_only()) { // To restrict search_current() to selected region
|
||||
text_editor->set_caret_line(selection_begin.width);
|
||||
text_editor->set_caret_column(selection_begin.height);
|
||||
text_editor->set_caret_line(selection_begin.width, false, true, 0, 0);
|
||||
text_editor->set_caret_column(selection_begin.height, true, 0);
|
||||
}
|
||||
|
||||
if (search_current()) {
|
||||
text_editor->unfold_line(result_line);
|
||||
text_editor->select(result_line, result_col, result_line, result_col + search_text_len);
|
||||
text_editor->select(result_line, result_col, result_line, result_col + search_text_len, 0);
|
||||
|
||||
if (selection_enabled && is_selection_only()) {
|
||||
Point2i match_from(result_line, result_col);
|
||||
Point2i match_to(result_line, result_col + search_text_len);
|
||||
if (!(match_from < selection_begin || match_to > selection_end)) {
|
||||
text_editor->insert_text_at_caret(replace_text);
|
||||
text_editor->insert_text_at_caret(replace_text, 0);
|
||||
if (match_to.x == selection_end.x) { // Adjust selection bounds if necessary
|
||||
selection_end.y += replace_text.length() - search_text_len;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
text_editor->insert_text_at_caret(replace_text);
|
||||
text_editor->insert_text_at_caret(replace_text, 0);
|
||||
}
|
||||
}
|
||||
text_editor->end_complex_operation();
|
||||
|
@ -216,29 +217,29 @@ void FindReplaceBar::_replace() {
|
|||
|
||||
if (selection_enabled && is_selection_only()) {
|
||||
// Reselect in order to keep 'Replace' restricted to selection
|
||||
text_editor->select(selection_begin.x, selection_begin.y, selection_end.x, selection_end.y);
|
||||
text_editor->select(selection_begin.x, selection_begin.y, selection_end.x, selection_end.y, 0);
|
||||
} else {
|
||||
text_editor->deselect();
|
||||
text_editor->deselect(0);
|
||||
}
|
||||
}
|
||||
|
||||
void FindReplaceBar::_replace_all() {
|
||||
text_editor->disconnect("text_changed", callable_mp(this, &FindReplaceBar::_editor_text_changed));
|
||||
// Line as x so it gets priority in comparison, column as y.
|
||||
Point2i orig_cursor(text_editor->get_caret_line(), text_editor->get_caret_column());
|
||||
Point2i orig_cursor(text_editor->get_caret_line(0), text_editor->get_caret_column(0));
|
||||
Point2i prev_match = Point2(-1, -1);
|
||||
|
||||
bool selection_enabled = text_editor->has_selection();
|
||||
bool selection_enabled = text_editor->has_selection(0);
|
||||
Point2i selection_begin, selection_end;
|
||||
if (selection_enabled) {
|
||||
selection_begin = Point2i(text_editor->get_selection_from_line(), text_editor->get_selection_from_column());
|
||||
selection_end = Point2i(text_editor->get_selection_to_line(), text_editor->get_selection_to_column());
|
||||
selection_begin = Point2i(text_editor->get_selection_from_line(0), text_editor->get_selection_from_column(0));
|
||||
selection_end = Point2i(text_editor->get_selection_to_line(0), text_editor->get_selection_to_column(0));
|
||||
}
|
||||
|
||||
int vsval = text_editor->get_v_scroll();
|
||||
|
||||
text_editor->set_caret_line(0);
|
||||
text_editor->set_caret_column(0);
|
||||
text_editor->set_caret_line(0, false, true, 0, 0);
|
||||
text_editor->set_caret_column(0, true, 0);
|
||||
|
||||
String replace_text = get_replace_text();
|
||||
int search_text_len = get_search_text().length();
|
||||
|
@ -250,8 +251,8 @@ void FindReplaceBar::_replace_all() {
|
|||
text_editor->begin_complex_operation();
|
||||
|
||||
if (selection_enabled && is_selection_only()) {
|
||||
text_editor->set_caret_line(selection_begin.width);
|
||||
text_editor->set_caret_column(selection_begin.height);
|
||||
text_editor->set_caret_line(selection_begin.width, false, true, 0, 0);
|
||||
text_editor->set_caret_column(selection_begin.height, true, 0);
|
||||
}
|
||||
if (search_current()) {
|
||||
do {
|
||||
|
@ -266,7 +267,7 @@ void FindReplaceBar::_replace_all() {
|
|||
prev_match = Point2i(result_line, result_col + replace_text.length());
|
||||
|
||||
text_editor->unfold_line(result_line);
|
||||
text_editor->select(result_line, result_col, result_line, match_to.y);
|
||||
text_editor->select(result_line, result_col, result_line, match_to.y, 0);
|
||||
|
||||
if (selection_enabled && is_selection_only()) {
|
||||
if (match_from < selection_begin || match_to > selection_end) {
|
||||
|
@ -274,14 +275,14 @@ void FindReplaceBar::_replace_all() {
|
|||
}
|
||||
|
||||
// Replace but adjust selection bounds.
|
||||
text_editor->insert_text_at_caret(replace_text);
|
||||
text_editor->insert_text_at_caret(replace_text, 0);
|
||||
if (match_to.x == selection_end.x) {
|
||||
selection_end.y += replace_text.length() - search_text_len;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Just replace.
|
||||
text_editor->insert_text_at_caret(replace_text);
|
||||
text_editor->insert_text_at_caret(replace_text, 0);
|
||||
}
|
||||
|
||||
rc++;
|
||||
|
@ -293,14 +294,14 @@ void FindReplaceBar::_replace_all() {
|
|||
replace_all_mode = false;
|
||||
|
||||
// Restore editor state (selection, cursor, scroll).
|
||||
text_editor->set_caret_line(orig_cursor.x);
|
||||
text_editor->set_caret_column(orig_cursor.y);
|
||||
text_editor->set_caret_line(orig_cursor.x, false, true, 0, 0);
|
||||
text_editor->set_caret_column(orig_cursor.y, true, 0);
|
||||
|
||||
if (selection_enabled && is_selection_only()) {
|
||||
// Reselect.
|
||||
text_editor->select(selection_begin.x, selection_begin.y, selection_end.x, selection_end.y);
|
||||
text_editor->select(selection_begin.x, selection_begin.y, selection_end.x, selection_end.y, 0);
|
||||
} else {
|
||||
text_editor->deselect();
|
||||
text_editor->deselect(0);
|
||||
}
|
||||
|
||||
text_editor->set_v_scroll(vsval);
|
||||
|
@ -314,10 +315,10 @@ void FindReplaceBar::_replace_all() {
|
|||
}
|
||||
|
||||
void FindReplaceBar::_get_search_from(int &r_line, int &r_col) {
|
||||
r_line = text_editor->get_caret_line();
|
||||
r_col = text_editor->get_caret_column();
|
||||
r_line = text_editor->get_caret_line(0);
|
||||
r_col = text_editor->get_caret_column(0);
|
||||
|
||||
if (text_editor->has_selection() && is_selection_only()) {
|
||||
if (text_editor->has_selection(0) && is_selection_only()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -434,7 +435,7 @@ bool FindReplaceBar::search_prev() {
|
|||
|
||||
int line, col;
|
||||
_get_search_from(line, col);
|
||||
if (text_editor->has_selection()) {
|
||||
if (text_editor->has_selection(0)) {
|
||||
col--; // Skip currently selected word.
|
||||
}
|
||||
|
||||
|
@ -512,8 +513,8 @@ void FindReplaceBar::_show_search(bool p_focus_replace, bool p_show_only) {
|
|||
search_text->call_deferred(SNAME("grab_focus"));
|
||||
}
|
||||
|
||||
if (text_editor->has_selection() && !selection_only->is_pressed()) {
|
||||
search_text->set_text(text_editor->get_selected_text());
|
||||
if (text_editor->has_selection(0) && !selection_only->is_pressed()) {
|
||||
search_text->set_text(text_editor->get_selected_text(0));
|
||||
}
|
||||
|
||||
if (!get_search_text().is_empty()) {
|
||||
|
@ -548,9 +549,9 @@ void FindReplaceBar::popup_replace() {
|
|||
hbc_option_replace->show();
|
||||
}
|
||||
|
||||
selection_only->set_pressed((text_editor->has_selection() && text_editor->get_selection_from_line() < text_editor->get_selection_to_line()));
|
||||
selection_only->set_pressed((text_editor->has_selection(0) && text_editor->get_selection_from_line(0) < text_editor->get_selection_to_line(0)));
|
||||
|
||||
_show_search(is_visible() || text_editor->has_selection());
|
||||
_show_search(is_visible() || text_editor->has_selection(0));
|
||||
}
|
||||
|
||||
void FindReplaceBar::_search_options_changed(bool p_pressed) {
|
||||
|
@ -587,7 +588,7 @@ void FindReplaceBar::_search_text_submitted(const String &p_text) {
|
|||
}
|
||||
|
||||
void FindReplaceBar::_replace_text_submitted(const String &p_text) {
|
||||
if (selection_only->is_pressed() && text_editor->has_selection()) {
|
||||
if (selection_only->is_pressed() && text_editor->has_selection(0)) {
|
||||
_replace_all();
|
||||
_hide_bar();
|
||||
} else if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
|
||||
|
@ -1091,6 +1092,7 @@ void CodeTextEditor::trim_trailing_whitespace() {
|
|||
}
|
||||
|
||||
if (trimed_whitespace) {
|
||||
text_editor->merge_overlapping_carets();
|
||||
text_editor->end_complex_operation();
|
||||
text_editor->queue_redraw();
|
||||
}
|
||||
|
@ -1122,8 +1124,11 @@ void CodeTextEditor::convert_indent_to_spaces() {
|
|||
indent += " ";
|
||||
}
|
||||
|
||||
int cursor_line = text_editor->get_caret_line();
|
||||
int cursor_column = text_editor->get_caret_column();
|
||||
Vector<int> cursor_columns;
|
||||
cursor_columns.resize(text_editor->get_caret_count());
|
||||
for (int c = 0; c < text_editor->get_caret_count(); c++) {
|
||||
cursor_columns.write[c] = text_editor->get_caret_column(c);
|
||||
}
|
||||
|
||||
bool changed_indentation = false;
|
||||
for (int i = 0; i < text_editor->get_line_count(); i++) {
|
||||
|
@ -1140,8 +1145,10 @@ void CodeTextEditor::convert_indent_to_spaces() {
|
|||
text_editor->begin_complex_operation();
|
||||
changed_indentation = true;
|
||||
}
|
||||
if (cursor_line == i && cursor_column > j) {
|
||||
cursor_column += indent_size - 1;
|
||||
for (int c = 0; c < text_editor->get_caret_count(); c++) {
|
||||
if (text_editor->get_caret_line(c) == i && text_editor->get_caret_column(c) > j) {
|
||||
cursor_columns.write[c] += indent_size - 1;
|
||||
}
|
||||
}
|
||||
line = line.left(j) + indent + line.substr(j + 1);
|
||||
}
|
||||
|
@ -1152,7 +1159,10 @@ void CodeTextEditor::convert_indent_to_spaces() {
|
|||
}
|
||||
}
|
||||
if (changed_indentation) {
|
||||
text_editor->set_caret_column(cursor_column);
|
||||
for (int c = 0; c < text_editor->get_caret_count(); c++) {
|
||||
text_editor->set_caret_column(cursor_columns[c], c == 0, c);
|
||||
}
|
||||
text_editor->merge_overlapping_carets();
|
||||
text_editor->end_complex_operation();
|
||||
text_editor->queue_redraw();
|
||||
}
|
||||
|
@ -1162,8 +1172,11 @@ void CodeTextEditor::convert_indent_to_tabs() {
|
|||
int indent_size = EditorSettings::get_singleton()->get("text_editor/behavior/indent/size");
|
||||
indent_size -= 1;
|
||||
|
||||
int cursor_line = text_editor->get_caret_line();
|
||||
int cursor_column = text_editor->get_caret_column();
|
||||
Vector<int> cursor_columns;
|
||||
cursor_columns.resize(text_editor->get_caret_count());
|
||||
for (int c = 0; c < text_editor->get_caret_count(); c++) {
|
||||
cursor_columns.write[c] = text_editor->get_caret_column(c);
|
||||
}
|
||||
|
||||
bool changed_indentation = false;
|
||||
for (int i = 0; i < text_editor->get_line_count(); i++) {
|
||||
|
@ -1184,8 +1197,10 @@ void CodeTextEditor::convert_indent_to_tabs() {
|
|||
text_editor->begin_complex_operation();
|
||||
changed_indentation = true;
|
||||
}
|
||||
if (cursor_line == i && cursor_column > j) {
|
||||
cursor_column -= indent_size;
|
||||
for (int c = 0; c < text_editor->get_caret_count(); c++) {
|
||||
if (text_editor->get_caret_line(c) == i && text_editor->get_caret_column(c) > j) {
|
||||
cursor_columns.write[c] -= indent_size;
|
||||
}
|
||||
}
|
||||
line = line.left(j - indent_size) + "\t" + line.substr(j + 1);
|
||||
j = 0;
|
||||
|
@ -1201,7 +1216,10 @@ void CodeTextEditor::convert_indent_to_tabs() {
|
|||
}
|
||||
}
|
||||
if (changed_indentation) {
|
||||
text_editor->set_caret_column(cursor_column);
|
||||
for (int c = 0; c < text_editor->get_caret_count(); c++) {
|
||||
text_editor->set_caret_column(cursor_columns[c], c == 0, c);
|
||||
}
|
||||
text_editor->merge_overlapping_carets();
|
||||
text_editor->end_complex_operation();
|
||||
text_editor->queue_redraw();
|
||||
}
|
||||
|
@ -1211,13 +1229,18 @@ void CodeTextEditor::convert_case(CaseStyle p_case) {
|
|||
if (!text_editor->has_selection()) {
|
||||
return;
|
||||
}
|
||||
|
||||
text_editor->begin_complex_operation();
|
||||
|
||||
int begin = text_editor->get_selection_from_line();
|
||||
int end = text_editor->get_selection_to_line();
|
||||
int begin_col = text_editor->get_selection_from_column();
|
||||
int end_col = text_editor->get_selection_to_column();
|
||||
Vector<int> caret_edit_order = text_editor->get_caret_index_edit_order();
|
||||
for (const int &c : caret_edit_order) {
|
||||
if (!text_editor->has_selection(c)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int begin = text_editor->get_selection_from_line(c);
|
||||
int end = text_editor->get_selection_to_line(c);
|
||||
int begin_col = text_editor->get_selection_from_column(c);
|
||||
int end_col = text_editor->get_selection_to_column(c);
|
||||
|
||||
for (int i = begin; i <= end; i++) {
|
||||
int len = text_editor->get_line(i).length();
|
||||
|
@ -1249,21 +1272,66 @@ void CodeTextEditor::convert_case(CaseStyle p_case) {
|
|||
}
|
||||
text_editor->set_line(i, new_line);
|
||||
}
|
||||
}
|
||||
text_editor->end_complex_operation();
|
||||
}
|
||||
|
||||
void CodeTextEditor::move_lines_up() {
|
||||
text_editor->begin_complex_operation();
|
||||
if (text_editor->has_selection()) {
|
||||
int from_line = text_editor->get_selection_from_line();
|
||||
int from_col = text_editor->get_selection_from_column();
|
||||
int to_line = text_editor->get_selection_to_line();
|
||||
int to_column = text_editor->get_selection_to_column();
|
||||
int cursor_line = text_editor->get_caret_line();
|
||||
|
||||
for (int i = from_line; i <= to_line; i++) {
|
||||
int line_id = i;
|
||||
int next_id = i - 1;
|
||||
Vector<int> carets_to_remove;
|
||||
|
||||
Vector<int> caret_edit_order = text_editor->get_caret_index_edit_order();
|
||||
for (int i = 0; i < caret_edit_order.size(); i++) {
|
||||
int c = caret_edit_order[i];
|
||||
int cl = text_editor->get_caret_line(c);
|
||||
|
||||
bool swaped_caret = false;
|
||||
for (int j = i + 1; j < caret_edit_order.size(); j++) {
|
||||
if (text_editor->has_selection(caret_edit_order[j])) {
|
||||
if (text_editor->get_selection_from_line() == cl) {
|
||||
carets_to_remove.push_back(caret_edit_order[j]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (text_editor->get_selection_to_line() == cl) {
|
||||
if (text_editor->has_selection(c)) {
|
||||
if (text_editor->get_selection_to_line(c) != cl) {
|
||||
text_editor->select(cl + 1, 0, text_editor->get_selection_to_line(c), text_editor->get_selection_to_column(c), c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
carets_to_remove.push_back(c);
|
||||
i = j - 1;
|
||||
swaped_caret = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (text_editor->get_caret_line(caret_edit_order[j]) == cl) {
|
||||
carets_to_remove.push_back(caret_edit_order[j]);
|
||||
i = j;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (swaped_caret) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (text_editor->has_selection(c)) {
|
||||
int from_line = text_editor->get_selection_from_line(c);
|
||||
int from_col = text_editor->get_selection_from_column(c);
|
||||
int to_line = text_editor->get_selection_to_line(c);
|
||||
int to_column = text_editor->get_selection_to_column(c);
|
||||
int cursor_line = text_editor->get_caret_line(c);
|
||||
|
||||
for (int j = from_line; j <= to_line; j++) {
|
||||
int line_id = j;
|
||||
int next_id = j - 1;
|
||||
|
||||
if (line_id == 0 || next_id < 0) {
|
||||
return;
|
||||
|
@ -1273,15 +1341,15 @@ void CodeTextEditor::move_lines_up() {
|
|||
text_editor->unfold_line(next_id);
|
||||
|
||||
text_editor->swap_lines(line_id, next_id);
|
||||
text_editor->set_caret_line(next_id);
|
||||
text_editor->set_caret_line(next_id, c == 0, true, 0, c);
|
||||
}
|
||||
int from_line_up = from_line > 0 ? from_line - 1 : from_line;
|
||||
int to_line_up = to_line > 0 ? to_line - 1 : to_line;
|
||||
int cursor_line_up = cursor_line > 0 ? cursor_line - 1 : cursor_line;
|
||||
text_editor->select(from_line_up, from_col, to_line_up, to_column);
|
||||
text_editor->set_caret_line(cursor_line_up);
|
||||
text_editor->select(from_line_up, from_col, to_line_up, to_column, c);
|
||||
text_editor->set_caret_line(cursor_line_up, c == 0, true, 0, c);
|
||||
} else {
|
||||
int line_id = text_editor->get_caret_line();
|
||||
int line_id = text_editor->get_caret_line(c);
|
||||
int next_id = line_id - 1;
|
||||
|
||||
if (line_id == 0 || next_id < 0) {
|
||||
|
@ -1292,148 +1360,261 @@ void CodeTextEditor::move_lines_up() {
|
|||
text_editor->unfold_line(next_id);
|
||||
|
||||
text_editor->swap_lines(line_id, next_id);
|
||||
text_editor->set_caret_line(next_id);
|
||||
text_editor->set_caret_line(next_id, c == 0, true, 0, c);
|
||||
}
|
||||
}
|
||||
text_editor->end_complex_operation();
|
||||
text_editor->merge_overlapping_carets();
|
||||
text_editor->queue_redraw();
|
||||
}
|
||||
|
||||
void CodeTextEditor::move_lines_down() {
|
||||
text_editor->begin_complex_operation();
|
||||
if (text_editor->has_selection()) {
|
||||
int from_line = text_editor->get_selection_from_line();
|
||||
int from_col = text_editor->get_selection_from_column();
|
||||
int to_line = text_editor->get_selection_to_line();
|
||||
int to_column = text_editor->get_selection_to_column();
|
||||
int cursor_line = text_editor->get_caret_line();
|
||||
|
||||
for (int i = to_line; i >= from_line; i--) {
|
||||
int line_id = i;
|
||||
int next_id = i + 1;
|
||||
Vector<int> carets_to_remove;
|
||||
|
||||
Vector<int> caret_edit_order = text_editor->get_caret_index_edit_order();
|
||||
for (int i = 0; i < caret_edit_order.size(); i++) {
|
||||
int c = caret_edit_order[i];
|
||||
int cl = text_editor->get_caret_line(c);
|
||||
|
||||
bool swaped_caret = false;
|
||||
for (int j = i + 1; j < caret_edit_order.size(); j++) {
|
||||
if (text_editor->has_selection(caret_edit_order[j])) {
|
||||
if (text_editor->get_selection_from_line() == cl) {
|
||||
carets_to_remove.push_back(caret_edit_order[j]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (text_editor->get_selection_to_line() == cl) {
|
||||
if (text_editor->has_selection(c)) {
|
||||
if (text_editor->get_selection_to_line(c) != cl) {
|
||||
text_editor->select(cl + 1, 0, text_editor->get_selection_to_line(c), text_editor->get_selection_to_column(c), c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
carets_to_remove.push_back(c);
|
||||
i = j - 1;
|
||||
swaped_caret = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (text_editor->get_caret_line(caret_edit_order[j]) == cl) {
|
||||
carets_to_remove.push_back(caret_edit_order[j]);
|
||||
i = j;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (swaped_caret) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (text_editor->has_selection(c)) {
|
||||
int from_line = text_editor->get_selection_from_line(c);
|
||||
int from_col = text_editor->get_selection_from_column(c);
|
||||
int to_line = text_editor->get_selection_to_line(c);
|
||||
int to_column = text_editor->get_selection_to_column(c);
|
||||
int cursor_line = text_editor->get_caret_line(c);
|
||||
|
||||
for (int l = to_line; l >= from_line; l--) {
|
||||
int line_id = l;
|
||||
int next_id = l + 1;
|
||||
|
||||
if (line_id == text_editor->get_line_count() - 1 || next_id > text_editor->get_line_count()) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
text_editor->unfold_line(line_id);
|
||||
text_editor->unfold_line(next_id);
|
||||
|
||||
text_editor->swap_lines(line_id, next_id);
|
||||
text_editor->set_caret_line(next_id);
|
||||
text_editor->set_caret_line(next_id, c == 0, true, 0, c);
|
||||
}
|
||||
int from_line_down = from_line < text_editor->get_line_count() ? from_line + 1 : from_line;
|
||||
int to_line_down = to_line < text_editor->get_line_count() ? to_line + 1 : to_line;
|
||||
int cursor_line_down = cursor_line < text_editor->get_line_count() ? cursor_line + 1 : cursor_line;
|
||||
text_editor->select(from_line_down, from_col, to_line_down, to_column);
|
||||
text_editor->set_caret_line(cursor_line_down);
|
||||
text_editor->select(from_line_down, from_col, to_line_down, to_column, c);
|
||||
text_editor->set_caret_line(cursor_line_down, c == 0, true, 0, c);
|
||||
} else {
|
||||
int line_id = text_editor->get_caret_line();
|
||||
int line_id = text_editor->get_caret_line(c);
|
||||
int next_id = line_id + 1;
|
||||
|
||||
if (line_id == text_editor->get_line_count() - 1 || next_id > text_editor->get_line_count()) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
text_editor->unfold_line(line_id);
|
||||
text_editor->unfold_line(next_id);
|
||||
|
||||
text_editor->swap_lines(line_id, next_id);
|
||||
text_editor->set_caret_line(next_id);
|
||||
text_editor->set_caret_line(next_id, c == 0, true, 0, c);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort and remove backwards to preserve indexes.
|
||||
carets_to_remove.sort();
|
||||
for (int i = carets_to_remove.size() - 1; i >= 0; i--) {
|
||||
text_editor->remove_caret(carets_to_remove[i]);
|
||||
}
|
||||
|
||||
text_editor->merge_overlapping_carets();
|
||||
text_editor->end_complex_operation();
|
||||
text_editor->queue_redraw();
|
||||
}
|
||||
|
||||
void CodeTextEditor::_delete_line(int p_line) {
|
||||
void CodeTextEditor::_delete_line(int p_line, int p_caret) {
|
||||
// this is currently intended to be called within delete_lines()
|
||||
// so `begin_complex_operation` is omitted here
|
||||
text_editor->set_line(p_line, "");
|
||||
if (p_line == 0 && text_editor->get_line_count() > 1) {
|
||||
text_editor->set_caret_line(1);
|
||||
text_editor->set_caret_column(0);
|
||||
text_editor->set_caret_line(1, p_caret == 0, true, 0, p_caret);
|
||||
text_editor->set_caret_column(0, p_caret == 0, p_caret);
|
||||
}
|
||||
text_editor->backspace();
|
||||
text_editor->backspace(p_caret);
|
||||
if (p_line < text_editor->get_line_count()) {
|
||||
text_editor->unfold_line(p_line);
|
||||
}
|
||||
text_editor->set_caret_line(p_line);
|
||||
text_editor->set_caret_line(p_line, p_caret == 0, true, 0, p_caret);
|
||||
}
|
||||
|
||||
void CodeTextEditor::delete_lines() {
|
||||
text_editor->begin_complex_operation();
|
||||
if (text_editor->has_selection()) {
|
||||
int to_line = text_editor->get_selection_to_line();
|
||||
int from_line = text_editor->get_selection_from_line();
|
||||
|
||||
Vector<int> carets_to_remove;
|
||||
|
||||
Vector<int> caret_edit_order = text_editor->get_caret_index_edit_order();
|
||||
for (int i = 0; i < caret_edit_order.size(); i++) {
|
||||
int c = caret_edit_order[i];
|
||||
int cl = text_editor->get_caret_line(c);
|
||||
|
||||
bool swaped_caret = false;
|
||||
for (int j = i + 1; j < caret_edit_order.size(); j++) {
|
||||
if (text_editor->has_selection(caret_edit_order[j])) {
|
||||
if (text_editor->get_selection_from_line() == cl) {
|
||||
carets_to_remove.push_back(caret_edit_order[j]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (text_editor->get_selection_to_line() == cl) {
|
||||
if (text_editor->has_selection(c)) {
|
||||
if (text_editor->get_selection_to_line(c) != cl) {
|
||||
text_editor->select(cl + 1, 0, text_editor->get_selection_to_line(c), text_editor->get_selection_to_column(c), c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
carets_to_remove.push_back(c);
|
||||
i = j - 1;
|
||||
swaped_caret = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (text_editor->get_caret_line(caret_edit_order[j]) == cl) {
|
||||
carets_to_remove.push_back(caret_edit_order[j]);
|
||||
i = j;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (swaped_caret) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (text_editor->has_selection(c)) {
|
||||
int to_line = text_editor->get_selection_to_line(c);
|
||||
int from_line = text_editor->get_selection_from_line(c);
|
||||
int count = Math::abs(to_line - from_line) + 1;
|
||||
|
||||
text_editor->set_caret_line(from_line, false);
|
||||
text_editor->deselect();
|
||||
for (int i = 0; i < count; i++) {
|
||||
_delete_line(from_line);
|
||||
text_editor->set_caret_line(from_line, false, true, 0, c);
|
||||
text_editor->deselect(c);
|
||||
for (int j = 0; j < count; j++) {
|
||||
_delete_line(from_line, c);
|
||||
}
|
||||
} else {
|
||||
_delete_line(text_editor->get_caret_line());
|
||||
_delete_line(text_editor->get_caret_line(c), c);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort and remove backwards to preserve indexes.
|
||||
carets_to_remove.sort();
|
||||
for (int i = carets_to_remove.size() - 1; i >= 0; i--) {
|
||||
text_editor->remove_caret(carets_to_remove[i]);
|
||||
}
|
||||
text_editor->merge_overlapping_carets();
|
||||
text_editor->end_complex_operation();
|
||||
}
|
||||
|
||||
void CodeTextEditor::duplicate_selection() {
|
||||
const int cursor_column = text_editor->get_caret_column();
|
||||
int from_line = text_editor->get_caret_line();
|
||||
int to_line = text_editor->get_caret_line();
|
||||
text_editor->begin_complex_operation();
|
||||
|
||||
Vector<int> caret_edit_order = text_editor->get_caret_index_edit_order();
|
||||
for (const int &c : caret_edit_order) {
|
||||
const int cursor_column = text_editor->get_caret_column(c);
|
||||
int from_line = text_editor->get_caret_line(c);
|
||||
int to_line = text_editor->get_caret_line(c);
|
||||
int from_column = 0;
|
||||
int to_column = 0;
|
||||
int cursor_new_line = to_line + 1;
|
||||
int cursor_new_column = text_editor->get_caret_column();
|
||||
int cursor_new_column = text_editor->get_caret_column(c);
|
||||
String new_text = "\n" + text_editor->get_line(from_line);
|
||||
bool selection_active = false;
|
||||
|
||||
text_editor->set_caret_column(text_editor->get_line(from_line).length());
|
||||
if (text_editor->has_selection()) {
|
||||
from_column = text_editor->get_selection_from_column();
|
||||
to_column = text_editor->get_selection_to_column();
|
||||
text_editor->set_caret_column(text_editor->get_line(from_line).length(), c == 0, c);
|
||||
if (text_editor->has_selection(c)) {
|
||||
from_column = text_editor->get_selection_from_column(c);
|
||||
to_column = text_editor->get_selection_to_column(c);
|
||||
|
||||
from_line = text_editor->get_selection_from_line();
|
||||
to_line = text_editor->get_selection_to_line();
|
||||
cursor_new_line = to_line + text_editor->get_caret_line() - from_line;
|
||||
from_line = text_editor->get_selection_from_line(c);
|
||||
to_line = text_editor->get_selection_to_line(c);
|
||||
cursor_new_line = to_line + text_editor->get_caret_line(c) - from_line;
|
||||
cursor_new_column = to_column == cursor_column ? 2 * to_column - from_column : to_column;
|
||||
new_text = text_editor->get_selected_text();
|
||||
new_text = text_editor->get_selected_text(c);
|
||||
selection_active = true;
|
||||
|
||||
text_editor->set_caret_line(to_line);
|
||||
text_editor->set_caret_column(to_column);
|
||||
text_editor->set_caret_line(to_line, c == 0, true, 0, c);
|
||||
text_editor->set_caret_column(to_column, c == 0, c);
|
||||
}
|
||||
|
||||
text_editor->begin_complex_operation();
|
||||
|
||||
for (int i = from_line; i <= to_line; i++) {
|
||||
text_editor->unfold_line(i);
|
||||
}
|
||||
text_editor->deselect();
|
||||
text_editor->insert_text_at_caret(new_text);
|
||||
text_editor->set_caret_line(cursor_new_line);
|
||||
text_editor->set_caret_column(cursor_new_column);
|
||||
text_editor->deselect(c);
|
||||
text_editor->insert_text_at_caret(new_text, c);
|
||||
text_editor->set_caret_line(cursor_new_line, c == 0, true, 0, c);
|
||||
text_editor->set_caret_column(cursor_new_column, c == 0, c);
|
||||
if (selection_active) {
|
||||
text_editor->select(to_line, to_column, 2 * to_line - from_line, to_line == from_line ? 2 * to_column - from_column : to_column);
|
||||
text_editor->select(to_line, to_column, 2 * to_line - from_line, to_line == from_line ? 2 * to_column - from_column : to_column, c);
|
||||
}
|
||||
|
||||
}
|
||||
text_editor->merge_overlapping_carets();
|
||||
text_editor->end_complex_operation();
|
||||
text_editor->queue_redraw();
|
||||
}
|
||||
|
||||
void CodeTextEditor::toggle_inline_comment(const String &delimiter) {
|
||||
text_editor->begin_complex_operation();
|
||||
if (text_editor->has_selection()) {
|
||||
int begin = text_editor->get_selection_from_line();
|
||||
int end = text_editor->get_selection_to_line();
|
||||
|
||||
Vector<int> caret_edit_order = text_editor->get_caret_index_edit_order();
|
||||
for (const int &c : caret_edit_order) {
|
||||
if (text_editor->has_selection(c)) {
|
||||
int begin = text_editor->get_selection_from_line(c);
|
||||
int end = text_editor->get_selection_to_line(c);
|
||||
|
||||
// End of selection ends on the first column of the last line, ignore it.
|
||||
if (text_editor->get_selection_to_column() == 0) {
|
||||
if (text_editor->get_selection_to_column(c) == 0) {
|
||||
end -= 1;
|
||||
}
|
||||
|
||||
int col_to = text_editor->get_selection_to_column();
|
||||
int cursor_pos = text_editor->get_caret_column();
|
||||
int col_to = text_editor->get_selection_to_column(c);
|
||||
int cursor_pos = text_editor->get_caret_column(c);
|
||||
|
||||
// Check if all lines in the selected block are commented.
|
||||
bool is_commented = true;
|
||||
|
@ -1460,29 +1641,29 @@ void CodeTextEditor::toggle_inline_comment(const String &delimiter) {
|
|||
|
||||
// Adjust selection & cursor position.
|
||||
int offset = (is_commented ? -1 : 1) * delimiter.length();
|
||||
int col_from = text_editor->get_selection_from_column() > 0 ? text_editor->get_selection_from_column() + offset : 0;
|
||||
int col_from = text_editor->get_selection_from_column(c) > 0 ? text_editor->get_selection_from_column(c) + offset : 0;
|
||||
|
||||
if (is_commented && text_editor->get_caret_column() == text_editor->get_line(text_editor->get_caret_line()).length() + 1) {
|
||||
if (is_commented && text_editor->get_caret_column(c) == text_editor->get_line(text_editor->get_caret_line(c)).length() + 1) {
|
||||
cursor_pos += 1;
|
||||
}
|
||||
|
||||
if (text_editor->get_selection_to_column() != 0 && col_to != text_editor->get_line(text_editor->get_selection_to_line()).length() + 1) {
|
||||
if (text_editor->get_selection_to_column(c) != 0 && col_to != text_editor->get_line(text_editor->get_selection_to_line(c)).length() + 1) {
|
||||
col_to += offset;
|
||||
}
|
||||
|
||||
if (text_editor->get_caret_column() != 0) {
|
||||
if (text_editor->get_caret_column(c) != 0) {
|
||||
cursor_pos += offset;
|
||||
}
|
||||
|
||||
text_editor->select(begin, col_from, text_editor->get_selection_to_line(), col_to);
|
||||
text_editor->set_caret_column(cursor_pos);
|
||||
text_editor->select(begin, col_from, text_editor->get_selection_to_line(c), col_to, c);
|
||||
text_editor->set_caret_column(cursor_pos, c == 0, c);
|
||||
|
||||
} else {
|
||||
int begin = text_editor->get_caret_line();
|
||||
int begin = text_editor->get_caret_line(c);
|
||||
String line_text = text_editor->get_line(begin);
|
||||
int delimiter_length = delimiter.length();
|
||||
|
||||
int col = text_editor->get_caret_column();
|
||||
int col = text_editor->get_caret_column(c);
|
||||
if (line_text.begins_with(delimiter)) {
|
||||
line_text = line_text.substr(delimiter_length, line_text.length());
|
||||
col -= delimiter_length;
|
||||
|
@ -1492,19 +1673,23 @@ void CodeTextEditor::toggle_inline_comment(const String &delimiter) {
|
|||
}
|
||||
|
||||
text_editor->set_line(begin, line_text);
|
||||
text_editor->set_caret_column(col);
|
||||
text_editor->set_caret_column(col, c == 0, c);
|
||||
}
|
||||
}
|
||||
text_editor->merge_overlapping_carets();
|
||||
text_editor->end_complex_operation();
|
||||
text_editor->queue_redraw();
|
||||
}
|
||||
|
||||
void CodeTextEditor::goto_line(int p_line) {
|
||||
text_editor->remove_secondary_carets();
|
||||
text_editor->deselect();
|
||||
text_editor->unfold_line(p_line);
|
||||
text_editor->call_deferred(SNAME("set_caret_line"), p_line);
|
||||
}
|
||||
|
||||
void CodeTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
|
||||
text_editor->remove_secondary_carets();
|
||||
text_editor->unfold_line(p_line);
|
||||
text_editor->call_deferred(SNAME("set_caret_line"), p_line);
|
||||
text_editor->call_deferred(SNAME("set_caret_column"), p_begin);
|
||||
|
@ -1617,6 +1802,7 @@ void CodeTextEditor::goto_error() {
|
|||
if (text_editor->get_line_count() != error_line) {
|
||||
text_editor->unfold_line(error_line);
|
||||
}
|
||||
text_editor->remove_secondary_carets();
|
||||
text_editor->set_caret_line(error_line);
|
||||
text_editor->set_caret_column(error_column);
|
||||
text_editor->center_viewport_to_caret();
|
||||
|
@ -1793,8 +1979,10 @@ void CodeTextEditor::set_warning_count(int p_warning_count) {
|
|||
}
|
||||
|
||||
void CodeTextEditor::toggle_bookmark() {
|
||||
int line = text_editor->get_caret_line();
|
||||
for (int i = 0; i < text_editor->get_caret_count(); i++) {
|
||||
int line = text_editor->get_caret_line(i);
|
||||
text_editor->set_line_as_bookmarked(line, !text_editor->is_line_bookmarked(line));
|
||||
}
|
||||
}
|
||||
|
||||
void CodeTextEditor::goto_next_bookmark() {
|
||||
|
@ -1803,6 +1991,7 @@ void CodeTextEditor::goto_next_bookmark() {
|
|||
return;
|
||||
}
|
||||
|
||||
text_editor->remove_secondary_carets();
|
||||
int line = text_editor->get_caret_line();
|
||||
if (line >= (int)bmarks[bmarks.size() - 1]) {
|
||||
text_editor->unfold_line(bmarks[0]);
|
||||
|
@ -1827,6 +2016,7 @@ void CodeTextEditor::goto_prev_bookmark() {
|
|||
return;
|
||||
}
|
||||
|
||||
text_editor->remove_secondary_carets();
|
||||
int line = text_editor->get_caret_line();
|
||||
if (line <= (int)bmarks[0]) {
|
||||
text_editor->unfold_line(bmarks[bmarks.size() - 1]);
|
||||
|
|
|
@ -197,7 +197,7 @@ class CodeTextEditor : public VBoxContainer {
|
|||
|
||||
void _update_status_bar_theme();
|
||||
|
||||
void _delete_line(int p_line);
|
||||
void _delete_line(int p_line, int p_caret);
|
||||
void _toggle_scripts_pressed();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -267,6 +267,7 @@ void ScriptTextEditor::_warning_clicked(Variant p_line) {
|
|||
|
||||
void ScriptTextEditor::_error_clicked(Variant p_line) {
|
||||
if (p_line.get_type() == Variant::INT) {
|
||||
code_editor->get_text_editor()->remove_secondary_carets();
|
||||
code_editor->get_text_editor()->set_caret_line(p_line.operator int64_t());
|
||||
}
|
||||
}
|
||||
|
@ -295,6 +296,7 @@ void ScriptTextEditor::reload_text() {
|
|||
void ScriptTextEditor::add_callback(const String &p_function, PackedStringArray p_args) {
|
||||
String code = code_editor->get_text_editor()->get_text();
|
||||
int pos = script->get_language()->find_function(p_function, code);
|
||||
code_editor->get_text_editor()->remove_secondary_carets();
|
||||
if (pos == -1) {
|
||||
//does not exist
|
||||
code_editor->get_text_editor()->deselect();
|
||||
|
@ -1367,6 +1369,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
|
|||
return;
|
||||
}
|
||||
|
||||
tx->remove_secondary_carets();
|
||||
int line = tx->get_caret_line();
|
||||
|
||||
// wrap around
|
||||
|
@ -1393,6 +1396,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
|
|||
return;
|
||||
}
|
||||
|
||||
tx->remove_secondary_carets();
|
||||
int line = tx->get_caret_line();
|
||||
// wrap around
|
||||
if (line <= (int)bpoints[0]) {
|
||||
|
@ -1413,21 +1417,21 @@ void ScriptTextEditor::_edit_option(int p_op) {
|
|||
|
||||
} break;
|
||||
case HELP_CONTEXTUAL: {
|
||||
String text = tx->get_selected_text();
|
||||
String text = tx->get_selected_text(0);
|
||||
if (text.is_empty()) {
|
||||
text = tx->get_word_under_caret();
|
||||
text = tx->get_word_under_caret(0);
|
||||
}
|
||||
if (!text.is_empty()) {
|
||||
emit_signal(SNAME("request_help"), text);
|
||||
}
|
||||
} break;
|
||||
case LOOKUP_SYMBOL: {
|
||||
String text = tx->get_word_under_caret();
|
||||
String text = tx->get_word_under_caret(0);
|
||||
if (text.is_empty()) {
|
||||
text = tx->get_selected_text();
|
||||
text = tx->get_selected_text(0);
|
||||
}
|
||||
if (!text.is_empty()) {
|
||||
_lookup_symbol(text, tx->get_caret_line(), tx->get_caret_column());
|
||||
_lookup_symbol(text, tx->get_caret_line(0), tx->get_caret_column(0));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
@ -1605,6 +1609,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
|
|||
int col = pos.x;
|
||||
|
||||
if (d.has("type") && String(d["type"]) == "resource") {
|
||||
te->remove_secondary_carets();
|
||||
Ref<Resource> res = d["resource"];
|
||||
if (!res.is_valid()) {
|
||||
return;
|
||||
|
@ -1622,6 +1627,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
|
|||
}
|
||||
|
||||
if (d.has("type") && (String(d["type"]) == "files" || String(d["type"]) == "files_and_dirs")) {
|
||||
te->remove_secondary_carets();
|
||||
Array files = d["files"];
|
||||
|
||||
String text_to_drop;
|
||||
|
@ -1645,6 +1651,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
|
|||
}
|
||||
|
||||
if (d.has("type") && String(d["type"]) == "nodes") {
|
||||
te->remove_secondary_carets();
|
||||
Node *scene_root = get_tree()->get_edited_scene_root();
|
||||
if (!scene_root) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Can't drop nodes without an open scene."));
|
||||
|
@ -1729,6 +1736,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
|
|||
}
|
||||
|
||||
if (d.has("type") && String(d["type"]) == "obj_property") {
|
||||
te->remove_secondary_carets();
|
||||
const String text_to_drop = String(d["property"]).c_escape().quote(quote_style);
|
||||
|
||||
te->set_caret_line(row);
|
||||
|
@ -1749,8 +1757,8 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
|
|||
local_pos = mb->get_global_position() - tx->get_global_position();
|
||||
create_menu = true;
|
||||
} else if (k.is_valid() && k->is_action("ui_menu", true)) {
|
||||
tx->adjust_viewport_to_caret();
|
||||
local_pos = tx->get_caret_draw_pos();
|
||||
tx->adjust_viewport_to_caret(0);
|
||||
local_pos = tx->get_caret_draw_pos(0);
|
||||
create_menu = true;
|
||||
}
|
||||
|
||||
|
@ -1761,6 +1769,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
|
|||
|
||||
tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/behavior/navigation/move_caret_on_right_click"));
|
||||
if (tx->is_move_caret_on_right_click_enabled()) {
|
||||
tx->remove_secondary_carets();
|
||||
if (tx->has_selection()) {
|
||||
int from_line = tx->get_selection_from_line();
|
||||
int to_line = tx->get_selection_to_line();
|
||||
|
@ -1780,10 +1789,10 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
|
|||
|
||||
String word_at_pos = tx->get_word_at_pos(local_pos);
|
||||
if (word_at_pos.is_empty()) {
|
||||
word_at_pos = tx->get_word_under_caret();
|
||||
word_at_pos = tx->get_word_under_caret(0);
|
||||
}
|
||||
if (word_at_pos.is_empty()) {
|
||||
word_at_pos = tx->get_selected_text();
|
||||
word_at_pos = tx->get_selected_text(0);
|
||||
}
|
||||
|
||||
bool has_color = (word_at_pos == "Color");
|
||||
|
|
|
@ -445,6 +445,7 @@ void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
|
|||
bool is_folded = tx->is_line_folded(row);
|
||||
|
||||
if (tx->is_move_caret_on_right_click_enabled()) {
|
||||
tx->remove_secondary_carets();
|
||||
if (tx->has_selection()) {
|
||||
int from_line = tx->get_selection_from_line();
|
||||
int to_line = tx->get_selection_to_line();
|
||||
|
@ -471,9 +472,9 @@ void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
|
|||
Ref<InputEventKey> k = ev;
|
||||
if (k.is_valid() && k->is_pressed() && k->is_action("ui_menu", true)) {
|
||||
CodeEdit *tx = code_editor->get_text_editor();
|
||||
int line = tx->get_caret_line();
|
||||
tx->adjust_viewport_to_caret();
|
||||
_make_context_menu(tx->has_selection(), tx->can_fold_line(line), tx->is_line_folded(line), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->get_caret_draw_pos()));
|
||||
int line = tx->get_caret_line(0);
|
||||
tx->adjust_viewport_to_caret(0);
|
||||
_make_context_menu(tx->has_selection(0), tx->can_fold_line(line), tx->is_line_folded(line), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->get_caret_draw_pos(0)));
|
||||
context_menu->grab_focus();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -958,6 +958,7 @@ void TextShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
|
|||
tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/behavior/navigation/move_caret_on_right_click"));
|
||||
|
||||
if (tx->is_move_caret_on_right_click_enabled()) {
|
||||
tx->remove_secondary_carets();
|
||||
if (tx->has_selection()) {
|
||||
int from_line = tx->get_selection_from_line();
|
||||
int to_line = tx->get_selection_to_line();
|
||||
|
|
|
@ -609,38 +609,42 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
|
|||
/* Text manipulation */
|
||||
|
||||
// Overridable actions
|
||||
void CodeEdit::_handle_unicode_input_internal(const uint32_t p_unicode) {
|
||||
bool had_selection = has_selection();
|
||||
String selection_text = (had_selection ? get_selected_text() : "");
|
||||
void CodeEdit::_handle_unicode_input_internal(const uint32_t p_unicode, int p_caret) {
|
||||
start_action(EditAction::ACTION_TYPING);
|
||||
Vector<int> caret_edit_order = get_caret_index_edit_order();
|
||||
for (const int &i : caret_edit_order) {
|
||||
if (p_caret != -1 && p_caret != i) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool had_selection = has_selection(i);
|
||||
String selection_text = (had_selection ? get_selected_text(i) : "");
|
||||
|
||||
if (had_selection) {
|
||||
begin_complex_operation();
|
||||
delete_selection();
|
||||
delete_selection(i);
|
||||
}
|
||||
|
||||
// Remove the old character if in overtype mode and no selection.
|
||||
if (is_overtype_mode_enabled() && !had_selection) {
|
||||
begin_complex_operation();
|
||||
|
||||
/* Make sure we don't try and remove empty space. */
|
||||
if (get_caret_column() < get_line(get_caret_line()).length()) {
|
||||
remove_text(get_caret_line(), get_caret_column(), get_caret_line(), get_caret_column() + 1);
|
||||
// Make sure we don't try and remove empty space.
|
||||
if (get_caret_column(i) < get_line(get_caret_line(i)).length()) {
|
||||
remove_text(get_caret_line(i), get_caret_column(i), get_caret_line(i), get_caret_column(i) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
const char32_t chr[2] = { (char32_t)p_unicode, 0 };
|
||||
|
||||
if (auto_brace_completion_enabled) {
|
||||
int cl = get_caret_line();
|
||||
int cc = get_caret_column();
|
||||
int cl = get_caret_line(i);
|
||||
int cc = get_caret_column(i);
|
||||
|
||||
if (had_selection) {
|
||||
insert_text_at_caret(chr);
|
||||
insert_text_at_caret(chr, i);
|
||||
|
||||
String close_key = get_auto_brace_completion_close_key(chr);
|
||||
if (!close_key.is_empty()) {
|
||||
insert_text_at_caret(selection_text + close_key);
|
||||
set_caret_column(get_caret_column() - 1);
|
||||
insert_text_at_caret(selection_text + close_key, i);
|
||||
set_caret_column(get_caret_column(i) - 1, i == 0, i);
|
||||
}
|
||||
} else {
|
||||
int caret_move_offset = 1;
|
||||
|
@ -648,33 +652,31 @@ void CodeEdit::_handle_unicode_input_internal(const uint32_t p_unicode) {
|
|||
int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1;
|
||||
|
||||
if (has_string_delimiter(chr) && cc > 0 && !is_symbol(get_line(cl)[cc - 1]) && post_brace_pair == -1) {
|
||||
insert_text_at_caret(chr);
|
||||
insert_text_at_caret(chr, i);
|
||||
} else if (cc < get_line(cl).length() && !is_symbol(get_line(cl)[cc])) {
|
||||
insert_text_at_caret(chr);
|
||||
insert_text_at_caret(chr, i);
|
||||
} else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) {
|
||||
caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length();
|
||||
} else if (is_in_comment(cl, cc) != -1 || (is_in_string(cl, cc) != -1 && has_string_delimiter(chr))) {
|
||||
insert_text_at_caret(chr);
|
||||
insert_text_at_caret(chr, i);
|
||||
} else {
|
||||
insert_text_at_caret(chr);
|
||||
insert_text_at_caret(chr, i);
|
||||
|
||||
int pre_brace_pair = _get_auto_brace_pair_open_at_pos(cl, cc + 1);
|
||||
if (pre_brace_pair != -1) {
|
||||
insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key);
|
||||
insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key, i);
|
||||
}
|
||||
}
|
||||
set_caret_column(cc + caret_move_offset);
|
||||
set_caret_column(cc + caret_move_offset, i == 0, i);
|
||||
}
|
||||
} else {
|
||||
insert_text_at_caret(chr);
|
||||
insert_text_at_caret(chr, i);
|
||||
}
|
||||
|
||||
if ((is_overtype_mode_enabled() && !had_selection) || (had_selection)) {
|
||||
end_complex_operation();
|
||||
}
|
||||
end_action();
|
||||
}
|
||||
|
||||
void CodeEdit::_backspace_internal() {
|
||||
void CodeEdit::_backspace_internal(int p_caret) {
|
||||
if (!is_editable()) {
|
||||
return;
|
||||
}
|
||||
|
@ -684,15 +686,22 @@ void CodeEdit::_backspace_internal() {
|
|||
return;
|
||||
}
|
||||
|
||||
int cc = get_caret_column();
|
||||
int cl = get_caret_line();
|
||||
begin_complex_operation();
|
||||
Vector<int> caret_edit_order = get_caret_index_edit_order();
|
||||
for (const int &i : caret_edit_order) {
|
||||
if (p_caret != -1 && p_caret != i) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int cc = get_caret_column(i);
|
||||
int cl = get_caret_line(i);
|
||||
|
||||
if (cc == 0 && cl == 0) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cl > 0 && _is_line_hidden(cl - 1)) {
|
||||
unfold_line(get_caret_line() - 1);
|
||||
unfold_line(get_caret_line(i) - 1);
|
||||
}
|
||||
|
||||
int prev_line = cc ? cl : cl - 1;
|
||||
|
@ -710,9 +719,11 @@ void CodeEdit::_backspace_internal() {
|
|||
} else {
|
||||
remove_text(prev_line, prev_column, cl, cc);
|
||||
}
|
||||
set_caret_line(prev_line, false, true);
|
||||
set_caret_column(prev_column);
|
||||
return;
|
||||
set_caret_line(prev_line, false, true, 0, i);
|
||||
set_caret_column(prev_column, i == 0, i);
|
||||
|
||||
adjust_carets_after_edit(i, prev_line, prev_column, cl, cc + auto_brace_completion_pairs[idx].close_key.length());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -727,8 +738,13 @@ void CodeEdit::_backspace_internal() {
|
|||
|
||||
remove_text(prev_line, prev_column, cl, cc);
|
||||
|
||||
set_caret_line(prev_line, false, true);
|
||||
set_caret_column(prev_column);
|
||||
set_caret_line(prev_line, false, true, 0, i);
|
||||
set_caret_column(prev_column, i == 0, i);
|
||||
|
||||
adjust_carets_after_edit(i, prev_line, prev_column, cl, cc);
|
||||
}
|
||||
merge_overlapping_carets();
|
||||
end_complex_operation();
|
||||
}
|
||||
|
||||
/* Indent management */
|
||||
|
@ -803,10 +819,15 @@ void CodeEdit::do_indent() {
|
|||
return;
|
||||
}
|
||||
|
||||
int spaces_to_add = _calculate_spaces_till_next_right_indent(get_caret_column());
|
||||
begin_complex_operation();
|
||||
Vector<int> caret_edit_order = get_caret_index_edit_order();
|
||||
for (const int &i : caret_edit_order) {
|
||||
int spaces_to_add = _calculate_spaces_till_next_right_indent(get_caret_column(i));
|
||||
if (spaces_to_add > 0) {
|
||||
insert_text_at_caret(String(" ").repeat(spaces_to_add));
|
||||
insert_text_at_caret(String(" ").repeat(spaces_to_add), i);
|
||||
}
|
||||
}
|
||||
end_complex_operation();
|
||||
}
|
||||
|
||||
void CodeEdit::indent_lines() {
|
||||
|
@ -815,19 +836,20 @@ void CodeEdit::indent_lines() {
|
|||
}
|
||||
|
||||
begin_complex_operation();
|
||||
|
||||
/* This value informs us by how much we changed selection position by indenting right. */
|
||||
/* Default is 1 for tab indentation. */
|
||||
Vector<int> caret_edit_order = get_caret_index_edit_order();
|
||||
for (const int &c : caret_edit_order) {
|
||||
// This value informs us by how much we changed selection position by indenting right.
|
||||
// Default is 1 for tab indentation.
|
||||
int selection_offset = 1;
|
||||
|
||||
int start_line = get_caret_line();
|
||||
int start_line = get_caret_line(c);
|
||||
int end_line = start_line;
|
||||
if (has_selection()) {
|
||||
start_line = get_selection_from_line();
|
||||
end_line = get_selection_to_line();
|
||||
if (has_selection(c)) {
|
||||
start_line = get_selection_from_line(c);
|
||||
end_line = get_selection_to_line(c);
|
||||
|
||||
/* Ignore the last line if the selection is not past the first column. */
|
||||
if (get_selection_to_column() == 0) {
|
||||
// Ignore the last line if the selection is not past the first column.
|
||||
if (get_selection_to_column(c) == 0) {
|
||||
selection_offset = 0;
|
||||
end_line--;
|
||||
}
|
||||
|
@ -835,7 +857,7 @@ void CodeEdit::indent_lines() {
|
|||
|
||||
for (int i = start_line; i <= end_line; i++) {
|
||||
const String line_text = get_line(i);
|
||||
if (line_text.size() == 0 && has_selection()) {
|
||||
if (line_text.size() == 0 && has_selection(c)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -844,19 +866,19 @@ void CodeEdit::indent_lines() {
|
|||
continue;
|
||||
}
|
||||
|
||||
/* We don't really care where selection is - we just need to know indentation level at the beginning of the line. */
|
||||
/* Since we will add this many spaces, we want to move the whole selection and caret by this much. */
|
||||
// We don't really care where selection is - we just need to know indentation level at the beginning of the line.
|
||||
// Since we will add this many spaces, we want to move the whole selection and caret by this much.
|
||||
int spaces_to_add = _calculate_spaces_till_next_right_indent(get_first_non_whitespace_column(i));
|
||||
set_line(i, String(" ").repeat(spaces_to_add) + line_text);
|
||||
selection_offset = spaces_to_add;
|
||||
}
|
||||
|
||||
/* Fix selection and caret being off after shifting selection right.*/
|
||||
if (has_selection()) {
|
||||
select(start_line, get_selection_from_column() + selection_offset, get_selection_to_line(), get_selection_to_column() + selection_offset);
|
||||
// Fix selection and caret being off after shifting selection right.
|
||||
if (has_selection(c)) {
|
||||
select(start_line, get_selection_from_column(c) + selection_offset, get_selection_to_line(c), get_selection_to_column(c) + selection_offset, c);
|
||||
}
|
||||
set_caret_column(get_caret_column(c) + selection_offset, false, c);
|
||||
}
|
||||
set_caret_column(get_caret_column() + selection_offset, false);
|
||||
|
||||
end_complex_operation();
|
||||
}
|
||||
|
||||
|
@ -872,17 +894,21 @@ void CodeEdit::do_unindent() {
|
|||
return;
|
||||
}
|
||||
|
||||
int cl = get_caret_line();
|
||||
begin_complex_operation();
|
||||
Vector<int> caret_edit_order = get_caret_index_edit_order();
|
||||
for (const int &c : caret_edit_order) {
|
||||
int cl = get_caret_line(c);
|
||||
const String &line = get_line(cl);
|
||||
|
||||
if (line[cc - 1] == '\t') {
|
||||
remove_text(cl, cc - 1, cl, cc);
|
||||
set_caret_column(MAX(0, cc - 1));
|
||||
return;
|
||||
set_caret_column(MAX(0, cc - 1), c == 0, c);
|
||||
adjust_carets_after_edit(c, cl, cc, cl, cc - 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line[cc - 1] != ' ') {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc);
|
||||
|
@ -894,8 +920,10 @@ void CodeEdit::do_unindent() {
|
|||
}
|
||||
}
|
||||
remove_text(cl, cc - spaces_to_remove, cl, cc);
|
||||
set_caret_column(MAX(0, cc - spaces_to_remove));
|
||||
set_caret_column(MAX(0, cc - spaces_to_remove), c == 0, c);
|
||||
}
|
||||
}
|
||||
end_complex_operation();
|
||||
}
|
||||
|
||||
void CodeEdit::unindent_lines() {
|
||||
|
@ -905,21 +933,23 @@ void CodeEdit::unindent_lines() {
|
|||
|
||||
begin_complex_operation();
|
||||
|
||||
/* Moving caret and selection after unindenting can get tricky because */
|
||||
/* changing content of line can move caret and selection on its own (if new line ends before previous position of either), */
|
||||
/* therefore we just remember initial values and at the end of the operation offset them by number of removed characters. */
|
||||
Vector<int> caret_edit_order = get_caret_index_edit_order();
|
||||
for (const int &c : caret_edit_order) {
|
||||
// Moving caret and selection after unindenting can get tricky because
|
||||
// changing content of line can move caret and selection on its own (if new line ends before previous position of either)
|
||||
// therefore we just remember initial values and at the end of the operation offset them by number of removed characters.
|
||||
int removed_characters = 0;
|
||||
int initial_selection_end_column = 0;
|
||||
int initial_cursor_column = get_caret_column();
|
||||
int initial_cursor_column = get_caret_column(c);
|
||||
|
||||
int start_line = get_caret_line();
|
||||
int start_line = get_caret_line(c);
|
||||
int end_line = start_line;
|
||||
if (has_selection()) {
|
||||
start_line = get_selection_from_line();
|
||||
end_line = get_selection_to_line();
|
||||
if (has_selection(c)) {
|
||||
start_line = get_selection_from_line(c);
|
||||
end_line = get_selection_to_line(c);
|
||||
|
||||
/* Ignore the last line if the selection is not past the first column. */
|
||||
initial_selection_end_column = get_selection_to_column();
|
||||
// Ignore the last line if the selection is not past the first column.
|
||||
initial_selection_end_column = get_selection_to_column(c);
|
||||
if (initial_selection_end_column == 0) {
|
||||
end_line--;
|
||||
}
|
||||
|
@ -943,9 +973,9 @@ void CodeEdit::unindent_lines() {
|
|||
}
|
||||
|
||||
if (line_text.begins_with(" ")) {
|
||||
/* When unindenting we aim to remove spaces before line that has selection no matter what is selected, */
|
||||
/* Here we remove only enough spaces to align text to nearest full multiple of indentation_size. */
|
||||
/* In case where selection begins at the start of indentation_size multiple we remove whole indentation level. */
|
||||
// When unindenting we aim to remove spaces before line that has selection no matter what is selected.
|
||||
// Here we remove only enough spaces to align text to nearest full multiple of indentation_size.
|
||||
// In case where selection begins at the start of indentation_size multiple we remove whole indentation level.
|
||||
int spaces_to_remove = _calculate_spaces_till_next_left_indent(get_first_non_whitespace_column(i));
|
||||
line_text = line_text.substr(spaces_to_remove, line_text.length());
|
||||
|
||||
|
@ -957,19 +987,19 @@ void CodeEdit::unindent_lines() {
|
|||
}
|
||||
}
|
||||
|
||||
if (has_selection()) {
|
||||
/* Fix selection being off by one on the first line. */
|
||||
if (has_selection(c)) {
|
||||
// Fix selection being off by one on the first line.
|
||||
if (first_line_edited) {
|
||||
select(get_selection_from_line(), get_selection_from_column() - removed_characters, get_selection_to_line(), initial_selection_end_column);
|
||||
select(get_selection_from_line(c), get_selection_from_column(c) - removed_characters, get_selection_to_line(c), initial_selection_end_column, c);
|
||||
}
|
||||
|
||||
/* Fix selection being off by one on the last line. */
|
||||
// Fix selection being off by one on the last line.
|
||||
if (last_line_edited) {
|
||||
select(get_selection_from_line(), get_selection_from_column(), get_selection_to_line(), initial_selection_end_column - removed_characters);
|
||||
select(get_selection_from_line(c), get_selection_from_column(c), get_selection_to_line(c), initial_selection_end_column - removed_characters, c);
|
||||
}
|
||||
}
|
||||
set_caret_column(initial_cursor_column - removed_characters, false);
|
||||
|
||||
set_caret_column(initial_cursor_column - removed_characters, false, c);
|
||||
}
|
||||
end_complex_operation();
|
||||
}
|
||||
|
||||
|
@ -990,15 +1020,18 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
|
|||
return;
|
||||
}
|
||||
|
||||
/* When not splitting the line, we need to factor in indentation from the end of the current line. */
|
||||
const int cc = p_split_current_line ? get_caret_column() : get_line(get_caret_line()).length();
|
||||
const int cl = get_caret_line();
|
||||
begin_complex_operation();
|
||||
Vector<int> caret_edit_order = get_caret_index_edit_order();
|
||||
for (const int &i : caret_edit_order) {
|
||||
// When not splitting the line, we need to factor in indentation from the end of the current line.
|
||||
const int cc = p_split_current_line ? get_caret_column(i) : get_line(get_caret_line(i)).length();
|
||||
const int cl = get_caret_line(i);
|
||||
|
||||
const String line = get_line(cl);
|
||||
|
||||
String ins = "\n";
|
||||
|
||||
/* Append current indentation. */
|
||||
// Append current indentation.
|
||||
int space_count = 0;
|
||||
int line_col = 0;
|
||||
for (; line_col < cc; line_col++) {
|
||||
|
@ -1024,9 +1057,9 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
|
|||
unfold_line(cl);
|
||||
}
|
||||
|
||||
/* Indent once again if the previous line needs it, ie ':'. */
|
||||
/* Then add an addition new line for any closing pairs aka '()'. */
|
||||
/* Skip this in comments or if we are going above. */
|
||||
// Indent once again if the previous line needs it, ie ':'.
|
||||
// Then add an addition new line for any closing pairs aka '()'.
|
||||
// Skip this in comments or if we are going above.
|
||||
bool brace_indent = false;
|
||||
if (auto_indent && !p_above && cc > 0 && is_in_comment(cl) == -1) {
|
||||
bool should_indent = false;
|
||||
|
@ -1040,8 +1073,8 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
|
|||
continue;
|
||||
}
|
||||
|
||||
/* Make sure this is the last char, trailing whitespace or comments are okay. */
|
||||
/* Increment column for comments because the delimiter (#) should be ignored. */
|
||||
// Make sure this is the last char, trailing whitespace or comments are okay.
|
||||
// Increment column for comments because the delimiter (#) should be ignored.
|
||||
if (should_indent && (!is_whitespace(c) && is_in_comment(cl, line_col + 1) == -1)) {
|
||||
should_indent = false;
|
||||
}
|
||||
|
@ -1052,7 +1085,7 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
|
|||
|
||||
String closing_pair = get_auto_brace_completion_close_key(String::chr(indent_char));
|
||||
if (!closing_pair.is_empty() && line.find(closing_pair, cc) == cc) {
|
||||
/* No need to move the brace below if we are not taking the text with us. */
|
||||
// No need to move the brace below if we are not taking the text with us.
|
||||
if (p_split_current_line) {
|
||||
brace_indent = true;
|
||||
ins += "\n" + ins.substr(indent_text.size(), ins.length() - 2);
|
||||
|
@ -1064,32 +1097,31 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
|
|||
}
|
||||
}
|
||||
|
||||
begin_complex_operation();
|
||||
|
||||
bool first_line = false;
|
||||
if (!p_split_current_line) {
|
||||
deselect();
|
||||
deselect(i);
|
||||
|
||||
if (p_above) {
|
||||
if (cl > 0) {
|
||||
set_caret_line(cl - 1, false);
|
||||
set_caret_column(get_line(get_caret_line()).length());
|
||||
set_caret_line(cl - 1, false, true, 0, i);
|
||||
set_caret_column(get_line(get_caret_line(i)).length(), i == 0, i);
|
||||
} else {
|
||||
set_caret_column(0);
|
||||
set_caret_column(0, i == 0, i);
|
||||
first_line = true;
|
||||
}
|
||||
} else {
|
||||
set_caret_column(line.length());
|
||||
set_caret_column(line.length(), i == 0, i);
|
||||
}
|
||||
}
|
||||
|
||||
insert_text_at_caret(ins);
|
||||
insert_text_at_caret(ins, i);
|
||||
|
||||
if (first_line) {
|
||||
set_caret_line(0);
|
||||
set_caret_line(0, i == 0, true, 0, i);
|
||||
} else if (brace_indent) {
|
||||
set_caret_line(get_caret_line() - 1, false);
|
||||
set_caret_column(get_line(get_caret_line()).length());
|
||||
set_caret_line(get_caret_line(i) - 1, false, true, 0, i);
|
||||
set_caret_column(get_line(get_caret_line(i)).length(), i == 0, i);
|
||||
}
|
||||
}
|
||||
|
||||
end_complex_operation();
|
||||
|
@ -1522,22 +1554,26 @@ void CodeEdit::fold_line(int p_line) {
|
|||
_set_line_as_hidden(i, true);
|
||||
}
|
||||
|
||||
/* Fix selection. */
|
||||
if (has_selection()) {
|
||||
if (_is_line_hidden(get_selection_from_line()) && _is_line_hidden(get_selection_to_line())) {
|
||||
deselect();
|
||||
} else if (_is_line_hidden(get_selection_from_line())) {
|
||||
select(p_line, 9999, get_selection_to_line(), get_selection_to_column());
|
||||
} else if (_is_line_hidden(get_selection_to_line())) {
|
||||
select(get_selection_from_line(), get_selection_from_column(), p_line, 9999);
|
||||
for (int i = 0; i < get_caret_count(); i++) {
|
||||
// Fix selection.
|
||||
if (has_selection(i)) {
|
||||
if (_is_line_hidden(get_selection_from_line(i)) && _is_line_hidden(get_selection_to_line(i))) {
|
||||
deselect(i);
|
||||
} else if (_is_line_hidden(get_selection_from_line(i))) {
|
||||
select(p_line, 9999, get_selection_to_line(i), get_selection_to_column(i), i);
|
||||
} else if (_is_line_hidden(get_selection_to_line(i))) {
|
||||
select(get_selection_from_line(i), get_selection_from_column(i), p_line, 9999, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset caret. */
|
||||
if (_is_line_hidden(get_caret_line())) {
|
||||
set_caret_line(p_line, false, false);
|
||||
set_caret_column(get_line(p_line).length(), false);
|
||||
// Reset caret.
|
||||
if (_is_line_hidden(get_caret_line(i))) {
|
||||
set_caret_line(p_line, false, false, 0, i);
|
||||
set_caret_column(get_line(p_line).length(), false, i);
|
||||
}
|
||||
}
|
||||
|
||||
merge_overlapping_carets();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
@ -1950,17 +1986,19 @@ void CodeEdit::confirm_code_completion(bool p_replace) {
|
|||
return;
|
||||
}
|
||||
|
||||
char32_t caret_last_completion_char;
|
||||
begin_complex_operation();
|
||||
|
||||
int caret_line = get_caret_line();
|
||||
Vector<int> caret_edit_order = get_caret_index_edit_order();
|
||||
for (const int &i : caret_edit_order) {
|
||||
int caret_line = get_caret_line(i);
|
||||
|
||||
const String &insert_text = code_completion_options[code_completion_current_selected].insert_text;
|
||||
const String &display_text = code_completion_options[code_completion_current_selected].display;
|
||||
|
||||
if (p_replace) {
|
||||
/* Find end of current section */
|
||||
// Find end of current section.
|
||||
const String line = get_line(caret_line);
|
||||
int caret_col = get_caret_column();
|
||||
int caret_col = get_caret_column(i);
|
||||
int caret_remove_line = caret_line;
|
||||
|
||||
bool merge_text = true;
|
||||
|
@ -1982,14 +2020,15 @@ void CodeEdit::confirm_code_completion(bool p_replace) {
|
|||
}
|
||||
}
|
||||
|
||||
/* Replace. */
|
||||
remove_text(caret_line, get_caret_column() - code_completion_base.length(), caret_remove_line, caret_col);
|
||||
set_caret_column(get_caret_column() - code_completion_base.length(), false);
|
||||
insert_text_at_caret(insert_text);
|
||||
// Replace.
|
||||
remove_text(caret_line, get_caret_column(i) - code_completion_base.length(), caret_remove_line, caret_col);
|
||||
adjust_carets_after_edit(i, caret_line, caret_col - code_completion_base.length(), caret_remove_line, caret_col);
|
||||
set_caret_column(get_caret_column(i) - code_completion_base.length(), false, i);
|
||||
insert_text_at_caret(insert_text, i);
|
||||
} else {
|
||||
/* Get first non-matching char. */
|
||||
// Get first non-matching char.
|
||||
const String line = get_line(caret_line);
|
||||
int caret_col = get_caret_column();
|
||||
int caret_col = get_caret_column(i);
|
||||
int matching_chars = code_completion_base.length();
|
||||
for (; matching_chars <= insert_text.length(); matching_chars++) {
|
||||
if (caret_col >= line.length() || line[caret_col] != insert_text[matching_chars]) {
|
||||
|
@ -1998,50 +2037,57 @@ void CodeEdit::confirm_code_completion(bool p_replace) {
|
|||
caret_col++;
|
||||
}
|
||||
|
||||
/* Remove base completion text. */
|
||||
remove_text(caret_line, get_caret_column() - code_completion_base.length(), caret_line, get_caret_column());
|
||||
set_caret_column(get_caret_column() - code_completion_base.length(), false);
|
||||
// Remove base completion text.
|
||||
remove_text(caret_line, get_caret_column(i) - code_completion_base.length(), caret_line, get_caret_column(i));
|
||||
adjust_carets_after_edit(i, caret_line, get_caret_column(i) - code_completion_base.length(), caret_line, get_caret_column(i));
|
||||
set_caret_column(get_caret_column(i) - code_completion_base.length(), false, i);
|
||||
|
||||
/* Merge with text. */
|
||||
insert_text_at_caret(insert_text.substr(0, code_completion_base.length()));
|
||||
set_caret_column(caret_col, false);
|
||||
insert_text_at_caret(insert_text.substr(matching_chars));
|
||||
// Merge with text.
|
||||
insert_text_at_caret(insert_text.substr(0, code_completion_base.length()), i);
|
||||
set_caret_column(caret_col, false, i);
|
||||
insert_text_at_caret(insert_text.substr(matching_chars), i);
|
||||
}
|
||||
|
||||
/* Handle merging of symbols eg strings, brackets. */
|
||||
//* Handle merging of symbols eg strings, brackets.
|
||||
const String line = get_line(caret_line);
|
||||
char32_t next_char = line[get_caret_column()];
|
||||
char32_t next_char = line[get_caret_column(i)];
|
||||
char32_t last_completion_char = insert_text[insert_text.length() - 1];
|
||||
if (i == 0) {
|
||||
caret_last_completion_char = last_completion_char;
|
||||
}
|
||||
char32_t last_completion_char_display = display_text[display_text.length() - 1];
|
||||
|
||||
int pre_brace_pair = get_caret_column() > 0 ? _get_auto_brace_pair_open_at_pos(caret_line, get_caret_column()) : -1;
|
||||
int post_brace_pair = get_caret_column() < get_line(caret_line).length() ? _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column()) : -1;
|
||||
int pre_brace_pair = get_caret_column(i) > 0 ? _get_auto_brace_pair_open_at_pos(caret_line, get_caret_column(i)) : -1;
|
||||
int post_brace_pair = get_caret_column(i) < get_line(caret_line).length() ? _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column(i)) : -1;
|
||||
|
||||
if (post_brace_pair != -1 && (last_completion_char == next_char || last_completion_char_display == next_char)) {
|
||||
remove_text(caret_line, get_caret_column(), caret_line, get_caret_column() + 1);
|
||||
remove_text(caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1);
|
||||
adjust_carets_after_edit(i, caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1);
|
||||
}
|
||||
|
||||
if (pre_brace_pair != -1 && pre_brace_pair != post_brace_pair && (last_completion_char == next_char || last_completion_char_display == next_char)) {
|
||||
remove_text(caret_line, get_caret_column(), caret_line, get_caret_column() + 1);
|
||||
remove_text(caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1);
|
||||
adjust_carets_after_edit(i, caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1);
|
||||
} else if (auto_brace_completion_enabled && pre_brace_pair != -1 && post_brace_pair == -1) {
|
||||
insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key);
|
||||
set_caret_column(get_caret_column() - auto_brace_completion_pairs[pre_brace_pair].close_key.length());
|
||||
insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key, i);
|
||||
set_caret_column(get_caret_column(i) - auto_brace_completion_pairs[pre_brace_pair].close_key.length(), i == 0, i);
|
||||
}
|
||||
|
||||
if (pre_brace_pair == -1 && post_brace_pair == -1 && get_caret_column() > 0 && get_caret_column() < get_line(caret_line).length()) {
|
||||
pre_brace_pair = _get_auto_brace_pair_open_at_pos(caret_line, get_caret_column() + 1);
|
||||
if (pre_brace_pair != -1 && pre_brace_pair == _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column() - 1)) {
|
||||
remove_text(caret_line, get_caret_column() - 2, caret_line, get_caret_column());
|
||||
if (_get_auto_brace_pair_close_at_pos(caret_line, get_caret_column() - 1) != pre_brace_pair) {
|
||||
set_caret_column(get_caret_column() - 1);
|
||||
if (pre_brace_pair == -1 && post_brace_pair == -1 && get_caret_column(i) > 0 && get_caret_column(i) < get_line(caret_line).length()) {
|
||||
pre_brace_pair = _get_auto_brace_pair_open_at_pos(caret_line, get_caret_column(i) + 1);
|
||||
if (pre_brace_pair != -1 && pre_brace_pair == _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column(i) - 1)) {
|
||||
remove_text(caret_line, get_caret_column(i) - 2, caret_line, get_caret_column(i));
|
||||
adjust_carets_after_edit(i, caret_line, get_caret_column(i) - 2, caret_line, get_caret_column(i));
|
||||
if (_get_auto_brace_pair_close_at_pos(caret_line, get_caret_column(i) - 1) != pre_brace_pair) {
|
||||
set_caret_column(get_caret_column(i) - 1, i == 0, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end_complex_operation();
|
||||
|
||||
cancel_code_completion();
|
||||
if (code_completion_prefixes.has(last_completion_char)) {
|
||||
if (code_completion_prefixes.has(caret_last_completion_char)) {
|
||||
request_code_completion();
|
||||
}
|
||||
}
|
||||
|
@ -2399,6 +2445,7 @@ void CodeEdit::_gutter_clicked(int p_line, int p_gutter) {
|
|||
}
|
||||
|
||||
if (p_gutter == line_number_gutter) {
|
||||
remove_secondary_carets();
|
||||
set_selection_mode(TextEdit::SelectionMode::SELECTION_MODE_LINE, p_line, 0);
|
||||
select(p_line, 0, p_line + 1, 0);
|
||||
set_caret_line(p_line + 1);
|
||||
|
|
|
@ -261,8 +261,8 @@ protected:
|
|||
/* Text manipulation */
|
||||
|
||||
// Overridable actions
|
||||
virtual void _handle_unicode_input_internal(const uint32_t p_unicode) override;
|
||||
virtual void _backspace_internal() override;
|
||||
virtual void _handle_unicode_input_internal(const uint32_t p_unicode, int p_caret) override;
|
||||
virtual void _backspace_internal(int p_caret) override;
|
||||
|
||||
GDVIRTUAL1(_confirm_code_completion, bool)
|
||||
GDVIRTUAL1(_request_code_completion, bool)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -42,6 +42,14 @@ class TextEdit : public Control {
|
|||
GDCLASS(TextEdit, Control);
|
||||
|
||||
public:
|
||||
/* Edit Actions. */
|
||||
enum EditAction {
|
||||
ACTION_NONE,
|
||||
ACTION_TYPING,
|
||||
ACTION_BACKSPACE,
|
||||
ACTION_DELETE,
|
||||
};
|
||||
|
||||
/* Caret. */
|
||||
enum CaretType {
|
||||
CARET_TYPE_LINE,
|
||||
|
@ -299,12 +307,15 @@ private:
|
|||
Key _get_menu_action_accelerator(const String &p_action);
|
||||
|
||||
/* Versioning */
|
||||
struct Caret;
|
||||
struct TextOperation {
|
||||
enum Type {
|
||||
TYPE_NONE,
|
||||
TYPE_INSERT,
|
||||
TYPE_REMOVE
|
||||
};
|
||||
Vector<Caret> start_carets;
|
||||
Vector<Caret> end_carets;
|
||||
|
||||
Type type = TYPE_NONE;
|
||||
int from_line = 0;
|
||||
|
@ -321,6 +332,10 @@ private:
|
|||
bool undo_enabled = true;
|
||||
int undo_stack_max_size = 50;
|
||||
|
||||
EditAction current_action = EditAction::ACTION_NONE;
|
||||
bool pending_action_end = false;
|
||||
bool in_action = false;
|
||||
|
||||
int complex_operation_count = 0;
|
||||
bool next_operation_is_complex = false;
|
||||
|
||||
|
@ -361,19 +376,39 @@ private:
|
|||
int _get_char_pos_for_line(int p_px, int p_line, int p_wrap_index = 0) const;
|
||||
|
||||
/* Caret. */
|
||||
struct Selection {
|
||||
bool active = false;
|
||||
bool shiftclick_left = false;
|
||||
|
||||
int selecting_line = 0;
|
||||
int selecting_column = 0;
|
||||
int selected_word_beg = 0;
|
||||
int selected_word_end = 0;
|
||||
int selected_word_origin = 0;
|
||||
|
||||
int from_line = 0;
|
||||
int from_column = 0;
|
||||
int to_line = 0;
|
||||
int to_column = 0;
|
||||
};
|
||||
|
||||
struct Caret {
|
||||
Selection selection;
|
||||
|
||||
Point2 draw_pos;
|
||||
bool visible = false;
|
||||
int last_fit_x = 0;
|
||||
int line = 0;
|
||||
int column = 0;
|
||||
int x_ofs = 0;
|
||||
int line_ofs = 0;
|
||||
int wrap_ofs = 0;
|
||||
} caret;
|
||||
};
|
||||
|
||||
// Vector containing all the carets, index '0' is the "main caret" and should never be removed.
|
||||
Vector<Caret> carets;
|
||||
Vector<int> caret_index_edit_order;
|
||||
|
||||
bool setting_caret_line = false;
|
||||
bool caret_pos_dirty = false;
|
||||
bool caret_index_edit_dirty = true;
|
||||
|
||||
Color caret_color = Color(1, 1, 1);
|
||||
Color caret_background_color = Color(0, 0, 0);
|
||||
|
@ -389,6 +424,8 @@ private:
|
|||
|
||||
bool caret_mid_grapheme_enabled = true;
|
||||
|
||||
bool multi_carets_enabled = true;
|
||||
|
||||
bool drag_action = false;
|
||||
bool drag_caret_force_displayed = false;
|
||||
|
||||
|
@ -397,28 +434,10 @@ private:
|
|||
void _reset_caret_blink_timer();
|
||||
void _toggle_draw_caret();
|
||||
|
||||
int _get_column_x_offset_for_line(int p_char, int p_line) const;
|
||||
int _get_column_x_offset_for_line(int p_char, int p_line, int p_column) const;
|
||||
|
||||
/* Selection. */
|
||||
struct Selection {
|
||||
SelectionMode selecting_mode = SelectionMode::SELECTION_MODE_NONE;
|
||||
int selecting_line = 0;
|
||||
int selecting_column = 0;
|
||||
int selected_word_beg = 0;
|
||||
int selected_word_end = 0;
|
||||
int selected_word_origin = 0;
|
||||
bool selecting_text = false;
|
||||
|
||||
bool active = false;
|
||||
|
||||
int from_line = 0;
|
||||
int from_column = 0;
|
||||
int to_line = 0;
|
||||
int to_column = 0;
|
||||
|
||||
bool shiftclick_left = false;
|
||||
bool drag_attempt = false;
|
||||
} selection;
|
||||
|
||||
bool selecting_enabled = true;
|
||||
bool deselect_on_focus_loss_enabled = true;
|
||||
|
@ -428,6 +447,7 @@ private:
|
|||
Color selection_color = Color(1, 1, 1);
|
||||
bool override_selected_font_color = false;
|
||||
|
||||
bool selection_drag_attempt = false;
|
||||
bool dragging_selection = false;
|
||||
|
||||
Timer *click_select_held = nullptr;
|
||||
|
@ -439,8 +459,8 @@ private:
|
|||
void _update_selection_mode_word();
|
||||
void _update_selection_mode_line();
|
||||
|
||||
void _pre_shift_selection();
|
||||
void _post_shift_selection();
|
||||
void _pre_shift_selection(int p_caret);
|
||||
void _post_shift_selection(int p_caret);
|
||||
|
||||
/* Line wrapping. */
|
||||
LineWrappingMode line_wrapping_mode = LineWrappingMode::LINE_WRAPPING_NONE;
|
||||
|
@ -450,8 +470,6 @@ private:
|
|||
|
||||
void _update_wrap_at_column(bool p_force = false);
|
||||
|
||||
void _update_caret_wrap_offset();
|
||||
|
||||
/* Viewport. */
|
||||
HScrollBar *h_scroll = nullptr;
|
||||
VScrollBar *v_scroll = nullptr;
|
||||
|
@ -466,6 +484,10 @@ private:
|
|||
float v_scroll_speed = 80.0;
|
||||
|
||||
// Scrolling.
|
||||
int first_visible_line = 0;
|
||||
int first_visible_line_wrap_ofs = 0;
|
||||
int first_visible_col = 0;
|
||||
|
||||
bool scrolling = false;
|
||||
bool updating_scrolls = false;
|
||||
|
||||
|
@ -583,6 +605,17 @@ protected:
|
|||
|
||||
/* Internal API for CodeEdit, pending public API. */
|
||||
// brace matching
|
||||
struct BraceMatchingData {
|
||||
int open_match_line = -1;
|
||||
int open_match_column = -1;
|
||||
bool open_matching = false;
|
||||
bool open_mismatch = false;
|
||||
int close_match_line = -1;
|
||||
int close_match_column = -1;
|
||||
bool close_matching = false;
|
||||
bool close_mismatch = false;
|
||||
};
|
||||
|
||||
bool highlight_matching_braces_enabled = false;
|
||||
Color brace_mismatch_color;
|
||||
|
||||
|
@ -607,20 +640,20 @@ protected:
|
|||
/* Text manipulation */
|
||||
|
||||
// Overridable actions
|
||||
virtual void _handle_unicode_input_internal(const uint32_t p_unicode);
|
||||
virtual void _backspace_internal();
|
||||
virtual void _handle_unicode_input_internal(const uint32_t p_unicode, int p_caret);
|
||||
virtual void _backspace_internal(int p_caret);
|
||||
|
||||
virtual void _cut_internal();
|
||||
virtual void _copy_internal();
|
||||
virtual void _paste_internal();
|
||||
virtual void _paste_primary_clipboard_internal();
|
||||
virtual void _cut_internal(int p_caret);
|
||||
virtual void _copy_internal(int p_caret);
|
||||
virtual void _paste_internal(int p_caret);
|
||||
virtual void _paste_primary_clipboard_internal(int p_caret);
|
||||
|
||||
GDVIRTUAL1(_handle_unicode_input, int)
|
||||
GDVIRTUAL0(_backspace)
|
||||
GDVIRTUAL0(_cut)
|
||||
GDVIRTUAL0(_copy)
|
||||
GDVIRTUAL0(_paste)
|
||||
GDVIRTUAL0(_paste_primary_clipboard)
|
||||
GDVIRTUAL2(_handle_unicode_input, int, int)
|
||||
GDVIRTUAL1(_backspace, int)
|
||||
GDVIRTUAL1(_cut, int)
|
||||
GDVIRTUAL1(_copy, int)
|
||||
GDVIRTUAL1(_paste, int)
|
||||
GDVIRTUAL1(_paste_primary_clipboard, int)
|
||||
|
||||
public:
|
||||
/* General overrides. */
|
||||
|
@ -696,7 +729,7 @@ public:
|
|||
void swap_lines(int p_from_line, int p_to_line);
|
||||
|
||||
void insert_line_at(int p_at, const String &p_text);
|
||||
void insert_text_at_caret(const String &p_text);
|
||||
void insert_text_at_caret(const String &p_text, int p_caret = -1);
|
||||
|
||||
void remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column);
|
||||
|
||||
|
@ -705,13 +738,13 @@ public:
|
|||
Point2i get_next_visible_line_index_offset_from(int p_line_from, int p_wrap_index_from, int p_visible_amount) const;
|
||||
|
||||
// Overridable actions
|
||||
void handle_unicode_input(const uint32_t p_unicode);
|
||||
void backspace();
|
||||
void handle_unicode_input(const uint32_t p_unicode, int p_caret = -1);
|
||||
void backspace(int p_caret = -1);
|
||||
|
||||
void cut();
|
||||
void copy();
|
||||
void paste();
|
||||
void paste_primary_clipboard();
|
||||
void cut(int p_caret = -1);
|
||||
void copy(int p_caret = -1);
|
||||
void paste(int p_caret = -1);
|
||||
void paste_primary_clipboard(int p_caret = -1);
|
||||
|
||||
// Context menu.
|
||||
PopupMenu *get_menu() const;
|
||||
|
@ -719,6 +752,10 @@ public:
|
|||
void menu_option(int p_option);
|
||||
|
||||
/* Versioning */
|
||||
void start_action(EditAction p_action);
|
||||
void end_action();
|
||||
EditAction get_current_action() const;
|
||||
|
||||
void begin_complex_operation();
|
||||
void end_complex_operation();
|
||||
|
||||
|
@ -753,7 +790,7 @@ public:
|
|||
int get_minimap_line_at_pos(const Point2i &p_pos) const;
|
||||
|
||||
bool is_dragging_cursor() const;
|
||||
bool is_mouse_over_selection(bool p_edges = true) const;
|
||||
bool is_mouse_over_selection(bool p_edges = true, int p_caret = -1) const;
|
||||
|
||||
/* Caret */
|
||||
void set_caret_type(CaretType p_type);
|
||||
|
@ -771,18 +808,30 @@ public:
|
|||
void set_caret_mid_grapheme_enabled(const bool p_enabled);
|
||||
bool is_caret_mid_grapheme_enabled() const;
|
||||
|
||||
bool is_caret_visible() const;
|
||||
Point2 get_caret_draw_pos() const;
|
||||
void set_multiple_carets_enabled(bool p_enabled);
|
||||
bool is_multiple_carets_enabled() const;
|
||||
|
||||
void set_caret_line(int p_line, bool p_adjust_viewport = true, bool p_can_be_hidden = true, int p_wrap_index = 0);
|
||||
int get_caret_line() const;
|
||||
int add_caret(int p_line, int p_col);
|
||||
void remove_caret(int p_caret);
|
||||
void remove_secondary_carets();
|
||||
void merge_overlapping_carets();
|
||||
int get_caret_count() const;
|
||||
|
||||
void set_caret_column(int p_col, bool p_adjust_viewport = true);
|
||||
int get_caret_column() const;
|
||||
Vector<int> get_caret_index_edit_order();
|
||||
void adjust_carets_after_edit(int p_caret, int p_from_line, int p_from_col, int p_to_line, int p_to_col);
|
||||
|
||||
int get_caret_wrap_index() const;
|
||||
bool is_caret_visible(int p_caret = 0) const;
|
||||
Point2 get_caret_draw_pos(int p_caret = 0) const;
|
||||
|
||||
String get_word_under_caret() const;
|
||||
void set_caret_line(int p_line, bool p_adjust_viewport = true, bool p_can_be_hidden = true, int p_wrap_index = 0, int p_caret = 0);
|
||||
int get_caret_line(int p_caret = 0) const;
|
||||
|
||||
void set_caret_column(int p_col, bool p_adjust_viewport = true, int p_caret = 0);
|
||||
int get_caret_column(int p_caret = 0) const;
|
||||
|
||||
int get_caret_wrap_index(int p_caret = 0) const;
|
||||
|
||||
String get_word_under_caret(int p_caret = -1) const;
|
||||
|
||||
/* Selection. */
|
||||
void set_selecting_enabled(const bool p_enabled);
|
||||
|
@ -797,27 +846,27 @@ public:
|
|||
void set_override_selected_font_color(bool p_override_selected_font_color);
|
||||
bool is_overriding_selected_font_color() const;
|
||||
|
||||
void set_selection_mode(SelectionMode p_mode, int p_line = -1, int p_column = -1);
|
||||
void set_selection_mode(SelectionMode p_mode, int p_line = -1, int p_column = -1, int p_caret = 0);
|
||||
SelectionMode get_selection_mode() const;
|
||||
|
||||
void select_all();
|
||||
void select_word_under_caret();
|
||||
void select(int p_from_line, int p_from_column, int p_to_line, int p_to_column);
|
||||
void select_word_under_caret(int p_caret = -1);
|
||||
void select(int p_from_line, int p_from_column, int p_to_line, int p_to_column, int p_caret = 0);
|
||||
|
||||
bool has_selection() const;
|
||||
bool has_selection(int p_caret = -1) const;
|
||||
|
||||
String get_selected_text() const;
|
||||
String get_selected_text(int p_caret = -1);
|
||||
|
||||
int get_selection_line() const;
|
||||
int get_selection_column() const;
|
||||
int get_selection_line(int p_caret = 0) const;
|
||||
int get_selection_column(int p_caret = 0) const;
|
||||
|
||||
int get_selection_from_line() const;
|
||||
int get_selection_from_column() const;
|
||||
int get_selection_to_line() const;
|
||||
int get_selection_to_column() const;
|
||||
int get_selection_from_line(int p_caret = 0) const;
|
||||
int get_selection_from_column(int p_caret = 0) const;
|
||||
int get_selection_to_line(int p_caret = 0) const;
|
||||
int get_selection_to_column(int p_caret = 0) const;
|
||||
|
||||
void deselect();
|
||||
void delete_selection();
|
||||
void deselect(int p_caret = -1);
|
||||
void delete_selection(int p_caret = -1);
|
||||
|
||||
/* Line wrapping. */
|
||||
void set_line_wrapping_mode(LineWrappingMode p_wrapping_mode);
|
||||
|
@ -866,8 +915,8 @@ public:
|
|||
int get_total_visible_line_count() const;
|
||||
|
||||
// Auto Adjust
|
||||
void adjust_viewport_to_caret();
|
||||
void center_viewport_to_caret();
|
||||
void adjust_viewport_to_caret(int p_caret = 0);
|
||||
void center_viewport_to_caret(int p_caret = 0);
|
||||
|
||||
// Minimap
|
||||
void set_draw_minimap(bool p_enabled);
|
||||
|
@ -949,6 +998,7 @@ public:
|
|||
TextEdit(const String &p_placeholder = String());
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(TextEdit::EditAction);
|
||||
VARIANT_ENUM_CAST(TextEdit::CaretType);
|
||||
VARIANT_ENUM_CAST(TextEdit::LineWrappingMode);
|
||||
VARIANT_ENUM_CAST(TextEdit::SelectionMode);
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue