From b2e378564f8d674b93130b4e9bff958828e7dfa6 Mon Sep 17 00:00:00 2001 From: MaxKless <34165455+MaxKless@users.noreply.github.com> Date: Mon, 31 Jul 2023 13:04:52 +0200 Subject: [PATCH] feat: enable opening project configuration files & running tasks from graph (#1841) --- .../dev/nx/console/graph/ui/NxGraphBrowser.kt | 61 ++++++++++++++++++- apps/vscode/package.json | 15 ++++- .../nx-workspace/src/lib/get-nx-workspace.ts | 2 +- .../project-graph/src/lib/graph-webview.ts | 31 +++++++++- .../vscode/project-graph/src/lib/load-html.ts | 23 ++++++- 5 files changed, 125 insertions(+), 7 deletions(-) diff --git a/apps/intellij/src/main/kotlin/dev/nx/console/graph/ui/NxGraphBrowser.kt b/apps/intellij/src/main/kotlin/dev/nx/console/graph/ui/NxGraphBrowser.kt index 4c8a24870f..13c545b760 100644 --- a/apps/intellij/src/main/kotlin/dev/nx/console/graph/ui/NxGraphBrowser.kt +++ b/apps/intellij/src/main/kotlin/dev/nx/console/graph/ui/NxGraphBrowser.kt @@ -17,14 +17,14 @@ import dev.nx.console.graph.NxGraphService import dev.nx.console.graph.NxGraphStates import dev.nx.console.models.NxVersion import dev.nx.console.models.ProjectGraphOutput +import dev.nx.console.run.NxTaskExecutionManager import dev.nx.console.services.NxlsService +import dev.nx.console.telemetry.TelemetryService import dev.nx.console.ui.Notifier -import dev.nx.console.utils.isWslInterpreter +import dev.nx.console.utils.* import dev.nx.console.utils.jcef.OpenDevToolsContextMenuHandler import dev.nx.console.utils.jcef.getHexColor import dev.nx.console.utils.jcef.onBrowserLoadEnd -import dev.nx.console.utils.nodeInterpreter -import dev.nx.console.utils.nxBasePath import io.github.z4kn4fein.semver.toVersion import java.io.File import java.nio.file.Paths @@ -67,6 +67,8 @@ class NxGraphBrowser( null } registerFileClickHandler(browser) + registerOpenProjectConfigHandler(browser) + registerRunTaskHandler(browser) } val component = browser.component @@ -435,6 +437,8 @@ class NxGraphBrowser( onBrowserLoadEnd(browser) { val query = JBCefJSQuery.create(browser as JBCefBrowserBase) query.addHandler { msg -> + TelemetryService.getInstance(project).featureUsed("Nx Graph Open Project Edge File") + val path = Paths.get(project.nxBasePath, msg).toString() val file = LocalFileSystem.getInstance().findFileByPath(path) if (file == null) { @@ -459,6 +463,57 @@ class NxGraphBrowser( browser.executeJavaScriptAsync(js) } } + private fun registerOpenProjectConfigHandler(browser: JBCefBrowser) { + onBrowserLoadEnd(browser) { + val query = JBCefJSQuery.create(browser as JBCefBrowserBase) + query.addHandler { msg -> + CoroutineScope(Dispatchers.Default).launch { + TelemetryService.getInstance(project) + .featureUsed("Nx Graph Open Project Config File") + + project.nxWorkspace()?.workspace?.projects?.get(msg)?.apply { + val path = nxProjectConfigurationPath(project, root) ?: return@apply + val file = + LocalFileSystem.getInstance().findFileByPath(path) ?: return@apply + ApplicationManager.getApplication().invokeLater { + FileEditorManager.getInstance(project).openFile(file, true) + } + } + } + + null + } + val js = + """ + window.externalApi?.registerOpenProjectConfigCallback?.((message) => { + ${query.inject("message")} + }) + """ + browser.executeJavaScriptAsync(js) + } + } + private fun registerRunTaskHandler(browser: JBCefBrowser) { + onBrowserLoadEnd(browser) { + val query = JBCefJSQuery.create(browser as JBCefBrowserBase) + query.addHandler { msg -> + CoroutineScope(Dispatchers.Default).launch { + TelemetryService.getInstance(project).featureUsed("Nx Graph Run Task") + + val (projectName, targetName) = msg.split(":") + NxTaskExecutionManager.getInstance(project).execute(projectName, targetName) + } + + null + } + val js = + """ + window.externalApi?.registerRunTaskCallback?.((message) => { + ${query.inject("message")} + }) + """ + browser.executeJavaScriptAsync(js) + } + } override fun dispose() { browser.dispose() } diff --git a/apps/vscode/package.json b/apps/vscode/package.json index 9c5aaaf91a..9bd966f1ae 100644 --- a/apps/vscode/package.json +++ b/apps/vscode/package.json @@ -92,6 +92,11 @@ "when": "isNxWorkspace", "group": "2_workspace@5", "command": "nx.graph.select" + }, + { + "when": "isNxWorkspace", + "group": "2_workspace@5", + "command": "nx.graph.task" } ], "view/title": [ @@ -183,6 +188,13 @@ "group": "inline" } ], + "editor/title": [ + { + "command": "nx.graph.refresh", + "group": "navigation", + "when": "graphWebviewVisible" + } + ], "commandPalette": [ { "command": "nx.generate.ui.fileexplorer", @@ -374,7 +386,8 @@ { "category": "Nx", "title": "Refresh Nx Graph", - "command": "nx.graph.refresh" + "command": "nx.graph.refresh", + "icon": "$(refresh)" }, { "category": "Nx", diff --git a/libs/vscode/nx-workspace/src/lib/get-nx-workspace.ts b/libs/vscode/nx-workspace/src/lib/get-nx-workspace.ts index 45a09bcab0..84114e7153 100644 --- a/libs/vscode/nx-workspace/src/lib/get-nx-workspace.ts +++ b/libs/vscode/nx-workspace/src/lib/get-nx-workspace.ts @@ -21,7 +21,7 @@ export async function getNxWorkspaceProjects(reset?: boolean): Promise<{ } export async function getNxWorkspacePath(): Promise { - const { workspacePath, nxVersion } = await getNxWorkspace(); + const { workspacePath } = await getNxWorkspace(); return workspacePath; } diff --git a/libs/vscode/project-graph/src/lib/graph-webview.ts b/libs/vscode/project-graph/src/lib/graph-webview.ts index 97c8c6c6f5..a564e78ee3 100644 --- a/libs/vscode/project-graph/src/lib/graph-webview.ts +++ b/libs/vscode/project-graph/src/lib/graph-webview.ts @@ -1,8 +1,10 @@ import { getNxWorkspacePath, + getNxWorkspaceProjects, getProjectGraphOutput, + revealNxProject, } from '@nx-console/vscode/nx-workspace'; -import { getOutputChannel } from '@nx-console/vscode/utils'; +import { getOutputChannel, getTelemetry } from '@nx-console/vscode/utils'; import { commands, Disposable, @@ -16,6 +18,7 @@ import { MessageType } from './graph-message-type'; import { graphService } from './graph.machine'; import { loadError, loadHtml, loadNoProject, loadSpinner } from './load-html'; import { join } from 'node:path'; +import { CliTaskProvider } from '@nx-console/vscode/tasks'; export class GraphWebView implements Disposable { panel: WebviewPanel | undefined; @@ -78,6 +81,7 @@ export class GraphWebView implements Disposable { this.panel.onDidDispose(() => { this.panel = undefined; graphService.send('VIEW_DESTROYED'); + commands.executeCommand('setContext', 'graphWebviewVisible', false); }); this.panel.webview.onDidReceiveMessage(async (event) => { @@ -89,11 +93,36 @@ export class GraphWebView implements Disposable { commands.executeCommand('nxConsole.refreshWorkspace'); } if (event.command === 'fileClick') { + getTelemetry().featureUsed('nx.graph.openProjectEdgeFile'); commands.executeCommand( 'vscode.open', Uri.file(join(workspacePath, event.data)) ); } + if (event.command === 'openProject') { + getTelemetry().featureUsed('nx.graph.openProjectConfigFile'); + getNxWorkspaceProjects().then((projects) => { + const root = projects[event.data]?.root; + if (!root) return; + revealNxProject(event.data, root); + }); + } + if (event.command === 'runTask') { + getTelemetry().featureUsed('nx.graph.runTask'); + CliTaskProvider.instance.executeTask({ + command: 'run', + positional: event.data, + flags: [], + }); + } + }); + + this.panel.onDidChangeViewState(({ webviewPanel }) => { + commands.executeCommand( + 'setContext', + 'graphWebviewVisible', + webviewPanel.visible + ); }); graphService.send('GET_CONTENT'); diff --git a/libs/vscode/project-graph/src/lib/load-html.ts b/libs/vscode/project-graph/src/lib/load-html.ts index d9c4719ecf..a23fb08913 100644 --- a/libs/vscode/project-graph/src/lib/load-html.ts +++ b/libs/vscode/project-graph/src/lib/load-html.ts @@ -234,6 +234,8 @@ export async function loadHtml(panel: WebviewPanel) { html` ` ); @@ -405,6 +407,25 @@ function registerFileClickListener() { data: message }) }) - `; } +function registerOpenProjectConfigCallback() { + return ` +window.externalApi?.registerOpenProjectConfigCallback?.((message) => { + window.vscode.postMessage({ + command: 'openProject', + data: message + }) +}) +`; +} +function registerRunTaskCallback() { + return ` +window.externalApi?.registerRunTaskCallback?.((message) => { + window.vscode.postMessage({ + command: 'runTask', + data: message + }) +}) +`; +}