private async _doUpdate()

in src/vs/workbench/contrib/outline/browser/outlinePanel.ts [444:605]


	private async _doUpdate(editor: ICodeEditor | undefined, event: IModelContentChangedEvent | undefined): Promise<void> {
		this._editorDisposables.clear();

		this._progressBar.infinite().show(150);

		const oldModel = this._tree.getInput();

		// persist state
		if (oldModel) {
			this._treeStates.set(oldModel.textModel.uri.toString(), this._tree.getViewState());
		}

		if (!editor || !editor.hasModel() || !DocumentSymbolProviderRegistry.has(editor.getModel())) {
			return this._showMessage(localize('no-editor', "The active editor cannot provide outline information."));
		}

		let textModel = editor.getModel();
		let loadingMessage: IDisposable | undefined;
		if (!oldModel) {
			loadingMessage = new TimeoutTimer(
				() => this._showMessage(localize('loading', "Loading document symbols for '{0}'...", basename(textModel.uri))),
				100
			);
		}

		let createdModel = await OutlinePanel._createOutlineModel(textModel, this._editorDisposables);
		dispose(loadingMessage);
		if (!createdModel) {
			return;
		}

		let newModel = createdModel;
		if (TreeElement.empty(newModel)) {
			return this._showMessage(localize('no-symbols', "No symbols found in document '{0}'", basename(textModel.uri)));
		}

		dom.removeClass(this._domNode, 'message');

		if (event && oldModel && textModel.getLineCount() >= 25) {
			// heuristic: when the symbols-to-lines ratio changes by 50% between edits
			// wait a little (and hope that the next change isn't as drastic).
			let newSize = TreeElement.size(newModel);
			let newLength = textModel.getValueLength();
			let newRatio = newSize / newLength;
			let oldSize = TreeElement.size(oldModel);
			let oldLength = newLength - event.changes.reduce((prev, value) => prev + value.rangeLength, 0);
			let oldRatio = oldSize / oldLength;
			if (newRatio <= oldRatio * 0.5 || newRatio >= oldRatio * 1.5) {

				let waitPromise = new Promise<boolean>(resolve => {
					let handle: any = setTimeout(() => {
						handle = undefined;
						resolve(true);
					}, 2000);
					this._disposables.push({
						dispose() {
							clearTimeout(handle);
							resolve(false);
						}
					});
				});

				if (!await waitPromise) {
					return;
				}
			}
		}

		this._progressBar.stop().hide();

		if (oldModel && oldModel.merge(newModel)) {
			this._tree.updateChildren();
			newModel = oldModel;
		} else {
			let state = this._treeStates.get(newModel.textModel.uri.toString());
			await this._tree.setInput(newModel, state);
		}

		// transfer focus from domNode to the tree
		if (this._domNode === document.activeElement) {
			this._tree.domFocus();
		}

		this._editorDisposables.add(toDisposable(() => this._contextKeyFiltered.reset()));

		// feature: reveal outline selection in editor
		// on change -> reveal/select defining range
		this._editorDisposables.add(this._tree.onDidChangeSelection(e => {
			if (e.browserEvent === this._treeFakeUIEvent /* || e.payload && e.payload.didClickOnTwistie */) {
				return;
			}
			let [first] = e.elements;
			if (!(first instanceof OutlineElement)) {
				return;
			}

			let focus = false;
			let aside = false;
			// todo@Joh
			if (e.browserEvent) {
				if (e.browserEvent.type === 'keydown') {
					focus = true;
				} else if (e.browserEvent.type === 'click') {
					const event = new StandardMouseEvent(e.browserEvent as MouseEvent);
					focus = e.browserEvent.detail === 2;
					aside = (!this._tree.useAltAsMultipleSelectionModifier && event.altKey)
						|| (this._tree.useAltAsMultipleSelectionModifier && (event.ctrlKey || event.metaKey));
				}
			}
			this._revealTreeSelection(newModel, first, focus, aside);
		}));

		// feature: reveal editor selection in outline
		this._revealEditorSelection(newModel, editor.getSelection());
		const versionIdThen = newModel.textModel.getVersionId();
		this._editorDisposables.add(editor.onDidChangeCursorSelection(e => {
			// first check if the document has changed and stop revealing the
			// cursor position iff it has -> we will update/recompute the
			// outline view then anyways
			if (!newModel.textModel.isDisposed() && newModel.textModel.getVersionId() === versionIdThen) {
				this._revealEditorSelection(newModel, e.selection);
			}
		}));

		// feature: show markers in outline
		const updateMarker = (model: ITextModel, ignoreEmpty?: boolean) => {
			if (!this._configurationService.getValue(OutlineConfigKeys.problemsEnabled)) {
				return;
			}
			if (model !== textModel) {
				return;
			}
			const markers: IOutlineMarker[] = [];
			for (const [range, marker] of this._markerDecorationService.getLiveMarkers(textModel)) {
				if (marker.severity === MarkerSeverity.Error || marker.severity === MarkerSeverity.Warning) {
					markers.push({ ...range, severity: marker.severity });
				}
			}
			if (markers.length > 0 || !ignoreEmpty) {
				newModel.updateMarker(markers);
				this._tree.updateChildren();
			}
		};
		updateMarker(textModel, true);
		this._editorDisposables.add(this._markerDecorationService.onDidChangeMarker(updateMarker));

		this._editorDisposables.add(this.configurationService.onDidChangeConfiguration(e => {
			if (e.affectsConfiguration(OutlineConfigKeys.problemsBadges) || e.affectsConfiguration(OutlineConfigKeys.problemsColors)) {
				this._tree.updateChildren();
				return;
			}
			if (!e.affectsConfiguration(OutlineConfigKeys.problemsEnabled)) {
				return;
			}
			if (!this._configurationService.getValue(OutlineConfigKeys.problemsEnabled)) {
				newModel.updateMarker([]);
				this._tree.updateChildren();
			} else {
				updateMarker(textModel, true);
			}
		}));
	}