Skip to content

Commit 50f2a75

Browse files
LSP: Fix file URI handling + warn about workspace project mismatch
1 parent 4b36c04 commit 50f2a75

File tree

4 files changed

+143
-9
lines changed

4 files changed

+143
-9
lines changed

editor/editor_file_system.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1930,9 +1930,16 @@ bool EditorFileSystem::_find_file(const String &p_file, EditorFileSystemDirector
19301930

19311931
int cpos = -1;
19321932
for (int i = 0; i < fs->files.size(); i++) {
1933-
if (fs->files[i]->file == file) {
1934-
cpos = i;
1935-
break;
1933+
if (fs_case_sensitive) {
1934+
if (fs->files[i]->file == file) {
1935+
cpos = i;
1936+
break;
1937+
}
1938+
} else {
1939+
if (fs->files[i]->file.to_lower() == file.to_lower()) {
1940+
cpos = i;
1941+
break;
1942+
}
19361943
}
19371944
}
19381945

modules/gdscript/language_server/gdscript_language_protocol.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,35 @@ void GDScriptLanguageProtocol::_bind_methods() {
171171
Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
172172
LSP::InitializeResult ret;
173173

174+
{
175+
// Warn if the workspace root does not match with the project that is currently open in Godot,
176+
// since it might lead to unexpected behavior, like wrong warnings about duplicate class names.
177+
178+
String root = "";
179+
Variant root_uri_var = p_params["rootUri"];
180+
Variant root_var = p_params["rootUri"];
181+
if (root_uri_var.is_string()) {
182+
root = get_workspace()->get_file_path(root_uri_var);
183+
} else if (root_var.is_string()) {
184+
root = root_var;
185+
}
186+
187+
if (ProjectSettings::get_singleton()->localize_path(root) != "res://") {
188+
LSP::ShowMessageParams params{
189+
LSP::MessageType::Warning,
190+
"The GDScript Language Server might not work correctly with other projects than the one opened in Godot."
191+
};
192+
Variant res = make_notification("window/showMessage", params.to_json());
193+
194+
Ref<LSPeer> peer = clients.get(latest_client_id);
195+
if (peer.is_valid()) {
196+
String msg = res.to_json_string();
197+
msg = format_output(msg);
198+
(*peer)->res_queue.push_back(msg.utf8());
199+
}
200+
}
201+
}
202+
174203
String root_uri = p_params["rootUri"];
175204
String root = p_params["rootPath"];
176205
bool is_same_workspace;

modules/gdscript/language_server/gdscript_workspace.cpp

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -559,16 +559,76 @@ Error GDScriptWorkspace::parse_local_script(const String &p_path) {
559559
}
560560

561561
String GDScriptWorkspace::get_file_path(const String &p_uri) const {
562-
String path = p_uri.uri_decode();
563-
String base_uri = root_uri.uri_decode();
564-
path = path.replacen(base_uri + "/", "res://");
565-
return path;
562+
int port;
563+
String scheme, host, encoded_path, fragment;
564+
p_uri.parse_url(scheme, host, port, encoded_path, fragment);
565+
566+
// TODO: Make the parsing RFC-3986 compliant.
567+
if (scheme != "file" && scheme != "file:" && scheme != "file://") {
568+
// The language server does only support the file protocol.
569+
return "";
570+
}
571+
572+
// Treat host like authority for now and ignore the port. It's an edge case for invalid file URI's anyway.
573+
if (host != "" && host != "localhost") {
574+
// The language server does not support nonlocal files.
575+
return "";
576+
}
577+
578+
// If query or fragment are present, the URI is not a valid file URI as per RFC-8089.
579+
// We currently don't handle the query and it will be part of the path. However,
580+
// this should not be a problem for a correct file URI.
581+
if (fragment != "") {
582+
return "";
583+
}
584+
585+
String simple_path = encoded_path.uri_decode().simplify_path();
586+
587+
Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
588+
589+
String local_path;
590+
591+
String os_res = ProjectSettings::get_singleton()->globalize_path("res://");
592+
int offset = 0;
593+
594+
while (offset <= simple_path.length()) {
595+
offset = simple_path.find_char('/', offset);
596+
597+
String part = simple_path.substr(0, offset);
598+
599+
if (!part.is_empty()) {
600+
bool is_equal = dir->is_equivalent(os_res, part);
601+
602+
if (is_equal) {
603+
String res_file = ProjectSettings::get_singleton()->localize_path(os_res.path_join(simple_path.substr(offset)));
604+
605+
EditorFileSystemDirectory *editor_dir;
606+
int file_idx;
607+
editor_dir = EditorFileSystem::get_singleton()->find_file(res_file, &file_idx);
608+
if (editor_dir) {
609+
return editor_dir->get_file_path(file_idx);
610+
} else {
611+
return res_file;
612+
}
613+
}
614+
}
615+
616+
if (offset == -1) {
617+
break;
618+
}
619+
offset += 1;
620+
}
621+
622+
// Not inside the project dir.
623+
local_path = simple_path;
624+
625+
return local_path;
566626
}
567627

568628
String GDScriptWorkspace::get_file_uri(const String &p_path) const {
569629
String uri = p_path;
570-
uri = uri.replace("res://", root_uri + "/");
571-
return uri;
630+
631+
return "file:///" + ProjectSettings::get_singleton()->globalize_path(p_path).lstrip("/").uri_encode();
572632
}
573633

574634
void GDScriptWorkspace::publish_diagnostics(const String &p_path) {

modules/gdscript/language_server/godot_lsp.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,25 @@ struct ReferenceContext {
238238
bool includeDeclaration = false;
239239
};
240240

241+
struct ShowMessageParams {
242+
/**
243+
* The message type. See {@link MessageType}.
244+
*/
245+
int type;
246+
247+
/**
248+
* The actual message.
249+
*/
250+
String message;
251+
252+
_FORCE_INLINE_ Dictionary to_json() const {
253+
Dictionary dict;
254+
dict["type"] = type;
255+
dict["message"] = message;
256+
return dict;
257+
}
258+
};
259+
241260
struct ReferenceParams : TextDocumentPositionParams {
242261
ReferenceContext context;
243262
};
@@ -405,6 +424,25 @@ static const int Full = 1;
405424
static const int Incremental = 2;
406425
}; // namespace TextDocumentSyncKind
407426

427+
namespace MessageType {
428+
/**
429+
* An error message.
430+
*/
431+
static const int Error = 1;
432+
/**
433+
* A warning message.
434+
*/
435+
static const int Warning = 2;
436+
/**
437+
* An information message.
438+
*/
439+
static const int Info = 3;
440+
/**
441+
* A log message.
442+
*/
443+
static const int Log = 4;
444+
}; // namespace MessageType
445+
408446
/**
409447
* Completion options.
410448
*/

0 commit comments

Comments
 (0)