@tool extends CodeEdit enum CompletionKind { IGNORE, FORMATTING, COLOR, COMMAND, CLASS_REFERENCE, REFERENCE_START, REFERENCE_END, REFERENCING_TAG, ANNOTATION, } const Completions = preload("res://addons/bbcode_edit.editor/completions_db/completions.gd") const Scraper = preload("res://addons/bbcode_edit.editor/editor_interface_scraper.gd") const ACTION_TOGGLE_BOLD = &"bbcode_edit/toggle_bold" const ACTION_TOGGLE_ITALIC = &"bbcode_edit/toggle_italic" const ACTION_TOGGLE_UNDERLINE = &"bbcode_edit/toggle_underline" const ACTION_TOGGLE_STRIKE = &"bbcode_edit/toggle_strike" const TOGGLING_ACTIONS = { ACTION_TOGGLE_BOLD: "b", ACTION_TOGGLE_ITALIC: "i", ACTION_TOGGLE_UNDERLINE: "u", ACTION_TOGGLE_STRIKE: "s", } const BBCODE_COMPLETION_ICON = preload("res://addons/bbcode_edit.editor/bbcode_completion_icon.svg") const COLOR_PICKER_CONTAINER_PATH = ^"_BBCodeEditColorPicker" const COLOR_PICKER_PATH = ^"_BBCodeEditColorPicker/ColorPicker" const MALFORMED = "MALFORMED" const COMMAND_PREFIX_CHAR = "\u0001" const ORDER_PREFIX = "\ufffe" const CLASS_REFERENCE_PREFIX_CHAR = "\uffff" const REFERENCE_START_SUFFIX_CHAR = "\uffff" const REFERENCE_END_SUFFIX_CHAR = "\ufffe" const ANNOTATION_SUFFIX_CHAR = "\ufff0" const _COMMAND_COLOR_PICKER = "color_picker" const CLASS_DOC_ENDERS: Array[String] = [ "##", "signal", "enum", "const", "@export", "var", "@onready", "static", "func", "class ", ] static var REGEX_PARENTHESES = RegEx.create_from_string(r"\(([^)]+)\)") func _init() -> void: set_process_input(true) if has_meta(&"initialized"): return code_completion_requested.connect(add_completion_options) text_changed.connect(_on_text_changed) code_completion_prefixes += ["["] # Use assignation because append don't work set_meta(&"initialized", true) func add_completion_options() -> void: #print("Code completion options requested") var line_i: int = get_caret_line() var line: String = get_line(line_i) var column_i: int = get_caret_column() var comment_i: int = is_in_comment(line_i, column_i) if comment_i == -1 or get_delimiter_start_key(comment_i) != "##": if line[column_i-1] == "[": #print_rich("[color=red]Emergency cancel[/color]") cancel_code_completion() #print_rich("[color=red]not in bbcode completion[/color]") return #print_rich("[color=red]in bbcode completion[/color]") var to_test: String = trim_doc_comment_start(line.left(column_i)) var line_only: String = to_test if line_only[0] == "@" and not ( line_only.begins_with("@tutorial: ") or line_only.begins_with("@deprecated: ") or line_only.begins_with("@experimental: ") ): add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, "@deprecated" + ANNOTATION_SUFFIX_CHAR, "deprecated\n## ", get_theme_color(&"font-color"), Scraper.get_icon(&"StatusError"), ) add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, "@deprecated: Some explaination" + ANNOTATION_SUFFIX_CHAR, "deprecated: ", get_theme_color(&"font-color"), Scraper.get_icon(&"StatusError"), ) add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, "@experimental" + ANNOTATION_SUFFIX_CHAR, "experimental\n## ", get_theme_color(&"font-color"), Scraper.get_icon(&"NodeWarning"), ) add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, "@experimental: Some explaination" + ANNOTATION_SUFFIX_CHAR, "experimental: ", get_theme_color(&"font-color"), Scraper.get_icon(&"NodeWarning"), ) var class_comment_end_line: int = 0 while not _is_line_class_doc_ender(get_line(class_comment_end_line)): class_comment_end_line += 1 while get_line(class_comment_end_line).begins_with("##"): class_comment_end_line += 1 if _is_line_class_doc_ender(get_line(class_comment_end_line)): class_comment_end_line = -1 if line_i < class_comment_end_line: add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, "@tutorial: https://example.com" + ANNOTATION_SUFFIX_CHAR, "tutorial: https://", get_theme_color(&"font-color"), Scraper.get_icon(&"ExternalLink"), ) add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, "@tutorial(Title): https://example.com" + ANNOTATION_SUFFIX_CHAR, "tutorial(|): https://", get_theme_color(&"font-color"), Scraper.get_icon(&"ExternalLink"), ) update_code_completion_options(true) return var prev_line_i: int = line_i - 1 var prev_line: String = get_line(prev_line_i).strip_edges(true, false) while prev_line.begins_with("##"): to_test = prev_line.trim_prefix("##").strip_edges() + " " + to_test prev_line_i -= 1 prev_line = get_line(prev_line_i).strip_edges(true, false) to_test = to_test.split("]")[-1]#.split("=")[-1] #print_rich("to_test:[color=magenta][code] ", to_test) if "[" not in to_test: #print("No BRACKET") update_code_completion_options(true) return var describes_i: int = line_i while is_in_comment(describes_i) != -1: describes_i += 1 var describes: String = get_line(describes_i) if check_parameter_completions(to_test, describes_i, describes): return var font_color: Color = get_theme_color(&"font_color") if line_only[0] == "[": add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, "Note:", "b]Note:[/b] ", font_color, Scraper.get_icon(&"TextMesh"), ) add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, "Warning:", "b]Warning:[/b] ", font_color, Scraper.get_icon(&"TextMesh"), ) add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, "Example:", "b]Example:[/b] ", font_color, Scraper.get_icon(&"TextMesh"), ) # TODO only propose valid tags var completions: Array[String] = ( Completions.TAGS_UNIVERSAL + Completions.TAGS_DOC_COMMENT_FORMATTING # TODO MAYBE: I have to refactor everything related to tag availability. #+ Completions.TAGS_RICH_TEXT_LABEL ) var displays: Array[String] = [] displays.assign(completions.map(_bracket)) #print("First completion is: ", completions[0]) for i in completions.size(): add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, displays[i].replace("|", ""), completions[i], font_color, BBCODE_COMPLETION_ICON, ) var reference_completions: Array[String] = Completions.TAGS_DOC_COMMENT_REFERENCE var reference_displays: Array[String] = [] for completion in reference_completions: reference_displays.append(_bracket(completion.trim_suffix("|") + "Class.name")) var reference_icon: Texture2D = Scraper.get_reference_icon() for i in reference_completions.size(): add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, reference_displays[i].replace("|", ""), reference_completions[i], font_color, reference_icon, ) if describes.begins_with("func "): add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, "[param name]", "param |", font_color, reference_icon, ) var class_completions := Completions.get_class_completions() for i in len(class_completions.names): var name_: String = class_completions.names[i] add_code_completion_option( CodeEdit.KIND_CLASS, CLASS_REFERENCE_PREFIX_CHAR + "[" + name_ + "]", name_ + "||", font_color, class_completions.icons[i], ) update_code_completion_options(true) # NEEDED so that `[` triggers popup func _is_line_class_doc_ender(line: String) -> bool: for doc_ender in CLASS_DOC_ENDERS: if line.begins_with(doc_ender): return true return false func _bracket(string: String) -> String: return "[" + string + "]" func trim_doc_comment_start(line: String) -> String: return line.strip_edges(true, false).trim_prefix("##").strip_edges(true, false) func check_parameter_completions(to_test: String, describes_i: int, describes: String) -> bool: to_test = to_test.split("[")[-1] var parts: PackedStringArray = to_test.split(" ", false) var parameters: PackedStringArray = PackedStringArray() var values: PackedStringArray = PackedStringArray() for part in parts: # TODO MAYBE impleement sub parameter handling ? (e.g. [font otv="wght=200,wdth=400"]) var split: PackedStringArray = part.split("=", 1) parameters.append(split[0]) values.append(split[1] if split.size() == 2 else MALFORMED) #print_rich("Parameters:[color=magenta] ", parameters) #print_rich("Values:[color=magenta] ", values) if parameters.is_empty(): return false if parameters.size() == 1 and values[0] != "MALFORMED": var value: String = values[0] match parameters[0]: "color": if value.begins_with(HEX_PREFIX) and value.substr(HEX_PREFIX.length()).is_valid_html_color(): add_hex_color(value.substr(HEX_PREFIX.length()), true) elif value.is_valid_html_color(): if value.is_valid_int(): insert_text(HEX_PREFIX, get_caret_line(), get_caret_column()-value.length()) request_code_completion.call_deferred(true) add_hex_color(value) add_color_completions(value.length()) return true match parameters[0]: "param": if not describes.begins_with("func "): return false if ")" not in describes: var next_line_i: int = describes_i + 1 var next_line: String = get_line(next_line_i) while ")" not in next_line: describes += next_line next_line_i += 1 next_line = get_line(next_line_i) describes += next_line #print_rich("Describes: [color=purple][code]", describes) for part in ( REGEX_PARENTHESES.search(describes).get_string().trim_prefix("(").trim_suffix(")").split(",") ): var param_parts := part.split(":", true, 1) var parameter: String = param_parts[0].strip_edges() add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, parameter + REFERENCE_END_SUFFIX_CHAR, parameter + "||", get_theme_color(&"font_color"), Scraper.get_icon(&"Variant") if param_parts.size() == 1 else Scraper.try_get_icon(param_parts[1].split("=", true, 1)[0].strip_edges(), &"Variant") ) update_code_completion_options(true) return true "member": if parameters.size() >= 2: var path: PackedStringArray = parameters[1].split(".") if path.size() >= 2: if ClassDB.class_exists(path[0]): add_member_completion_from_class_name(path[0]) else: for other_class_ in ProjectSettings.get_global_class_list(): if other_class_["class"] == path[0]: add_member_completion_from_script(load(other_class_["path"])) break update_code_completion_options(true) return true add_member_completion_from_script(EditorInterface.get_script_editor().get_current_script()) add_classes_completion() return true "method": if parameters.size() >= 2: var path: PackedStringArray = parameters[1].split(".") if path.size() >= 2: if ClassDB.class_exists(path[0]): add_method_completion_from_class_name(path[0]) else: for other_class_ in ProjectSettings.get_global_class_list(): if other_class_["class"] == path[0]: add_method_completion_from_script(load(other_class_["path"])) break update_code_completion_options(true) return true add_method_completion_from_script(EditorInterface.get_script_editor().get_current_script()) add_classes_completion() return true "constant": if parameters.size() >= 2: var path: PackedStringArray = parameters[1].split(".") if path.size() >= 2: if ClassDB.class_exists(path[0]): add_constant_completion_from_class_name(path[0]) else: for other_class_ in ProjectSettings.get_global_class_list(): if other_class_["class"] == path[0]: add_constant_completion_from_script(load(other_class_["path"])) break update_code_completion_options(true) return true add_constant_completion_from_script(EditorInterface.get_script_editor().get_current_script()) add_classes_completion() return true "signal": if parameters.size() >= 2: var path: PackedStringArray = parameters[1].split(".") if path.size() >= 2: if ClassDB.class_exists(path[0]): add_signal_completion_from_class_name(path[0]) else: for other_class_ in ProjectSettings.get_global_class_list(): if other_class_["class"] == path[0]: add_signal_completion_from_script(load(other_class_["path"])) break update_code_completion_options(true) return true add_signal_completion_from_script(EditorInterface.get_script_editor().get_current_script()) add_classes_completion() return true "enum": if parameters.size() >= 2: var path: PackedStringArray = parameters[1].split(".") if path.size() >= 2: if ClassDB.class_exists(path[0]): add_enum_completion_from_class_name(path[0]) else: for other_class_ in ProjectSettings.get_global_class_list(): if other_class_["class"] == path[0]: add_enum_completion_from_script(load(other_class_["path"])) break update_code_completion_options(true) return true add_enum_completion_from_script(EditorInterface.get_script_editor().get_current_script()) add_classes_completion() return true return false func substr_clamped_start(string: String, from: int, len: int) -> String: if from < 0: if len != -1: len += from if len < 0: len = 0 from = 0 return string.substr(from, len) const HEX_PREFIX = "0x" func add_hex_color(hex: String, include_prefix: bool = false) -> void: print_rich("Valid color: ", hex, " [color=", hex, "]██████") add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, HEX_PREFIX + hex + " ", HEX_PREFIX + hex if include_prefix else hex, get_theme_color(&"font_color"), Scraper.get_color_icon(), Color.html(hex), ) func add_color_completions(chars_typed: int) -> void: var icon = Scraper.get_color_icon() for color in Completions.COLORS: add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, color, color, get_theme_color(&"font_color"), icon, Color.from_string(color, Color.RED), ) add_code_completion_option( CodeEdit.KIND_PLAIN_TEXT, COMMAND_PREFIX_CHAR + "Bring color picker", _COMMAND_COLOR_PICKER + "," + str(chars_typed), get_theme_color(&"font_color"), EditorInterface.get_base_control().get_theme_icon("ColorPicker", "EditorIcons"), ) update_code_completion_options(true) ## Add completion for classes WITH SUBSCRIPT (aka it will add a dot at the end) func add_classes_completion() -> void: var class_completions := Completions.get_class_completions() for i in len(class_completions.names): var name_: String = class_completions.names[i] add_code_completion_option( CodeEdit.KIND_CLASS, ORDER_PREFIX + name_ + "." + REFERENCE_START_SUFFIX_CHAR, name_ + ".", get_theme_color(&"font_color"), class_completions.icons[i], ) update_code_completion_options(true) func add_member_completion_from_script(class_: Script) -> void: add_members(class_.get_script_property_list()) # Don't show inherited things, because the reference won't work. #add_member_completion_from_class_name(class_.get_instance_base_type()) func add_member_completion_from_class_name(class_: StringName) -> void: add_members(ClassDB.class_get_property_list(class_, true)) func add_members(members: Array[Dictionary]) -> void: for member in members: if member["usage"] & ( PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP ): continue add_code_completion_option( CodeEdit.KIND_MEMBER, member["name"] + REFERENCE_END_SUFFIX_CHAR, member["name"] + "||", get_theme_color(&"font-color"), get_icon_for_member(member), ) func get_icon_for_member(member: Dictionary, fallback: StringName = &"MemberProperty") -> Texture2D: if member["type"] == TYPE_OBJECT: return Scraper.get_class_icon(member["class_name"], fallback) return Scraper.get_builtin_type_icon(member["type"], fallback) func add_method_completion_from_script(class_: Script) -> void: add_methods(class_.get_method_list()) # Don't show inherited things, because the reference won't work. #add_method_completion_from_class_name(class_.get_instance_base_type()) func add_method_completion_from_class_name(class_: StringName) -> void: add_methods(ClassDB.class_get_method_list(class_, true)) func add_methods(methods: Array[Dictionary]) -> void: for method in methods: add_code_completion_option( CodeEdit.KIND_FUNCTION, method["name"] + REFERENCE_END_SUFFIX_CHAR, method["name"] + "||", get_theme_color(&"font-color"), get_icon_for_method(method), ) func get_icon_for_method(method: Dictionary) -> Texture2D: var returned: Dictionary = method["return"] if returned["type"] == TYPE_NIL: return Scraper.get_icon(&"MemberMethod") return get_icon_for_member(returned, &"Function") func add_constant_completion_from_script(class_: Script) -> void: add_constants(class_.get_script_constant_map()) # Don't show inherited things, because the reference won't work. #add_constant_completion_from_class_name(class_.get_instance_base_type()) func add_constant_completion_from_class_name(class_: StringName) -> void: for constant_name in ClassDB.class_get_integer_constant_list(class_, true): add_constants({constant_name: ClassDB.class_get_integer_constant(class_, constant_name)}) func add_constants(constants: Dictionary) -> void: for constant in constants: var value: Variant = constants[constant] var type: int = typeof(value) if type == TYPE_OBJECT and value is Script and value.resource_path.is_empty(): # Inner classes don't work in class continue var display_text: String = constant if type != TYPE_COLOR: var repr: String = var_to_str(value) if repr.is_empty(): if type == TYPE_OBJECT: repr = value.to_string() else: repr = str(repr) if repr.length() > 32: repr = repr.left(32) + "..." display_text += " (" + repr + ")" display_text += REFERENCE_END_SUFFIX_CHAR add_code_completion_option( CodeEdit.KIND_CONSTANT, display_text, constant + "||", get_theme_color(&"font-color"), Scraper.get_type_icon(value, &"MemberConstant"), value ) func add_signal_completion_from_script(class_: Script) -> void: add_signals(class_.get_script_signal_list()) # Don't show inherited things, because the reference won't work. #add_signal_completion_from_class_name(class_.get_instance_base_type()) func add_signal_completion_from_class_name(class_: StringName) -> void: add_signals(ClassDB.class_get_signal_list(class_, true)) func add_signals(signals: Array[Dictionary]) -> void: var icon: Texture2D = Scraper.get_icon(&"MemberSignal") for signal_ in signals: add_code_completion_option( CodeEdit.KIND_SIGNAL, signal_["name"] + REFERENCE_END_SUFFIX_CHAR, signal_["name"] + "||", get_theme_color(&"font-color"), icon, ) func add_enum_completion_from_script(class_: Script) -> void: var map := class_.get_script_constant_map() var probable_enums: PackedStringArray for constant in map: if typeof(map[constant]) == TYPE_DICTIONARY: var candidate: Dictionary = map[constant] if candidate.values().all(_is_int): probable_enums.append(constant) if probable_enums: add_enums(probable_enums) # Don't show inherited things, because the reference won't work. #add_enum_completion_from_class_name(class_.get_instance_base_type()) static func _is_int(value: Variant) -> bool: return typeof(value) == TYPE_INT func add_enum_completion_from_class_name(class_: StringName) -> void: add_enums(ClassDB.class_get_enum_list(class_, true)) func add_enums(enums: PackedStringArray) -> void: var icon: Texture2D = Scraper.get_icon(&"Enum") for enum_ in enums: add_code_completion_option( CodeEdit.KIND_ENUM, enum_ + REFERENCE_END_SUFFIX_CHAR, enum_ + "||", get_theme_color(&"font-color"), icon, ) func toggle_tag(tag: String) -> void: var prefix: String = "[" + tag + "]" var prefix_len: int = prefix.length() var suffix: String = "[/" + tag + "]" var suffix_len: int = suffix.length() var main_selection_from_column: int = get_selection_from_column() var main_selection_from_line: int = get_selection_from_line() var main_selection_to_column: int = get_selection_to_column() var main_selection_to_line: int = get_selection_to_line() var main_selection_end_line: String = get_line(main_selection_to_line) if ( main_selection_from_column > prefix_len and get_line(main_selection_from_line).substr( main_selection_from_column - prefix_len, prefix_len ) == prefix and main_selection_to_column <= main_selection_end_line.length() - suffix_len and main_selection_end_line.substr( main_selection_to_column, suffix_len ) == suffix ): begin_complex_operation() begin_multicaret_edit() for caret in get_caret_count(): if multicaret_edit_ignore_caret(caret): continue var initial_text: String = get_selected_text(caret) var initial_start_column: int = get_selection_from_column(caret) var initial_end_column: int = get_selection_to_column(caret) select( get_selection_from_line(caret), initial_start_column - prefix_len, get_selection_to_line(caret), initial_end_column + suffix_len, caret ) insert_text_at_caret(initial_text, caret) select( get_selection_from_line(caret), initial_start_column - prefix_len, get_selection_to_line(caret), initial_end_column - prefix_len, caret ) end_multicaret_edit() end_complex_operation() return begin_complex_operation() begin_multicaret_edit() for caret in get_caret_count(): if multicaret_edit_ignore_caret(caret): continue var initial_start_column: int = get_selection_from_column(caret) var initial_end_column: int = get_selection_to_column(caret) insert_text_at_caret(prefix + get_selected_text(caret) + suffix, caret) select( get_selection_from_line(caret), initial_start_column + prefix_len, get_selection_to_line(caret), initial_end_column + prefix_len, caret ) end_multicaret_edit() end_complex_operation() func _confirm_code_completion(replace: bool = false) -> void: var selected_completion: Dictionary = get_code_completion_option(get_code_completion_selected_index()) var display_text: String = selected_completion["display_text"] var prefix: String = display_text[0] if prefix == COMMAND_PREFIX_CHAR: var parts: PackedStringArray = selected_completion["insert_text"].split(",") var chars_to_remove: int = int(parts[1]) if parts.size() >= 2 else 0 if chars_to_remove: for caret in get_caret_count(): var line_i: int = get_caret_line(caret) var line: String = get_line(line_i) var column_i: int = get_caret_column(caret) set_line(line_i, line.left(column_i - chars_to_remove) + line.substr(column_i)) set_caret_column(column_i - chars_to_remove, false, caret) match parts[0]: _COMMAND_COLOR_PICKER: if not has_node(^"BBCODE_EDIT_COLOR_PICKER"): add_child(preload("res://addons/bbcode_edit.editor/color_picker.tscn").instantiate()) var container: PopupPanel = get_node(COLOR_PICKER_CONTAINER_PATH) var picker: ColorPicker = get_node(COLOR_PICKER_PATH) container.position = Vector2(get_pos_at_line_column(get_caret_line(), get_caret_column())) + global_position + Vector2(0, get_line_height()) container.add_theme_stylebox_override(&"panel", EditorInterface.get_base_control().get_theme_stylebox(&"Content", &"EditorStyles")) picker.color_changed.connect(_on_color_picker_color_changed) cancel_code_completion() return var icon: Texture2D = selected_completion["icon"] var suffix: String = display_text[-1] var kind: CompletionKind = ( CompletionKind.FORMATTING if icon == BBCODE_COMPLETION_ICON else CompletionKind.COLOR if icon == Scraper.get_color_icon() else CompletionKind.CLASS_REFERENCE if prefix == CLASS_REFERENCE_PREFIX_CHAR else CompletionKind.REFERENCE_START if suffix == REFERENCE_START_SUFFIX_CHAR else CompletionKind.REFERENCE_END if suffix == REFERENCE_END_SUFFIX_CHAR else CompletionKind.ANNOTATION if suffix == ANNOTATION_SUFFIX_CHAR else CompletionKind.REFERENCING_TAG if icon == Scraper.get_reference_icon() else 0 ) #print_rich("Kind is [color=red][code]", CompletionKind.find_key(kind)) begin_complex_operation() # Don't use the following code, it's a dev crime. # Oops, I just did... # This code block allows to call the code that is meant to be executed # when the virtual method isn't implemented. var script: GDScript = get_script() set_script(null) super.confirm_code_completion(replace) set_script(script) if ( kind == CompletionKind.FORMATTING or kind == CompletionKind.CLASS_REFERENCE or kind == CompletionKind.REFERENCING_TAG ): for caret in get_caret_count(): var line: String = get_line(get_caret_line(caret)) + " " # Add space so that column is in range var column: int = get_caret_column(caret) if not line[column] == "]": insert_text_at_caret("]", caret) # Replace caret at it's previous column set_caret_column(column, false, caret) if kind == CompletionKind.COLOR: var line_i: int = get_caret_line() var line: String = get_line(line_i) var column: int = get_caret_column() var color_start: int = column - selected_completion["display_text"].length() + 1 if line.substr(color_start).begins_with(HEX_PREFIX): set_line( line_i, line.left(color_start) + line.substr(color_start + HEX_PREFIX.length()) ) set_caret_column(column - HEX_PREFIX.length()) for caret in get_caret_count(): set_caret_column(get_caret_column(caret) + 1, false, caret) elif kind: for caret in get_caret_count(): var line_i: int = get_caret_line(caret) var line: String = get_line(line_i) var first_pipe: int = line.find("|") if first_pipe == -1: # Just in case continue var pipe_end: int = first_pipe + 1 while pipe_end < line.length() and line[pipe_end] == "|": pipe_end += 1 set_line(line_i, line.left(first_pipe) + line.substr(pipe_end)) set_caret_column(pipe_end-1, false, caret) end_complex_operation() if kind == CompletionKind.REFERENCING_TAG: var prefixes := code_completion_prefixes code_completion_prefixes += [" "] request_code_completion() code_completion_prefixes = prefixes elif kind and kind != CompletionKind.ANNOTATION: request_code_completion() func _gui_input(event: InputEvent) -> void: if not event.is_pressed() or event.is_echo(): return if has_node(COLOR_PICKER_CONTAINER_PATH): if event is InputEventKey or event is InputEventMouseButton: get_node(COLOR_PICKER_CONTAINER_PATH).free() for action in TOGGLING_ACTIONS: if is_action(event, action): toggle_tag(TOGGLING_ACTIONS[action]) func is_action(event: InputEvent, action: StringName) -> bool: return InputMap.has_action(action) and event.is_action(action, true) func _on_text_changed() -> void: var line_i: int = get_caret_line() var column_i: int = get_caret_column() var line: String = get_line(get_caret_line()) if ( is_in_comment(line_i, column_i) == -1 and is_in_string(line_i, column_i) == -1 and line and line[column_i-1] == "[" ): cancel_code_completion() # Prevent completing when typing array fast func _on_color_picker_color_changed(color: Color) -> void: var hex: String = color.to_html(color.a8 != 255) for caret in get_caret_count(): var line_i: int = get_caret_line(caret) var line: String = get_line(line_i) var column_i: int = get_caret_column(caret) if line[column_i] != "]" and line[column_i] != " ": var to_scan: String = line.substr(column_i) var end: int = to_scan.find("]") if end == -1: end = to_scan.find(" ") else: var other: int = to_scan.find(" ") if other != -1 and other < end: end = other set_line(line_i, line.left(column_i) + to_scan.substr(end)) insert_text_at_caret(hex, caret) set_caret_column(column_i, false, caret)