Files
godot-vscode-plugin/src/providers/hover.ts
David Kincaid a04f58c82d Scene Preview Improvements (relative path drag/drop) (#815)
* Improve scene preview lock behavior
* Add convert_uri_to_resource_path utility function
* Implement relative nodepath dropping
* Prevent a possible error when trying to refresh a scene that doesn't exist
* Fix wrong command name being called (scenePreview.openMainScene has actually never worked)
2025-03-10 04:50:00 -04:00

123 lines
3.8 KiB
TypeScript

import * as vscode from "vscode";
import {
Uri,
Position,
TextDocument,
CancellationToken,
ExtensionContext,
HoverProvider,
MarkdownString,
Hover,
} from "vscode";
import { SceneParser } from "../scene_tools";
import { convert_resource_path_to_uri, createLogger } from "../utils";
const log = createLogger("providers.hover");
export class GDHoverProvider implements HoverProvider {
public parser = new SceneParser();
constructor(private context: ExtensionContext) {
const selector = [
{ language: "gdresource", scheme: "file" },
{ language: "gdscene", scheme: "file" },
{ language: "gdscript", scheme: "file" },
];
context.subscriptions.push(
vscode.languages.registerHoverProvider(selector, this),
);
}
async get_links(text: string): Promise<string> {
let links = "";
for (const match of text.matchAll(/res:\/\/[^"^']*/g)) {
const uri = await convert_resource_path_to_uri(match[0]);
if (uri instanceof Uri) {
links += `* [${match[0]}](${uri})\n`;
}
}
return links;
}
async provideHover(document: TextDocument, position: Position, token: CancellationToken): Promise<Hover> {
if (["gdresource", "gdscene"].includes(document.languageId)) {
const scene = this.parser.parse_scene(document);
const wordPattern = /(?:Ext|Sub)Resource\(\s?"?(\w+)\s?"?\)/;
const word = document.getText(document.getWordRangeAtPosition(position, wordPattern));
if (word.startsWith("ExtResource")) {
const match = word.match(wordPattern);
const id = match[1];
const resource = scene.externalResources.get(id);
const definition = resource.body;
const links = await this.get_links(definition);
const contents = new MarkdownString();
contents.appendMarkdown(links);
const uri = await convert_resource_path_to_uri(resource.path);
contents.appendMarkdown("\n---\n");
contents.appendCodeblock(definition, "gdresource");
if (resource.type === "Texture") {
contents.appendMarkdown("\n---\n");
contents.appendMarkdown(`<img src="${uri}" min-width=100px max-width=500px/>\n`);
contents.supportHtml = true;
contents.isTrusted = true;
}
if (resource.type === "Script") {
contents.appendMarkdown("\n---\n");
const text = (await vscode.workspace.openTextDocument(uri)).getText();
contents.appendCodeblock(text, "gdscript");
}
const hover = new Hover(contents);
return hover;
}
if (word.startsWith("SubResource")) {
const match = word.match(wordPattern);
const id = match[1];
let definition = scene.subResources.get(id).body;
// don't display contents of giant arrays
definition = definition?.replace(/Array\([0-9,\.\- ]*\)/, "Array(...)");
const contents = new MarkdownString();
contents.appendCodeblock(definition, "gdresource");
const hover = new Hover(contents);
return hover;
}
}
const link = document.getText(document.getWordRangeAtPosition(position, /res:\/\/[^"^']*/));
if (link.startsWith("res://")) {
let type = "";
if (link.endsWith(".gd")) {
type = "gdscript";
} else if (link.endsWith(".cs")) {
type = "csharp";
} else if (link.endsWith(".tscn")) {
type = "gdscene";
} else if (link.endsWith(".tres")) {
type = "gdresource";
} else if (link.endsWith(".png") || link.endsWith(".svg")) {
type = "image";
} else {
return;
}
const uri = await convert_resource_path_to_uri(link);
const contents = new MarkdownString();
if (type === "image") {
contents.appendMarkdown(`<img src="${uri}" min-width=100px max-width=500px/>`);
contents.supportHtml = true;
contents.isTrusted = true;
} else {
const text = (await vscode.workspace.openTextDocument(uri)).getText();
contents.appendCodeblock(text, type);
}
const hover = new Hover(contents);
return hover;
}
}
}