Просмотр исходного кода

Improve Scene Preview ergonomics (#665)

* Rename Scene Preview "pinning" to "locking"

* Add "refresh scene preview" command as button

* Expose scene preview commands to command palette

* Add openCurrentScene and openMainScript commands
David Kincaid 1 год назад
Родитель
Сommit
17af8e20c9
2 измененных файлов с 127 добавлено и 66 удалено
  1. 56 25
      package.json
  2. 71 41
      src/scene_tools/preview.ts

+ 56 - 25
package.json

@@ -149,19 +149,47 @@
 			{
 				"category": "Godot Tools",
 				"command": "godotTools.scenePreview.refresh",
-				"title": "Refresh Scene Preview"
+				"title": "Refresh Scene Preview",
+				"icon": {
+					"light": "resources/godot_icons/light/Reload.svg",
+					"dark": "resources/godot_icons/dark/Reload.svg"
+				}
 			},
 			{
 				"category": "Godot Tools",
-				"command": "godotTools.scenePreview.pin",
-				"title": "Pin Scene Preview",
-				"icon": "resources/pin_off.svg"
+				"command": "godotTools.scenePreview.openCurrentScene",
+				"title": "Open the Scene Preview's current scene",
+				"icon": {
+					"light": "resources/godot_icons/light/PackedScene.svg",
+					"dark": "resources/godot_icons/dark/PackedScene.svg"
+				}
 			},
 			{
 				"category": "Godot Tools",
-				"command": "godotTools.scenePreview.unpin",
-				"title": "Unpin Scene Preview",
-				"icon": "resources/pin_on.svg"
+				"command": "godotTools.scenePreview.openMainScript",
+				"title": "Open the main script of the Scene Preview's current scene",
+				"icon": {
+					"light": "resources/godot_icons/light/Script.svg",
+					"dark": "resources/godot_icons/dark/Script.svg"
+				}
+			},
+			{
+				"category": "Godot Tools",
+				"command": "godotTools.scenePreview.lock",
+				"title": "Lock Scene Preview",
+				"icon": {
+					"light": "resources/godot_icons/light/Unlock.svg",
+					"dark": "resources/godot_icons/dark/Unlock.svg"
+				}
+			},
+			{
+				"category": "Godot Tools",
+				"command": "godotTools.scenePreview.unlock",
+				"title": "Unlock Scene Preview",
+				"icon": {
+					"light": "resources/godot_icons/light/Lock.svg",
+					"dark": "resources/godot_icons/dark/Lock.svg"
+				}
 			},
 			{
 				"category": "Godot Tools",
@@ -628,10 +656,6 @@
 					"command": "godotTools.listGodotClasses",
 					"when": "godotTools.context.connectedToLSP"
 				},
-				{
-					"command": "godotTools.scenePreview.refresh",
-					"when": "false"
-				},
 				{
 					"command": "godotTools.scenePreview.goToDefinition",
 					"when": "false"
@@ -640,14 +664,6 @@
 					"command": "godotTools.scenePreview.openDocumentation",
 					"when": "false"
 				},
-				{
-					"command": "godotTools.scenePreview.pin",
-					"when": "false"
-				},
-				{
-					"command": "godotTools.scenePreview.unpin",
-					"when": "false"
-				},
 				{
 					"command": "godotTools.scenePreview.copyNodePath",
 					"when": "false"
@@ -693,14 +709,29 @@
 					"group": "navigation"
 				},
 				{
-					"command": "godotTools.scenePreview.pin",
-					"when": "view == scenePreview && !godotTools.context.scenePreview.pinned",
-					"group": "navigation"
+					"command": "godotTools.scenePreview.lock",
+					"when": "view == scenePreview && !godotTools.context.scenePreview.locked",
+					"group": "navigation@1"
 				},
 				{
-					"command": "godotTools.scenePreview.unpin",
-					"when": "view == scenePreview && godotTools.context.scenePreview.pinned",
-					"group": "navigation"
+					"command": "godotTools.scenePreview.unlock",
+					"when": "view == scenePreview && godotTools.context.scenePreview.locked",
+					"group": "navigation@1"
+				},
+				{
+					"command": "godotTools.scenePreview.refresh",
+					"when": "view == scenePreview",
+					"group": "navigation@2"
+				},
+				{
+					"command": "godotTools.scenePreview.openMainScript",
+					"when": "view == scenePreview",
+					"group": "navigation@3"
+				},
+				{
+					"command": "godotTools.scenePreview.openCurrentScene",
+					"when": "view == scenePreview",
+					"group": "navigation@4"
 				}
 			],
 			"view/item/context": [

+ 71 - 41
src/scene_tools/preview.ts

@@ -1,23 +1,23 @@
 import * as vscode from "vscode";
 import {
-	TreeDataProvider,
-	TreeDragAndDropController,
-	ExtensionContext,
+	type TreeDataProvider,
+	type TreeDragAndDropController,
+	type ExtensionContext,
 	EventEmitter,
-	Event,
-	TreeView,
-	ProviderResult,
-	TreeItem,
+	type Event,
+	type TreeView,
+	type ProviderResult,
+	type TreeItem,
 	TreeItemCollapsibleState,
 	window,
 	languages,
-	Uri,
-	CancellationToken,
-	FileDecoration,
-	DocumentDropEditProvider,
+	type Uri,
+	type CancellationToken,
+	type FileDecoration,
+	type DocumentDropEditProvider,
 	workspace,
 } from "vscode";
-import * as fs from "fs";
+import * as fs from "node:fs";
 import {
 	get_configuration,
 	find_file,
@@ -28,15 +28,17 @@ import {
 	make_docs_uri,
 } from "../utils";
 import { SceneParser } from "./parser";
-import { SceneNode, Scene } from "./types";
+import type { SceneNode, Scene } from "./types";
 
 const log = createLogger("scenes.preview");
 
-export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDragAndDropController<SceneNode>, DocumentDropEditProvider {
+export class ScenePreviewProvider
+	implements TreeDataProvider<SceneNode>, TreeDragAndDropController<SceneNode>, DocumentDropEditProvider
+{
 	public dropMimeTypes = [];
 	public dragMimeTypes = [];
 	private tree: TreeView<SceneNode>;
-	private scenePreviewPinned = false;
+	private scenePreviewLocked = false;
 	private currentScene = "";
 	public parser = new SceneParser();
 	public scene: Scene;
@@ -52,7 +54,7 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
 	constructor(private context: ExtensionContext) {
 		this.tree = vscode.window.createTreeView("scenePreview", {
 			treeDataProvider: this,
-			dragAndDropController: this
+			dragAndDropController: this,
 		});
 
 		const selector = [
@@ -60,12 +62,14 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
 			{ language: "gdscript", scheme: "file" },
 		];
 		context.subscriptions.push(
-			register_command("scenePreview.pin", this.pin_preview.bind(this)),
-			register_command("scenePreview.unpin", this.unpin_preview.bind(this)),
+			register_command("scenePreview.lock", this.lock_preview.bind(this)),
+			register_command("scenePreview.unlock", this.unlock_preview.bind(this)),
 			register_command("scenePreview.copyNodePath", this.copy_node_path.bind(this)),
 			register_command("scenePreview.copyResourcePath", this.copy_resource_path.bind(this)),
 			register_command("scenePreview.openScene", this.open_scene.bind(this)),
 			register_command("scenePreview.openScript", this.open_script.bind(this)),
+			register_command("scenePreview.openCurrentScene", this.open_current_scene.bind(this)),
+			register_command("scenePreview.openCurrentScript", this.open_main_script.bind(this)),
 			register_command("scenePreview.goToDefinition", this.go_to_definition.bind(this)),
 			register_command("scenePreview.openDocumentation", this.open_documentation.bind(this)),
 			register_command("scenePreview.refresh", this.refresh.bind(this)),
@@ -82,12 +86,21 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
 		this.refresh();
 	}
 
-	public handleDrag(source: readonly SceneNode[], data: vscode.DataTransfer, token: vscode.CancellationToken): void | Thenable<void> {
+	public handleDrag(
+		source: readonly SceneNode[],
+		data: vscode.DataTransfer,
+		token: vscode.CancellationToken,
+	): void | Thenable<void> {
 		data.set("godot/path", new vscode.DataTransferItem(source[0].relativePath));
 		data.set("godot/class", new vscode.DataTransferItem(source[0].className));
 	}
 
-	public provideDocumentDropEdits(document: vscode.TextDocument, position: vscode.Position, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): vscode.ProviderResult<vscode.DocumentDropEdit> {
+	public provideDocumentDropEdits(
+		document: vscode.TextDocument,
+		position: vscode.Position,
+		dataTransfer: vscode.DataTransfer,
+		token: vscode.CancellationToken,
+	): vscode.ProviderResult<vscode.DocumentDropEdit> {
 		const path = dataTransfer.get("godot/path").value;
 		const className = dataTransfer.get("godot/class").value;
 
@@ -106,7 +119,7 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
 			return;
 		}
 		setTimeout(async () => {
-			if (uri.fsPath == this.currentScene) {
+			if (uri.fsPath === this.currentScene) {
 				this.refresh();
 			} else {
 				const document = await vscode.workspace.openTextDocument(uri);
@@ -116,7 +129,7 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
 	}
 
 	public async refresh() {
-		if (this.scenePreviewPinned) {
+		if (this.scenePreviewLocked) {
 			return;
 		}
 
@@ -128,7 +141,7 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
 			if (!fileName.endsWith(".tscn")) {
 				const searchName = fileName.replace(".gd", ".tscn").replace(".cs", ".tscn");
 
-				if (mode == "anyFolder") {
+				if (mode === "anyFolder") {
 					const relatedScene = await find_file(searchName);
 					if (!relatedScene) {
 						return;
@@ -136,14 +149,14 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
 					fileName = relatedScene.fsPath;
 				}
 
-				if (mode == "sameFolder") {
+				if (mode === "sameFolder") {
 					if (fs.existsSync(searchName)) {
 						fileName = searchName;
 					} else {
 						return;
 					}
 				}
-				if (mode == "off") {
+				if (mode === "off") {
 					return;
 				}
 			}
@@ -162,20 +175,20 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
 		}
 	}
 
-	private pin_preview() {
-		this.scenePreviewPinned = true;
-		set_context("scenePreview.pinned", true);
+	private lock_preview() {
+		this.scenePreviewLocked = true;
+		set_context("scenePreview.locked", true);
 	}
 
-	private unpin_preview() {
-		this.scenePreviewPinned = false;
-		set_context("scenePreview.pinned", false);
+	private unlock_preview() {
+		this.scenePreviewLocked = false;
+		set_context("scenePreview.locked", false);
 		this.refresh();
 	}
 
 	private copy_node_path(item: SceneNode) {
 		if (item.unique) {
-			vscode.env.clipboard.writeText("%" + item.label);
+			vscode.env.clipboard.writeText(`%${item.label}`);
 			return;
 		}
 		vscode.env.clipboard.writeText(item.relativePath);
@@ -201,6 +214,26 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
 		}
 	}
 
+	private async open_current_scene() {
+		if (this.currentScene) {
+			const document = await vscode.workspace.openTextDocument(this.currentScene);
+			vscode.window.showTextDocument(document);
+		}
+	}
+
+	private async open_main_script() {
+		if (this.currentScene) {
+			const root = this.scene.root;
+			if (root?.hasScript) {
+				const path = this.scene.externalResources[root.scriptId].path;
+				const uri = await convert_resource_path_to_uri(path);
+				if (uri) {
+					vscode.window.showTextDocument(uri, { preview: true });
+				}
+			}
+		}
+	}
+
 	private async go_to_definition(item: SceneNode) {
 		const document = await vscode.workspace.openTextDocument(this.currentScene);
 		const start = document.positionAt(item.position);
@@ -216,7 +249,6 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
 	private tree_selection_changed(event: vscode.TreeViewSelectionChangeEvent<SceneNode>) {
 		// const item = event.selection[0];
 		// log(item.body);
-
 		// const editor = vscode.window.activeTextEditor;
 		// const range = editor.document.getText()
 		// editor.revealRange(range)
@@ -226,12 +258,10 @@ export class ScenePreviewProvider implements TreeDataProvider<SceneNode>, TreeDr
 		if (!element) {
 			if (!this.scene?.root) {
 				return Promise.resolve([]);
-			} else {
-				return Promise.resolve([this.scene?.root]);
 			}
-		} else {
-			return Promise.resolve(element.children);
+			return Promise.resolve([this.scene?.root]);
 		}
+		return Promise.resolve(element.children);
 	}
 
 	public getTreeItem(element: SceneNode): TreeItem | Thenable<TreeItem> {
@@ -254,13 +284,13 @@ class UniqueDecorationProvider implements vscode.FileDecorationProvider {
 		return this.changeDecorationsEvent.event;
 	}
 
-	constructor(private previewer: ScenePreviewProvider) { }
+	constructor(private previewer: ScenePreviewProvider) {}
 
 	provideFileDecoration(uri: Uri, token: CancellationToken): FileDecoration | undefined {
 		if (uri.scheme !== "godot") return undefined;
 
 		const node = this.previewer.scene?.nodes.get(uri.path);
-		if (node && node.unique) {
+		if (node?.unique) {
 			return {
 				badge: "%",
 			};
@@ -274,13 +304,13 @@ class ScriptDecorationProvider implements vscode.FileDecorationProvider {
 		return this.changeDecorationsEvent.event;
 	}
 
-	constructor(private previewer: ScenePreviewProvider) { }
+	constructor(private previewer: ScenePreviewProvider) {}
 
 	provideFileDecoration(uri: Uri, token: CancellationToken): FileDecoration | undefined {
 		if (uri.scheme !== "godot") return undefined;
 
 		const node = this.previewer.scene?.nodes.get(uri.path);
-		if (node && node.hasScript) {
+		if (node?.hasScript) {
 			return {
 				badge: "S",
 			};