Skip to content

Commit e9d0bed

Browse files
authored
Reduce duplication for image/audio preview (microsoft#159507)
1 parent 2ffd80a commit e9d0bed

File tree

7 files changed

+165
-189
lines changed

7 files changed

+165
-189
lines changed

extensions/image-preview/src/audioPreview.ts

Lines changed: 10 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import * as vscode from 'vscode';
77
import * as nls from 'vscode-nls';
88
import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry';
9-
import { Disposable } from './util/dispose';
9+
import { MediaPreview, reopenAsText } from './mediaPreview';
1010
import { escapeAttribute, getNonce } from './util/dom';
1111

1212
const localize = nls.loadMessageBundle();
@@ -29,113 +29,34 @@ class AudioPreviewProvider implements vscode.CustomReadonlyEditorProvider {
2929
}
3030
}
3131

32-
const enum PreviewState {
33-
Disposed,
34-
Visible,
35-
Active,
36-
}
37-
38-
class AudioPreview extends Disposable {
3932

40-
private readonly id: string = `${Date.now()}-${Math.random().toString()}`;
41-
42-
private _previewState = PreviewState.Visible;
43-
private _binarySize: number | undefined;
33+
class AudioPreview extends MediaPreview {
4434

4535
private readonly emptyAudioDataUri = 'data:audio/wav;base64,';
4636

4737
constructor(
4838
private readonly extensionRoot: vscode.Uri,
49-
private readonly resource: vscode.Uri,
50-
private readonly webviewEditor: vscode.WebviewPanel,
51-
private readonly binarySizeStatusBarEntry: BinarySizeStatusBarEntry,
39+
resource: vscode.Uri,
40+
webviewEditor: vscode.WebviewPanel,
41+
binarySizeStatusBarEntry: BinarySizeStatusBarEntry,
5242
) {
53-
super();
54-
55-
const resourceRoot = resource.with({
56-
path: resource.path.replace(/\/[^\/]+?\.\w+$/, '/'),
57-
});
58-
59-
webviewEditor.webview.options = {
60-
enableScripts: true,
61-
enableForms: false,
62-
localResourceRoots: [
63-
resourceRoot,
64-
extensionRoot,
65-
]
66-
};
43+
super(extensionRoot, resource, webviewEditor, binarySizeStatusBarEntry);
6744

6845
this._register(webviewEditor.webview.onDidReceiveMessage(message => {
6946
switch (message.type) {
7047
case 'reopen-as-text': {
71-
vscode.commands.executeCommand('vscode.openWith', resource, 'default', webviewEditor.viewColumn);
48+
reopenAsText(resource, webviewEditor.viewColumn);
7249
break;
7350
}
7451
}
7552
}));
7653

77-
this._register(webviewEditor.onDidChangeViewState(() => {
78-
this.update();
79-
}));
80-
81-
this._register(webviewEditor.onDidDispose(() => {
82-
if (this._previewState === PreviewState.Active) {
83-
this.binarySizeStatusBarEntry.hide(this.id);
84-
}
85-
this._previewState = PreviewState.Disposed;
86-
}));
87-
88-
const watcher = this._register(vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(resource, '*')));
89-
this._register(watcher.onDidChange(e => {
90-
if (e.toString() === this.resource.toString()) {
91-
this.render();
92-
}
93-
}));
94-
this._register(watcher.onDidDelete(e => {
95-
if (e.toString() === this.resource.toString()) {
96-
this.webviewEditor.dispose();
97-
}
98-
}));
99-
100-
vscode.workspace.fs.stat(resource).then(({ size }) => {
101-
this._binarySize = size;
102-
this.update();
103-
});
104-
54+
this.updateBinarySize();
10555
this.render();
106-
this.update();
107-
}
108-
109-
private async render() {
110-
if (this._previewState === PreviewState.Disposed) {
111-
return;
112-
}
113-
114-
const content = await this.getWebviewContents();
115-
if (this._previewState as PreviewState === PreviewState.Disposed) {
116-
return;
117-
}
118-
119-
this.webviewEditor.webview.html = content;
120-
}
121-
122-
private update() {
123-
if (this._previewState === PreviewState.Disposed) {
124-
return;
125-
}
126-
127-
if (this.webviewEditor.active) {
128-
this._previewState = PreviewState.Active;
129-
this.binarySizeStatusBarEntry.show(this.id, this._binarySize);
130-
} else {
131-
if (this._previewState === PreviewState.Active) {
132-
this.binarySizeStatusBarEntry.hide(this.id);
133-
}
134-
this._previewState = PreviewState.Visible;
135-
}
56+
this.updateState();
13657
}
13758

138-
private async getWebviewContents(): Promise<string> {
59+
protected async getWebviewContents(): Promise<string> {
13960
const version = Date.now().toString();
14061
const settings = {
14162
src: await this.getResourcePath(this.webviewEditor, this.resource, version),

extensions/image-preview/src/binarySizeStatusBarEntry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class BinarySizeStatusBarEntry extends PreviewStatusBarEntry {
4242
super('status.imagePreview.binarySize', localize('sizeStatusBar.name', "Image Binary Size"), vscode.StatusBarAlignment.Right, 100);
4343
}
4444

45-
public show(owner: string, size: number | undefined) {
45+
public show(owner: unknown, size: number | undefined) {
4646
if (typeof size === 'number') {
4747
super.showItem(owner, BinarySize.formatSize(size));
4848
} else {

extensions/image-preview/src/imagePreview/index.ts

Lines changed: 42 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import * as vscode from 'vscode';
77
import * as nls from 'vscode-nls';
88
import { BinarySizeStatusBarEntry } from '../binarySizeStatusBarEntry';
9-
import { Disposable } from '../util/dispose';
9+
import { MediaPreview, PreviewState, reopenAsText } from '../mediaPreview';
1010
import { escapeAttribute, getNonce } from '../util/dom';
1111
import { SizeStatusBarEntry } from './sizeStatusBarEntry';
1212
import { Scale, ZoomStatusBarEntry } from './zoomStatusBarEntry';
@@ -17,8 +17,8 @@ export class PreviewManager implements vscode.CustomReadonlyEditorProvider {
1717

1818
public static readonly viewType = 'imagePreview.previewEditor';
1919

20-
private readonly _previews = new Set<Preview>();
21-
private _activePreview: Preview | undefined;
20+
private readonly _previews = new Set<ImagePreview>();
21+
private _activePreview: ImagePreview | undefined;
2222

2323
constructor(
2424
private readonly extensionRoot: vscode.Uri,
@@ -35,7 +35,7 @@ export class PreviewManager implements vscode.CustomReadonlyEditorProvider {
3535
document: vscode.CustomDocument,
3636
webviewEditor: vscode.WebviewPanel,
3737
): Promise<void> {
38-
const preview = new Preview(this.extensionRoot, document.uri, webviewEditor, this.sizeStatusBarEntry, this.binarySizeStatusBarEntry, this.zoomStatusBarEntry);
38+
const preview = new ImagePreview(this.extensionRoot, document.uri, webviewEditor, this.sizeStatusBarEntry, this.binarySizeStatusBarEntry, this.zoomStatusBarEntry);
3939
this._previews.add(preview);
4040
this.setActivePreview(preview);
4141

@@ -52,154 +52,102 @@ export class PreviewManager implements vscode.CustomReadonlyEditorProvider {
5252

5353
public get activePreview() { return this._activePreview; }
5454

55-
private setActivePreview(value: Preview | undefined): void {
55+
private setActivePreview(value: ImagePreview | undefined): void {
5656
this._activePreview = value;
5757
}
5858
}
5959

60-
const enum PreviewState {
61-
Disposed,
62-
Visible,
63-
Active,
64-
}
65-
66-
class Preview extends Disposable {
6760

68-
private readonly id: string = `${Date.now()}-${Math.random().toString()}`;
61+
class ImagePreview extends MediaPreview {
6962

70-
private _previewState = PreviewState.Visible;
7163
private _imageSize: string | undefined;
72-
private _imageBinarySize: number | undefined;
7364
private _imageZoom: Scale | undefined;
7465

7566
private readonly emptyPngDataUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAEElEQVR42gEFAPr/AP///wAI/AL+Sr4t6gAAAABJRU5ErkJggg==';
7667

7768
constructor(
7869
private readonly extensionRoot: vscode.Uri,
79-
private readonly resource: vscode.Uri,
80-
private readonly webviewEditor: vscode.WebviewPanel,
70+
resource: vscode.Uri,
71+
webviewEditor: vscode.WebviewPanel,
8172
private readonly sizeStatusBarEntry: SizeStatusBarEntry,
82-
private readonly binarySizeStatusBarEntry: BinarySizeStatusBarEntry,
73+
binarySizeStatusBarEntry: BinarySizeStatusBarEntry,
8374
private readonly zoomStatusBarEntry: ZoomStatusBarEntry,
8475
) {
85-
super();
86-
const resourceRoot = resource.with({
87-
path: resource.path.replace(/\/[^\/]+?\.\w+$/, '/'),
88-
});
89-
90-
webviewEditor.webview.options = {
91-
enableScripts: true,
92-
enableForms: false,
93-
localResourceRoots: [
94-
resourceRoot,
95-
extensionRoot,
96-
]
97-
};
76+
super(extensionRoot, resource, webviewEditor, binarySizeStatusBarEntry);
9877

9978
this._register(webviewEditor.webview.onDidReceiveMessage(message => {
10079
switch (message.type) {
101-
case 'size':
102-
{
103-
this._imageSize = message.value;
104-
this.update();
105-
break;
106-
}
107-
case 'zoom':
108-
{
109-
this._imageZoom = message.value;
110-
this.update();
111-
break;
112-
}
113-
114-
case 'reopen-as-text':
115-
{
116-
vscode.commands.executeCommand('vscode.openWith', resource, 'default', webviewEditor.viewColumn);
117-
break;
118-
}
80+
case 'size': {
81+
this._imageSize = message.value;
82+
this.updateState();
83+
break;
84+
}
85+
case 'zoom': {
86+
this._imageZoom = message.value;
87+
this.updateState();
88+
break;
89+
}
90+
case 'reopen-as-text': {
91+
reopenAsText(resource, webviewEditor.viewColumn);
92+
break;
93+
}
11994
}
12095
}));
12196

12297
this._register(zoomStatusBarEntry.onDidChangeScale(e => {
123-
if (this._previewState === PreviewState.Active) {
98+
if (this.previewState === PreviewState.Active) {
12499
this.webviewEditor.webview.postMessage({ type: 'setScale', scale: e.scale });
125100
}
126101
}));
127102

128103
this._register(webviewEditor.onDidChangeViewState(() => {
129-
this.update();
130104
this.webviewEditor.webview.postMessage({ type: 'setActive', value: this.webviewEditor.active });
131105
}));
132106

133107
this._register(webviewEditor.onDidDispose(() => {
134-
if (this._previewState === PreviewState.Active) {
135-
this.sizeStatusBarEntry.hide(this.id);
136-
this.binarySizeStatusBarEntry.hide(this.id);
137-
this.zoomStatusBarEntry.hide(this.id);
138-
}
139-
this._previewState = PreviewState.Disposed;
140-
}));
141-
142-
const watcher = this._register(vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(resource, '*')));
143-
this._register(watcher.onDidChange(e => {
144-
if (e.toString() === this.resource.toString()) {
145-
this.render();
146-
}
147-
}));
148-
this._register(watcher.onDidDelete(e => {
149-
if (e.toString() === this.resource.toString()) {
150-
this.webviewEditor.dispose();
108+
if (this.previewState === PreviewState.Active) {
109+
this.sizeStatusBarEntry.hide(this);
110+
this.zoomStatusBarEntry.hide(this);
151111
}
112+
this.previewState = PreviewState.Disposed;
152113
}));
153114

154-
vscode.workspace.fs.stat(resource).then(({ size }) => {
155-
this._imageBinarySize = size;
156-
this.update();
157-
});
158-
115+
this.updateBinarySize();
159116
this.render();
160-
this.update();
117+
this.updateState();
118+
161119
this.webviewEditor.webview.postMessage({ type: 'setActive', value: this.webviewEditor.active });
162120
}
163121

164122
public zoomIn() {
165-
if (this._previewState === PreviewState.Active) {
123+
if (this.previewState === PreviewState.Active) {
166124
this.webviewEditor.webview.postMessage({ type: 'zoomIn' });
167125
}
168126
}
169127

170128
public zoomOut() {
171-
if (this._previewState === PreviewState.Active) {
129+
if (this.previewState === PreviewState.Active) {
172130
this.webviewEditor.webview.postMessage({ type: 'zoomOut' });
173131
}
174132
}
175133

176-
private async render() {
177-
if (this._previewState !== PreviewState.Disposed) {
178-
this.webviewEditor.webview.html = await this.getWebviewContents();
179-
}
180-
}
134+
protected override updateState() {
135+
super.updateState();
181136

182-
private update() {
183-
if (this._previewState === PreviewState.Disposed) {
137+
if (this.previewState === PreviewState.Disposed) {
184138
return;
185139
}
186140

187141
if (this.webviewEditor.active) {
188-
this._previewState = PreviewState.Active;
189-
this.sizeStatusBarEntry.show(this.id, this._imageSize || '');
190-
this.binarySizeStatusBarEntry.show(this.id, this._imageBinarySize);
191-
this.zoomStatusBarEntry.show(this.id, this._imageZoom || 'fit');
142+
this.sizeStatusBarEntry.show(this, this._imageSize || '');
143+
this.zoomStatusBarEntry.show(this, this._imageZoom || 'fit');
192144
} else {
193-
if (this._previewState === PreviewState.Active) {
194-
this.sizeStatusBarEntry.hide(this.id);
195-
this.binarySizeStatusBarEntry.hide(this.id);
196-
this.zoomStatusBarEntry.hide(this.id);
197-
}
198-
this._previewState = PreviewState.Visible;
145+
this.sizeStatusBarEntry.hide(this);
146+
this.zoomStatusBarEntry.hide(this);
199147
}
200148
}
201149

202-
private async getWebviewContents(): Promise<string> {
150+
protected override async getWebviewContents(): Promise<string> {
203151
const version = Date.now().toString();
204152
const settings = {
205153
src: await this.getResourcePath(this.webviewEditor, this.resource, version),

extensions/image-preview/src/imagePreview/sizeStatusBarEntry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export class SizeStatusBarEntry extends PreviewStatusBarEntry {
1515
super('status.imagePreview.size', localize('sizeStatusBar.name', "Image Size"), vscode.StatusBarAlignment.Right, 101 /* to the left of editor status (100) */);
1616
}
1717

18-
public show(owner: string, text: string) {
18+
public show(owner: unknown, text: string) {
1919
this.showItem(owner, text);
2020
}
2121
}

extensions/image-preview/src/imagePreview/zoomStatusBarEntry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export class ZoomStatusBarEntry extends OwnedStatusBarEntry {
4141
this.entry.command = selectZoomLevelCommandId;
4242
}
4343

44-
public show(owner: string, scale: Scale) {
44+
public show(owner: unknown, scale: Scale) {
4545
this.showItem(owner, this.zoomLabel(scale));
4646
}
4747

0 commit comments

Comments
 (0)