Skip to content

Commit d2af550

Browse files
committed
Merge remote-tracking branch 'origin/main' into koesie10/bundle-codicons
2 parents 3749e17 + cf36a52 commit d2af550

File tree

6 files changed

+277
-298
lines changed

6 files changed

+277
-298
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import {
2+
WebviewPanel,
3+
ExtensionContext,
4+
window as Window,
5+
ViewColumn,
6+
Uri,
7+
WebviewPanelOptions,
8+
WebviewOptions
9+
} from 'vscode';
10+
import * as path from 'path';
11+
12+
import { DisposableObject } from './pure/disposable-object';
13+
import { tmpDir } from './helpers';
14+
import { getHtmlForWebview, WebviewMessage, WebviewView } from './interface-utils';
15+
16+
export type InterfacePanelConfig = {
17+
viewId: string;
18+
title: string;
19+
viewColumn: ViewColumn;
20+
view: WebviewView;
21+
preserveFocus?: boolean;
22+
additionalOptions?: WebviewPanelOptions & WebviewOptions;
23+
}
24+
25+
export abstract class AbstractInterfaceManager<ToMessage extends WebviewMessage, FromMessage extends WebviewMessage> extends DisposableObject {
26+
protected panel: WebviewPanel | undefined;
27+
protected panelLoaded = false;
28+
protected panelLoadedCallBacks: (() => void)[] = [];
29+
30+
constructor(
31+
protected readonly ctx: ExtensionContext
32+
) {
33+
super();
34+
}
35+
36+
protected get isShowingPanel() {
37+
return !!this.panel;
38+
}
39+
40+
protected getPanel(): WebviewPanel {
41+
if (this.panel == undefined) {
42+
const { ctx } = this;
43+
44+
const config = this.getPanelConfig();
45+
46+
this.panel = Window.createWebviewPanel(
47+
config.viewId,
48+
config.title,
49+
{ viewColumn: ViewColumn.Active, preserveFocus: true },
50+
{
51+
enableScripts: true,
52+
enableFindWidget: true,
53+
retainContextWhenHidden: true,
54+
...config.additionalOptions,
55+
localResourceRoots: [
56+
...(config.additionalOptions?.localResourceRoots ?? []),
57+
Uri.file(tmpDir.name),
58+
Uri.file(path.join(ctx.extensionPath, 'out')),
59+
Uri.file(path.join(ctx.extensionPath, 'node_modules/@vscode/codicons/dist')),
60+
],
61+
}
62+
);
63+
this.push(
64+
this.panel.onDidDispose(
65+
() => {
66+
this.panel = undefined;
67+
this.panelLoaded = false;
68+
this.onPanelDispose();
69+
},
70+
null,
71+
ctx.subscriptions
72+
)
73+
);
74+
75+
this.panel.webview.html = getHtmlForWebview(
76+
ctx,
77+
this.panel.webview,
78+
config.view,
79+
{
80+
allowInlineStyles: true,
81+
}
82+
);
83+
this.push(
84+
this.panel.webview.onDidReceiveMessage(
85+
async (e) => this.onMessage(e),
86+
undefined,
87+
ctx.subscriptions
88+
)
89+
);
90+
}
91+
return this.panel;
92+
}
93+
94+
protected abstract getPanelConfig(): InterfacePanelConfig;
95+
96+
protected abstract onPanelDispose(): void;
97+
98+
protected abstract onMessage(msg: FromMessage): Promise<void>;
99+
100+
protected waitForPanelLoaded(): Promise<void> {
101+
return new Promise((resolve) => {
102+
if (this.panelLoaded) {
103+
resolve();
104+
} else {
105+
this.panelLoadedCallBacks.push(resolve);
106+
}
107+
});
108+
}
109+
110+
protected onWebViewLoaded(): void {
111+
this.panelLoaded = true;
112+
this.panelLoadedCallBacks.forEach((cb) => cb());
113+
this.panelLoadedCallBacks = [];
114+
}
115+
116+
protected postMessage(msg: ToMessage): Thenable<boolean> {
117+
return this.getPanel().webview.postMessage(msg);
118+
}
119+
}

extensions/ql-vscode/src/compare/compare-interface.ts

Lines changed: 17 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
1-
import { DisposableObject } from '../pure/disposable-object';
21
import {
3-
WebviewPanel,
42
ExtensionContext,
5-
window as Window,
63
ViewColumn,
7-
Uri,
84
} from 'vscode';
9-
import * as path from 'path';
105

