walkThroughPart.ts 23.2 KB
Newer Older
C
Christof Marti 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

import 'vs/css!./walkThroughPart';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import * as strings from 'vs/base/common/strings';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { $, Dimension, Builder } from 'vs/base/browser/builder';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { EditorOptions } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
19
import { WalkThroughInput } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughInput';
C
Christof Marti 已提交
20 21 22 23 24 25 26 27 28
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { marked } from 'vs/base/common/marked/marked';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IFileService } from 'vs/platform/files/common/files';
import { IModelService } from 'vs/editor/common/services/modelService';
import { CodeEditor } from 'vs/editor/browser/codeEditor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { localize } from 'vs/nls';
29 30
import { IStorageService } from 'vs/platform/storage/common/storage';
import { Scope } from 'vs/workbench/common/memento';
31 32
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
33
import { once } from 'vs/base/common/event';
34
import { isObject } from 'vs/base/common/types';
35
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
36
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
C
Christof Marti 已提交
37
import { IPartService } from 'vs/workbench/services/part/common/partService';
38
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
39
import { IMessageService, Severity } from 'vs/platform/message/common/message';
C
Christof Marti 已提交
40
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
41
import { registerColor, focusBorder, textLinkForeground, textLinkActiveForeground, textPreformatForeground, contrastBorder, textBlockQuoteBackground, textBlockQuoteBorder } from 'vs/platform/theme/common/colorRegistry';
42
import { getExtraColor } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughUtils';
43 44
import { UILabelProvider } from 'vs/platform/keybinding/common/keybindingLabels';
import { OS, OperatingSystem } from 'vs/base/common/platform';
45 46

export const WALK_THROUGH_FOCUS = new RawContextKey<boolean>('interactivePlaygroundFocus', false);
C
Christof Marti 已提交
47 48

const UNBOUND_COMMAND = localize('walkThrough.unboundCommand', "unbound");
49 50 51 52 53 54 55
const WALK_THROUGH_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'walkThroughEditorViewState';

interface IViewState {
	scrollTop: number;
	scrollLeft: number;
}

A
Alex Dima 已提交
56
interface IWalkThroughEditorViewState {
57 58 59 60 61 62 63 64
	viewState: IViewState;
}

interface IWalkThroughEditorViewStates {
	0?: IWalkThroughEditorViewState;
	1?: IWalkThroughEditorViewState;
	2?: IWalkThroughEditorViewState;
}
C
Christof Marti 已提交
65

66 67 68 69 70 71 72 73 74
class WalkThroughCodeEditor extends CodeEditor {

	constructor(
		domElement: HTMLElement,
		options: IEditorOptions,
		private telemetryData: Object,
		@IInstantiationService instantiationService: IInstantiationService,
		@ICodeEditorService codeEditorService: ICodeEditorService,
		@ICommandService commandService: ICommandService,
75 76
		@IContextKeyService contextKeyService: IContextKeyService,
		@IThemeService themeService: IThemeService
77
	) {
78
		super(domElement, options, instantiationService, codeEditorService, commandService, contextKeyService, themeService);
79 80 81 82 83 84 85
	}

	getTelemetryData() {
		return this.telemetryData;
	}
}

C
Christof Marti 已提交
86 87 88 89 90 91 92 93
export class WalkThroughPart extends BaseEditor {

	static ID: string = 'workbench.editor.walkThroughPart';

	private disposables: IDisposable[] = [];
	private contentDisposables: IDisposable[] = [];
	private content: HTMLDivElement;
	private scrollbar: DomScrollableElement;
94
	private editorFocus: IContextKey<boolean>;
95
	private size: Dimension;
C
Christof Marti 已提交
96 97 98 99

	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
		@IInstantiationService private instantiationService: IInstantiationService,
100
		@IThemeService protected themeService: IThemeService,
C
Christof Marti 已提交
101 102 103 104
		@IOpenerService private openerService: IOpenerService,
		@IFileService private fileService: IFileService,
		@IModelService protected modelService: IModelService,
		@IKeybindingService private keybindingService: IKeybindingService,
105
		@IStorageService private storageService: IStorageService,
106 107
		@IContextKeyService private contextKeyService: IContextKeyService,
		@IConfigurationService private configurationService: IConfigurationService,
B
Benjamin Pasero 已提交
108
		@IModeService private modeService: IModeService,
109
		@IMessageService private messageService: IMessageService,
B
Benjamin Pasero 已提交
110
		@IPartService private partService: IPartService
C
Christof Marti 已提交
111
	) {
B
Benjamin Pasero 已提交
112
		super(WalkThroughPart.ID, telemetryService, themeService);
113
		this.editorFocus = WALK_THROUGH_FOCUS.bindTo(this.contextKeyService);
C
Christof Marti 已提交
114 115 116 117 118 119
	}

	createEditor(parent: Builder): void {
		const container = parent.getHTMLElement();

		this.content = document.createElement('div');
120 121
		this.content.tabIndex = 0;
		this.content.style.outlineStyle = 'none';
C
Christof Marti 已提交
122 123 124 125 126 127 128 129

		this.scrollbar = new DomScrollableElement(this.content, {
			horizontal: ScrollbarVisibility.Auto,
			vertical: ScrollbarVisibility.Auto
		});
		this.disposables.push(this.scrollbar);
		container.appendChild(this.scrollbar.getDomNode());

C
Christof Marti 已提交
130
		this.registerFocusHandlers();
C
Christof Marti 已提交
131
		this.registerClickHandler();
C
Christof Marti 已提交
132 133 134 135 136

		this.disposables.push(this.scrollbar.onScroll(e => this.updatedScrollPosition()));
	}

	private updatedScrollPosition() {
A
Alex Dima 已提交
137 138
		const scrollState = this.scrollbar.getScrollState();
		const scrollHeight = scrollState.scrollHeight;
C
Christof Marti 已提交
139
		if (scrollHeight && this.input instanceof WalkThroughInput) {
A
Alex Dima 已提交
140 141
			const scrollTop = scrollState.scrollTop;
			const height = scrollState.height;
C
Christof Marti 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
			this.input.relativeScrollPosition(scrollTop / scrollHeight, (scrollTop + height) / scrollHeight);
		}
	}

	private addEventListener<K extends keyof HTMLElementEventMap, E extends HTMLElement>(element: E, type: K, listener: (this: E, ev: HTMLElementEventMap[K]) => any, useCapture?: boolean): IDisposable;
	private addEventListener<E extends HTMLElement>(element: E, type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): IDisposable;
	private addEventListener<E extends HTMLElement>(element: E, type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): IDisposable {
		element.addEventListener(type, listener, useCapture);
		return { dispose: () => { element.removeEventListener(type, listener, useCapture); } };
	}

	private registerFocusHandlers() {
		this.disposables.push(this.addEventListener(this.content, 'mousedown', e => {
			this.focus();
		}));
		this.disposables.push(this.addEventListener(this.content, 'focus', e => {
			this.editorFocus.set(true);
		}));
		this.disposables.push(this.addEventListener(this.content, 'blur', e => {
			this.editorFocus.reset();
		}));
		this.disposables.push(this.addEventListener(this.content, 'focusin', e => {
			// Work around scrolling as side-effect of setting focus on the offscreen zone widget (#18929)
			if (e.target instanceof HTMLElement && e.target.classList.contains('zone-widget-container')) {
A
Alex Dima 已提交
166 167 168
				let scrollState = this.scrollbar.getScrollState();
				this.content.scrollTop = scrollState.scrollTop;
				this.content.scrollLeft = scrollState.scrollLeft;
C
Christof Marti 已提交
169 170
			}
		}));
C
Christof Marti 已提交
171 172 173 174
	}

	private registerClickHandler() {
		this.content.addEventListener('click', event => {
C
Christof Marti 已提交
175 176 177 178 179
			for (let node = event.target as HTMLElement; node; node = node.parentNode as HTMLElement) {
				if (node instanceof HTMLAnchorElement && node.href) {
					let baseElement = window.document.getElementsByTagName('base')[0] || window.location;
					if (baseElement && node.href.indexOf(baseElement.href) >= 0 && node.hash) {
						let scrollTarget = this.content.querySelector(node.hash);
180 181 182 183 184
						this.telemetryService.publicLog('revealInDocument', {
							hash: node.hash,
							broken: !scrollTarget,
							from: this.input instanceof WalkThroughInput ? this.input.getTelemetryFrom() : undefined
						});
185 186 187 188
						const innerContent = this.content.firstElementChild;
						if (scrollTarget && innerContent) {
							const targetTop = scrollTarget.getBoundingClientRect().top - 20;
							const containerTop = innerContent.getBoundingClientRect().top;
189
							this.scrollbar.updateState({ scrollTop: targetTop - containerTop });
C
Christof Marti 已提交
190 191
						}
					} else {
192
						this.open(URI.parse(node.href));
C
Christof Marti 已提交
193
					}
C
Christof Marti 已提交
194 195 196 197 198
					event.preventDefault();
					break;
				} else if (node instanceof HTMLButtonElement) {
					const href = node.getAttribute('data-href');
					if (href) {
199
						this.open(URI.parse(href));
C
Christof Marti 已提交
200 201 202 203
					}
					break;
				} else if (node === event.currentTarget) {
					break;
C
Christof Marti 已提交
204 205 206 207 208
				}
			}
		});
	}

