Add comment and string tracking to CodeEdit
This commit is contained in:
parent
c9ce5367e3
commit
680dc9e81a
5 changed files with 657 additions and 21 deletions
|
@ -203,6 +203,26 @@ void ScriptTextEditor::_set_theme_for_script() {
|
|||
CodeEdit *text_edit = code_editor->get_text_editor();
|
||||
text_edit->get_syntax_highlighter()->update_cache();
|
||||
|
||||
List<String> strings;
|
||||
script->get_language()->get_string_delimiters(&strings);
|
||||
text_edit->clear_string_delimiters();
|
||||
for (List<String>::Element *E = strings.front(); E; E = E->next()) {
|
||||
String string = E->get();
|
||||
String beg = string.get_slice(" ", 0);
|
||||
String end = string.get_slice_count(" ") > 1 ? string.get_slice(" ", 1) : String();
|
||||
text_edit->add_string_delimiter(beg, end, end == "");
|
||||
}
|
||||
|
||||
List<String> comments;
|
||||
script->get_language()->get_comment_delimiters(&comments);
|
||||
text_edit->clear_comment_delimiters();
|
||||
for (List<String>::Element *E = comments.front(); E; E = E->next()) {
|
||||
String comment = E->get();
|
||||
String beg = comment.get_slice(" ", 0);
|
||||
String end = comment.get_slice_count(" ") > 1 ? comment.get_slice(" ", 1) : String();
|
||||
text_edit->add_comment_delimiter(beg, end, end == "");
|
||||
}
|
||||
|
||||
/* add keywords for auto completion */
|
||||
// singleton autoloads (as types, just as engine singletons are)
|
||||
Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
|
||||
|
|
|
@ -154,6 +154,10 @@ void ShaderTextEditor::_load_theme_settings() {
|
|||
syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
|
||||
syntax_highlighter->add_color_region("//", "", comment_color, true);
|
||||
|
||||
text_editor->clear_comment_delimiters();
|
||||
text_editor->add_comment_delimiter("/*", "*/", false);
|
||||
text_editor->add_comment_delimiter("//", "", true);
|
||||
|
||||
if (warnings_panel) {
|
||||
// Warnings panel
|
||||
warnings_panel->add_theme_font_override("normal_font", EditorNode::get_singleton()->get_gui_base()->get_theme_font("main", "EditorFonts"));
|
||||
|
|
|
@ -848,6 +848,10 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
|
|||
expression_syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
|
||||
expression_syntax_highlighter->add_color_region("//", "", comment_color, true);
|
||||
|
||||
expression_box->clear_comment_delimiters();
|
||||
expression_box->add_comment_delimiter("/*", "*/", false);
|
||||
expression_box->add_comment_delimiter("//", "", true);
|
||||
|
||||
expression_box->set_text(expression);
|
||||
expression_box->set_context_menu_enabled(false);
|
||||
expression_box->set_draw_line_numbers(true);
|
||||
|
@ -2984,6 +2988,10 @@ void VisualShaderEditor::_notification(int p_what) {
|
|||
syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
|
||||
syntax_highlighter->add_color_region("//", "", comment_color, true);
|
||||
|
||||
preview_text->clear_comment_delimiters();
|
||||
preview_text->add_comment_delimiter("/*", "*/", false);
|
||||
preview_text->add_comment_delimiter("//", "", true);
|
||||
|
||||
error_text->add_theme_font_override("font", get_theme_font("status_source", "EditorFonts"));
|
||||
error_text->add_theme_font_size_override("font_size", get_theme_font_size("status_source_size", "EditorFonts"));
|
||||
error_text->add_theme_color_override("font_color", get_theme_color("error_color", "Editor"));
|
||||
|
|
|
@ -30,6 +30,12 @@
|
|||
|
||||
#include "code_edit.h"
|
||||
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
static bool _is_whitespace(char32_t c) {
|
||||
return c == '\t' || c == ' ';
|
||||
}
|
||||
|
||||
void CodeEdit::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_THEME_CHANGED:
|
||||
|
@ -275,6 +281,175 @@ void CodeEdit::_fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_regi
|
|||
folded_icon->draw_rect(get_canvas_item(), p_region, false, folding_color);
|
||||
}
|
||||
|
||||
/* Delimiters */
|
||||
// Strings
|
||||
void CodeEdit::add_string_delimiter(const String &p_start_key, const String &p_end_key, bool p_line_only) {
|
||||
_add_delimiter(p_start_key, p_end_key, p_line_only, TYPE_STRING);
|
||||
}
|
||||
|
||||
void CodeEdit::remove_string_delimiter(const String &p_start_key) {
|
||||
_remove_delimiter(p_start_key, TYPE_STRING);
|
||||
}
|
||||
|
||||
bool CodeEdit::has_string_delimiter(const String &p_start_key) const {
|
||||
return _has_delimiter(p_start_key, TYPE_STRING);
|
||||
}
|
||||
|
||||
void CodeEdit::set_string_delimiters(const TypedArray<String> &p_string_delimiters) {
|
||||
_set_delimiters(p_string_delimiters, TYPE_STRING);
|
||||
}
|
||||
|
||||
void CodeEdit::clear_string_delimiters() {
|
||||
_clear_delimiters(TYPE_STRING);
|
||||
}
|
||||
|
||||
TypedArray<String> CodeEdit::get_string_delimiters() const {
|
||||
return _get_delimiters(TYPE_STRING);
|
||||
}
|
||||
|
||||
int CodeEdit::is_in_string(int p_line, int p_column) const {
|
||||
return _is_in_delimiter(p_line, p_column, TYPE_STRING);
|
||||
}
|
||||
|
||||
// Comments
|
||||
void CodeEdit::add_comment_delimiter(const String &p_start_key, const String &p_end_key, bool p_line_only) {
|
||||
_add_delimiter(p_start_key, p_end_key, p_line_only, TYPE_COMMENT);
|
||||
}
|
||||
|
||||
void CodeEdit::remove_comment_delimiter(const String &p_start_key) {
|
||||
_remove_delimiter(p_start_key, TYPE_COMMENT);
|
||||
}
|
||||
|
||||
bool CodeEdit::has_comment_delimiter(const String &p_start_key) const {
|
||||
return _has_delimiter(p_start_key, TYPE_COMMENT);
|
||||
}
|
||||
|
||||
void CodeEdit::set_comment_delimiters(const TypedArray<String> &p_comment_delimiters) {
|
||||
_set_delimiters(p_comment_delimiters, TYPE_COMMENT);
|
||||
}
|
||||
|
||||
void CodeEdit::clear_comment_delimiters() {
|
||||
_clear_delimiters(TYPE_COMMENT);
|
||||
}
|
||||
|
||||
TypedArray<String> CodeEdit::get_comment_delimiters() const {
|
||||
return _get_delimiters(TYPE_COMMENT);
|
||||
}
|
||||
|
||||
int CodeEdit::is_in_comment(int p_line, int p_column) const {
|
||||
return _is_in_delimiter(p_line, p_column, TYPE_COMMENT);
|
||||
}
|
||||
|
||||
String CodeEdit::get_delimiter_start_key(int p_delimiter_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_delimiter_idx, delimiters.size(), "");
|
||||
return delimiters[p_delimiter_idx].start_key;
|
||||
}
|
||||
|
||||
String CodeEdit::get_delimiter_end_key(int p_delimiter_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_delimiter_idx, delimiters.size(), "");
|
||||
return delimiters[p_delimiter_idx].end_key;
|
||||
}
|
||||
|
||||
Point2 CodeEdit::get_delimiter_start_position(int p_line, int p_column) const {
|
||||
if (delimiters.size() == 0) {
|
||||
return Point2(-1, -1);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_line, get_line_count(), Point2(-1, -1));
|
||||
ERR_FAIL_COND_V(p_column - 1 > get_line(p_line).size(), Point2(-1, -1));
|
||||
|
||||
Point2 start_position;
|
||||
start_position.y = -1;
|
||||
start_position.x = -1;
|
||||
|
||||
bool in_region = ((p_line <= 0 || delimiter_cache[p_line - 1].size() < 1) ? -1 : delimiter_cache[p_line - 1].back()->value()) != -1;
|
||||
|
||||
/* Check the keys for this line. */
|
||||
for (Map<int, int>::Element *E = delimiter_cache[p_line].front(); E; E = E->next()) {
|
||||
if (E->key() > p_column) {
|
||||
break;
|
||||
}
|
||||
in_region = E->value() != -1;
|
||||
start_position.x = in_region ? E->key() : -1;
|
||||
}
|
||||
|
||||
/* Region was found on this line and is not a multiline continuation. */
|
||||
if (start_position.x != -1 && start_position.x != get_line(p_line).length() + 1) {
|
||||
start_position.y = p_line;
|
||||
return start_position;
|
||||
}
|
||||
|
||||
/* Not in a region */
|
||||
if (!in_region) {
|
||||
return start_position;
|
||||
}
|
||||
|
||||
/* Region starts on a previous line */
|
||||
for (int i = p_line - 1; i >= 0; i--) {
|
||||
if (delimiter_cache[i].size() < 1) {
|
||||
continue;
|
||||
}
|
||||
start_position.y = i;
|
||||
start_position.x = delimiter_cache[i].back()->key();
|
||||
|
||||
/* Make sure it's not a multiline continuation. */
|
||||
if (start_position.x != get_line(i).length() + 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return start_position;
|
||||
}
|
||||
|
||||
Point2 CodeEdit::get_delimiter_end_position(int p_line, int p_column) const {
|
||||
if (delimiters.size() == 0) {
|
||||
return Point2(-1, -1);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_line, get_line_count(), Point2(-1, -1));
|
||||
ERR_FAIL_COND_V(p_column - 1 > get_line(p_line).size(), Point2(-1, -1));
|
||||
|
||||
Point2 end_position;
|
||||
end_position.y = -1;
|
||||
end_position.x = -1;
|
||||
|
||||
int region = (p_line <= 0 || delimiter_cache[p_line - 1].size() < 1) ? -1 : delimiter_cache[p_line - 1].back()->value();
|
||||
|
||||
/* Check the keys for this line. */
|
||||
for (Map<int, int>::Element *E = delimiter_cache[p_line].front(); E; E = E->next()) {
|
||||
end_position.x = (E->value() == -1) ? E->key() : -1;
|
||||
if (E->key() > p_column) {
|
||||
break;
|
||||
}
|
||||
region = E->value();
|
||||
}
|
||||
|
||||
/* Region was found on this line and is not a multiline continuation. */
|
||||
if (region != -1 && end_position.x != -1 && (delimiters[region].line_only || end_position.x != get_line(p_line).length() + 1)) {
|
||||
end_position.y = p_line;
|
||||
return end_position;
|
||||
}
|
||||
|
||||
/* Not in a region */
|
||||
if (region == -1) {
|
||||
end_position.x = -1;
|
||||
return end_position;
|
||||
}
|
||||
|
||||
/* Region ends on a later line */
|
||||
for (int i = p_line + 1; i < get_line_count(); i++) {
|
||||
if (delimiter_cache[i].size() < 1 || delimiter_cache[i].front()->value() != -1) {
|
||||
continue;
|
||||
}
|
||||
end_position.x = delimiter_cache[i].front()->key();
|
||||
|
||||
/* Make sure it's not a multiline continuation. */
|
||||
if (get_line(i).length() > 0 && end_position.x != get_line(i).length() + 1) {
|
||||
end_position.y = i;
|
||||
break;
|
||||
}
|
||||
end_position.x = -1;
|
||||
}
|
||||
return end_position;
|
||||
}
|
||||
|
||||
void CodeEdit::_bind_methods() {
|
||||
/* Main Gutter */
|
||||
ClassDB::bind_method(D_METHOD("_main_gutter_draw_callback"), &CodeEdit::_main_gutter_draw_callback);
|
||||
|
@ -320,6 +495,36 @@ void CodeEdit::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_draw_fold_gutter", "enable"), &CodeEdit::set_draw_fold_gutter);
|
||||
ClassDB::bind_method(D_METHOD("is_drawing_fold_gutter"), &CodeEdit::is_drawing_fold_gutter);
|
||||
|
||||
/* Delimiters */
|
||||
// Strings
|
||||
ClassDB::bind_method(D_METHOD("add_string_delimiter", "start_key", "end_key", "line_only"), &CodeEdit::add_string_delimiter, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("remove_string_delimiter", "start_key"), &CodeEdit::remove_string_delimiter);
|
||||
ClassDB::bind_method(D_METHOD("has_string_delimiter", "start_key"), &CodeEdit::has_string_delimiter);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_string_delimiters", "string_delimiters"), &CodeEdit::set_string_delimiters);
|
||||
ClassDB::bind_method(D_METHOD("clear_string_delimiters"), &CodeEdit::clear_string_delimiters);
|
||||
ClassDB::bind_method(D_METHOD("get_string_delimiters"), &CodeEdit::get_string_delimiters);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_in_string", "line", "column"), &CodeEdit::is_in_string, DEFVAL(-1));
|
||||
|
||||
// Comments
|
||||
ClassDB::bind_method(D_METHOD("add_comment_delimiter", "start_key", "end_key", "line_only"), &CodeEdit::add_comment_delimiter, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("remove_comment_delimiter", "start_key"), &CodeEdit::remove_comment_delimiter);
|
||||
ClassDB::bind_method(D_METHOD("has_comment_delimiter", "start_key"), &CodeEdit::has_comment_delimiter);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_comment_delimiters", "comment_delimiters"), &CodeEdit::set_comment_delimiters);
|
||||
ClassDB::bind_method(D_METHOD("clear_comment_delimiters"), &CodeEdit::clear_comment_delimiters);
|
||||
ClassDB::bind_method(D_METHOD("get_comment_delimiters"), &CodeEdit::get_comment_delimiters);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_in_comment", "line", "column"), &CodeEdit::is_in_comment, DEFVAL(-1));
|
||||
|
||||
// Util
|
||||
ClassDB::bind_method(D_METHOD("get_delimiter_start_key", "delimiter_index"), &CodeEdit::get_delimiter_start_key);
|
||||
ClassDB::bind_method(D_METHOD("get_delimiter_end_key", "delimiter_index"), &CodeEdit::get_delimiter_end_key);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_delimiter_start_postion", "line", "column"), &CodeEdit::get_delimiter_start_position);
|
||||
ClassDB::bind_method(D_METHOD("get_delimiter_end_postion", "line", "column"), &CodeEdit::get_delimiter_end_position);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_breakpoints_gutter"), "set_draw_breakpoints_gutter", "is_drawing_breakpoints_gutter");
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_bookmarks"), "set_draw_bookmarks_gutter", "is_drawing_bookmarks_gutter");
|
||||
|
@ -331,6 +536,10 @@ void CodeEdit::_bind_methods() {
|
|||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter");
|
||||
|
||||
ADD_GROUP("Delimiters", "delimiter_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "delimiter_strings"), "set_string_delimiters", "get_string_delimiters");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "delimiter_comments"), "set_comment_delimiters", "get_comment_delimiters");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "line")));
|
||||
}
|
||||
|
||||
|
@ -360,7 +569,332 @@ void CodeEdit::_gutter_clicked(int p_line, int p_gutter) {
|
|||
}
|
||||
}
|
||||
|
||||
void CodeEdit::_update_gutter_indexes() {
|
||||
for (int i = 0; i < get_gutter_count(); i++) {
|
||||
if (get_gutter_name(i) == "main_gutter") {
|
||||
main_gutter = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (get_gutter_name(i) == "line_numbers") {
|
||||
line_number_gutter = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (get_gutter_name(i) == "fold_gutter") {
|
||||
fold_gutter = i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Delimiters */
|
||||
void CodeEdit::_update_delimiter_cache(int p_from_line, int p_to_line) {
|
||||
if (delimiters.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int line_count = get_line_count();
|
||||
if (p_to_line == -1) {
|
||||
p_to_line = line_count;
|
||||
}
|
||||
|
||||
int start_line = MIN(p_from_line, p_to_line);
|
||||
int end_line = MAX(p_from_line, p_to_line);
|
||||
|
||||
/* Make sure delimiter_cache has all the lines. */
|
||||
if (start_line != end_line) {
|
||||
if (p_to_line < p_from_line) {
|
||||
for (int i = end_line; i > start_line; i--) {
|
||||
delimiter_cache.remove(i);
|
||||
}
|
||||
} else {
|
||||
for (int i = start_line; i < end_line; i++) {
|
||||
delimiter_cache.insert(i, Map<int, int>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int in_region = -1;
|
||||
for (int i = start_line; i < MIN(end_line + 1, line_count); i++) {
|
||||
int current_end_region = (i <= 0 || delimiter_cache[i].size() < 1) ? -1 : delimiter_cache[i].back()->value();
|
||||
in_region = (i <= 0 || delimiter_cache[i - 1].size() < 1) ? -1 : delimiter_cache[i - 1].back()->value();
|
||||
|
||||
const String &str = get_line(i);
|
||||
const int line_length = str.length();
|
||||
delimiter_cache.write[i].clear();
|
||||
|
||||
if (str.length() == 0) {
|
||||
if (in_region != -1) {
|
||||
delimiter_cache.write[i][0] = in_region;
|
||||
}
|
||||
if (i == end_line && current_end_region != in_region) {
|
||||
end_line = MIN(end_line++, line_count);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int end_region = -1;
|
||||
for (int j = 0; j < line_length; j++) {
|
||||
int from = j;
|
||||
for (; from < line_length; from++) {
|
||||
if (str[from] == '\\') {
|
||||
from++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* check if we are in entering a region */
|
||||
bool same_line = false;
|
||||
if (in_region == -1) {
|
||||
for (int d = 0; d < delimiters.size(); d++) {
|
||||
/* check there is enough room */
|
||||
int chars_left = line_length - from;
|
||||
int start_key_length = delimiters[d].start_key.length();
|
||||
int end_key_length = delimiters[d].end_key.length();
|
||||
if (chars_left < start_key_length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* search the line */
|
||||
bool match = true;
|
||||
const char32_t *start_key = delimiters[d].start_key.get_data();
|
||||
for (int k = 0; k < start_key_length; k++) {
|
||||
if (start_key[k] != str[from + k]) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
same_line = true;
|
||||
in_region = d;
|
||||
delimiter_cache.write[i][from + 1] = d;
|
||||
from += start_key_length;
|
||||
|
||||
/* check if it's the whole line */
|
||||
if (end_key_length == 0 || delimiters[d].line_only || from + end_key_length > line_length) {
|
||||
j = line_length;
|
||||
if (delimiters[d].line_only) {
|
||||
delimiter_cache.write[i][line_length + 1] = -1;
|
||||
} else {
|
||||
end_region = in_region;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (j == line_length || in_region == -1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we are in one find the end key */
|
||||
/* search the line */
|
||||
int region_end_index = -1;
|
||||
int end_key_length = delimiters[in_region].end_key.length();
|
||||
const char32_t *end_key = delimiters[in_region].end_key.get_data();
|
||||
for (; from < line_length; from++) {
|
||||
if (line_length - from < end_key_length) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_symbol(str[from])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str[from] == '\\') {
|
||||
from++;
|
||||
continue;
|
||||
}
|
||||
|
||||
region_end_index = from;
|
||||
for (int k = 0; k < end_key_length; k++) {
|
||||
if (end_key[k] != str[from + k]) {
|
||||
region_end_index = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (region_end_index != -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
j = from + (end_key_length - 1);
|
||||
end_region = (region_end_index == -1) ? in_region : -1;
|
||||
if (!same_line || region_end_index != -1) {
|
||||
delimiter_cache.write[i][j + 1] = end_region;
|
||||
}
|
||||
in_region = -1;
|
||||
}
|
||||
|
||||
if (i == end_line && current_end_region != end_region) {
|
||||
end_line = MIN(end_line++, line_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) const {
|
||||
if (delimiters.size() == 0) {
|
||||
return -1;
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_line, get_line_count(), 0);
|
||||
|
||||
int region = (p_line <= 0 || delimiter_cache[p_line - 1].size() < 1) ? -1 : delimiter_cache[p_line - 1].back()->value();
|
||||
bool in_region = region != -1 && delimiters[region].type == p_type;
|
||||
for (Map<int, int>::Element *E = delimiter_cache[p_line].front(); E; E = E->next()) {
|
||||
/* If column is specified, loop untill the key is larger then the column. */
|
||||
if (p_column != -1) {
|
||||
if (E->key() > p_column) {
|
||||
break;
|
||||
}
|
||||
in_region = E->value() != -1 && delimiters[E->value()].type == p_type;
|
||||
region = in_region ? E->value() : -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If no column, calulate if the entire line is a region */
|
||||
/* excluding whitespace. */
|
||||
const String line = get_line(p_line);
|
||||
if (!in_region) {
|
||||
if (E->value() == -1 || delimiters[E->value()].type != p_type) {
|
||||
break;
|
||||
}
|
||||
|
||||
region = E->value();
|
||||
in_region = true;
|
||||
for (int i = E->key() - 2; i >= 0; i--) {
|
||||
if (!_is_whitespace(line[i])) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (delimiters[region].line_only) {
|
||||
return region;
|
||||
}
|
||||
|
||||
int end_col = E->key();
|
||||
if (E->value() != -1) {
|
||||
if (!E->next()) {
|
||||
return region;
|
||||
}
|
||||
end_col = E->next()->key();
|
||||
}
|
||||
|
||||
for (int i = end_col; i < line.length(); i++) {
|
||||
if (!_is_whitespace(line[i])) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return region;
|
||||
}
|
||||
return in_region ? region : -1;
|
||||
}
|
||||
|
||||
void CodeEdit::_add_delimiter(const String &p_start_key, const String &p_end_key, bool p_line_only, DelimiterType p_type) {
|
||||
if (p_start_key.length() > 0) {
|
||||
for (int i = 0; i < p_start_key.length(); i++) {
|
||||
ERR_FAIL_COND_MSG(!is_symbol(p_start_key[i]), "delimiter must start with a symbol");
|
||||
}
|
||||
}
|
||||
|
||||
if (p_end_key.length() > 0) {
|
||||
for (int i = 0; i < p_end_key.length(); i++) {
|
||||
ERR_FAIL_COND_MSG(!is_symbol(p_end_key[i]), "delimiter must end with a symbol");
|
||||
}
|
||||
}
|
||||
|
||||
int at = 0;
|
||||
for (int i = 0; i < delimiters.size(); i++) {
|
||||
ERR_FAIL_COND_MSG(delimiters[i].start_key == p_start_key, "delimiter with start key '" + p_start_key + "' already exists.");
|
||||
if (p_start_key.length() < delimiters[i].start_key.length()) {
|
||||
at++;
|
||||
}
|
||||
}
|
||||
|
||||
Delimiter delimiter;
|
||||
delimiter.type = p_type;
|
||||
delimiter.start_key = p_start_key;
|
||||
delimiter.end_key = p_end_key;
|
||||
delimiter.line_only = p_line_only || p_end_key == "";
|
||||
delimiters.insert(at, delimiter);
|
||||
if (!setting_delimiters) {
|
||||
delimiter_cache.clear();
|
||||
_update_delimiter_cache();
|
||||
}
|
||||
}
|
||||
|
||||
void CodeEdit::_remove_delimiter(const String &p_start_key, DelimiterType p_type) {
|
||||
for (int i = 0; i < delimiters.size(); i++) {
|
||||
if (delimiters[i].start_key != p_start_key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (delimiters[i].type != p_type) {
|
||||
break;
|
||||
}
|
||||
|
||||
delimiters.remove(i);
|
||||
if (!setting_delimiters) {
|
||||
delimiter_cache.clear();
|
||||
_update_delimiter_cache();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool CodeEdit::_has_delimiter(const String &p_start_key, DelimiterType p_type) const {
|
||||
for (int i = 0; i < delimiters.size(); i++) {
|
||||
if (delimiters[i].start_key == p_start_key) {
|
||||
return delimiters[i].type == p_type;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CodeEdit::_set_delimiters(const TypedArray<String> &p_delimiters, DelimiterType p_type) {
|
||||
setting_delimiters = true;
|
||||
_clear_delimiters(p_type);
|
||||
|
||||
for (int i = 0; i < p_delimiters.size(); i++) {
|
||||
String key = p_delimiters[i].is_null() ? "" : p_delimiters[i];
|
||||
|
||||
const String start_key = key.get_slice(" ", 0);
|
||||
const String end_key = key.get_slice_count(" ") > 1 ? key.get_slice(" ", 1) : String();
|
||||
|
||||
_add_delimiter(start_key, end_key, end_key == "", p_type);
|
||||
}
|
||||
setting_delimiters = false;
|
||||
_update_delimiter_cache();
|
||||
}
|
||||
|
||||
void CodeEdit::_clear_delimiters(DelimiterType p_type) {
|
||||
for (int i = delimiters.size() - 1; i >= 0; i--) {
|
||||
if (delimiters[i].type == p_type) {
|
||||
delimiters.remove(i);
|
||||
}
|
||||
}
|
||||
delimiter_cache.clear();
|
||||
}
|
||||
|
||||
TypedArray<String> CodeEdit::_get_delimiters(DelimiterType p_type) const {
|
||||
TypedArray<String> r_delimiters;
|
||||
for (int i = 0; i < delimiters.size(); i++) {
|
||||
if (delimiters[i].type != p_type) {
|
||||
continue;
|
||||
}
|
||||
r_delimiters.push_back(delimiters[i].start_key + (delimiters[i].end_key.empty() ? "" : " " + delimiters[i].end_key));
|
||||
}
|
||||
return r_delimiters;
|
||||
}
|
||||
|
||||
void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) {
|
||||
_update_delimiter_cache(p_from_line, p_to_line);
|
||||
|
||||
if (p_from_line == p_to_line) {
|
||||
return;
|
||||
}
|
||||
|
@ -392,25 +926,6 @@ void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) {
|
|||
}
|
||||
}
|
||||
|
||||
void CodeEdit::_update_gutter_indexes() {
|
||||
for (int i = 0; i < get_gutter_count(); i++) {
|
||||
if (get_gutter_name(i) == "main_gutter") {
|
||||
main_gutter = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (get_gutter_name(i) == "line_numbers") {
|
||||
line_number_gutter = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (get_gutter_name(i) == "fold_gutter") {
|
||||
fold_gutter = i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CodeEdit::CodeEdit() {
|
||||
/* Text Direction */
|
||||
set_layout_direction(LAYOUT_DIRECTION_LTR);
|
||||
|
|
|
@ -80,10 +80,72 @@ private:
|
|||
void _fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_region);
|
||||
|
||||
void _gutter_clicked(int p_line, int p_gutter);
|
||||
void _lines_edited_from(int p_from_line, int p_to_line);
|
||||
|
||||
void _update_gutter_indexes();
|
||||
|
||||
/* Delimiters */
|
||||
enum DelimiterType {
|
||||
TYPE_STRING,
|
||||
TYPE_COMMENT,
|
||||
};
|
||||
|
||||
struct Delimiter {
|
||||
DelimiterType type;
|
||||
String start_key = "";
|
||||
String end_key = "";
|
||||
bool line_only = true;
|
||||
};
|
||||
bool setting_delimiters = false;
|
||||
Vector<Delimiter> delimiters;
|
||||
/*
|
||||
* Vector entry per line, contains a Map of column numbers to delimiter index, -1 marks the end of a region.
|
||||
* e.g the following text will be stored as so:
|
||||
*
|
||||
* 0: nothing here
|
||||
* 1:
|
||||
* 2: # test
|
||||
* 3: "test" text "multiline
|
||||
* 4:
|
||||
* 5: test
|
||||
* 6: string"
|
||||
*
|
||||
* Vector [
|
||||
* 0 = []
|
||||
* 1 = []
|
||||
* 2 = [
|
||||
* 1 = 1
|
||||
* 6 = -1
|
||||
* ]
|
||||
* 3 = [
|
||||
* 1 = 0
|
||||
* 6 = -1
|
||||
* 13 = 0
|
||||
* ]
|
||||
* 4 = [
|
||||
* 0 = 0
|
||||
* ]
|
||||
* 5 = [
|
||||
* 5 = 0
|
||||
* ]
|
||||
* 6 = [
|
||||
* 7 = -1
|
||||
* ]
|
||||
* ]
|
||||
*/
|
||||
Vector<Map<int, int>> delimiter_cache;
|
||||
|
||||
void _update_delimiter_cache(int p_from_line = 0, int p_to_line = -1);
|
||||
int _is_in_delimiter(int p_line, int p_column, DelimiterType p_type) const;
|
||||
|
||||
void _add_delimiter(const String &p_start_key, const String &p_end_key, bool p_line_only, DelimiterType p_type);
|
||||
void _remove_delimiter(const String &p_start_key, DelimiterType p_type);
|
||||
bool _has_delimiter(const String &p_start_key, DelimiterType p_type) const;
|
||||
|
||||
void _set_delimiters(const TypedArray<String> &p_delimiters, DelimiterType p_type);
|
||||
void _clear_delimiters(DelimiterType p_type);
|
||||
TypedArray<String> _get_delimiters(DelimiterType p_type) const;
|
||||
|
||||
void _lines_edited_from(int p_from_line, int p_to_line);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
|
@ -128,6 +190,33 @@ public:
|
|||
void set_draw_fold_gutter(bool p_draw);
|
||||
bool is_drawing_fold_gutter() const;
|
||||
|
||||
/* Delimiters */
|
||||
void add_string_delimiter(const String &p_start_key, const String &p_end_key, bool p_line_only = false);
|
||||
void remove_string_delimiter(const String &p_start_key);
|
||||
bool has_string_delimiter(const String &p_start_key) const;
|
||||
|
||||
void set_string_delimiters(const TypedArray<String> &p_string_delimiters);
|
||||
void clear_string_delimiters();
|
||||
TypedArray<String> get_string_delimiters() const;
|
||||
|
||||
int is_in_string(int p_line, int p_column = -1) const;
|
||||
|
||||
void add_comment_delimiter(const String &p_start_key, const String &p_end_key, bool p_line_only = false);
|
||||
void remove_comment_delimiter(const String &p_start_key);
|
||||
bool has_comment_delimiter(const String &p_start_key) const;
|
||||
|
||||
void set_comment_delimiters(const TypedArray<String> &p_comment_delimiters);
|
||||
void clear_comment_delimiters();
|
||||
TypedArray<String> get_comment_delimiters() const;
|
||||
|
||||
int is_in_comment(int p_line, int p_column = -1) const;
|
||||
|
||||
String get_delimiter_start_key(int p_delimiter_idx) const;
|
||||
String get_delimiter_end_key(int p_delimiter_idx) const;
|
||||
|
||||
Point2 get_delimiter_start_position(int p_line, int p_column) const;
|
||||
Point2 get_delimiter_end_position(int p_line, int p_column) const;
|
||||
|
||||
CodeEdit();
|
||||
~CodeEdit();
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue