mirror of
https://github.com/godotengine/godot.git
synced 2024-11-21 03:18:37 +08:00
Add Duplicate Lines shortcut to CodeTextEditor
This keyboard shortcut has been made with inspiration from the VS Code keyboard shortcut editor.action.copyLinesDownAction. It duplicates all selected lines and inserts them below no matter where the caret is within the line.
This commit is contained in:
parent
43b9e89a07
commit
d2e651f403
@ -152,6 +152,12 @@
|
||||
Perform an indent as if the user activated the "ui_text_indent" action.
|
||||
</description>
|
||||
</method>
|
||||
<method name="duplicate_lines">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Duplicates all lines currently selected with any caret. Duplicates the entire line beneath the current one no matter where the caret is within the line.
|
||||
</description>
|
||||
</method>
|
||||
<method name="fold_all_lines">
|
||||
<return type="void" />
|
||||
<description>
|
||||
|
@ -805,6 +805,11 @@ void CodeTextEditor::input(const Ref<InputEvent> &event) {
|
||||
accept_event();
|
||||
return;
|
||||
}
|
||||
if (ED_IS_SHORTCUT("script_text_editor/duplicate_lines", key_event)) {
|
||||
text_editor->duplicate_lines();
|
||||
accept_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CodeTextEditor::_text_editor_gui_input(const Ref<InputEvent> &p_event) {
|
||||
|
@ -1306,6 +1306,9 @@ void ScriptTextEditor::_edit_option(int p_op) {
|
||||
case EDIT_DUPLICATE_SELECTION: {
|
||||
code_editor->duplicate_selection();
|
||||
} break;
|
||||
case EDIT_DUPLICATE_LINES: {
|
||||
code_editor->get_text_editor()->duplicate_lines();
|
||||
} break;
|
||||
case EDIT_TOGGLE_FOLD_LINE: {
|
||||
int previous_line = -1;
|
||||
for (int caret_idx : tx->get_caret_index_edit_order()) {
|
||||
@ -2173,6 +2176,7 @@ void ScriptTextEditor::_enable_code_editor() {
|
||||
edit_menu->get_popup()->add_separator();
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL);
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_selection"), EDIT_DUPLICATE_SELECTION);
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_lines"), EDIT_DUPLICATE_LINES);
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/evaluate_selection"), EDIT_EVALUATE);
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_word_wrap"), EDIT_TOGGLE_WORD_WRAP);
|
||||
edit_menu->get_popup()->add_separator();
|
||||
@ -2395,6 +2399,8 @@ void ScriptTextEditor::register_editor() {
|
||||
ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), Key::NONE);
|
||||
ED_SHORTCUT("script_text_editor/duplicate_selection", TTR("Duplicate Selection"), KeyModifierMask::SHIFT | KeyModifierMask::CTRL | Key::D);
|
||||
ED_SHORTCUT_OVERRIDE("script_text_editor/duplicate_selection", "macos", KeyModifierMask::SHIFT | KeyModifierMask::META | Key::C);
|
||||
ED_SHORTCUT("script_text_editor/duplicate_lines", TTR("Duplicate Lines"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::DOWN);
|
||||
ED_SHORTCUT_OVERRIDE("script_text_editor/duplicate_lines", "macos", KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::DOWN);
|
||||
ED_SHORTCUT("script_text_editor/evaluate_selection", TTR("Evaluate Selection"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::E);
|
||||
ED_SHORTCUT("script_text_editor/toggle_word_wrap", TTR("Toggle Word Wrap"), KeyModifierMask::ALT | Key::Z);
|
||||
ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::T);
|
||||
|
@ -126,6 +126,7 @@ class ScriptTextEditor : public ScriptEditorBase {
|
||||
EDIT_UNINDENT,
|
||||
EDIT_DELETE_LINE,
|
||||
EDIT_DUPLICATE_SELECTION,
|
||||
EDIT_DUPLICATE_LINES,
|
||||
EDIT_PICK_COLOR,
|
||||
EDIT_TO_UPPERCASE,
|
||||
EDIT_TO_LOWERCASE,
|
||||
|
@ -392,6 +392,9 @@ void TextEditor::_edit_option(int p_op) {
|
||||
case EDIT_DUPLICATE_SELECTION: {
|
||||
code_editor->duplicate_selection();
|
||||
} break;
|
||||
case EDIT_DUPLICATE_LINES: {
|
||||
code_editor->get_text_editor()->duplicate_lines();
|
||||
} break;
|
||||
case EDIT_TOGGLE_FOLD_LINE: {
|
||||
int previous_line = -1;
|
||||
for (int caret_idx : tx->get_caret_index_edit_order()) {
|
||||
@ -651,6 +654,7 @@ TextEditor::TextEditor() {
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_all_lines"), EDIT_UNFOLD_ALL_LINES);
|
||||
edit_menu->get_popup()->add_separator();
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_selection"), EDIT_DUPLICATE_SELECTION);
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_lines"), EDIT_DUPLICATE_LINES);
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_word_wrap"), EDIT_TOGGLE_WORD_WRAP);
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/trim_trailing_whitespace"), EDIT_TRIM_TRAILING_WHITESAPCE);
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_spaces"), EDIT_CONVERT_INDENT_TO_SPACES);
|
||||
|
@ -71,6 +71,7 @@ private:
|
||||
EDIT_UNINDENT,
|
||||
EDIT_DELETE_LINE,
|
||||
EDIT_DUPLICATE_SELECTION,
|
||||
EDIT_DUPLICATE_LINES,
|
||||
EDIT_TO_UPPERCASE,
|
||||
EDIT_TO_LOWERCASE,
|
||||
EDIT_CAPITALIZE,
|
||||
|
@ -671,6 +671,9 @@ void TextShaderEditor::_menu_option(int p_option) {
|
||||
case EDIT_DUPLICATE_SELECTION: {
|
||||
shader_editor->duplicate_selection();
|
||||
} break;
|
||||
case EDIT_DUPLICATE_LINES: {
|
||||
shader_editor->get_text_editor()->duplicate_lines();
|
||||
} break;
|
||||
case EDIT_TOGGLE_WORD_WRAP: {
|
||||
TextEdit::LineWrappingMode wrap = shader_editor->get_text_editor()->get_line_wrapping_mode();
|
||||
shader_editor->get_text_editor()->set_line_wrapping_mode(wrap == TextEdit::LINE_WRAPPING_BOUNDARY ? TextEdit::LINE_WRAPPING_NONE : TextEdit::LINE_WRAPPING_BOUNDARY);
|
||||
@ -1122,6 +1125,7 @@ TextShaderEditor::TextShaderEditor() {
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE);
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_selection"), EDIT_DUPLICATE_SELECTION);
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_lines"), EDIT_DUPLICATE_LINES);
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_word_wrap"), EDIT_TOGGLE_WORD_WRAP);
|
||||
edit_menu->get_popup()->add_separator();
|
||||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE);
|
||||
|
@ -120,6 +120,7 @@ class TextShaderEditor : public MarginContainer {
|
||||
EDIT_UNINDENT,
|
||||
EDIT_DELETE_LINE,
|
||||
EDIT_DUPLICATE_SELECTION,
|
||||
EDIT_DUPLICATE_LINES,
|
||||
EDIT_TOGGLE_WORD_WRAP,
|
||||
EDIT_TOGGLE_COMMENT,
|
||||
EDIT_COMPLETE,
|
||||
|
@ -2399,6 +2399,68 @@ void CodeEdit::set_symbol_lookup_word_as_valid(bool p_valid) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Text manipulation */
|
||||
void CodeEdit::duplicate_lines() {
|
||||
begin_complex_operation();
|
||||
|
||||
Vector<int> caret_edit_order = get_caret_index_edit_order();
|
||||
for (const int &caret_index : caret_edit_order) {
|
||||
// The text that will be inserted. All lines in one string.
|
||||
String insert_text;
|
||||
|
||||
// The new line position of the caret after the operation.
|
||||
int new_caret_line = get_caret_line(caret_index);
|
||||
// The new column position of the caret after the operation.
|
||||
int new_caret_column = get_caret_column(caret_index);
|
||||
// The caret positions of the selection. Stays -1 if there is no selection.
|
||||
int select_from_line = -1;
|
||||
int select_to_line = -1;
|
||||
int select_from_column = -1;
|
||||
int select_to_column = -1;
|
||||
// Number of lines of the selection.
|
||||
int select_num_lines = -1;
|
||||
|
||||
if (has_selection(caret_index)) {
|
||||
select_from_line = get_selection_from_line(caret_index);
|
||||
select_to_line = get_selection_to_line(caret_index);
|
||||
select_from_column = get_selection_from_column(caret_index);
|
||||
select_to_column = get_selection_to_column(caret_index);
|
||||
select_num_lines = select_to_line - select_from_line + 1;
|
||||
|
||||
for (int i = select_from_line; i <= select_to_line; i++) {
|
||||
insert_text += "\n" + get_line(i);
|
||||
unfold_line(i);
|
||||
}
|
||||
new_caret_line = select_to_line + select_num_lines;
|
||||
} else {
|
||||
insert_text = "\n" + get_line(new_caret_line);
|
||||
new_caret_line++;
|
||||
|
||||
unfold_line(get_caret_line(caret_index));
|
||||
}
|
||||
|
||||
// The text will be inserted at the end of the current line.
|
||||
set_caret_column(get_line(get_caret_line(caret_index)).length(), false, caret_index);
|
||||
|
||||
deselect(caret_index);
|
||||
|
||||
insert_text_at_caret(insert_text, caret_index);
|
||||
set_caret_line(new_caret_line, false, true, 0, caret_index);
|
||||
set_caret_column(new_caret_column, true, caret_index);
|
||||
|
||||
if (select_from_line != -1) {
|
||||
// Advance the selection by the number of duplicated lines.
|
||||
select_from_line += select_num_lines;
|
||||
select_to_line += select_num_lines;
|
||||
|
||||
select(select_from_line, select_from_column, select_to_line, select_to_column, caret_index);
|
||||
}
|
||||
}
|
||||
|
||||
end_complex_operation();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
/* Visual */
|
||||
Color CodeEdit::_get_brace_mismatch_color() const {
|
||||
return theme_cache.brace_mismatch_color;
|
||||
@ -2598,6 +2660,9 @@ void CodeEdit::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_symbol_lookup_word_as_valid", "valid"), &CodeEdit::set_symbol_lookup_word_as_valid);
|
||||
|
||||
/* Text manipulation */
|
||||
ClassDB::bind_method(D_METHOD("duplicate_lines"), &CodeEdit::duplicate_lines);
|
||||
|
||||
/* Inspector */
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "symbol_lookup_on_click"), "set_symbol_lookup_on_click_enabled", "is_symbol_lookup_on_click_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "line_folding"), "set_line_folding_enabled", "is_line_folding_enabled");
|
||||
|
@ -486,6 +486,9 @@ public:
|
||||
|
||||
void set_symbol_lookup_word_as_valid(bool p_valid);
|
||||
|
||||
/* Text manipulation */
|
||||
void duplicate_lines();
|
||||
|
||||
CodeEdit();
|
||||
~CodeEdit();
|
||||
};
|
||||
|
@ -3886,6 +3886,84 @@ TEST_CASE("[SceneTree][CodeEdit] New Line") {
|
||||
memdelete(code_edit);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][CodeEdit] Duplicate Lines") {
|
||||
CodeEdit *code_edit = memnew(CodeEdit);
|
||||
SceneTree::get_singleton()->get_root()->add_child(code_edit);
|
||||
code_edit->grab_focus();
|
||||
|
||||
code_edit->set_text(R"(extends Node
|
||||
|
||||
func _ready():
|
||||
var a := len(OS.get_cmdline_args())
|
||||
var b := get_child_count()
|
||||
var c := a + b
|
||||
for i in range(c):
|
||||
print("This is the solution: ", sin(i))
|
||||
var pos = get_index() - 1
|
||||
print("Make sure this exits: %b" % pos)
|
||||
)");
|
||||
|
||||
/* Duplicate a single line without selection. */
|
||||
code_edit->set_caret_line(0);
|
||||
code_edit->duplicate_lines();
|
||||
CHECK(code_edit->get_line(0) == "extends Node");
|
||||
CHECK(code_edit->get_line(1) == "extends Node");
|
||||
CHECK(code_edit->get_line(2) == "");
|
||||
|
||||
/* Duplicate multiple lines with selection. */
|
||||
code_edit->set_caret_line(6);
|
||||
code_edit->set_caret_column(15);
|
||||
code_edit->select(4, 8, 6, 15);
|
||||
code_edit->duplicate_lines();
|
||||
CHECK(code_edit->get_line(6) == "\tvar c := a + b");
|
||||
CHECK(code_edit->get_line(7) == "\tvar a := len(OS.get_cmdline_args())");
|
||||
CHECK(code_edit->get_line(8) == "\tvar b := get_child_count()");
|
||||
CHECK(code_edit->get_line(9) == "\tvar c := a + b");
|
||||
CHECK(code_edit->get_line(10) == "\tfor i in range(c):");
|
||||
|
||||
/* Duplicate single lines with multiple carets. */
|
||||
code_edit->deselect();
|
||||
code_edit->set_caret_line(10);
|
||||
code_edit->set_caret_column(1);
|
||||
code_edit->add_caret(11, 2);
|
||||
code_edit->add_caret(12, 1);
|
||||
code_edit->duplicate_lines();
|
||||
CHECK(code_edit->get_line(9) == "\tvar c := a + b");
|
||||
CHECK(code_edit->get_line(10) == "\tfor i in range(c):");
|
||||
CHECK(code_edit->get_line(11) == "\tfor i in range(c):");
|
||||
CHECK(code_edit->get_line(12) == "\t\tprint(\"This is the solution: \", sin(i))");
|
||||
CHECK(code_edit->get_line(13) == "\t\tprint(\"This is the solution: \", sin(i))");
|
||||
CHECK(code_edit->get_line(14) == "\tvar pos = get_index() - 1");
|
||||
CHECK(code_edit->get_line(15) == "\tvar pos = get_index() - 1");
|
||||
CHECK(code_edit->get_line(16) == "\tprint(\"Make sure this exits: %b\" % pos)");
|
||||
|
||||
/* Duplicate multiple lines with multiple carets. */
|
||||
code_edit->select(0, 0, 1, 2, 0);
|
||||
code_edit->select(3, 0, 4, 2, 1);
|
||||
code_edit->select(16, 0, 17, 0, 2);
|
||||
code_edit->set_caret_line(1, false, true, 0, 0);
|
||||
code_edit->set_caret_column(2, false, 0);
|
||||
code_edit->set_caret_line(4, false, true, 0, 1);
|
||||
code_edit->set_caret_column(2, false, 1);
|
||||
code_edit->set_caret_line(17, false, true, 0, 2);
|
||||
code_edit->set_caret_column(0, false, 2);
|
||||
code_edit->duplicate_lines();
|
||||
CHECK(code_edit->get_line(1) == "extends Node");
|
||||
CHECK(code_edit->get_line(2) == "extends Node");
|
||||
CHECK(code_edit->get_line(3) == "extends Node");
|
||||
CHECK(code_edit->get_line(4) == "");
|
||||
CHECK(code_edit->get_line(6) == "\tvar a := len(OS.get_cmdline_args())");
|
||||
CHECK(code_edit->get_line(7) == "func _ready():");
|
||||
CHECK(code_edit->get_line(8) == "\tvar a := len(OS.get_cmdline_args())");
|
||||
CHECK(code_edit->get_line(9) == "\tvar b := get_child_count()");
|
||||
CHECK(code_edit->get_line(20) == "\tprint(\"Make sure this exits: %b\" % pos)");
|
||||
CHECK(code_edit->get_line(21) == "");
|
||||
CHECK(code_edit->get_line(22) == "\tprint(\"Make sure this exits: %b\" % pos)");
|
||||
CHECK(code_edit->get_line(23) == "");
|
||||
|
||||
memdelete(code_edit);
|
||||
}
|
||||
|
||||
} // namespace TestCodeEdit
|
||||
|
||||
#endif // TEST_CODE_EDIT_H
|
||||
|
Loading…
Reference in New Issue
Block a user