Add link to open query results from compare view
This commit is contained in:
Родитель
b803a80d39
Коммит
9be355aa9d
|
@ -35,10 +35,13 @@ export class CompareInterfaceManager extends DisposableObject {
|
|||
private panelLoadedCallBacks: (() => void)[] = [];
|
||||
|
||||
constructor(
|
||||
public ctx: ExtensionContext,
|
||||
public databaseManager: DatabaseManager,
|
||||
public cliServer: CodeQLCliServer,
|
||||
public logger: Logger
|
||||
private ctx: ExtensionContext,
|
||||
private databaseManager: DatabaseManager,
|
||||
private cliServer: CodeQLCliServer,
|
||||
private logger: Logger,
|
||||
private showQueryResultsCallback: (
|
||||
item: CompletedQuery
|
||||
) => Promise<void>
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
@ -57,7 +60,11 @@ export class CompareInterfaceManager extends DisposableObject {
|
|||
currentResultSetName,
|
||||
fromResultSet,
|
||||
toResultSet,
|
||||
] = await this.findCommonResultSetNames(from, to, selectedResultSetName);
|
||||
] = await this.findCommonResultSetNames(
|
||||
from,
|
||||
to,
|
||||
selectedResultSetName
|
||||
);
|
||||
if (currentResultSetName) {
|
||||
await this.postMessage({
|
||||
t: "setComparisons",
|
||||
|
@ -147,7 +154,9 @@ export class CompareInterfaceManager extends DisposableObject {
|
|||
});
|
||||
}
|
||||
|
||||
private async handleMsgFromView(msg: FromCompareViewMessage): Promise<void> {
|
||||
private async handleMsgFromView(
|
||||
msg: FromCompareViewMessage
|
||||
): Promise<void> {
|
||||
switch (msg.t) {
|
||||
case "compareViewLoaded":
|
||||
this.panelLoaded = true;
|
||||
|
@ -162,6 +171,10 @@ export class CompareInterfaceManager extends DisposableObject {
|
|||
case "viewSourceFile":
|
||||
await jumpToLocation(msg, this.databaseManager, this.logger);
|
||||
break;
|
||||
|
||||
case "openQuery":
|
||||
await this.openQuery(msg.kind);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,7 +196,9 @@ export class CompareInterfaceManager extends DisposableObject {
|
|||
const fromSchemaNames = fromSchemas["result-sets"].map(
|
||||
(schema) => schema.name
|
||||
);
|
||||
const toSchemaNames = toSchemas["result-sets"].map((schema) => schema.name);
|
||||
const toSchemaNames = toSchemas["result-sets"].map(
|
||||
(schema) => schema.name
|
||||
);
|
||||
const commonResultSetNames = fromSchemaNames.filter((name) =>
|
||||
toSchemaNames.includes(name)
|
||||
);
|
||||
|
@ -229,7 +244,10 @@ export class CompareInterfaceManager extends DisposableObject {
|
|||
if (!schema) {
|
||||
throw new Error(`Schema ${resultSetName} not found.`);
|
||||
}
|
||||
const chunk = await this.cliServer.bqrsDecode(resultsPath, resultSetName);
|
||||
const chunk = await this.cliServer.bqrsDecode(
|
||||
resultsPath,
|
||||
resultSetName
|
||||
);
|
||||
const adaptedSchema = adaptSchema(schema);
|
||||
return adaptBqrs(adaptedSchema, chunk);
|
||||
}
|
||||
|
@ -241,4 +259,12 @@ export class CompareInterfaceManager extends DisposableObject {
|
|||
// Only compare columns that have the same name
|
||||
return resultsDiff(fromResults, toResults);
|
||||
}
|
||||
|
||||
private openQuery(kind: "from" | "to") {
|
||||
const toOpen =
|
||||
kind === "from" ? this.comparePair?.from : this.comparePair?.to;
|
||||
if (toOpen) {
|
||||
this.showQueryResultsCallback(toOpen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,8 +60,22 @@ export function Compare(props: {}): JSX.Element {
|
|||
<table className="vscode-codeql__compare-body">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{comparison.stats.fromQuery?.name}</td>
|
||||
<td>{comparison.stats.toQuery?.name}</td>
|
||||
<td>
|
||||
<a
|
||||
onClick={() => openQuery("from")}
|
||||
className="vscode-codeql__compare-open"
|
||||
>
|
||||
{comparison.stats.fromQuery?.name}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a
|
||||
onClick={() => openQuery("to")}
|
||||
className="vscode-codeql__compare-open"
|
||||
>
|
||||
{comparison.stats.toQuery?.name}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{comparison.stats.fromQuery?.time}</td>
|
||||
|
@ -105,6 +119,13 @@ export function Compare(props: {}): JSX.Element {
|
|||
}
|
||||
}
|
||||
|
||||
async function openQuery(kind: "from" | "to") {
|
||||
vscode.postMessage({
|
||||
t: "openQuery",
|
||||
kind,
|
||||
});
|
||||
}
|
||||
|
||||
function createRows(rows: ResultRow[], databaseUri: string) {
|
||||
return (
|
||||
<tbody>
|
||||
|
|
|
@ -272,54 +272,87 @@ export async function activate(ctx: ExtensionContext): Promise<void> {
|
|||
});
|
||||
}
|
||||
|
||||
async function activateWithInstalledDistribution(ctx: ExtensionContext, distributionManager: DistributionManager): Promise<void> {
|
||||
async function activateWithInstalledDistribution(
|
||||
ctx: ExtensionContext,
|
||||
distributionManager: DistributionManager
|
||||
): Promise<void> {
|
||||
beganMainExtensionActivation = true;
|
||||
// Remove any error stubs command handlers left over from first part
|
||||
// of activation.
|
||||
errorStubs.forEach(stub => stub.dispose());
|
||||
errorStubs.forEach((stub) => stub.dispose());
|
||||
|
||||
logger.log('Initializing configuration listener...');
|
||||
const qlConfigurationListener = await QueryServerConfigListener.createQueryServerConfigListener(distributionManager);
|
||||
logger.log("Initializing configuration listener...");
|
||||
const qlConfigurationListener = await QueryServerConfigListener.createQueryServerConfigListener(
|
||||
distributionManager
|
||||
);
|
||||
ctx.subscriptions.push(qlConfigurationListener);
|
||||
|
||||
logger.log('Initializing CodeQL cli server...');
|
||||
logger.log("Initializing CodeQL cli server...");
|
||||
const cliServer = new CodeQLCliServer(distributionManager, logger);
|
||||
ctx.subscriptions.push(cliServer);
|
||||
|
||||
logger.log('Initializing query server client.');
|
||||
const qs = new qsClient.QueryServerClient(qlConfigurationListener, cliServer, {
|
||||
logger: queryServerLogger,
|
||||
}, task => Window.withProgress({ title: 'CodeQL query server', location: ProgressLocation.Window }, task));
|
||||
logger.log("Initializing query server client.");
|
||||
const qs = new qsClient.QueryServerClient(
|
||||
qlConfigurationListener,
|
||||
cliServer,
|
||||
{
|
||||
logger: queryServerLogger,
|
||||
},
|
||||
(task) =>
|
||||
Window.withProgress(
|
||||
{ title: "CodeQL query server", location: ProgressLocation.Window },
|
||||
task
|
||||
)
|
||||
);
|
||||
ctx.subscriptions.push(qs);
|
||||
await qs.startQueryServer();
|
||||
|
||||
logger.log('Initializing database manager.');
|
||||
logger.log("Initializing database manager.");
|
||||
const dbm = new DatabaseManager(ctx, qlConfigurationListener, logger);
|
||||
ctx.subscriptions.push(dbm);
|
||||
logger.log('Initializing database panel.');
|
||||
const databaseUI = new DatabaseUI(ctx, cliServer, dbm, qs, getContextStoragePath(ctx));
|
||||
logger.log("Initializing database panel.");
|
||||
const databaseUI = new DatabaseUI(
|
||||
ctx,
|
||||
cliServer,
|
||||
dbm,
|
||||
qs,
|
||||
getContextStoragePath(ctx)
|
||||
);
|
||||
ctx.subscriptions.push(databaseUI);
|
||||
|
||||
logger.log('Initializing query history manager.');
|
||||
logger.log("Initializing query history manager.");
|
||||
const queryHistoryConfigurationListener = new QueryHistoryConfigListener();
|
||||
const showResults = async (item: CompletedQuery) =>
|
||||
showResultsForCompletedQuery(item, WebviewReveal.Forced);
|
||||
|
||||
const qhm = new QueryHistoryManager(
|
||||
ctx,
|
||||
queryHistoryConfigurationListener,
|
||||
async item => showResultsForCompletedQuery(item, WebviewReveal.Forced),
|
||||
async (from: CompletedQuery, to: CompletedQuery) => showResultsForComparison(from, to),
|
||||
showResults,
|
||||
async (from: CompletedQuery, to: CompletedQuery) =>
|
||||
showResultsForComparison(from, to),
|
||||
);
|
||||
logger.log('Initializing results panel interface.');
|
||||
logger.log("Initializing results panel interface.");
|
||||
const intm = new InterfaceManager(ctx, dbm, cliServer, queryServerLogger);
|
||||
ctx.subscriptions.push(intm);
|
||||
|
||||
logger.log('Initializing compare panel interface.');
|
||||
const cmpm = new CompareInterfaceManager(ctx, dbm, cliServer, queryServerLogger);
|
||||
logger.log("Initializing compare panel interface.");
|
||||
const cmpm = new CompareInterfaceManager(
|
||||
ctx,
|
||||
dbm,
|
||||
cliServer,
|
||||
queryServerLogger,
|
||||
showResults
|
||||
);
|
||||
ctx.subscriptions.push(cmpm);
|
||||
|
||||
logger.log('Initializing source archive filesystem provider.');
|
||||
logger.log("Initializing source archive filesystem provider.");
|
||||
archiveFilesystemProvider.activate(ctx);
|
||||
|
||||
async function showResultsForComparison(from: CompletedQuery, to: CompletedQuery): Promise<void> {
|
||||
async function showResultsForComparison(
|
||||
from: CompletedQuery,
|
||||
to: CompletedQuery
|
||||
): Promise<void> {
|
||||
try {
|
||||
await cmpm.showResults(from, to);
|
||||
} catch (e) {
|
||||
|
@ -327,18 +360,30 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu
|
|||
}
|
||||
}
|
||||
|
||||
async function showResultsForCompletedQuery(query: CompletedQuery, forceReveal: WebviewReveal): Promise<void> {
|
||||
async function showResultsForCompletedQuery(
|
||||
query: CompletedQuery,
|
||||
forceReveal: WebviewReveal
|
||||
): Promise<void> {
|
||||
await intm.showResults(query, forceReveal, false);
|
||||
}
|
||||
|
||||
async function compileAndRunQuery(quickEval: boolean, selectedQuery: Uri | undefined): Promise<void> {
|
||||
async function compileAndRunQuery(
|
||||
quickEval: boolean,
|
||||
selectedQuery: Uri | undefined
|
||||
): Promise<void> {
|
||||
if (qs !== undefined) {
|
||||
try {
|
||||
const dbItem = await databaseUI.getDatabaseItem();
|
||||
if (dbItem === undefined) {
|
||||
throw new Error('Can\'t run query without a selected database');
|
||||
throw new Error("Can't run query without a selected database");
|
||||
}
|
||||
const info = await compileAndRunQueryAgainstDatabase(cliServer, qs, dbItem, quickEval, selectedQuery);
|
||||
const info = await compileAndRunQueryAgainstDatabase(
|
||||
cliServer,
|
||||
qs,
|
||||
dbItem,
|
||||
quickEval,
|
||||
selectedQuery
|
||||
);
|
||||
const item = qhm.addQuery(info);
|
||||
await showResultsForCompletedQuery(item, WebviewReveal.NotForced);
|
||||
} catch (e) {
|
||||
|
@ -355,21 +400,28 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu
|
|||
|
||||
ctx.subscriptions.push(tmpDirDisposal);
|
||||
|
||||
logger.log('Initializing CodeQL language server.');
|
||||
const client = new LanguageClient('CodeQL Language Server', () => spawnIdeServer(qlConfigurationListener), {
|
||||
documentSelector: [
|
||||
{ language: 'ql', scheme: 'file' },
|
||||
{ language: 'yaml', scheme: 'file', pattern: '**/qlpack.yml' }
|
||||
],
|
||||
synchronize: {
|
||||
configurationSection: 'codeQL'
|
||||
logger.log("Initializing CodeQL language server.");
|
||||
const client = new LanguageClient(
|
||||
"CodeQL Language Server",
|
||||
() => spawnIdeServer(qlConfigurationListener),
|
||||
{
|
||||
documentSelector: [
|
||||
{ language: "ql", scheme: "file" },
|
||||
{ language: "yaml", scheme: "file", pattern: "**/qlpack.yml" },
|
||||
],
|
||||
synchronize: {
|
||||
configurationSection: "codeQL",
|
||||
},
|
||||
// Ensure that language server exceptions are logged to the same channel as its output.
|
||||
outputChannel: ideServerLogger.outputChannel,
|
||||
},
|
||||
// Ensure that language server exceptions are logged to the same channel as its output.
|
||||
outputChannel: ideServerLogger.outputChannel
|
||||
}, true);
|
||||
true
|
||||
);
|
||||
|
||||
logger.log('Initializing QLTest interface.');
|
||||
const testExplorerExtension = extensions.getExtension<TestHub>(testExplorerExtensionId);
|
||||
logger.log("Initializing QLTest interface.");
|
||||
const testExplorerExtension = extensions.getExtension<TestHub>(
|
||||
testExplorerExtensionId
|
||||
);
|
||||
if (testExplorerExtension) {
|
||||
const testHub = testExplorerExtension.exports;
|
||||
const testAdapterFactory = new QLTestAdapterFactory(testHub, cliServer);
|
||||
|
@ -379,24 +431,58 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu
|
|||
ctx.subscriptions.push(testUIService);
|
||||
}
|
||||
|
||||
logger.log('Registering top-level command palette commands.');
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.runQuery', async (uri: Uri | undefined) => await compileAndRunQuery(false, uri)));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.quickEval', async (uri: Uri | undefined) => await compileAndRunQuery(true, uri)));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.quickQuery', async () => displayQuickQuery(ctx, cliServer, databaseUI)));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.restartQueryServer', async () => {
|
||||
await qs.restartQueryServer();
|
||||
helpers.showAndLogInformationMessage('CodeQL Query Server restarted.', { outputLogger: queryServerLogger });
|
||||
}));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseFolder', () => databaseUI.handleChooseDatabaseFolder()));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseArchive', () => databaseUI.handleChooseDatabaseArchive()));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseLgtm', () => databaseUI.handleChooseDatabaseLgtm()));
|
||||
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseInternet', () => databaseUI.handleChooseDatabaseInternet()));
|
||||
logger.log("Registering top-level command palette commands.");
|
||||
ctx.subscriptions.push(
|
||||
commands.registerCommand(
|
||||
"codeQL.runQuery",
|
||||
async (uri: Uri | undefined) => await compileAndRunQuery(false, uri)
|
||||
)
|
||||
);
|
||||
ctx.subscriptions.push(
|
||||
commands.registerCommand(
|
||||
"codeQL.quickEval",
|
||||
async (uri: Uri | undefined) => await compileAndRunQuery(true, uri)
|
||||
)
|
||||
);
|
||||
ctx.subscriptions.push(
|
||||
commands.registerCommand("codeQL.quickQuery", async () =>
|
||||
displayQuickQuery(ctx, cliServer, databaseUI)
|
||||
)
|
||||
);
|
||||
ctx.subscriptions.push(
|
||||
commands.registerCommand("codeQL.restartQueryServer", async () => {
|
||||
await qs.restartQueryServer();
|
||||
helpers.showAndLogInformationMessage("CodeQL Query Server restarted.", {
|
||||
outputLogger: queryServerLogger,
|
||||
});
|
||||
})
|
||||
);
|
||||
ctx.subscriptions.push(
|
||||
commands.registerCommand("codeQL.chooseDatabaseFolder", () =>
|
||||
databaseUI.handleChooseDatabaseFolder()
|
||||
)
|
||||
);
|
||||
ctx.subscriptions.push(
|
||||
commands.registerCommand("codeQL.chooseDatabaseArchive", () =>
|
||||
databaseUI.handleChooseDatabaseArchive()
|
||||
)
|
||||
);
|
||||
ctx.subscriptions.push(
|
||||
commands.registerCommand("codeQL.chooseDatabaseLgtm", () =>
|
||||
databaseUI.handleChooseDatabaseLgtm()
|
||||
)
|
||||
);
|
||||
ctx.subscriptions.push(
|
||||
commands.registerCommand("codeQL.chooseDatabaseInternet", () =>
|
||||
databaseUI.handleChooseDatabaseInternet()
|
||||
)
|
||||
);
|
||||
|
||||
logger.log('Starting language server.');
|
||||
logger.log("Starting language server.");
|
||||
ctx.subscriptions.push(client.start());
|
||||
|
||||
// Jump-to-definition and find-references
|
||||
logger.log('Registering jump-to-definition handlers.');
|
||||
logger.log("Registering jump-to-definition handlers.");
|
||||
languages.registerDefinitionProvider(
|
||||
{ scheme: archiveFilesystemProvider.zipArchiveScheme },
|
||||
new TemplateQueryDefinitionProvider(cliServer, qs, dbm)
|
||||
|
@ -406,7 +492,7 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu
|
|||
new TemplateQueryReferenceProvider(cliServer, qs, dbm)
|
||||
);
|
||||
|
||||
logger.log('Successfully finished extension initialization.');
|
||||
logger.log("Successfully finished extension initialization.");
|
||||
}
|
||||
|
||||
function getContextStoragePath(ctx: ExtensionContext) {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import * as sarif from 'sarif';
|
||||
import * as sarif from "sarif";
|
||||
import {
|
||||
ResolvableLocationValue,
|
||||
ColumnSchema,
|
||||
ResultSetSchema,
|
||||
} from "semmle-bqrs";
|
||||
import { ResultRow, ParsedResultSets, RawResultSet } from './adapt';
|
||||
import { ResultRow, ParsedResultSets, RawResultSet } from "./adapt";
|
||||
|
||||
/**
|
||||
* This module contains types and code that are shared between
|
||||
|
@ -87,11 +87,11 @@ export type SortedResultsMap = { [resultSet: string]: SortedResultSetInfo };
|
|||
* As a result of receiving this message, listeners might want to display a loading indicator.
|
||||
*/
|
||||
export interface ResultsUpdatingMsg {
|
||||
t: 'resultsUpdating';
|
||||
t: "resultsUpdating";
|
||||
}
|
||||
|
||||
export interface SetStateMsg {
|
||||
t: 'setState';
|
||||
t: "setState";
|
||||
resultsPath: string;
|
||||
origResultsPaths: ResultsPaths;
|
||||
sortedResultsMap: SortedResultsMap;
|
||||
|
@ -115,13 +115,16 @@ export interface SetStateMsg {
|
|||
|
||||
/** Advance to the next or previous path no in the path viewer */
|
||||
export interface NavigatePathMsg {
|
||||
t: 'navigatePath';
|
||||
t: "navigatePath";
|
||||
|
||||
/** 1 for next, -1 for previous */
|
||||
direction: number;
|
||||
}
|
||||
|
||||
export type IntoResultsViewMsg = ResultsUpdatingMsg | SetStateMsg | NavigatePathMsg;
|
||||
export type IntoResultsViewMsg =
|
||||
| ResultsUpdatingMsg
|
||||
| SetStateMsg
|
||||
| NavigatePathMsg;
|
||||
|
||||
export type FromResultsViewMsg =
|
||||
| ViewSourceFileMsg
|
||||
|
@ -132,13 +135,13 @@ export type FromResultsViewMsg =
|
|||
| ChangePage;
|
||||
|
||||
export interface ViewSourceFileMsg {
|
||||
t: 'viewSourceFile';
|
||||
t: "viewSourceFile";
|
||||
loc: ResolvableLocationValue;
|
||||
databaseUri: string;
|
||||
}
|
||||
|
||||
interface ToggleDiagnostics {
|
||||
t: 'toggleDiagnostics';
|
||||
t: "toggleDiagnostics";
|
||||
databaseUri: string;
|
||||
metadata?: QueryMetadata;
|
||||
origResultsPaths: ResultsPaths;
|
||||
|
@ -147,17 +150,18 @@ interface ToggleDiagnostics {
|
|||
}
|
||||
|
||||
interface ResultViewLoaded {
|
||||
t: 'resultViewLoaded';
|
||||
t: "resultViewLoaded";
|
||||
}
|
||||
|
||||
interface ChangePage {
|
||||
t: 'changePage';
|
||||
t: "changePage";
|
||||
pageNumber: number; // 0-indexed, displayed to the user as 1-indexed
|
||||
selectedTable: string;
|
||||
}
|
||||
|
||||
export enum SortDirection {
|
||||
asc, desc
|
||||
asc,
|
||||
desc,
|
||||
}
|
||||
|
||||
export interface RawResultsSortState {
|
||||
|
@ -165,8 +169,7 @@ export interface RawResultsSortState {
|
|||
sortDirection: SortDirection;
|
||||
}
|
||||
|
||||
export type InterpretedResultsSortColumn =
|
||||
'alert-message';
|
||||
export type InterpretedResultsSortColumn = "alert-message";
|
||||
|
||||
export interface InterpretedResultsSortState {
|
||||
sortBy: InterpretedResultsSortColumn;
|
||||
|
@ -174,7 +177,7 @@ export interface InterpretedResultsSortState {
|
|||
}
|
||||
|
||||
interface ChangeRawResultsSortMsg {
|
||||
t: 'changeSort';
|
||||
t: "changeSort";
|
||||
resultSetName: string;
|
||||
/**
|
||||
* sortState being undefined means don't sort, just present results in the order
|
||||
|
@ -184,7 +187,7 @@ interface ChangeRawResultsSortMsg {
|
|||
}
|
||||
|
||||
interface ChangeInterpretedResultsSortMsg {
|
||||
t: 'changeInterpretedSort';
|
||||
t: "changeInterpretedSort";
|
||||
/**
|
||||
* sortState being undefined means don't sort, just present results in the order
|
||||
* they appear in the sarif file.
|
||||
|
@ -195,21 +198,26 @@ interface ChangeInterpretedResultsSortMsg {
|
|||
export type FromCompareViewMessage =
|
||||
| CompareViewLoadedMessage
|
||||
| ChangeCompareMessage
|
||||
| ViewSourceFileMsg;
|
||||
| ViewSourceFileMsg
|
||||
| OpenQueryMessage;
|
||||
|
||||
interface CompareViewLoadedMessage {
|
||||
t: 'compareViewLoaded';
|
||||
t: "compareViewLoaded";
|
||||
}
|
||||
|
||||
export interface OpenQueryMessage {
|
||||
readonly t: "openQuery";
|
||||
readonly kind: "from" | "to";
|
||||
}
|
||||
|
||||
interface ChangeCompareMessage {
|
||||
t: 'changeCompare';
|
||||
t: "changeCompare";
|
||||
newResultSetName: string;
|
||||
// TODO do we need to include the ids of the queries
|
||||
}
|
||||
|
||||
export type ToCompareViewMessage = SetComparisonsMessage;
|
||||
|
||||
export interface SetComparisonsMessage {
|
||||
export interface SetComparisonsMessage {
|
||||
readonly t: "setComparisons";
|
||||
readonly stats: {
|
||||
fromQuery?: {
|
||||
|
@ -231,9 +239,9 @@ export interface SetComparisonsMessage {
|
|||
}
|
||||
|
||||
export enum DiffKind {
|
||||
Add = 'Add',
|
||||
Remove = 'Remove',
|
||||
Change = 'Change'
|
||||
Add = "Add",
|
||||
Remove = "Remove",
|
||||
Change = "Change",
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -165,3 +165,8 @@ td.vscode-codeql__path-index-cell {
|
|||
margin: 20px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.vscode-codeql__compare-open {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче