mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-12 11:08:31 -05:00
Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 (#8962)
* Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 * skip failing tests * update mac build image
This commit is contained in:
committed by
Karl Burtram
parent
0eaee18dc4
commit
fefe1454de
@@ -5,22 +5,27 @@
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import * as nls from 'vs/nls';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService';
|
||||
import { ExtHostAuthenticationShape, ExtHostContext, IExtHostContext, MainContext, MainThreadAuthenticationShape } from '../common/extHost.protocol';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
|
||||
export class MainThreadAuthenticationProvider {
|
||||
constructor(
|
||||
private readonly _proxy: ExtHostAuthenticationShape,
|
||||
public readonly id: string
|
||||
public readonly id: string,
|
||||
public readonly displayName: string
|
||||
) { }
|
||||
|
||||
getSessions(): Promise<ReadonlyArray<modes.Session>> {
|
||||
return this._proxy.$getSessions(this.id);
|
||||
}
|
||||
|
||||
login(): Promise<modes.Session> {
|
||||
return this._proxy.$login(this.id);
|
||||
login(scopes: string[]): Promise<modes.Session> {
|
||||
return this._proxy.$login(this.id, scopes);
|
||||
}
|
||||
|
||||
logout(accountId: string): Promise<void> {
|
||||
@@ -35,13 +40,15 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IStorageService private readonly storageService: IStorageService
|
||||
) {
|
||||
super();
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication);
|
||||
}
|
||||
|
||||
$registerAuthenticationProvider(id: string): void {
|
||||
const provider = new MainThreadAuthenticationProvider(this._proxy, id);
|
||||
$registerAuthenticationProvider(id: string, displayName: string): void {
|
||||
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName);
|
||||
this.authenticationService.registerAuthenticationProvider(id, provider);
|
||||
}
|
||||
|
||||
@@ -49,7 +56,55 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
this.authenticationService.unregisterAuthenticationProvider(id);
|
||||
}
|
||||
|
||||
$onDidChangeSessions(id: string) {
|
||||
$onDidChangeSessions(id: string): void {
|
||||
this.authenticationService.sessionsUpdate(id);
|
||||
}
|
||||
|
||||
async $getSessionsPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
|
||||
const alwaysAllow = this.storageService.get(`${extensionId}-${providerId}`, StorageScope.GLOBAL);
|
||||
if (alwaysAllow) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { choice } = await this.dialogService.show(
|
||||
Severity.Info,
|
||||
nls.localize('confirmAuthenticationAccess', "The extension '{0}' is trying to access authentication information from {1}.", extensionName, providerName),
|
||||
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow"), nls.localize('alwaysAllow', "Always Allow"),],
|
||||
{ cancelId: 0 }
|
||||
);
|
||||
|
||||
switch (choice) {
|
||||
case 1/** Allow */:
|
||||
return true;
|
||||
case 2 /** Always Allow */:
|
||||
this.storageService.store(`${extensionId}-${providerId}`, 'true', StorageScope.GLOBAL);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async $loginPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
|
||||
const alwaysAllow = this.storageService.get(`${extensionId}-${providerId}`, StorageScope.GLOBAL);
|
||||
if (alwaysAllow) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { choice } = await this.dialogService.show(
|
||||
Severity.Info,
|
||||
nls.localize('confirmLogin', "The extension '{0}' wants to sign in using {1}.", extensionName, providerName),
|
||||
[nls.localize('cancel', "Cancel"), nls.localize('continue', "Continue"), nls.localize('neverAgain', "Don't Show Again")],
|
||||
{ cancelId: 0 }
|
||||
);
|
||||
|
||||
switch (choice) {
|
||||
case 1/** Allow */:
|
||||
return true;
|
||||
case 2 /** Always Allow */:
|
||||
this.storageService.store(`${extensionId}-${providerId}`, 'true', StorageScope.GLOBAL);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape {
|
||||
private deriveConfigurationTarget(key: string, overrides: IConfigurationOverrides): ConfigurationTarget {
|
||||
if (overrides.resource && this._workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
||||
const configurationProperties = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
|
||||
if (configurationProperties[key] && (configurationProperties[key].scope === ConfigurationScope.RESOURCE || configurationProperties[key].scope === ConfigurationScope.RESOURCE_LANGUAGE)) {
|
||||
if (configurationProperties[key] && (configurationProperties[key].scope === ConfigurationScope.RESOURCE || configurationProperties[key].scope === ConfigurationScope.LANGUAGE_OVERRIDABLE)) {
|
||||
return ConfigurationTarget.WORKSPACE_FOLDER;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@ export class MainThreadDiagnostics implements MainThreadDiagnosticsShape {
|
||||
relatedInformation.resource = URI.revive(relatedInformation.resource);
|
||||
}
|
||||
}
|
||||
if (marker.code && typeof marker.code !== 'string') {
|
||||
marker.code.link = URI.revive(marker.code.link);
|
||||
}
|
||||
}
|
||||
}
|
||||
this._markerService.changeOne(owner, URI.revive(uri), markers);
|
||||
|
||||
@@ -160,7 +160,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
|
||||
// --- from extension host process
|
||||
|
||||
$trySaveDocument(uri: UriComponents): Promise<boolean> {
|
||||
return this._textFileService.save(URI.revive(uri));
|
||||
return this._textFileService.save(URI.revive(uri)).then(target => !!target);
|
||||
}
|
||||
|
||||
$tryOpenDocument(_uri: UriComponents): Promise<any> {
|
||||
|
||||
@@ -216,7 +216,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
|
||||
}
|
||||
|
||||
$tryApplyWorkspaceEdit(dto: IWorkspaceEditDto): Promise<boolean> {
|
||||
const { edits } = reviveWorkspaceEditDto(dto);
|
||||
const { edits } = reviveWorkspaceEditDto(dto)!;
|
||||
return this._bulkEditService.apply({ edits }).then(() => true, _err => false);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
@@ -27,7 +27,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
|
||||
private readonly _notificationService: INotificationService;
|
||||
private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService;
|
||||
private readonly _hostService: IHostService;
|
||||
private readonly _extensionEnablementService: IExtensionEnablementService;
|
||||
private readonly _extensionEnablementService: IWorkbenchExtensionEnablementService;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@@ -35,7 +35,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
|
||||
@INotificationService notificationService: INotificationService,
|
||||
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IHostService hostService: IHostService,
|
||||
@IExtensionEnablementService extensionEnablementService: IExtensionEnablementService
|
||||
@IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService
|
||||
) {
|
||||
this._extensionService = extensionService;
|
||||
this._notificationService = notificationService;
|
||||
|
||||
@@ -11,7 +11,7 @@ import * as search from 'vs/workbench/contrib/search/common/search';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Position as EditorPosition } from 'vs/editor/common/core/position';
|
||||
import { Range as EditorRange, IRange } from 'vs/editor/common/core/range';
|
||||
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto, ISuggestDataDtoField } from '../common/extHost.protocol';
|
||||
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto, ISuggestDataDtoField, ISuggestResultDtoField } from '../common/extHost.protocol';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
@@ -316,7 +316,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
|
||||
$registerRenameSupport(handle: number, selector: IDocumentFilterDto[], supportResolveLocation: boolean): void {
|
||||
this._registrations.set(handle, modes.RenameProviderRegistry.register(selector, <modes.RenameProvider>{
|
||||
provideRenameEdits: (model: ITextModel, position: EditorPosition, newName: string, token: CancellationToken): Promise<modes.WorkspaceEdit> => {
|
||||
provideRenameEdits: (model: ITextModel, position: EditorPosition, newName: string, token: CancellationToken) => {
|
||||
return this._proxy.$provideRenameEdits(handle, model.uri, position, newName, token).then(reviveWorkspaceEditDto);
|
||||
},
|
||||
resolveRenameLocation: supportResolveLocation
|
||||
@@ -340,7 +340,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
private static _inflateSuggestDto(defaultRange: IRange | { insert: IRange, replace: IRange }, data: ISuggestDataDto): modes.CompletionItem {
|
||||
|
||||
return {
|
||||
label: data[ISuggestDataDtoField.label],
|
||||
label: data[ISuggestDataDtoField.label2] || data[ISuggestDataDtoField.label],
|
||||
kind: data[ISuggestDataDtoField.kind],
|
||||
tags: data[ISuggestDataDtoField.kindModifier],
|
||||
detail: data[ISuggestDataDtoField.detail],
|
||||
@@ -369,11 +369,15 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
// {{SQL CARBON EDIT}} @todo anthonydresser 4/12/19 required because of strict null checks
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
suggestions: result.b.map(d => MainThreadLanguageFeatures._inflateSuggestDto(result.a, d)),
|
||||
incomplete: result.c,
|
||||
isDetailsResolved: result.d,
|
||||
dispose: () => typeof result.x === 'number' && this._proxy.$releaseCompletionItems(handle, result.x)
|
||||
suggestions: result[ISuggestResultDtoField.completions].map(d => MainThreadLanguageFeatures._inflateSuggestDto(result[ISuggestResultDtoField.defaultRanges], d)),
|
||||
incomplete: result[ISuggestResultDtoField.isIncomplete] || false,
|
||||
dispose: () => {
|
||||
if (typeof result.x === 'number') {
|
||||
this._proxy.$releaseCompletionItems(handle, result.x);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, Transf
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
interface QuickInputSession {
|
||||
input: IQuickInput;
|
||||
@@ -185,14 +186,22 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
|
||||
return this._quickInputService.backButton;
|
||||
}
|
||||
const { iconPath, tooltip, handle } = button;
|
||||
return {
|
||||
iconPath: iconPath && {
|
||||
dark: URI.revive(iconPath.dark),
|
||||
light: iconPath.light && URI.revive(iconPath.light)
|
||||
},
|
||||
tooltip,
|
||||
handle
|
||||
};
|
||||
if ('id' in iconPath) {
|
||||
return {
|
||||
iconClass: ThemeIcon.asClassName(iconPath),
|
||||
tooltip,
|
||||
handle
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
iconPath: {
|
||||
dark: URI.revive(iconPath.dark),
|
||||
light: iconPath.light && URI.revive(iconPath.light)
|
||||
},
|
||||
tooltip,
|
||||
handle
|
||||
};
|
||||
}
|
||||
});
|
||||
} else {
|
||||
(input as any)[param] = params[param];
|
||||
|
||||
@@ -33,23 +33,16 @@ import { ISaveParticipant, IResolvedTextFileEditorModel, ITextFileEditorModel }
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../common/extHost.protocol';
|
||||
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService'; // {{SQL CARBON EDIT}}
|
||||
import { IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/common/statusbar';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { SettingsEditor2 } from 'vs/workbench/contrib/preferences/browser/settingsEditor2';
|
||||
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { canceled, isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
|
||||
export interface ICodeActionsOnSaveOptions {
|
||||
[kind: string]: boolean;
|
||||
}
|
||||
|
||||
class SaveParticipantError extends Error {
|
||||
constructor(message: string, readonly setting?: string) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* An update participant that ensures any un-tracked changes are synced to the JSON file contents for a
|
||||
* Notebook before save occurs. While every effort is made to ensure model changes are notified and a listener
|
||||
@@ -363,7 +356,7 @@ class ExtHostSaveParticipant implements ISaveParticipantParticipant {
|
||||
token.onCancellationRequested(() => reject(canceled()));
|
||||
|
||||
setTimeout(
|
||||
() => reject(new SaveParticipantError(localize('timeout.onWillSave', "Aborted onWillSaveTextDocument-event after 1750ms"))),
|
||||
() => reject(new Error(localize('timeout.onWillSave', "Aborted onWillSaveTextDocument-event after 1750ms"))),
|
||||
1750
|
||||
);
|
||||
this._proxy.$participateInSave(editorModel.resource, env.reason).then(values => {
|
||||
@@ -386,7 +379,6 @@ export class SaveParticipant implements ISaveParticipant {
|
||||
extHostContext: IExtHostContext,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IProgressService private readonly _progressService: IProgressService,
|
||||
@IStatusbarService private readonly _statusbarService: IStatusbarService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
) {
|
||||
@@ -420,54 +412,23 @@ export class SaveParticipant implements ISaveParticipant {
|
||||
delay: model.isDirty() ? 3000 : 5000
|
||||
}, async progress => {
|
||||
|
||||
let firstError: SaveParticipantError | undefined;
|
||||
for (let p of this._saveParticipants.getValue()) {
|
||||
|
||||
if (cts.token.isCancellationRequested) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
await p.participate(model, env, progress, cts.token);
|
||||
|
||||
} catch (err) {
|
||||
if (!isPromiseCanceledError(err)) {
|
||||
this._logService.warn(err);
|
||||
firstError = !firstError && err instanceof SaveParticipantError ? err : firstError;
|
||||
}
|
||||
this._logService.warn(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (firstError) {
|
||||
this._showParticipantError(firstError);
|
||||
}
|
||||
|
||||
}, () => {
|
||||
// user cancel
|
||||
cts.dispose(true);
|
||||
});
|
||||
}
|
||||
|
||||
private _showParticipantError(err: SaveParticipantError): void {
|
||||
|
||||
let entry: any = {
|
||||
text: localize('title', "$(error) Save Participants Failed: {0}", err.message)
|
||||
};
|
||||
if (err.setting) {
|
||||
entry.command = '_showSettings';
|
||||
entry.arguments = [err.setting];
|
||||
}
|
||||
|
||||
const handle = this._statusbarService.addEntry(
|
||||
entry,
|
||||
'saveParticipants.error',
|
||||
localize('status.message', "Save Participants Errors"),
|
||||
StatusbarAlignment.LEFT,
|
||||
-Number.MAX_VALUE /* far right on left hand side */
|
||||
);
|
||||
|
||||
setTimeout(() => handle.dispose(), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand('_showSettings', (accessor, ...args: any[]) => {
|
||||
|
||||
@@ -8,9 +8,11 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ITunnelProvider, ITunnelService, TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import type { TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTunnelService)
|
||||
export class MainThreadTunnelService implements MainThreadTunnelServiceShape {
|
||||
export class MainThreadTunnelService extends Disposable implements MainThreadTunnelServiceShape {
|
||||
private readonly _proxy: ExtHostTunnelServiceShape;
|
||||
|
||||
constructor(
|
||||
@@ -18,7 +20,10 @@ export class MainThreadTunnelService implements MainThreadTunnelServiceShape {
|
||||
@IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService,
|
||||
@ITunnelService private readonly tunnelService: ITunnelService
|
||||
) {
|
||||
super();
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTunnelService);
|
||||
this._register(tunnelService.onTunnelOpened(() => this._proxy.$onDidTunnelsChange()));
|
||||
this._register(tunnelService.onTunnelClosed(() => this._proxy.$onDidTunnelsChange()));
|
||||
}
|
||||
|
||||
async $openTunnel(tunnelOptions: TunnelOptions): Promise<TunnelDto | undefined> {
|
||||
@@ -33,6 +38,15 @@ export class MainThreadTunnelService implements MainThreadTunnelServiceShape {
|
||||
return this.remoteExplorerService.close(remote);
|
||||
}
|
||||
|
||||
async $getTunnels(): Promise<TunnelDescription[]> {
|
||||
return (await this.tunnelService.tunnels).map(tunnel => {
|
||||
return {
|
||||
remoteAddress: { port: tunnel.tunnelRemotePort, host: tunnel.tunnelRemoteHost },
|
||||
localAddress: tunnel.localAddress
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async $registerCandidateFinder(): Promise<void> {
|
||||
this.remoteExplorerService.registerCandidateFinder(() => this._proxy.$findCandidatePorts());
|
||||
}
|
||||
@@ -59,7 +73,23 @@ export class MainThreadTunnelService implements MainThreadTunnelServiceShape {
|
||||
this.tunnelService.setTunnelProvider(tunnelProvider);
|
||||
}
|
||||
|
||||
async $setCandidateFilter(): Promise<void> {
|
||||
this._register(this.remoteExplorerService.setCandidateFilter(async (candidates: { host: string, port: number, detail: string }[]): Promise<{ host: string, port: number, detail: string }[]> => {
|
||||
const filters: boolean[] = await this._proxy.$filterCandidates(candidates);
|
||||
const filteredCandidates: { host: string, port: number, detail: string }[] = [];
|
||||
if (filters.length !== candidates.length) {
|
||||
return candidates;
|
||||
}
|
||||
for (let i = 0; i < candidates.length; i++) {
|
||||
if (filters[i]) {
|
||||
filteredCandidates.push(candidates[i]);
|
||||
}
|
||||
}
|
||||
return filteredCandidates;
|
||||
}));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
//
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createCancelablePromise } from 'vs/base/common/async';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
@@ -306,6 +307,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
|
||||
provider.dispose();
|
||||
this._editorProviders.delete(viewType);
|
||||
|
||||
this._customEditorService.models.disposeAllModelsForView(viewType);
|
||||
}
|
||||
|
||||
private async retainCustomEditorModel(webviewInput: WebviewInput, resource: URI, viewType: string, capabilities: readonly extHostProtocol.WebviewEditorCapabilities[]) {
|
||||
@@ -323,21 +326,23 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
const capabilitiesSet = new Set(capabilities);
|
||||
const isEditable = capabilitiesSet.has(extHostProtocol.WebviewEditorCapabilities.Editable);
|
||||
if (isEditable) {
|
||||
model.onUndo(edits => {
|
||||
this._proxy.$undoEdits(resource, viewType, edits.map(x => x.data));
|
||||
model.onUndo(e => {
|
||||
this._proxy.$undoEdits(resource, viewType, e.edits);
|
||||
});
|
||||
|
||||
model.onApplyEdit(edits => {
|
||||
const editsToApply = edits.filter(x => x.source !== model).map(x => x.data);
|
||||
if (editsToApply.length) {
|
||||
this._proxy.$applyEdits(resource, viewType, editsToApply);
|
||||
model.onDisposeEdits(e => {
|
||||
this._proxy.$disposeEdits(e.edits);
|
||||
});
|
||||
|
||||
model.onApplyEdit(e => {
|
||||
if (e.trigger !== model) {
|
||||
this._proxy.$applyEdits(resource, viewType, e.edits);
|
||||
}
|
||||
});
|
||||
|
||||
model.onWillSave(e => {
|
||||
e.waitUntil(this._proxy.$onSave(resource.toJSON(), viewType));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Save as should always be implemented even if the model is readonly
|
||||
@@ -349,6 +354,14 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
e.waitUntil(this._fileService.copy(e.resource, e.targetResource, false /* overwrite */));
|
||||
}
|
||||
});
|
||||
|
||||
if (capabilitiesSet.has(extHostProtocol.WebviewEditorCapabilities.SupportsHotExit)) {
|
||||
model.onBackup(() => {
|
||||
return createCancelablePromise(token =>
|
||||
this._proxy.$backup(model.resource.toJSON(), viewType, token));
|
||||
});
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -365,13 +378,13 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
}
|
||||
}
|
||||
|
||||
public $onEdit(resource: UriComponents, viewType: string, editData: any): void {
|
||||
public $onEdit(resource: UriComponents, viewType: string, editId: number): void {
|
||||
const model = this._customEditorService.models.get(URI.revive(resource), viewType);
|
||||
if (!model) {
|
||||
throw new Error('Could not find model for webview editor');
|
||||
}
|
||||
|
||||
model.pushEdit({ source: model, data: editData });
|
||||
model.pushEdit(editId, model);
|
||||
}
|
||||
|
||||
private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput) {
|
||||
|
||||
@@ -23,14 +23,7 @@ import { VIEWLET_ID as REMOTE } from 'vs/workbench/contrib/remote/common/remote.
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ViewletRegistry, Extensions as ViewletExtensions, ShowViewletAction } from 'vs/workbench/browser/viewlet';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
@@ -313,27 +306,13 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
|
||||
if (!viewContainer) {
|
||||
|
||||
|
||||
class CustomViewPaneContainer extends ViewPaneContainer {
|
||||
constructor(
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IWorkspaceContextService protected contextService: IWorkspaceContextService,
|
||||
@IStorageService protected storageService: IStorageService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IInstantiationService protected instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
) {
|
||||
super(id, `${id}.state`, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService);
|
||||
}
|
||||
}
|
||||
|
||||
viewContainer = this.viewContainersRegistry.registerViewContainer({
|
||||
id,
|
||||
name: title, extensionId,
|
||||
ctorDescriptor: new SyncDescriptor(CustomViewPaneContainer),
|
||||
ctorDescriptor: new SyncDescriptor(
|
||||
ViewPaneContainer,
|
||||
[id, `${id}.state`, { mergeViewWithContainerWhenSingleView: true }]
|
||||
),
|
||||
hideIfEmpty: true,
|
||||
icon,
|
||||
}, ViewContainerLocation.Sidebar);
|
||||
|
||||
39
src/vs/workbench/api/common/cache.ts
Normal file
39
src/vs/workbench/api/common/cache.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export class Cache<T> {
|
||||
|
||||
private static readonly enableDebugLogging = false;
|
||||
|
||||
private readonly _data = new Map<number, readonly T[]>();
|
||||
private _idPool = 1;
|
||||
|
||||
constructor(
|
||||
private readonly id: string
|
||||
) { }
|
||||
|
||||
add(item: readonly T[]): number {
|
||||
const id = this._idPool++;
|
||||
this._data.set(id, item);
|
||||
this.logDebugInfo();
|
||||
return id;
|
||||
}
|
||||
|
||||
get(pid: number, id: number): T | undefined {
|
||||
return this._data.has(pid) ? this._data.get(pid)![id] : undefined;
|
||||
}
|
||||
|
||||
delete(id: number) {
|
||||
this._data.delete(id);
|
||||
this.logDebugInfo();
|
||||
}
|
||||
|
||||
private logDebugInfo() {
|
||||
if (!Cache.enableDebugLogging) {
|
||||
return;
|
||||
}
|
||||
console.log(`${this.id} cache size — ${this._data.size}`);
|
||||
}
|
||||
}
|
||||
@@ -39,13 +39,14 @@ const configurationEntrySchema: IJSONSchema = {
|
||||
},
|
||||
scope: {
|
||||
type: 'string',
|
||||
enum: ['application', 'machine', 'window', 'resource', 'machine-overridable'],
|
||||
enum: ['application', 'machine', 'window', 'resource', 'language-overridable', 'machine-overridable'],
|
||||
default: 'window',
|
||||
enumDescriptions: [
|
||||
nls.localize('scope.application.description', "Configuration that can be configured only in the user settings."),
|
||||
nls.localize('scope.machine.description', "Configuration that can be configured only in the user settings when the extension is running locally, or only in the remote settings when the extension is running remotely."),
|
||||
nls.localize('scope.window.description', "Configuration that can be configured in the user, remote or workspace settings."),
|
||||
nls.localize('scope.resource.description', "Configuration that can be configured in the user, remote, workspace or folder settings."),
|
||||
nls.localize('scope.language-overridable.description', "Resource configuration that can be configured in language specific settings."),
|
||||
nls.localize('scope.machine-overridable.description', "Machine configuration that can be configured also in workspace or folder settings.")
|
||||
],
|
||||
description: nls.localize('scope.description', "Scope in which the configuration is applicable. Available scopes are `application`, `machine`, `window`, `resource`, and `machine-overridable`.")
|
||||
@@ -218,8 +219,8 @@ function validateProperties(configuration: IConfigurationNode, extension: IExten
|
||||
propertyConfiguration.scope = ConfigurationScope.RESOURCE;
|
||||
} else if (propertyConfiguration.scope.toString() === 'machine-overridable') {
|
||||
propertyConfiguration.scope = ConfigurationScope.MACHINE_OVERRIDABLE;
|
||||
} else if (propertyConfiguration.scope.toString() === 'resource-language') {
|
||||
propertyConfiguration.scope = ConfigurationScope.RESOURCE_LANGUAGE;
|
||||
} else if (propertyConfiguration.scope.toString() === 'language-overridable') {
|
||||
propertyConfiguration.scope = ConfigurationScope.LANGUAGE_OVERRIDABLE;
|
||||
} else {
|
||||
propertyConfiguration.scope = ConfigurationScope.WINDOW;
|
||||
}
|
||||
|
||||
@@ -186,17 +186,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable {
|
||||
return extHostAuthentication.registerAuthenticationProvider(provider);
|
||||
},
|
||||
login(providerId: string): Promise<vscode.Session> {
|
||||
return extHostAuthentication.$login(providerId);
|
||||
},
|
||||
logout(providerId: string, accountId: string): Promise<void> {
|
||||
return extHostAuthentication.$logout(providerId, accountId);
|
||||
},
|
||||
getSessions(providerId: string): Promise<ReadonlyArray<vscode.Session>> {
|
||||
return extHostAuthentication.$getSessions(providerId);
|
||||
},
|
||||
get onDidChangeSessions() {
|
||||
return extHostAuthentication.onDidChangeSessions;
|
||||
get providers() {
|
||||
return extHostAuthentication.providers(extension);
|
||||
},
|
||||
get onDidRegisterAuthenticationProvider() {
|
||||
return extHostAuthentication.onDidRegisterAuthenticationProvider;
|
||||
@@ -765,6 +756,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
openTunnel: (forward: vscode.TunnelOptions) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTunnelService.openTunnel(forward);
|
||||
},
|
||||
get tunnels() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTunnelService.getTunnels();
|
||||
},
|
||||
onDidTunnelsChange: (listener, thisArg?, disposables?) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTunnelService.onDidTunnelsChange(listener, thisArg, disposables);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import { LogLevel } from 'vs/platform/log/common/log';
|
||||
import { IMarkerData } from 'vs/platform/markers/common/markers';
|
||||
import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress';
|
||||
import * as quickInput from 'vs/platform/quickinput/common/quickInput';
|
||||
import { RemoteAuthorityResolverErrorCode, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { RemoteAuthorityResolverErrorCode, ResolverResult, TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import * as statusbar from 'vs/workbench/services/statusbar/common/statusbar';
|
||||
import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings';
|
||||
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
|
||||
@@ -152,9 +152,11 @@ export interface MainThreadCommentsShape extends IDisposable {
|
||||
}
|
||||
|
||||
export interface MainThreadAuthenticationShape extends IDisposable {
|
||||
$registerAuthenticationProvider(id: string): void;
|
||||
$registerAuthenticationProvider(id: string, displayName: string): void;
|
||||
$unregisterAuthenticationProvider(id: string): void;
|
||||
$onDidChangeSessions(id: string): void;
|
||||
$getSessionsPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
|
||||
$loginPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface MainThreadConfigurationShape extends IDisposable {
|
||||
@@ -441,8 +443,10 @@ export interface TransferQuickPickItems extends quickInput.IQuickPickItem {
|
||||
handle: number;
|
||||
}
|
||||
|
||||
export interface TransferQuickInputButton extends quickInput.IQuickInputButton {
|
||||
export interface TransferQuickInputButton {
|
||||
handle: number;
|
||||
iconPath: { dark: URI; light?: URI; } | { id: string; };
|
||||
tooltip?: string;
|
||||
}
|
||||
|
||||
export type TransferQuickInput = TransferQuickPick | TransferInputBox;
|
||||
@@ -567,6 +571,7 @@ export interface WebviewExtensionDescription {
|
||||
|
||||
export enum WebviewEditorCapabilities {
|
||||
Editable,
|
||||
SupportsHotExit,
|
||||
}
|
||||
|
||||
export interface MainThreadWebviewsShape extends IDisposable {
|
||||
@@ -587,7 +592,7 @@ export interface MainThreadWebviewsShape extends IDisposable {
|
||||
$registerEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: readonly WebviewEditorCapabilities[]): void;
|
||||
$unregisterEditorProvider(viewType: string): void;
|
||||
|
||||
$onEdit(resource: UriComponents, viewType: string, editJson: any): void;
|
||||
$onEdit(resource: UriComponents, viewType: string, editId: number): void;
|
||||
}
|
||||
|
||||
export interface WebviewPanelViewStateData {
|
||||
@@ -607,11 +612,14 @@ export interface ExtHostWebviewsShape {
|
||||
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
|
||||
$undoEdits(resource: UriComponents, viewType: string, edits: readonly any[]): void;
|
||||
$applyEdits(resource: UriComponents, viewType: string, edits: readonly any[]): void;
|
||||
$undoEdits(resource: UriComponents, viewType: string, editIds: readonly number[]): void;
|
||||
$applyEdits(resource: UriComponents, viewType: string, editIds: readonly number[]): void;
|
||||
$disposeEdits(editIds: readonly number[]): void;
|
||||
|
||||
$onSave(resource: UriComponents, viewType: string): Promise<void>;
|
||||
$onSaveAs(resource: UriComponents, viewType: string, targetResource: UriComponents): Promise<void>;
|
||||
|
||||
$backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface MainThreadUrlsShape extends IDisposable {
|
||||
@@ -787,8 +795,10 @@ export interface MainThreadWindowShape extends IDisposable {
|
||||
export interface MainThreadTunnelServiceShape extends IDisposable {
|
||||
$openTunnel(tunnelOptions: TunnelOptions): Promise<TunnelDto | undefined>;
|
||||
$closeTunnel(remote: { host: string, port: number }): Promise<void>;
|
||||
$getTunnels(): Promise<TunnelDescription[]>;
|
||||
$registerCandidateFinder(): Promise<void>;
|
||||
$setTunnelProvider(): Promise<void>;
|
||||
$setCandidateFilter(): Promise<void>;
|
||||
}
|
||||
|
||||
// -- extension host
|
||||
@@ -905,8 +915,8 @@ export interface ExtHostLabelServiceShape {
|
||||
|
||||
export interface ExtHostAuthenticationShape {
|
||||
$getSessions(id: string): Promise<ReadonlyArray<modes.Session>>;
|
||||
$login(id: string): Promise<modes.Session>;
|
||||
$logout(id: string, accountId: string): Promise<void>;
|
||||
$login(id: string, scopes: string[]): Promise<modes.Session>;
|
||||
$logout(id: string, sessionId: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface ExtHostSearchShape {
|
||||
@@ -1004,10 +1014,14 @@ export const enum ISuggestDataDtoField {
|
||||
additionalTextEdits = 'l',
|
||||
command = 'm',
|
||||
kindModifier = 'n',
|
||||
|
||||
// to merge into label
|
||||
label2 = 'o',
|
||||
}
|
||||
|
||||
export interface ISuggestDataDto {
|
||||
[ISuggestDataDtoField.label]: string;
|
||||
[ISuggestDataDtoField.label2]?: string | modes.CompletionItemLabel;
|
||||
[ISuggestDataDtoField.kind]: modes.CompletionItemKind;
|
||||
[ISuggestDataDtoField.detail]?: string;
|
||||
[ISuggestDataDtoField.documentation]?: string | IMarkdownString;
|
||||
@@ -1025,12 +1039,17 @@ export interface ISuggestDataDto {
|
||||
x?: ChainedCacheId;
|
||||
}
|
||||
|
||||
export const enum ISuggestResultDtoField {
|
||||
defaultRanges = 'a',
|
||||
completions = 'b',
|
||||
isIncomplete = 'c'
|
||||
}
|
||||
|
||||
export interface ISuggestResultDto {
|
||||
[ISuggestResultDtoField.defaultRanges]: { insert: IRange, replace: IRange; };
|
||||
[ISuggestResultDtoField.completions]: ISuggestDataDto[];
|
||||
[ISuggestResultDtoField.isIncomplete]: undefined | true;
|
||||
x?: number;
|
||||
a: { insert: IRange, replace: IRange; };
|
||||
b: ISuggestDataDto[];
|
||||
c?: true;
|
||||
d?: true;
|
||||
}
|
||||
|
||||
export interface ISignatureHelpDto {
|
||||
@@ -1070,25 +1089,22 @@ export interface IWorkspaceSymbolsDto extends IdObject {
|
||||
symbols: IWorkspaceSymbolDto[];
|
||||
}
|
||||
|
||||
export interface IResourceFileEditDto {
|
||||
export interface IWorkspaceFileEditDto {
|
||||
oldUri?: UriComponents;
|
||||
newUri?: UriComponents;
|
||||
options?: {
|
||||
overwrite?: boolean;
|
||||
ignoreIfExists?: boolean;
|
||||
ignoreIfNotExists?: boolean;
|
||||
recursive?: boolean;
|
||||
};
|
||||
options?: modes.WorkspaceFileEditOptions
|
||||
metadata?: modes.WorkspaceEditMetadata;
|
||||
}
|
||||
|
||||
export interface IResourceTextEditDto {
|
||||
export interface IWorkspaceTextEditDto {
|
||||
resource: UriComponents;
|
||||
edit: modes.TextEdit;
|
||||
modelVersionId?: number;
|
||||
edits: modes.TextEdit[];
|
||||
metadata?: modes.WorkspaceEditMetadata;
|
||||
}
|
||||
|
||||
export interface IWorkspaceEditDto {
|
||||
edits: Array<IResourceFileEditDto | IResourceTextEditDto>;
|
||||
edits: Array<IWorkspaceFileEditDto | IWorkspaceTextEditDto>;
|
||||
|
||||
// todo@joh reject should go into rename
|
||||
rejectReason?: string;
|
||||
@@ -1097,11 +1113,11 @@ export interface IWorkspaceEditDto {
|
||||
export function reviveWorkspaceEditDto(data: IWorkspaceEditDto | undefined): modes.WorkspaceEdit {
|
||||
if (data && data.edits) {
|
||||
for (const edit of data.edits) {
|
||||
if (typeof (<IResourceTextEditDto>edit).resource === 'object') {
|
||||
(<IResourceTextEditDto>edit).resource = URI.revive((<IResourceTextEditDto>edit).resource);
|
||||
if (typeof (<IWorkspaceTextEditDto>edit).resource === 'object') {
|
||||
(<IWorkspaceTextEditDto>edit).resource = URI.revive((<IWorkspaceTextEditDto>edit).resource);
|
||||
} else {
|
||||
(<IResourceFileEditDto>edit).newUri = URI.revive((<IResourceFileEditDto>edit).newUri);
|
||||
(<IResourceFileEditDto>edit).oldUri = URI.revive((<IResourceFileEditDto>edit).oldUri);
|
||||
(<IWorkspaceFileEditDto>edit).newUri = URI.revive((<IWorkspaceFileEditDto>edit).newUri);
|
||||
(<IWorkspaceFileEditDto>edit).oldUri = URI.revive((<IWorkspaceFileEditDto>edit).oldUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1423,8 +1439,10 @@ export interface MainThreadThemingShape extends IDisposable {
|
||||
|
||||
export interface ExtHostTunnelServiceShape {
|
||||
$findCandidatePorts(): Promise<{ host: string, port: number, detail: string }[]>;
|
||||
$filterCandidates(candidates: { host: string, port: number, detail: string }[]): Promise<boolean[]>;
|
||||
$forwardPort(tunnelOptions: TunnelOptions): Promise<TunnelDto> | undefined;
|
||||
$closeTunnel(remote: { host: string, port: number }): Promise<void>;
|
||||
$onDidTunnelsChange(): Promise<void>;
|
||||
}
|
||||
|
||||
// --- proxy identifiers
|
||||
|
||||
@@ -7,75 +7,96 @@ import * as vscode from 'vscode';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IMainContext, MainContext, MainThreadAuthenticationShape, ExtHostAuthenticationShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
const _onDidUnregisterAuthenticationProvider = new Emitter<string>();
|
||||
const _onDidChangeSessions = new Emitter<string>();
|
||||
export class AuthenticationProviderWrapper implements vscode.AuthenticationProvider {
|
||||
onDidChangeSessions: Event<void>;
|
||||
|
||||
export class ExtHostAuthenticationProvider implements IDisposable {
|
||||
constructor(private _provider: vscode.AuthenticationProvider,
|
||||
private _id: string,
|
||||
constructor(private _requestingExtension: IExtensionDescription,
|
||||
private _provider: vscode.AuthenticationProvider,
|
||||
private _proxy: MainThreadAuthenticationShape) {
|
||||
this._provider.onDidChangeSessions(x => {
|
||||
this._proxy.$onDidChangeSessions(this._id);
|
||||
_onDidChangeSessions.fire(this._id);
|
||||
});
|
||||
|
||||
this.onDidChangeSessions = this._provider.onDidChangeSessions;
|
||||
}
|
||||
|
||||
getSessions(): Promise<ReadonlyArray<vscode.Session>> {
|
||||
get id(): string {
|
||||
return this._provider.id;
|
||||
}
|
||||
|
||||
get displayName(): string {
|
||||
return this._provider.displayName;
|
||||
}
|
||||
|
||||
async getSessions(): Promise<ReadonlyArray<vscode.Session>> {
|
||||
const isAllowed = await this._proxy.$getSessionsPrompt(this._provider.id, this.displayName, ExtensionIdentifier.toKey(this._requestingExtension.identifier), this._requestingExtension.displayName || this._requestingExtension.name);
|
||||
if (!isAllowed) {
|
||||
throw new Error('User did not consent to session access.');
|
||||
}
|
||||
|
||||
return this._provider.getSessions();
|
||||
}
|
||||
|
||||
login(): Promise<vscode.Session> {
|
||||
return this._provider.login();
|
||||
async login(scopes: string[]): Promise<vscode.Session> {
|
||||
const isAllowed = await this._proxy.$loginPrompt(this._provider.id, this.displayName, ExtensionIdentifier.toKey(this._requestingExtension.identifier), this._requestingExtension.displayName || this._requestingExtension.name);
|
||||
if (!isAllowed) {
|
||||
throw new Error('User did not consent to login.');
|
||||
}
|
||||
|
||||
return this._provider.login(scopes);
|
||||
}
|
||||
|
||||
logout(sessionId: string): Promise<void> {
|
||||
return this._provider.logout(sessionId);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._proxy.$unregisterAuthenticationProvider(this._id);
|
||||
_onDidUnregisterAuthenticationProvider.fire(this._id);
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
private _proxy: MainThreadAuthenticationShape;
|
||||
private _authenticationProviders: Map<string, ExtHostAuthenticationProvider> = new Map<string, ExtHostAuthenticationProvider>();
|
||||
private _authenticationProviders: Map<string, vscode.AuthenticationProvider> = new Map<string, vscode.AuthenticationProvider>();
|
||||
|
||||
private _onDidRegisterAuthenticationProvider = new Emitter<string>();
|
||||
readonly onDidRegisterAuthenticationProvider: Event<string> = this._onDidRegisterAuthenticationProvider.event;
|
||||
|
||||
readonly onDidUnregisterAuthenticationProvider: Event<string> = _onDidUnregisterAuthenticationProvider.event;
|
||||
|
||||
readonly onDidChangeSessions: Event<string> = _onDidChangeSessions.event;
|
||||
private _onDidUnregisterAuthenticationProvider = new Emitter<string>();
|
||||
readonly onDidUnregisterAuthenticationProvider: Event<string> = this._onDidUnregisterAuthenticationProvider.event;
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadAuthentication);
|
||||
|
||||
this.onDidUnregisterAuthenticationProvider(providerId => {
|
||||
this._authenticationProviders.delete(providerId);
|
||||
});
|
||||
}
|
||||
|
||||
registerAuthenticationProvider(provider: vscode.AuthenticationProvider) {
|
||||
providers(requestingExtension: IExtensionDescription): vscode.AuthenticationProvider[] {
|
||||
let providers: vscode.AuthenticationProvider[] = [];
|
||||
this._authenticationProviders.forEach(provider => providers.push(new AuthenticationProviderWrapper(requestingExtension, provider, this._proxy)));
|
||||
return providers;
|
||||
}
|
||||
|
||||
registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable {
|
||||
if (this._authenticationProviders.get(provider.id)) {
|
||||
throw new Error(`An authentication provider with id '${provider.id}' is already registered.`);
|
||||
}
|
||||
|
||||
const authenticationProvider = new ExtHostAuthenticationProvider(provider, provider.id, this._proxy);
|
||||
this._authenticationProviders.set(provider.id, authenticationProvider);
|
||||
this._authenticationProviders.set(provider.id, provider);
|
||||
|
||||
this._proxy.$registerAuthenticationProvider(provider.id);
|
||||
const listener = provider.onDidChangeSessions(_ => {
|
||||
this._proxy.$onDidChangeSessions(provider.id);
|
||||
});
|
||||
|
||||
this._proxy.$registerAuthenticationProvider(provider.id, provider.displayName);
|
||||
this._onDidRegisterAuthenticationProvider.fire(provider.id);
|
||||
return authenticationProvider;
|
||||
|
||||
return new Disposable(() => {
|
||||
listener.dispose();
|
||||
this._authenticationProviders.delete(provider.id);
|
||||
this._proxy.$unregisterAuthenticationProvider(provider.id);
|
||||
this._onDidUnregisterAuthenticationProvider.fire(provider.id);
|
||||
});
|
||||
}
|
||||
|
||||
$login(providerId: string): Promise<modes.Session> {
|
||||
$login(providerId: string, scopes: string[]): Promise<modes.Session> {
|
||||
const authProvider = this._authenticationProviders.get(providerId);
|
||||
if (authProvider) {
|
||||
return Promise.resolve(authProvider.login());
|
||||
return Promise.resolve(authProvider.login(scopes));
|
||||
}
|
||||
|
||||
throw new Error(`Unable to find authentication provider with handle: ${0}`);
|
||||
|
||||
@@ -46,7 +46,7 @@ type ConfigurationInspect<T> = {
|
||||
workspaceLanguageValue?: T;
|
||||
workspaceFolderLanguageValue?: T;
|
||||
|
||||
languages?: string[];
|
||||
languageIds?: string[];
|
||||
};
|
||||
|
||||
function isUri(thing: any): thing is vscode.Uri {
|
||||
@@ -85,6 +85,9 @@ function scopeToOverrides(scope: vscode.ConfigurationScope | undefined | null):
|
||||
if (isWorkspaceFolder(scope)) {
|
||||
return { resource: scope.uri };
|
||||
}
|
||||
if (scope === null) {
|
||||
return { resource: null };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -267,7 +270,7 @@ export class ExtHostConfigProvider {
|
||||
workspaceLanguageValue: config.workspace?.override,
|
||||
workspaceFolderLanguageValue: config.workspaceFolder?.override,
|
||||
|
||||
languages: config.overrideIdentifiers
|
||||
languageIds: config.overrideIdentifiers
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
@@ -301,7 +304,7 @@ export class ExtHostConfigProvider {
|
||||
const scope = OVERRIDE_PROPERTY_PATTERN.test(key) ? ConfigurationScope.RESOURCE : this._configurationScopes.get(key);
|
||||
const extensionIdText = extensionId ? `[${extensionId.value}] ` : '';
|
||||
if (ConfigurationScope.RESOURCE === scope) {
|
||||
if (overrides?.resource) {
|
||||
if (typeof overrides?.resource === 'undefined') {
|
||||
this._logService.warn(`${extensionIdText}Accessing a resource scoped configuration without providing a resource is not expected. To get the effective value for '${key}', provide the URI of a resource or 'null' for any resource.`);
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/event';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { sequence } from 'vs/base/common/async';
|
||||
import { illegalState } from 'vs/base/common/errors';
|
||||
import { ExtHostDocumentSaveParticipantShape, MainThreadTextEditorsShape, IResourceTextEditDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostDocumentSaveParticipantShape, MainThreadTextEditorsShape, IWorkspaceEditDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { TextEdit } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { Range, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
@@ -141,19 +141,17 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
|
||||
});
|
||||
|
||||
}).then(values => {
|
||||
|
||||
const resourceEdit: IResourceTextEditDto = {
|
||||
resource: document.uri,
|
||||
edits: []
|
||||
};
|
||||
|
||||
const dto: IWorkspaceEditDto = { edits: [] };
|
||||
for (const value of values) {
|
||||
if (Array.isArray(value) && (<vscode.TextEdit[]>value).every(e => e instanceof TextEdit)) {
|
||||
for (const { newText, newEol, range } of value) {
|
||||
resourceEdit.edits.push({
|
||||
range: range && Range.from(range),
|
||||
text: newText,
|
||||
eol: newEol && EndOfLine.from(newEol)
|
||||
dto.edits.push({
|
||||
resource: document.uri,
|
||||
edit: {
|
||||
range: range && Range.from(range),
|
||||
text: newText,
|
||||
eol: newEol && EndOfLine.from(newEol)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -161,12 +159,12 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
|
||||
|
||||
// apply edits if any and if document
|
||||
// didn't change somehow in the meantime
|
||||
if (resourceEdit.edits.length === 0) {
|
||||
if (dto.edits.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (version === document.version) {
|
||||
return this._mainThreadEditors.$tryApplyWorkspaceEdit({ edits: [resourceEdit] });
|
||||
return this._mainThreadEditors.$tryApplyWorkspaceEdit(dto);
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('concurrent_edits'));
|
||||
|
||||
@@ -645,7 +645,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
|
||||
|
||||
try {
|
||||
const result = await resolver.resolve(remoteAuthority, { resolveAttempt });
|
||||
this._disposables.add(await this._extHostTunnelService.setForwardPortProvider(resolver));
|
||||
this._disposables.add(await this._extHostTunnelService.setTunnelExtensionFunctions(resolver));
|
||||
|
||||
// Split merged API result into separate authority/options
|
||||
const authority: ResolvedAuthority = {
|
||||
@@ -662,7 +662,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
|
||||
value: {
|
||||
authority,
|
||||
options,
|
||||
tunnelInformation: { environmentTunnels: result.environmentTunnels, hideCandidatePorts: result.hideCandidatePorts }
|
||||
tunnelInformation: { environmentTunnels: result.environmentTunnels }
|
||||
}
|
||||
};
|
||||
} catch (err) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { IRelativePattern, parse } from 'vs/base/common/glob';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import type * as vscode from 'vscode';
|
||||
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, MainThreadTextEditorsShape, IResourceFileEditDto, IResourceTextEditDto } from './extHost.protocol';
|
||||
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, MainThreadTextEditorsShape, IWorkspaceFileEditDto, IWorkspaceTextEditDto } from './extHost.protocol';
|
||||
import * as typeConverter from './extHostTypeConverters';
|
||||
import { Disposable, WorkspaceEdit } from './extHostTypes';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
@@ -219,7 +219,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
|
||||
if (edits.length > 0) {
|
||||
// flatten all WorkspaceEdits collected via waitUntil-call
|
||||
// and apply them in one go.
|
||||
const allEdits = new Array<Array<IResourceFileEditDto | IResourceTextEditDto>>();
|
||||
const allEdits = new Array<Array<IWorkspaceFileEditDto | IWorkspaceTextEditDto>>();
|
||||
for (let edit of edits) {
|
||||
let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
|
||||
allEdits.push(edits);
|
||||
|
||||
@@ -30,6 +30,7 @@ import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { encodeSemanticTokensDto } from 'vs/workbench/api/common/shared/semanticTokens';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
import { Cache } from './cache';
|
||||
|
||||
// --- adapter
|
||||
|
||||
@@ -818,20 +819,20 @@ class SuggestAdapter {
|
||||
const disposables = new DisposableStore();
|
||||
this._disposables.set(pid, disposables);
|
||||
|
||||
const completions: extHostProtocol.ISuggestDataDto[] = [];
|
||||
const result: extHostProtocol.ISuggestResultDto = {
|
||||
x: pid,
|
||||
b: [],
|
||||
a: { replace: typeConvert.Range.from(replaceRange), insert: typeConvert.Range.from(insertRange) },
|
||||
c: list.isIncomplete || undefined,
|
||||
d: list.isDetailsResolved || undefined
|
||||
[extHostProtocol.ISuggestResultDtoField.completions]: completions,
|
||||
[extHostProtocol.ISuggestResultDtoField.defaultRanges]: { replace: typeConvert.Range.from(replaceRange), insert: typeConvert.Range.from(insertRange) },
|
||||
[extHostProtocol.ISuggestResultDtoField.isIncomplete]: list.isIncomplete || undefined
|
||||
};
|
||||
|
||||
for (let i = 0; i < list.items.length; i++) {
|
||||
const suggestion = this._convertCompletionItem(list.items[i], pos, [pid, i]);
|
||||
// check for bad completion item
|
||||
// for the converter did warn
|
||||
if (suggestion) {
|
||||
result.b.push(suggestion);
|
||||
const item = list.items[i];
|
||||
// check for bad completion item first
|
||||
if (this._validateCompletionItem(item, pos)) {
|
||||
const dto = this._convertCompletionItem(item, [pid, i], insertRange, replaceRange);
|
||||
completions.push(dto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -850,12 +851,13 @@ class SuggestAdapter {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
const pos = typeConvert.Position.to(position);
|
||||
const _mustNotChange = SuggestAdapter._mustNotChangeHash(item);
|
||||
const _mayNotChange = SuggestAdapter._mayNotChangeHash(item);
|
||||
|
||||
return asPromise(() => this._provider.resolveCompletionItem!(item, token)).then(resolvedItem => {
|
||||
|
||||
if (!resolvedItem) {
|
||||
if (!resolvedItem || !this._validateCompletionItem(resolvedItem, pos)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -874,19 +876,18 @@ class SuggestAdapter {
|
||||
let _mustNotChangeIndex = !this._didWarnMust && SuggestAdapter._mustNotChangeDiff(_mustNotChange, resolvedItem);
|
||||
if (typeof _mustNotChangeIndex === 'string') {
|
||||
this._logService.warn(`[${this._extension.identifier.value}] INVALID result from 'resolveCompletionItem', extension MUST NOT change any of: label, sortText, filterText, insertText, or textEdit`);
|
||||
this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('resolveCompletionItem/invalid', { extensionId: this._extension.identifier.value, kind: 'must', index: _mustNotChangeIndex });
|
||||
this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('badresolvecompletion', { extensionId: this._extension.identifier.value, kind: 'must', index: _mustNotChangeIndex });
|
||||
this._didWarnMust = true;
|
||||
}
|
||||
|
||||
let _mayNotChangeIndex = !this._didWarnShould && SuggestAdapter._mayNotChangeDiff(_mayNotChange, resolvedItem);
|
||||
if (typeof _mayNotChangeIndex === 'string') {
|
||||
this._logService.info(`[${this._extension.identifier.value}] UNSAVE result from 'resolveCompletionItem', extension SHOULD NOT change any of: additionalTextEdits, or command`);
|
||||
this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('resolveCompletionItem/invalid', { extensionId: this._extension.identifier.value, kind: 'should', index: _mayNotChangeIndex });
|
||||
this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('badresolvecompletion', { extensionId: this._extension.identifier.value, kind: 'should', index: _mayNotChangeIndex });
|
||||
this._didWarnShould = true;
|
||||
}
|
||||
|
||||
const pos = typeConvert.Position.to(position);
|
||||
return this._convertCompletionItem(resolvedItem, pos, id);
|
||||
return this._convertCompletionItem(resolvedItem, id);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -896,11 +897,7 @@ class SuggestAdapter {
|
||||
this._cache.delete(id);
|
||||
}
|
||||
|
||||
private _convertCompletionItem(item: vscode.CompletionItem, position: vscode.Position, id: extHostProtocol.ChainedCacheId): extHostProtocol.ISuggestDataDto | undefined {
|
||||
if (typeof item.label !== 'string' || item.label.length === 0) {
|
||||
this._logService.warn('INVALID text edit -> must have at least a label');
|
||||
return undefined;
|
||||
}
|
||||
private _convertCompletionItem(item: vscode.CompletionItem, id: extHostProtocol.ChainedCacheId, defaultInsertRange?: vscode.Range, defaultReplaceRange?: vscode.Range): extHostProtocol.ISuggestDataDto {
|
||||
|
||||
const disposables = this._disposables.get(id[0]);
|
||||
if (!disposables) {
|
||||
@@ -912,6 +909,7 @@ class SuggestAdapter {
|
||||
x: id,
|
||||
//
|
||||
[extHostProtocol.ISuggestDataDtoField.label]: item.label,
|
||||
[extHostProtocol.ISuggestDataDtoField.label2]: item.label2,
|
||||
[extHostProtocol.ISuggestDataDtoField.kind]: typeConvert.CompletionItemKind.from(item.kind),
|
||||
[extHostProtocol.ISuggestDataDtoField.kindModifier]: item.tags && item.tags.map(typeConvert.CompletionItemTag.from),
|
||||
[extHostProtocol.ISuggestDataDtoField.detail]: item.detail,
|
||||
@@ -927,9 +925,7 @@ class SuggestAdapter {
|
||||
|
||||
// 'insertText'-logic
|
||||
if (item.textEdit) {
|
||||
this._apiDeprecation.report('CompletionItem.textEdit', this._extension,
|
||||
`Use 'CompletionItem.insertText' and 'CompletionItem.range' instead.`);
|
||||
|
||||
this._apiDeprecation.report('CompletionItem.textEdit', this._extension, `Use 'CompletionItem.insertText' and 'CompletionItem.range' instead.`);
|
||||
result[extHostProtocol.ISuggestDataDtoField.insertText] = item.textEdit.newText;
|
||||
|
||||
} else if (typeof item.insertText === 'string') {
|
||||
@@ -948,36 +944,44 @@ class SuggestAdapter {
|
||||
range = item.range;
|
||||
}
|
||||
|
||||
if (range) {
|
||||
if (Range.isRange(range)) {
|
||||
if (!SuggestAdapter._isValidRangeForCompletion(range, position)) {
|
||||
this._logService.trace('INVALID range -> must be single line and on the same line');
|
||||
return undefined;
|
||||
}
|
||||
result[extHostProtocol.ISuggestDataDtoField.range] = typeConvert.Range.from(range);
|
||||
if (Range.isRange(range)) {
|
||||
// "old" range
|
||||
result[extHostProtocol.ISuggestDataDtoField.range] = typeConvert.Range.from(range);
|
||||
|
||||
} else {
|
||||
if (
|
||||
!SuggestAdapter._isValidRangeForCompletion(range.inserting, position)
|
||||
|| !SuggestAdapter._isValidRangeForCompletion(range.replacing, position)
|
||||
|| !range.inserting.start.isEqual(range.replacing.start)
|
||||
|| !range.replacing.contains(range.inserting)
|
||||
) {
|
||||
this._logService.trace('INVALID range -> must be single line, on the same line, insert range must be a prefix of replace range');
|
||||
return undefined;
|
||||
}
|
||||
result[extHostProtocol.ISuggestDataDtoField.range] = {
|
||||
insert: typeConvert.Range.from(range.inserting),
|
||||
replace: typeConvert.Range.from(range.replacing)
|
||||
};
|
||||
}
|
||||
} else if (range && !defaultInsertRange?.isEqual(range.inserting) && !defaultReplaceRange?.isEqual(range.replacing)) {
|
||||
// ONLY send range when it's different from the default ranges (safe bandwidth)
|
||||
result[extHostProtocol.ISuggestDataDtoField.range] = {
|
||||
insert: typeConvert.Range.from(range.inserting),
|
||||
replace: typeConvert.Range.from(range.replacing)
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static _isValidRangeForCompletion(range: vscode.Range, position: vscode.Position): boolean {
|
||||
return range.isSingleLine || range.start.line === position.line;
|
||||
private _validateCompletionItem(item: vscode.CompletionItem, position: vscode.Position): boolean {
|
||||
if (typeof item.label !== 'string' || item.label.length === 0) {
|
||||
this._logService.warn('INVALID text edit -> must have at least a label');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Range.isRange(item.range)) {
|
||||
if (!item.range.isSingleLine || item.range.start.line !== position.line) {
|
||||
this._logService.trace('INVALID range -> must be single line and on the same line');
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (item.range) {
|
||||
if (!item.range.inserting.isSingleLine || item.range.inserting.start.line !== position.line
|
||||
|| !item.range.replacing.isSingleLine || item.range.replacing.start.line !== position.line
|
||||
|| !item.range.inserting.start.isEqual(item.range.replacing.start)
|
||||
|| !item.range.replacing.contains(item.range.inserting)
|
||||
) {
|
||||
this._logService.trace('INVALID range -> must be single line, on the same line, insert range must be a prefix of replace range');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static _mustNotChangeHash(item: vscode.CompletionItem) {
|
||||
@@ -1064,40 +1068,6 @@ class SignatureHelpAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
class Cache<T> {
|
||||
private static readonly enableDebugLogging = false;
|
||||
|
||||
private readonly _data = new Map<number, readonly T[]>();
|
||||
private _idPool = 1;
|
||||
|
||||
constructor(
|
||||
private readonly id: string
|
||||
) { }
|
||||
|
||||
add(item: readonly T[]): number {
|
||||
const id = this._idPool++;
|
||||
this._data.set(id, item);
|
||||
this.logDebugInfo();
|
||||
return id;
|
||||
}
|
||||
|
||||
get(pid: number, id: number): T | undefined {
|
||||
return this._data.has(pid) ? this._data.get(pid)![id] : undefined;
|
||||
}
|
||||
|
||||
delete(id: number) {
|
||||
this._data.delete(id);
|
||||
this.logDebugInfo();
|
||||
}
|
||||
|
||||
private logDebugInfo() {
|
||||
if (!Cache.enableDebugLogging) {
|
||||
return;
|
||||
}
|
||||
console.log(`${this.id} cache size — ${this._data.size}`);
|
||||
}
|
||||
}
|
||||
|
||||
class LinkProviderAdapter {
|
||||
|
||||
private _cache = new Cache<vscode.DocumentLink>('DocumentLink');
|
||||
|
||||
@@ -443,31 +443,21 @@ class ExtHostQuickInput implements QuickInput {
|
||||
}
|
||||
}
|
||||
|
||||
function getIconUris(iconPath: QuickInputButton['iconPath']): { dark: URI, light?: URI } | undefined {
|
||||
const dark = getDarkIconUri(iconPath);
|
||||
const light = getLightIconUri(iconPath);
|
||||
if (!light && !dark) {
|
||||
return undefined;
|
||||
function getIconUris(iconPath: QuickInputButton['iconPath']): { dark: URI, light?: URI } | { id: string } {
|
||||
if (iconPath instanceof ThemeIcon) {
|
||||
return { id: iconPath.id };
|
||||
}
|
||||
return { dark: (dark || light)!, light };
|
||||
const dark = getDarkIconUri(iconPath as any);
|
||||
const light = getLightIconUri(iconPath as any);
|
||||
return { dark, light };
|
||||
}
|
||||
|
||||
function getLightIconUri(iconPath: QuickInputButton['iconPath']) {
|
||||
if (iconPath && !(iconPath instanceof ThemeIcon)) {
|
||||
if (typeof iconPath === 'string'
|
||||
|| URI.isUri(iconPath)) {
|
||||
return getIconUri(iconPath);
|
||||
}
|
||||
return getIconUri((iconPath as any).light);
|
||||
}
|
||||
return undefined;
|
||||
function getLightIconUri(iconPath: string | URI | { light: URI; dark: URI; }) {
|
||||
return getIconUri(typeof iconPath === 'object' && 'light' in iconPath ? iconPath.light : iconPath);
|
||||
}
|
||||
|
||||
function getDarkIconUri(iconPath: QuickInputButton['iconPath']) {
|
||||
if (iconPath && !(iconPath instanceof ThemeIcon) && (iconPath as any).dark) {
|
||||
return getIconUri((iconPath as any).dark);
|
||||
}
|
||||
return undefined;
|
||||
function getDarkIconUri(iconPath: string | URI | { light: URI; dark: URI; }) {
|
||||
return getIconUri(typeof iconPath === 'object' && 'dark' in iconPath ? iconPath.dark : iconPath);
|
||||
}
|
||||
|
||||
function getIconUri(iconPath: string | URI) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import * as Platform from 'vs/base/common/platform';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
|
||||
export interface IExtHostTask extends ExtHostTaskShape {
|
||||
|
||||
@@ -376,6 +377,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
|
||||
protected readonly _configurationService: IExtHostConfiguration;
|
||||
protected readonly _terminalService: IExtHostTerminalService;
|
||||
protected readonly _logService: ILogService;
|
||||
protected readonly _deprecationService: IExtHostApiDeprecationService;
|
||||
protected _handleCounter: number;
|
||||
protected _handlers: Map<number, HandlerData>;
|
||||
protected _taskExecutions: Map<string, TaskExecutionImpl>;
|
||||
@@ -396,7 +398,8 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
|
||||
@IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService,
|
||||
@ILogService logService: ILogService
|
||||
@ILogService logService: ILogService,
|
||||
@IExtHostApiDeprecationService deprecationService: IExtHostApiDeprecationService
|
||||
) {
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTask);
|
||||
this._workspaceProvider = workspaceService;
|
||||
@@ -410,6 +413,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
|
||||
this._notProvidedCustomExecutions = new Set<string>();
|
||||
this._activeCustomExecutions2 = new Map<string, types.CustomExecution>();
|
||||
this._logService = logService;
|
||||
this._deprecationService = deprecationService;
|
||||
}
|
||||
|
||||
public registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable {
|
||||
@@ -576,6 +580,8 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
}
|
||||
|
||||
this.checkDeprecation(resolvedTask, handler);
|
||||
|
||||
const resolvedTaskDTO: tasks.TaskDTO | undefined = TaskDTO.from(resolvedTask, handler.extension);
|
||||
if (!resolvedTaskDTO) {
|
||||
throw new Error('Unexpected: Task cannot be resolved.');
|
||||
@@ -630,6 +636,13 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
|
||||
return createdResult;
|
||||
}
|
||||
|
||||
protected checkDeprecation(task: vscode.Task, handler: HandlerData) {
|
||||
const tTask = (task as types.Task);
|
||||
if (tTask._deprecated) {
|
||||
this._deprecationService.report('Task.constructor', handler.extension, 'Use the Task constructor that takes a `scope` instead.');
|
||||
}
|
||||
}
|
||||
|
||||
private customExecutionComplete(execution: tasks.TaskExecutionDTO): void {
|
||||
const extensionCallback2: vscode.CustomExecution | undefined = this._activeCustomExecutions2.get(execution.id);
|
||||
if (extensionCallback2) {
|
||||
@@ -666,9 +679,10 @@ export class WorkerExtHostTask extends ExtHostTaskBase {
|
||||
@IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService,
|
||||
@ILogService logService: ILogService
|
||||
@ILogService logService: ILogService,
|
||||
@IExtHostApiDeprecationService deprecationService: IExtHostApiDeprecationService
|
||||
) {
|
||||
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService, logService);
|
||||
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService, logService, deprecationService);
|
||||
if (initData.remote.isRemote && initData.remote.authority) {
|
||||
this.registerTaskSystem(Schemas.vscodeRemote, {
|
||||
scheme: Schemas.vscodeRemote,
|
||||
@@ -700,6 +714,7 @@ export class WorkerExtHostTask extends ExtHostTaskBase {
|
||||
const taskDTOs: tasks.TaskDTO[] = [];
|
||||
if (value) {
|
||||
for (let task of value) {
|
||||
this.checkDeprecation(task, handler);
|
||||
if (!task.definition || !validTypes[task.definition.type]) {
|
||||
this._logService.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ export class ExtHostTreeView<T> extends Disposable {
|
||||
result.message = true;
|
||||
}
|
||||
return result;
|
||||
}, 200)(({ message, elements }) => {
|
||||
}, 200, true)(({ message, elements }) => {
|
||||
if (elements.length) {
|
||||
this.refreshQueue = this.refreshQueue.then(() => {
|
||||
const _promiseCallback = promiseCallback;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
||||
import type * as vscode from 'vscode';
|
||||
import { RemoteTunnel, TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
export interface TunnelDto {
|
||||
remoteAddress: { port: number, host: string };
|
||||
@@ -31,21 +32,31 @@ export interface Tunnel extends vscode.Disposable {
|
||||
export interface IExtHostTunnelService extends ExtHostTunnelServiceShape {
|
||||
readonly _serviceBrand: undefined;
|
||||
openTunnel(forward: TunnelOptions): Promise<vscode.Tunnel | undefined>;
|
||||
setForwardPortProvider(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable>;
|
||||
getTunnels(): Promise<vscode.TunnelDescription[]>;
|
||||
onDidTunnelsChange: vscode.Event<void>;
|
||||
setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable>;
|
||||
}
|
||||
|
||||
export const IExtHostTunnelService = createDecorator<IExtHostTunnelService>('IExtHostTunnelService');
|
||||
|
||||
export class ExtHostTunnelService implements IExtHostTunnelService {
|
||||
_serviceBrand: undefined;
|
||||
onDidTunnelsChange: vscode.Event<void> = (new Emitter<void>()).event;
|
||||
|
||||
async openTunnel(forward: TunnelOptions): Promise<vscode.Tunnel | undefined> {
|
||||
return undefined;
|
||||
}
|
||||
async getTunnels(): Promise<vscode.TunnelDescription[]> {
|
||||
return [];
|
||||
}
|
||||
async $findCandidatePorts(): Promise<{ host: string, port: number; detail: string; }[]> {
|
||||
return [];
|
||||
}
|
||||
async setForwardPortProvider(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> { return { dispose: () => { } }; }
|
||||
async $filterCandidates(candidates: { host: string, port: number, detail: string }[]): Promise<boolean[]> {
|
||||
return candidates.map(() => true);
|
||||
}
|
||||
async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> { return { dispose: () => { } }; }
|
||||
$forwardPort(tunnelOptions: TunnelOptions): Promise<TunnelDto> | undefined { return undefined; }
|
||||
async $closeTunnel(remote: { host: string, port: number }): Promise<void> { }
|
||||
|
||||
async $onDidTunnelsChange(): Promise<void> { }
|
||||
}
|
||||
|
||||
@@ -127,11 +127,19 @@ export namespace DiagnosticTag {
|
||||
|
||||
export namespace Diagnostic {
|
||||
export function from(value: vscode.Diagnostic): IMarkerData {
|
||||
let code: string | { value: string; link: URI } | undefined = isString(value.code) || isNumber(value.code) ? String(value.code) : undefined;
|
||||
if (value.code2) {
|
||||
code = {
|
||||
value: String(value.code2.value),
|
||||
link: value.code2.link
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...Range.from(value.range),
|
||||
message: value.message,
|
||||
source: value.source,
|
||||
code: isString(value.code) || isNumber(value.code) ? String(value.code) : undefined,
|
||||
code,
|
||||
severity: DiagnosticSeverity.from(value.severity),
|
||||
relatedInformation: value.relatedInformation && value.relatedInformation.map(DiagnosticRelatedInformation.from),
|
||||
tags: Array.isArray(value.tags) ? coalesce(value.tags.map(DiagnosticTag.from)) : undefined,
|
||||
@@ -141,7 +149,7 @@ export namespace Diagnostic {
|
||||
export function to(value: IMarkerData): vscode.Diagnostic {
|
||||
const res = new types.Diagnostic(Range.to(value), value.message, DiagnosticSeverity.to(value.severity));
|
||||
res.source = value.source;
|
||||
res.code = value.code;
|
||||
res.code = isString(value.code) ? value.code : value.code?.value;
|
||||
res.relatedInformation = value.relatedInformation && value.relatedInformation.map(DiagnosticRelatedInformation.to);
|
||||
res.tags = value.tags && coalesce(value.tags.map(DiagnosticTag.to));
|
||||
return res;
|
||||
@@ -483,15 +491,29 @@ export namespace WorkspaceEdit {
|
||||
const result: extHostProtocol.IWorkspaceEditDto = {
|
||||
edits: []
|
||||
};
|
||||
for (const entry of (value as types.WorkspaceEdit)._allEntries()) {
|
||||
const [uri, uriOrEdits] = entry;
|
||||
if (Array.isArray(uriOrEdits)) {
|
||||
// text edits
|
||||
const doc = documents && uri ? documents.getDocument(uri) : undefined;
|
||||
result.edits.push(<extHostProtocol.IResourceTextEditDto>{ resource: uri, modelVersionId: doc && doc.version, edits: uriOrEdits.map(TextEdit.from) });
|
||||
} else {
|
||||
// resource edits
|
||||
result.edits.push(<extHostProtocol.IResourceFileEditDto>{ oldUri: uri, newUri: uriOrEdits, options: entry[2] });
|
||||
|
||||
if (value instanceof types.WorkspaceEdit) {
|
||||
for (let entry of value.allEntries()) {
|
||||
|
||||
if (entry._type === 1) {
|
||||
// file operation
|
||||
result.edits.push(<extHostProtocol.IWorkspaceFileEditDto>{
|
||||
oldUri: entry.from,
|
||||
newUri: entry.to,
|
||||
options: entry.options,
|
||||
metadata: entry.metadata
|
||||
});
|
||||
|
||||
} else {
|
||||
// text edits
|
||||
const doc = documents?.getDocument(entry.uri);
|
||||
result.edits.push(<extHostProtocol.IWorkspaceTextEditDto>{
|
||||
resource: entry.uri,
|
||||
edit: TextEdit.from(entry.edit),
|
||||
modelVersionId: doc?.version,
|
||||
metadata: entry.metadata
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -500,16 +522,17 @@ export namespace WorkspaceEdit {
|
||||
export function to(value: extHostProtocol.IWorkspaceEditDto) {
|
||||
const result = new types.WorkspaceEdit();
|
||||
for (const edit of value.edits) {
|
||||
if (Array.isArray((<extHostProtocol.IResourceTextEditDto>edit).edits)) {
|
||||
result.set(
|
||||
URI.revive((<extHostProtocol.IResourceTextEditDto>edit).resource),
|
||||
<types.TextEdit[]>(<extHostProtocol.IResourceTextEditDto>edit).edits.map(TextEdit.to)
|
||||
if ((<extHostProtocol.IWorkspaceTextEditDto>edit).edit) {
|
||||
result.replace(
|
||||
URI.revive((<extHostProtocol.IWorkspaceTextEditDto>edit).resource),
|
||||
Range.to((<extHostProtocol.IWorkspaceTextEditDto>edit).edit.range),
|
||||
(<extHostProtocol.IWorkspaceTextEditDto>edit).edit.text
|
||||
);
|
||||
} else {
|
||||
result.renameFile(
|
||||
URI.revive((<extHostProtocol.IResourceFileEditDto>edit).oldUri!),
|
||||
URI.revive((<extHostProtocol.IResourceFileEditDto>edit).newUri!),
|
||||
(<extHostProtocol.IResourceFileEditDto>edit).options
|
||||
URI.revive((<extHostProtocol.IWorkspaceFileEditDto>edit).oldUri!),
|
||||
URI.revive((<extHostProtocol.IWorkspaceFileEditDto>edit).newUri!),
|
||||
(<extHostProtocol.IWorkspaceFileEditDto>edit).options
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -832,7 +855,12 @@ export namespace CompletionItemKind {
|
||||
export namespace CompletionItem {
|
||||
|
||||
export function to(suggestion: modes.CompletionItem, converter?: CommandsConverter): types.CompletionItem {
|
||||
const result = new types.CompletionItem(suggestion.label);
|
||||
|
||||
const result = new types.CompletionItem(typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name);
|
||||
if (typeof suggestion.label !== 'string') {
|
||||
result.label2 = suggestion.label;
|
||||
}
|
||||
|
||||
result.insertText = suggestion.insertText;
|
||||
result.kind = CompletionItemKind.to(suggestion.kind);
|
||||
result.tags = suggestion.tags && suggestion.tags.map(CompletionItemTag.to);
|
||||
|
||||
@@ -564,7 +564,6 @@ export class TextEdit {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export interface IFileOperationOptions {
|
||||
overwrite?: boolean;
|
||||
ignoreIfExists?: boolean;
|
||||
@@ -577,12 +576,14 @@ export interface IFileOperation {
|
||||
from?: URI;
|
||||
to?: URI;
|
||||
options?: IFileOperationOptions;
|
||||
metadata?: vscode.WorkspaceEditMetadata;
|
||||
}
|
||||
|
||||
export interface IFileTextEdit {
|
||||
_type: 2;
|
||||
uri: URI;
|
||||
edit: TextEdit;
|
||||
metadata?: vscode.WorkspaceEditMetadata;
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
@@ -590,28 +591,28 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
|
||||
|
||||
private _edits = new Array<IFileOperation | IFileTextEdit>();
|
||||
|
||||
renameFile(from: vscode.Uri, to: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean; }): void {
|
||||
this._edits.push({ _type: 1, from, to, options });
|
||||
renameFile(from: vscode.Uri, to: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean; }, metadata?: vscode.WorkspaceEditMetadata): void {
|
||||
this._edits.push({ _type: 1, from, to, options, metadata });
|
||||
}
|
||||
|
||||
createFile(uri: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean; }): void {
|
||||
this._edits.push({ _type: 1, from: undefined, to: uri, options });
|
||||
createFile(uri: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean; }, metadata?: vscode.WorkspaceEditMetadata): void {
|
||||
this._edits.push({ _type: 1, from: undefined, to: uri, options, metadata });
|
||||
}
|
||||
|
||||
deleteFile(uri: vscode.Uri, options?: { recursive?: boolean, ignoreIfNotExists?: boolean; }): void {
|
||||
this._edits.push({ _type: 1, from: uri, to: undefined, options });
|
||||
deleteFile(uri: vscode.Uri, options?: { recursive?: boolean, ignoreIfNotExists?: boolean; }, metadata?: vscode.WorkspaceEditMetadata): void {
|
||||
this._edits.push({ _type: 1, from: uri, to: undefined, options, metadata });
|
||||
}
|
||||
|
||||
replace(uri: URI, range: Range, newText: string): void {
|
||||
this._edits.push({ _type: 2, uri, edit: new TextEdit(range, newText) });
|
||||
replace(uri: URI, range: Range, newText: string, metadata?: vscode.WorkspaceEditMetadata): void {
|
||||
this._edits.push({ _type: 2, uri, edit: new TextEdit(range, newText), metadata });
|
||||
}
|
||||
|
||||
insert(resource: URI, position: Position, newText: string): void {
|
||||
this.replace(resource, new Range(position, position), newText);
|
||||
insert(resource: URI, position: Position, newText: string, metadata?: vscode.WorkspaceEditMetadata): void {
|
||||
this.replace(resource, new Range(position, position), newText, metadata);
|
||||
}
|
||||
|
||||
delete(resource: URI, range: Range): void {
|
||||
this.replace(resource, range, '');
|
||||
delete(resource: URI, range: Range, metadata?: vscode.WorkspaceEditMetadata): void {
|
||||
this.replace(resource, range, '', metadata);
|
||||
}
|
||||
|
||||
has(uri: URI): boolean {
|
||||
@@ -663,18 +664,22 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
|
||||
return values(textEdits);
|
||||
}
|
||||
|
||||
_allEntries(): ([URI, TextEdit[]] | [URI?, URI?, IFileOperationOptions?])[] {
|
||||
const res: ([URI, TextEdit[]] | [URI?, URI?, IFileOperationOptions?])[] = [];
|
||||
for (let edit of this._edits) {
|
||||
if (edit._type === 1) {
|
||||
res.push([edit.from, edit.to, edit.options]);
|
||||
} else {
|
||||
res.push([edit.uri, [edit.edit]]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
allEntries(): ReadonlyArray<IFileTextEdit | IFileOperation> {
|
||||
return this._edits;
|
||||
}
|
||||
|
||||
// _allEntries(): ([URI, TextEdit] | [URI?, URI?, IFileOperationOptions?])[] {
|
||||
// const res: ([URI, TextEdit] | [URI?, URI?, IFileOperationOptions?])[] = [];
|
||||
// for (let edit of this._edits) {
|
||||
// if (edit._type === 1) {
|
||||
// res.push([edit.from, edit.to, edit.options]);
|
||||
// } else {
|
||||
// res.push([edit.uri, edit.edit]);
|
||||
// }
|
||||
// }
|
||||
// return res;
|
||||
// }
|
||||
|
||||
get size(): number {
|
||||
return this.entries().length;
|
||||
}
|
||||
@@ -1351,10 +1356,19 @@ export enum CompletionItemTag {
|
||||
Deprecated = 1,
|
||||
}
|
||||
|
||||
export interface CompletionItemLabel {
|
||||
name: string;
|
||||
signature?: string;
|
||||
qualifier?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
|
||||
@es5ClassCompat
|
||||
export class CompletionItem implements vscode.CompletionItem {
|
||||
|
||||
label: string;
|
||||
label2?: CompletionItemLabel;
|
||||
kind?: CompletionItemKind;
|
||||
tags?: CompletionItemTag[];
|
||||
detail?: string;
|
||||
@@ -1378,6 +1392,7 @@ export class CompletionItem implements vscode.CompletionItem {
|
||||
toJSON(): any {
|
||||
return {
|
||||
label: this.label,
|
||||
label2: this.label2,
|
||||
kind: this.kind && CompletionItemKind[this.kind],
|
||||
detail: this.detail,
|
||||
documentation: this.documentation,
|
||||
@@ -1394,7 +1409,6 @@ export class CompletionItem implements vscode.CompletionItem {
|
||||
export class CompletionList {
|
||||
|
||||
isIncomplete?: boolean;
|
||||
isDetailsResolved?: boolean;
|
||||
items: vscode.CompletionItem[];
|
||||
|
||||
constructor(items: vscode.CompletionItem[] = [], isIncomplete: boolean = false) {
|
||||
@@ -1824,6 +1838,7 @@ export class Task implements vscode.Task2 {
|
||||
private static EmptyType: string = '$empty';
|
||||
|
||||
private __id: string | undefined;
|
||||
private __deprecated: boolean = false;
|
||||
|
||||
private _definition: vscode.TaskDefinition;
|
||||
private _scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined;
|
||||
@@ -1848,6 +1863,7 @@ export class Task implements vscode.Task2 {
|
||||
this._source = this.source = arg3;
|
||||
this.execution = arg4;
|
||||
problemMatchers = arg5;
|
||||
this.__deprecated = true;
|
||||
} else if (arg2 === TaskScope.Global || arg2 === TaskScope.Workspace) {
|
||||
this.target = arg2;
|
||||
this._name = this.name = arg3;
|
||||
@@ -1884,6 +1900,10 @@ export class Task implements vscode.Task2 {
|
||||
this.__id = value;
|
||||
}
|
||||
|
||||
get _deprecated(): boolean {
|
||||
return this.__deprecated;
|
||||
}
|
||||
|
||||
private clear(): void {
|
||||
if (this.__id === undefined) {
|
||||
return;
|
||||
|
||||
@@ -15,8 +15,10 @@ import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
|
||||
import type * as vscode from 'vscode';
|
||||
import { Cache } from './cache';
|
||||
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewEditorCapabilities, WebviewPanelHandle, WebviewPanelViewStateData } from './extHost.protocol';
|
||||
import { Disposable as VSCodeDisposable } from './extHostTypes';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
type IconPath = URI | { light: URI, dark: URI };
|
||||
|
||||
@@ -251,8 +253,18 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
|
||||
|
||||
private readonly _proxy: MainThreadWebviewsShape;
|
||||
private readonly _webviewPanels = new Map<WebviewPanelHandle, ExtHostWebviewEditor>();
|
||||
private readonly _serializers = new Map<string, { readonly serializer: vscode.WebviewPanelSerializer, readonly extension: IExtensionDescription }>();
|
||||
private readonly _editorProviders = new Map<string, { readonly provider: vscode.WebviewCustomEditorProvider, readonly extension: IExtensionDescription }>();
|
||||
|
||||
private readonly _serializers = new Map<string, {
|
||||
readonly serializer: vscode.WebviewPanelSerializer;
|
||||
readonly extension: IExtensionDescription;
|
||||
}>();
|
||||
|
||||
private readonly _editorProviders = new Map<string, {
|
||||
readonly provider: vscode.WebviewCustomEditorProvider;
|
||||
readonly extension: IExtensionDescription;
|
||||
}>();
|
||||
|
||||
private readonly _edits = new Cache<unknown>('edits');
|
||||
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
@@ -312,11 +324,14 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
|
||||
if (this._editorProviders.has(viewType)) {
|
||||
throw new Error(`Editor provider for '${viewType}' already registered`);
|
||||
}
|
||||
|
||||
this._editorProviders.set(viewType, { extension, provider, });
|
||||
|
||||
this._proxy.$registerEditorProvider({ id: extension.identifier, location: extension.extensionLocation }, viewType, options || {}, this.getCapabilites(provider));
|
||||
|
||||
// Hook up events
|
||||
provider?.editingDelegate?.onEdit(({ edit, resource }) => {
|
||||
this._proxy.$onEdit(resource, viewType, edit);
|
||||
const id = this._edits.add([edit]);
|
||||
this._proxy.$onEdit(resource, viewType, id);
|
||||
});
|
||||
|
||||
return new VSCodeDisposable(() => {
|
||||
@@ -426,14 +441,32 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
|
||||
await provider.resolveWebviewEditor(revivedResource, revivedPanel);
|
||||
}
|
||||
|
||||
$undoEdits(resource: UriComponents, viewType: string, edits: readonly any[]): void {
|
||||
$undoEdits(resourceComponents: UriComponents, viewType: string, editIds: readonly number[]): void {
|
||||
const provider = this.getEditorProvider(viewType);
|
||||
provider?.editingDelegate?.undoEdits(URI.revive(resource), edits);
|
||||
if (!provider?.editingDelegate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resource = URI.revive(resourceComponents);
|
||||
const edits = editIds.map(id => this._edits.get(id, 0));
|
||||
provider.editingDelegate.undoEdits(resource, edits);
|
||||
}
|
||||
|
||||
$applyEdits(resource: UriComponents, viewType: string, edits: readonly any[]): void {
|
||||
$applyEdits(resourceComponents: UriComponents, viewType: string, editIds: readonly number[]): void {
|
||||
const provider = this.getEditorProvider(viewType);
|
||||
provider?.editingDelegate?.applyEdits(URI.revive(resource), edits);
|
||||
if (!provider?.editingDelegate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resource = URI.revive(resourceComponents);
|
||||
const edits = editIds.map(id => this._edits.get(id, 0));
|
||||
provider.editingDelegate.applyEdits(resource, edits);
|
||||
}
|
||||
|
||||
$disposeEdits(editIds: readonly number[]): void {
|
||||
for (const edit of editIds) {
|
||||
this._edits.delete(edit);
|
||||
}
|
||||
}
|
||||
|
||||
async $onSave(resource: UriComponents, viewType: string): Promise<void> {
|
||||
@@ -446,6 +479,14 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
|
||||
return provider?.editingDelegate?.saveAs(URI.revive(resource), URI.revive(targetResource));
|
||||
}
|
||||
|
||||
async $backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<boolean> {
|
||||
const provider = this.getEditorProvider(viewType);
|
||||
if (!provider?.editingDelegate?.backup) {
|
||||
return false;
|
||||
}
|
||||
return provider.editingDelegate.backup(URI.revive(resource), cancellation);
|
||||
}
|
||||
|
||||
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined {
|
||||
return this._webviewPanels.get(handle);
|
||||
}
|
||||
@@ -459,6 +500,9 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
|
||||
if (capabilities.editingDelegate) {
|
||||
declaredCapabilites.push(WebviewEditorCapabilities.Editable);
|
||||
}
|
||||
if (capabilities.editingDelegate?.backup) {
|
||||
declaredCapabilites.push(WebviewEditorCapabilities.SupportsHotExit);
|
||||
}
|
||||
return declaredCapabilites;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { ExtHostTaskBase, TaskHandleDTO, TaskDTO, CustomExecutionDTO, HandlerDat
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
|
||||
export class ExtHostTask extends ExtHostTaskBase {
|
||||
private _variableResolver: ExtHostVariableResolverService | undefined;
|
||||
@@ -35,9 +36,10 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||
@IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService,
|
||||
@ILogService logService: ILogService
|
||||
@ILogService logService: ILogService,
|
||||
@IExtHostApiDeprecationService deprecationService: IExtHostApiDeprecationService
|
||||
) {
|
||||
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService, logService);
|
||||
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService, logService, deprecationService);
|
||||
if (initData.remote.isRemote && initData.remote.authority) {
|
||||
this.registerTaskSystem(Schemas.vscodeRemote, {
|
||||
scheme: Schemas.vscodeRemote,
|
||||
@@ -73,6 +75,8 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||
const taskDTOs: tasks.TaskDTO[] = [];
|
||||
if (value) {
|
||||
for (let task of value) {
|
||||
this.checkDeprecation(task, handler);
|
||||
|
||||
if (!task.definition || !validTypes[task.definition.type]) {
|
||||
this._logService.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,10 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
readonly _serviceBrand: undefined;
|
||||
private readonly _proxy: MainThreadTunnelServiceShape;
|
||||
private _forwardPortProvider: ((tunnelOptions: TunnelOptions) => Thenable<vscode.Tunnel> | undefined) | undefined;
|
||||
private _showCandidatePort: (host: string, port: number, detail: string) => Thenable<boolean> = () => { return Promise.resolve(true); };
|
||||
private _extensionTunnels: Map<string, Map<number, vscode.Tunnel>> = new Map();
|
||||
private _onDidTunnelsChange: Emitter<void> = new Emitter<void>();
|
||||
onDidTunnelsChange: vscode.Event<void> = this._onDidTunnelsChange.event;
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@@ -61,14 +64,30 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async getTunnels(): Promise<vscode.TunnelDescription[]> {
|
||||
return this._proxy.$getTunnels();
|
||||
}
|
||||
|
||||
registerCandidateFinder(): Promise<void> {
|
||||
return this._proxy.$registerCandidateFinder();
|
||||
}
|
||||
|
||||
async setForwardPortProvider(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> {
|
||||
if (provider && provider.tunnelFactory) {
|
||||
this._forwardPortProvider = provider.tunnelFactory;
|
||||
await this._proxy.$setTunnelProvider();
|
||||
$filterCandidates(candidates: { host: string, port: number, detail: string }[]): Promise<boolean[]> {
|
||||
return Promise.all(candidates.map(candidate => {
|
||||
return this._showCandidatePort(candidate.host, candidate.port, candidate.detail);
|
||||
}));
|
||||
}
|
||||
|
||||
async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> {
|
||||
if (provider) {
|
||||
if (provider.showCandidatePort) {
|
||||
this._showCandidatePort = provider.showCandidatePort;
|
||||
await this._proxy.$setCandidateFilter();
|
||||
}
|
||||
if (provider.tunnelFactory) {
|
||||
this._forwardPortProvider = provider.tunnelFactory;
|
||||
await this._proxy.$setTunnelProvider();
|
||||
}
|
||||
} else {
|
||||
this._forwardPortProvider = undefined;
|
||||
}
|
||||
@@ -87,6 +106,10 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
}
|
||||
}
|
||||
|
||||
async $onDidTunnelsChange(): Promise<void> {
|
||||
this._onDidTunnelsChange.fire();
|
||||
}
|
||||
|
||||
$forwardPort(tunnelOptions: TunnelOptions): Promise<TunnelDto> | undefined {
|
||||
if (this._forwardPortProvider) {
|
||||
const providedPort = this._forwardPortProvider!(tunnelOptions);
|
||||
@@ -128,9 +151,7 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
const childStat = fs.statSync(childUri.fsPath);
|
||||
if (childStat.isDirectory() && !isNaN(pid)) {
|
||||
const cwd = fs.readlinkSync(resources.joinPath(childUri, 'cwd').fsPath);
|
||||
const rawCmd = fs.readFileSync(resources.joinPath(childUri, 'cmdline').fsPath, 'utf8');
|
||||
const nullIndex = rawCmd.indexOf('\0');
|
||||
const cmd = rawCmd.substr(0, nullIndex > 0 ? nullIndex : rawCmd.length).trim();
|
||||
const cmd = fs.readFileSync(resources.joinPath(childUri, 'cmdline').fsPath, 'utf8');
|
||||
processes.push({ pid, cwd, cmd });
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user