mirror of
https://github.com/godotengine/godot-vscode-plugin.git
synced 2025-12-31 13:48:24 +03:00
Add snippet/placeholder behavior to Scene Preview file drops (#813)
This commit is contained in:
@@ -5,6 +5,7 @@ import { attemptSettingsUpdate, get_extension_uri, clean_godot_path } from "./ut
|
||||
import {
|
||||
GDInlayHintsProvider,
|
||||
GDHoverProvider,
|
||||
GDDocumentDropEditProvider,
|
||||
GDDocumentLinkProvider,
|
||||
GDSemanticTokensProvider,
|
||||
GDCompletionItemProvider,
|
||||
@@ -36,6 +37,7 @@ interface Extension {
|
||||
debug?: GodotDebugger;
|
||||
scenePreviewProvider?: ScenePreviewProvider;
|
||||
linkProvider?: GDDocumentLinkProvider;
|
||||
dropsProvider?: GDDocumentDropEditProvider;
|
||||
hoverProvider?: GDHoverProvider;
|
||||
inlayProvider?: GDInlayHintsProvider;
|
||||
formattingProvider?: FormattingProvider;
|
||||
@@ -56,6 +58,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
globals.debug = new GodotDebugger(context);
|
||||
globals.scenePreviewProvider = new ScenePreviewProvider(context);
|
||||
globals.linkProvider = new GDDocumentLinkProvider(context);
|
||||
globals.dropsProvider = new GDDocumentDropEditProvider(context);
|
||||
globals.hoverProvider = new GDHoverProvider(context);
|
||||
globals.inlayProvider = new GDInlayHintsProvider(context);
|
||||
globals.formattingProvider = new FormattingProvider(context);
|
||||
@@ -213,7 +216,7 @@ async function open_godot_editor_settings() {
|
||||
|
||||
const choices: vscode.QuickPickItem[] = [];
|
||||
for (const file of files) {
|
||||
const pick: vscode.QuickPickItem = {
|
||||
const pick: vscode.QuickPickItem = {
|
||||
label: file,
|
||||
description: path.join(dir, file),
|
||||
};
|
||||
|
||||
86
src/providers/document_drops.ts
Normal file
86
src/providers/document_drops.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import * as vscode from "vscode";
|
||||
import {
|
||||
CancellationToken,
|
||||
DataTransfer,
|
||||
DocumentDropEdit,
|
||||
DocumentDropEditProvider,
|
||||
ExtensionContext,
|
||||
languages,
|
||||
Position,
|
||||
ProviderResult,
|
||||
Range,
|
||||
TextDocument,
|
||||
Uri,
|
||||
} from "vscode";
|
||||
import { createLogger, node_name_to_snake, get_project_version } from "../utils";
|
||||
|
||||
const log = createLogger("providers.drops");
|
||||
|
||||
export class GDDocumentDropEditProvider implements DocumentDropEditProvider {
|
||||
constructor(private context: ExtensionContext) {
|
||||
const dropEditSelector = [
|
||||
{ language: "csharp", scheme: "file" },
|
||||
{ language: "gdscript", scheme: "file" },
|
||||
];
|
||||
context.subscriptions.push(languages.registerDocumentDropEditProvider(dropEditSelector, this));
|
||||
}
|
||||
|
||||
public async provideDocumentDropEdits(
|
||||
document: TextDocument,
|
||||
position: Position,
|
||||
dataTransfer: DataTransfer,
|
||||
token: CancellationToken,
|
||||
): Promise<DocumentDropEdit> {
|
||||
// log.debug("provideDocumentDropEdits", document, dataTransfer);
|
||||
|
||||
// const origin = dataTransfer.get("text/plain").value;
|
||||
// log.debug(origin);
|
||||
|
||||
// TODO: compare the source scene to the target file
|
||||
// What should happen when you drag a node into a script that isn't the
|
||||
// "main" script for that scene?
|
||||
// Attempt to calculate a relative path that resolves correctly?
|
||||
|
||||
const className: string = dataTransfer.get("godot/class")?.value;
|
||||
if (className) {
|
||||
const path: string = dataTransfer.get("godot/path")?.value;
|
||||
const unique = dataTransfer.get("godot/unique")?.value === "true";
|
||||
const label: string = dataTransfer.get("godot/label")?.value;
|
||||
|
||||
// For the root node, the path is empty and needs to be replaced with the node name
|
||||
const savePath = path || label;
|
||||
|
||||
if (document.languageId === "gdscript") {
|
||||
let qualifiedPath = `$${savePath}`;
|
||||
|
||||
if (unique) {
|
||||
// For unique nodes, we can use the % syntax and drop the full path
|
||||
qualifiedPath = `%${label}`;
|
||||
}
|
||||
|
||||
const line = document.lineAt(position.line);
|
||||
if (line.text === "") {
|
||||
// We assume that if the user is dropping a node in an empty line, they are at the top of
|
||||
// the script and want to declare an onready variable
|
||||
|
||||
const snippet = new vscode.SnippetString();
|
||||
|
||||
if ((await get_project_version())?.startsWith("4")) {
|
||||
snippet.appendText("@");
|
||||
}
|
||||
snippet.appendText("onready var ");
|
||||
snippet.appendPlaceholder(node_name_to_snake(label));
|
||||
snippet.appendText(`: ${className} = ${qualifiedPath}`);
|
||||
return new vscode.DocumentDropEdit(snippet);
|
||||
}
|
||||
|
||||
// In any other place, we assume the user wants to get a reference to the node itself
|
||||
return new vscode.DocumentDropEdit(qualifiedPath);
|
||||
}
|
||||
|
||||
if (document.languageId === "csharp") {
|
||||
return new vscode.DocumentDropEdit(`GetNode<${className}>("${savePath}")`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
export * from "./completions";
|
||||
export * from "./definition";
|
||||
export * from "./document_drops";
|
||||
export * from "./document_link";
|
||||
export * from "./documentation";
|
||||
export * from "./hover";
|
||||
export * from "./inlay_hints";
|
||||
export * from "./semantic_tokens";
|
||||
export * from "./documentation";
|
||||
export * from "./tasks";
|
||||
|
||||
@@ -1,41 +1,36 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as vscode from "vscode";
|
||||
import {
|
||||
type CancellationToken,
|
||||
type Event,
|
||||
EventEmitter,
|
||||
type ExtensionContext,
|
||||
type FileDecoration,
|
||||
type ProviderResult,
|
||||
type TreeDataProvider,
|
||||
type TreeDragAndDropController,
|
||||
type ExtensionContext,
|
||||
EventEmitter,
|
||||
type Event,
|
||||
type TreeView,
|
||||
type ProviderResult,
|
||||
type TreeItem,
|
||||
TreeItemCollapsibleState,
|
||||
window,
|
||||
languages,
|
||||
type TreeView,
|
||||
type Uri,
|
||||
type CancellationToken,
|
||||
type FileDecoration,
|
||||
type DocumentDropEditProvider,
|
||||
window,
|
||||
workspace,
|
||||
} from "vscode";
|
||||
import * as fs from "node:fs";
|
||||
import {
|
||||
get_configuration,
|
||||
find_file,
|
||||
set_context,
|
||||
convert_resource_path_to_uri,
|
||||
register_command,
|
||||
createLogger,
|
||||
find_file,
|
||||
get_configuration,
|
||||
make_docs_uri,
|
||||
node_name_to_snake,
|
||||
register_command,
|
||||
set_context,
|
||||
} from "../utils";
|
||||
import { SceneParser } from "./parser";
|
||||
import type { SceneNode, Scene } from "./types";
|
||||
import type { Scene, SceneNode } from "./types";
|
||||
|
||||
const log = createLogger("scenes.preview");
|
||||
|
||||
export class ScenePreviewProvider
|
||||
implements TreeDataProvider<SceneNode>, TreeDragAndDropController<SceneNode>, DocumentDropEditProvider
|
||||
{
|
||||
export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDragAndDropController<SceneNode> {
|
||||
public dropMimeTypes = [];
|
||||
public dragMimeTypes = [];
|
||||
private tree: TreeView<SceneNode>;
|
||||
@@ -58,10 +53,6 @@ export class ScenePreviewProvider
|
||||
dragAndDropController: this,
|
||||
});
|
||||
|
||||
const selector = [
|
||||
{ language: "csharp", scheme: "file" },
|
||||
{ language: "gdscript", scheme: "file" },
|
||||
];
|
||||
context.subscriptions.push(
|
||||
register_command("scenePreview.lock", this.lock_preview.bind(this)),
|
||||
register_command("scenePreview.unlock", this.unlock_preview.bind(this)),
|
||||
@@ -77,7 +68,6 @@ export class ScenePreviewProvider
|
||||
window.onDidChangeActiveTextEditor(this.refresh.bind(this)),
|
||||
window.registerFileDecorationProvider(this.uniqueDecorator),
|
||||
window.registerFileDecorationProvider(this.scriptDecorator),
|
||||
languages.registerDocumentDropEditProvider(selector, this),
|
||||
this.watcher.onDidChange(this.on_file_changed.bind(this)),
|
||||
this.watcher,
|
||||
this.tree.onDidChangeSelection(this.tree_selection_changed),
|
||||
@@ -92,59 +82,13 @@ export class ScenePreviewProvider
|
||||
data: vscode.DataTransfer,
|
||||
token: vscode.CancellationToken,
|
||||
): void | Thenable<void> {
|
||||
data.set("godot/scene", new vscode.DataTransferItem(this.currentScene));
|
||||
data.set("godot/path", new vscode.DataTransferItem(source[0].relativePath));
|
||||
data.set("godot/class", new vscode.DataTransferItem(source[0].className));
|
||||
data.set("godot/unique", new vscode.DataTransferItem(source[0].unique));
|
||||
data.set("godot/label", new vscode.DataTransferItem(source[0].label));
|
||||
}
|
||||
|
||||
public provideDocumentDropEdits(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position,
|
||||
dataTransfer: vscode.DataTransfer,
|
||||
token: vscode.CancellationToken,
|
||||
): vscode.ProviderResult<vscode.DocumentDropEdit> {
|
||||
const path: string = dataTransfer.get("godot/path").value;
|
||||
const className: string = dataTransfer.get("godot/class").value;
|
||||
const line = document.lineAt(position.line);
|
||||
const unique = dataTransfer.get("godot/unique").value === "true";
|
||||
const label: string = dataTransfer.get("godot/label").value;
|
||||
|
||||
// TODO: compare the source scene to the target file
|
||||
// What should happen when you drag a node into a script that isn't the
|
||||
// "main" script for that scene?
|
||||
// Attempt to calculate a relative path that resolves correctly?
|
||||
|
||||
if (className) {
|
||||
// For the root node, the path is empty and needs to be replaced with the node name
|
||||
const savePath = path || label;
|
||||
|
||||
if (document.languageId === "gdscript") {
|
||||
let qualifiedPath = `$${savePath}`;
|
||||
|
||||
if (unique) {
|
||||
// For unique nodes, we can use the % syntax and drop the full path
|
||||
qualifiedPath = `%${label}`;
|
||||
}
|
||||
|
||||
if (line.text === "") {
|
||||
// We assume that if the user is dropping a node in an empty line, they are at the top of
|
||||
// the script and want to declare an onready variable
|
||||
return new vscode.DocumentDropEdit(
|
||||
`@onready var ${node_name_to_snake(label)}: ${className} = ${qualifiedPath}\n`,
|
||||
);
|
||||
}
|
||||
|
||||
// In any other place, we assume the user wants to get a reference to the node itself
|
||||
return new vscode.DocumentDropEdit(qualifiedPath);
|
||||
}
|
||||
|
||||
if (document.languageId === "csharp") {
|
||||
return new vscode.DocumentDropEdit(`GetNode<${className}>("${savePath}")`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async on_file_changed(uri: vscode.Uri) {
|
||||
if (!uri.fsPath.endsWith(".tscn")) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user