Merge branch 'master' into pty-links

This commit is contained in:
Daniel Imms
2020-02-04 10:31:35 -08:00
committed by GitHub
1018 changed files with 47988 additions and 32885 deletions

View File

@@ -14,6 +14,8 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation
import { IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { IWorkspacesService, hasWorkspaceFileExtension, IRecent } from 'vs/platform/workspaces/common/workspaces';
import { Schemas } from 'vs/base/common/network';
import { ILogService } from 'vs/platform/log/common/log';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
// -----------------------------------------------------------------
// The following commands are registered on both sides separately.
@@ -134,16 +136,20 @@ CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand
export class OpenWithAPICommand {
public static readonly ID = 'vscode.openWith';
public static execute(executor: ICommandsExecutor, resource: URI, viewType: string, column?: vscode.ViewColumn): Promise<any> {
public static execute(executor: ICommandsExecutor, resource: URI, viewType: string, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions): Promise<any> {
let options: ITextEditorOptions | undefined;
let position: EditorViewColumn | undefined;
if (typeof column === 'number') {
position = typeConverters.ViewColumn.from(column);
if (typeof columnOrOptions === 'number') {
position = typeConverters.ViewColumn.from(columnOrOptions);
} else if (typeof columnOrOptions !== 'undefined') {
options = typeConverters.TextEditorOptions.from(columnOrOptions);
}
return executor.executeCommand('_workbench.openWith', [
resource,
viewType,
options,
position
]);
}
@@ -233,3 +239,18 @@ CommandsRegistry.registerCommand({
}]
}
});
CommandsRegistry.registerCommand('_extensionTests.setLogLevel', function (accessor: ServicesAccessor, level: number) {
const logService = accessor.get(ILogService);
const environmentService = accessor.get(IEnvironmentService);
if (environmentService.isExtensionDevelopment && !!environmentService.extensionTestsLocationURI) {
logService.setLevel(level);
}
});
CommandsRegistry.registerCommand('_extensionTests.getLogLevel', function (accessor: ServicesAccessor) {
const logService = accessor.get(ILogService);
return logService.getLevel();
});

View File

@@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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}`);
}
}

View File

@@ -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;
}

View File

@@ -69,6 +69,9 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming';
import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication';
import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline';
export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -89,6 +92,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostStorage = accessor.get(IExtHostStorage);
const extHostLogService = accessor.get(ILogService);
const extHostTunnelService = accessor.get(IExtHostTunnelService);
const extHostApiDeprecation = accessor.get(IExtHostApiDeprecationService);
// register addressable instances
rpcProtocol.set(ExtHostContext.ExtHostLogService, <ExtHostLogServiceShape><any>extHostLogService);
@@ -118,7 +122,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.environment));
const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol, extHostLogService));
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService));
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService, extHostApiDeprecation));
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostLogService, extHostDocumentsAndEditors));
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
@@ -128,6 +132,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHostLabelService, new ExtHostLabelService(rpcProtocol));
const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol));
const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol));
const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol));
// Check that no named customers are missing
const expected: ProxyIdentifier<any>[] = values(ExtHostContext);
@@ -175,6 +181,17 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
};
})();
const authentication: typeof vscode.authentication = {
registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable {
return extHostAuthentication.registerAuthenticationProvider(provider);
},
get providers() {
return extHostAuthentication.providers(extension);
},
get onDidChangeAuthenticationProviders(): Event<vscode.AuthenticationProvidersChangeEvent> {
return extHostAuthentication.onDidChangeAuthenticationProviders;
}
};
// namespace: commands
const commands: typeof vscode.commands = {
@@ -352,9 +369,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacters: string[]): vscode.Disposable {
return extHostLanguageFeatures.registerOnTypeFormattingEditProvider(extension, checkSelector(selector), provider, [firstTriggerCharacter].concat(moreTriggerCharacters));
},
registerSemanticTokensProvider(selector: vscode.DocumentSelector, provider: vscode.SemanticTokensProvider, legend: vscode.SemanticTokensLegend): vscode.Disposable {
registerDocumentSemanticTokensProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSemanticTokensProvider, legend: vscode.SemanticTokensLegend): vscode.Disposable {
checkProposedApiEnabled(extension);
return extHostLanguageFeatures.registerSemanticTokensProvider(extension, checkSelector(selector), provider, legend);
return extHostLanguageFeatures.registerDocumentSemanticTokensProvider(extension, checkSelector(selector), provider, legend);
},
registerDocumentRangeSemanticTokensProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentRangeSemanticTokensProvider, legend: vscode.SemanticTokensLegend): vscode.Disposable {
checkProposedApiEnabled(extension);
return extHostLanguageFeatures.registerDocumentRangeSemanticTokensProvider(extension, checkSelector(selector), provider, legend);
},
registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, firstItem?: string | vscode.SignatureHelpProviderMetadata, ...remaining: string[]): vscode.Disposable {
if (typeof firstItem === 'object') {
@@ -381,7 +402,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostLanguageFeatures.registerCallHierarchyProvider(extension, selector, provider);
},
setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => {
return extHostLanguageFeatures.setLanguageConfiguration(language, configuration);
return extHostLanguageFeatures.setLanguageConfiguration(extension, language, configuration);
}
};
@@ -501,6 +522,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable);
},
withScmProgress<R>(task: (progress: vscode.Progress<number>) => Thenable<R>) {
extHostApiDeprecation.report('window.withScmProgress', extension,
`Use 'withProgress' instead.`);
return extHostProgress.withProgress(extension, { location: extHostTypes.ProgressLocation.SourceControl }, (progress, token) => task({ report(n: number) { /*noop*/ } }));
},
withProgress<R>(options: vscode.ProgressOptions, task: (progress: vscode.Progress<{ message?: string; worked?: number }>, token: vscode.CancellationToken) => Thenable<R>) {
@@ -562,13 +586,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
};
// namespace: workspace
let warnedRootPathDeprecated = false;
const workspace: typeof vscode.workspace = {
get rootPath() {
if (extension.isUnderDevelopment && !warnedRootPathDeprecated) {
warnedRootPathDeprecated = true;
extHostLogService.warn(`[Deprecation Warning] 'workspace.rootPath' is deprecated and should no longer be used. Please use 'workspace.workspaceFolders' instead. More details: https://aka.ms/vscode-eliminating-rootpath`);
}
extHostApiDeprecation.report('workspace.rootPath', extension,
`Please use 'workspace.workspaceFolders' instead. More details: https://aka.ms/vscode-eliminating-rootpath`);
return extHostWorkspace.getPath();
},
@@ -682,6 +704,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostDocumentContentProviders.registerTextDocumentContentProvider(scheme, provider);
},
registerTaskProvider: (type: string, provider: vscode.TaskProvider) => {
extHostApiDeprecation.report('window.registerTaskProvider', extension,
`Use the corresponding function on the 'tasks' namespace instead`);
return extHostTask.registerTaskProvider(extension, type, provider);
},
registerFileSystemProvider(scheme, provider, options) {
@@ -727,12 +752,28 @@ 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);
},
registerTimelineProvider: (scheme: string, provider: vscode.TimelineProvider) => {
checkProposedApiEnabled(extension);
return extHostTimeline.registerTimelineProvider(provider, extHostCommands.converter);
}
};
// namespace: scm
const scm: typeof vscode.scm = {
get inputBox() {
extHostApiDeprecation.report('scm.inputBox', extension,
`Use 'SourceControl.inputBox' instead`);
return extHostSCM.getLastInputBox(extension)!; // Strict null override - Deprecated api
},
createSourceControl(id: string, label: string, rootUri?: vscode.Uri) {
@@ -830,6 +871,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return <typeof vscode>{
version: initData.version,
// namespaces
authentication,
commands,
debug,
env,
@@ -946,7 +988,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
Decoration: extHostTypes.Decoration,
WebviewContentState: extHostTypes.WebviewContentState,
UIKind: UIKind,
ColorThemeKind: extHostTypes.ColorThemeKind
ColorThemeKind: extHostTypes.ColorThemeKind,
TimelineItem: extHostTypes.TimelineItem
};
};
}