11-
import { tmpDir } from '../helpers';
126
import {
137
FromCompareViewMessage,
148
ToCompareViewMessage,
@@ -17,26 +11,24 @@ import {
1711
import { Logger } from '../logging';
1812
import { CodeQLCliServer } from '../cli';
1913
import { DatabaseManager } from '../databases';
20-
import { getHtmlForWebview, jumpToLocation } from '../interface-utils';
14+
import { jumpToLocation } from '../interface-utils';
2115
import { transformBqrsResultSet, RawResultSet, BQRSInfo } from '../pure/bqrs-cli-types';
2216
import resultsDiff from './resultsDiff';
2317
import { CompletedLocalQueryInfo } from '../query-results';
2418
import { getErrorMessage } from '../pure/helpers-pure';
2519
import { HistoryItemLabelProvider } from '../history-item-label-provider';
20+
import { AbstractInterfaceManager, InterfacePanelConfig } from '../abstract-interface-manager';
2621

2722
interface ComparePair {
2823
from: CompletedLocalQueryInfo;
2924
to: CompletedLocalQueryInfo;
3025
}
3126

32-
export class CompareInterfaceManager extends DisposableObject {
27+
export class CompareInterfaceManager extends AbstractInterfaceManager<ToCompareViewMessage, FromCompareViewMessage> {
3328
private comparePair: ComparePair | undefined;
34-
private panel: WebviewPanel | undefined;
35-
private panelLoaded = false;
36-
private panelLoadedCallBacks: (() => void)[] = [];
3729

3830
constructor(
39-
private ctx: ExtensionContext,
31+
ctx: ExtensionContext,
4032
private databaseManager: DatabaseManager,
4133
private cliServer: CodeQLCliServer,
4234
private logger: Logger,
@@ -45,7 +37,7 @@ export class CompareInterfaceManager extends DisposableObject {
4537
item: CompletedLocalQueryInfo
4638
) => Promise<void>
4739
) {
48-
super();
40+
super(ctx);
4941
}
5042

5143
async showResults(
@@ -103,64 +95,24 @@ export class CompareInterfaceManager extends DisposableObject {
10395
}
10496
}
10597

106-
getPanel(): WebviewPanel {
107-
if (this.panel == undefined) {
108-
const { ctx } = this;
109-
const panel = (this.panel = Window.createWebviewPanel(
110-
'compareView',
111-
'Compare CodeQL Query Results',
112-
{ viewColumn: ViewColumn.Active, preserveFocus: true },
113-
{
114-
enableScripts: true,
115-
enableFindWidget: true,
116-
retainContextWhenHidden: true,
117-
localResourceRoots: [
118-
Uri.file(tmpDir.name),
119-
Uri.file(path.join(this.ctx.extensionPath, 'out')),
120-
],
121-
}
122-
));
123-
this.push(this.panel.onDidDispose(
124-
() => {
125-
this.panel = undefined;
126-
this.comparePair = undefined;
127-
},
128-
null,
129-
ctx.subscriptions
130-
));
131-
132-
panel.webview.html = getHtmlForWebview(
133-
ctx,
134-
panel.webview,
135-
'compare'
136-
);
137-
this.push(panel.webview.onDidReceiveMessage(
138-
async (e) => this.handleMsgFromView(e),
139-
undefined,
140-
ctx.subscriptions
141-
));
142-
}
143-
return this.panel;
98+
protected getPanelConfig(): InterfacePanelConfig {
99+
return {
100+
viewId: 'compareView',
101+
title: 'Compare CodeQL Query Results',
102+
viewColumn: ViewColumn.Active,
103+
preserveFocus: true,
104+
view: 'compare',
105+
};
144106
}
145107

146-
private waitForPanelLoaded(): Promise<void> {
147-
return new Promise((resolve) => {
148-
if (this.panelLoaded) {
149-
resolve();
150-
} else {
151-
this.panelLoadedCallBacks.push(resolve);
152-
}
153-
});
108+
protected onPanelDispose(): void {
109+
this.comparePair = undefined;
154110
}
155111

156-
private async handleMsgFromView(
157-
msg: FromCompareViewMessage
158-
): Promise<void> {
112+
protected async onMessage(msg: FromCompareViewMessage): Promise<void> {
159113
switch (msg.t) {
160114
case 'compareViewLoaded':
161-
this.panelLoaded = true;
162-
this.panelLoadedCallBacks.forEach((cb) => cb());
163-
this.panelLoadedCallBacks = [];
115+
this.onWebViewLoaded();
164116
break;
165117

166118
case 'changeCompare':
@@ -177,10 +129,6 @@ export class CompareInterfaceManager extends DisposableObject {
177129
}
178130
}
179131

180-
private postMessage(msg: ToCompareViewMessage): Thenable<boolean> {
181-
return this.getPanel().webview.postMessage(msg);
182-
}
183-
184132
private async findCommonResultSetNames(
185133
from: CompletedLocalQueryInfo,
186134
to: CompletedLocalQueryInfo,

extensions/ql-vscode/src/interface-utils.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,28 +112,41 @@ export function tryResolveLocation(
112112
}
113113
}
114114

115+
export type WebviewView = 'results' | 'compare' | 'remote-queries';
116+
117+
export interface WebviewMessage {
118+
t: string;
119+
}
120+
115121
/**
116122
* Returns HTML to populate the given webview.
117123
* Uses a content security policy that only loads the given script.
118124
*/
119125
export function getHtmlForWebview(
120126
ctx: ExtensionContext,
121127
webview: Webview,
122-
view: 'results' | 'compare' | 'remote-queries',
128+
view: WebviewView,
123129
{
124-
allowInlineStyles
130+
allowInlineStyles,
125131
}: {
126132
allowInlineStyles?: boolean;
127133
} = {
128-
allowInlineStyles: false
134+
allowInlineStyles: false,
129135
}
130136
): string {
131137
const scriptUriOnDisk = Uri.file(
132138
ctx.asAbsolutePath('out/webview.js')
133139
);
134140

141+
// Allows use of the VS Code "codicons" icon set.
142+
// See https://github.com/microsoft/vscode-codicons
143+
const codiconsPathOnDisk = Uri.file(
144+
ctx.asAbsolutePath('node_modules/@vscode/codicons/dist/codicon.css')
145+
);
146+
135147
const stylesheetUrisOnDisk = [
136-
Uri.file(ctx.asAbsolutePath('out/webview.css'))
148+
Uri.file(ctx.asAbsolutePath('out/webview.css')),
149+
codiconsPathOnDisk
137150
];
138151

139152
// Convert the on-disk URIs into webview URIs.

0 commit comments

Comments
 (0)