209 210 211 212 213 214 215
	private open(uri: URI) {
		if (uri.scheme === 'http' || uri.scheme === 'https') {
			this.telemetryService.publicLog('openExternal', {
				uri: uri.toString(true),
				from: this.input instanceof WalkThroughInput ? this.input.getTelemetryFrom() : undefined
			});
		}
216 217 218 219
		if (uri.scheme === 'command' && uri.path === 'git.clone' && !CommandsRegistry.getCommand('git.clone')) {
			this.messageService.show(Severity.Info, localize('walkThrough.gitNotFound', "It looks like Git is not installed on your system."));
			return;
		}
220 221 222
		this.openerService.open(this.addFrom(uri));
	}

C
Christof Marti 已提交
223
	private addFrom(uri: URI) {
224
		if (uri.scheme !== 'command' || !(this.input instanceof WalkThroughInput)) {
C
Christof Marti 已提交
225 226 227
			return uri;
		}
		const query = uri.query ? JSON.parse(uri.query) : {};
228
		query.from = this.input.getTelemetryFrom();
C
Christof Marti 已提交
229 230 231
		return uri.with({ query: JSON.stringify(query) });
	}

232 233 234 235
	layout(size: Dimension): void {
		this.size = size;
		$(this.content).style({ height: `${size.height}px`, width: `${size.width}px` });
		this.updateSizeClasses();
C
Christof Marti 已提交
236 237 238 239 240 241 242 243
		this.contentDisposables.forEach(disposable => {
			if (disposable instanceof CodeEditor) {
				disposable.layout();
			}
		});
		this.scrollbar.scanDomNode();
	}

244 245 246 247
	private updateSizeClasses() {
		const innerContent = this.content.firstElementChild;
		if (this.size && innerContent) {
			const classList = innerContent.classList;
248
			classList[this.size.height <= 685 ? 'add' : 'remove']('max-height-685px');
249 250 251
		}
	}

C
Christof Marti 已提交
252
	focus(): void {
253 254 255 256 257 258 259
		let active = document.activeElement;
		while (active && active !== this.content) {
			active = active.parentElement;
		}
		if (!active) {
			this.content.focus();
		}
260 261 262 263
		this.editorFocus.set(true);
	}

	arrowUp() {
A
Alex Dima 已提交
264 265
		const scrollState = this.scrollbar.getScrollState();
		this.scrollbar.updateState({ scrollTop: scrollState.scrollTop - this.getArrowScrollHeight() });
266 267 268
	}

	arrowDown() {
A
Alex Dima 已提交
269 270
		const scrollState = this.scrollbar.getScrollState();
		this.scrollbar.updateState({ scrollTop: scrollState.scrollTop + this.getArrowScrollHeight() });
271 272 273 274 275 276 277 278 279 280 281
	}

	private getArrowScrollHeight() {
		let fontSize = this.configurationService.lookup<number>('editor.fontSize').value;
		if (typeof fontSize !== 'number' || fontSize < 1) {
			fontSize = 12;
		}
		return 3 * fontSize;
	}

	pageUp() {
A
Alex Dima 已提交
282 283
		const scrollState = this.scrollbar.getScrollState();
		this.scrollbar.updateState({ scrollTop: scrollState.scrollTop - scrollState.height });
284 285 286
	}

	pageDown() {
A
Alex Dima 已提交
287 288
		const scrollState = this.scrollbar.getScrollState();
		this.scrollbar.updateState({ scrollTop: scrollState.scrollTop + scrollState.height });
C
Christof Marti 已提交
289 290 291
	}

	setInput(input: WalkThroughInput, options: EditorOptions): TPromise<void> {
292 293 294 295
		if (this.input instanceof WalkThroughInput && this.input.matches(input)) {
			return TPromise.as(undefined);
		}

296 297 298 299
		if (this.input instanceof WalkThroughInput) {
			this.saveTextEditorViewState(this.input.getResource());
		}

C
Christof Marti 已提交
300 301 302 303
		this.contentDisposables = dispose(this.contentDisposables);
		this.content.innerHTML = '';

		return super.setInput(input, options)
C
Christof Marti 已提交
304 305 306 307
			.then(() => {
				return input.resolve(true);
			})
			.then(model => {
C
Christof Marti 已提交
308
				const content = model.main.textEditorModel.getLinesContent().join('\n');
309
				if (!strings.endsWith(input.getResource().path, '.md')) {
C
Christof Marti 已提交
310
					this.content.innerHTML = content;
311
					this.updateSizeClasses();
C
Christof Marti 已提交
312
					this.decorateContent();
313
					this.contentDisposables.push(this.keybindingService.onDidUpdateKeybindings(() => this.decorateContent()));
C
Christof Marti 已提交
314
					if (input.onReady) {
315
						input.onReady(this.content.firstElementChild as HTMLElement);
C
Christof Marti 已提交
316 317
					}
					this.scrollbar.scanDomNode();
318
					this.loadTextEditorViewState(input.getResource());
C
Christof Marti 已提交
319
					this.updatedScrollPosition();
C
Christof Marti 已提交
320 321 322
					return;
				}

C
Christof Marti 已提交
323
				let i = 0;
C
Christof Marti 已提交
324 325
				const renderer = new marked.Renderer();
				renderer.code = (code, lang) => {
326
					const id = `snippet-${model.snippets[i++].textEditorModel.uri.fragment}`;
C
Christof Marti 已提交
327
					return `<div id="${id}" class="walkThroughEditorContainer" ></div>`;
C
Christof Marti 已提交
328
				};
329 330
				const innerContent = document.createElement('div');
				innerContent.classList.add('walkThroughContent'); // only for markdown files
C
Christof Marti 已提交
331
				const markdown = this.expandMacros(content);
332 333
				innerContent.innerHTML = marked(markdown, { renderer });
				this.content.appendChild(innerContent);
C
Christof Marti 已提交
334

C
Christof Marti 已提交
335
				model.snippets.forEach((snippet, i) => {
C
Christof Marti 已提交
336
					const model = snippet.textEditorModel;
337
					const id = `snippet-${model.uri.fragment}`;
338
					const div = innerContent.querySelector(`#${id.replace(/\./g, '\\.')}`) as HTMLElement;
C
Christof Marti 已提交
339

340 341 342 343 344 345
					const options = this.getEditorOptions(snippet.textEditorModel.getModeId());
					const telemetryData = {
						target: this.input instanceof WalkThroughInput ? this.input.getTelemetryFrom() : undefined,
						snippet: i
					};
					const editor = this.instantiationService.createInstance(WalkThroughCodeEditor, div, options, telemetryData);
C
Christof Marti 已提交
346 347 348
					editor.setModel(model);
					this.contentDisposables.push(editor);

349 350 351 352 353 354 355 356 357 358 359 360 361
					const updateHeight = (initial: boolean) => {
						const lineHeight = editor.getConfiguration().lineHeight;
						const height = `${Math.max(model.getLineCount() + 1, 4) * lineHeight}px`;
						if (div.style.height !== height) {
							div.style.height = height;
							editor.layout();
							if (!initial) {
								this.scrollbar.scanDomNode();
							}
						}
					};
					updateHeight(true);
					this.contentDisposables.push(editor.onDidChangeModelContent(() => updateHeight(false)));
362 363 364 365 366 367 368 369
					this.contentDisposables.push(editor.onDidChangeCursorPosition(e => {
						const innerContent = this.content.firstElementChild;
						if (innerContent) {
							const targetTop = div.getBoundingClientRect().top;
							const containerTop = innerContent.getBoundingClientRect().top;
							const lineHeight = editor.getConfiguration().lineHeight;
							const lineTop = (targetTop + (e.position.lineNumber - 1) * lineHeight) - containerTop;
							const lineBottom = lineTop + lineHeight;
A
Alex Dima 已提交
370 371 372
							const scrollState = this.scrollbar.getScrollState();
							const scrollTop = scrollState.scrollTop;
							const height = scrollState.height;
373 374 375 376 377 378 379
							if (scrollTop > lineTop) {
								this.scrollbar.updateState({ scrollTop: lineTop });
							} else if (scrollTop < lineBottom - height) {
								this.scrollbar.updateState({ scrollTop: lineBottom - height });
							}
						}
					}));
C
Christof Marti 已提交
380

381 382 383 384 385
					this.contentDisposables.push(this.configurationService.onDidUpdateConfiguration(() => {
						if (snippet.textEditorModel) {
							editor.updateOptions(this.getEditorOptions(snippet.textEditorModel.getModeId()));
						}
					}));
386 387 388

					this.contentDisposables.push(once(editor.onMouseDown)(() => {
						this.telemetryService.publicLog('walkThroughSnippetInteraction', {
C
Christof Marti 已提交
389
							from: this.input instanceof WalkThroughInput ? this.input.getTelemetryFrom() : undefined,
390 391 392 393 394 395
							type: 'mouseDown',
							snippet: i
						});
					}));
					this.contentDisposables.push(once(editor.onKeyDown)(() => {
						this.telemetryService.publicLog('walkThroughSnippetInteraction', {
C
Christof Marti 已提交
396
							from: this.input instanceof WalkThroughInput ? this.input.getTelemetryFrom() : undefined,
397 398 399 400
							type: 'keyDown',
							snippet: i
						});
					}));
C
Christof Marti 已提交
401 402
					this.contentDisposables.push(once(editor.onDidChangeModelContent)(() => {
						this.telemetryService.publicLog('walkThroughSnippetInteraction', {
C
Christof Marti 已提交
403
							from: this.input instanceof WalkThroughInput ? this.input.getTelemetryFrom() : undefined,
C
Christof Marti 已提交
404 405 406 407
							type: 'changeModelContent',
							snippet: i
						});
					}));
C
Christof Marti 已提交
408
				});
409
				this.updateSizeClasses();
410 411
				this.multiCursorModifier();
				this.contentDisposables.push(this.configurationService.onDidUpdateConfiguration(() => this.multiCursorModifier()));
C
Christof Marti 已提交
412
				if (input.onReady) {
413
					input.onReady(innerContent);
C
Christof Marti 已提交
414
				}
C
Christof Marti 已提交
415
				this.scrollbar.scanDomNode();
416
				this.loadTextEditorViewState(input.getResource());
C
Christof Marti 已提交
417
				this.updatedScrollPosition();
C
Christof Marti 已提交
418
			});
C
Christof Marti 已提交
419 420
	}

421
	private getEditorOptions(language: string): IEditorOptions {
422
		const config = this.configurationService.getConfiguration<IEditorOptions>('editor', { language });
423 424 425
		return {
			...isObject(config) ? config : Object.create(null),
			scrollBeyondLastLine: false,
426 427 428 429 430 431 432
			scrollbar: {
				verticalScrollbarSize: 14,
				horizontal: 'auto',
				useShadows: true,
				verticalHasArrows: false,
				horizontalHasArrows: false
			},
433 434 435
			overviewRulerLanes: 3,
			fixedOverflowWidgets: true,
			lineNumbersMinChars: 1,
436
			minimap: { enabled: false },
437 438 439
		};
	}

C
Christof Marti 已提交
440 441
	private expandMacros(input: string) {
		return input.replace(/kb\(([a-z.\d\-]+)\)/gi, (match: string, kb: string) => {
442
			const keybinding = this.keybindingService.lookupKeybinding(kb);
443
			const shortcut = keybinding ? keybinding.getLabel() : UNBOUND_COMMAND;
444
			return `<span class="shortcut">${strings.escape(shortcut)}</span>`;
C
Christof Marti 已提交
445
		});
C
Christof Marti 已提交
446 447
	}

C
Christof Marti 已提交
448 449 450 451
	private decorateContent() {
		const keys = this.content.querySelectorAll('.shortcut[data-command]');
		Array.prototype.forEach.call(keys, (key: Element) => {
			const command = key.getAttribute('data-command');
452
			const keybinding = command && this.keybindingService.lookupKeybinding(command);
453
			const label = keybinding ? keybinding.getLabel() : UNBOUND_COMMAND;
454 455 456
			while (key.firstChild) {
				key.removeChild(key.firstChild);
			}
C
Christof Marti 已提交
457 458
			key.appendChild(document.createTextNode(label));
		});
459 460 461 462 463 464
		const ifkeys = this.content.querySelectorAll('.if_shortcut[data-command]');
		Array.prototype.forEach.call(ifkeys, (key: HTMLElement) => {
			const command = key.getAttribute('data-command');
			const keybinding = command && this.keybindingService.lookupKeybinding(command);
			key.style.display = !keybinding ? 'none' : '';
		});
C
Christof Marti 已提交
465
	}
