Move indent management to CodeEdit

This commit is contained in:
Paulb23 2021-06-15 15:05:01 +01:00
parent 953de68cfc
commit 1a0cfc219b
10 changed files with 762 additions and 477 deletions

View file

@ -143,6 +143,20 @@
Inserts the selected entry into the text. If [code]replace[/code] is true, any existing text is replaced rather then merged.
</description>
</method>
<method name="do_indent">
<return type="void">
</return>
<description>
Perform an indent as if the user activated the "ui_text_indent" action.
</description>
</method>
<method name="do_unindent">
<return type="void">
</return>
<description>
Perform an unindent as if the user activated the "ui_text_unindent" action.
</description>
</method>
<method name="fold_all_lines">
<return type="void">
</return>
@ -278,6 +292,13 @@
Returns [code]true[/code] if string [code]start_key[/code] exists.
</description>
</method>
<method name="indent_lines">
<return type="void">
</return>
<description>
Indents selected lines, or in the case of no selection the caret line by one.
</description>
</method>
<method name="is_in_comment" qualifiers="const">
<return type="int">
</return>
@ -441,6 +462,13 @@
Unfolds all lines that were previously folded.
</description>
</method>
<method name="unindent_lines">
<return type="void">
</return>
<description>
Unindents selected lines, or in the case of no selection the caret line by one.
</description>
</method>
<method name="update_code_completion_options">
<return type="void">
</return>
@ -475,6 +503,18 @@
</member>
<member name="draw_line_numbers" type="bool" setter="set_draw_line_numbers" getter="is_draw_line_numbers_enabled" default="false">
</member>
<member name="indent_automatic" type="bool" setter="set_auto_indent_enabled" getter="is_auto_indent_enabled" default="false">
Sets whether automatic indent are enabled, this will add an extra indent if a prefix or brace is found.
</member>
<member name="indent_automatic_prefixes" type="String[]" setter="set_auto_indent_prefixes" getter="get_auto_indent_prefixes" default="[&quot;(&quot;, &quot;:&quot;, &quot;[&quot;, &quot;{&quot;]">
Prefixes to trigger an automatic indent.
</member>
<member name="indent_size" type="int" setter="set_indent_size" getter="get_indent_size" default="4">
Size of tabs, if [code]indent_use_spaces[/code] is enabled the amount of spaces to use.
</member>
<member name="indent_use_spaces" type="bool" setter="set_indent_using_spaces" getter="is_indent_using_spaces" default="false">
Use spaces instead of tabs for indentation.
</member>
<member name="layout_direction" type="int" setter="set_layout_direction" getter="get_layout_direction" override="true" enum="Control.LayoutDirection" default="2" />
<member name="line_folding" type="bool" setter="set_line_folding_enabled" getter="is_line_folding_enabled" default="true">
Sets whether line folding is allowed.

View file

@ -10,6 +10,12 @@
<tutorials>
</tutorials>
<methods>
<method name="_backspace" qualifiers="virtual">
<return type="void">
</return>
<description>
</description>
</method>
<method name="add_gutter">
<return type="void">
</return>
@ -18,6 +24,12 @@
<description>
</description>
</method>
<method name="backspace">
<return type="void">
</return>
<description>
</description>
</method>
<method name="center_viewport_to_cursor">
<return type="void">
</return>
@ -96,6 +108,12 @@
Cut's the current selection.
</description>
</method>
<method name="delete_selection">
<return type="void">
</return>
<description>
</description>
</method>
<method name="deselect">
<return type="void">
</return>
@ -110,6 +128,14 @@
Gets the caret pixel draw poistion.
</description>
</method>
<method name="get_first_non_whitespace_column" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="line" type="int">
</argument>
<description>
</description>
</method>
<method name="get_gutter_count" qualifiers="const">
<return type="int">
</return>
@ -140,6 +166,14 @@
<description>
</description>
</method>
<method name="get_indent_level" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="line" type="int">
</argument>
<description>
</description>
</method>
<method name="get_line" qualifiers="const">
<return type="String">
</return>
@ -274,6 +308,12 @@
Returns the selection end line.
</description>
</method>
<method name="get_tab_size" qualifiers="const">
<return type="int">
</return>
<description>
</description>
</method>
<method name="get_visible_line_count" qualifiers="const">
<return type="int">
</return>
@ -354,6 +394,16 @@
Triggers a right-click menu action by the specified index. See [enum MenuItems] for a list of available indexes.
</description>
</method>
<method name="merge_gutters">
<return type="void">
</return>
<argument index="0" name="from_line" type="int">
</argument>
<argument index="1" name="to_line" type="int">
</argument>
<description>
</description>
</method>
<method name="paste">
<return type="void">
</return>
@ -611,6 +661,14 @@
<description>
</description>
</method>
<method name="set_tab_size">
<return type="void">
</return>
<argument index="0" name="size" type="int">
</argument>
<description>
</description>
</method>
<method name="undo">
<return type="void">
</return>

View file

