From 06cf608ed4752a4e63cd371ba377e77da5bbf85c Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 4 Apr 2023 13:47:10 +0200 Subject: [PATCH] Add link to example usage in data extensions editor This will allow a user to click on the number of usages to jump to an example usage in the code. --- .../data-extensions-editor-view.ts | 36 +++++++++++++++++-- .../ql-vscode/src/pure/interface-types.ts | 9 ++++- .../view/data-extensions-editor/MethodRow.tsx | 19 +++++++++- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts b/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts index 9b0696ead..ac69fd29e 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts @@ -1,4 +1,9 @@ -import { CancellationTokenSource, ExtensionContext, ViewColumn } from "vscode"; +import { + CancellationTokenSource, + ExtensionContext, + ViewColumn, + window, +} from "vscode"; import { AbstractWebview, WebviewPanelConfig } from "../abstract-webview"; import { FromDataExtensionsEditorMessage, @@ -14,6 +19,9 @@ import { dump } from "js-yaml"; import { getOnDiskWorkspaceFolders } from "../helpers"; import { DatabaseItem } from "../local-databases"; import { CodeQLCliServer } from "../cli"; +import { assertNever } from "../pure/helpers-pure"; +import { ResolvableLocationValue } from "../pure/bqrs-cli-types"; +import { showResolvableLocation } from "../interface-utils"; export class DataExtensionsEditorView extends AbstractWebview< ToDataExtensionsEditorMessage, @@ -57,9 +65,13 @@ export class DataExtensionsEditorView extends AbstractWebview< case "viewLoaded": await this.onWebViewLoaded(); + break; + case "jumpToUsage": + await this.jumpToUsage(msg.location); + break; default: - throw new Error("Unexpected message type"); + assertNever(msg); } } @@ -69,6 +81,26 @@ export class DataExtensionsEditorView extends AbstractWebview< await this.loadExternalApiUsages(); } + protected async jumpToUsage( + location: ResolvableLocationValue, + ): Promise { + try { + await showResolvableLocation(location, this.databaseItem); + } catch (e) { + if (e instanceof Error) { + if (e.message.match(/File not found/)) { + void window.showErrorMessage( + "Original file of this result is not in the database's source archive.", + ); + } else { + void extLogger.log(`Unable to handleMsgFromView: ${e.message}`); + } + } else { + void extLogger.log(`Unable to handleMsgFromView: ${e}`); + } + } + } + protected async loadExternalApiUsages(): Promise { const queryResult = await this.runQuery(); if (!queryResult) { diff --git a/extensions/ql-vscode/src/pure/interface-types.ts b/extensions/ql-vscode/src/pure/interface-types.ts index b3ed4809c..8f1e9202a 100644 --- a/extensions/ql-vscode/src/pure/interface-types.ts +++ b/extensions/ql-vscode/src/pure/interface-types.ts @@ -492,8 +492,15 @@ export interface ShowProgressMessage { message: string; } +export interface JumpToUsageMessage { + t: "jumpToUsage"; + location: ResolvableLocationValue; +} + export type ToDataExtensionsEditorMessage = | SetExternalApiResultsMessage | ShowProgressMessage; -export type FromDataExtensionsEditorMessage = ViewLoadedMsg; +export type FromDataExtensionsEditorMessage = + | ViewLoadedMsg + | JumpToUsageMessage; diff --git a/extensions/ql-vscode/src/view/data-extensions-editor/MethodRow.tsx b/extensions/ql-vscode/src/view/data-extensions-editor/MethodRow.tsx index d02259a20..89652a7f3 100644 --- a/extensions/ql-vscode/src/view/data-extensions-editor/MethodRow.tsx +++ b/extensions/ql-vscode/src/view/data-extensions-editor/MethodRow.tsx @@ -12,6 +12,7 @@ import { import * as React from "react"; import { useCallback, useMemo } from "react"; import styled from "styled-components"; +import { vscode } from "../vscode-api"; const Dropdown = styled(VSCodeDropdown)` width: 100%; @@ -29,6 +30,13 @@ const SupportedUnsupportedSpan = styled.span` color: ${(props) => (props.supported ? "green" : "red")}; `; +const UsagesButton = styled.button` + color: var(--vscode-editor-foreground); + background-color: transparent; + border: none; + cursor: pointer; +`; + type Props = { method: ExternalApiUsage; model: ModeledMethod | undefined; @@ -105,6 +113,13 @@ export const MethodRow = ({ method, model, onChange }: Props) => { [onChange, method, model], ); + const jumpToUsage = useCallback(() => { + vscode.postMessage({ + t: "jumpToUsage", + location: method.usages[0].url, + }); + }, [method]); + return ( @@ -119,7 +134,9 @@ export const MethodRow = ({ method, model, onChange }: Props) => { - {method.usages.length} + + {method.usages.length} + {(!method.supported || (model && model?.type !== "none")) && (