mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-27 07:10:30 -04:00
Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
This commit is contained in:
@@ -0,0 +1,353 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { StableEditorScrollState } from 'vs/editor/browser/core/editorState';
|
||||
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
|
||||
import { CodeInsetProviderRegistry, getCodeInsetData, ICodeInsetData } from '../common/codeInset';
|
||||
import { CodeInsetWidget, CodeInsetHelper } from './codeInsetWidget';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement';
|
||||
// import { localize } from 'vs/nls';
|
||||
|
||||
export class CodeInsetController implements editorCommon.IEditorContribution {
|
||||
|
||||
static get(editor: editorBrowser.ICodeEditor): CodeInsetController {
|
||||
return editor.getContribution(CodeInsetController.ID);
|
||||
}
|
||||
|
||||
private static readonly ID: string = 'css.editor.codeInset';
|
||||
|
||||
private _isEnabled: boolean;
|
||||
|
||||
private _globalToDispose: IDisposable[];
|
||||
private _localToDispose: IDisposable[];
|
||||
private _insetWidgets: CodeInsetWidget[];
|
||||
private _pendingWebviews = new Map<string, (element: WebviewElement) => any>();
|
||||
private _currentFindCodeInsetSymbolsPromise: CancelablePromise<ICodeInsetData[]> | null;
|
||||
private _modelChangeCounter: number;
|
||||
private _currentResolveCodeInsetSymbolsPromise: CancelablePromise<any> | null;
|
||||
private _detectVisibleInsets: RunOnceScheduler;
|
||||
|
||||
constructor(
|
||||
private _editor: editorBrowser.ICodeEditor,
|
||||
@IConfigurationService private readonly _configService: IConfigurationService,
|
||||
) {
|
||||
this._isEnabled = this._configService.getValue<boolean>('editor.codeInsets');
|
||||
|
||||
this._globalToDispose = [];
|
||||
this._localToDispose = [];
|
||||
this._insetWidgets = [];
|
||||
this._currentFindCodeInsetSymbolsPromise = null;
|
||||
this._modelChangeCounter = 0;
|
||||
|
||||
this._globalToDispose.push(this._editor.onDidChangeModel(() => this._onModelChange()));
|
||||
this._globalToDispose.push(this._editor.onDidChangeModelLanguage(() => this._onModelChange()));
|
||||
this._globalToDispose.push(this._configService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('editor.codeInsets')) {
|
||||
let prevIsEnabled = this._isEnabled;
|
||||
this._isEnabled = this._configService.getValue<boolean>('editor.codeInsets');
|
||||
if (prevIsEnabled !== this._isEnabled) {
|
||||
this._onModelChange();
|
||||
}
|
||||
}
|
||||
}));
|
||||
this._globalToDispose.push(CodeInsetProviderRegistry.onDidChange(this._onModelChange, this));
|
||||
this._onModelChange();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._localDispose();
|
||||
this._globalToDispose = dispose(this._globalToDispose);
|
||||
}
|
||||
|
||||
acceptWebview(symbolId: string, webviewElement: WebviewElement): boolean {
|
||||
const pendingWebview = this._pendingWebviews.get(symbolId);
|
||||
if (pendingWebview) {
|
||||
pendingWebview(webviewElement);
|
||||
this._pendingWebviews.delete(symbolId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private _localDispose(): void {
|
||||
if (this._currentFindCodeInsetSymbolsPromise) {
|
||||
this._currentFindCodeInsetSymbolsPromise.cancel();
|
||||
this._currentFindCodeInsetSymbolsPromise = null;
|
||||
this._modelChangeCounter++;
|
||||
}
|
||||
if (this._currentResolveCodeInsetSymbolsPromise) {
|
||||
this._currentResolveCodeInsetSymbolsPromise.cancel();
|
||||
this._currentResolveCodeInsetSymbolsPromise = null;
|
||||
}
|
||||
this._localToDispose = dispose(this._localToDispose);
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return CodeInsetController.ID;
|
||||
}
|
||||
|
||||
private _onModelChange(): void {
|
||||
this._localDispose();
|
||||
|
||||
const model = this._editor.getModel();
|
||||
if (!model || !this._isEnabled || !CodeInsetProviderRegistry.has(model)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const provider of CodeInsetProviderRegistry.all(model)) {
|
||||
if (typeof provider.onDidChange === 'function') {
|
||||
let registration = provider.onDidChange(() => scheduler.schedule());
|
||||
this._localToDispose.push(registration);
|
||||
}
|
||||
}
|
||||
|
||||
this._detectVisibleInsets = new RunOnceScheduler(() => {
|
||||
this._onViewportChanged();
|
||||
}, 500);
|
||||
|
||||
const scheduler = new RunOnceScheduler(() => {
|
||||
const counterValue = ++this._modelChangeCounter;
|
||||
if (this._currentFindCodeInsetSymbolsPromise) {
|
||||
this._currentFindCodeInsetSymbolsPromise.cancel();
|
||||
}
|
||||
|
||||
this._currentFindCodeInsetSymbolsPromise = createCancelablePromise(token => getCodeInsetData(model, token));
|
||||
|
||||
this._currentFindCodeInsetSymbolsPromise.then(codeInsetData => {
|
||||
if (counterValue === this._modelChangeCounter) { // only the last one wins
|
||||
this._renderCodeInsetSymbols(codeInsetData);
|
||||
this._detectVisibleInsets.schedule();
|
||||
}
|
||||
}, onUnexpectedError);
|
||||
}, 250);
|
||||
|
||||
this._localToDispose.push(scheduler);
|
||||
|
||||
this._localToDispose.push(this._detectVisibleInsets);
|
||||
|
||||
this._localToDispose.push(this._editor.onDidChangeModelContent(() => {
|
||||
this._editor.changeDecorations(changeAccessor => {
|
||||
this._editor.changeViewZones(viewAccessor => {
|
||||
let toDispose: CodeInsetWidget[] = [];
|
||||
let lastInsetLineNumber: number = -1;
|
||||
this._insetWidgets.forEach(inset => {
|
||||
if (!inset.isValid() || lastInsetLineNumber === inset.getLineNumber()) {
|
||||
// invalid -> Inset collapsed, attach range doesn't exist anymore
|
||||
// line_number -> insets should never be on the same line
|
||||
toDispose.push(inset);
|
||||
}
|
||||
else {
|
||||
inset.reposition(viewAccessor);
|
||||
lastInsetLineNumber = inset.getLineNumber();
|
||||
}
|
||||
});
|
||||
let helper = new CodeInsetHelper();
|
||||
toDispose.forEach((l) => {
|
||||
l.dispose(helper, viewAccessor);
|
||||
this._insetWidgets.splice(this._insetWidgets.indexOf(l), 1);
|
||||
});
|
||||
helper.commit(changeAccessor);
|
||||
});
|
||||
});
|
||||
// Compute new `visible` code insets
|
||||
this._detectVisibleInsets.schedule();
|
||||
// Ask for all references again
|
||||
scheduler.schedule();
|
||||
}));
|
||||
|
||||
this._localToDispose.push(this._editor.onDidScrollChange(e => {
|
||||
if (e.scrollTopChanged && this._insetWidgets.length > 0) {
|
||||
this._detectVisibleInsets.schedule();
|
||||
}
|
||||
}));
|
||||
|
||||
this._localToDispose.push(this._editor.onDidLayoutChange(() => {
|
||||
this._detectVisibleInsets.schedule();
|
||||
}));
|
||||
|
||||
this._localToDispose.push(toDisposable(() => {
|
||||
if (this._editor.getModel()) {
|
||||
const scrollState = StableEditorScrollState.capture(this._editor);
|
||||
this._editor.changeDecorations((changeAccessor) => {
|
||||
this._editor.changeViewZones((accessor) => {
|
||||
this._disposeAllInsets(changeAccessor, accessor);
|
||||
});
|
||||
});
|
||||
scrollState.restore(this._editor);
|
||||
} else {
|
||||
// No accessors available
|
||||
this._disposeAllInsets(null, null);
|
||||
}
|
||||
}));
|
||||
|
||||
scheduler.schedule();
|
||||
}
|
||||
|
||||
private _disposeAllInsets(decChangeAccessor: IModelDecorationsChangeAccessor | null, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor | null): void {
|
||||
let helper = new CodeInsetHelper();
|
||||
this._insetWidgets.forEach((Inset) => Inset.dispose(helper, viewZoneChangeAccessor));
|
||||
if (decChangeAccessor) {
|
||||
helper.commit(decChangeAccessor);
|
||||
}
|
||||
this._insetWidgets = [];
|
||||
}
|
||||
|
||||
private _renderCodeInsetSymbols(symbols: ICodeInsetData[]): void {
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let maxLineNumber = this._editor.getModel().getLineCount();
|
||||
let groups: ICodeInsetData[][] = [];
|
||||
let lastGroup: ICodeInsetData[] | undefined;
|
||||
|
||||
for (let symbol of symbols) {
|
||||
let line = symbol.symbol.range.startLineNumber;
|
||||
if (line < 1 || line > maxLineNumber) {
|
||||
// invalid code Inset
|
||||
continue;
|
||||
} else if (lastGroup && lastGroup[lastGroup.length - 1].symbol.range.startLineNumber === line) {
|
||||
// on same line as previous
|
||||
lastGroup.push(symbol);
|
||||
} else {
|
||||
// on later line as previous
|
||||
lastGroup = [symbol];
|
||||
groups.push(lastGroup);
|
||||
}
|
||||
}
|
||||
|
||||
const scrollState = StableEditorScrollState.capture(this._editor);
|
||||
|
||||
this._editor.changeDecorations(changeAccessor => {
|
||||
this._editor.changeViewZones(accessor => {
|
||||
|
||||
let codeInsetIndex = 0, groupsIndex = 0, helper = new CodeInsetHelper();
|
||||
|
||||
while (groupsIndex < groups.length && codeInsetIndex < this._insetWidgets.length) {
|
||||
|
||||
let symbolsLineNumber = groups[groupsIndex][0].symbol.range.startLineNumber;
|
||||
let codeInsetLineNumber = this._insetWidgets[codeInsetIndex].getLineNumber();
|
||||
|
||||
if (codeInsetLineNumber < symbolsLineNumber) {
|
||||
this._insetWidgets[codeInsetIndex].dispose(helper, accessor);
|
||||
this._insetWidgets.splice(codeInsetIndex, 1);
|
||||
} else if (codeInsetLineNumber === symbolsLineNumber) {
|
||||
this._insetWidgets[codeInsetIndex].updateCodeInsetSymbols(groups[groupsIndex], helper);
|
||||
groupsIndex++;
|
||||
codeInsetIndex++;
|
||||
} else {
|
||||
this._insetWidgets.splice(
|
||||
codeInsetIndex,
|
||||
0,
|
||||
new CodeInsetWidget(groups[groupsIndex], this._editor, helper)
|
||||
);
|
||||
codeInsetIndex++;
|
||||
groupsIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete extra code insets
|
||||
while (codeInsetIndex < this._insetWidgets.length) {
|
||||
this._insetWidgets[codeInsetIndex].dispose(helper, accessor);
|
||||
this._insetWidgets.splice(codeInsetIndex, 1);
|
||||
}
|
||||
|
||||
// Create extra symbols
|
||||
while (groupsIndex < groups.length) {
|
||||
this._insetWidgets.push(new CodeInsetWidget(
|
||||
groups[groupsIndex],
|
||||
this._editor, helper
|
||||
));
|
||||
groupsIndex++;
|
||||
}
|
||||
|
||||
helper.commit(changeAccessor);
|
||||
});
|
||||
});
|
||||
|
||||
scrollState.restore(this._editor);
|
||||
}
|
||||
|
||||
private _onViewportChanged(): void {
|
||||
if (this._currentResolveCodeInsetSymbolsPromise) {
|
||||
this._currentResolveCodeInsetSymbolsPromise.cancel();
|
||||
this._currentResolveCodeInsetSymbolsPromise = null;
|
||||
}
|
||||
|
||||
const model = this._editor.getModel();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
const allWidgetRequests: ICodeInsetData[][] = [];
|
||||
const insetWidgets: CodeInsetWidget[] = [];
|
||||
this._insetWidgets.forEach(inset => {
|
||||
const widgetRequests = inset.computeIfNecessary(model);
|
||||
if (widgetRequests) {
|
||||
allWidgetRequests.push(widgetRequests);
|
||||
insetWidgets.push(inset);
|
||||
}
|
||||
});
|
||||
|
||||
if (allWidgetRequests.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._currentResolveCodeInsetSymbolsPromise = createCancelablePromise(token => {
|
||||
|
||||
const allPromises = allWidgetRequests.map((widgetRequests, r) => {
|
||||
|
||||
const widgetPromises = widgetRequests.map(request => {
|
||||
if (request.resolved) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
let a = new Promise(resolve => {
|
||||
this._pendingWebviews.set(request.symbol.id, element => {
|
||||
request.resolved = true;
|
||||
insetWidgets[r].adoptWebview(element);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
let b = request.provider.resolveCodeInset(model, request.symbol, token);
|
||||
return Promise.all([a, b]);
|
||||
});
|
||||
|
||||
return Promise.all(widgetPromises);
|
||||
});
|
||||
|
||||
return Promise.all(allPromises);
|
||||
});
|
||||
|
||||
this._currentResolveCodeInsetSymbolsPromise.then(() => {
|
||||
this._currentResolveCodeInsetSymbolsPromise = null;
|
||||
}).catch(err => {
|
||||
this._currentResolveCodeInsetSymbolsPromise = null;
|
||||
onUnexpectedError(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(CodeInsetController);
|
||||
|
||||
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration({
|
||||
id: 'editor',
|
||||
properties: {
|
||||
// ['editor.codeInsets']: {
|
||||
// description: localize('editor.codeInsets', "Enable/disable editor code insets"),
|
||||
// type: 'boolean',
|
||||
// default: false
|
||||
// }
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user