@ -930,7 +930,7 @@ void CodeTextEditor::update_editor_settings() {
text_editor->set_highlight_current_line(EditorSettings::get_singleton()->get("text_editor/highlighting/highlight_current_line"));
text_editor->set_indent_using_spaces(EditorSettings::get_singleton()->get("text_editor/indent/type"));
text_editor->set_indent_size(EditorSettings::get_singleton()->get("text_editor/indent/size"));
text_editor->set_auto_indent(EditorSettings::get_singleton()->get("text_editor/indent/auto_indent"));
text_editor->set_auto_indent_enabled(EditorSettings::get_singleton()->get("text_editor/indent/auto_indent"));
text_editor->set_draw_tabs(EditorSettings::get_singleton()->get("text_editor/indent/draw_tabs"));
text_editor->set_draw_spaces(EditorSettings::get_singleton()->get("text_editor/indent/draw_spaces"));
text_editor->set_smooth_scroll_enabled(EditorSettings::get_singleton()->get("text_editor/navigation/smooth_scrolling"));
@ -1255,7 +1255,7 @@ void CodeTextEditor::_delete_line(int p_line) {
text_editor->cursor_set_line(1);
text_editor->cursor_set_column(0);
}
text_editor->backspace_at_cursor();
text_editor->backspace();
text_editor->unfold_line(p_line);
text_editor->cursor_set_line(p_line);
}
@ -1809,7 +1809,7 @@ CodeTextEditor::CodeTextEditor() {
text_editor->set_draw_line_numbers(true);
text_editor->set_brace_matching(true);
text_editor->set_auto_indent(true);
text_editor->set_auto_indent_enabled(true);
status_bar = memnew(HBoxContainer);
add_child(status_bar);

View file

@ -524,7 +524,7 @@ void ScriptTextEditor::_validate_script() {
if (safe_lines.has(i + 1)) {
te->set_line_gutter_item_color(i, line_number_gutter, safe_line_number_color);
last_is_safe = true;
} else if (last_is_safe && (te->is_line_comment(i) || te->get_line(i).strip_edges().is_empty())) {
} else if (last_is_safe && (te->is_in_comment(i) != -1 || te->get_line(i).strip_edges().is_empty())) {
te->set_line_gutter_item_color(i, line_number_gutter, safe_line_number_color);
} else {
te->set_line_gutter_item_color(i, line_number_gutter, default_line_number_color);
@ -1038,7 +1038,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
return;
}
tx->indent_selected_lines_left();
tx->unindent_lines();
} break;
case EDIT_INDENT_RIGHT: {
Ref<Script> scr = script;
@ -1046,7 +1046,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
return;
}
tx->indent_selected_lines_right();
tx->indent_lines();
} break;
case EDIT_DELETE_LINE: {
code_editor->delete_lines();

View file

@ -323,19 +323,13 @@ void ShaderEditor::_menu_option(int p_option) {
if (shader.is_null()) {
return;
}
CodeEdit *tx = shader_editor->get_text_editor();
tx->indent_selected_lines_left();
shader_editor->get_text_editor()->unindent_lines();
} break;
case EDIT_INDENT_RIGHT: {
if (shader.is_null()) {
return;
}
CodeEdit *tx = shader_editor->get_text_editor();
tx->indent_selected_lines_right();
shader_editor->get_text_editor()->indent_lines();
} break;
case EDIT_DELETE_LINE: {
shader_editor->delete_lines();

View file

@ -320,10 +320,10 @@ void TextEditor::_edit_option(int p_op) {
code_editor->move_lines_down();
} break;
case EDIT_INDENT_LEFT: {
tx->indent_selected_lines_left();
tx->unindent_lines();
} break;
case EDIT_INDENT_RIGHT: {
tx->indent_selected_lines_right();
tx->indent_lines();
} break;
case EDIT_DELETE_LINE: {
code_editor->delete_lines();

View file

@ -360,7 +360,7 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
return;
}
if (k->is_action("ui_text_backspace", true)) {
backspace_at_cursor();
backspace();
_filter_code_completion_candidates();
accept_event();
return;
@ -387,14 +387,34 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
set_code_hint("");
}
/* Override input to unfold lines where needed. */
if (!is_readonly()) {
if (k->is_action("ui_text_newline_above", true) || k->is_action("ui_text_newline_blank", true) || k->is_action("ui_text_newline", true)) {
unfold_line(cursor_get_line());
}
if (cursor_get_line() > 0 && k->is_action("ui_text_backspace", true)) {
unfold_line(cursor_get_line() - 1);
}
/* Indentation */
if (k->is_action("ui_text_indent", true)) {
do_indent();
accept_event();
return;
}
if (k->is_action("ui_text_dedent", true)) {
do_unindent();
accept_event();
return;
}
// Override new line actions, for auto indent
if (k->is_action("ui_text_newline_above", true)) {
_new_line(false, true);
accept_event();
return;
}
if (k->is_action("ui_text_newline_blank", true)) {
_new_line(false);
accept_event();
return;
}
if (k->is_action("ui_text_newline", true)) {
_new_line();
accept_event();
return;
}
/* Remove shift otherwise actions will not match. */
@ -439,6 +459,443 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
return TextEdit::get_cursor_shape(p_pos);
}
/* Indent management */
void CodeEdit::set_indent_size(const int p_size) {
ERR_FAIL_COND_MSG(p_size <= 0, "Indend size must be greater than 0.");
if (indent_size == p_size) {
return;
}
indent_size = p_size;
if (indent_using_spaces) {
indent_text = String(" ").repeat(p_size);
} else {
indent_text = "\t";
}
set_tab_size(p_size);
}
int CodeEdit::get_indent_size() const {
return indent_size;
}
void CodeEdit::set_indent_using_spaces(const bool p_use_spaces) {
indent_using_spaces = p_use_spaces;
if (indent_using_spaces) {
indent_text = String(" ").repeat(indent_size);
} else {
indent_text = "\t";
}
}
bool CodeEdit::is_indent_using_spaces() const {
return indent_using_spaces;
}
void CodeEdit::set_auto_indent_enabled(bool p_enabled) {
auto_indent = p_enabled;
}
bool CodeEdit::is_auto_indent_enabled() const {
return auto_indent;
}
void CodeEdit::set_auto_indent_prefixes(const TypedArray<String> &p_prefixes) {
auto_indent_prefixes.clear();
for (int i = 0; i < p_prefixes.size(); i++) {
const String prefix = p_prefixes[i];
auto_indent_prefixes.insert(prefix[0]);
}
}
TypedArray<String> CodeEdit::get_auto_indent_prefixes() const {
TypedArray<String> prefixes;
for (const Set<char32_t>::Element *E = auto_indent_prefixes.front(); E; E = E->next()) {
prefixes.push_back(String::chr(E->get()));
}
return prefixes;
}
void CodeEdit::do_indent() {
if (is_readonly()) {
return;
}
if (is_selection_active()) {
indent_lines();
return;
}
if (!indent_using_spaces) {
_insert_text_at_cursor("\t");
return;
}
int spaces_to_add = _calculate_spaces_till_next_right_indent(cursor_get_column());
if (spaces_to_add > 0) {
_insert_text_at_cursor(String(" ").repeat(spaces_to_add));
}
}
void CodeEdit::indent_lines() {
if (is_readonly()) {
return;
}
begin_complex_operation();
/* 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 = cursor_get_line();
int end_line = start_line;
if (is_selection_active()) {
start_line = get_selection_from_line();
end_line = get_selection_to_line();
/* Ignore the last line if the selection is not past the first column. */
if (get_selection_to_column() == 0) {
selection_offset = 0;
end_line--;
}
}
for (int i = start_line; i <= end_line; i++) {
const String line_text = get_line(i);
if (line_text.size() == 0 && is_selection_active()) {
continue;
}
if (!indent_using_spaces) {
set_line(i, '\t' + line_text);
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. */
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 (is_selection_active()) {
select(start_line, get_selection_from_column() + selection_offset, get_selection_to_line(), get_selection_to_column() + selection_offset);
}
cursor_set_column(cursor_get_column() + selection_offset, false);
end_complex_operation();
}
void CodeEdit::do_unindent() {
if (is_readonly()) {
return;
}
int cc = cursor_get_column();
if (is_selection_active() || cc <= 0) {
unindent_lines();
return;
}
int cl = cursor_get_line();
const String &line = get_line(cl);
if (line[cc - 1] == '\t') {
_remove_text(cl, cc - 1, cl, cc);
cursor_set_column(MAX(0, cc - 1));
return;
}
if (line[cc - 1] != ' ') {
return;
}
int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc);
if (spaces_to_remove > 0) {
for (int i = 1; i <= spaces_to_remove; i++) {
if (line[cc - i] != ' ') {
spaces_to_remove = i - 1;
break;
}
}
_remove_text(cl, cc - spaces_to_remove, cl, cc);
cursor_set_column(MAX(0, cc - spaces_to_remove));
}
}
void CodeEdit::unindent_lines() {
if (is_readonly()) {
return;
}
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. */
int removed_characters = 0;
int initial_selection_end_column = 0;
int initial_cursor_column = cursor_get_column();
int start_line = cursor_get_line();
int end_line = start_line;
if (is_selection_active()) {
start_line = get_selection_from_line();
end_line = get_selection_to_line();
/* Ignore the last line if the selection is not past the first column. */
initial_selection_end_column = get_selection_to_column();
if (initial_selection_end_column == 0) {
end_line--;
}
}
bool first_line_edited = false;
bool last_line_edited = false;
for (int i = start_line; i <= end_line; i++) {
String line_text = get_line(i);
if (line_text.begins_with("\t")) {
line_text = line_text.substr(1, line_text.length());
set_line(i, line_text);
removed_characters = 1;
first_line_edited = (i == start_line) ? true : first_line_edited;
last_line_edited = (i == end_line) ? true : last_line_edited;
continue;
}
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. */
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());
set_line(i, line_text);
removed_characters = spaces_to_remove;
first_line_edited = (i == start_line) ? true : first_line_edited;
last_line_edited = (i == end_line) ? true : last_line_edited;
}
}
if (is_selection_active()) {
/* 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);
}
/* 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);
}
}
cursor_set_column(initial_cursor_column - removed_characters, false);
end_complex_operation();
}
int CodeEdit::_calculate_spaces_till_next_left_indent(int p_column) const {
int spaces_till_indent = p_column % indent_size;
if (spaces_till_indent == 0) {
spaces_till_indent = indent_size;
}
return spaces_till_indent;
}
int CodeEdit::_calculate_spaces_till_next_right_indent(int p_column) const {
return indent_size - p_column % indent_size;
}
/* TODO: remove once brace completion is refactored. */
static char32_t _get_right_pair_symbol(char32_t c) {
if (c == '"') {
return '"';
}
if (c == '\'') {
return '\'';
}
if (c == '(') {
return ')';
}
if (c == '[') {
return ']';
}
if (c == '{') {
return '}';
}
return 0;
}
static bool _is_pair_left_symbol(char32_t c) {
return c == '"' ||
c == '\'' ||
c == '(' ||
c == '[' ||
c == '{';
}
void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
if (is_readonly()) {
return;
}
const int cc = cursor_get_column();
const int cl = cursor_get_line();
const String line = get_line(cl);
String ins = "\n";
/* Append current indentation. */
int space_count = 0;
int line_col = 0;
for (; line_col < cc; line_col++) {
if (line[line_col] == '\t') {
ins += indent_text;
space_count = 0;
continue;
}
if (line[line_col] == ' ') {
space_count++;
if (space_count == indent_size) {
ins += indent_text;
space_count = 0;
}
continue;
}
break;
}
if (is_line_folded(cl)) {
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. */
bool brace_indent = false;
if (auto_indent && !p_above && cc > 0 && is_in_comment(cl) == -1) {
bool should_indent = false;
char32_t indent_char = ' ';
for (; line_col < cc; line_col++) {
char32_t c = line[line_col];
if (auto_indent_prefixes.has(c)) {
should_indent = true;
indent_char = c;
continue;
}
/* Make sure this is the last char, trailing whitespace or comments are okay. */
if (should_indent && (!_is_whitespace(c) && is_in_comment(cl, cc) == -1)) {
should_indent = false;
}
}
if (should_indent) {
ins += indent_text;
/* TODO: Change when brace completion is refactored. */
char32_t closing_char = _get_right_pair_symbol(indent_char);
if (closing_char != 0 && closing_char == line[cc]) {
/* 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(1, ins.length() - 2);
} else {
brace_indent = false;
ins = "\n" + ins.substr(1, ins.length() - 2);
}
}
}
}
begin_complex_operation();
bool first_line = false;
if (!p_split_current_line) {
if (p_above) {
if (cl > 0) {
cursor_set_line(cl - 1, false);
cursor_set_column(get_line(cursor_get_line()).length());
} else {
cursor_set_column(0);
first_line = true;
}
} else {
cursor_set_column(line.length());
}
}
insert_text_at_cursor(ins);
if (first_line) {
cursor_set_line(0);
} else if (brace_indent) {
cursor_set_line(cursor_get_line() - 1, false);
cursor_set_column(get_line(cursor_get_line()).length());
}
end_complex_operation();
}
void CodeEdit::backspace() {
if (is_readonly()) {
return;
}
int cc = cursor_get_column();
int cl = cursor_get_line();
if (cc == 0 && cl == 0) {
return;
}
if (is_selection_active()) {
delete_selection();
return;
}
if (cl > 0 && is_line_hidden(cl - 1)) {
unfold_line(cursor_get_line() - 1);
}
int prev_line = cc ? cl : cl - 1;
int prev_column = cc ? (cc - 1) : (get_line(cl - 1).length());
merge_gutters(cl, prev_line);
/* TODO: Change when brace completion is refactored. */
if (auto_brace_completion_enabled && cc > 0 && _is_pair_left_symbol(get_line(cl)[cc - 1])) {
_consume_backspace_for_pair_symbol(prev_line, prev_column);
cursor_set_line(prev_line, false, true);
cursor_set_column(prev_column);
return;
}
/* For space indentation we need to do a simple unindent if there are no chars to the left, acting in the */
/* same way as tabs. */
if (indent_using_spaces && cc != 0) {
if (get_first_non_whitespace_column(cl) > cc) {
prev_column = cc - _calculate_spaces_till_next_left_indent(cc);
prev_line = cl;
}
}
_remove_text(prev_line, prev_column, cl, cc);
cursor_set_line(prev_line, false, true);
cursor_set_column(prev_column);
}
/* Main Gutter */
void CodeEdit::_update_draw_main_gutter() {
set_gutter_draw(main_gutter, draw_breakpoints || draw_bookmarks || draw_executing_lines);
@ -1286,6 +1743,25 @@ void CodeEdit::cancel_code_completion() {
}
void CodeEdit::_bind_methods() {
/* Indent management */
ClassDB::bind_method(D_METHOD("set_indent_size", "size"), &CodeEdit::set_indent_size);
ClassDB::bind_method(D_METHOD("get_indent_size"), &CodeEdit::get_indent_size);
ClassDB::bind_method(D_METHOD("set_indent_using_spaces", "use_spaces"), &CodeEdit::set_indent_using_spaces);
ClassDB::bind_method(D_METHOD("is_indent_using_spaces"), &CodeEdit::is_indent_using_spaces);
ClassDB::bind_method(D_METHOD("set_auto_indent_enabled", "enable"), &CodeEdit::set_auto_indent_enabled);
ClassDB::bind_method(D_METHOD("is_auto_indent_enabled"), &CodeEdit::is_auto_indent_enabled);
ClassDB::bind_method(D_METHOD("set_auto_indent_prefixes", "prefixes"), &CodeEdit::set_auto_indent_prefixes);
ClassDB::bind_method(D_METHOD("get_auto_indent_prefixes"), &CodeEdit::get_auto_indent_prefixes);
ClassDB::bind_method(D_METHOD("do_indent"), &CodeEdit::do_indent);
ClassDB::bind_method(D_METHOD("do_unindent"), &CodeEdit::do_unindent);
ClassDB::bind_method(D_METHOD("indent_lines"), &CodeEdit::indent_lines);
ClassDB::bind_method(D_METHOD("unindent_lines"), &CodeEdit::unindent_lines);
/* Main Gutter */
ClassDB::bind_method(D_METHOD("_main_gutter_draw_callback"), &CodeEdit::_main_gutter_draw_callback);
@ -1436,6 +1912,12 @@ void CodeEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "code_completion_enabled"), "set_code_completion_enabled", "is_code_completion_enabled");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "code_completion_prefixes"), "set_code_completion_prefixes", "get_code_comletion_prefixes");
ADD_GROUP("Indentation", "indent_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "indent_size"), "set_indent_size", "get_indent_size");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indent_use_spaces"), "set_indent_using_spaces", "is_indent_using_spaces");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indent_automatic"), "set_auto_indent_enabled", "is_auto_indent_enabled");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "indent_automatic_prefixes"), "set_auto_indent_prefixes", "get_auto_indent_prefixes");
/* Signals */
ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "line")));
ADD_SIGNAL(MethodInfo("request_code_completion"));
@ -2062,6 +2544,12 @@ void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) {
}
CodeEdit::CodeEdit() {
/* Indent management */
auto_indent_prefixes.insert(':');
auto_indent_prefixes.insert('{');
auto_indent_prefixes.insert('[');
auto_indent_prefixes.insert('(');
/* Text Direction */
set_layout_direction(LAYOUT_DIRECTION_LTR);
set_text_direction(TEXT_DIRECTION_LTR);

View file

@ -53,6 +53,19 @@ public:
};
private:
/* Indent management */
int indent_size = 4;
String indent_text = "\t";
bool auto_indent = false;
Set<char32_t> auto_indent_prefixes;
bool indent_using_spaces = false;
int _calculate_spaces_till_next_left_indent(int p_column) const;
int _calculate_spaces_till_next_right_indent(int p_column) const;
void _new_line(bool p_split_current_line = true, bool p_above = false);
/* Main Gutter */
enum MainGutterType {
MAIN_GUTTER_BREAKPOINT = 0x01,
@ -206,6 +219,27 @@ protected:
public:
virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
/* Indent management */
void set_indent_size(const int p_size);
int get_indent_size() const;
void set_indent_using_spaces(const bool p_use_spaces);
bool is_indent_using_spaces() const;
void set_auto_indent_enabled(bool p_enabled);
bool is_auto_indent_enabled() const;
void set_auto_indent_prefixes(const TypedArray<String> &p_prefixes);
TypedArray<String> get_auto_indent_prefixes() const;
void do_indent();
void do_unindent();
void indent_lines();
void unindent_lines();
virtual void backspace() override;
/* Main Gutter */
void set_draw_breakpoints_gutter(bool p_draw);
bool is_drawing_breakpoints_gutter() const;

View file

@ -102,14 +102,6 @@ static char32_t _get_right_pair_symbol(char32_t c) {
return 0;
}
static int _find_first_non_whitespace_column_of_line(const String &line) {
int left = 0;
while (left < line.length() && _is_whitespace(line[left])) {
left++;
}
return left;
}
///////////////////////////////////////////////////////////////////////////////
void TextEdit::Text::set_font(const Ref<Font> &p_font) {
@ -120,8 +112,12 @@ void TextEdit::Text::set_font_size(int p_font_size) {
font_size = p_font_size;
}
void TextEdit::Text::set_indent_size(int p_indent_size) {
indent_size = p_indent_size;
void TextEdit::Text::set_tab_size(int p_tab_size) {
tab_size = p_tab_size;
}
int TextEdit::Text::get_tab_size() const {
return tab_size;
}
void TextEdit::Text::set_font_features(const Dictionary &p_features) {
@ -204,9 +200,9 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_
}
// Apply tab align.
if (indent_size > 0) {
if (tab_size > 0) {
Vector<float> tabs;
tabs.push_back(font->get_char_size(' ', 0, font_size).width * indent_size);
tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size);
text.write[p_line].data_buf->tab_align(tabs);
}
}
@ -214,9 +210,9 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_
void TextEdit::Text::invalidate_all_lines() {
for (int i = 0; i < text.size(); i++) {
text.write[i].data_buf->set_width(width);
if (indent_size > 0) {
if (tab_size > 0) {
Vector<float> tabs;
tabs.push_back(font->get_char_size(' ', 0, font_size).width * indent_size);
tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size);
text.write[i].data_buf->tab_align(tabs);
}
}
@ -822,7 +818,7 @@ void TextEdit::_notification(int p_what) {
if (draw_minimap) {
int minimap_visible_lines = _get_minimap_visible_rows();
int minimap_line_height = (minimap_char_size.y + minimap_line_spacing);
int minimap_tab_size = minimap_char_size.x * indent_size;
int minimap_tab_size = minimap_char_size.x * text.get_tab_size();
// calculate viewport size and y offset
int viewport_height = (draw_amount - 1) * minimap_line_height;
@ -1695,7 +1691,13 @@ void TextEdit::_consume_backspace_for_pair_symbol(int prev_line, int prev_column
}
}
void TextEdit::backspace_at_cursor() {
void TextEdit::backspace() {
ScriptInstance *si = get_script_instance();
if (si && si->has_method("_backspace")) {
si->call("_backspace");
return;
}
if (readonly) {
return;
}
@ -1704,34 +1706,15 @@ void TextEdit::backspace_at_cursor() {
return;
}
if (is_selection_active()) {
delete_selection();
return;
}
int prev_line = cursor.column ? cursor.line : cursor.line - 1;
int prev_column = cursor.column ? (cursor.column - 1) : (text[cursor.line - 1].length());
if (cursor.line != prev_line) {
for (int i = 0; i < gutters.size(); i++) {
if (!gutters[i].overwritable) {
continue;
}
if (text.get_line_gutter_text(cursor.line, i) != "") {
text.set_line_gutter_text(prev_line, i, text.get_line_gutter_text(cursor.line, i));
text.set_line_gutter_item_color(prev_line, i, text.get_line_gutter_item_color(cursor.line, i));
}
if (text.get_line_gutter_icon(cursor.line, i).is_valid()) {
text.set_line_gutter_icon(prev_line, i, text.get_line_gutter_icon(cursor.line, i));
text.set_line_gutter_item_color(prev_line, i, text.get_line_gutter_item_color(cursor.line, i));
}
if (text.get_line_gutter_metadata(cursor.line, i) != "") {
text.set_line_gutter_metadata(prev_line, i, text.get_line_gutter_metadata(cursor.line, i));
}
if (text.is_line_gutter_clickable(cursor.line, i)) {
text.set_line_gutter_clickable(prev_line, i, true);
}
}
}
merge_gutters(cursor.line, prev_line);
if (is_line_hidden(cursor.line)) {
set_line_as_hidden(prev_line, true);
@ -1742,168 +1725,13 @@ void TextEdit::backspace_at_cursor() {
_is_pair_left_symbol(text[cursor.line][cursor.column - 1])) {
_consume_backspace_for_pair_symbol(prev_line, prev_column);
} else {
// Handle space indentation.
if (cursor.column != 0 && indent_using_spaces) {
// Check if there are no other chars before cursor, just indentation.
bool unindent = true;
int i = 0;
while (i < cursor.column && i < text[cursor.line].length()) {
if (!_is_whitespace(text[cursor.line][i])) {
unindent = false;
break;
}
i++;
}
// Then we can remove all spaces as a single character.
if (unindent) {
// We want to remove spaces up to closest indent, or whole indent if cursor is pointing at it.
int spaces_to_delete = _calculate_spaces_till_next_left_indent(cursor.column);
prev_column = cursor.column - spaces_to_delete;
_remove_text(cursor.line, prev_column, cursor.line, cursor.column);
} else {
_remove_text(prev_line, prev_column, cursor.line, cursor.column);
}
} else {
_remove_text(prev_line, prev_column, cursor.line, cursor.column);
}
_remove_text(prev_line, prev_column, cursor.line, cursor.column);
}
cursor_set_line(prev_line, false, true);
cursor_set_column(prev_column);
}
void TextEdit::indent_selected_lines_right() {
int start_line;
int end_line;
// This value informs us by how much we changed selection position by indenting right.
// Default is 1 for tab indentation.
int selection_offset = 1;
begin_complex_operation();
if (is_selection_active()) {
start_line = get_selection_from_line();
end_line = get_selection_to_line();
} else {
start_line = cursor.line;
end_line = start_line;
}
// Ignore if the cursor is not past the first column.
if (is_selection_active() && get_selection_to_column() == 0) {
selection_offset = 0;
end_line--;
}
for (int i = start_line; i <= end_line; i++) {
String line_text = get_line(i);
if (line_text.size() == 0 && is_selection_active()) {
continue;
}
if (indent_using_spaces) {
// We don't really care where selection is - we just need to know indentation level at the beginning of the line.
int left = _find_first_non_whitespace_column_of_line(line_text);
int spaces_to_add = _calculate_spaces_till_next_right_indent(left);
// Since we will add these many spaces, we want to move the whole selection and cursor by this much.
selection_offset = spaces_to_add;
for (int j = 0; j < spaces_to_add; j++) {
line_text = ' ' + line_text;
}
} else {
line_text = '\t' + line_text;
}
set_line(i, line_text);
}
// Fix selection and cursor being off after shifting selection right.
if (is_selection_active()) {
select(selection.from_line, selection.from_column + selection_offset, selection.to_line, selection.to_column + selection_offset);
}
cursor_set_column(cursor.column + selection_offset, false);
end_complex_operation();
update();
}
void TextEdit::indent_selected_lines_left() {
int start_line;
int end_line;
// Moving cursor and selection after unindenting can get tricky because
// changing content of line can move cursor 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 = selection.to_column;
int initial_cursor_column = cursor.column;
begin_complex_operation();
if (is_selection_active()) {
start_line = get_selection_from_line();
end_line = get_selection_to_line();
} else {
start_line = cursor.line;
end_line = start_line;
}
// Ignore if the cursor is not past the first column.
if (is_selection_active() && get_selection_to_column() == 0) {
end_line--;
}
String first_line_text = get_line(start_line);
String last_line_text = get_line(end_line);
for (int i = start_line; i <= end_line; i++) {
String line_text = get_line(i);
if (line_text.begins_with("\t")) {
line_text = line_text.substr(1, line_text.length());
set_line(i, line_text);
removed_characters = 1;
} else if (line_text.begins_with(" ")) {
// When unindenting we aim to remove spaces before line that has selection no matter what is selected,
// so we start of by finding first non whitespace character of line
int left = _find_first_non_whitespace_column_of_line(line_text);
// 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(left);
line_text = line_text.substr(spaces_to_remove, line_text.length());
set_line(i, line_text);
removed_characters = spaces_to_remove;
}
}
if (is_selection_active()) {
// Fix selection being off by one on the first line.
if (first_line_text != get_line(start_line)) {
select(selection.from_line, selection.from_column - removed_characters,
selection.to_line, initial_selection_end_column);
}
// Fix selection being off by one on the last line.
if (last_line_text != get_line(end_line)) {
select(selection.from_line, selection.from_column,
selection.to_line, initial_selection_end_column - removed_characters);
}
}
cursor_set_column(initial_cursor_column - removed_characters, false);
end_complex_operation();
update();
}
int TextEdit::_calculate_spaces_till_next_left_indent(int column) {
int spaces_till_indent = column % indent_size;
if (spaces_till_indent == 0) {
spaces_till_indent = indent_size;
}
return spaces_till_indent;
}
int TextEdit::_calculate_spaces_till_next_right_indent(int column) {
return indent_size - column % indent_size;
}
void TextEdit::_swap_current_input_direction() {
if (input_direction == TEXT_DIRECTION_LTR) {
input_direction = TEXT_DIRECTION_RTL;
@ -1919,90 +1747,8 @@ void TextEdit::_new_line(bool p_split_current_line, bool p_above) {
return;
}
String ins = "\n";
// Keep indentation.
int space_count = 0;
for (int i = 0; i < cursor.column; i++) {
if (text[cursor.line][i] == '\t') {
if (indent_using_spaces) {
ins += space_indent;
} else {
ins += "\t";
}
space_count = 0;
} else if (text[cursor.line][i] == ' ') {
space_count++;
if (space_count == indent_size) {
if (indent_using_spaces) {
ins += space_indent;
} else {
ins += "\t";
}
space_count = 0;
}
} else {
break;
}
}
bool brace_indent = false;
// No need to indent if we are going upwards.
if (auto_indent && !p_above) {
// Indent once again if previous line will end with ':','{','[','(' and the line is not a comment
// (i.e. colon/brace precedes current cursor position).
if (cursor.column > 0) {
bool indent_char_found = false;
bool should_indent = false;
char indent_char = ':';
char c = text[cursor.line][cursor.column];
for (int i = 0; i < cursor.column; i++) {
c = text[cursor.line][i];
switch (c) {
case ':':
case '{':
case '[':
case '(':
indent_char_found = true;
should_indent = true;
indent_char = c;
continue;
}
if (indent_char_found && is_line_comment(cursor.line)) {
should_indent = true;
break;
} else if (indent_char_found && !_is_whitespace(c)) {
should_indent = false;
indent_char_found = false;
}
}
if (!is_line_comment(cursor.line) && should_indent) {
if (indent_using_spaces) {
ins += space_indent;
} else {
ins += "\t";
}
// No need to move the brace below if we are not taking the text with us.
char32_t closing_char = _get_right_pair_symbol(indent_char);
if ((closing_char != 0) && (closing_char == text[cursor.line][cursor.column])) {
if (p_split_current_line) {
brace_indent = true;
ins += "\n" + ins.substr(1, ins.length() - 2);
} else {
brace_indent = false;
ins = "\n" + ins.substr(1, ins.length() - 2);
}
}
}
}
}
begin_complex_operation();
bool first_line = false;
if (!p_split_current_line) {
if (p_above) {
@ -2018,85 +1764,15 @@ void TextEdit::_new_line(bool p_split_current_line, bool p_above) {
}
}
insert_text_at_cursor(ins);
insert_text_at_cursor("\n");
if (first_line) {
cursor_set_line(0);
} else if (brace_indent) {
cursor_set_line(cursor.line - 1, false);
cursor_set_column(text[cursor.line].length());
}
end_complex_operation();
}
void TextEdit::_indent_right() {
if (readonly) {
return;
}
if (is_selection_active()) {
indent_selected_lines_right();
} else {
// Simple indent.
if (indent_using_spaces) {
// Insert only as much spaces as needed till next indentation level.
int spaces_to_add = _calculate_spaces_till_next_right_indent(cursor.column);
String indent_to_insert = String();
for (int i = 0; i < spaces_to_add; i++) {
indent_to_insert = ' ' + indent_to_insert;
}
_insert_text_at_cursor(indent_to_insert);
} else {
_insert_text_at_cursor("\t");
}
}
}
void TextEdit::_indent_left() {
if (readonly) {
return;
}
if (is_selection_active()) {
indent_selected_lines_left();
} else {
// Simple unindent.
int cc = cursor.column;
const String &line = text[cursor.line];
int left = _find_first_non_whitespace_column_of_line(line);
cc = MIN(cc, left);
while (cc < indent_size && cc < left && line[cc] == ' ') {
cc++;
}
if (cc > 0 && cc <= text[cursor.line].length()) {
if (text[cursor.line][cc - 1] == '\t') {
// Tabs unindentation.
_remove_text(cursor.line, cc - 1, cursor.line, cc);
if (cursor.column >= left) {
cursor_set_column(MAX(0, cursor.column - 1));
}
update();
} else {
// Spaces unindentation.
int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc);
if (spaces_to_remove > 0) {
_remove_text(cursor.line, cc - spaces_to_remove, cursor.line, cc);
if (cursor.column > left - spaces_to_remove) { // Inside text?
cursor_set_column(MAX(0, cursor.column - spaces_to_remove));
}
update();
}
}
} else if (cc == 0 && line.length() > 0 && line[0] == '\t') {
_remove_text(cursor.line, 0, cursor.line, 1);
update();
}
}
}
void TextEdit::_move_cursor_left(bool p_select, bool p_move_by_word) {
// Handle selection
if (p_select) {
@ -2331,20 +2007,24 @@ void TextEdit::_move_cursor_page_down(bool p_select) {
}
}
void TextEdit::_backspace(bool p_word, bool p_all_to_left) {
void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) {
if (readonly) {
return;
}
if (is_selection_active()) {
_delete_selection();
if (is_selection_active() || (!p_all_to_left && !p_word)) {
backspace();
return;
}
if (p_all_to_left) {
int cursor_current_column = cursor.column;
cursor.column = 0;
_remove_text(cursor.line, 0, cursor.line, cursor_current_column);
} else if (p_word) {
return;
}
if (p_word) {
int line = cursor.line;
int column = cursor.column;
@ -2360,8 +2040,7 @@ void TextEdit::_backspace(bool p_word, bool p_all_to_left) {
cursor_set_line(line, false);
cursor_set_column(column);
} else {
backspace_at_cursor();
return;
}
}
@ -2371,7 +2050,7 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) {
}
if (is_selection_active()) {
_delete_selection();
delete_selection();
return;
}
int curline_len = text[cursor.line].length();
@ -2416,15 +2095,16 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) {
update();
}
void TextEdit::_delete_selection() {
if (is_selection_active()) {
selection.active = false;
update();
_remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
cursor_set_line(selection.from_line, false, false);
cursor_set_column(selection.from_column);
update();
void TextEdit::delete_selection() {
if (!is_selection_active()) {
return;
}
selection.active = false;
_remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
cursor_set_line(selection.from_line, false, false);
cursor_set_column(selection.from_column);
update();
}
void TextEdit::_move_cursor_document_start(bool p_select) {
@ -2459,7 +2139,7 @@ void TextEdit::_move_cursor_document_end(bool p_select) {
void TextEdit::_handle_unicode_character(uint32_t unicode, bool p_had_selection) {
if (p_had_selection) {
_delete_selection();
delete_selection();
}
// Remove the old character if in insert mode and no selection.
@ -2942,31 +2622,19 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
return;
}
// INDENTATION.
if (k->is_action("ui_text_dedent", true)) {
_indent_left();
accept_event();
return;
}
if (k->is_action("ui_text_indent", true)) {
_indent_right();
accept_event();
return;
}
// BACKSPACE AND DELETE.
if (k->is_action("ui_text_backspace_all_to_left", true)) {
_backspace(false, true);
_do_backspace(false, true);
accept_event();
return;
}
if (k->is_action("ui_text_backspace_word", true)) {
_backspace(true);
_do_backspace(true);
accept_event();
return;
}
if (k->is_action("ui_text_backspace", true)) {
_backspace();
_do_backspace();
accept_event();
return;
}
@ -4540,6 +4208,39 @@ bool TextEdit::is_gutter_overwritable(int p_gutter) const {
return gutters[p_gutter].overwritable;
}
void TextEdit::merge_gutters(int p_from_line, int p_to_line) {
ERR_FAIL_INDEX(p_from_line, text.size());
ERR_FAIL_INDEX(p_to_line, text.size());
if (p_from_line == p_to_line) {
return;
}
for (int i = 0; i < gutters.size(); i++) {
if (!gutters[i].overwritable) {
continue;
}
if (text.get_line_gutter_text(p_from_line, i) != "") {
text.set_line_gutter_text(p_to_line, i, text.get_line_gutter_text(p_from_line, i));
text.set_line_gutter_item_color(p_to_line, i, text.get_line_gutter_item_color(p_from_line, i));
}
if (text.get_line_gutter_icon(p_from_line, i).is_valid()) {
text.set_line_gutter_icon(p_to_line, i, text.get_line_gutter_icon(p_from_line, i));
text.set_line_gutter_item_color(p_to_line, i, text.get_line_gutter_item_color(p_from_line, i));
}
if (text.get_line_gutter_metadata(p_from_line, i) != "") {
text.set_line_gutter_metadata(p_to_line, i, text.get_line_gutter_metadata(p_from_line, i));
}
if (text.is_line_gutter_clickable(p_from_line, i)) {
text.set_line_gutter_clickable(p_to_line, i, true);
}
}
update();
}
void TextEdit::set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback) {
ERR_FAIL_INDEX(p_gutter, gutters.size());
ERR_FAIL_NULL(p_object);
@ -4625,10 +4326,6 @@ Color TextEdit::get_line_background_color(int p_line) {
return text.get_line_background_color(p_line);
}
void TextEdit::set_auto_indent(bool p_auto_indent) {
auto_indent = p_auto_indent;
}
void TextEdit::cut() {
if (readonly) {
return;
@ -4644,7 +4341,7 @@ void TextEdit::cut() {
_remove_text(cursor.line, 0, cursor.line + 1, 0);
} else {
_remove_text(cursor.line, 0, cursor.line, text[cursor.line].length());
backspace_at_cursor();
backspace();
cursor_set_line(cursor.line + 1);
}
@ -5191,7 +4888,6 @@ int TextEdit::get_last_unhidden_line() const {
int TextEdit::get_indent_level(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
// Counts number of tabs and spaces before line starts.
int tab_count = 0;
int whitespace_count = 0;
int line_length = text[p_line].size();
@ -5204,28 +4900,17 @@ int TextEdit::get_indent_level(int p_line) const {
break;
}
}
return tab_count * indent_size + whitespace_count;
return tab_count * text.get_tab_size() + whitespace_count;
}
bool TextEdit::is_line_comment(int p_line) const {
// Checks to see if this line is the start of a comment.
ERR_FAIL_INDEX_V(p_line, text.size(), false);
int TextEdit::get_first_non_whitespace_column(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
int line_length = text[p_line].size();
for (int i = 0; i < line_length - 1; i++) {
if (_is_whitespace(text[p_line][i])) {
continue;
}
if (_is_symbol(text[p_line][i])) {
if (text[p_line][i] == '\\') {
i++; // Skip quoted anything.
continue;
}
return text[p_line][i] == '#' || (i + 1 < line_length && text[p_line][i] == '/' && text[p_line][i + 1] == '/');
}
break;
int col = 0;
while (col < text[p_line].length() && _is_whitespace(text[p_line][col])) {
col++;
}
return false;
return col;
}
int TextEdit::get_line_count() const {
@ -5401,32 +5086,18 @@ void TextEdit::_push_current_op() {
}
}
void TextEdit::set_indent_using_spaces(const bool p_use_spaces) {
indent_using_spaces = p_use_spaces;
}
bool TextEdit::is_indent_using_spaces() const {
return indent_using_spaces;
}
void TextEdit::set_indent_size(const int p_size) {
ERR_FAIL_COND_MSG(p_size <= 0, "Indend size must be greater than 0.");
if (indent_size != p_size) {
indent_size = p_size;
text.set_indent_size(p_size);
text.invalidate_all_lines();
void TextEdit::set_tab_size(const int p_size) {
ERR_FAIL_COND_MSG(p_size <= 0, "Tab size must be greater than 0.");
if (p_size == text.get_tab_size()) {
return;
}
space_indent = "";
for (int i = 0; i < p_size; i++) {
space_indent += " ";
}
text.set_tab_size(p_size);
text.invalidate_all_lines();
update();
}
int TextEdit::get_indent_size() {
return indent_size;
int TextEdit::get_tab_size() const {
return text.get_tab_size();
}
void TextEdit::set_draw_tabs(bool p_draw) {
@ -6019,6 +5690,11 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_language", "language"), &TextEdit::set_language);
ClassDB::bind_method(D_METHOD("get_language"), &TextEdit::get_language);
ClassDB::bind_method(D_METHOD("get_first_non_whitespace_column", "line"), &TextEdit::get_first_non_whitespace_column);
ClassDB::bind_method(D_METHOD("get_indent_level", "line"), &TextEdit::get_indent_level);
ClassDB::bind_method(D_METHOD("set_tab_size", "size"), &TextEdit::set_tab_size);
ClassDB::bind_method(D_METHOD("get_tab_size"), &TextEdit::get_tab_size);
ClassDB::bind_method(D_METHOD("set_text", "text"), &TextEdit::set_text);
ClassDB::bind_method(D_METHOD("insert_text_at_cursor", "text"), &TextEdit::insert_text_at_cursor);
@ -6073,6 +5749,10 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &TextEdit::set_selecting_enabled);
ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &TextEdit::is_selecting_enabled);
ClassDB::bind_method(D_METHOD("delete_selection"), &TextEdit::delete_selection);
ClassDB::bind_method(D_METHOD("backspace"), &TextEdit::backspace);
BIND_VMETHOD(MethodInfo("_backspace"));
ClassDB::bind_method(D_METHOD("cut"), &TextEdit::cut);
ClassDB::bind_method(D_METHOD("copy"), &TextEdit::copy);
ClassDB::bind_method(D_METHOD("paste"), &TextEdit::paste);
@ -6128,6 +5808,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_gutter_clickable", "gutter"), &TextEdit::is_gutter_clickable);
ClassDB::bind_method(D_METHOD("set_gutter_overwritable", "gutter", "overwritable"), &TextEdit::set_gutter_overwritable);
ClassDB::bind_method(D_METHOD("is_gutter_overwritable", "gutter"), &TextEdit::is_gutter_overwritable);
ClassDB::bind_method(D_METHOD("merge_gutters", "from_line", "to_line"), &TextEdit::merge_gutters);
ClassDB::bind_method(D_METHOD("set_gutter_custom_draw", "column", "object", "callback"), &TextEdit::set_gutter_custom_draw);
// Line gutters.
@ -6254,7 +5935,7 @@ TextEdit::TextEdit() {
_update_caches();
set_default_cursor_shape(CURSOR_IBEAM);
text.set_indent_size(indent_size);
text.set_tab_size(text.get_tab_size());
text.clear();
h_scroll = memnew(HScrollBar);

View file

@ -112,11 +112,12 @@ private:
int width = -1;
int indent_size = 4;
int tab_size = 4;
int gutter_count = 0;
public:
void set_indent_size(int p_indent_size);
void set_tab_size(int p_tab_size);
int get_tab_size() const;
void set_font(const Ref<Font> &p_font);
void set_font_size(int p_font_size);
void set_font_features(const Dictionary &p_features);
@ -259,9 +260,6 @@ private:
int max_chars = 0;
bool readonly = true; // Initialise to opposite first, so we get past the early-out in set_readonly.
bool indent_using_spaces = false;
int indent_size = 4;
String space_indent = " ";
Timer *caret_blink_timer;
bool caret_blink_enabled = false;
@ -296,7 +294,6 @@ private:
bool scroll_past_end_of_file_enabled = false;
bool brace_matching_enabled = false;
bool highlight_current_line = false;
bool auto_indent = false;
String cut_copy_line;
bool insert_mode = false;
@ -420,14 +417,9 @@ private:
void _clear();
int _calculate_spaces_till_next_left_indent(int column);
int _calculate_spaces_till_next_right_indent(int column);
// Methods used in shortcuts
void _swap_current_input_direction();
void _new_line(bool p_split_current = true, bool p_above = false);
void _indent_right();
void _indent_left();
void _move_cursor_left(bool p_select, bool p_move_by_word = false);
void _move_cursor_right(bool p_select, bool p_move_by_word = false);
void _move_cursor_up(bool p_select);
@ -436,9 +428,8 @@ private:
void _move_cursor_to_line_end(bool p_select);
void _move_cursor_page_up(bool p_select);
void _move_cursor_page_down(bool p_select);
void _backspace(bool p_word = false, bool p_all_to_left = false);
void _do_backspace(bool p_word = false, bool p_all_to_left = false);
void _delete(bool p_word = false, bool p_all_to_right = false);
void _delete_selection();
void _move_cursor_document_start(bool p_select);
void _move_cursor_document_end(bool p_select);
void _handle_unicode_character(uint32_t unicode, bool p_had_selection);
@ -522,6 +513,8 @@ public:
void set_gutter_overwritable(int p_gutter, bool p_overwritable);
bool is_gutter_overwritable(int p_gutter) const;
void merge_gutters(int p_from_line, int p_to_line);
void set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback);
// Line gutters.
@ -635,12 +628,9 @@ public:
bool has_ime_text() const;
void set_line(int line, String new_text);
int get_row_height() const;
void backspace_at_cursor();
void indent_selected_lines_left();
void indent_selected_lines_right();
int get_indent_level(int p_line) const;
bool is_line_comment(int p_line) const;
int get_first_non_whitespace_column(int p_line) const;
inline void set_scroll_pass_end_of_file(bool p_enabled) {
scroll_past_end_of_file_enabled = p_enabled;
@ -653,7 +643,6 @@ public:
brace_matching_enabled = p_enabled;
update();
}
void set_auto_indent(bool p_auto_indent);
void center_viewport_to_cursor();
@ -699,6 +688,9 @@ public:
void clear();
void delete_selection();
virtual void backspace();
void cut();
void copy();
void paste();
@ -730,10 +722,8 @@ public:
void redo();
void clear_undo_history();
void set_indent_using_spaces(const bool p_use_spaces);
bool is_indent_using_spaces() const;
void set_indent_size(const int p_size);
int get_indent_size();
void set_tab_size(const int p_size);
int get_tab_size() const;
void set_draw_tabs(bool p_draw);
bool is_drawing_tabs() const;
void set_draw_spaces(bool p_draw);