466

467 468 469 470 471 472 473 474 475 476 477 478 479
	private multiCursorModifier() {
		const labels = UILabelProvider.modifierLabels[OS];
		const setting = this.configurationService.lookup<string>('editor.multiCursorModifier');
		const modifier = labels[setting.value === 'ctrlCmd' ? (OS === OperatingSystem.Macintosh ? 'metaKey' : 'ctrlKey') : 'altKey'];
		const keys = this.content.querySelectorAll('.multi-cursor-modifier');
		Array.prototype.forEach.call(keys, (key: Element) => {
			while (key.firstChild) {
				key.removeChild(key.firstChild);
			}
			key.appendChild(document.createTextNode(modifier));
		});
	}

480 481 482 483 484 485 486 487
	private saveTextEditorViewState(resource: URI): void {
		const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
		let editorViewStateMemento = memento[WALK_THROUGH_EDITOR_VIEW_STATE_PREFERENCE_KEY];
		if (!editorViewStateMemento) {
			editorViewStateMemento = Object.create(null);
			memento[WALK_THROUGH_EDITOR_VIEW_STATE_PREFERENCE_KEY] = editorViewStateMemento;
		}

A
Alex Dima 已提交
488
		const scrollState = this.scrollbar.getScrollState();
489 490
		const editorViewState: IWalkThroughEditorViewState = {
			viewState: {
A
Alex Dima 已提交
491 492
				scrollTop: scrollState.scrollTop,
				scrollLeft: scrollState.scrollLeft
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
			}
		};

		let fileViewState: IWalkThroughEditorViewStates = editorViewStateMemento[resource.toString()];
		if (!fileViewState) {
			fileViewState = Object.create(null);
			editorViewStateMemento[resource.toString()] = fileViewState;
		}

		if (typeof this.position === 'number') {
			fileViewState[this.position] = editorViewState;
		}
	}

	private loadTextEditorViewState(resource: URI) {
		const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
		const editorViewStateMemento = memento[WALK_THROUGH_EDITOR_VIEW_STATE_PREFERENCE_KEY];
		if (editorViewStateMemento) {
			const fileViewState: IWalkThroughEditorViewStates = editorViewStateMemento[resource.toString()];
			if (fileViewState) {
				const state: IWalkThroughEditorViewState = fileViewState[this.position];
				if (state) {
					this.scrollbar.updateState(state.viewState);
				}
			}
		}
	}

	public clearInput(): void {
		if (this.input instanceof WalkThroughInput) {
			this.saveTextEditorViewState(this.input.getResource());
		}
		super.clearInput();
	}

	public shutdown(): void {
		if (this.input instanceof WalkThroughInput) {
			this.saveTextEditorViewState(this.input.getResource());
		}
		super.shutdown();
	}