View File

@@ -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';
@@ -49,6 +49,7 @@ import { SaveReason } from 'vs/workbench/common/editor';
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
import { TunnelOptions } from 'vs/platform/remote/common/tunnel';
import { TimelineItem, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
@@ -147,6 +148,14 @@ export interface MainThreadCommentsShape extends IDisposable {
$onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent): void;
}
export interface MainThreadAuthenticationShape extends IDisposable {
$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 {
$updateConfigurationOption(target: ConfigurationTarget | null, key: string, value: any, overrides: IConfigurationOverrides | undefined, scopeToLanguage: boolean | undefined): Promise<void>;
$removeConfigurationOption(target: ConfigurationTarget | null, key: string, overrides: IConfigurationOverrides | undefined, scopeToLanguage: boolean | undefined): Promise<void>;
@@ -352,7 +361,8 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerOnTypeFormattingSupport(handle: number, selector: IDocumentFilterDto[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void;
$registerNavigateTypeSupport(handle: number): void;
$registerRenameSupport(handle: number, selector: IDocumentFilterDto[], supportsResolveInitialValues: boolean): void;
$registerSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend): void;
$registerDocumentSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend): void;
$registerDocumentRangeSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend): void;
$registerSuggestSupport(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, extensionId: ExtensionIdentifier): void;
$registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void;
$registerDocumentLinkProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean): void;
@@ -430,8 +440,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;
@@ -556,6 +568,7 @@ export interface WebviewExtensionDescription {
export enum WebviewEditorCapabilities {
Editable,
SupportsHotExit,
}
export interface MainThreadWebviewsShape extends IDisposable {
@@ -576,7 +589,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 {
@@ -596,11 +609,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 {
@@ -695,7 +711,7 @@ export interface SCMGroupFeatures {
export type SCMRawResource = [
number /*handle*/,
UriComponents /*resourceUri*/,
string[] /*icons: light, dark*/,
UriComponents[] /*icons: light, dark*/,
string /*tooltip*/,
boolean /*strike through*/,
boolean /*faded*/
@@ -776,8 +792,18 @@ 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>;
}
export interface MainThreadTimelineShape extends IDisposable {
$registerTimelineProvider(provider: TimelineProviderDescriptor): void;
$unregisterTimelineProvider(source: string): void;
$emitTimelineChangeEvent(source: string, uri: UriComponents | undefined): void;
$getTimeline(uri: UriComponents, token: CancellationToken): Promise<TimelineItem[]>;
}
// -- extension host
@@ -891,6 +917,12 @@ export interface ExtHostLabelServiceShape {
$registerResourceLabelFormatter(formatter: ResourceLabelFormatter): IDisposable;
}
export interface ExtHostAuthenticationShape {
$getSessions(id: string): Promise<ReadonlyArray<modes.AuthenticationSession>>;
$login(id: string, scopes: string[]): Promise<modes.AuthenticationSession>;
$logout(id: string, sessionId: string): Promise<void>;
}
export interface ExtHostSearchShape {
$provideFileSearchResults(handle: number, session: number, query: search.IRawQuery, token: CancellationToken): Promise<search.ISearchCompleteStats>;
$provideTextSearchResults(handle: number, session: number, query: search.IRawTextQuery, token: CancellationToken): Promise<search.ISearchCompleteStats>;
@@ -986,16 +1018,20 @@ 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;
[ISuggestDataDtoField.sortText]?: string;
[ISuggestDataDtoField.filterText]?: string;
[ISuggestDataDtoField.preselect]?: boolean;
[ISuggestDataDtoField.preselect]?: true;
[ISuggestDataDtoField.insertText]?: string;
[ISuggestDataDtoField.insertTextRules]?: modes.CompletionItemInsertTextRule;
[ISuggestDataDtoField.range]?: IRange | { insert: IRange, replace: IRange; };
@@ -1007,12 +1043,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?: boolean;
d?: boolean;
}
export interface ISignatureHelpDto {
@@ -1052,25 +1093,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;
@@ -1079,11 +1117,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);
}
}
}
@@ -1176,8 +1214,9 @@ export interface ExtHostLanguageFeaturesShape {
$releaseWorkspaceSymbols(handle: number, id: number): void;
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<IWorkspaceEditDto | undefined>;
$resolveRenameLocation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.RenameLocation | undefined>;
$provideSemanticTokens(handle: number, resource: UriComponents, ranges: IRange[] | null, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null>;
$releaseSemanticTokens(handle: number, semanticColoringResultId: number): void;
$provideDocumentSemanticTokens(handle: number, resource: UriComponents, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null>;
$releaseDocumentSemanticTokens(handle: number, semanticColoringResultId: number): void;
$provideDocumentRangeSemanticTokens(handle: number, resource: UriComponents, range: IRange, token: CancellationToken): Promise<VSBuffer | null>;
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<ISuggestResultDto | undefined>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, id: ChainedCacheId, token: CancellationToken): Promise<ISuggestDataDto | undefined>;
$releaseCompletionItems(handle: number, id: number): void;
@@ -1405,13 +1444,20 @@ 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>;
}
export interface ExtHostTimelineShape {
$getTimeline(source: string, uri: UriComponents, token: CancellationToken): Promise<TimelineItem[]>;
}
// --- proxy identifiers
export const MainContext = {
MainThreadAuthentication: createMainId<MainThreadAuthenticationShape>('MainThreadAuthentication'),
MainThreadClipboard: createMainId<MainThreadClipboardShape>('MainThreadClipboard'),
MainThreadCommands: createMainId<MainThreadCommandsShape>('MainThreadCommands'),
MainThreadComments: createMainId<MainThreadCommentsShape>('MainThreadComments'),
@@ -1451,7 +1497,8 @@ export const MainContext = {
MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
MainThreadLabelService: createMainId<MainThreadLabelServiceShape>('MainThreadLabelService'),
MainThreadTheming: createMainId<MainThreadThemingShape>('MainThreadTheming'),
MainThreadTunnelService: createMainId<MainThreadTunnelServiceShape>('MainThreadTunnelService')
MainThreadTunnelService: createMainId<MainThreadTunnelServiceShape>('MainThreadTunnelService'),
MainThreadTimeline: createMainId<MainThreadTimelineShape>('MainThreadTimeline')
};
export const ExtHostContext = {
@@ -1487,5 +1534,7 @@ export const ExtHostContext = {
ExtHostOutputService: createMainId<ExtHostOutputServiceShape>('ExtHostOutputService'),
ExtHostLabelService: createMainId<ExtHostLabelServiceShape>('ExtHostLabelService'),
ExtHostTheming: createMainId<ExtHostThemingShape>('ExtHostTheming'),
ExtHostTunnelService: createMainId<ExtHostTunnelServiceShape>('ExtHostTunnelService')
ExtHostTunnelService: createMainId<ExtHostTunnelServiceShape>('ExtHostTunnelService'),
ExtHostAuthentication: createMainId<ExtHostAuthenticationShape>('ExtHostAuthentication'),
ExtHostTimeline: createMainId<ExtHostTimelineShape>('ExtHostTimeline')
};

View File

@@ -142,7 +142,7 @@ const newCommands: ApiCommand[] = [
),
// -- go to symbol (definition, type definition, declaration, impl, references)
new ApiCommand(
'vscode.executeDefinitionProvider', '_executeDefinitionProvider', 'Execute all definition provider.',
'vscode.executeDefinitionProvider', '_executeDefinitionProvider', 'Execute all definition providers.',
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
new ApiCommandResult<modes.Location[], types.Location[] | undefined>('A promise that resolves to an array of Location-instances.', tryMapWith(typeConverters.location.to))
),
@@ -168,7 +168,7 @@ const newCommands: ApiCommand[] = [
),
// -- hover
new ApiCommand(
'vscode.executeHoverProvider', '_executeHoverProvider', 'Execute all hover provider.',
'vscode.executeHoverProvider', '_executeHoverProvider', 'Execute all hover providers.',
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
new ApiCommandResult<modes.Hover[], types.Hover[] | undefined>('A promise that resolves to an array of Hover-instances.', tryMapWith(typeConverters.Hover.to))
),
@@ -188,7 +188,7 @@ const newCommands: ApiCommand[] = [
),
// -- symbol search
new ApiCommand(
'vscode.executeWorkspaceSymbolProvider', '_executeWorkspaceSymbolProvider', 'Execute all workspace symbol provider.',
'vscode.executeWorkspaceSymbolProvider', '_executeWorkspaceSymbolProvider', 'Execute all workspace symbol providers.',
[new ApiCommandArgument('query', 'Search string', v => typeof v === 'string', v => v)],
new ApiCommandResult<[search.IWorkspaceSymbolProvider, search.IWorkspaceSymbol[]][], types.SymbolInformation[]>('A promise that resolves to an array of SymbolInformation-instances.', value => {
const result: types.SymbolInformation[] = [];

View File

@@ -0,0 +1,71 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
export interface IExtHostApiDeprecationService {
_serviceBrand: undefined;
report(apiId: string, extension: IExtensionDescription, migrationSuggestion: string): void;
}
export const IExtHostApiDeprecationService = createDecorator<IExtHostApiDeprecationService>('IExtHostApiDeprecationService');
export class ExtHostApiDeprecationService implements IExtHostApiDeprecationService {
_serviceBrand: undefined;
private readonly _reportedUsages = new Set<string>();
private readonly _telemetryShape: extHostProtocol.MainThreadTelemetryShape;
constructor(
@IExtHostRpcService rpc: IExtHostRpcService,
@ILogService private readonly _extHostLogService: ILogService,
) {
this._telemetryShape = rpc.getProxy(extHostProtocol.MainContext.MainThreadTelemetry);
}
public report(apiId: string, extension: IExtensionDescription, migrationSuggestion: string): void {
const key = this.getUsageKey(apiId, extension);
if (this._reportedUsages.has(key)) {
return;
}
this._reportedUsages.add(key);
if (extension.isUnderDevelopment) {
this._extHostLogService.warn(`[Deprecation Warning] '${apiId}' is deprecated. ${migrationSuggestion}`);
}
type DeprecationTelemetry = {
extensionId: string;
apiId: string;
};
type DeprecationTelemetryMeta = {
extensionId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
apiId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
};
this._telemetryShape.$publicLog2<DeprecationTelemetry, DeprecationTelemetryMeta>('extHostDeprecatedApiUsage', {
extensionId: extension.identifier.value,
apiId: apiId,
});
}
private getUsageKey(apiId: string, extension: IExtensionDescription): string {
return `${apiId}-${extension.identifier.value}`;
}
}
export const NullApiDeprecationService = Object.freeze(new class implements IExtHostApiDeprecationService {
_serviceBrand: undefined;
public report(_apiId: string, _extension: IExtensionDescription, _warningMessage: string): void {
// noop
}
}());

View File

@@ -0,0 +1,119 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
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 { Disposable } from 'vs/workbench/api/common/extHostTypes';
import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export class AuthenticationProviderWrapper implements vscode.AuthenticationProvider {
onDidChangeSessions: Event<void>;
constructor(private _requestingExtension: IExtensionDescription,
private _provider: vscode.AuthenticationProvider,
private _proxy: MainThreadAuthenticationShape) {
this.onDidChangeSessions = this._provider.onDidChangeSessions;
}
get id(): string {
return this._provider.id;
}
get displayName(): string {
return this._provider.displayName;
}
async getSessions(): Promise<ReadonlyArray<vscode.AuthenticationSession>> {
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();
}
async login(scopes: string[]): Promise<vscode.AuthenticationSession> {
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);
}
}
export class ExtHostAuthentication implements ExtHostAuthenticationShape {
private _proxy: MainThreadAuthenticationShape;
private _authenticationProviders: Map<string, vscode.AuthenticationProvider> = new Map<string, vscode.AuthenticationProvider>();
private _onDidChangeAuthenticationProviders = new Emitter<vscode.AuthenticationProvidersChangeEvent>();
readonly onDidChangeAuthenticationProviders: Event<vscode.AuthenticationProvidersChangeEvent> = this._onDidChangeAuthenticationProviders.event;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.getProxy(MainContext.MainThreadAuthentication);
}
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.`);
}
this._authenticationProviders.set(provider.id, provider);
const listener = provider.onDidChangeSessions(_ => {
this._proxy.$onDidChangeSessions(provider.id);
});
this._proxy.$registerAuthenticationProvider(provider.id, provider.displayName);
this._onDidChangeAuthenticationProviders.fire({ added: [provider.id], removed: [] });
return new Disposable(() => {
listener.dispose();
this._authenticationProviders.delete(provider.id);
this._proxy.$unregisterAuthenticationProvider(provider.id);
this._onDidChangeAuthenticationProviders.fire({ added: [], removed: [provider.id] });
});
}
$login(providerId: string, scopes: string[]): Promise<modes.AuthenticationSession> {
const authProvider = this._authenticationProviders.get(providerId);
if (authProvider) {
return Promise.resolve(authProvider.login(scopes));
}
throw new Error(`Unable to find authentication provider with handle: ${0}`);
}
$logout(providerId: string, sessionId: string): Promise<void> {
const authProvider = this._authenticationProviders.get(providerId);
if (authProvider) {
return Promise.resolve(authProvider.logout(sessionId));
}
throw new Error(`Unable to find authentication provider with handle: ${0}`);
}
$getSessions(providerId: string): Promise<ReadonlyArray<modes.AuthenticationSession>> {
const authProvider = this._authenticationProviders.get(providerId);
if (authProvider) {
return Promise.resolve(authProvider.getSessions());
}
throw new Error(`Unable to find authentication provider with handle: ${0}`);
}
}

View File

@@ -42,11 +42,11 @@ type ConfigurationInspect<T> = {
workspaceFolderValue?: T,
defaultLanguageValue?: T;
userLanguageValue?: T;
globalLanguageValue?: 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;
}
@@ -257,17 +260,17 @@ export class ExtHostConfigProvider {
return {
key,
defaultValue: config.defaultValue,
globalValue: config.userValue,
workspaceValue: config.workspaceValue,
workspaceFolderValue: config.workspaceFolderValue,
defaultValue: config.default?.value,
globalValue: config.user?.value,
workspaceValue: config.workspace?.value,
workspaceFolderValue: config.workspaceFolder?.value,
defaultLanguageValue: config.default?.override,
userLanguageValue: config.user?.override,
globalLanguageValue: config.user?.override,
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;

View File

@@ -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'));

View File

@@ -13,6 +13,7 @@ import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocum
import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import type * as vscode from 'vscode';
import { assertIsDefined } from 'vs/base/common/types';
import { deepFreeze } from 'vs/base/common/objects';
export class ExtHostDocuments implements ExtHostDocumentsShape {
@@ -144,7 +145,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
}
data._acceptIsDirty(isDirty);
data.onEvents(events);
this._onDidChangeDocument.fire({
this._onDidChangeDocument.fire(deepFreeze({
document: data.document,
contentChanges: events.changes.map((change) => {
return {
@@ -154,7 +155,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
text: change.text
};
})
});
}));
}
public setWordDefinitionFor(modeId: string, wordDefinition: RegExp | undefined): void {

View File

@@ -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) {

View File

@@ -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);

View File

@@ -29,6 +29,8 @@ import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
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
@@ -326,7 +328,8 @@ class CodeActionAdapter {
private readonly _diagnostics: ExtHostDiagnostics,
private readonly _provider: vscode.CodeActionProvider,
private readonly _logService: ILogService,
private readonly _extensionId: ExtensionIdentifier
private readonly _extension: IExtensionDescription,
private readonly _apiDeprecation: IExtHostApiDeprecationService,
) { }
provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<extHostProtocol.ICodeActionListDto | undefined> {
@@ -367,6 +370,10 @@ class CodeActionAdapter {
}
if (CodeActionAdapter._isCommand(candidate)) {
// old school: synthetic code action
this._apiDeprecation.report('CodeActionProvider.provideCodeActions - return commands', this._extension,
`Return 'CodeAction' instances instead.`);
actions.push({
_isSynthetic: true,
title: candidate.title,
@@ -375,9 +382,9 @@ class CodeActionAdapter {
} else {
if (codeActionContext.only) {
if (!candidate.kind) {
this._logService.warn(`${this._extensionId.value} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action does not have a 'kind'. Code action will be dropped. Please set 'CodeAction.kind'.`);
this._logService.warn(`${this._extension.identifier.value} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action does not have a 'kind'. Code action will be dropped. Please set 'CodeAction.kind'.`);
} else if (!codeActionContext.only.contains(candidate.kind)) {
this._logService.warn(`${this._extensionId.value} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action is of kind '${candidate.kind.value}'. Code action will be dropped. Please check 'CodeActionContext.only' to only return requested code actions.`);
this._logService.warn(`${this._extension.identifier.value} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action is of kind '${candidate.kind.value}'. Code action will be dropped. Please check 'CodeActionContext.only' to only return requested code actions.`);
}
}
@@ -623,37 +630,38 @@ class SemanticTokensPreviousResult {
) { }
}
export class SemanticTokensAdapter {
export class DocumentSemanticTokensAdapter {
private readonly _previousResults: Map<number, SemanticTokensPreviousResult>;
private _nextResultId = 1;
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.SemanticTokensProvider,
private readonly _provider: vscode.DocumentSemanticTokensProvider,
) {
this._previousResults = new Map<number, SemanticTokensPreviousResult>();
}
provideSemanticTokens(resource: URI, ranges: IRange[] | null, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null> {
provideDocumentSemanticTokens(resource: URI, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null> {
const doc = this._documents.getDocument(resource);
const previousResult = (previousResultId !== 0 ? this._previousResults.get(previousResultId) : null);
const opts: vscode.SemanticTokensRequestOptions = {
ranges: (Array.isArray(ranges) && ranges.length > 0 ? ranges.map<Range>(typeConvert.Range.to) : undefined),
previousResultId: (previousResult ? previousResult.resultId : undefined)
};
return asPromise(() => this._provider.provideSemanticTokens(doc, opts, token)).then(value => {
if (!value) {
return null;
return asPromise(() => {
if (previousResult && typeof previousResult.resultId === 'string' && typeof this._provider.provideDocumentSemanticTokensEdits === 'function') {
return this._provider.provideDocumentSemanticTokensEdits(doc, previousResult.resultId, token);
}
return this._provider.provideDocumentSemanticTokens(doc, token);
}).then(value => {
if (previousResult) {
this._previousResults.delete(previousResultId);
}
return this._send(SemanticTokensAdapter._convertToEdits(previousResult, value), value);
if (!value) {
return null;
}
return this._send(DocumentSemanticTokensAdapter._convertToEdits(previousResult, value), value);
});
}
async releaseSemanticColoring(semanticColoringResultId: number): Promise<void> {
async releaseDocumentSemanticColoring(semanticColoringResultId: number): Promise<void> {
this._previousResults.delete(semanticColoringResultId);
}
@@ -666,7 +674,7 @@ export class SemanticTokensAdapter {
}
private static _convertToEdits(previousResult: SemanticTokensPreviousResult | null | undefined, newResult: vscode.SemanticTokens | vscode.SemanticTokensEdits): vscode.SemanticTokens | vscode.SemanticTokensEdits {
if (!SemanticTokensAdapter._isSemanticTokens(newResult)) {
if (!DocumentSemanticTokensAdapter._isSemanticTokens(newResult)) {
return newResult;
}
if (!previousResult || !previousResult.tokens) {
@@ -702,7 +710,7 @@ export class SemanticTokensAdapter {
}
private _send(value: vscode.SemanticTokens | vscode.SemanticTokensEdits, original: vscode.SemanticTokens | vscode.SemanticTokensEdits): VSBuffer | null {
if (SemanticTokensAdapter._isSemanticTokens(value)) {
if (DocumentSemanticTokensAdapter._isSemanticTokens(value)) {
const myId = this._nextResultId++;
this._previousResults.set(myId, new SemanticTokensPreviousResult(value.resultId, value.data));
return encodeSemanticTokensDto({
@@ -712,9 +720,9 @@ export class SemanticTokensAdapter {
});
}
if (SemanticTokensAdapter._isSemanticTokensEdits(value)) {
if (DocumentSemanticTokensAdapter._isSemanticTokensEdits(value)) {
const myId = this._nextResultId++;
if (SemanticTokensAdapter._isSemanticTokens(original)) {
if (DocumentSemanticTokensAdapter._isSemanticTokens(original)) {
// store the original
this._previousResults.set(myId, new SemanticTokensPreviousResult(original.resultId, original.data));
} else {
@@ -731,6 +739,33 @@ export class SemanticTokensAdapter {
}
}
export class DocumentRangeSemanticTokensAdapter {
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.DocumentRangeSemanticTokensProvider,
) {
}
provideDocumentRangeSemanticTokens(resource: URI, range: IRange, token: CancellationToken): Promise<VSBuffer | null> {
const doc = this._documents.getDocument(resource);
return asPromise(() => this._provider.provideDocumentRangeSemanticTokens(doc, typeConvert.Range.to(range), token)).then(value => {
if (!value) {
return null;
}
return this._send(value);
});
}
private _send(value: vscode.SemanticTokens): VSBuffer | null {
return encodeSemanticTokensDto({
id: 0,
type: 'full',
data: value.data
});
}
}
class SuggestAdapter {
static supportsResolving(provider: vscode.CompletionItemProvider): boolean {
@@ -748,8 +783,9 @@ class SuggestAdapter {
private readonly _commands: CommandsConverter,
private readonly _provider: vscode.CompletionItemProvider,
private readonly _logService: ILogService,
private readonly _apiDeprecation: IExtHostApiDeprecationService,
private readonly _telemetry: extHostProtocol.MainThreadTelemetryShape,
private readonly _extensionId: ExtensionIdentifier
private readonly _extension: IExtensionDescription,
) { }
provideCompletionItems(resource: URI, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<extHostProtocol.ISuggestResultDto | undefined> {
@@ -783,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);
}
}
@@ -815,39 +851,43 @@ 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;
}
type BlameExtension = {
extensionId: string;
kind: string
kind: string;
index: string;
};
type BlameExtensionMeta = {
extensionId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
kind: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
index: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
};
if (!this._didWarnMust && _mustNotChange !== SuggestAdapter._mustNotChangeHash(resolvedItem)) {
this._logService.warn(`[${this._extensionId.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._extensionId.value, kind: 'must' });
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>('badresolvecompletion', { extensionId: this._extension.identifier.value, kind: 'must', index: _mustNotChangeIndex });
this._didWarnMust = true;
}
if (!this._didWarnShould && _mayNotChange !== SuggestAdapter._mayNotChangeHash(resolvedItem)) {
this._logService.info(`[${this._extensionId.value}] UNSAVE result from 'resolveCompletionItem', extension SHOULD NOT change any of: additionalTextEdits, or command`);
this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('resolveCompletionItem/invalid', { extensionId: this._extensionId.value, kind: 'should' });
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>('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);
});
}
@@ -857,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) {
@@ -873,13 +909,14 @@ 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,
[extHostProtocol.ISuggestDataDtoField.documentation]: typeof item.documentation === 'undefined' ? undefined : typeConvert.MarkdownString.fromStrict(item.documentation),
[extHostProtocol.ISuggestDataDtoField.sortText]: item.sortText,
[extHostProtocol.ISuggestDataDtoField.filterText]: item.filterText,
[extHostProtocol.ISuggestDataDtoField.preselect]: item.preselect,
[extHostProtocol.ISuggestDataDtoField.preselect]: item.preselect || undefined,
[extHostProtocol.ISuggestDataDtoField.insertTextRules]: item.keepWhitespace ? modes.CompletionItemInsertTextRule.KeepWhitespace : 0,
[extHostProtocol.ISuggestDataDtoField.commitCharacters]: item.commitCharacters,
[extHostProtocol.ISuggestDataDtoField.additionalTextEdits]: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from),
@@ -888,6 +925,7 @@ class SuggestAdapter {
// 'insertText'-logic
if (item.textEdit) {
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') {
@@ -904,51 +942,86 @@ class SuggestAdapter {
range = item.textEdit.range;
} else if (item.range) {
range = item.range;
} else if (item.range2) {
range = item.range2;
}
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) {
const args = [item.label, item.sortText, item.filterText, item.insertText, item.range, item.range2];
const res = JSON.stringify(args);
const res = JSON.stringify([item.label, item.sortText, item.filterText, item.insertText, item.range]);
return res;
}
private static _mustNotChangeDiff(hash: string, item: vscode.CompletionItem): string | void {
const thisArr = [item.label, item.sortText, item.filterText, item.insertText, item.range];
const thisHash = JSON.stringify(thisArr);
if (hash === thisHash) {
return;
}
const arr = JSON.parse(hash);
for (let i = 0; i < 6; i++) {
if (JSON.stringify(arr[i] !== JSON.stringify(thisArr[i]))) {
return i.toString();
}
}
return 'unknown';
}
private static _mayNotChangeHash(item: vscode.CompletionItem) {
return JSON.stringify([item.additionalTextEdits, item.command]);
}
private static _mayNotChangeDiff(hash: string, item: vscode.CompletionItem): string | void {
const thisArr = [item.additionalTextEdits, item.command];
const thisHash = JSON.stringify(thisArr);
if (hash === thisHash) {
return;
}
const arr = JSON.parse(hash);
for (let i = 0; i < 6; i++) {
if (JSON.stringify(arr[i] !== JSON.stringify(thisArr[i]))) {
return i.toString();
}
}
return 'unknown';
}
}
class SignatureHelpAdapter {
@@ -995,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');
@@ -1283,9 +1322,9 @@ class CallHierarchyAdapter {
type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter
| DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
| SemanticTokensAdapter | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter
| ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter
| DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter;
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter
| TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter
| SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter;
class AdapterData {
constructor(
@@ -1306,6 +1345,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
private _diagnostics: ExtHostDiagnostics;
private _adapter = new Map<number, AdapterData>();
private readonly _logService: ILogService;
private readonly _apiDeprecation: IExtHostApiDeprecationService;
constructor(
mainContext: extHostProtocol.IMainContext,
@@ -1313,7 +1353,8 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
documents: ExtHostDocuments,
commands: ExtHostCommands,
diagnostics: ExtHostDiagnostics,
logService: ILogService
logService: ILogService,
apiDeprecationService: IExtHostApiDeprecationService,
) {
this._uriTransformer = uriTransformer;
this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadLanguageFeatures);
@@ -1322,6 +1363,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
this._commands = commands;
this._diagnostics = diagnostics;
this._logService = logService;
this._apiDeprecation = apiDeprecationService;
}
private _transformDocumentSelector(selector: vscode.DocumentSelector): Array<extHostProtocol.IDocumentFilterDto> {
@@ -1529,7 +1571,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
// --- quick fix
registerCodeActionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable {
const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension.identifier), extension);
const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension, this._apiDeprecation), extension);
this._proxy.$registerQuickFixSupport(handle, this._transformDocumentSelector(selector), (metadata && metadata.providedCodeActionKinds) ? metadata.providedCodeActionKinds.map(kind => kind.value) : undefined);
return this._createDisposable(handle);
}
@@ -1613,18 +1655,28 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
//#region semantic coloring
registerSemanticTokensProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.SemanticTokensProvider, legend: vscode.SemanticTokensLegend): vscode.Disposable {
const handle = this._addNewAdapter(new SemanticTokensAdapter(this._documents, provider), extension);
this._proxy.$registerSemanticTokensProvider(handle, this._transformDocumentSelector(selector), legend);
registerDocumentSemanticTokensProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentSemanticTokensProvider, legend: vscode.SemanticTokensLegend): vscode.Disposable {
const handle = this._addNewAdapter(new DocumentSemanticTokensAdapter(this._documents, provider), extension);
this._proxy.$registerDocumentSemanticTokensProvider(handle, this._transformDocumentSelector(selector), legend);
return this._createDisposable(handle);
}
$provideSemanticTokens(handle: number, resource: UriComponents, ranges: IRange[] | null, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null> {
return this._withAdapter(handle, SemanticTokensAdapter, adapter => adapter.provideSemanticTokens(URI.revive(resource), ranges, previousResultId, token), null);
$provideDocumentSemanticTokens(handle: number, resource: UriComponents, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null> {
return this._withAdapter(handle, DocumentSemanticTokensAdapter, adapter => adapter.provideDocumentSemanticTokens(URI.revive(resource), previousResultId, token), null);
}
$releaseSemanticTokens(handle: number, semanticColoringResultId: number): void {
this._withAdapter(handle, SemanticTokensAdapter, adapter => adapter.releaseSemanticColoring(semanticColoringResultId), undefined);
$releaseDocumentSemanticTokens(handle: number, semanticColoringResultId: number): void {
this._withAdapter(handle, DocumentSemanticTokensAdapter, adapter => adapter.releaseDocumentSemanticColoring(semanticColoringResultId), undefined);
}
registerDocumentRangeSemanticTokensProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentRangeSemanticTokensProvider, legend: vscode.SemanticTokensLegend): vscode.Disposable {
const handle = this._addNewAdapter(new DocumentRangeSemanticTokensAdapter(this._documents, provider), extension);
this._proxy.$registerDocumentRangeSemanticTokensProvider(handle, this._transformDocumentSelector(selector), legend);
return this._createDisposable(handle);
}
$provideDocumentRangeSemanticTokens(handle: number, resource: UriComponents, range: IRange, token: CancellationToken): Promise<VSBuffer | null> {
return this._withAdapter(handle, DocumentRangeSemanticTokensAdapter, adapter => adapter.provideDocumentRangeSemanticTokens(URI.revive(resource), range, token), null);
}
//#endregion
@@ -1632,7 +1684,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
// --- suggestion
registerCompletionItemProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable {
const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider, this._logService, this._telemetryShape, extension.identifier), extension);
const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider, this._logService, this._apiDeprecation, this._telemetryShape, extension), extension);
this._proxy.$registerSuggestSupport(handle, this._transformDocumentSelector(selector), triggerCharacters, SuggestAdapter.supportsResolving(provider), extension.identifier);
return this._createDisposable(handle);
}
@@ -1780,7 +1832,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
return onEnterRules.map(ExtHostLanguageFeatures._serializeOnEnterRule);
}
setLanguageConfiguration(languageId: string, configuration: vscode.LanguageConfiguration): vscode.Disposable {
setLanguageConfiguration(extension: IExtensionDescription, languageId: string, configuration: vscode.LanguageConfiguration): vscode.Disposable {
let { wordPattern } = configuration;
// check for a valid word pattern
@@ -1795,6 +1847,16 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
this._documents.setWordDefinitionFor(languageId, undefined);
}
if (configuration.__electricCharacterSupport) {
this._apiDeprecation.report('LanguageConfiguration.__electricCharacterSupport', extension,
`Do not use.`);
}
if (configuration.__characterPairSupport) {
this._apiDeprecation.report('LanguageConfiguration.__characterPairSupport', extension,
`Do not use.`);
}
const handle = this._nextHandle();
const serializedConfiguration: extHostProtocol.ILanguageConfigurationDto = {
comments: configuration.comments,

View File

@@ -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) {

View File

@@ -22,15 +22,14 @@ type ProviderHandle = number;
type GroupHandle = number;
type ResourceStateHandle = number;
function getIconPath(decorations?: vscode.SourceControlResourceThemableDecorations): string | undefined {
function getIconResource(decorations?: vscode.SourceControlResourceThemableDecorations): vscode.Uri | undefined {
if (!decorations) {
return undefined;
} else if (typeof decorations.iconPath === 'string') {
return URI.file(decorations.iconPath).toString();
} else if (decorations.iconPath) {
return `${decorations.iconPath}`;
return URI.file(decorations.iconPath);
} else {
return decorations.iconPath;
}
return undefined;
}
function compareResourceThemableDecorations(a: vscode.SourceControlResourceThemableDecorations, b: vscode.SourceControlResourceThemableDecorations): number {
@@ -287,28 +286,28 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
this._resourceStatesMap.set(handle, r);
const sourceUri = r.resourceUri;
const iconPath = getIconPath(r.decorations);
const lightIconPath = r.decorations && getIconPath(r.decorations.light) || iconPath;
const darkIconPath = r.decorations && getIconPath(r.decorations.dark) || iconPath;
const icons: string[] = [];
const iconUri = getIconResource(r.decorations);
const lightIconUri = r.decorations && getIconResource(r.decorations.light) || iconUri;
const darkIconUri = r.decorations && getIconResource(r.decorations.dark) || iconUri;
const icons: UriComponents[] = [];
if (r.command) {
this._resourceStatesCommandsMap.set(handle, r.command);
}
if (lightIconPath) {
icons.push(lightIconPath);
if (lightIconUri) {
icons.push(lightIconUri);
}
if (darkIconPath && (darkIconPath !== lightIconPath)) {
icons.push(darkIconPath);
if (darkIconUri && (darkIconUri.toString() !== lightIconUri?.toString())) {
icons.push(darkIconUri);
}
const tooltip = (r.decorations && r.decorations.tooltip) || '';
const strikeThrough = r.decorations && !!r.decorations.strikeThrough;
const faded = r.decorations && !!r.decorations.faded;
const rawResource = [handle, <UriComponents>sourceUri, icons, tooltip, strikeThrough, faded] as SCMRawResource;
const rawResource = [handle, sourceUri, icons, tooltip, strikeThrough, faded] as SCMRawResource;
return { rawResource, handle };
});

View File

@@ -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;
}
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.`);
}

View File

@@ -32,6 +32,7 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape {
createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal;
attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void;
getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string;
}
export const IExtHostTerminalService = createDecorator<IExtHostTerminalService>('IExtHostTerminalService');
@@ -322,6 +323,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
public abstract createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal;
public abstract createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal;
public abstract getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
public abstract getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string;
public abstract $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<void>;
public abstract $requestAvailableShells(): Promise<IShellDefinitionDto[]>;
public abstract $requestDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
@@ -596,6 +598,10 @@ export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
throw new Error('Not implemented');
}
public getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string {
throw new Error('Not implemented');
}
public $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<void> {
throw new Error('Not implemented');
}

View File

@@ -0,0 +1,128 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { UriComponents, URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ExtHostTimelineShape, MainThreadTimelineShape, IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { TimelineItem, TimelineItemWithSource, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline';
import { IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { CancellationToken } from 'vs/base/common/cancellation';
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
import { ThemeIcon } from 'vs/workbench/api/common/extHostTypes';
export interface IExtHostTimeline extends ExtHostTimelineShape {
readonly _serviceBrand: undefined;
$getTimeline(source: string, uri: UriComponents, token: vscode.CancellationToken): Promise<TimelineItem[]>;
}
export const IExtHostTimeline = createDecorator<IExtHostTimeline>('IExtHostTimeline');
export class ExtHostTimeline implements IExtHostTimeline {
_serviceBrand: undefined;
private _proxy: MainThreadTimelineShape;
private _providers = new Map<string, TimelineProvider>();
constructor(
mainContext: IMainContext,
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadTimeline);
}
async $getTimeline(source: string, uri: UriComponents, token: vscode.CancellationToken): Promise<TimelineItem[]> {
const provider = this._providers.get(source);
return provider?.provideTimeline(URI.revive(uri), token) ?? [];
}
registerTimelineProvider(provider: vscode.TimelineProvider, commandConverter: CommandsConverter): IDisposable {
const timelineDisposables = new DisposableStore();
const convertTimelineItem = this.convertTimelineItem(provider.source, commandConverter, timelineDisposables);
let disposable: IDisposable | undefined;
if (provider.onDidChange) {
disposable = provider.onDidChange(this.emitTimelineChangeEvent(provider.source), this);
}
return this.registerTimelineProviderCore({
...provider,
async provideTimeline(uri: URI, token: CancellationToken) {
timelineDisposables.clear();
const results = await provider.provideTimeline(uri, token);
// Intentional == we don't know how a provider will respond
// eslint-disable-next-line eqeqeq
return results != null
? results.map(item => convertTimelineItem(item))
: [];
},
dispose() {
disposable?.dispose();
timelineDisposables.dispose();
}
});
}
private convertTimelineItem(source: string, commandConverter: CommandsConverter, disposables: DisposableStore): (item: vscode.TimelineItem) => TimelineItemWithSource {
return (item: vscode.TimelineItem) => {
const { iconPath, ...props } = item;
let icon;
let iconDark;
let themeIcon;
if (item.iconPath) {
if (iconPath instanceof ThemeIcon) {
themeIcon = { id: iconPath.id };
}
else if (URI.isUri(iconPath)) {
icon = iconPath;
iconDark = iconPath;
}
else {
({ light: icon, dark: iconDark } = iconPath as { light: URI; dark: URI });
}
}
return {
...props,
source: source,
command: item.command ? commandConverter.toInternal(item.command, disposables) : undefined,
icon: icon,
iconDark: iconDark,
themeIcon: themeIcon
};
};
}
private emitTimelineChangeEvent(source: string) {
return (uri: vscode.Uri | undefined) => {
this._proxy.$emitTimelineChangeEvent(source, uri);
};
}
private registerTimelineProviderCore(provider: TimelineProvider): IDisposable {
// console.log(`ExtHostTimeline#registerTimelineProvider: source=${provider.source}`);
const existing = this._providers.get(provider.source);
if (existing && !existing.replaceable) {
throw new Error(`Timeline Provider ${provider.source} already exists.`);
}
this._proxy.$registerTimelineProvider({
source: provider.source,
sourceDescription: provider.sourceDescription,
replaceable: provider.replaceable
});
this._providers.set(provider.source, provider);
return toDisposable(() => {
this._providers.delete(provider.source);
this._proxy.$unregisterTimelineProvider(provider.source);
provider.dispose();
});
}
}

View File

@@ -237,7 +237,7 @@ 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;

View File

@@ -5,9 +5,10 @@
import { ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import type * as vscode from 'vscode';
import * 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> { }
}

View File

@@ -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);
@@ -842,8 +870,14 @@ export namespace CompletionItem {
result.filterText = suggestion.filterText;
result.preselect = suggestion.preselect;
result.commitCharacters = suggestion.commitCharacters;
result.range = editorRange.Range.isIRange(suggestion.range) ? Range.to(suggestion.range) : undefined;
result.range2 = editorRange.Range.isIRange(suggestion.range) ? undefined : { inserting: Range.to(suggestion.range.insert), replacing: Range.to(suggestion.range.replace) };
// range
if (editorRange.Range.isIRange(suggestion.range)) {
result.range = Range.to(suggestion.range);
} else if (typeof suggestion.range === 'object') {
result.range = { inserting: Range.to(suggestion.range.insert), replacing: Range.to(suggestion.range.replace) };
}
result.keepWhitespace = typeof suggestion.insertTextRules === 'undefined' ? false : Boolean(suggestion.insertTextRules & modes.CompletionItemInsertTextRule.KeepWhitespace);
// 'insertText'-logic
if (typeof suggestion.insertTextRules !== 'undefined' && suggestion.insertTextRules & modes.CompletionItemInsertTextRule.InsertAsSnippet) {

View File

@@ -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;
@@ -1364,8 +1378,7 @@ export class CompletionItem implements vscode.CompletionItem {
preselect?: boolean;
insertText?: string | SnippetString;
keepWhitespace?: boolean;
range?: Range;
range2?: Range | { inserting: Range; replacing: Range; };
range?: Range | { inserting: Range; replacing: Range; };
commitCharacters?: string[];
textEdit?: TextEdit;
additionalTextEdits?: TextEdit[];
@@ -1379,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,
@@ -1395,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) {
@@ -1825,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;
@@ -1849,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;
@@ -1885,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;
@@ -2525,3 +2544,12 @@ export enum ColorThemeKind {
}
//#endregion Theming
//#region Timeline
@es5ClassCompat
export class TimelineItem implements vscode.TimelineItem {
constructor(public label: string, public timestamp: number) { }
}
//#endregion Timeline

View File

@@ -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;
}
}

View File

@@ -25,7 +25,7 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { Range, RelativePattern } from 'vs/workbench/api/common/extHostTypes';
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
import { IRawFileMatch2, resultIsMatch } from 'vs/workbench/services/search/common/search';
import type * as vscode from 'vscode';
import * as vscode from 'vscode';
import { ExtHostWorkspaceShape, IWorkspaceData, MainContext, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol';
export interface IExtHostWorkspaceProvider {

View File

@@ -304,7 +304,7 @@ namespace schema {
type: 'string'
},
icon: {
description: localize('vscode.extension.contributes.commandType.icon', '(Optional) Icon which is used to represent the command in the UI. Either a file path or a themable configuration'),
description: localize('vscode.extension.contributes.commandType.icon', '(Optional) Icon which is used to represent the command in the UI. Either a file path, an object with file paths for dark and light themes, or a theme icon references, like `$(zap)`'),
anyOf: [{
type: 'string'
},
@@ -414,7 +414,7 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyM
}
const menu = schema.parseMenuId(entry.key);
if (typeof menu !== 'number') {
if (typeof menu === 'undefined') {
collector.warn(localize('menuId.invalid', "`{0}` is not a valid menu identifier", entry.key));
return;
}