Merge from vscode cfc1ab4c5f816765b91fb7ead3c3427a7c8581a3

This commit is contained in:
ADS Merger
2020-03-11 04:19:23 +00:00
parent 16fab722d5
commit 4c3e48773d
880 changed files with 20441 additions and 11232 deletions

View File

@@ -146,7 +146,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostClipboard = new ExtHostClipboard(rpcProtocol);
const extHostMessageService = new ExtHostMessageService(rpcProtocol, extHostLogService);
const extHostDialogs = new ExtHostDialogs(rpcProtocol);
const extHostStatusBar = new ExtHostStatusBar(rpcProtocol);
const extHostStatusBar = new ExtHostStatusBar(rpcProtocol, extHostCommands.converter);
const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments);
// Register API-ish commands
@@ -188,12 +188,21 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable {
return extHostAuthentication.registerAuthenticationProvider(provider);
},
get providers() {
return extHostAuthentication.providers(extension);
},
get onDidChangeAuthenticationProviders(): Event<vscode.AuthenticationProvidersChangeEvent> {
return extHostAuthentication.onDidChangeAuthenticationProviders;
}
},
get providerIds(): string[] {
return extHostAuthentication.providerIds;
},
getSessions(providerId: string, scopes: string[]): Thenable<readonly vscode.AuthenticationSession[]> {
return extHostAuthentication.getSessions(extension, providerId, scopes);
},
login(providerId: string, scopes: string[]): Thenable<vscode.AuthenticationSession> {
return extHostAuthentication.login(extension, providerId, scopes);
},
get onDidChangeSessions(): Event<string[]> {
return extHostAuthentication.onDidChangeSessions;
},
};
// namespace: commands
@@ -769,11 +778,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostTunnelService.onDidChangeTunnels(listener, thisArg, disposables);
},
onDidTunnelsChange: (listener, thisArg?, disposables?) => {
checkProposedApiEnabled(extension);
return extHostTunnelService.onDidChangeTunnels(listener, thisArg, disposables);
},
registerTimelineProvider: (scheme: string | string[], provider: vscode.TimelineProvider) => {
checkProposedApiEnabled(extension);
@@ -1037,6 +1041,7 @@ class Extension<T> implements vscode.Extension<T> {
private _identifier: ExtensionIdentifier;
readonly id: string;
readonly extensionUri: URI;
readonly extensionPath: string;
readonly packageJSON: IExtensionDescription;
readonly extensionKind: vscode.ExtensionKind;
@@ -1046,6 +1051,7 @@ class Extension<T> implements vscode.Extension<T> {
this._originExtensionId = originExtensionId;
this._identifier = description.identifier;
this.id = description.identifier.value;
this.extensionUri = description.extensionLocation;
this.extensionPath = path.normalize(originalFSPath(description.extensionLocation));
this.packageJSON = description;
this.extensionKind = kind;

View File

@@ -49,7 +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 { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline';
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline';
import { revive } from 'vs/base/common/marshalling';
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { Dto } from 'vs/base/common/types';
@@ -371,7 +371,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;
$registerDocumentSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend): void;
$registerDocumentSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend, eventHandle: number | undefined): void;
$emitDocumentSemanticTokensEvent(eventHandle: number): 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;
@@ -536,7 +537,7 @@ export interface MainThreadQuickOpenShape extends IDisposable {
}
export interface MainThreadStatusBarShape extends IDisposable {
$setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: string | undefined, color: string | ThemeColor | undefined, alignment: statusbar.StatusbarAlignment, priority: number | undefined): void;
$setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: ICommandDto | undefined, color: string | ThemeColor | undefined, alignment: statusbar.StatusbarAlignment, priority: number | undefined): void;
$dispose(id: number): void;
}
@@ -621,7 +622,7 @@ export interface ExtHostWebviewsShape {
$undo(resource: UriComponents, viewType: string): void;
$redo(resource: UriComponents, viewType: string): void;
$revert(resource: UriComponents, viewType: string): void;
$onSave(resource: UriComponents, viewType: string): Promise<void>;
$onSave(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void>;
$onSaveAs(resource: UriComponents, viewType: string, targetResource: UriComponents): Promise<void>;
$backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void>;
@@ -804,6 +805,7 @@ export interface MainThreadTunnelServiceShape extends IDisposable {
$registerCandidateFinder(): Promise<void>;
$setTunnelProvider(): Promise<void>;
$setCandidateFilter(): Promise<void>;
$tunnelServiceReady(): Promise<void>;
}
export interface MainThreadTimelineShape extends IDisposable {
@@ -1472,7 +1474,7 @@ export interface ExtHostTunnelServiceShape {
}
export interface ExtHostTimelineShape {
$getTimeline(source: string, uri: UriComponents, options: TimelineOptions, token: CancellationToken, internalOptions?: { cacheResults?: boolean }): Promise<Timeline | undefined>;
$getTimeline(source: string, uri: UriComponents, options: TimelineOptions, token: CancellationToken, internalOptions?: InternalTimelineOptions): Promise<Timeline | undefined>;
}
// --- proxy identifiers

View File

@@ -174,7 +174,7 @@ const newCommands: ApiCommand[] = [
// -- selection range
new ApiCommand(
'vscode.executeSelectionRangeProvider', '_executeSelectionRangeProvider', 'Execute selection range provider.',
[ApiCommandArgument.Uri, new ApiCommandArgument('position', 'A positions in a text document', v => Array.isArray(v) && v.every(v => types.Position.isPosition(v)), v => v.map(typeConverters.Position.from))],
[ApiCommandArgument.Uri, new ApiCommandArgument<types.Position[], IPosition[]>('position', 'A positions in a text document', v => Array.isArray(v) && v.every(v => types.Position.isPosition(v)), v => v.map(typeConverters.Position.from))],
new ApiCommandResult<IRange[][], types.SelectionRange[]>('A promise that resolves to an array of ranges.', result => {
return result.map(ranges => {
let node: types.SelectionRange | undefined;

View File

@@ -10,61 +10,6 @@ import { IMainContext, MainContext, MainThreadAuthenticationShape, ExtHostAuthen
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export class AuthenticationProviderWrapper implements vscode.AuthenticationProvider {
readonly onDidChangeSessions: vscode.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>> {
return (await this._provider.getSessions()).map(session => {
return {
id: session.id,
accountName: session.accountName,
scopes: session.scopes,
getAccessToken: async () => {
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 token access.');
}
return session.getAccessToken();
}
};
});
}
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): Thenable<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>();
@@ -72,14 +17,65 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
private _onDidChangeAuthenticationProviders = new Emitter<vscode.AuthenticationProvidersChangeEvent>();
readonly onDidChangeAuthenticationProviders: Event<vscode.AuthenticationProvidersChangeEvent> = this._onDidChangeAuthenticationProviders.event;
private _onDidChangeSessions = new Emitter<string[]>();
readonly onDidChangeSessions: Event<string[]> = this._onDidChangeSessions.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;
get providerIds(): string[] {
const ids: string[] = [];
this._authenticationProviders.forEach(provider => {
ids.push(provider.id);
});
return ids;
}
async getSessions(requestingExtension: IExtensionDescription, providerId: string, scopes: string[]): Promise<readonly vscode.AuthenticationSession[]> {
const provider = this._authenticationProviders.get(providerId);
if (!provider) {
throw new Error(`No authentication provider with id '${providerId}' is currently registered.`);
}
const orderedScopes = scopes.sort().join(' ');
return (await provider.getSessions())
.filter(session => session.scopes.sort().join(' ') === orderedScopes)
.map(session => {
return {
id: session.id,
accountName: session.accountName,
scopes: session.scopes,
getAccessToken: async () => {
const isAllowed = await this._proxy.$getSessionsPrompt(
provider.id,
provider.displayName,
ExtensionIdentifier.toKey(requestingExtension.identifier),
requestingExtension.displayName || requestingExtension.name);
if (!isAllowed) {
throw new Error('User did not consent to token access.');
}
return session.getAccessToken();
}
};
});
}
async login(requestingExtension: IExtensionDescription, providerId: string, scopes: string[]): Promise<vscode.AuthenticationSession> {
const provider = this._authenticationProviders.get(providerId);
if (!provider) {
throw new Error(`No authentication provider with id '${providerId}' is currently registered.`);
}
const isAllowed = await this._proxy.$loginPrompt(provider.id, provider.displayName, ExtensionIdentifier.toKey(requestingExtension.identifier), requestingExtension.displayName || requestingExtension.name);
if (!isAllowed) {
throw new Error('User did not consent to login.');
}
return provider.login(scopes);
}
registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable {
@@ -91,6 +87,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
const listener = provider.onDidChangeSessions(_ => {
this._proxy.$onDidChangeSessions(provider.id);
this._onDidChangeSessions.fire([provider.id]);
});
this._proxy.$registerAuthenticationProvider(provider.id, provider.displayName);

View File

@@ -12,7 +12,6 @@ import { DiagnosticSeverity } from './extHostTypes';
import * as converter from './extHostTypeConverters';
import { mergeSort } from 'vs/base/common/arrays';
import { Event, Emitter } from 'vs/base/common/event';
import { keys } from 'vs/base/common/map';
import { ILogService } from 'vs/platform/log/common/log';
export class DiagnosticCollection implements vscode.DiagnosticCollection {
@@ -36,7 +35,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
dispose(): void {
if (!this._isDisposed) {
this._onDidChangeDiagnostics.fire(keys(this._data));
this._onDidChangeDiagnostics.fire([...this._data.keys()]);
if (this._proxy) {
this._proxy.$clear(this._owner);
}
@@ -169,7 +168,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
clear(): void {
this._checkDisposed();
this._onDidChangeDiagnostics.fire(keys(this._data));
this._onDidChangeDiagnostics.fire([...this._data.keys()]);
this._data.clear();
if (this._proxy) {
this._proxy.$clear(this._owner);

View File

@@ -252,7 +252,15 @@ export class ExtensionsActivator {
return;
}
const currentExtension = this._registry.getExtensionDescription(currentActivation.id)!;
const currentExtension = this._registry.getExtensionDescription(currentActivation.id);
if (!currentExtension) {
// Error condition 0: unknown extension
this._host.onExtensionActivationError(currentActivation.id, new MissingDependencyError(currentActivation.id.value));
const error = new Error(`Unknown dependency '${currentActivation.id.value}'`);
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentActivation.id), new FailedExtension(error));
return;
}
const depIds = (typeof currentExtension.extensionDependencies === 'undefined' ? [] : currentExtension.extensionDependencies);
let currentExtensionGetsGreenLight = true;

View File

@@ -366,6 +366,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
globalState,
workspaceState,
subscriptions: [],
get extensionUri() { return extensionDescription.extensionLocation; },
get extensionPath() { return extensionDescription.extensionLocation.fsPath; },
get storagePath() { return that._storagePath.workspaceValue(extensionDescription); },
get globalStoragePath() { return that._storagePath.globalValue(extensionDescription); },

View File

@@ -148,7 +148,16 @@ class ConsumerFileSystem implements vscode.FileSystem {
}
// file system error
throw new FileSystemError(err.message, err.name as files.FileSystemProviderErrorCode);
switch (err.name) {
case files.FileSystemProviderErrorCode.FileExists: throw FileSystemError.FileExists(err.message);
case files.FileSystemProviderErrorCode.FileNotFound: throw FileSystemError.FileNotFound(err.message);
case files.FileSystemProviderErrorCode.FileNotADirectory: throw FileSystemError.FileNotADirectory(err.message);
case files.FileSystemProviderErrorCode.FileIsADirectory: throw FileSystemError.FileIsADirectory(err.message);
case files.FileSystemProviderErrorCode.NoPermissions: throw FileSystemError.NoPermissions(err.message);
case files.FileSystemProviderErrorCode.Unavailable: throw FileSystemError.Unavailable(err.message);
default: throw new FileSystemError(err.message, err.name as files.FileSystemProviderErrorCode);
}
}
}

View File

@@ -1702,9 +1702,19 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
//#region semantic coloring
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);
const handle = this._nextHandle();
const eventHandle = (typeof provider.onDidChangeSemanticTokens === 'function' ? this._nextHandle() : undefined);
this._adapter.set(handle, new AdapterData(new DocumentSemanticTokensAdapter(this._documents, provider), extension));
this._proxy.$registerDocumentSemanticTokensProvider(handle, this._transformDocumentSelector(selector), legend, eventHandle);
let result = this._createDisposable(handle);
if (eventHandle) {
const subscription = provider.onDidChangeSemanticTokens!(_ => this._proxy.$emitDocumentSemanticTokensEvent(eventHandle));
result = Disposable.from(result, subscription);
}
return result;
}
$provideDocumentSemanticTokens(handle: number, resource: UriComponents, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null> {

View File

@@ -5,11 +5,13 @@
import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/workbench/services/statusbar/common/statusbar';
import { StatusBarAlignment as ExtHostStatusBarAlignment, Disposable, ThemeColor } from './extHostTypes';
import { StatusBarItem, StatusBarAlignment } from 'vscode';
import { MainContext, MainThreadStatusBarShape, IMainContext } from './extHost.protocol';
import type * as vscode from 'vscode';
import { MainContext, MainThreadStatusBarShape, IMainContext, ICommandDto } from './extHost.protocol';
import { localize } from 'vs/nls';
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
import { DisposableStore } from 'vs/base/common/lifecycle';
export class ExtHostStatusBarEntry implements StatusBarItem {
export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
private static ID_GEN = 0;
private _id: number;
@@ -24,14 +26,20 @@ export class ExtHostStatusBarEntry implements StatusBarItem {
private _text: string = '';
private _tooltip?: string;
private _color?: string | ThemeColor;
private _command?: string;
private readonly _internalCommandRegistration = new DisposableStore();
private _command?: {
readonly fromApi: string | vscode.Command,
readonly internal: ICommandDto,
};
private _timeoutHandle: any;
private _proxy: MainThreadStatusBarShape;
private _commands: CommandsConverter;
constructor(proxy: MainThreadStatusBarShape, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) {
constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) {
this._id = ExtHostStatusBarEntry.ID_GEN++;
this._proxy = proxy;
this._commands = commands;
this._statusId = id;
this._statusName = name;
this._alignment = alignment;
@@ -42,7 +50,7 @@ export class ExtHostStatusBarEntry implements StatusBarItem {
return this._id;
}
public get alignment(): StatusBarAlignment {
public get alignment(): vscode.StatusBarAlignment {
return this._alignment;
}
@@ -62,8 +70,8 @@ export class ExtHostStatusBarEntry implements StatusBarItem {
return this._color;
}
public get command(): string | undefined {
return this._command;
public get command(): string | vscode.Command | undefined {
return this._command?.fromApi;
}
public set text(text: string) {
@@ -81,8 +89,25 @@ export class ExtHostStatusBarEntry implements StatusBarItem {
this.update();
}
public set command(command: string | undefined) {
this._command = command;
public set command(command: string | vscode.Command | undefined) {
if (this._command?.fromApi === command) {
return;
}
this._internalCommandRegistration.clear();
if (typeof command === 'string') {
this._command = {
fromApi: command,
internal: this._commands.toInternal({ title: '', command }, this._internalCommandRegistration),
};
} else if (command) {
this._command = {
fromApi: command,
internal: this._commands.toInternal(command, this._internalCommandRegistration),
};
} else {
this._command = undefined;
}
this.update();
}
@@ -109,7 +134,7 @@ export class ExtHostStatusBarEntry implements StatusBarItem {
this._timeoutHandle = undefined;
// Set to status bar
this._proxy.$setEntry(this.id, this._statusId, this._statusName, this.text, this.tooltip, this.command, this.color,
this._proxy.$setEntry(this.id, this._statusId, this._statusName, this.text, this.tooltip, this._command?.internal, this.color,
this._alignment === ExtHostStatusBarAlignment.Left ? MainThreadStatusBarAlignment.LEFT : MainThreadStatusBarAlignment.RIGHT,
this._priority);
}, 0);
@@ -123,7 +148,7 @@ export class ExtHostStatusBarEntry implements StatusBarItem {
class StatusBarMessage {
private _item: StatusBarItem;
private _item: vscode.StatusBarItem;
private _messages: { message: string }[] = [];
constructor(statusBar: ExtHostStatusBar) {
@@ -161,16 +186,18 @@ class StatusBarMessage {
export class ExtHostStatusBar {
private _proxy: MainThreadStatusBarShape;
private readonly _proxy: MainThreadStatusBarShape;
private readonly _commands: CommandsConverter;
private _statusMessage: StatusBarMessage;
constructor(mainContext: IMainContext) {
constructor(mainContext: IMainContext, commands: CommandsConverter) {
this._proxy = mainContext.getProxy(MainContext.MainThreadStatusBar);
this._commands = commands;
this._statusMessage = new StatusBarMessage(this);
}
createStatusBarEntry(id: string, name: string, alignment?: ExtHostStatusBarAlignment, priority?: number): StatusBarItem {
return new ExtHostStatusBarEntry(this._proxy, id, name, alignment, priority);
createStatusBarEntry(id: string, name: string, alignment?: ExtHostStatusBarAlignment, priority?: number): vscode.StatusBarItem {
return new ExtHostStatusBarEntry(this._proxy, this._commands, id, name, alignment, priority);
}
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): Disposable {

View File

@@ -7,7 +7,7 @@ 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 { Timeline, TimelineItem, TimelineOptions, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline';
import { Timeline, TimelineItem, TimelineOptions, TimelineProvider, InternalTimelineOptions } 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, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
@@ -16,21 +16,19 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export interface IExtHostTimeline extends ExtHostTimelineShape {
readonly _serviceBrand: undefined;
$getTimeline(id: string, uri: UriComponents, options: vscode.TimelineOptions, token: vscode.CancellationToken, internalOptions?: { cacheResults?: boolean }): Promise<Timeline | undefined>;
$getTimeline(id: string, uri: UriComponents, options: vscode.TimelineOptions, token: vscode.CancellationToken, internalOptions?: InternalTimelineOptions): Promise<Timeline | undefined>;
}
export const IExtHostTimeline = createDecorator<IExtHostTimeline>('IExtHostTimeline');
export class ExtHostTimeline implements IExtHostTimeline {
private static handlePool = 0;
_serviceBrand: undefined;
private _proxy: MainThreadTimelineShape;
private _providers = new Map<string, TimelineProvider>();
private _itemsBySourceByUriMap = new Map<string | undefined, Map<string, Map<string, vscode.TimelineItem>>>();
private _itemsBySourceAndUriMap = new Map<string, Map<string | undefined, Map<string, vscode.TimelineItem>>>();
constructor(
mainContext: IMainContext,
@@ -42,7 +40,7 @@ export class ExtHostTimeline implements IExtHostTimeline {
processArgument: arg => {
if (arg && arg.$mid === 11) {
const uri = arg.uri === undefined ? undefined : URI.revive(arg.uri);
return this._itemsBySourceByUriMap.get(getUriKey(uri))?.get(arg.source)?.get(arg.handle);
return this._itemsBySourceAndUriMap.get(arg.source)?.get(getUriKey(uri))?.get(arg.handle);
}
return arg;
@@ -50,7 +48,7 @@ export class ExtHostTimeline implements IExtHostTimeline {
});
}
async $getTimeline(id: string, uri: UriComponents, options: vscode.TimelineOptions, token: vscode.CancellationToken, internalOptions?: { cacheResults?: boolean }): Promise<Timeline | undefined> {
async $getTimeline(id: string, uri: UriComponents, options: vscode.TimelineOptions, token: vscode.CancellationToken, internalOptions?: InternalTimelineOptions): Promise<Timeline | undefined> {
const provider = this._providers.get(id);
return provider?.provideTimeline(URI.revive(uri), options, token, internalOptions);
}
@@ -62,26 +60,21 @@ export class ExtHostTimeline implements IExtHostTimeline {
let disposable: IDisposable | undefined;
if (provider.onDidChange) {
disposable = provider.onDidChange(this.emitTimelineChangeEvent(provider.id), this);
disposable = provider.onDidChange(e => this._proxy.$emitTimelineChangeEvent({ ...e, id: provider.id }), this);
}
const itemsBySourceByUriMap = this._itemsBySourceByUriMap;
const itemsBySourceAndUriMap = this._itemsBySourceAndUriMap;
return this.registerTimelineProviderCore({
...provider,
scheme: scheme,
onDidChange: undefined,
async provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: { cacheResults?: boolean }) {
// For now, only allow the caching of a single Uri
if (internalOptions?.cacheResults) {
if (options.cursor === undefined) {
timelineDisposables.clear();
}
if (!itemsBySourceByUriMap.has(getUriKey(uri))) {
itemsBySourceByUriMap.clear();
}
} else {
async provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: InternalTimelineOptions) {
if (internalOptions?.resetCache) {
timelineDisposables.clear();
// For now, only allow the caching of a single Uri
// itemsBySourceAndUriMap.get(provider.id)?.get(getUriKey(uri))?.clear();
itemsBySourceAndUriMap.get(provider.id)?.clear();
}
const result = await provider.provideTimeline(uri, options, token);
@@ -91,8 +84,9 @@ export class ExtHostTimeline implements IExtHostTimeline {
return undefined;
}
// TODO: Determine if we should cache dependent on who calls us (internal vs external)
const convertItem = convertTimelineItem(uri, internalOptions?.cacheResults ?? false);
// TODO: Should we bother converting all the data if we aren't caching? Meaning it is being requested by an extension?
const convertItem = convertTimelineItem(uri, internalOptions);
return {
...result,
source: provider.id,
@@ -100,6 +94,10 @@ export class ExtHostTimeline implements IExtHostTimeline {
};
},
dispose() {
for (const sourceMap of itemsBySourceAndUriMap.values()) {
sourceMap.get(provider.id)?.clear();
}
disposable?.dispose();
timelineDisposables.dispose();
}
@@ -107,29 +105,28 @@ export class ExtHostTimeline implements IExtHostTimeline {
}
private convertTimelineItem(source: string, commandConverter: CommandsConverter, disposables: DisposableStore) {
return (uri: URI, cacheResults: boolean) => {
let itemsMap: Map<string, vscode.TimelineItem> | undefined;
if (cacheResults) {
const uriKey = getUriKey(uri);
let sourceMap = this._itemsBySourceByUriMap.get(uriKey);
if (sourceMap === undefined) {
sourceMap = new Map();
this._itemsBySourceByUriMap.set(uriKey, sourceMap);
return (uri: URI, options?: InternalTimelineOptions) => {
let items: Map<string, vscode.TimelineItem> | undefined;
if (options?.cacheResults) {
let itemsByUri = this._itemsBySourceAndUriMap.get(source);
if (itemsByUri === undefined) {
itemsByUri = new Map();
this._itemsBySourceAndUriMap.set(source, itemsByUri);
}
itemsMap = sourceMap.get(source);
if (itemsMap === undefined) {
itemsMap = new Map();
sourceMap.set(source, itemsMap);
const uriKey = getUriKey(uri);
items = itemsByUri.get(uriKey);
if (items === undefined) {
items = new Map();
itemsByUri.set(uriKey, items);
}
}
return (item: vscode.TimelineItem): TimelineItem => {
const { iconPath, ...props } = item;
const handle = `${source}|${item.id ?? `${item.timestamp}-${ExtHostTimeline.handlePool++}`}`;
itemsMap?.set(handle, item);
const handle = `${source}|${item.id ?? item.timestamp}`;
items?.set(handle, item);
let icon;
let iconDark;
@@ -161,22 +158,6 @@ export class ExtHostTimeline implements IExtHostTimeline {
};
}
private emitTimelineChangeEvent(id: string) {
return (e: vscode.TimelineChangeEvent) => {
// Clear caches
if (e?.uri === undefined) {
for (const sourceMap of this._itemsBySourceByUriMap.values()) {
sourceMap.get(id)?.clear();
}
}
else {
this._itemsBySourceByUriMap.get(getUriKey(e.uri))?.clear();
}
this._proxy.$emitTimelineChangeEvent({ ...e, id: id });
};
}
private registerTimelineProviderCore(provider: TimelineProvider): IDisposable {
// console.log(`ExtHostTimeline#registerTimelineProvider: id=${provider.id}`);
@@ -193,7 +174,7 @@ export class ExtHostTimeline implements IExtHostTimeline {
this._providers.set(provider.id, provider);
return toDisposable(() => {
for (const sourceMap of this._itemsBySourceByUriMap.values()) {
for (const sourceMap of this._itemsBySourceAndUriMap.values()) {
sourceMap.get(provider.id)?.clear();
}

View File

@@ -3,16 +3,17 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostTunnelServiceShape, MainContext, MainThreadTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
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';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
export interface TunnelDto {
remoteAddress: { port: number, host: string };
localAddress: string;
localAddress: { port: number, host: string } | string;
}
export namespace TunnelDto {
@@ -42,6 +43,13 @@ export const IExtHostTunnelService = createDecorator<IExtHostTunnelService>('IEx
export class ExtHostTunnelService implements IExtHostTunnelService {
_serviceBrand: undefined;
onDidChangeTunnels: vscode.Event<void> = (new Emitter<void>()).event;
private readonly _proxy: MainThreadTunnelServiceShape;
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTunnelService);
}
async openTunnel(forward: TunnelOptions): Promise<vscode.Tunnel | undefined> {
return undefined;
@@ -55,7 +63,10 @@ export class ExtHostTunnelService implements IExtHostTunnelService {
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: () => { } }; }
async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> {
await this._proxy.$tunnelServiceReady();
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

@@ -7,7 +7,6 @@ import { coalesce, equals } from 'vs/base/common/arrays';
import { illegalArgument } from 'vs/base/common/errors';
import { IRelativePattern } from 'vs/base/common/glob';
import { isMarkdownString } from 'vs/base/common/htmlContent';
import { values } from 'vs/base/common/map';
import { startsWith } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
@@ -44,16 +43,16 @@ export class Disposable {
});
}
private _callOnDispose?: () => any;
#callOnDispose?: () => any;
constructor(callOnDispose: () => any) {
this._callOnDispose = callOnDispose;
this.#callOnDispose = callOnDispose;
}
dispose(): any {
if (typeof this._callOnDispose === 'function') {
this._callOnDispose();
this._callOnDispose = undefined;
if (typeof this.#callOnDispose === 'function') {
this.#callOnDispose();
this.#callOnDispose = undefined;
}
}
}
@@ -661,7 +660,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
textEdit[1].push(candidate.edit);
}
}
return values(textEdits);
return [...textEdits.values()];
}
allEntries(): ReadonlyArray<IFileTextEdit | IFileOperation> {
@@ -2334,9 +2333,13 @@ export class FileSystemError extends Error {
return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.Unavailable, FileSystemError.Unavailable);
}
readonly code?: string;
constructor(uriOrMessage?: string | URI, code: FileSystemProviderErrorCode = FileSystemProviderErrorCode.Unknown, terminator?: Function) {
super(URI.isUri(uriOrMessage) ? uriOrMessage.toString(true) : uriOrMessage);
this.code = terminator?.name;
// mark the error as file system provider error so that
// we can extract the error code on the receiving side
markAsFileSystemProviderError(this, code);

View File

@@ -104,19 +104,20 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
private _title: string;
private _iconPath?: IconPath;
private readonly _options: vscode.WebviewPanelOptions;
private readonly _webview: ExtHostWebview;
private _viewColumn: vscode.ViewColumn | undefined;
private _visible: boolean = true;
private _active: boolean = true;
readonly #options: vscode.WebviewPanelOptions;
readonly #webview: ExtHostWebview;
_isDisposed: boolean = false;
#viewColumn: vscode.ViewColumn | undefined = undefined;
#visible: boolean = true;
#active: boolean = true;
readonly _onDisposeEmitter = this._register(new Emitter<void>());
public readonly onDidDispose: Event<void> = this._onDisposeEmitter.event;
#isDisposed: boolean = false;
readonly _onDidChangeViewStateEmitter = this._register(new Emitter<vscode.WebviewPanelOnDidChangeViewStateEvent>());
public readonly onDidChangeViewState: Event<vscode.WebviewPanelOnDidChangeViewStateEvent> = this._onDidChangeViewStateEmitter.event;
readonly #onDidDispose = this._register(new Emitter<void>());
public readonly onDidDispose = this.#onDidDispose.event;
readonly #onDidChangeViewState = this._register(new Emitter<vscode.WebviewPanelOnDidChangeViewStateEvent>());
public readonly onDidChangeViewState = this.#onDidChangeViewState.event;
constructor(
handle: WebviewPanelHandle,
@@ -131,27 +132,28 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
this._handle = handle;
this._proxy = proxy;
this._viewType = viewType;
this._options = editorOptions;
this._viewColumn = viewColumn;
this.#options = editorOptions;
this.#viewColumn = viewColumn;
this._title = title;
this._webview = webview;
this.#webview = webview;
}
public dispose() {
if (this._isDisposed) {
if (this.#isDisposed) {
return;
}
this._isDisposed = true;
this._onDisposeEmitter.fire();
this.#isDisposed = true;
this.#onDidDispose.fire();
this._proxy.$disposeWebview(this._handle);
this._webview.dispose();
this.#webview.dispose();
super.dispose();
}
get webview() {
this.assertNotDisposed();
return this._webview;
return this.#webview;
}
get viewType(): string {
@@ -187,42 +189,40 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
}
get options() {
return this._options;
return this.#options;
}
get viewColumn(): vscode.ViewColumn | undefined {
this.assertNotDisposed();
if (typeof this._viewColumn === 'number' && this._viewColumn < 0) {
if (typeof this.#viewColumn === 'number' && this.#viewColumn < 0) {
// We are using a symbolic view column
// Return undefined instead to indicate that the real view column is currently unknown but will be resolved.
return undefined;
}
return this._viewColumn;
}
_setViewColumn(value: vscode.ViewColumn) {
this.assertNotDisposed();
this._viewColumn = value;
return this.#viewColumn;
}
public get active(): boolean {
this.assertNotDisposed();
return this._active;
}
_setActive(value: boolean) {
this.assertNotDisposed();
this._active = value;
return this.#active;
}
public get visible(): boolean {
this.assertNotDisposed();
return this._visible;
return this.#visible;
}
_setVisible(value: boolean) {
this.assertNotDisposed();
this._visible = value;
_updateViewState(newState: { active: boolean; visible: boolean; viewColumn: vscode.ViewColumn; }) {
if (this.#isDisposed) {
return;
}
if (this.active !== newState.active || this.visible !== newState.visible || this.viewColumn !== newState.viewColumn) {
this.#active = newState.active;
this.#visible = newState.visible;
this.#viewColumn = newState.viewColumn;
this.#onDidChangeViewState.fire({ webviewPanel: this });
}
}
public postMessage(message: any): Promise<boolean> {
@@ -239,7 +239,7 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
}
private assertNotDisposed() {
if (this._isDisposed) {
if (this.#isDisposed) {
throw new Error('Webview is disposed');
}
}
@@ -303,23 +303,26 @@ class CustomDocument extends Disposable implements vscode.CustomDocument {
});
}
/** @internal*/ _revert() {
/** @internal*/ async _revert() {
const editing = this.getEditingCapability();
if (this.#currentEditIndex === this.#savePoint) {
return true;
}
let undoneEdits: EditType[] = [];
let appliedEdits: EditType[] = [];
if (this.#currentEditIndex >= this.#savePoint) {
const editsToUndo = this.#edits.slice(this.#savePoint, this.#currentEditIndex);
editing.undoEdits(editsToUndo.reverse());
undoneEdits = this.#edits.slice(this.#savePoint, this.#currentEditIndex).reverse();
} else if (this.#currentEditIndex < this.#savePoint) {
const editsToRedo = this.#edits.slice(this.#currentEditIndex, this.#savePoint);
editing.applyEdits(editsToRedo);
appliedEdits = this.#edits.slice(this.#currentEditIndex, this.#savePoint);
}
this.#currentEditIndex = this.#savePoint;
this.spliceEdits();
await editing.revert({ undoneEdits, appliedEdits });
this.updateState();
return true;
}
@@ -350,8 +353,8 @@ class CustomDocument extends Disposable implements vscode.CustomDocument {
this.updateState();
}
/** @internal*/ _save() {
return this.getEditingCapability().save();
/** @internal*/ _save(cancellation: CancellationToken) {
return this.getEditingCapability().save(cancellation);
}
/** @internal*/ _saveAs(target: vscode.Uri) {
@@ -585,18 +588,16 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
for (const handle of handles) {
const panel = this.getWebviewPanel(handle);
if (!panel || panel._isDisposed) {
if (!panel) {
continue;
}
const newState = newStates[handle];
const viewColumn = typeConverters.ViewColumn.to(newState.position);
if (panel.active !== newState.active || panel.visible !== newState.visible || panel.viewColumn !== viewColumn) {
panel._setActive(newState.active);
panel._setVisible(newState.visible);
panel._setViewColumn(viewColumn);
panel._onDidChangeViewStateEmitter.fire({ webviewPanel: panel });
}
panel._updateViewState({
active: newState.active,
visible: newState.visible,
viewColumn: typeConverters.ViewColumn.to(newState.position),
});
}
}
@@ -659,7 +660,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
const revivedResource = URI.revive(resource);
const document = this.getDocument(viewType, revivedResource);
const document = this.getCustomDocument(viewType, revivedResource);
this._documents.delete(document);
document.dispose();
}
@@ -686,12 +687,11 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
switch (entry.type) {
case WebviewEditorType.Custom:
{
const document = this.getDocument(viewType, revivedResource);
const document = this.getCustomDocument(viewType, revivedResource);
return entry.provider.resolveCustomEditor(document, revivedPanel);
}
case WebviewEditorType.Text:
{
await this._extHostDocuments.ensureDocumentData(revivedResource);
const document = this._extHostDocuments.getDocument(revivedResource);
return entry.provider.resolveCustomTextEditor(document, revivedPanel);
}
@@ -703,32 +703,32 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
async $undo(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
const document = this.getCustomDocument(viewType, resourceComponents);
document._undo();
}
async $redo(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
const document = this.getCustomDocument(viewType, resourceComponents);
document._redo();
}
async $revert(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
const document = this.getCustomDocument(viewType, resourceComponents);
document._revert();
}
async $onSave(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
document._save();
async $onSave(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
const document = this.getCustomDocument(viewType, resourceComponents);
document._save(cancellation);
}
async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
const document = this.getCustomDocument(viewType, resourceComponents);
return document._saveAs(URI.revive(targetResource));
}
async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
const document = this.getCustomDocument(viewType, resourceComponents);
return document._backup(cancellation);
}
@@ -736,7 +736,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
return this._webviewPanels.get(handle);
}
private getDocument(viewType: string, resource: UriComponents): CustomDocument {
private getCustomDocument(viewType: string, resource: UriComponents): CustomDocument {
const document = this._documents.get(viewType, URI.revive(resource));
if (!document) {
throw new Error('No webview editor custom document found');

View File

@@ -473,7 +473,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
ignoreSymlinks: typeof options.followSymlinks === 'boolean' ? !options.followSymlinks : undefined,
disregardIgnoreFiles: typeof options.useIgnoreFiles === 'boolean' ? !options.useIgnoreFiles : undefined,
disregardGlobalIgnoreFiles: typeof options.useGlobalIgnoreFiles === 'boolean' ? !options.useGlobalIgnoreFiles : undefined,
disregardExcludeSettings: options.exclude === null,
disregardExcludeSettings: typeof options.useDefaultExcludes === 'boolean' ? !options.useDefaultExcludes : true,
fileEncoding: options.encoding,
maxResults: options.maxResults,
previewOptions,

View File

@@ -7,9 +7,10 @@ import * as nls from 'vs/nls';
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import * as strings from 'vs/base/common/strings';
import * as resources from 'vs/base/common/resources';
import { isString } from 'vs/base/common/types';
interface IJSONValidationExtensionPoint {
fileMatch: string;
fileMatch: string | string[];
url: string;
}
@@ -25,8 +26,11 @@ const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint<IJSONVal
defaultSnippets: [{ body: { fileMatch: '${1:file.json}', url: '${2:url}' } }],
properties: {
fileMatch: {
type: 'string',
description: nls.localize('contributes.jsonValidation.fileMatch', 'The file pattern to match, for example "package.json" or "*.launch".'),
type: ['string', 'array'],
description: nls.localize('contributes.jsonValidation.fileMatch', 'The file pattern (or an array of patterns) to match, for example "package.json" or "*.launch". Exclusion patterns start with \'!\''),
items: {
type: ['string']
}
},
url: {
description: nls.localize('contributes.jsonValidation.url', 'A schema URL (\'http:\', \'https:\') or relative path to the extension folder (\'./\').'),
@@ -51,12 +55,12 @@ export class JSONValidationExtensionPoint {
return;
}
extensionValue.forEach(extension => {
if (typeof extension.fileMatch !== 'string') {
collector.error(nls.localize('invalid.fileMatch', "'configuration.jsonValidation.fileMatch' must be defined"));
if (!isString(extension.fileMatch) && !(Array.isArray(extension.fileMatch) && extension.fileMatch.every(isString))) {
collector.error(nls.localize('invalid.fileMatch', "'configuration.jsonValidation.fileMatch' must be defined as a string or an array of strings."));
return;
}
let uri = extension.url;
if (typeof extension.url !== 'string') {
if (!isString(uri)) {
collector.error(nls.localize('invalid.url', "'configuration.jsonValidation.url' must be a URL or relative path"));
return;
}