C
Christof Marti 已提交
534 535

	dispose(): void {
536
		this.editorFocus.reset();
C
Christof Marti 已提交
537 538 539 540 541
		this.contentDisposables = dispose(this.contentDisposables);
		this.disposables = dispose(this.disposables);
		super.dispose();
	}
}
542 543 544 545 546 547 548 549 550 551 552

// theming

const embeddedEditorBackground = registerColor('walkThrough.embeddedEditorBackground', { dark: null, light: null, hc: null }, localize('walkThrough.embeddedEditorBackground', 'Background color for the embedded editors on the Interactive Playground.'));

registerThemingParticipant((theme, collector) => {
	const color = getExtraColor(theme, embeddedEditorBackground, { dark: 'rgba(0, 0, 0, .4)', extra_dark: 'rgba(200, 235, 255, .064)', light: 'rgba(0,0,0,.08)', hc: null });
	if (color) {
		collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent .monaco-editor-background,
			.monaco-workbench > .part.editor > .content .walkThroughContent .margin-view-overlays { background: ${color}; }`);
	}
C
Christof Marti 已提交
553 554 555 556
	const link = theme.getColor(textLinkForeground);
	if (link) {
		collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent a { color: ${link}; }`);
	}
557 558 559 560 561
	const activeLink = theme.getColor(textLinkActiveForeground);
	if (activeLink) {
		collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent a:hover,
			.monaco-workbench > .part.editor > .content .walkThroughContent a:active { color: ${activeLink}; }`);
	}
562 563 564 565
	const focusColor = theme.getColor(focusBorder);
	if (focusColor) {
		collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent a:focus { outline-color: ${focusColor}; }`);
	}
C
Christof Marti 已提交
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
	const shortcut = theme.getColor(textPreformatForeground);
	if (shortcut) {
		collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent code,
			.monaco-workbench > .part.editor > .content .walkThroughContent .shortcut { color: ${shortcut}; }`);
	}
	const border = theme.getColor(contrastBorder);
	if (border) {
		collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent .monaco-editor { border-color: ${border}; }`);
	}
	const quoteBackground = theme.getColor(textBlockQuoteBackground);
	if (quoteBackground) {
		collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent blockquote { background: ${quoteBackground}; }`);
	}
	const quoteBorder = theme.getColor(textBlockQuoteBorder);
	if (quoteBorder) {
		collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent blockquote { border-color: ${quoteBorder}; }`);
	}
583
});