mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 1b314ab317fbff7d799b21754326b7d849889ceb
This commit is contained in:
@@ -75,7 +75,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
constructor(
|
||||
private readonly _proxy: ExtHostAuthenticationShape,
|
||||
public readonly id: string,
|
||||
public readonly displayName: string,
|
||||
public readonly label: string,
|
||||
public readonly supportsMultipleAccounts: boolean,
|
||||
private readonly notificationService: INotificationService,
|
||||
private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
|
||||
@@ -135,17 +135,17 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
}
|
||||
|
||||
private registerSession(session: modes.AuthenticationSession) {
|
||||
this._sessions.set(session.id, session.account.displayName);
|
||||
this._sessions.set(session.id, session.account.label);
|
||||
|
||||
const existingSessionsForAccount = this._accounts.get(session.account.displayName);
|
||||
const existingSessionsForAccount = this._accounts.get(session.account.label);
|
||||
if (existingSessionsForAccount) {
|
||||
this._accounts.set(session.account.displayName, existingSessionsForAccount.concat(session.id));
|
||||
this._accounts.set(session.account.label, existingSessionsForAccount.concat(session.id));
|
||||
return;
|
||||
} else {
|
||||
this._accounts.set(session.account.displayName, [session.id]);
|
||||
this._accounts.set(session.account.label, [session.id]);
|
||||
}
|
||||
|
||||
this.storageKeysSyncRegistryService.registerStorageKey({ key: `${this.id}-${session.account.displayName}`, version: 1 });
|
||||
this.storageKeysSyncRegistryService.registerStorageKey({ key: `${this.id}-${session.account.label}`, version: 1 });
|
||||
}
|
||||
|
||||
async signOut(accountName: string): Promise<void> {
|
||||
@@ -219,15 +219,15 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication);
|
||||
|
||||
this._register(this.authenticationService.onDidChangeSessions(e => {
|
||||
this._proxy.$onDidChangeAuthenticationSessions(e.providerId, e.event);
|
||||
this._proxy.$onDidChangeAuthenticationSessions(e.providerId, e.label, e.event);
|
||||
}));
|
||||
|
||||
this._register(this.authenticationService.onDidRegisterAuthenticationProvider(providerId => {
|
||||
this._proxy.$onDidChangeAuthenticationProviders([providerId], []);
|
||||
this._register(this.authenticationService.onDidRegisterAuthenticationProvider(info => {
|
||||
this._proxy.$onDidChangeAuthenticationProviders([info], []);
|
||||
}));
|
||||
|
||||
this._register(this.authenticationService.onDidUnregisterAuthenticationProvider(providerId => {
|
||||
this._proxy.$onDidChangeAuthenticationProviders([], [providerId]);
|
||||
this._register(this.authenticationService.onDidUnregisterAuthenticationProvider(info => {
|
||||
this._proxy.$onDidChangeAuthenticationProviders([], [info]);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -235,8 +235,8 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
return Promise.resolve(this.authenticationService.getProviderIds());
|
||||
}
|
||||
|
||||
async $registerAuthenticationProvider(id: string, displayName: string, supportsMultipleAccounts: boolean): Promise<void> {
|
||||
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, supportsMultipleAccounts, this.notificationService, this.storageKeysSyncRegistryService, this.storageService, this.quickInputService, this.dialogService);
|
||||
async $registerAuthenticationProvider(id: string, label: string, supportsMultipleAccounts: boolean): Promise<void> {
|
||||
const provider = new MainThreadAuthenticationProvider(this._proxy, id, label, supportsMultipleAccounts, this.notificationService, this.storageKeysSyncRegistryService, this.storageService, this.quickInputService, this.dialogService);
|
||||
await provider.initialize();
|
||||
this.authenticationService.registerAuthenticationProvider(id, provider);
|
||||
}
|
||||
@@ -267,13 +267,13 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
|
||||
async $getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: { createIfNone: boolean, clearSessionPreference: boolean }): Promise<modes.AuthenticationSession | undefined> {
|
||||
const orderedScopes = scopes.sort().join(' ');
|
||||
const sessions = (await this.$getSessions(providerId)).filter(session => session.scopes.sort().join(' ') === orderedScopes);
|
||||
const displayName = this.authenticationService.getDisplayName(providerId);
|
||||
const sessions = (await this.$getSessions(providerId)).filter(session => session.scopes.slice().sort().join(' ') === orderedScopes);
|
||||
const label = this.authenticationService.getLabel(providerId);
|
||||
|
||||
if (sessions.length) {
|
||||
if (!this.authenticationService.supportsMultipleAccounts(providerId)) {
|
||||
const session = sessions[0];
|
||||
const allowed = await this.$getSessionsPrompt(providerId, session.account.displayName, displayName, extensionId, extensionName);
|
||||
const allowed = await this.$getSessionsPrompt(providerId, session.account.label, label, extensionId, extensionName);
|
||||
if (allowed) {
|
||||
return session;
|
||||
} else {
|
||||
@@ -282,17 +282,17 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
}
|
||||
|
||||
// On renderer side, confirm consent, ask user to choose between accounts if multiple sessions are valid
|
||||
const selected = await this.$selectSession(providerId, displayName, extensionId, extensionName, sessions, scopes, !!options.clearSessionPreference);
|
||||
const selected = await this.$selectSession(providerId, label, extensionId, extensionName, sessions, scopes, !!options.clearSessionPreference);
|
||||
return sessions.find(session => session.id === selected.id);
|
||||
} else {
|
||||
if (options.createIfNone) {
|
||||
const isAllowed = await this.$loginPrompt(displayName, extensionName);
|
||||
const isAllowed = await this.$loginPrompt(label, extensionName);
|
||||
if (!isAllowed) {
|
||||
throw new Error('User did not consent to login.');
|
||||
}
|
||||
|
||||
const session = await this.authenticationService.login(providerId, scopes);
|
||||
await this.$setTrustedExtension(providerId, session.account.displayName, extensionId, extensionName);
|
||||
await this.$setTrustedExtension(providerId, session.account.label, extensionId, extensionName);
|
||||
return session;
|
||||
} else {
|
||||
await this.$requestNewSession(providerId, scopes, extensionId, extensionName);
|
||||
@@ -313,7 +313,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
if (existingSessionPreference) {
|
||||
const matchingSession = potentialSessions.find(session => session.id === existingSessionPreference);
|
||||
if (matchingSession) {
|
||||
const allowed = await this.$getSessionsPrompt(providerId, matchingSession.account.displayName, providerName, extensionId, extensionName);
|
||||
const allowed = await this.$getSessionsPrompt(providerId, matchingSession.account.label, providerName, extensionId, extensionName);
|
||||
if (allowed) {
|
||||
return matchingSession;
|
||||
}
|
||||
@@ -326,7 +326,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
quickPick.ignoreFocusOut = true;
|
||||
const items: { label: string, session?: modes.AuthenticationSession }[] = potentialSessions.map(session => {
|
||||
return {
|
||||
label: session.account.displayName,
|
||||
label: session.account.label,
|
||||
session
|
||||
};
|
||||
});
|
||||
@@ -351,7 +351,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
|
||||
const session = selected.session ?? await this.authenticationService.login(providerId, scopes);
|
||||
|
||||
const accountName = session.account.displayName;
|
||||
const accountName = session.account.label;
|
||||
|
||||
const allowList = readAllowedExtensions(this.storageService, providerId, accountName);
|
||||
if (!allowList.find(allowed => allowed.id === extensionId)) {
|
||||
|
||||
@@ -232,7 +232,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
noDebug: options.noDebug,
|
||||
parentSession: this.getSession(options.parentSessionID),
|
||||
repl: options.repl,
|
||||
noCompact: options.noCompact
|
||||
compact: options.compact
|
||||
};
|
||||
return this.debugService.startDebugging(launch, nameOrConfig, debugOptions).then(success => {
|
||||
return success;
|
||||
@@ -262,6 +262,26 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
return Promise.reject(new Error('debug session not found'));
|
||||
}
|
||||
|
||||
public $terminateDebugSession(sessionId: DebugSessionUUID): Promise<void> {
|
||||
const session = this.debugService.getModel().getSession(sessionId, true);
|
||||
if (session) {
|
||||
return session.terminate();
|
||||
}
|
||||
return Promise.reject(new Error('debug session not found'));
|
||||
}
|
||||
|
||||
public $stopDebugging(sessionId: DebugSessionUUID | undefined): Promise<void> {
|
||||
if (sessionId) {
|
||||
const session = this.debugService.getModel().getSession(sessionId, true);
|
||||
if (session) {
|
||||
return this.debugService.stopSession(session);
|
||||
}
|
||||
} else { // stop all
|
||||
return this.debugService.stopSession(undefined);
|
||||
}
|
||||
return Promise.reject(new Error('debug session not found'));
|
||||
}
|
||||
|
||||
public $appendDebugConsole(value: string): void {
|
||||
// Use warning as severity to get the orange color for messages coming from the debug extension
|
||||
const session = this.debugService.getViewModel().focusedSession;
|
||||
|
||||
@@ -23,37 +23,37 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape {
|
||||
//
|
||||
}
|
||||
|
||||
$showOpenDialog(options: MainThreadDialogOpenOptions): Promise<URI[] | undefined> {
|
||||
$showOpenDialog(options?: MainThreadDialogOpenOptions): Promise<URI[] | undefined> {
|
||||
return Promise.resolve(this._fileDialogService.showOpenDialog(MainThreadDialogs._convertOpenOptions(options)));
|
||||
}
|
||||
|
||||
$showSaveDialog(options: MainThreadDialogSaveOptions): Promise<URI | undefined> {
|
||||
$showSaveDialog(options?: MainThreadDialogSaveOptions): Promise<URI | undefined> {
|
||||
return Promise.resolve(this._fileDialogService.showSaveDialog(MainThreadDialogs._convertSaveOptions(options)));
|
||||
}
|
||||
|
||||
private static _convertOpenOptions(options: MainThreadDialogOpenOptions): IOpenDialogOptions {
|
||||
private static _convertOpenOptions(options?: MainThreadDialogOpenOptions): IOpenDialogOptions {
|
||||
const result: IOpenDialogOptions = {
|
||||
openLabel: options.openLabel || undefined,
|
||||
canSelectFiles: options.canSelectFiles || (!options.canSelectFiles && !options.canSelectFolders),
|
||||
canSelectFolders: options.canSelectFolders,
|
||||
canSelectMany: options.canSelectMany,
|
||||
defaultUri: options.defaultUri ? URI.revive(options.defaultUri) : undefined,
|
||||
title: options.title || undefined
|
||||
openLabel: options?.openLabel || undefined,
|
||||
canSelectFiles: options?.canSelectFiles || (!options?.canSelectFiles && !options?.canSelectFolders),
|
||||
canSelectFolders: options?.canSelectFolders,
|
||||
canSelectMany: options?.canSelectMany,
|
||||
defaultUri: options?.defaultUri ? URI.revive(options.defaultUri) : undefined,
|
||||
title: options?.title || undefined
|
||||
};
|
||||
if (options.filters) {
|
||||
if (options?.filters) {
|
||||
result.filters = [];
|
||||
forEach(options.filters, entry => result.filters!.push({ name: entry.key, extensions: entry.value }));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static _convertSaveOptions(options: MainThreadDialogSaveOptions): ISaveDialogOptions {
|
||||
private static _convertSaveOptions(options?: MainThreadDialogSaveOptions): ISaveDialogOptions {
|
||||
const result: ISaveDialogOptions = {
|
||||
defaultUri: options.defaultUri ? URI.revive(options.defaultUri) : undefined,
|
||||
saveLabel: options.saveLabel || undefined,
|
||||
title: options.title || undefined
|
||||
defaultUri: options?.defaultUri ? URI.revive(options.defaultUri) : undefined,
|
||||
saveLabel: options?.saveLabel || undefined,
|
||||
title: options?.title || undefined
|
||||
};
|
||||
if (options.filters) {
|
||||
if (options?.filters) {
|
||||
result.filters = [];
|
||||
forEach(options.filters, entry => result.filters!.push({ name: entry.key, extensions: entry.value }));
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { IDisposable, IReference, dispose, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IReference, dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
@@ -19,6 +19,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
|
||||
import { toLocalResource, extUri, IExtUri } from 'vs/base/common/resources';
|
||||
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
|
||||
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
export class BoundModelReferenceCollection {
|
||||
|
||||
@@ -73,7 +74,36 @@ export class BoundModelReferenceCollection {
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadDocuments implements MainThreadDocumentsShape {
|
||||
class ModelTracker extends Disposable {
|
||||
|
||||
private _knownVersionId: number;
|
||||
|
||||
constructor(
|
||||
private readonly _model: ITextModel,
|
||||
private readonly _onIsCaughtUpWithContentChanges: Emitter<URI>,
|
||||
private readonly _proxy: ExtHostDocumentsShape,
|
||||
private readonly _textFileService: ITextFileService,
|
||||
) {
|
||||
super();
|
||||
this._knownVersionId = this._model.getVersionId();
|
||||
this._register(this._model.onDidChangeContent((e) => {
|
||||
this._knownVersionId = e.versionId;
|
||||
this._proxy.$acceptModelChanged(this._model.uri, e, this._textFileService.isDirty(this._model.uri));
|
||||
if (this.isCaughtUpWithContentChanges()) {
|
||||
this._onIsCaughtUpWithContentChanges.fire(this._model.uri);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public isCaughtUpWithContentChanges(): boolean {
|
||||
return (this._model.getVersionId() === this._knownVersionId);
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadDocuments extends Disposable implements MainThreadDocumentsShape {
|
||||
|
||||
private _onIsCaughtUpWithContentChanges = this._register(new Emitter<URI>());
|
||||
public readonly onIsCaughtUpWithContentChanges = this._onIsCaughtUpWithContentChanges.event;
|
||||
|
||||
private readonly _modelService: IModelService;
|
||||
private readonly _textModelResolverService: ITextModelService;
|
||||
@@ -82,8 +112,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
|
||||
private readonly _environmentService: IWorkbenchEnvironmentService;
|
||||
private readonly _uriIdentityService: IUriIdentityService;
|
||||
|
||||
private readonly _toDispose = new DisposableStore();
|
||||
private _modelToDisposeMap: { [modelUrl: string]: IDisposable; };
|
||||
private _modelTrackers: { [modelUrl: string]: ModelTracker; };
|
||||
private readonly _proxy: ExtHostDocumentsShape;
|
||||
private readonly _modelIsSynced = new Set<string>();
|
||||
private readonly _modelReferenceCollection: BoundModelReferenceCollection;
|
||||
@@ -99,6 +128,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
|
||||
@IUriIdentityService uriIdentityService: IUriIdentityService,
|
||||
@IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService
|
||||
) {
|
||||
super();
|
||||
this._modelService = modelService;
|
||||
this._textModelResolverService = textModelResolverService;
|
||||
this._textFileService = textFileService;
|
||||
@@ -106,26 +136,26 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
|
||||
this._environmentService = environmentService;
|
||||
this._uriIdentityService = uriIdentityService;
|
||||
|
||||
this._modelReferenceCollection = this._toDispose.add(new BoundModelReferenceCollection(uriIdentityService.extUri));
|
||||
this._modelReferenceCollection = this._register(new BoundModelReferenceCollection(uriIdentityService.extUri));
|
||||
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocuments);
|
||||
|
||||
this._toDispose.add(documentsAndEditors.onDocumentAdd(models => models.forEach(this._onModelAdded, this)));
|
||||
this._toDispose.add(documentsAndEditors.onDocumentRemove(urls => urls.forEach(this._onModelRemoved, this)));
|
||||
this._toDispose.add(modelService.onModelModeChanged(this._onModelModeChanged, this));
|
||||
this._register(documentsAndEditors.onDocumentAdd(models => models.forEach(this._onModelAdded, this)));
|
||||
this._register(documentsAndEditors.onDocumentRemove(urls => urls.forEach(this._onModelRemoved, this)));
|
||||
this._register(modelService.onModelModeChanged(this._onModelModeChanged, this));
|
||||
|
||||
this._toDispose.add(textFileService.files.onDidSave(e => {
|
||||
this._register(textFileService.files.onDidSave(e => {
|
||||
if (this._shouldHandleFileEvent(e.model.resource)) {
|
||||
this._proxy.$acceptModelSaved(e.model.resource);
|
||||
}
|
||||
}));
|
||||
this._toDispose.add(textFileService.files.onDidChangeDirty(m => {
|
||||
this._register(textFileService.files.onDidChangeDirty(m => {
|
||||
if (this._shouldHandleFileEvent(m.resource)) {
|
||||
this._proxy.$acceptDirtyStateChanged(m.resource, m.isDirty());
|
||||
}
|
||||
}));
|
||||
|
||||
this._toDispose.add(workingCopyFileService.onDidRunWorkingCopyFileOperation(e => {
|
||||
this._register(workingCopyFileService.onDidRunWorkingCopyFileOperation(e => {
|
||||
if (e.operation === FileOperation.MOVE || e.operation === FileOperation.DELETE) {
|
||||
for (const { source } of e.files) {
|
||||
if (source) {
|
||||
@@ -135,15 +165,23 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
|
||||
}
|
||||
}));
|
||||
|
||||
this._modelToDisposeMap = Object.create(null);
|
||||
this._modelTrackers = Object.create(null);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
Object.keys(this._modelToDisposeMap).forEach((modelUrl) => {
|
||||
this._modelToDisposeMap[modelUrl].dispose();
|
||||
Object.keys(this._modelTrackers).forEach((modelUrl) => {
|
||||
this._modelTrackers[modelUrl].dispose();
|
||||
});
|
||||
this._modelToDisposeMap = Object.create(null);
|
||||
this._toDispose.dispose();
|
||||
this._modelTrackers = Object.create(null);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public isCaughtUpWithContentChanges(resource: URI): boolean {
|
||||
const modelUrl = resource.toString();
|
||||
if (this._modelTrackers[modelUrl]) {
|
||||
return this._modelTrackers[modelUrl].isCaughtUpWithContentChanges();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private _shouldHandleFileEvent(resource: URI): boolean {
|
||||
@@ -159,9 +197,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
|
||||
}
|
||||
const modelUrl = model.uri;
|
||||
this._modelIsSynced.add(modelUrl.toString());
|
||||
this._modelToDisposeMap[modelUrl.toString()] = model.onDidChangeContent((e) => {
|
||||
this._proxy.$acceptModelChanged(modelUrl, e, this._textFileService.isDirty(modelUrl));
|
||||
});
|
||||
this._modelTrackers[modelUrl.toString()] = new ModelTracker(model, this._onIsCaughtUpWithContentChanges, this._proxy, this._textFileService);
|
||||
}
|
||||
|
||||
private _onModelModeChanged(event: { model: ITextModel; oldModeId: string; }): void {
|
||||
@@ -179,8 +215,8 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
|
||||
return;
|
||||
}
|
||||
this._modelIsSynced.delete(strModelUrl);
|
||||
this._modelToDisposeMap[strModelUrl].dispose();
|
||||
delete this._modelToDisposeMap[strModelUrl];
|
||||
this._modelTrackers[strModelUrl].dispose();
|
||||
delete this._modelTrackers[strModelUrl];
|
||||
}
|
||||
|
||||
// --- from extension host process
|
||||
|
||||
@@ -306,6 +306,7 @@ export class MainThreadDocumentsAndEditors {
|
||||
|
||||
private readonly _toDispose = new DisposableStore();
|
||||
private readonly _proxy: ExtHostDocumentsAndEditorsShape;
|
||||
private readonly _mainThreadDocuments: MainThreadDocuments;
|
||||
private readonly _textEditors = new Map<string, MainThreadTextEditor>();
|
||||
|
||||
private readonly _onTextEditorAdd = new Emitter<MainThreadTextEditor[]>();
|
||||
@@ -336,8 +337,8 @@ export class MainThreadDocumentsAndEditors {
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentsAndEditors);
|
||||
|
||||
const mainThreadDocuments = this._toDispose.add(new MainThreadDocuments(this, extHostContext, this._modelService, this._textFileService, fileService, textModelResolverService, environmentService, uriIdentityService, workingCopyFileService));
|
||||
extHostContext.set(MainContext.MainThreadDocuments, mainThreadDocuments);
|
||||
this._mainThreadDocuments = this._toDispose.add(new MainThreadDocuments(this, extHostContext, this._modelService, this._textFileService, fileService, textModelResolverService, environmentService, uriIdentityService, workingCopyFileService));
|
||||
extHostContext.set(MainContext.MainThreadDocuments, this._mainThreadDocuments);
|
||||
|
||||
const mainThreadTextEditors = this._toDispose.add(new MainThreadTextEditors(this, extHostContext, codeEditorService, bulkEditService, this._editorService, this._editorGroupService));
|
||||
extHostContext.set(MainContext.MainThreadTextEditors, mainThreadTextEditors);
|
||||
@@ -367,7 +368,7 @@ export class MainThreadDocumentsAndEditors {
|
||||
// added editors
|
||||
for (const apiEditor of delta.addedEditors) {
|
||||
const mainThreadEditor = new MainThreadTextEditor(apiEditor.id, apiEditor.editor.getModel(),
|
||||
apiEditor.editor, { onGainedFocus() { }, onLostFocus() { } }, this._modelService, this._clipboardService);
|
||||
apiEditor.editor, { onGainedFocus() { }, onLostFocus() { } }, this._mainThreadDocuments, this._modelService, this._clipboardService);
|
||||
|
||||
this._textEditors.set(apiEditor.id, mainThreadEditor);
|
||||
addedEditors.push(mainThreadEditor);
|
||||
|
||||
@@ -20,6 +20,7 @@ import { equals } from 'vs/base/common/arrays';
|
||||
import { CodeEditorStateFlag, EditorState } from 'vs/editor/browser/core/editorState';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser';
|
||||
import { MainThreadDocuments } from 'vs/workbench/api/browser/mainThreadDocuments';
|
||||
|
||||
export interface IFocusTracker {
|
||||
onGainedFocus(): void;
|
||||
@@ -160,7 +161,8 @@ export class MainThreadTextEditorProperties {
|
||||
export class MainThreadTextEditor {
|
||||
|
||||
private readonly _id: string;
|
||||
private _model: ITextModel;
|
||||
private readonly _model: ITextModel;
|
||||
private readonly _mainThreadDocuments: MainThreadDocuments;
|
||||
private readonly _modelService: IModelService;
|
||||
private readonly _clipboardService: IClipboardService;
|
||||
private readonly _modelListeners = new DisposableStore();
|
||||
@@ -176,6 +178,7 @@ export class MainThreadTextEditor {
|
||||
model: ITextModel,
|
||||
codeEditor: ICodeEditor,
|
||||
focusTracker: IFocusTracker,
|
||||
mainThreadDocuments: MainThreadDocuments,
|
||||
modelService: IModelService,
|
||||
clipboardService: IClipboardService,
|
||||
) {
|
||||
@@ -184,6 +187,7 @@ export class MainThreadTextEditor {
|
||||
this._codeEditor = null;
|
||||
this._properties = null;
|
||||
this._focusTracker = focusTracker;
|
||||
this._mainThreadDocuments = mainThreadDocuments;
|
||||
this._modelService = modelService;
|
||||
this._clipboardService = clipboardService;
|
||||
|
||||
@@ -198,7 +202,6 @@ export class MainThreadTextEditor {
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._model = null!;
|
||||
this._modelListeners.dispose();
|
||||
this._codeEditor = null;
|
||||
this._codeEditorListeners.dispose();
|
||||
@@ -257,21 +260,46 @@ export class MainThreadTextEditor {
|
||||
this._focusTracker.onLostFocus();
|
||||
}));
|
||||
|
||||
let nextSelectionChangeSource: string | null = null;
|
||||
this._codeEditorListeners.add(this._mainThreadDocuments.onIsCaughtUpWithContentChanges((uri) => {
|
||||
if (uri.toString() === this._model.uri.toString()) {
|
||||
const selectionChangeSource = nextSelectionChangeSource;
|
||||
nextSelectionChangeSource = null;
|
||||
this._updatePropertiesNow(selectionChangeSource);
|
||||
}
|
||||
}));
|
||||
|
||||
const updateProperties = (selectionChangeSource: string | null) => {
|
||||
// Some editor events get delivered faster than model content changes. This is
|
||||
// problematic, as this leads to editor properties reaching the extension host
|
||||
// too soon, before the model content change that was the root cause.
|
||||
//
|
||||
// If this case is identified, then let's update editor properties on the next model
|
||||
// content change instead.
|
||||
if (this._mainThreadDocuments.isCaughtUpWithContentChanges(this._model.uri)) {
|
||||
nextSelectionChangeSource = null;
|
||||
this._updatePropertiesNow(selectionChangeSource);
|
||||
} else {
|
||||
// update editor properties on the next model content change
|
||||
nextSelectionChangeSource = selectionChangeSource;
|
||||
}
|
||||
};
|
||||
|
||||
this._codeEditorListeners.add(this._codeEditor.onDidChangeCursorSelection((e) => {
|
||||
// selection
|
||||
this._updatePropertiesNow(e.source);
|
||||
updateProperties(e.source);
|
||||
}));
|
||||
this._codeEditorListeners.add(this._codeEditor.onDidChangeConfiguration(() => {
|
||||
// options
|
||||
this._updatePropertiesNow(null);
|
||||
updateProperties(null);
|
||||
}));
|
||||
this._codeEditorListeners.add(this._codeEditor.onDidLayoutChange(() => {
|
||||
// visibleRanges
|
||||
this._updatePropertiesNow(null);
|
||||
updateProperties(null);
|
||||
}));
|
||||
this._codeEditorListeners.add(this._codeEditor.onDidScrollChange(() => {
|
||||
// visibleRanges
|
||||
this._updatePropertiesNow(null);
|
||||
updateProperties(null);
|
||||
}));
|
||||
this._updatePropertiesNow(null);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
|
||||
export class MainThreadTextEditors implements MainThreadTextEditorsShape {
|
||||
|
||||
@@ -276,7 +278,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
|
||||
|
||||
// --- commands
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.open', function (accessor: ServicesAccessor, args: [URI, IEditorOptions, EditorViewColumn, string?]) {
|
||||
CommandsRegistry.registerCommand('_workbench.open', async function (accessor: ServicesAccessor, args: [URI, IEditorOptions, EditorViewColumn, string?]) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const openerService = accessor.get(IOpenerService);
|
||||
@@ -285,16 +287,17 @@ CommandsRegistry.registerCommand('_workbench.open', function (accessor: Services
|
||||
|
||||
if (options || typeof position === 'number') {
|
||||
// use editor options or editor view column as a hint to use the editor service for opening
|
||||
return editorService.openEditor({ resource, options, label }, viewColumnToEditorGroup(editorGroupService, position)).then(_ => undefined);
|
||||
await editorService.openEditor({ resource, options, label }, viewColumnToEditorGroup(editorGroupService, position));
|
||||
return;
|
||||
}
|
||||
|
||||
if (resource && resource.scheme === 'command') {
|
||||
// do not allow to execute commands from here
|
||||
return Promise.resolve(undefined);
|
||||
return;
|
||||
|
||||
}
|
||||
// finally, delegate to opener service
|
||||
return openerService.open(resource).then(_ => undefined);
|
||||
await openerService.open(resource);
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAccessor, args: [URI, string, ITextEditorOptions | undefined, EditorViewColumn | undefined]) => {
|
||||
@@ -313,7 +316,7 @@ CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAcces
|
||||
});
|
||||
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.diff', function (accessor: ServicesAccessor, args: [URI, URI, string, string, IEditorOptions, EditorViewColumn]) {
|
||||
CommandsRegistry.registerCommand('_workbench.diff', async function (accessor: ServicesAccessor, args: [URI, URI, string, string, IEditorOptions, EditorViewColumn]) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
|
||||
@@ -329,5 +332,17 @@ CommandsRegistry.registerCommand('_workbench.diff', function (accessor: Services
|
||||
label = localize('diffLeftRightLabel', "{0} ⟷ {1}", leftResource.toString(true), rightResource.toString(true));
|
||||
}
|
||||
|
||||
return editorService.openEditor({ leftResource, rightResource, label, description, options }, viewColumnToEditorGroup(editorGroupService, position)).then(() => undefined);
|
||||
await editorService.openEditor({ leftResource, rightResource, label, description, options }, viewColumnToEditorGroup(editorGroupService, position));
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.revertAllDirty', async function (accessor: ServicesAccessor) {
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
if (!environmentService.extensionTestsLocationURI) {
|
||||
throw new Error('Command is only available when running extension tests.');
|
||||
}
|
||||
|
||||
const workingCopyService = accessor.get(IWorkingCopyService);
|
||||
for (const workingCopy of workingCopyService.dirtyWorkingCopies) {
|
||||
await workingCopy.revert({ soft: true });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -30,12 +30,14 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
|
||||
$setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation): void {
|
||||
// if there are icons in the text use the tooltip for the aria label
|
||||
let ariaLabel: string;
|
||||
let role: string | undefined = undefined;
|
||||
if (accessibilityInformation) {
|
||||
ariaLabel = accessibilityInformation.label;
|
||||
role = accessibilityInformation.role;
|
||||
} else {
|
||||
ariaLabel = text ? text.replace(MainThreadStatusBar.CODICON_REGEXP, (_match, codiconName) => codiconName) : '';
|
||||
}
|
||||
const entry: IStatusbarEntry = { text, tooltip, command, color, ariaLabel };
|
||||
const entry: IStatusbarEntry = { text, tooltip, command, color, ariaLabel, role };
|
||||
|
||||
if (typeof priority === 'undefined') {
|
||||
priority = 0;
|
||||
|
||||
@@ -7,7 +7,6 @@ import * as nls from 'vs/nls';
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as Objects from 'vs/base/common/objects';
|
||||
import * as Types from 'vs/base/common/types';
|
||||
import * as Platform from 'vs/base/common/platform';
|
||||
import { IStringDictionary, forEach } from 'vs/base/common/collections';
|
||||
@@ -64,7 +63,7 @@ namespace TaskProcessEndedDTO {
|
||||
|
||||
namespace TaskDefinitionDTO {
|
||||
export function from(value: KeyedTaskIdentifier): TaskDefinitionDTO {
|
||||
const result = Objects.assign(Object.create(null), value);
|
||||
const result = Object.assign(Object.create(null), value);
|
||||
delete result._key;
|
||||
return result;
|
||||
}
|
||||
@@ -85,13 +84,13 @@ namespace TaskPresentationOptionsDTO {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return Objects.assign(Object.create(null), value);
|
||||
return Object.assign(Object.create(null), value);
|
||||
}
|
||||
export function to(value: TaskPresentationOptionsDTO | undefined): PresentationOptions {
|
||||
if (value === undefined || value === null) {
|
||||
return PresentationOptions.defaults;
|
||||
}
|
||||
return Objects.assign(Object.create(null), PresentationOptions.defaults, value);
|
||||
return Object.assign(Object.create(null), PresentationOptions.defaults, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,13 +99,13 @@ namespace RunOptionsDTO {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return Objects.assign(Object.create(null), value);
|
||||
return Object.assign(Object.create(null), value);
|
||||
}
|
||||
export function to(value: RunOptionsDTO | undefined): RunOptions {
|
||||
if (value === undefined || value === null) {
|
||||
return RunOptions.defaults;
|
||||
}
|
||||
return Objects.assign(Object.create(null), RunOptions.defaults, value);
|
||||
return Object.assign(Object.create(null), RunOptions.defaults, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +367,7 @@ namespace TaskDTO {
|
||||
|
||||
const label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
|
||||
const definition = TaskDefinitionDTO.to(task.definition, executeOnly)!;
|
||||
const id = `${task.source.extensionId}.${definition._key}`;
|
||||
const id = (CustomExecutionDTO.is(task.execution!) && task._id) ? task._id : `${task.source.extensionId}.${definition._key}`;
|
||||
const result: ContributedTask = new ContributedTask(
|
||||
id, // uuidMap.getUUID(identifier)
|
||||
source,
|
||||
@@ -534,20 +533,47 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
}
|
||||
}
|
||||
|
||||
public $executeTask(value: TaskDTO): Promise<TaskExecutionDTO> {
|
||||
// Passing in a TaskHandleDTO will cause the task to get re-resolved, which is important for tasks are coming from the core,
|
||||
// such as those gotten from a fetchTasks, since they can have missing configuration properties.
|
||||
public $executeTask(value: TaskHandleDTO | TaskDTO): Promise<TaskExecutionDTO> {
|
||||
return new Promise<TaskExecutionDTO>((resolve, reject) => {
|
||||
const task = TaskDTO.to(value, this._workspaceContextServer, true)!;
|
||||
this._taskService.run(task).then(undefined, reason => {
|
||||
// eat the error, it has already been surfaced to the user and we don't care about it here
|
||||
});
|
||||
const result: TaskExecutionDTO = {
|
||||
id: task._id,
|
||||
task: TaskDTO.from(task)
|
||||
};
|
||||
resolve(result);
|
||||
if (TaskHandleDTO.is(value)) {
|
||||
const workspaceFolder = typeof value.workspaceFolder === 'string' ? value.workspaceFolder : this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder));
|
||||
if (workspaceFolder) {
|
||||
this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task | undefined) => {
|
||||
if (!task) {
|
||||
reject(new Error('Task not found'));
|
||||
} else {
|
||||
this._taskService.run(task).then(undefined, reason => {
|
||||
// eat the error, it has already been surfaced to the user and we don't care about it here
|
||||
});
|
||||
const result: TaskExecutionDTO = {
|
||||
id: value.id,
|
||||
task: TaskDTO.from(task)
|
||||
};
|
||||
resolve(result);
|
||||
}
|
||||
}, (_error) => {
|
||||
reject(new Error('Task not found'));
|
||||
});
|
||||
} else {
|
||||
reject(new Error('No workspace folder'));
|
||||
}
|
||||
} else {
|
||||
const task = TaskDTO.to(value, this._workspaceContextServer, true)!;
|
||||
this._taskService.run(task).then(undefined, reason => {
|
||||
// eat the error, it has already been surfaced to the user and we don't care about it here
|
||||
});
|
||||
const result: TaskExecutionDTO = {
|
||||
id: task._id,
|
||||
task: TaskDTO.from(task)
|
||||
};
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public $customExecutionComplete(id: string, result?: number): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this._taskService.getActiveTasks().then((tasks) => {
|
||||
@@ -642,6 +668,9 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
},
|
||||
getDefaultShellAndArgs: (): Promise<{ shell: string, args: string[] | string | undefined }> => {
|
||||
return Promise.resolve(this._proxy.$getDefaultShellAndArgs());
|
||||
},
|
||||
findExecutable: (command: string, cwd?: string, paths?: string[]): Promise<string | undefined> => {
|
||||
return this._proxy.$findExecutable(command, cwd, paths);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
import { checkGlobFileExists } from 'vs/workbench/api/common/shared/workspaceContains';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
|
||||
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
@@ -188,26 +189,8 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
return search;
|
||||
}
|
||||
|
||||
$checkExists(folders: UriComponents[], includes: string[], token: CancellationToken): Promise<boolean> {
|
||||
const queryBuilder = this._instantiationService.createInstance(QueryBuilder);
|
||||
const query = queryBuilder.file(folders.map(folder => toWorkspaceFolder(URI.revive(folder))), {
|
||||
_reason: 'checkExists',
|
||||
includePattern: includes.join(', '),
|
||||
expandPatterns: true,
|
||||
exists: true
|
||||
});
|
||||
|
||||
return this._searchService.fileSearch(query, token).then(
|
||||
result => {
|
||||
return !!result.limitHit;
|
||||
},
|
||||
err => {
|
||||
if (!isPromiseCanceledError(err)) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
$checkExists(folders: readonly UriComponents[], includes: string[], token: CancellationToken): Promise<boolean> {
|
||||
return this._instantiationService.invokeFunction((accessor) => checkGlobFileExists(accessor, folders, includes, token));
|
||||
}
|
||||
|
||||
// --- save & edit resources ---
|
||||
|
||||
Reference in New Issue
Block a user