@theia/preview 1.45.1 → 1.46.0-next.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -45
- package/lib/browser/index.d.ts +3 -3
- package/lib/browser/index.js +29 -29
- package/lib/browser/markdown/index.d.ts +1 -1
- package/lib/browser/markdown/index.js +28 -28
- package/lib/browser/markdown/markdown-preview-handler.d.ts +28 -28
- package/lib/browser/markdown/markdown-preview-handler.js +301 -301
- package/lib/browser/markdown/markdown-preview-handler.spec.d.ts +1 -1
- package/lib/browser/markdown/markdown-preview-handler.spec.js +193 -193
- package/lib/browser/preview-contribution.d.ts +50 -50
- package/lib/browser/preview-contribution.js +262 -262
- package/lib/browser/preview-frontend-module.d.ts +5 -5
- package/lib/browser/preview-frontend-module.js +52 -52
- package/lib/browser/preview-handler.d.ts +104 -104
- package/lib/browser/preview-handler.js +76 -76
- package/lib/browser/preview-link-normalizer.d.ts +7 -7
- package/lib/browser/preview-link-normalizer.js +54 -54
- package/lib/browser/preview-preferences.d.ts +11 -11
- package/lib/browser/preview-preferences.js +46 -46
- package/lib/browser/preview-uri.d.ts +8 -8
- package/lib/browser/preview-uri.js +47 -47
- package/lib/browser/preview-widget.d.ts +54 -54
- package/lib/browser/preview-widget.js +261 -261
- package/lib/package.spec.js +25 -25
- package/package.json +7 -7
- package/src/browser/index.ts +19 -19
- package/src/browser/markdown/index.ts +17 -17
- package/src/browser/markdown/markdown-preview-handler.spec.ts +228 -228
- package/src/browser/markdown/markdown-preview-handler.ts +309 -309
- package/src/browser/markdown/style/index.css +18 -18
- package/src/browser/markdown/style/markdown.css +203 -203
- package/src/browser/markdown/style/tomorrow.css +105 -105
- package/src/browser/preview-contribution.ts +276 -276
- package/src/browser/preview-frontend-module.ts +57 -57
- package/src/browser/preview-handler.ts +141 -141
- package/src/browser/preview-link-normalizer.ts +40 -40
- package/src/browser/preview-preferences.ts +58 -58
- package/src/browser/preview-uri.ts +43 -43
- package/src/browser/preview-widget.ts +277 -277
- package/src/browser/style/index.css +17 -17
- package/src/browser/style/preview-widget.css +29 -29
- package/src/package.spec.ts +29 -29
|
@@ -1,276 +1,276 @@
|
|
|
1
|
-
// *****************************************************************************
|
|
2
|
-
// Copyright (C) 2018 TypeFox and others.
|
|
3
|
-
//
|
|
4
|
-
// This program and the accompanying materials are made available under the
|
|
5
|
-
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
-
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
-
//
|
|
8
|
-
// This Source Code may also be made available under the following Secondary
|
|
9
|
-
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
-
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
-
// with the GNU Classpath Exception which is available at
|
|
12
|
-
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
-
//
|
|
14
|
-
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
-
// *****************************************************************************
|
|
16
|
-
|
|
17
|
-
import { injectable, inject } from '@theia/core/shared/inversify';
|
|
18
|
-
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
|
19
|
-
import { FrontendApplicationContribution, WidgetOpenerOptions, NavigatableWidgetOpenHandler, codicon } from '@theia/core/lib/browser';
|
|
20
|
-
import { EditorManager, TextEditor, EditorWidget, EditorContextMenu } from '@theia/editor/lib/browser';
|
|
21
|
-
import { DisposableCollection, CommandContribution, CommandRegistry, Command, MenuContribution, MenuModelRegistry, Disposable } from '@theia/core/lib/common';
|
|
22
|
-
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
|
23
|
-
import { MiniBrowserCommands } from '@theia/mini-browser/lib/browser/mini-browser-open-handler';
|
|
24
|
-
import URI from '@theia/core/lib/common/uri';
|
|
25
|
-
import { Position } from '@theia/core/shared/vscode-languageserver-protocol';
|
|
26
|
-
import { PreviewWidget } from './preview-widget';
|
|
27
|
-
import { PreviewHandlerProvider } from './preview-handler';
|
|
28
|
-
import { PreviewUri } from './preview-uri';
|
|
29
|
-
import { PreviewPreferences } from './preview-preferences';
|
|
30
|
-
import { nls } from '@theia/core/lib/common/nls';
|
|
31
|
-
|
|
32
|
-
import debounce = require('@theia/core/shared/lodash.debounce');
|
|
33
|
-
|
|
34
|
-
export namespace PreviewCommands {
|
|
35
|
-
/**
|
|
36
|
-
* No `label`. Otherwise, it would show up in the `Command Palette` and we already have the `Preview` open handler.
|
|
37
|
-
* See in (`WorkspaceCommandContribution`)[https://bit.ly/2DncrSD].
|
|
38
|
-
*/
|
|
39
|
-
export const OPEN = Command.toLocalizedCommand({
|
|
40
|
-
id: 'preview:open',
|
|
41
|
-
label: 'Open Preview',
|
|
42
|
-
iconClass: codicon('open-preview')
|
|
43
|
-
}, 'vscode.markdown-language-features/package/markdown.preview.title');
|
|
44
|
-
export const OPEN_SOURCE: Command = {
|
|
45
|
-
id: 'preview.open.source',
|
|
46
|
-
iconClass: codicon('go-to-file')
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface PreviewOpenerOptions extends WidgetOpenerOptions {
|
|
51
|
-
originUri?: URI;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
@injectable()
|
|
55
|
-
// eslint-disable-next-line max-len
|
|
56
|
-
export class PreviewContribution extends NavigatableWidgetOpenHandler<PreviewWidget> implements CommandContribution, MenuContribution, FrontendApplicationContribution, TabBarToolbarContribution {
|
|
57
|
-
|
|
58
|
-
readonly id = PreviewUri.id;
|
|
59
|
-
readonly label = nls.localize(MiniBrowserCommands.PREVIEW_CATEGORY_KEY, MiniBrowserCommands.PREVIEW_CATEGORY);
|
|
60
|
-
|
|
61
|
-
@inject(EditorManager)
|
|
62
|
-
protected readonly editorManager: EditorManager;
|
|
63
|
-
|
|
64
|
-
@inject(PreviewHandlerProvider)
|
|
65
|
-
protected readonly previewHandlerProvider: PreviewHandlerProvider;
|
|
66
|
-
|
|
67
|
-
@inject(PreviewPreferences)
|
|
68
|
-
protected readonly preferences: PreviewPreferences;
|
|
69
|
-
|
|
70
|
-
protected readonly synchronizedUris = new Set<string>();
|
|
71
|
-
|
|
72
|
-
protected scrollSyncLockOn: 'preview' | 'editor' | undefined = undefined;
|
|
73
|
-
|
|
74
|
-
protected scrollSyncLockTimeout: number | undefined;
|
|
75
|
-
|
|
76
|
-
onStart(): void {
|
|
77
|
-
this.onCreated(previewWidget => {
|
|
78
|
-
this.registerOpenOnDoubleClick(previewWidget);
|
|
79
|
-
this.registerEditorAndPreviewSync(previewWidget);
|
|
80
|
-
});
|
|
81
|
-
this.editorManager.onCreated(editorWidget => {
|
|
82
|
-
this.registerEditorAndPreviewSync(editorWidget);
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
protected async lockScrollSync(on: 'preview' | 'editor', delay: number = 50): Promise<void> {
|
|
87
|
-
this.scrollSyncLockOn = on;
|
|
88
|
-
if (this.scrollSyncLockTimeout) {
|
|
89
|
-
window.clearTimeout(this.scrollSyncLockTimeout);
|
|
90
|
-
}
|
|
91
|
-
this.scrollSyncLockTimeout = window.setTimeout(() => {
|
|
92
|
-
this.scrollSyncLockOn = undefined;
|
|
93
|
-
}, delay);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
protected async registerEditorAndPreviewSync(source: PreviewWidget | EditorWidget): Promise<void> {
|
|
97
|
-
let uri: string;
|
|
98
|
-
let editorWidget: EditorWidget | undefined;
|
|
99
|
-
let previewWidget: PreviewWidget | undefined;
|
|
100
|
-
if (source instanceof EditorWidget) {
|
|
101
|
-
editorWidget = source;
|
|
102
|
-
uri = editorWidget.editor.uri.toString();
|
|
103
|
-
previewWidget = await this.getWidget(editorWidget.editor.uri);
|
|
104
|
-
} else {
|
|
105
|
-
previewWidget = source;
|
|
106
|
-
uri = previewWidget.getUri().toString();
|
|
107
|
-
editorWidget = await this.editorManager.getByUri(previewWidget.getUri());
|
|
108
|
-
}
|
|
109
|
-
if (!previewWidget || !editorWidget || !uri) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
if (this.synchronizedUris.has(uri)) {
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
const syncDisposables = new DisposableCollection();
|
|
116
|
-
previewWidget.disposed.connect(() => syncDisposables.dispose());
|
|
117
|
-
editorWidget.disposed.connect(() => syncDisposables.dispose());
|
|
118
|
-
|
|
119
|
-
const editor = editorWidget.editor;
|
|
120
|
-
syncDisposables.push(editor.onScrollChanged(debounce(() => {
|
|
121
|
-
if (this.scrollSyncLockOn === 'editor') {
|
|
122
|
-
// avoid recursive scroll synchronization
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
this.lockScrollSync('preview');
|
|
126
|
-
const range = editor.getVisibleRanges();
|
|
127
|
-
if (range.length > 0) {
|
|
128
|
-
this.revealSourceLineInPreview(previewWidget!, range[0].start);
|
|
129
|
-
}
|
|
130
|
-
}), 100));
|
|
131
|
-
syncDisposables.push(this.synchronizeScrollToEditor(previewWidget, editor));
|
|
132
|
-
|
|
133
|
-
this.synchronizedUris.add(uri);
|
|
134
|
-
syncDisposables.push(Disposable.create(() => this.synchronizedUris.delete(uri)));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
protected revealSourceLineInPreview(previewWidget: PreviewWidget, position: Position): void {
|
|
138
|
-
previewWidget.revealForSourceLine(position.line);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
protected synchronizeScrollToEditor(previewWidget: PreviewWidget, editor: TextEditor): Disposable {
|
|
142
|
-
return previewWidget.onDidScroll(sourceLine => {
|
|
143
|
-
if (this.scrollSyncLockOn === 'preview') {
|
|
144
|
-
// avoid recursive scroll synchronization
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
const line = Math.floor(sourceLine);
|
|
148
|
-
this.lockScrollSync('editor'); // avoid recursive scroll synchronization
|
|
149
|
-
editor.revealRange({
|
|
150
|
-
start: {
|
|
151
|
-
line,
|
|
152
|
-
character: 0
|
|
153
|
-
},
|
|
154
|
-
end: {
|
|
155
|
-
line: line + 1,
|
|
156
|
-
character: 0
|
|
157
|
-
}
|
|
158
|
-
}, { at: 'top' });
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
protected registerOpenOnDoubleClick(ref: PreviewWidget): void {
|
|
163
|
-
const disposable = ref.onDidDoubleClick(async location => {
|
|
164
|
-
const { editor } = await this.openSource(ref);
|
|
165
|
-
editor.revealPosition(location.range.start);
|
|
166
|
-
editor.selection = location.range;
|
|
167
|
-
ref.revealForSourceLine(location.range.start.line);
|
|
168
|
-
});
|
|
169
|
-
ref.disposed.connect(() => disposable.dispose());
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
canHandle(uri: URI): number {
|
|
173
|
-
if (!this.previewHandlerProvider.canHandle(uri)) {
|
|
174
|
-
return 0;
|
|
175
|
-
}
|
|
176
|
-
const editorPriority = this.editorManager.canHandle(uri);
|
|
177
|
-
if (editorPriority === 0) {
|
|
178
|
-
return 200;
|
|
179
|
-
}
|
|
180
|
-
if (PreviewUri.match(uri)) {
|
|
181
|
-
return editorPriority * 2;
|
|
182
|
-
}
|
|
183
|
-
return editorPriority * (this.openByDefault ? 2 : 0.5);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
protected get openByDefault(): boolean {
|
|
187
|
-
return this.preferences['preview.openByDefault'];
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
override async open(uri: URI, options?: PreviewOpenerOptions): Promise<PreviewWidget> {
|
|
191
|
-
const resolvedOptions = await this.resolveOpenerOptions(options);
|
|
192
|
-
return super.open(uri, resolvedOptions);
|
|
193
|
-
}
|
|
194
|
-
protected override serializeUri(uri: URI): string {
|
|
195
|
-
return super.serializeUri(PreviewUri.decode(uri));
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
protected async resolveOpenerOptions(options?: PreviewOpenerOptions): Promise<PreviewOpenerOptions> {
|
|
199
|
-
const resolved: PreviewOpenerOptions = { mode: 'activate', ...options };
|
|
200
|
-
if (resolved.originUri) {
|
|
201
|
-
const ref = await this.getWidget(resolved.originUri);
|
|
202
|
-
if (ref) {
|
|
203
|
-
resolved.widgetOptions = { ...resolved.widgetOptions, ref };
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
return resolved;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
registerCommands(registry: CommandRegistry): void {
|
|
210
|
-
registry.registerCommand(PreviewCommands.OPEN, {
|
|
211
|
-
execute: widget => this.openForEditor(widget),
|
|
212
|
-
isEnabled: widget => this.canHandleEditorUri(widget),
|
|
213
|
-
isVisible: widget => this.canHandleEditorUri(widget)
|
|
214
|
-
});
|
|
215
|
-
registry.registerCommand(PreviewCommands.OPEN_SOURCE, {
|
|
216
|
-
execute: widget => this.openSource(widget),
|
|
217
|
-
isEnabled: widget => widget instanceof PreviewWidget,
|
|
218
|
-
isVisible: widget => widget instanceof PreviewWidget
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
registerMenus(menus: MenuModelRegistry): void {
|
|
223
|
-
menus.registerMenuAction(EditorContextMenu.NAVIGATION, {
|
|
224
|
-
commandId: PreviewCommands.OPEN.id
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
|
229
|
-
registry.registerItem({
|
|
230
|
-
id: PreviewCommands.OPEN.id,
|
|
231
|
-
command: PreviewCommands.OPEN.id,
|
|
232
|
-
tooltip: nls.localize('vscode.markdown-language-features/package/markdown.previewSide.title', 'Open Preview to the Side')
|
|
233
|
-
});
|
|
234
|
-
registry.registerItem({
|
|
235
|
-
id: PreviewCommands.OPEN_SOURCE.id,
|
|
236
|
-
command: PreviewCommands.OPEN_SOURCE.id,
|
|
237
|
-
tooltip: nls.localize('vscode.markdown-language-features/package/markdown.showSource.title', 'Open Source')
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
protected canHandleEditorUri(widget?: Widget): boolean {
|
|
242
|
-
const uri = this.getCurrentEditorUri(widget);
|
|
243
|
-
return !!uri && this.previewHandlerProvider.canHandle(uri);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
protected getCurrentEditorUri(widget?: Widget): URI | undefined {
|
|
247
|
-
const current = this.getCurrentEditor(widget);
|
|
248
|
-
return current && current.editor.uri;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
protected getCurrentEditor(widget?: Widget): EditorWidget | undefined {
|
|
252
|
-
const current = widget ? widget : this.editorManager.currentEditor;
|
|
253
|
-
return current instanceof EditorWidget && current || undefined;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
protected async openForEditor(widget?: Widget): Promise<void> {
|
|
257
|
-
const ref = this.getCurrentEditor(widget);
|
|
258
|
-
if (!ref) {
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
await this.open(ref.editor.uri, {
|
|
262
|
-
mode: 'reveal',
|
|
263
|
-
widgetOptions: { ref, mode: 'open-to-right' }
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
protected async openSource(ref: PreviewWidget): Promise<EditorWidget>;
|
|
268
|
-
protected async openSource(ref?: Widget): Promise<EditorWidget | undefined> {
|
|
269
|
-
if (ref instanceof PreviewWidget) {
|
|
270
|
-
return this.editorManager.open(ref.uri, {
|
|
271
|
-
widgetOptions: { ref, mode: 'tab-after' }
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
}
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2018 TypeFox and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { injectable, inject } from '@theia/core/shared/inversify';
|
|
18
|
+
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
|
19
|
+
import { FrontendApplicationContribution, WidgetOpenerOptions, NavigatableWidgetOpenHandler, codicon } from '@theia/core/lib/browser';
|
|
20
|
+
import { EditorManager, TextEditor, EditorWidget, EditorContextMenu } from '@theia/editor/lib/browser';
|
|
21
|
+
import { DisposableCollection, CommandContribution, CommandRegistry, Command, MenuContribution, MenuModelRegistry, Disposable } from '@theia/core/lib/common';
|
|
22
|
+
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
|
23
|
+
import { MiniBrowserCommands } from '@theia/mini-browser/lib/browser/mini-browser-open-handler';
|
|
24
|
+
import URI from '@theia/core/lib/common/uri';
|
|
25
|
+
import { Position } from '@theia/core/shared/vscode-languageserver-protocol';
|
|
26
|
+
import { PreviewWidget } from './preview-widget';
|
|
27
|
+
import { PreviewHandlerProvider } from './preview-handler';
|
|
28
|
+
import { PreviewUri } from './preview-uri';
|
|
29
|
+
import { PreviewPreferences } from './preview-preferences';
|
|
30
|
+
import { nls } from '@theia/core/lib/common/nls';
|
|
31
|
+
|
|
32
|
+
import debounce = require('@theia/core/shared/lodash.debounce');
|
|
33
|
+
|
|
34
|
+
export namespace PreviewCommands {
|
|
35
|
+
/**
|
|
36
|
+
* No `label`. Otherwise, it would show up in the `Command Palette` and we already have the `Preview` open handler.
|
|
37
|
+
* See in (`WorkspaceCommandContribution`)[https://bit.ly/2DncrSD].
|
|
38
|
+
*/
|
|
39
|
+
export const OPEN = Command.toLocalizedCommand({
|
|
40
|
+
id: 'preview:open',
|
|
41
|
+
label: 'Open Preview',
|
|
42
|
+
iconClass: codicon('open-preview')
|
|
43
|
+
}, 'vscode.markdown-language-features/package/markdown.preview.title');
|
|
44
|
+
export const OPEN_SOURCE: Command = {
|
|
45
|
+
id: 'preview.open.source',
|
|
46
|
+
iconClass: codicon('go-to-file')
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface PreviewOpenerOptions extends WidgetOpenerOptions {
|
|
51
|
+
originUri?: URI;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@injectable()
|
|
55
|
+
// eslint-disable-next-line max-len
|
|
56
|
+
export class PreviewContribution extends NavigatableWidgetOpenHandler<PreviewWidget> implements CommandContribution, MenuContribution, FrontendApplicationContribution, TabBarToolbarContribution {
|
|
57
|
+
|
|
58
|
+
readonly id = PreviewUri.id;
|
|
59
|
+
readonly label = nls.localize(MiniBrowserCommands.PREVIEW_CATEGORY_KEY, MiniBrowserCommands.PREVIEW_CATEGORY);
|
|
60
|
+
|
|
61
|
+
@inject(EditorManager)
|
|
62
|
+
protected readonly editorManager: EditorManager;
|
|
63
|
+
|
|
64
|
+
@inject(PreviewHandlerProvider)
|
|
65
|
+
protected readonly previewHandlerProvider: PreviewHandlerProvider;
|
|
66
|
+
|
|
67
|
+
@inject(PreviewPreferences)
|
|
68
|
+
protected readonly preferences: PreviewPreferences;
|
|
69
|
+
|
|
70
|
+
protected readonly synchronizedUris = new Set<string>();
|
|
71
|
+
|
|
72
|
+
protected scrollSyncLockOn: 'preview' | 'editor' | undefined = undefined;
|
|
73
|
+
|
|
74
|
+
protected scrollSyncLockTimeout: number | undefined;
|
|
75
|
+
|
|
76
|
+
onStart(): void {
|
|
77
|
+
this.onCreated(previewWidget => {
|
|
78
|
+
this.registerOpenOnDoubleClick(previewWidget);
|
|
79
|
+
this.registerEditorAndPreviewSync(previewWidget);
|
|
80
|
+
});
|
|
81
|
+
this.editorManager.onCreated(editorWidget => {
|
|
82
|
+
this.registerEditorAndPreviewSync(editorWidget);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
protected async lockScrollSync(on: 'preview' | 'editor', delay: number = 50): Promise<void> {
|
|
87
|
+
this.scrollSyncLockOn = on;
|
|
88
|
+
if (this.scrollSyncLockTimeout) {
|
|
89
|
+
window.clearTimeout(this.scrollSyncLockTimeout);
|
|
90
|
+
}
|
|
91
|
+
this.scrollSyncLockTimeout = window.setTimeout(() => {
|
|
92
|
+
this.scrollSyncLockOn = undefined;
|
|
93
|
+
}, delay);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
protected async registerEditorAndPreviewSync(source: PreviewWidget | EditorWidget): Promise<void> {
|
|
97
|
+
let uri: string;
|
|
98
|
+
let editorWidget: EditorWidget | undefined;
|
|
99
|
+
let previewWidget: PreviewWidget | undefined;
|
|
100
|
+
if (source instanceof EditorWidget) {
|
|
101
|
+
editorWidget = source;
|
|
102
|
+
uri = editorWidget.editor.uri.toString();
|
|
103
|
+
previewWidget = await this.getWidget(editorWidget.editor.uri);
|
|
104
|
+
} else {
|
|
105
|
+
previewWidget = source;
|
|
106
|
+
uri = previewWidget.getUri().toString();
|
|
107
|
+
editorWidget = await this.editorManager.getByUri(previewWidget.getUri());
|
|
108
|
+
}
|
|
109
|
+
if (!previewWidget || !editorWidget || !uri) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (this.synchronizedUris.has(uri)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const syncDisposables = new DisposableCollection();
|
|
116
|
+
previewWidget.disposed.connect(() => syncDisposables.dispose());
|
|
117
|
+
editorWidget.disposed.connect(() => syncDisposables.dispose());
|
|
118
|
+
|
|
119
|
+
const editor = editorWidget.editor;
|
|
120
|
+
syncDisposables.push(editor.onScrollChanged(debounce(() => {
|
|
121
|
+
if (this.scrollSyncLockOn === 'editor') {
|
|
122
|
+
// avoid recursive scroll synchronization
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
this.lockScrollSync('preview');
|
|
126
|
+
const range = editor.getVisibleRanges();
|
|
127
|
+
if (range.length > 0) {
|
|
128
|
+
this.revealSourceLineInPreview(previewWidget!, range[0].start);
|
|
129
|
+
}
|
|
130
|
+
}), 100));
|
|
131
|
+
syncDisposables.push(this.synchronizeScrollToEditor(previewWidget, editor));
|
|
132
|
+
|
|
133
|
+
this.synchronizedUris.add(uri);
|
|
134
|
+
syncDisposables.push(Disposable.create(() => this.synchronizedUris.delete(uri)));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
protected revealSourceLineInPreview(previewWidget: PreviewWidget, position: Position): void {
|
|
138
|
+
previewWidget.revealForSourceLine(position.line);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
protected synchronizeScrollToEditor(previewWidget: PreviewWidget, editor: TextEditor): Disposable {
|
|
142
|
+
return previewWidget.onDidScroll(sourceLine => {
|
|
143
|
+
if (this.scrollSyncLockOn === 'preview') {
|
|
144
|
+
// avoid recursive scroll synchronization
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const line = Math.floor(sourceLine);
|
|
148
|
+
this.lockScrollSync('editor'); // avoid recursive scroll synchronization
|
|
149
|
+
editor.revealRange({
|
|
150
|
+
start: {
|
|
151
|
+
line,
|
|
152
|
+
character: 0
|
|
153
|
+
},
|
|
154
|
+
end: {
|
|
155
|
+
line: line + 1,
|
|
156
|
+
character: 0
|
|
157
|
+
}
|
|
158
|
+
}, { at: 'top' });
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
protected registerOpenOnDoubleClick(ref: PreviewWidget): void {
|
|
163
|
+
const disposable = ref.onDidDoubleClick(async location => {
|
|
164
|
+
const { editor } = await this.openSource(ref);
|
|
165
|
+
editor.revealPosition(location.range.start);
|
|
166
|
+
editor.selection = location.range;
|
|
167
|
+
ref.revealForSourceLine(location.range.start.line);
|
|
168
|
+
});
|
|
169
|
+
ref.disposed.connect(() => disposable.dispose());
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
canHandle(uri: URI): number {
|
|
173
|
+
if (!this.previewHandlerProvider.canHandle(uri)) {
|
|
174
|
+
return 0;
|
|
175
|
+
}
|
|
176
|
+
const editorPriority = this.editorManager.canHandle(uri);
|
|
177
|
+
if (editorPriority === 0) {
|
|
178
|
+
return 200;
|
|
179
|
+
}
|
|
180
|
+
if (PreviewUri.match(uri)) {
|
|
181
|
+
return editorPriority * 2;
|
|
182
|
+
}
|
|
183
|
+
return editorPriority * (this.openByDefault ? 2 : 0.5);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
protected get openByDefault(): boolean {
|
|
187
|
+
return this.preferences['preview.openByDefault'];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
override async open(uri: URI, options?: PreviewOpenerOptions): Promise<PreviewWidget> {
|
|
191
|
+
const resolvedOptions = await this.resolveOpenerOptions(options);
|
|
192
|
+
return super.open(uri, resolvedOptions);
|
|
193
|
+
}
|
|
194
|
+
protected override serializeUri(uri: URI): string {
|
|
195
|
+
return super.serializeUri(PreviewUri.decode(uri));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
protected async resolveOpenerOptions(options?: PreviewOpenerOptions): Promise<PreviewOpenerOptions> {
|
|
199
|
+
const resolved: PreviewOpenerOptions = { mode: 'activate', ...options };
|
|
200
|
+
if (resolved.originUri) {
|
|
201
|
+
const ref = await this.getWidget(resolved.originUri);
|
|
202
|
+
if (ref) {
|
|
203
|
+
resolved.widgetOptions = { ...resolved.widgetOptions, ref };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return resolved;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
registerCommands(registry: CommandRegistry): void {
|
|
210
|
+
registry.registerCommand(PreviewCommands.OPEN, {
|
|
211
|
+
execute: widget => this.openForEditor(widget),
|
|
212
|
+
isEnabled: widget => this.canHandleEditorUri(widget),
|
|
213
|
+
isVisible: widget => this.canHandleEditorUri(widget)
|
|
214
|
+
});
|
|
215
|
+
registry.registerCommand(PreviewCommands.OPEN_SOURCE, {
|
|
216
|
+
execute: widget => this.openSource(widget),
|
|
217
|
+
isEnabled: widget => widget instanceof PreviewWidget,
|
|
218
|
+
isVisible: widget => widget instanceof PreviewWidget
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
registerMenus(menus: MenuModelRegistry): void {
|
|
223
|
+
menus.registerMenuAction(EditorContextMenu.NAVIGATION, {
|
|
224
|
+
commandId: PreviewCommands.OPEN.id
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
|
229
|
+
registry.registerItem({
|
|
230
|
+
id: PreviewCommands.OPEN.id,
|
|
231
|
+
command: PreviewCommands.OPEN.id,
|
|
232
|
+
tooltip: nls.localize('vscode.markdown-language-features/package/markdown.previewSide.title', 'Open Preview to the Side')
|
|
233
|
+
});
|
|
234
|
+
registry.registerItem({
|
|
235
|
+
id: PreviewCommands.OPEN_SOURCE.id,
|
|
236
|
+
command: PreviewCommands.OPEN_SOURCE.id,
|
|
237
|
+
tooltip: nls.localize('vscode.markdown-language-features/package/markdown.showSource.title', 'Open Source')
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
protected canHandleEditorUri(widget?: Widget): boolean {
|
|
242
|
+
const uri = this.getCurrentEditorUri(widget);
|
|
243
|
+
return !!uri && this.previewHandlerProvider.canHandle(uri);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
protected getCurrentEditorUri(widget?: Widget): URI | undefined {
|
|
247
|
+
const current = this.getCurrentEditor(widget);
|
|
248
|
+
return current && current.editor.uri;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
protected getCurrentEditor(widget?: Widget): EditorWidget | undefined {
|
|
252
|
+
const current = widget ? widget : this.editorManager.currentEditor;
|
|
253
|
+
return current instanceof EditorWidget && current || undefined;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
protected async openForEditor(widget?: Widget): Promise<void> {
|
|
257
|
+
const ref = this.getCurrentEditor(widget);
|
|
258
|
+
if (!ref) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
await this.open(ref.editor.uri, {
|
|
262
|
+
mode: 'reveal',
|
|
263
|
+
widgetOptions: { ref, mode: 'open-to-right' }
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
protected async openSource(ref: PreviewWidget): Promise<EditorWidget>;
|
|
268
|
+
protected async openSource(ref?: Widget): Promise<EditorWidget | undefined> {
|
|
269
|
+
if (ref instanceof PreviewWidget) {
|
|
270
|
+
return this.editorManager.open(ref.uri, {
|
|
271
|
+
widgetOptions: { ref, mode: 'tab-after' }
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
}
|