| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- @tool
- extends Node
- class_name ModToolUtils
- # Utility functions used across the ModTool.
- # ! Not used currently. This can overwrite existing text very easily if the wrong script is shown in the text editor.
- static func reload_script(script: Script, mod_tool_store: ModToolStore) -> void:
- var pending_reloads := mod_tool_store.pending_reloads
- if script.resource_path in pending_reloads:
- var source_code_from_disc := FileAccess.open(script.resource_path, FileAccess.READ).get_as_text()
- var script_editor := EditorInterface.get_script_editor()
- var text_edit: CodeEdit = script_editor.get_current_editor().get_base_editor()
- var column := text_edit.get_caret_column()
- var row := text_edit.get_caret_line()
- var scroll_position_h := text_edit.get_h_scroll_bar().value
- var scroll_position_v := text_edit.get_v_scroll_bar().value
- text_edit.text = source_code_from_disc
- text_edit.set_caret_column(column)
- text_edit.set_caret_line(row)
- text_edit.scroll_horizontal = scroll_position_h
- text_edit.scroll_vertical = scroll_position_v
- text_edit.tag_saved_version()
- pending_reloads.erase(script.resource_path)
- # Takes a file path and an array of file extensions [.txt, .tscn, ..]
- static func is_file_extension(path: String, excluded_extensions: PackedStringArray) -> bool:
- var is_extension := false
- for extension in excluded_extensions:
- var file_name := path.get_file()
- if(extension in file_name):
- is_extension = true
- break
- else:
- is_extension = false
- return is_extension
- # Returns the content of the file from the given path as a string.
- static func file_get_as_text(path: String) -> String:
- var file_access := FileAccess.open(path, FileAccess.READ)
- var content := file_access.get_as_text()
- file_access.close()
- return content
- # Copies a file from a given src to the specified dst path.
- # src = path/to/file.extension
- # dst = other/path/to/file.extension
- static func file_copy(src: String, dst: String) -> void:
- var dst_dir := dst.get_base_dir()
- if not DirAccess.dir_exists_absolute(dst_dir):
- DirAccess.make_dir_recursive_absolute(dst_dir)
- DirAccess.copy_absolute(src, dst)
- # Log error messages
- static func output_error(message) -> void:
- printerr("ModTool Error: " + str(message))
- static func output_info(message) -> void:
- print("ModTool: " + str(message))
- static func save_to_manifest_json(manifest_data: ModManifest, path_manifest: String) -> bool:
- var is_success := _ModLoaderFile._save_string_to_file(
- manifest_data.to_json(),
- path_manifest
- )
- if is_success:
- output_info("Successfully saved manifest.json file!")
- return is_success
- static func make_dir_recursive(dst_dir: String) -> bool:
- var error := DirAccess.make_dir_recursive_absolute(dst_dir)
- if not error == OK:
- output_error("Failed creating directory at %s with error \"%s\"" % [dst_dir, error_string(error)])
- return false
- return true
- # Takes a directory path to get removed.
- # https://www.davidepesce.com/2019/11/04/essential-guide-to-godot-filesystem-api/
- static func remove_recursive(path: String) -> void:
- var directory := DirAccess.open(path)
- if not directory:
- print("Error removing " + path)
- return
- # List directory content
- directory.list_dir_begin()
- var file_name := directory.get_next()
- while file_name != "":
- if directory.current_is_dir():
- remove_recursive(path + "/" + file_name)
- else:
- directory.remove(file_name)
- file_name = directory.get_next()
- # Remove current path
- directory.remove(path)
- static func check_for_hooked_script(script_paths: Array[String], mod_tool_store: ModToolStore) -> int:
- var count := 0
- for script_path in script_paths:
- if mod_tool_store.hooked_scripts.has(script_path):
- count += 1
- return count
- static func quote_string(string: String) -> String:
- var settings: EditorSettings = EditorInterface.get_editor_settings()
- if settings.get_setting("text_editor/completion/use_single_quotes"):
- return "'%s'" % string
- return "\"%s\"" % string
- static func script_has_method(script_path: String, method: String) -> bool:
- var script: Script = load(script_path)
- for script_method in script.get_script_method_list():
- if script_method.name == method:
- return true
- if method in script.source_code:
- return true
- return false
- static func get_index_at_method_end(method_name: String, text: String) -> int:
- var starting_index := text.rfind(method_name)
- # Find the end of the method
- var next_method_line_index := text.find("func ", starting_index)
- var method_end := -1
- if next_method_line_index == -1:
- # Backtrack empty lines from the end of the file
- method_end = text.length() -1
- else:
- # Get the line before the next function line
- method_end = text.rfind("\n", next_method_line_index)
- # Backtrack to the last non-empty line
- var last_non_empty_line_index := method_end
- while last_non_empty_line_index > starting_index:
- last_non_empty_line_index -= 1
- # Remove spaces, tabs and newlines (whitespace) to check if the line really is empty
- if text[last_non_empty_line_index].rstrip("\t\n "):
- break # encountered a filled line
- return last_non_empty_line_index +1
- # Slightly modified version of:
- # https://gist.github.com/willnationsdev/00d97aa8339138fd7ef0d6bd42748f6e
- # Removed .import from the extension filter.
- # p_match is a string that filters the list of files.
- # If p_match_is_regex is false, p_match is directly string-searched against the FILENAME.
- # If it is true, a regex object compiles p_match and runs it against the FILEPATH.
- static func get_flat_view_dict(
- p_dir := "res://",
- p_match := "",
- p_match_file_extensions: Array[StringName] = [],
- p_match_is_regex := false,
- include_empty_dirs := false,
- ignored_dirs: Array[StringName] = []
- ) -> PackedStringArray:
- var data: PackedStringArray = []
- var regex: RegEx
- if p_match_is_regex:
- regex = RegEx.new()
- var _compile_error: int = regex.compile(p_match)
- if not regex.is_valid():
- return data
- var dirs := [p_dir]
- var first := true
- while not dirs.is_empty():
- var dir_name : String = dirs.back()
- var dir := DirAccess.open(dir_name)
- dirs.pop_back()
- if dir_name.lstrip("res://").get_slice("/", 0) in ignored_dirs:
- continue
- if dir:
- var _dirlist_error: int = dir.list_dir_begin()
- var file_name := dir.get_next()
- if include_empty_dirs and not dir_name == p_dir:
- data.append(dir_name)
- while file_name != "":
- if not dir_name == "res://":
- first = false
- # ignore hidden, temporary, or system content
- if not file_name.begins_with(".") and not file_name.get_extension() == "tmp":
- # If a directory, then add to list of directories to visit
- if dir.current_is_dir():
- dirs.push_back(dir.get_current_dir() + "/" + file_name)
- # If a file, check if we already have a record for the same name
- else:
- var path := dir.get_current_dir() + ("/" if not first else "") + file_name
- # grab all
- if not p_match and not p_match_file_extensions:
- data.append(path)
- # grab matching strings
- elif not p_match_is_regex and p_match and file_name.contains(p_match):
- data.append(path)
- # garb matching file extension
- elif p_match_file_extensions and file_name.get_extension() in p_match_file_extensions:
- data.append(path)
- # grab matching regex
- elif p_match_is_regex:
- var regex_match := regex.search(path)
- if regex_match != null:
- data.append(path)
- # Move on to the next file in this directory
- file_name = dir.get_next()
- # We've exhausted all files in this directory. Close the iterator.
- dir.list_dir_end()
- return data
|