From 4625a1e6d9095cbee0bf1eff0a6e6218f6935f0d Mon Sep 17 00:00:00 2001 From: Francisco Lopez Date: Tue, 23 Jan 2024 23:10:22 -0500 Subject: [PATCH] Was able to dispose of the webview resources properly. We are able to show the current webview if it hidden by selecting our extension. Only one webview is created, if another file is wanted by the user, old webview is disposed and a new one is created to prevent any memory leaks. --- src/extension.ts | 64 +++++++++++++++++++++++++++++++++++------------- src/panel.ts | 41 ++++++++++++++++++++++++------- 2 files changed, 79 insertions(+), 26 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 0d034e6..6cd2a82 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,37 +1,67 @@ import * as vscode from 'vscode'; import {createPanel} from './panel'; import { Parser } from './parser'; +import { Tree } from './types/tree'; + +let tree: Parser | undefined = undefined; +let panel: vscode.WebviewPanel | undefined = undefined + // This method is called when your extension is activated // Your extension is activated the very first time the command is executed - function activate(context: vscode.ExtensionContext) { - let disposable = vscode.commands.registerCommand('react-labyrinth.helloWorld', function () { - vscode.window.showInformationMessage('Hello World from React Labyrinth!'); - }); + // This is the column where Webview will be revealed to + const columnToShowIn : vscode.ViewColumn | undefined = vscode.window.activeTextEditor + ? vscode.window.activeTextEditor.viewColumn + : undefined; + + + // Command that allows for User to select the root file of their React application. + const pickFile: vscode.Disposable = vscode.commands.registerCommand('myExtension.pickFile', async () => { + - // pass in the command we want to register (refer to package.json) - // let result = vscode.commands.registerCommand('myExtension.showPanel', () => { - // // call helper func - // createPanel(context); - // }); + // Check if there is an existing webview panel, if so display it. + if(panel) { + panel.reveal(columnToShowIn) + } - vscode.commands.registerCommand('myExtension.pickFile', async () => { - const fileArray = await vscode.window.showOpenDialog({ canSelectFolders: false, canSelectFiles: true, canSelectMany: false }); + + // Opens window for the User to select the root file of React application + const fileArray: vscode.Uri[] = await vscode.window.showOpenDialog({ canSelectFolders: false, canSelectFiles: true, canSelectMany: false }); + + // Throw error message if no file was selected if (!fileArray || fileArray.length === 0) { vscode.window.showErrorMessage('No file selected'); return; } - - const tree = new Parser(fileArray[0].path); + + + // Create Tree to be inserted into returned HTML + tree = new Parser(fileArray[0].path); tree.parse(); - const data = tree.getTree(); - console.log('Data sent back: \n', data); - createPanel(context, data); + const data: Tree = tree.getTree(); + + + // Check if panel currently has a webview, if it does dispose of it and create another with updated root file selected. + // Otherwise create a new webview to display root file selected. + if(!panel) { + panel = createPanel(context, data, columnToShowIn); + } else { + panel.dispose() + panel = createPanel(context, data, columnToShowIn); + } }); - context.subscriptions.push(disposable); + + + // Command to show panel if it is hidden + const showPanel: vscode.Disposable = vscode.commands.registerCommand('myExtension.showPanel', () => { + panel.reveal(columnToShowIn) + }); + + + context.subscriptions.push(pickFile, showPanel); } // This method is called when your extension is deactivated diff --git a/src/panel.ts b/src/panel.ts index 606db1e..f06678a 100644 --- a/src/panel.ts +++ b/src/panel.ts @@ -2,13 +2,15 @@ import * as vscode from 'vscode'; import { getNonce } from './getNonce'; import { Tree } from './types/tree'; -export function createPanel(context: vscode.ExtensionContext, data: Tree) { - // if the current panel exists, then reveal the column, else make one? +let panel: vscode.WebviewPanel | undefined = undefined + +export function createPanel(context: vscode.ExtensionContext, data: Tree, columnToShowIn: vscode.ViewColumn) { // utilize method on vscode.window object to create webview - const panel = vscode.window.createWebviewPanel( + panel = vscode.window.createWebviewPanel( 'reactLabyrinth', 'React Labyrinth', + // create one new tab vscode.ViewColumn.One, { @@ -16,26 +18,42 @@ export function createPanel(context: vscode.ExtensionContext, data: Tree) { retainContextWhenHidden: true } ); + + // Set the icon logo of extension webview panel.iconPath = vscode.Uri.joinPath(context.extensionUri, 'media', 'favicon.ico'); + + + // Set URI to be the path to bundle + const bundlePath: vscode.Uri = vscode.Uri.joinPath(context.extensionUri, 'build', 'bundle.js'); - const bundlePath = vscode.Uri.joinPath(context.extensionUri, 'build', 'bundle.js'); // set webview URI to pass into html script - const bundleURI = panel.webview.asWebviewUri(bundlePath); + const bundleURI: vscode.Uri = panel.webview.asWebviewUri(bundlePath); + // render html of webview here panel.webview.html = createWebviewHTML(bundleURI, data); - // will need to use onDidDispose to clear cached data and reset tree when the webview and/or application is closed + // Listens for when webview is closed and disposes of webview resources + panel.onDidDispose( + () => { + panel = undefined; + }, + null, + context.subscriptions + ); + + + // Sends data to Flow.tsx to be displayed after parsed data is received panel.webview.onDidReceiveMessage( async (msg: any) => { switch (msg.type) { case 'onData': if (!msg.value) break; context.workspaceState.update('reactLabyrinth', msg.value); - // console.log('msg.value from panel.js: ', msg.value); + panel.webview.postMessage( { type: 'parsed-data', @@ -49,13 +67,18 @@ export function createPanel(context: vscode.ExtensionContext, data: Tree) { undefined, context.subscriptions ); + + return panel }; + + // getNonce generates a new random string each time ext is used to prevent external injection of foreign code into the html -const nonce = getNonce(); +const nonce: string = getNonce(); + // function to create the HTML page for webview -function createWebviewHTML(URI: vscode.Uri, initialData: Tree) { +function createWebviewHTML(URI: vscode.Uri, initialData: Tree) : string { return ( `