Kaynağa Gözat

Improve dragging items from Scene Preview into source code (#661)

Co-authored-by: David Kincaid <[email protected]>
Marvin Altemeier 1 yıl önce
ebeveyn
işleme
6456a789af
2 değiştirilmiş dosya ile 55 ekleme ve 5 silme
  1. 36 5
      src/scene_tools/preview.ts
  2. 19 0
      src/utils/index.ts

+ 36 - 5
src/scene_tools/preview.ts

@@ -26,6 +26,7 @@ import {
 	register_command,
 	createLogger,
 	make_docs_uri,
+	node_name_to_snake,
 } from "../utils";
 import { SceneParser } from "./parser";
 import type { SceneNode, Scene } from "./types";
@@ -93,6 +94,8 @@ export class ScenePreviewProvider
 	): void | Thenable<void> {
 		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(
@@ -101,15 +104,43 @@ export class ScenePreviewProvider
 		dataTransfer: vscode.DataTransfer,
 		token: vscode.CancellationToken,
 	): vscode.ProviderResult<vscode.DocumentDropEdit> {
-		const path = dataTransfer.get("godot/path").value;
-		const className = dataTransfer.get("godot/class").value;
+		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 (path && className) {
 			if (document.languageId === "gdscript") {
-				return new vscode.DocumentDropEdit(`$${path}`);
+				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}`,
+					);
+				}
+
+				// 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}>("${path}")`);
+				return new vscode.DocumentDropEdit(`GetNode<${className}>("${savePath}")`);
 			}
 		}
 	}

+ 19 - 0
src/utils/index.ts

@@ -42,3 +42,22 @@ export function make_docs_uri(path: string, fragment?: string) {
 		fragment: fragment,
 	});
 }
+
+/**
+ * Can be used to convert a conventional node name to a snake_case variable name.
+ * 
+ * @example
+ * ```ts
+ * nodeNameToVar("MyNode") // my_node
+ * nodeNameToVar("Sprite2D") // sprite_2d
+ * nodeNameToVar("UI") // ui
+ * ```
+ */
+export function node_name_to_snake(name: string): string {
+    const snakeCase: string = name.replace(/([a-z])([A-Z0-9])/g, "$1_$2").toLowerCase();
+    
+    if (snakeCase.startsWith("_")) {
+        return snakeCase.substring(1);
+    }
+    return snakeCase;
+}