mirror of
https://github.com/godotengine/godot-vscode-plugin.git
synced 2025-12-31 13:48:24 +03:00
Add support for uid:// references to hovers and document links (#841)
This commit is contained in:
@@ -65,6 +65,26 @@ type ChangeWorkspaceNotification = {
|
||||
};
|
||||
};
|
||||
|
||||
type DocumentLinkResult = {
|
||||
range: {
|
||||
end: {
|
||||
character: number;
|
||||
line: number;
|
||||
};
|
||||
start: {
|
||||
character: number;
|
||||
line: number;
|
||||
};
|
||||
};
|
||||
target: string;
|
||||
};
|
||||
|
||||
type DocumentLinkResponseMessage = {
|
||||
id: number;
|
||||
jsonrpc: string;
|
||||
result: DocumentLinkResult[];
|
||||
};
|
||||
|
||||
export default class GDScriptLanguageClient extends LanguageClient {
|
||||
public io: MessageIO = new MessageIO();
|
||||
|
||||
@@ -148,12 +168,28 @@ export default class GDScriptLanguageClient extends LanguageClient {
|
||||
showNotification?: boolean,
|
||||
): T {
|
||||
if (type.method === "textDocument/documentSymbol") {
|
||||
if (error.message.includes("selectionRange must be contained in fullRange")) {
|
||||
log.warn(`Request failed for method "${type.method}", suppressing notification - see issue #820`);
|
||||
return super.handleFailedRequest(type, token, error, defaultValue, false);
|
||||
if (
|
||||
error.message.includes("selectionRange must be contained in fullRange")
|
||||
) {
|
||||
log.warn(
|
||||
`Request failed for method "${type.method}", suppressing notification - see issue #820`
|
||||
);
|
||||
return super.handleFailedRequest(
|
||||
type,
|
||||
token,
|
||||
error,
|
||||
defaultValue,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
return super.handleFailedRequest(type, token, error, defaultValue, showNotification);
|
||||
return super.handleFailedRequest(
|
||||
type,
|
||||
token,
|
||||
error,
|
||||
defaultValue,
|
||||
showNotification
|
||||
);
|
||||
}
|
||||
|
||||
private request_filter(message: RequestMessage) {
|
||||
@@ -209,6 +245,32 @@ export default class GDScriptLanguageClient extends LanguageClient {
|
||||
|
||||
(message as HoverResponseMesssage).result.contents.value = value;
|
||||
}
|
||||
} else if (sentMessage.method === "textDocument/documentLink") {
|
||||
const results: DocumentLinkResult[] = (
|
||||
message as DocumentLinkResponseMessage
|
||||
).result;
|
||||
|
||||
if (!results) {
|
||||
return message;
|
||||
}
|
||||
|
||||
const final_result: DocumentLinkResult[] = [];
|
||||
// at this point, Godot's LSP server does not
|
||||
// return a valid path for resources identified
|
||||
// by "uid://""
|
||||
//
|
||||
// this is a dirty hack to remove any "uid://"
|
||||
// document links.
|
||||
//
|
||||
// to provide links for these, we will be relying on
|
||||
// the internal DocumentLinkProvider instead.
|
||||
for (const result of results) {
|
||||
if (!result.target.startsWith("uid://")) {
|
||||
final_result.push(result);
|
||||
}
|
||||
}
|
||||
|
||||
(message as DocumentLinkResponseMessage).result = final_result;
|
||||
}
|
||||
|
||||
return message;
|
||||
@@ -248,7 +310,10 @@ export default class GDScriptLanguageClient extends LanguageClient {
|
||||
return message;
|
||||
}
|
||||
|
||||
public async get_symbol_at_position(uri: vscode.Uri, position: vscode.Position) {
|
||||
public async get_symbol_at_position(
|
||||
uri: vscode.Uri,
|
||||
position: vscode.Position
|
||||
) {
|
||||
const params = {
|
||||
textDocument: { uri: uri.toString() },
|
||||
position: { line: position.line, character: position.character },
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
type ExtensionContext,
|
||||
} from "vscode";
|
||||
import { SceneParser } from "../scene_tools";
|
||||
import { convert_resource_path_to_uri, createLogger } from "../utils";
|
||||
import { convert_resource_path_to_uri, convert_uids_to_uris, createLogger } from "../utils";
|
||||
|
||||
const log = createLogger("providers.document_links");
|
||||
|
||||
@@ -70,6 +70,22 @@ export class GDDocumentLinkProvider implements DocumentLinkProvider {
|
||||
}
|
||||
}
|
||||
|
||||
const uids: Set<string> = new Set();
|
||||
const uid_matches: Array<[string, Range]> = [];
|
||||
for (const match of text.matchAll(/uid:\/\/([0-9a-z]*)/g)) {
|
||||
const r = this.create_range(document, match);
|
||||
uids.add(match[0]);
|
||||
uid_matches.push([match[0], r]);
|
||||
}
|
||||
|
||||
const uid_map = await convert_uids_to_uris(Array.from(uids));
|
||||
for (const uid of uid_matches) {
|
||||
const uri = uid_map.get(uid[0]);
|
||||
if (uri instanceof vscode.Uri) {
|
||||
links.push(new DocumentLink(uid[1], uri));
|
||||
}
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
Hover,
|
||||
} from "vscode";
|
||||
import { SceneParser } from "../scene_tools";
|
||||
import { convert_resource_path_to_uri, createLogger } from "../utils";
|
||||
import { convert_resource_path_to_uri, createLogger, convert_uid_to_uri, convert_uri_to_resource_path } from "../utils";
|
||||
|
||||
const log = createLogger("providers.hover");
|
||||
|
||||
@@ -36,6 +36,12 @@ export class GDHoverProvider implements HoverProvider {
|
||||
links += `* [${match[0]}](${uri})\n`;
|
||||
}
|
||||
}
|
||||
for (const match of text.matchAll(/uid:\/\/[0-9a-z]*/g)) {
|
||||
const uri = await convert_uid_to_uri(match[0]);
|
||||
if (uri instanceof Uri) {
|
||||
links += `* [${match[0]}](${uri})\n`;
|
||||
}
|
||||
}
|
||||
return links;
|
||||
}
|
||||
|
||||
@@ -88,7 +94,15 @@ export class GDHoverProvider implements HoverProvider {
|
||||
}
|
||||
}
|
||||
|
||||
const link = document.getText(document.getWordRangeAtPosition(position, /res:\/\/[^"^']*/));
|
||||
let link = document.getText(document.getWordRangeAtPosition(position, /res:\/\/[^"^']*/));
|
||||
if (!link.startsWith("res://")) {
|
||||
link = document.getText(document.getWordRangeAtPosition(position, /uid:\/\/[0-9a-z]*/));
|
||||
if (link.startsWith("uid://")) {
|
||||
const uri = await convert_uid_to_uri(link);
|
||||
link = await convert_uri_to_resource_path(uri);
|
||||
}
|
||||
}
|
||||
|
||||
if (link.startsWith("res://")) {
|
||||
let type = "";
|
||||
if (link.endsWith(".gd")) {
|
||||
|
||||
@@ -127,6 +127,69 @@ export async function convert_uri_to_resource_path(uri: vscode.Uri): Promise<str
|
||||
return `res://${relative_path}`;
|
||||
}
|
||||
|
||||
const uidCache: Map<string, vscode.Uri | null> = new Map();
|
||||
|
||||
export async function convert_uids_to_uris(uids: string[]): Promise<Map<string, vscode.Uri>> {
|
||||
const not_found_uids: string[] = [];
|
||||
const uris: Map<string, vscode.Uri> = new Map();
|
||||
|
||||
let found_all: boolean = true;
|
||||
for (const uid of uids) {
|
||||
if (!uid.startsWith("uid://")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uidCache.has(uid)) {
|
||||
const uri = uidCache.get(uid);
|
||||
if (fs.existsSync(uri.fsPath)) {
|
||||
uris.set(uid, uri);
|
||||
continue;
|
||||
}
|
||||
|
||||
uidCache.delete(uid);
|
||||
}
|
||||
|
||||
found_all = false;
|
||||
not_found_uids.push(uid);
|
||||
}
|
||||
|
||||
if (found_all) {
|
||||
return uris;
|
||||
}
|
||||
|
||||
const files = await vscode.workspace.findFiles("**/*.uid", null);
|
||||
|
||||
for (const file of files) {
|
||||
const document = await vscode.workspace.openTextDocument(file);
|
||||
const text = document.getText();
|
||||
const match = text.match(/uid:\/\/([0-9a-z]*)/);
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const found_match = not_found_uids.indexOf(match[0]) >= 0;
|
||||
|
||||
const file_path = file.fsPath.substring(0, file.fsPath.length - ".uid".length);
|
||||
if (!fs.existsSync(file_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const file_uri = vscode.Uri.file(file_path);
|
||||
uidCache.set(match[0], file_uri);
|
||||
|
||||
if (found_match) {
|
||||
uris.set(match[0], file_uri);
|
||||
}
|
||||
}
|
||||
|
||||
return uris;
|
||||
}
|
||||
|
||||
export async function convert_uid_to_uri(uid: string): Promise<vscode.Uri | undefined> {
|
||||
const uris = await convert_uids_to_uris([uid]);
|
||||
return uris.get(uid);
|
||||
}
|
||||
|
||||
export type VERIFY_STATUS = "SUCCESS" | "WRONG_VERSION" | "INVALID_EXE";
|
||||
export type VERIFY_RESULT = {
|
||||
status: VERIFY_STATUS;
|
||||
|
||||
Reference in New Issue
Block a user