Merge from vscode bead496a613e475819f89f08e9e882b841bc1fe8 (#14883)

* Merge from vscode bead496a613e475819f89f08e9e882b841bc1fe8

* Bump distro

* Upgrade GCC to 4.9 due to yarn install errors

* Update build image

* Fix bootstrap base url

* Bump distro

* Fix build errors

* Update source map file

* Disable checkbox for blocking migration issues (#15131)

* disable checkbox for blocking issues

* wip

* disable checkbox fixes

* fix strings

* Remove duplicate tsec command

* Default to off for tab color if settings not present

* re-skip failing tests

* Fix mocha error

* Bump sqlite version & fix notebooks search view

* Turn off esbuild warnings

* Update esbuild log level

* Fix overflowactionbar tests

* Fix ts-ignore in dropdown tests

* cleanup/fixes

* Fix hygiene

* Bundle in entire zone.js module

* Remove extra constructor param

* bump distro for web compile break

* bump distro for web compile break v2

* Undo log level change

* New distro

* Fix integration test scripts

* remove the "no yarn.lock changes" workflow

* fix scripts v2

* Update unit test scripts

* Ensure ads-kerberos2 updates in .vscodeignore

* Try fix unit tests

* Upload crash reports

* remove nogpu

* always upload crashes

* Use bash script

* Consolidate data/ext dir names

* Create in tmp directory

Co-authored-by: chlafreniere <hichise@gmail.com>
Co-authored-by: Christopher Suh <chsuh@microsoft.com>
Co-authored-by: chgagnon <chgagnon@microsoft.com>
This commit is contained in:
Karl Burtram
2021-04-27 14:01:59 -07:00
committed by GitHub
parent 7e1c0076ba
commit 867a963882
1817 changed files with 81812 additions and 50843 deletions

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI, UriComponents } from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
@@ -12,7 +12,6 @@ import { IWorkspacesService, IRecent } from 'vs/platform/workspaces/common/works
import { ILogService } from 'vs/platform/log/common/log';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IViewDescriptorService, IViewsService, ViewVisibilityState } from 'vs/workbench/common/views';
import { IOpenerService } from 'vs/platform/opener/common/opener';
// -----------------------------------------------------------------
// The following commands are registered on both sides separately.
@@ -128,12 +127,6 @@ CommandsRegistry.registerCommand('_extensionTests.setLogLevel', function (access
}
});
CommandsRegistry.registerCommand('_workbench.openExternal', function (accessor: ServicesAccessor, uri: UriComponents, options: { allowTunneling?: boolean }) {
// TODO: discuss martin, ben where to put this
const openerService = accessor.get(IOpenerService);
openerService.open(URI.revive(uri), options);
});
CommandsRegistry.registerCommand('_extensionTests.getLogLevel', function (accessor: ServicesAccessor) {
const logService = accessor.get(ILogService);

View File

@@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtHostSecretStateShape, MainContext, MainThreadSecretStateShape } from 'vs/workbench/api/common/extHost.protocol';
import { Emitter } from 'vs/base/common/event';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export class ExtHostSecretState implements ExtHostSecretStateShape {
private _proxy: MainThreadSecretStateShape;
private _onDidChangePassword = new Emitter<{ extensionId: string, key: string }>();
readonly onDidChangePassword = this._onDidChangePassword.event;
constructor(mainContext: IExtHostRpcService) {
this._proxy = mainContext.getProxy(MainContext.MainThreadSecretState);
}
async $onDidChangePassword(e: { extensionId: string, key: string }): Promise<void> {
this._onDidChangePassword.fire(e);
}
get(extensionId: string, key: string): Promise<string | undefined> {
return this._proxy.$getPassword(extensionId, key);
}
store(extensionId: string, key: string, value: string): Promise<void> {
return this._proxy.$setPassword(extensionId, key, value);
}
delete(extensionId: string, key: string): Promise<void> {
return this._proxy.$deletePassword(extensionId, key);
}
}
export interface IExtHostSecretState extends ExtHostSecretState { }
export const IExtHostSecretState = createDecorator<IExtHostSecretState>('IExtHostSecretState');

View File

@@ -82,6 +82,9 @@ import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPane
import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits';
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
import { ExtHostTesting } from 'vs/workbench/api/common/extHostTesting';
import { ExtHostUriOpeners } from 'vs/workbench/api/common/extHostUriOpener';
import { IExtHostSecretState } from 'vs/workbench/api/common/exHostSecretState';
import { ExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -107,6 +110,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostTunnelService = accessor.get(IExtHostTunnelService);
const extHostApiDeprecation = accessor.get(IExtHostApiDeprecationService);
const extHostWindow = accessor.get(IExtHostWindow);
const extHostSecretState = accessor.get(IExtHostSecretState);
// register addressable instances
rpcProtocol.set(ExtHostContext.ExtHostFileSystemInfo, extHostFileSystemInfo);
@@ -117,6 +121,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage);
rpcProtocol.set(ExtHostContext.ExtHostTunnelService, extHostTunnelService);
rpcProtocol.set(ExtHostContext.ExtHostWindow, extHostWindow);
rpcProtocol.set(ExtHostContext.ExtHostSecretState, extHostSecretState);
// automatically create and register addressable instances
const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, accessor.get(IExtHostDecorations));
@@ -129,6 +134,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, accessor.get(IExtHostOutputService));
// manually create and register addressable instances
const extHostEditorTabs = rpcProtocol.set(ExtHostContext.ExtHostEditorTabs, new ExtHostEditorTabs());
const extHostUrls = rpcProtocol.set(ExtHostContext.ExtHostUrls, new ExtHostUrls(rpcProtocol));
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService));
@@ -154,6 +160,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostCustomEditors = rpcProtocol.set(ExtHostContext.ExtHostCustomEditors, new ExtHostCustomEditors(rpcProtocol, extHostDocuments, extensionStoragePaths, extHostWebviews, extHostWebviewPanels));
const extHostWebviewViews = rpcProtocol.set(ExtHostContext.ExtHostWebviewViews, new ExtHostWebviewViews(rpcProtocol, extHostWebviews));
const extHostTesting = rpcProtocol.set(ExtHostContext.ExtHostTesting, new ExtHostTesting(rpcProtocol, extHostDocumentsAndEditors, extHostWorkspace));
const extHostUriOpeners = rpcProtocol.set(ExtHostContext.ExtHostUriOpeners, new ExtHostUriOpeners(rpcProtocol));
// Check that no named customers are missing
// {{SQL CARBON EDIT}} filter out the services we don't expose
@@ -212,22 +219,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
get onDidChangeSessions(): Event<vscode.AuthenticationSessionsChangeEvent> {
return extHostAuthentication.onDidChangeSessions;
},
registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable {
registerAuthenticationProvider(id: string, label: string, provider: vscode.AuthenticationProvider, options?: vscode.AuthenticationProviderOptions): vscode.Disposable {
checkProposedApiEnabled(extension);
return extHostAuthentication.registerAuthenticationProvider(provider);
return extHostAuthentication.registerAuthenticationProvider(id, label, provider, options);
},
get onDidChangeAuthenticationProviders(): Event<vscode.AuthenticationProvidersChangeEvent> {
checkProposedApiEnabled(extension);
return extHostAuthentication.onDidChangeAuthenticationProviders;
},
getProviderIds(): Thenable<ReadonlyArray<string>> {
checkProposedApiEnabled(extension);
return extHostAuthentication.getProviderIds();
},
get providerIds(): string[] {
checkProposedApiEnabled(extension);
return extHostAuthentication.providerIds;
},
get providers(): ReadonlyArray<vscode.AuthenticationProviderInformation> {
checkProposedApiEnabled(extension);
return extHostAuthentication.providers;
@@ -235,22 +234,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
logout(providerId: string, sessionId: string): Thenable<void> {
checkProposedApiEnabled(extension);
return extHostAuthentication.logout(providerId, sessionId);
},
getPassword(key: string): Thenable<string | undefined> {
checkProposedApiEnabled(extension);
return extHostAuthentication.getPassword(extension, key);
},
setPassword(key: string, value: string): Thenable<void> {
checkProposedApiEnabled(extension);
return extHostAuthentication.setPassword(extension, key, value);
},
deletePassword(key: string): Thenable<void> {
checkProposedApiEnabled(extension);
return extHostAuthentication.deletePassword(extension, key);
},
get onDidChangePassword(): Event<void> {
checkProposedApiEnabled(extension);
return extHostAuthentication.onDidChangePassword;
}
};
@@ -282,7 +265,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerDiffInformationCommand: (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => {
checkProposedApiEnabled(extension);
return extHostCommands.registerCommand(true, id, async (...args: any[]): Promise<any> => {
const activeTextEditor = extHostEditors.getActiveTextEditor();
const activeTextEditor = extHostDocumentsAndEditors.activeEditor(true);
if (!activeTextEditor) {
extHostLogService.warn('Cannot execute ' + id + ' because there is no active text editor.');
return undefined;
@@ -308,14 +291,15 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
get appName() { return initData.environment.appName; },
get appRoot() { return initData.environment.appRoot?.fsPath ?? ''; },
get uriScheme() { return initData.environment.appUriScheme; },
get clipboard(): vscode.Clipboard {
return extHostClipboard;
},
get clipboard(): vscode.Clipboard { return extHostClipboard.value; },
get shell() {
return extHostTerminalService.getDefaultShell(false, configProvider);
},
openExternal(uri: URI) {
return extHostWindow.openUri(uri, { allowTunneling: !!initData.remote.authority });
openExternal(uri: URI, options?: { allowContributedOpeners?: boolean | string; }) {
return extHostWindow.openUri(uri, {
allowTunneling: !!initData.remote.authority,
allowContributedOpeners: options?.allowContributedOpeners,
});
},
asExternalUri(uri: URI) {
if (uri.scheme === initData.environment.appUriScheme) {
@@ -357,6 +341,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostTesting.runTests(provider);
},
get onDidChangeTestResults() {
checkProposedApiEnabled(extension);
return extHostTesting.onLastResultsChanged;
},
get testResults() {
checkProposedApiEnabled(extension);
return extHostTesting.lastResults;
},
};
// namespace: extensions
@@ -483,6 +475,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
getTokenInformationAtPosition(doc: vscode.TextDocument, pos: vscode.Position) {
checkProposedApiEnabled(extension);
return extHostLanguages.tokenAtPosition(doc, pos);
},
registerInlineHintsProvider(selector: vscode.DocumentSelector, provider: vscode.InlineHintsProvider): vscode.Disposable {
checkProposedApiEnabled(extension);
return extHostLanguageFeatures.registerInlineHintsProvider(extension, selector, provider);
}
};
@@ -594,7 +590,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
priority = priority;
}
return extHostStatusBar.createStatusBarEntry(id, name, alignment, priority, accessibilityInformation, extension);
return extHostStatusBar.createStatusBarEntry(id, name, alignment, priority, accessibilityInformation);
},
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): vscode.Disposable {
return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable);
@@ -694,6 +690,18 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
showNotebookDocument(document, options?) {
checkProposedApiEnabled(extension);
return extHostNotebook.showNotebookDocument(document, options);
},
registerExternalUriOpener(id: string, opener: vscode.ExternalUriOpener, metadata: vscode.ExternalUriOpenerMetadata) {
checkProposedApiEnabled(extension);
return extHostUriOpeners.registerExternalUriOpener(extension.identifier, id, opener, metadata);
},
get openEditors() {
checkProposedApiEnabled(extension);
return extHostEditorTabs.tabs;
},
get onDidChangeOpenEditors() {
checkProposedApiEnabled(extension);
return extHostEditorTabs.onDidChangeTabs;
}
};
@@ -825,7 +833,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostFileSystem.registerFileSystemProvider(extension.identifier, scheme, provider, options);
},
get fs() {
return extHostConsumerFileSystem;
return extHostConsumerFileSystem.value;
},
registerFileSearchProvider: (scheme: string, provider: vscode.FileSearchProvider) => {
checkProposedApiEnabled(extension);
@@ -1125,6 +1133,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall,
CallHierarchyItem: extHostTypes.CallHierarchyItem,
CallHierarchyOutgoingCall: extHostTypes.CallHierarchyOutgoingCall,
CancellationError: errors.CancellationError,
CancellationTokenSource: CancellationTokenSource,
CodeAction: extHostTypes.CodeAction,
CodeActionKind: extHostTypes.CodeActionKind,
@@ -1165,6 +1174,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
EventEmitter: Emitter,
ExtensionKind: extHostTypes.ExtensionKind,
ExtensionMode: extHostTypes.ExtensionMode,
ExternalUriOpenerPriority: extHostTypes.ExternalUriOpenerPriority,
FileChangeType: extHostTypes.FileChangeType,
FileDecoration: extHostTypes.FileDecoration,
FileSystemError: extHostTypes.FileSystemError,
@@ -1174,6 +1184,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
FunctionBreakpoint: extHostTypes.FunctionBreakpoint,
Hover: extHostTypes.Hover,
IndentAction: languageConfiguration.IndentAction,
InlineHint: extHostTypes.InlineHint,
Location: extHostTypes.Location,
MarkdownString: extHostTypes.MarkdownString,
OverviewRulerLane: OverviewRulerLane,
@@ -1224,71 +1235,71 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
WorkspaceEdit: extHostTypes.WorkspaceEdit,
// proposed api types
get RemoteAuthorityResolverError() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.RemoteAuthorityResolverError;
},
get ResolvedAuthority() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.ResolvedAuthority;
},
get SourceControlInputBoxValidationType() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.SourceControlInputBoxValidationType;
},
get ExtensionRuntime() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.ExtensionRuntime;
},
get TimelineItem() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.TimelineItem;
},
get CellKind() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.CellKind;
},
get CellOutputKind() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.CellOutputKind;
},
get NotebookCellRunState() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.NotebookCellRunState;
},
get NotebookRunState() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.NotebookRunState;
},
get NotebookCellStatusBarAlignment() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.NotebookCellStatusBarAlignment;
},
get NotebookEditorRevealType() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.NotebookEditorRevealType;
},
get NotebookCellOutput() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.NotebookCellOutput;
},
get NotebookCellOutputItem() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.NotebookCellOutputItem;
},
get LinkedEditingRanges() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.LinkedEditingRanges;
},
get TestRunState() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.TestRunState;
},
get TestMessageSeverity() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.TestMessageSeverity;
},
get TestState() {
checkProposedApiEnabled(extension);
// checkProposedApiEnabled(extension);
return extHostTypes.TestState;
},
};
@@ -1297,9 +1308,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
class Extension<T> implements vscode.Extension<T> {
private _extensionService: IExtHostExtensionService;
private _originExtensionId: ExtensionIdentifier;
private _identifier: ExtensionIdentifier;
#extensionService: IExtHostExtensionService;
#originExtensionId: ExtensionIdentifier;
#identifier: ExtensionIdentifier;
readonly id: string;
readonly extensionUri: URI;
@@ -1308,9 +1319,9 @@ class Extension<T> implements vscode.Extension<T> {
readonly extensionKind: vscode.ExtensionKind;
constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: extHostTypes.ExtensionKind) {
this._extensionService = extensionService;
this._originExtensionId = originExtensionId;
this._identifier = description.identifier;
this.#extensionService = extensionService;
this.#originExtensionId = originExtensionId;
this.#identifier = description.identifier;
this.id = description.identifier.value;
this.extensionUri = description.extensionLocation;
this.extensionPath = path.normalize(originalFSPath(description.extensionLocation));
@@ -1319,17 +1330,17 @@ class Extension<T> implements vscode.Extension<T> {
}
get isActive(): boolean {
return this._extensionService.isActivated(this._identifier);
return this.#extensionService.isActivated(this.#identifier);
}
get exports(): T {
if (this.packageJSON.api === 'none') {
return undefined!; // Strict nulloverride - Public api
}
return <T>this._extensionService.getExtensionExports(this._identifier);
return <T>this.#extensionService.getExtensionExports(this.#identifier);
}
activate(): Thenable<T> {
return this._extensionService.activateByIdWithErrors(this._identifier, { startup: false, extensionId: this._originExtensionId, activationEvent: 'api' }).then(() => this.exports);
return this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' }).then(() => this.exports);
}
}

View File

@@ -21,6 +21,7 @@ import { IExtHostApiDeprecationService, ExtHostApiDeprecationService, } from 'vs
import { IExtHostWindow, ExtHostWindow } from 'vs/workbench/api/common/extHostWindow';
import { IExtHostConsumerFileSystem, ExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
import { IExtHostFileSystemInfo, ExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
import { IExtHostSecretState, ExtHostSecretState } from 'vs/workbench/api/common/exHostSecretState';
registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths);
registerSingleton(IExtHostApiDeprecationService, ExtHostApiDeprecationService);
@@ -39,3 +40,4 @@ registerSingleton(IExtHostTerminalService, WorkerExtHostTerminalService);
registerSingleton(IExtHostTunnelService, ExtHostTunnelService);
registerSingleton(IExtHostWindow, ExtHostWindow);
registerSingleton(IExtHostWorkspace, ExtHostWorkspace);
registerSingleton(IExtHostSecretState, ExtHostSecretState);

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as performance from 'vs/base/common/performance';
import { VSBuffer } from 'vs/base/common/buffer';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IRemoteConsoleLog } from 'vs/base/common/console';
@@ -41,13 +42,13 @@ import { IRevealOptions, ITreeItem } from 'vs/workbench/common/views';
import { IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug';
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
import { ITerminalDimensions, IShellLaunchConfig, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal';
import { ActivationKind, ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
import { ActivationKind, ExtensionActivationError, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import * as search from 'vs/workbench/services/search/common/search';
import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor';
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
import { TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel';
import { TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions } from 'vs/platform/remote/common/tunnel';
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline';
import { revive } from 'vs/base/common/marshalling';
import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter } from 'vs/workbench/contrib/notebook/common/notebookCommon';
@@ -57,7 +58,8 @@ import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib
import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes';
import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility';
import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync';
import { RunTestForProviderRequest, RunTestsRequest, RunTestsResult, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { InternalTestItem, InternalTestResults, RunTestForProviderRequest, RunTestsRequest, RunTestsResult, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
// {{SQL CARBON EDIT}}
import { ITreeItem as sqlITreeItem } from 'sql/workbench/common/views';
@@ -112,7 +114,8 @@ export interface IConfigurationInitData extends IConfigurationData {
}
export interface IExtHostContext extends IRPCProtocol {
remoteAuthority: string | null;
readonly remoteAuthority: string | null;
readonly extensionHostKind: ExtensionHostKind;
}
export interface IMainContext extends IRPCProtocol {
@@ -166,19 +169,12 @@ export interface MainThreadAuthenticationShape extends IDisposable {
$registerAuthenticationProvider(id: string, label: string, supportsMultipleAccounts: boolean): void;
$unregisterAuthenticationProvider(id: string): void;
$ensureProvider(id: string): Promise<void>;
$getProviderIds(): Promise<string[]>;
$sendDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void;
$getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: { createIfNone?: boolean, clearSessionPreference?: boolean }): Promise<modes.AuthenticationSession | undefined>;
$selectSession(providerId: string, providerName: string, extensionId: string, extensionName: string, potentialSessions: modes.AuthenticationSession[], scopes: string[], clearSessionPreference: boolean): Promise<modes.AuthenticationSession>;
$getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
$loginPrompt(providerName: string, extensionName: string): Promise<boolean>;
$setTrustedExtensionAndAccountPreference(providerId: string, accountName: string, extensionId: string, extensionName: string, sessionId: string): Promise<void>;
$requestNewSession(providerId: string, scopes: string[], extensionId: string, extensionName: string): Promise<void>;
$getSessions(providerId: string): Promise<ReadonlyArray<modes.AuthenticationSession>>;
$login(providerId: string, scopes: string[]): Promise<modes.AuthenticationSession>;
$logout(providerId: string, sessionId: string): Promise<void>;
}
export interface MainThreadSecretStateShape extends IDisposable {
$getPassword(extensionId: string, key: string): Promise<string | undefined>;
$setPassword(extensionId: string, key: string, value: string): Promise<void>;
$deletePassword(extensionId: string, key: string): Promise<void>;
@@ -236,7 +232,6 @@ export interface MainThreadDocumentsShape extends IDisposable {
export interface ITextEditorConfigurationUpdate {
tabSize?: number | 'auto';
indentSize?: number | 'tabSize';
insertSpaces?: boolean | 'auto';
cursorStyle?: TextEditorCursorStyle;
lineNumbers?: RenderLineNumbersType;
@@ -244,7 +239,6 @@ export interface ITextEditorConfigurationUpdate {
export interface IResolvedTextEditorConfiguration {
tabSize: number;
indentSize: number;
insertSpaces: boolean;
cursorStyle: TextEditorCursorStyle;
lineNumbers: RenderLineNumbersType;
@@ -334,7 +328,7 @@ export interface IIndentationRuleDto {
export interface IOnEnterRuleDto {
beforeText: IRegExpDto;
afterText?: IRegExpDto;
oneLineAboveText?: IRegExpDto;
previousLineText?: IRegExpDto;
action: EnterAction;
}
export interface ILanguageConfigurationDto {
@@ -401,6 +395,8 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerDocumentRangeSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend): void;
$registerSuggestSupport(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, displayName: string): void;
$registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void;
$registerInlineHintsProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void;
$emitInlineHintsEvent(eventHandle: number, event?: any): void;
$registerDocumentLinkProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean): void;
$registerDocumentColorProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerFoldingRangeProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void;
@@ -419,6 +415,7 @@ export interface MainThreadLanguagesShape extends IDisposable {
export interface MainThreadMessageOptions {
extension?: IExtensionDescription;
modal?: boolean;
useCustom?: boolean;
}
export interface MainThreadMessageServiceShape extends IDisposable {
@@ -442,6 +439,16 @@ export interface MainThreadProgressShape extends IDisposable {
$progressEnd(handle: number): void;
}
/**
* A terminal that is created on the extension host side is temporarily assigned
* a UUID by the extension host that created it. Once the renderer side has assigned
* a real numeric id, the numeric id will be used.
*
* All other terminals (that are not created on the extension host side) always
* use the numeric id.
*/
export type TerminalIdentifier = number | string;
export interface TerminalLaunchConfig {
name?: string;
shellPath?: string;
@@ -456,11 +463,11 @@ export interface TerminalLaunchConfig {
}
export interface MainThreadTerminalServiceShape extends IDisposable {
$createTerminal(config: TerminalLaunchConfig): Promise<{ id: number, name: string; }>;
$dispose(terminalId: number): void;
$hide(terminalId: number): void;
$sendText(terminalId: number, text: string, addNewLine: boolean): void;
$show(terminalId: number, preserveFocus: boolean): void;
$createTerminal(extHostTerminalId: string, config: TerminalLaunchConfig): Promise<void>;
$dispose(id: TerminalIdentifier): void;
$hide(id: TerminalIdentifier): void;
$sendText(id: TerminalIdentifier, text: string, addNewLine: boolean): void;
$show(id: TerminalIdentifier, preserveFocus: boolean): void;
$startSendingDataEvents(): void;
$stopSendingDataEvents(): void;
$startLinkProvider(): void;
@@ -598,6 +605,24 @@ export interface ExtHostEditorInsetsShape {
$onDidReceiveMessage(handle: number, message: any): void;
}
//#region --- open editors model
export interface MainThreadEditorTabsShape extends IDisposable {
// manage tabs: move, close, rearrange etc
}
export interface IEditorTabDto {
group: number;
name: string;
resource: UriComponents
}
export interface IExtHostEditorTabsShape {
$acceptEditorTabs(tabs: IEditorTabDto[]): void;
}
//#endregion
export type WebviewHandle = string;
export interface WebviewPanelShowOptions {
@@ -743,6 +768,7 @@ export enum NotebookEditorRevealType {
Default = 0,
InCenter = 1,
InCenterIfOutsideViewport = 2,
AtTop = 3
}
export interface INotebookDocumentShowOptions {
@@ -776,8 +802,6 @@ export interface MainThreadNotebookShape extends IDisposable {
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void;
$removeNotebookEditorDecorationType(key: string): void;
$trySetDecorations(id: string, range: ICellRange, decorationKey: string): void;
$onUndoableContentChange(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void;
$onContentChange(resource: UriComponents, viewType: string): void;
}
export interface MainThreadUrlsShape extends IDisposable {
@@ -790,6 +814,16 @@ export interface ExtHostUrlsShape {
$handleExternalUri(handle: number, uri: UriComponents): Promise<void>;
}
export interface MainThreadUriOpenersShape extends IDisposable {
$registerUriOpener(id: string, schemes: readonly string[], extensionId: ExtensionIdentifier, label: string): Promise<void>;
$unregisterUriOpener(id: string): Promise<void>;
}
export interface ExtHostUriOpenersShape {
$canOpenUri(id: string, uri: UriComponents, token: CancellationToken): Promise<modes.ExternalUriOpenerPriority>;
$openUri(id: string, context: { resolvedUri: UriComponents, sourceUri: UriComponents }, token: CancellationToken): Promise<void>;
}
export interface ITextSearchComplete {
limitHit?: boolean;
}
@@ -857,6 +891,7 @@ export interface MainThreadExtensionServiceShape extends IDisposable {
$onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise<void>;
$onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void;
$onExtensionHostExit(code: number): Promise<void>;
$setPerformanceMarks(marks: performance.PerformanceMark[]): Promise<void>;
}
export interface SCMProviderFeatures {
@@ -950,6 +985,7 @@ export interface MainThreadDebugServiceShape extends IDisposable {
export interface IOpenUriOptions {
readonly allowTunneling?: boolean;
readonly allowContributedOpeners?: boolean | string;
}
export interface MainThreadWindowShape extends IDisposable {
@@ -962,8 +998,9 @@ export interface MainThreadTunnelServiceShape extends IDisposable {
$openTunnel(tunnelOptions: TunnelOptions, source: string | undefined): Promise<TunnelDto | undefined>;
$closeTunnel(remote: { host: string, port: number }): Promise<void>;
$getTunnels(): Promise<TunnelDescription[]>;
$setTunnelProvider(): Promise<void>;
$tunnelServiceReady(): Promise<void>;
$setTunnelProvider(features: TunnelProviderFeatures): Promise<void>;
$setCandidateFinder(): Promise<void>;
$setCandidateFilter(): Promise<void>;
$onFoundNewCandidates(candidates: { host: string, port: number, detail: string }[]): Promise<void>;
}
@@ -1057,7 +1094,7 @@ export interface ExtHostTreeViewsShape {
$setSelection(treeViewId: string, treeItemHandles: string[]): void;
$setVisible(treeViewId: string, visible: boolean): void;
$hasResolve(treeViewId: string): Promise<boolean>;
$resolve(treeViewId: string, treeItemHandle: string): Promise<ITreeItem | undefined>;
$resolve(treeViewId: string, treeItemHandle: string, token: CancellationToken): Promise<ITreeItem | undefined>;
}
export interface ExtHostWorkspaceShape {
@@ -1099,7 +1136,10 @@ export interface ExtHostAuthenticationShape {
$onDidChangeAuthenticationSessions(id: string, label: string, event: modes.AuthenticationSessionsChangeEvent): Promise<void>;
$onDidChangeAuthenticationProviders(added: modes.AuthenticationProviderInformation[], removed: modes.AuthenticationProviderInformation[]): Promise<void>;
$setProviders(providers: modes.AuthenticationProviderInformation[]): Promise<void>;
$onDidChangePassword(): Promise<void>;
}
export interface ExtHostSecretStateShape {
$onDidChangePassword(e: { extensionId: string, key: string }): Promise<void>;
}
export interface ExtHostSearchShape {
@@ -1150,9 +1190,14 @@ export interface SourceTargetPair {
target: UriComponents;
}
export interface IWillRunFileOperationParticipation {
edit: IWorkspaceEditDto;
extensionNames: string[]
}
export interface ExtHostFileSystemEventServiceShape {
$onFileEvent(events: FileSystemEvents): void;
$onWillRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[], undoRedoGroupId: number | undefined, timeout: number, token: CancellationToken): Promise<any>;
$onWillRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise<IWillRunFileOperationParticipation | undefined>;
$onDidRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[]): void;
}
@@ -1257,6 +1302,18 @@ export interface ISignatureHelpContextDto {
readonly activeSignatureHelp?: ISignatureHelpDto;
}
export interface IInlineHintDto {
text: string;
range: IRange;
hoverMessage?: string;
whitespaceBefore?: boolean;
whitespaceAfter?: boolean;
}
export interface IInlineHintsDto {
hints: IInlineHintDto[]
}
export interface ILocationDto {
uri: UriComponents;
range: IRange;
@@ -1446,6 +1503,7 @@ export interface ExtHostLanguageFeaturesShape {
$releaseCompletionItems(handle: number, id: number): void;
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise<ISignatureHelpDto | undefined>;
$releaseSignatureHelp(handle: number, id: number): void;
$provideInlineHints(handle: number, resource: UriComponents, range: IRange, token: CancellationToken): Promise<IInlineHintsDto | undefined>
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<ILinksListDto | undefined>;
$resolveDocumentLink(handle: number, id: ChainedCacheId, token: CancellationToken): Promise<ILinkDto | undefined>;
$releaseDocumentLinks(handle: number, id: number): void;
@@ -1478,6 +1536,7 @@ export interface IShellLaunchConfigDto {
cwd?: string | UriComponents;
env?: { [key: string]: string | null; };
hideFromUser?: boolean;
flowControl?: boolean;
}
export interface IShellDefinitionDto {
@@ -1508,7 +1567,7 @@ export interface ITerminalDimensionsDto {
export interface ExtHostTerminalServiceShape {
$acceptTerminalClosed(id: number, exitCode: number | undefined): void;
$acceptTerminalOpened(id: number, name: string, shellLaunchConfig: IShellLaunchConfigDto): void;
$acceptTerminalOpened(id: number, extHostTerminalId: string | undefined, name: string, shellLaunchConfig: IShellLaunchConfigDto): void;
$acceptActiveTerminalChanged(id: number | null): void;
$acceptTerminalProcessId(id: number, processId: number): void;
$acceptTerminalProcessData(id: number, data: string): void;
@@ -1517,6 +1576,7 @@ export interface ExtHostTerminalServiceShape {
$acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): void;
$spawnExtHostProcess(id: number, shellLaunchConfig: IShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined>;
$startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise<ITerminalLaunchError | undefined>;
$acceptProcessAckDataEvent(id: number, charCount: number): void;
$acceptProcessInput(id: number, data: string): void;
$acceptProcessResize(id: number, cols: number, rows: number): void;
$acceptProcessShutdown(id: number, immediate: boolean): void;
@@ -1613,7 +1673,7 @@ export type IDebugSessionDto = IDebugSessionFullDto | DebugSessionUUID;
export interface ExtHostDebugServiceShape {
$substituteVariables(folder: UriComponents | undefined, config: IConfig): Promise<IConfig>;
$runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise<number | undefined>;
$runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined>;
$startDASession(handle: number, session: IDebugSessionDto): Promise<void>;
$stopDASession(handle: number): Promise<void>;
$sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void;
@@ -1730,16 +1790,13 @@ export interface ExtHostNotebookShape {
$saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise<boolean>;
$backup(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string | undefined>;
$acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void;
$acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelId: string | undefined }): void;
$acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelFriendlyId: string | undefined }): void;
$onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void;
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void;
$acceptModelSaved(uriComponents: UriComponents): void;
$acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void;
$acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void;
$acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): void;
$undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void>;
$redoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void>;
}
export interface ExtHostStorageShape {
@@ -1754,9 +1811,11 @@ export interface MainThreadThemingShape extends IDisposable {
}
export interface ExtHostTunnelServiceShape {
$forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<TunnelDto> | undefined;
$forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<TunnelDto | undefined>;
$closeTunnel(remote: { host: string, port: number }, silent?: boolean): Promise<void>;
$onDidTunnelsChange(): Promise<void>;
$registerCandidateFinder(enable: boolean): Promise<void>;
$applyCandidateFilter(candidates: CandidatePort[]): Promise<CandidatePort[]>;
}
export interface ExtHostTimelineShape {
@@ -1769,11 +1828,12 @@ export const enum ExtHostTestingResource {
}
export interface ExtHostTestingShape {
$runTestsForProvider(req: RunTestForProviderRequest): Promise<RunTestsResult>;
$runTestsForProvider(req: RunTestForProviderRequest, token: CancellationToken): Promise<RunTestsResult>;
$subscribeToTests(resource: ExtHostTestingResource, uri: UriComponents): void;
$unsubscribeFromTests(resource: ExtHostTestingResource, uri: UriComponents): void;
$lookupTest(test: TestIdWithProvider): Promise<InternalTestItem | undefined>;
$acceptDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
$publishTestResults(results: InternalTestResults): void;
}
export interface MainThreadTestingShape {
@@ -1782,7 +1842,7 @@ export interface MainThreadTestingShape {
$subscribeToDiffs(resource: ExtHostTestingResource, uri: UriComponents): void;
$unsubscribeFromDiffs(resource: ExtHostTestingResource, uri: UriComponents): void;
$publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
$runTests(req: RunTestsRequest): Promise<RunTestsResult>;
$runTests(req: RunTestsRequest, token: CancellationToken): Promise<RunTestsResult>;
}
// --- proxy identifiers
@@ -1803,6 +1863,7 @@ export const MainContext = {
MainThreadDocumentContentProviders: createMainId<MainThreadDocumentContentProvidersShape>('MainThreadDocumentContentProviders'),
MainThreadTextEditors: createMainId<MainThreadTextEditorsShape>('MainThreadTextEditors'),
MainThreadEditorInsets: createMainId<MainThreadEditorInsetsShape>('MainThreadEditorInsets'),
MainThreadEditorTabs: createMainId<MainThreadEditorTabsShape>('MainThreadEditorTabs'),
MainThreadErrors: createMainId<MainThreadErrorsShape>('MainThreadErrors'),
MainThreadTreeViews: createMainId<MainThreadTreeViewsShape>('MainThreadTreeViews'),
MainThreadDownloadService: createMainId<MainThreadDownloadServiceShape>('MainThreadDownloadService'),
@@ -1815,6 +1876,7 @@ export const MainContext = {
MainThreadProgress: createMainId<MainThreadProgressShape>('MainThreadProgress'),
MainThreadQuickOpen: createMainId<MainThreadQuickOpenShape>('MainThreadQuickOpen'),
MainThreadStatusBar: createMainId<MainThreadStatusBarShape>('MainThreadStatusBar'),
MainThreadSecretState: createMainId<MainThreadSecretStateShape>('MainThreadSecretState'),
MainThreadStorage: createMainId<MainThreadStorageShape>('MainThreadStorage'),
MainThreadTelemetry: createMainId<MainThreadTelemetryShape>('MainThreadTelemetry'),
MainThreadTerminalService: createMainId<MainThreadTerminalServiceShape>('MainThreadTerminalService'),
@@ -1823,6 +1885,7 @@ export const MainContext = {
MainThreadWebviewViews: createMainId<MainThreadWebviewViewsShape>('MainThreadWebviewViews'),
MainThreadCustomEditors: createMainId<MainThreadCustomEditorsShape>('MainThreadCustomEditors'),
MainThreadUrls: createMainId<MainThreadUrlsShape>('MainThreadUrls'),
MainThreadUriOpeners: createMainId<MainThreadUriOpenersShape>('MainThreadUriOpeners'),
MainThreadWorkspace: createMainId<MainThreadWorkspaceShape>('MainThreadWorkspace'),
MainThreadFileSystem: createMainId<MainThreadFileSystemShape>('MainThreadFileSystem'),
MainThreadExtensionService: createMainId<MainThreadExtensionServiceShape>('MainThreadExtensionService'),
@@ -1868,10 +1931,13 @@ export const ExtHostContext = {
ExtHostCustomEditors: createExtId<ExtHostCustomEditorsShape>('ExtHostCustomEditors'),
ExtHostWebviewViews: createExtId<ExtHostWebviewViewsShape>('ExtHostWebviewViews'),
ExtHostEditorInsets: createExtId<ExtHostEditorInsetsShape>('ExtHostEditorInsets'),
ExtHostEditorTabs: createExtId<IExtHostEditorTabsShape>('ExtHostEditorTabs'),
ExtHostProgress: createMainId<ExtHostProgressShape>('ExtHostProgress'),
ExtHostComments: createMainId<ExtHostCommentsShape>('ExtHostComments'),
ExtHostSecretState: createMainId<ExtHostSecretStateShape>('ExtHostSecretState'),
ExtHostStorage: createMainId<ExtHostStorageShape>('ExtHostStorage'),
ExtHostUrls: createExtId<ExtHostUrlsShape>('ExtHostUrls'),
ExtHostUriOpeners: createExtId<ExtHostUriOpenersShape>('ExtHostUriOpeners'),
ExtHostOutputService: createMainId<ExtHostOutputServiceShape>('ExtHostOutputService'),
ExtHosLabelService: createMainId<ExtHostLabelServiceShape>('ExtHostLabelService'),
ExtHostNotebook: createMainId<ExtHostNotebookShape>('ExtHostNotebook'),

View File

@@ -20,6 +20,8 @@ import { IRange } from 'vs/editor/common/core/range';
import { IPosition } from 'vs/editor/common/core/position';
import { TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { VSBuffer } from 'vs/base/common/buffer';
import { decodeSemanticTokensDto } from 'vs/editor/common/services/semanticTokensDto';
//#region --- NEW world
@@ -176,6 +178,57 @@ const newCommands: ApiCommand[] = [
[ApiCommandArgument.Uri, ApiCommandArgument.Number.with('linkResolveCount', 'Number of links that should be resolved, only when links are unresolved.').optional()],
new ApiCommandResult<modes.ILink[], vscode.DocumentLink[]>('A promise that resolves to an array of DocumentLink-instances.', value => value.map(typeConverters.DocumentLink.to))
),
// --- semantic tokens
new ApiCommand(
'vscode.provideDocumentSemanticTokensLegend', '_provideDocumentSemanticTokensLegend', 'Provide semantic tokens legend for a document',
[ApiCommandArgument.Uri],
new ApiCommandResult<modes.SemanticTokensLegend, types.SemanticTokensLegend | undefined>('A promise that resolves to SemanticTokensLegend.', value => {
if (!value) {
return undefined;
}
return new types.SemanticTokensLegend(value.tokenTypes, value.tokenModifiers);
})
),
new ApiCommand(
'vscode.provideDocumentSemanticTokens', '_provideDocumentSemanticTokens', 'Provide semantic tokens for a document',
[ApiCommandArgument.Uri],
new ApiCommandResult<VSBuffer, types.SemanticTokens | undefined>('A promise that resolves to SemanticTokens.', value => {
if (!value) {
return undefined;
}
const semanticTokensDto = decodeSemanticTokensDto(value);
if (semanticTokensDto.type !== 'full') {
// only accepting full semantic tokens from provideDocumentSemanticTokens
return undefined;
}
return new types.SemanticTokens(semanticTokensDto.data, undefined);
})
),
new ApiCommand(
'vscode.provideDocumentRangeSemanticTokensLegend', '_provideDocumentRangeSemanticTokensLegend', 'Provide semantic tokens legend for a document range',
[ApiCommandArgument.Uri],
new ApiCommandResult<modes.SemanticTokensLegend, types.SemanticTokensLegend | undefined>('A promise that resolves to SemanticTokensLegend.', value => {
if (!value) {
return undefined;
}
return new types.SemanticTokensLegend(value.tokenTypes, value.tokenModifiers);
})
),
new ApiCommand(
'vscode.provideDocumentRangeSemanticTokens', '_provideDocumentRangeSemanticTokens', 'Provide semantic tokens for a document range',
[ApiCommandArgument.Uri, ApiCommandArgument.Range],
new ApiCommandResult<VSBuffer, types.SemanticTokens | undefined>('A promise that resolves to SemanticTokens.', value => {
if (!value) {
return undefined;
}
const semanticTokensDto = decodeSemanticTokensDto(value);
if (semanticTokensDto.type !== 'full') {
// only accepting full semantic tokens from provideDocumentRangeSemanticTokens
return undefined;
}
return new types.SemanticTokens(semanticTokensDto.data, undefined);
})
),
// --- completions
new ApiCommand(
'vscode.executeCompletionItemProvider', '_executeCompletionItemProvider', 'Execute completion item provider.',
@@ -271,13 +324,21 @@ const newCommands: ApiCommand[] = [
return [];
})
),
// --- inline hints
new ApiCommand(
'vscode.executeInlineHintProvider', '_executeInlineHintProvider', 'Execute inline hints provider',
[ApiCommandArgument.Uri, ApiCommandArgument.Range],
new ApiCommandResult<modes.InlineHint[], vscode.InlineHint[]>('A promise that resolves to an array of InlineHint objects', result => {
return result.map(typeConverters.InlineHint.to);
})
),
// --- notebooks
new ApiCommand(
'vscode.resolveNotebookContentProviders', '_resolveNotebookContentProvider', 'Resolve Notebook Content Providers',
[
new ApiCommandArgument<string, string>('viewType', '', v => typeof v === 'string', v => v),
new ApiCommandArgument<string, string>('displayName', '', v => typeof v === 'string', v => v),
new ApiCommandArgument<object, object>('options', '', v => typeof v === 'object', v => v),
// new ApiCommandArgument<string, string>('viewType', '', v => typeof v === 'string', v => v),
// new ApiCommandArgument<string, string>('displayName', '', v => typeof v === 'string', v => v),
// new ApiCommandArgument<object, object>('options', '', v => typeof v === 'object', v => v),
],
new ApiCommandResult<{
viewType: string;

View File

@@ -15,11 +15,15 @@ interface GetSessionsRequest {
result: Promise<vscode.AuthenticationSession | undefined>;
}
interface ProviderWithMetadata {
label: string;
provider: vscode.AuthenticationProvider;
options: vscode.AuthenticationProviderOptions;
}
export class ExtHostAuthentication implements ExtHostAuthenticationShape {
private _proxy: MainThreadAuthenticationShape;
private _authenticationProviders: Map<string, vscode.AuthenticationProvider> = new Map<string, vscode.AuthenticationProvider>();
private _providerIds: string[] = [];
private _authenticationProviders: Map<string, ProviderWithMetadata> = new Map<string, ProviderWithMetadata>();
private _providers: vscode.AuthenticationProviderInformation[] = [];
@@ -29,9 +33,6 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
private _onDidChangeSessions = new Emitter<vscode.AuthenticationSessionsChangeEvent>();
readonly onDidChangeSessions: Event<vscode.AuthenticationSessionsChangeEvent> = this._onDidChangeSessions.event;
private _onDidChangePassword = new Emitter<void>();
readonly onDidChangePassword: Event<void> = this._onDidChangePassword.event;
private _inFlightRequests = new Map<string, GetSessionsRequest[]>();
constructor(mainContext: IMainContext) {
@@ -43,14 +44,6 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
return Promise.resolve();
}
getProviderIds(): Promise<ReadonlyArray<string>> {
return this._proxy.$getProviderIds();
}
get providerIds(): string[] {
return this._providerIds;
}
get providers(): ReadonlyArray<vscode.AuthenticationProviderInformation> {
return Object.freeze(this._providers.slice());
}
@@ -90,128 +83,83 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
private async _getSession(requestingExtension: IExtensionDescription, extensionId: string, providerId: string, scopes: string[], options: vscode.AuthenticationGetSessionOptions = {}): Promise<vscode.AuthenticationSession | undefined> {
await this._proxy.$ensureProvider(providerId);
const provider = this._authenticationProviders.get(providerId);
const extensionName = requestingExtension.displayName || requestingExtension.name;
if (!provider) {
return this._proxy.$getSession(providerId, scopes, extensionId, extensionName, options);
}
const orderedScopes = scopes.sort().join(' ');
const sessions = (await provider.getSessions()).filter(session => session.scopes.slice().sort().join(' ') === orderedScopes);
let session: vscode.AuthenticationSession | undefined = undefined;
if (sessions.length) {
if (!provider.supportsMultipleAccounts) {
session = sessions[0];
const allowed = await this._proxy.$getSessionsPrompt(providerId, session.account.label, provider.label, extensionId, extensionName);
if (!allowed) {
throw new Error('User did not consent to login.');
}
} else {
// On renderer side, confirm consent, ask user to choose between accounts if multiple sessions are valid
const selected = await this._proxy.$selectSession(providerId, provider.label, extensionId, extensionName, sessions, scopes, !!options.clearSessionPreference);
session = sessions.find(session => session.id === selected.id);
}
} else {
if (options.createIfNone) {
const isAllowed = await this._proxy.$loginPrompt(provider.label, extensionName);
if (!isAllowed) {
throw new Error('User did not consent to login.');
}
session = await provider.login(scopes);
await this._proxy.$setTrustedExtensionAndAccountPreference(providerId, session.account.label, extensionId, extensionName, session.id);
} else {
await this._proxy.$requestNewSession(providerId, scopes, extensionId, extensionName);
}
}
return session;
return this._proxy.$getSession(providerId, scopes, extensionId, extensionName, options);
}
async logout(providerId: string, sessionId: string): Promise<void> {
const provider = this._authenticationProviders.get(providerId);
if (!provider) {
const providerData = this._authenticationProviders.get(providerId);
if (!providerData) {
return this._proxy.$logout(providerId, sessionId);
}
return provider.logout(sessionId);
return providerData.provider.logout(sessionId);
}
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.`);
registerAuthenticationProvider(id: string, label: string, provider: vscode.AuthenticationProvider, options?: vscode.AuthenticationProviderOptions): vscode.Disposable {
if (this._authenticationProviders.get(id)) {
throw new Error(`An authentication provider with id '${id}' is already registered.`);
}
this._authenticationProviders.set(provider.id, provider);
if (!this._providerIds.includes(provider.id)) {
this._providerIds.push(provider.id);
}
this._authenticationProviders.set(id, { label, provider, options: options ?? { supportsMultipleAccounts: false } });
if (!this._providers.find(p => p.id === provider.id)) {
if (!this._providers.find(p => p.id === id)) {
this._providers.push({
id: provider.id,
label: provider.label
id: id,
label: label
});
}
const listener = provider.onDidChangeSessions(e => {
this._proxy.$sendDidChangeSessions(provider.id, e);
this._proxy.$sendDidChangeSessions(id, e);
});
this._proxy.$registerAuthenticationProvider(provider.id, provider.label, provider.supportsMultipleAccounts);
this._proxy.$registerAuthenticationProvider(id, label, options?.supportsMultipleAccounts ?? false);
return new Disposable(() => {
listener.dispose();
this._authenticationProviders.delete(provider.id);
const index = this._providerIds.findIndex(id => id === provider.id);
if (index > -1) {
this._providerIds.splice(index);
}
this._authenticationProviders.delete(id);
const i = this._providers.findIndex(p => p.id === provider.id);
const i = this._providers.findIndex(p => p.id === id);
if (i > -1) {
this._providers.splice(i);
}
this._proxy.$unregisterAuthenticationProvider(provider.id);
this._proxy.$unregisterAuthenticationProvider(id);
});
}
$login(providerId: string, scopes: string[]): Promise<modes.AuthenticationSession> {
const authProvider = this._authenticationProviders.get(providerId);
if (authProvider) {
return Promise.resolve(authProvider.login(scopes));
const providerData = this._authenticationProviders.get(providerId);
if (providerData) {
return Promise.resolve(providerData.provider.login(scopes));
}
throw new Error(`Unable to find authentication provider with handle: ${providerId}`);
}
$logout(providerId: string, sessionId: string): Promise<void> {
const authProvider = this._authenticationProviders.get(providerId);
if (authProvider) {
return Promise.resolve(authProvider.logout(sessionId));
const providerData = this._authenticationProviders.get(providerId);
if (providerData) {
return Promise.resolve(providerData.provider.logout(sessionId));
}
throw new Error(`Unable to find authentication provider with handle: ${providerId}`);
}
$getSessions(providerId: string): Promise<ReadonlyArray<modes.AuthenticationSession>> {
const authProvider = this._authenticationProviders.get(providerId);
if (authProvider) {
return Promise.resolve(authProvider.getSessions());
const providerData = this._authenticationProviders.get(providerId);
if (providerData) {
return Promise.resolve(providerData.provider.getSessions());
}
throw new Error(`Unable to find authentication provider with handle: ${providerId}`);
}
async $getSessionAccessToken(providerId: string, sessionId: string): Promise<string> {
const authProvider = this._authenticationProviders.get(providerId);
if (authProvider) {
const sessions = await authProvider.getSessions();
const providerData = this._authenticationProviders.get(providerId);
if (providerData) {
const sessions = await providerData.provider.getSessions();
const session = sessions.find(session => session.id === sessionId);
if (session) {
return session.accessToken;
@@ -245,23 +193,4 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
this._onDidChangeAuthenticationProviders.fire({ added, removed });
return Promise.resolve();
}
async $onDidChangePassword(): Promise<void> {
this._onDidChangePassword.fire();
}
getPassword(requestingExtension: IExtensionDescription, key: string): Promise<string | undefined> {
const extensionId = ExtensionIdentifier.toKey(requestingExtension.identifier);
return this._proxy.$getPassword(extensionId, key);
}
setPassword(requestingExtension: IExtensionDescription, key: string, value: string): Promise<void> {
const extensionId = ExtensionIdentifier.toKey(requestingExtension.identifier);
return this._proxy.$setPassword(extensionId, key, value);
}
deletePassword(requestingExtension: IExtensionDescription, key: string): Promise<void> {
const extensionId = ExtensionIdentifier.toKey(requestingExtension.identifier);
return this._proxy.$deletePassword(extensionId, key);
}
}

View File

@@ -3,22 +3,22 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IMainContext, MainContext, MainThreadClipboardShape } from 'vs/workbench/api/common/extHost.protocol';
import { IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import type * as vscode from 'vscode';
export class ExtHostClipboard implements vscode.Clipboard {
export class ExtHostClipboard {
private readonly _proxy: MainThreadClipboardShape;
readonly value: vscode.Clipboard;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.getProxy(MainContext.MainThreadClipboard);
}
readText(): Promise<string> {
return this._proxy.$readText();
}
writeText(value: string): Promise<void> {
return this._proxy.$writeText(value);
const proxy = mainContext.getProxy(MainContext.MainThreadClipboard);
this.value = Object.freeze({
readText() {
return proxy.$readText();
},
writeText(value: string) {
return proxy.$writeText(value);
}
});
}
}

View File

@@ -44,8 +44,8 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape {
createWebviewEditorInset(editor: vscode.TextEditor, line: number, height: number, options: vscode.WebviewOptions | undefined, extension: IExtensionDescription): vscode.WebviewEditorInset {
let apiEditor: ExtHostTextEditor | undefined;
for (const candidate of this._editors.getVisibleTextEditors()) {
if (candidate === editor) {
for (const candidate of this._editors.getVisibleTextEditors(true)) {
if (candidate.value === editor) {
apiEditor = <ExtHostTextEditor>candidate;
break;
}
@@ -121,7 +121,7 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape {
}
};
this._proxy.$createEditorInset(handle, apiEditor.id, apiEditor.document.uri, line + 1, height, options || {}, extension.identifier, extension.extensionLocation);
this._proxy.$createEditorInset(handle, apiEditor.id, apiEditor.value.document.uri, line + 1, height, options || {}, extension.identifier, extension.extensionLocation);
this._insets.set(handle, { editor, inset, onDidReceiveMessage });
return inset;

View File

@@ -103,7 +103,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
const internalArgs = apiCommand.args.map((arg, i) => {
if (!arg.validate(apiArgs[i])) {
throw new Error(`Invalid argument '${arg.name}' when running '${apiCommand.id}', receieved: ${apiArgs[i]}`);
throw new Error(`Invalid argument '${arg.name}' when running '${apiCommand.id}', received: ${apiArgs[i]}`);
}
return arg.convert(apiArgs[i]);
});
@@ -194,7 +194,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
}
}
private _executeContributedCommand<T>(id: string, args: any[]): Promise<T> {
private async _executeContributedCommand<T>(id: string, args: any[]): Promise<T> {
const command = this._commands.get(id);
if (!command) {
throw new Error('Unknown command');
@@ -205,17 +205,16 @@ export class ExtHostCommands implements ExtHostCommandsShape {
try {
validateConstraint(args[i], description.args[i].constraint);
} catch (err) {
return Promise.reject(new Error(`Running the contributed command: '${id}' failed. Illegal argument '${description.args[i].name}' - ${description.args[i].description}`));
throw new Error(`Running the contributed command: '${id}' failed. Illegal argument '${description.args[i].name}' - ${description.args[i].description}`);
}
}
}
try {
const result = callback.apply(thisArg, args);
return Promise.resolve(result);
return await callback.apply(thisArg, args);
} catch (err) {
this._logService.error(err, id);
return Promise.reject(new Error(`Running the contributed command: '${id}' failed.`));
throw new Error(`Running the contributed command: '${id}' failed.`);
}
}

View File

@@ -13,6 +13,7 @@ import * as modes from 'vs/editor/common/modes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels';
import { EditorGroupColumn } from 'vs/workbench/common/editor';
@@ -178,7 +179,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean },
): vscode.Disposable {
const disposables = new DisposableStore();
if ('resolveCustomTextEditor' in provider) {
if (isCustomTextEditorProvider(provider)) {
disposables.add(this._editorProviders.addTextProvider(viewType, extension, provider));
this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, {
supportsMove: !!provider.moveCustomTextEditor,
@@ -208,7 +209,6 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
}));
}
async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken) {
const entry = this._editorProviders.get(viewType);
if (!entry) {
@@ -261,8 +261,10 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
throw new Error(`No provider found for '${viewType}'`);
}
const viewColumn = typeConverters.ViewColumn.to(position);
const webview = this._extHostWebview.createNewWebview(handle, options, entry.extension);
const panel = this._extHostWebviewPanels.createNewWebviewPanel(handle, viewType, title, position, options, webview);
const panel = this._extHostWebviewPanels.createNewWebviewPanel(handle, viewType, title, viewColumn, options, webview);
const revivedResource = URI.revive(resource);
@@ -350,7 +352,6 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
return backup.id;
}
private getCustomDocumentEntry(viewType: string, resource: UriComponents): CustomDocumentStoreEntry {
const entry = this._documents.get(viewType, URI.revive(resource));
if (!entry) {
@@ -375,6 +376,9 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
}
}
function isCustomTextEditorProvider(provider: vscode.CustomReadonlyEditorProvider<vscode.CustomDocument> | vscode.CustomTextEditorProvider): provider is vscode.CustomTextEditorProvider {
return typeof (provider as vscode.CustomTextEditorProvider).resolveCustomTextEditor === 'function';
}
function isEditEvent(e: vscode.CustomDocumentContentChangeEvent | vscode.CustomDocumentEditEvent): e is vscode.CustomDocumentEditEvent {
return typeof (e as vscode.CustomDocumentEditEvent).undo === 'function'

View File

@@ -89,7 +89,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
get onDidReceiveDebugSessionCustomEvent(): Event<vscode.DebugSessionCustomEvent> { return this._onDidReceiveDebugSessionCustomEvent.event; }
private _activeDebugConsole: ExtHostDebugConsole;
get activeDebugConsole(): ExtHostDebugConsole { return this._activeDebugConsole; }
get activeDebugConsole(): vscode.DebugConsole { return this._activeDebugConsole.value; }
private _breakpoints: Map<string, vscode.Breakpoint>;
private _breakpointEventsActive: boolean;
@@ -152,7 +152,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
const source = <any>src;
if (typeof source.sourceReference === 'number') {
if (typeof source.sourceReference === 'number' && source.sourceReference > 0) {
// src can be retrieved via DAP's "source" request
let debug = `debug:${encodeURIComponent(source.path || '')}`;
@@ -361,7 +361,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
// RPC methods (ExtHostDebugServiceShape)
public async $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise<number | undefined> {
public async $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined> {
return Promise.resolve(undefined);
}
@@ -911,20 +911,20 @@ export class ExtHostDebugSession implements vscode.DebugSession {
}
}
export class ExtHostDebugConsole implements vscode.DebugConsole {
export class ExtHostDebugConsole {
private _debugServiceProxy: MainThreadDebugServiceShape;
readonly value: vscode.DebugConsole;
constructor(proxy: MainThreadDebugServiceShape) {
this._debugServiceProxy = proxy;
}
append(value: string): void {
this._debugServiceProxy.$appendDebugConsole(value);
}
appendLine(value: string): void {
this.append(value + '\n');
this.value = Object.freeze({
append(value: string): void {
proxy.$appendDebugConsole(value);
},
appendLine(value: string): void {
this.append(value + '\n');
}
});
}
}

View File

@@ -18,6 +18,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { ResourceMap } from 'vs/base/common/map';
import { Schemas } from 'vs/base/common/network';
import { Iterable } from 'vs/base/common/iterator';
import { Lazy } from 'vs/base/common/lazy';
class Reference<T> {
private _count = 0;
@@ -49,13 +50,13 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
private readonly _onDidAddDocuments = new Emitter<ExtHostDocumentData[]>();
private readonly _onDidRemoveDocuments = new Emitter<ExtHostDocumentData[]>();
private readonly _onDidChangeVisibleTextEditors = new Emitter<ExtHostTextEditor[]>();
private readonly _onDidChangeActiveTextEditor = new Emitter<ExtHostTextEditor | undefined>();
private readonly _onDidChangeVisibleTextEditors = new Emitter<vscode.TextEditor[]>();
private readonly _onDidChangeActiveTextEditor = new Emitter<vscode.TextEditor | undefined>();
readonly onDidAddDocuments: Event<ExtHostDocumentData[]> = this._onDidAddDocuments.event;
readonly onDidRemoveDocuments: Event<ExtHostDocumentData[]> = this._onDidRemoveDocuments.event;
readonly onDidChangeVisibleTextEditors: Event<ExtHostTextEditor[]> = this._onDidChangeVisibleTextEditors.event;
readonly onDidChangeActiveTextEditor: Event<ExtHostTextEditor | undefined> = this._onDidChangeActiveTextEditor.event;
readonly onDidChangeVisibleTextEditors: Event<vscode.TextEditor[]> = this._onDidChangeVisibleTextEditors.event;
readonly onDidChangeActiveTextEditor: Event<vscode.TextEditor | undefined> = this._onDidChangeActiveTextEditor.event;
constructor(
@IExtHostRpcService private readonly _extHostRpc: IExtHostRpcService,
@@ -135,7 +136,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
data.id,
this._extHostRpc.getProxy(MainContext.MainThreadTextEditors),
this._logService,
documentData,
new Lazy(() => documentData.document),
data.selections.map(typeConverters.Selection.to),
data.options,
data.visibleRanges.map(range => typeConverters.Range.to(range)),
@@ -162,7 +163,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
}
if (delta.removedEditors || delta.addedEditors) {
this._onDidChangeVisibleTextEditors.fire(this.allEditors());
this._onDidChangeVisibleTextEditors.fire(this.allEditors().map(editor => editor.value));
}
if (delta.newActiveEditor !== undefined) {
this._onDidChangeActiveTextEditor.fire(this.activeEditor());
@@ -181,11 +182,17 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
return this._editors.get(id);
}
activeEditor(): ExtHostTextEditor | undefined {
activeEditor(): vscode.TextEditor | undefined;
activeEditor(internal: true): ExtHostTextEditor | undefined;
activeEditor(internal?: true): vscode.TextEditor | ExtHostTextEditor | undefined {
if (!this._activeEditorId) {
return undefined;
}
const editor = this._editors.get(this._activeEditorId);
if (internal) {
return editor;
} else {
return this._editors.get(this._activeEditorId);
return editor?.value;
}
}

View File

@@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type * as vscode from 'vscode';
import { IEditorTabDto, IExtHostEditorTabsShape } from 'vs/workbench/api/common/extHost.protocol';
import { URI } from 'vs/base/common/uri';
import { Emitter, Event } from 'vs/base/common/event';
export interface IEditorTab {
name: string;
group: number;
resource: vscode.Uri
}
export class ExtHostEditorTabs implements IExtHostEditorTabsShape {
private readonly _onDidChangeTabs = new Emitter<void>();
readonly onDidChangeTabs: Event<void> = this._onDidChangeTabs.event;
private _tabs: IEditorTab[] = [];
get tabs(): readonly IEditorTab[] {
return this._tabs;
}
$acceptEditorTabs(tabs: IEditorTabDto[]): void {
this._tabs = tabs.map(dto => {
return {
name: dto.name,
group: dto.group,
resource: URI.revive(dto.resource)
};
});
this._onDidChangeTabs.fire();
}
}

View File

@@ -5,6 +5,7 @@
import * as nls from 'vs/nls';
import * as path from 'vs/base/common/path';
import * as performance from 'vs/base/common/performance';
import { originalFSPath, joinPath } from 'vs/base/common/resources';
import { Barrier, timeout } from 'vs/base/common/async';
import { dispose, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
@@ -35,6 +36,8 @@ import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelServ
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
import { Emitter, Event } from 'vs/base/common/event';
import { IExtensionActivationHost, checkActivateWorkspaceContainsExtension } from 'vs/workbench/api/common/shared/workspaceContains';
import { ExtHostSecretState, IExtHostSecretState } from 'vs/workbench/api/common/exHostSecretState';
import { ExtensionSecrets } from 'vs/workbench/api/common/extHostSecrets';
interface ITestRunner {
/** Old test runner API, as exported from `vscode/lib/testrunner` */
@@ -50,7 +53,7 @@ export const IHostUtils = createDecorator<IHostUtils>('IHostUtils');
export interface IHostUtils {
readonly _serviceBrand: undefined;
exit(code?: number): void;
exit(code: number): void;
exists(path: string): Promise<boolean>;
realpath(path: string): Promise<string>;
}
@@ -94,6 +97,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
private readonly _readyToRunExtensions: Barrier;
protected readonly _registry: ExtensionDescriptionRegistry;
private readonly _storage: ExtHostStorage;
private readonly _secretState: ExtHostSecretState;
private readonly _storagePath: IExtensionStoragePaths;
private readonly _activator: ExtensionsActivator;
private _extensionPathIndex: Promise<TernarySearchTree<string, IExtensionDescription>> | null;
@@ -115,7 +119,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
@IExtHostInitDataService initData: IExtHostInitDataService,
@IExtensionStoragePaths storagePath: IExtensionStoragePaths,
@IExtHostTunnelService extHostTunnelService: IExtHostTunnelService,
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService,
) {
super();
this._hostUtils = hostUtils;
@@ -138,10 +142,12 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
this._readyToRunExtensions = new Barrier();
this._registry = new ExtensionDescriptionRegistry(this._initData.extensions);
this._storage = new ExtHostStorage(this._extHostContext);
this._secretState = new ExtHostSecretState(this._extHostContext);
this._storagePath = storagePath;
this._instaService = instaService.createChild(new ServiceCollection(
[IExtHostStorage, this._storage]
[IExtHostStorage, this._storage],
[IExtHostSecretState, this._secretState]
));
const hostExtensions = new Set<string>();
@@ -184,6 +190,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
this._almostReadyToRunExtensions.open();
await this._extHostWorkspace.waitForInitializeCall();
performance.mark('code/extHost/ready');
this._readyToStartExtensionHost.open();
if (this._initData.autoStart) {
@@ -362,10 +369,14 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
return Promise.all([
this._loadCommonJSModule<IExtensionModule>(joinPath(extensionDescription.extensionLocation, entryPoint), activationTimesBuilder),
this._loadCommonJSModule<IExtensionModule>(extensionDescription.identifier, joinPath(extensionDescription.extensionLocation, entryPoint), activationTimesBuilder),
this._loadExtensionContext(extensionDescription)
]).then(values => {
performance.mark(`code/extHost/willActivateExtension/${extensionDescription.identifier.value}`);
return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder);
}).then((activatedExtension) => {
performance.mark(`code/extHost/didActivateExtension/${extensionDescription.identifier.value}`);
return activatedExtension;
});
}
@@ -373,6 +384,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
const globalState = new ExtensionGlobalMemento(extensionDescription, this._storage);
const workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage);
const secrets = new ExtensionSecrets(extensionDescription, this._secretState);
const extensionMode = extensionDescription.isUnderDevelopment
? (this._initData.environment.extensionTestsLocationURI ? ExtensionMode.Test : ExtensionMode.Development)
: ExtensionMode.Production;
@@ -388,6 +400,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
return Object.freeze<vscode.ExtensionContext>({
globalState,
workspaceState,
secrets,
subscriptions: [],
get extensionUri() { return extensionDescription.extensionLocation; },
get extensionPath() { return extensionDescription.extensionLocation.fsPath; },
@@ -456,6 +469,9 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
}
private _activateAllStartupFinished(): void {
// startup is considered finished
this._mainThreadExtensionsProxy.$setPerformanceMarks(performance.getMarks());
for (const desc of this._registry.getAllExtensionDescriptions()) {
if (desc.activationEvents) {
for (const activationEvent of desc.activationEvents) {
@@ -541,7 +557,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
let testRunner: ITestRunner | INewTestRunner | undefined;
let requireError: Error | undefined;
try {
testRunner = await this._loadCommonJSModule(URI.file(extensionTestsPath), new ExtensionActivationTimesBuilder(false));
testRunner = await this._loadCommonJSModule(null, URI.file(extensionTestsPath), new ExtensionActivationTimesBuilder(false));
} catch (error) {
requireError = error;
}
@@ -586,11 +602,17 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
}
private _testRunnerExit(code: number): void {
this._logService.info(`extension host terminating: test runner requested exit with code ${code}`);
this._logService.flush();
// wait at most 5000ms for the renderer to confirm our exit request and for the renderer socket to drain
// (this is to ensure all outstanding messages reach the renderer)
const exitPromise = this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
const drainPromise = this._extHostContext.drain();
Promise.race([Promise.all([exitPromise, drainPromise]), timeout(5000)]).then(() => {
this._logService.info(`exiting with code ${code}`);
this._logService.flush();
this._hostUtils.exit(code);
});
}
@@ -644,7 +666,9 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
}
try {
performance.mark(`code/extHost/willResolveAuthority/${authorityPrefix}`);
const result = await resolver.resolve(remoteAuthority, { resolveAttempt });
performance.mark(`code/extHost/didResolveAuthorityOK/${authorityPrefix}`);
this._disposables.add(await this._extHostTunnelService.setTunnelExtensionFunctions(resolver));
// Split merged API result into separate authority/options
@@ -667,6 +691,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
}
};
} catch (err) {
performance.mark(`code/extHost/didResolveAuthorityError/${authorityPrefix}`);
if (err instanceof RemoteAuthorityResolverError) {
return {
type: 'error',
@@ -754,7 +779,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
protected abstract _beforeAlmostReadyToRunExtensions(): Promise<void>;
protected abstract _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined;
protected abstract _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;
protected abstract _loadCommonJSModule<T>(extensionId: ExtensionIdentifier | null, module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;
public abstract $setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MainThreadFileSystemShape, MainContext } from './extHost.protocol';
import { MainContext } from './extHost.protocol';
import * as vscode from 'vscode';
import * as files from 'vs/platform/files/common/files';
import { FileSystemError } from 'vs/workbench/api/common/extHostTypes';
@@ -12,49 +12,51 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
export class ExtHostConsumerFileSystem implements vscode.FileSystem {
export class ExtHostConsumerFileSystem {
readonly _serviceBrand: undefined;
private readonly _proxy: MainThreadFileSystemShape;
readonly value: vscode.FileSystem;
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@IExtHostFileSystemInfo private readonly _fileSystemInfo: IExtHostFileSystemInfo,
@IExtHostFileSystemInfo fileSystemInfo: IExtHostFileSystemInfo,
) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadFileSystem);
}
const proxy = extHostRpc.getProxy(MainContext.MainThreadFileSystem);
stat(uri: vscode.Uri): Promise<vscode.FileStat> {
return this._proxy.$stat(uri).catch(ExtHostConsumerFileSystem._handleError);
}
readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
return this._proxy.$readdir(uri).catch(ExtHostConsumerFileSystem._handleError);
}
createDirectory(uri: vscode.Uri): Promise<void> {
return this._proxy.$mkdir(uri).catch(ExtHostConsumerFileSystem._handleError);
}
async readFile(uri: vscode.Uri): Promise<Uint8Array> {
return this._proxy.$readFile(uri).then(buff => buff.buffer).catch(ExtHostConsumerFileSystem._handleError);
}
writeFile(uri: vscode.Uri, content: Uint8Array): Promise<void> {
return this._proxy.$writeFile(uri, VSBuffer.wrap(content)).catch(ExtHostConsumerFileSystem._handleError);
}
delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise<void> {
return this._proxy.$delete(uri, { ...{ recursive: false, useTrash: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
}
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
return this._proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
}
copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
return this._proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
}
isWritableFileSystem(scheme: string): boolean | undefined {
const capabilities = this._fileSystemInfo.getCapabilities(scheme);
if (typeof capabilities === 'number') {
return !(capabilities & files.FileSystemProviderCapabilities.Readonly);
}
return undefined;
this.value = Object.freeze({
stat(uri: vscode.Uri): Promise<vscode.FileStat> {
return proxy.$stat(uri).catch(ExtHostConsumerFileSystem._handleError);
},
readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
return proxy.$readdir(uri).catch(ExtHostConsumerFileSystem._handleError);
},
createDirectory(uri: vscode.Uri): Promise<void> {
return proxy.$mkdir(uri).catch(ExtHostConsumerFileSystem._handleError);
},
async readFile(uri: vscode.Uri): Promise<Uint8Array> {
return proxy.$readFile(uri).then(buff => buff.buffer).catch(ExtHostConsumerFileSystem._handleError);
},
writeFile(uri: vscode.Uri, content: Uint8Array): Promise<void> {
return proxy.$writeFile(uri, VSBuffer.wrap(content)).catch(ExtHostConsumerFileSystem._handleError);
},
delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise<void> {
return proxy.$delete(uri, { ...{ recursive: false, useTrash: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
},
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
return proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
},
copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean; }): Promise<void> {
return proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
},
isWritableFileSystem(scheme: string): boolean | undefined {
const capabilities = fileSystemInfo.getCapabilities(scheme);
if (typeof capabilities === 'number') {
return !(capabilities & files.FileSystemProviderCapabilities.Readonly);
}
return undefined;
}
});
}
private static _handleError(err: any): never {

View File

@@ -3,12 +3,13 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AsyncEmitter, Emitter, Event, IWaitUntil } from 'vs/base/common/event';
import { Emitter, Event } from 'vs/base/common/event';
import { AsyncEmitter, IWaitUntil } from 'vs/base/common/async';
import { IRelativePattern, parse } from 'vs/base/common/glob';
import { URI } from 'vs/base/common/uri';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import type * as vscode from 'vscode';
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, SourceTargetPair, IWorkspaceEditDto, MainThreadBulkEditsShape } from './extHost.protocol';
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, SourceTargetPair, IWorkspaceEditDto, IWillRunFileOperationParticipation } from './extHost.protocol';
import * as typeConverter from './extHostTypeConverters';
import { Disposable, WorkspaceEdit } from './extHostTypes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
@@ -122,8 +123,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
constructor(
mainContext: IMainContext,
private readonly _logService: ILogService,
private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape = mainContext.getProxy(MainContext.MainThreadBulkEdits)
private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors
) {
//
}
@@ -178,24 +178,21 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
};
}
async $onWillRunFileOperation(operation: FileOperation, files: SourceTargetPair[], undoRedoGroupId: number | undefined, timeout: number, token: CancellationToken): Promise<any> {
async $onWillRunFileOperation(operation: FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise<IWillRunFileOperationParticipation | undefined> {
switch (operation) {
case FileOperation.MOVE:
await this._fireWillEvent(this._onWillRenameFile, { files: files.map(f => ({ oldUri: URI.revive(f.source!), newUri: URI.revive(f.target) })) }, undoRedoGroupId, timeout, token);
break;
return await this._fireWillEvent(this._onWillRenameFile, { files: files.map(f => ({ oldUri: URI.revive(f.source!), newUri: URI.revive(f.target) })) }, timeout, token);
case FileOperation.DELETE:
await this._fireWillEvent(this._onWillDeleteFile, { files: files.map(f => URI.revive(f.target)) }, undoRedoGroupId, timeout, token);
break;
return await this._fireWillEvent(this._onWillDeleteFile, { files: files.map(f => URI.revive(f.target)) }, timeout, token);
case FileOperation.CREATE:
await this._fireWillEvent(this._onWillCreateFile, { files: files.map(f => URI.revive(f.target)) }, undoRedoGroupId, timeout, token);
break;
default:
//ignore, dont send
return await this._fireWillEvent(this._onWillCreateFile, { files: files.map(f => URI.revive(f.target)) }, timeout, token);
}
return undefined;
}
private async _fireWillEvent<E extends IWaitUntil>(emitter: AsyncEmitter<E>, data: Omit<E, 'waitUntil'>, undoRedoGroupId: number | undefined, timeout: number, token: CancellationToken): Promise<any> {
private async _fireWillEvent<E extends IWaitUntil>(emitter: AsyncEmitter<E>, data: Omit<E, 'waitUntil'>, timeout: number, token: CancellationToken): Promise<IWillRunFileOperationParticipation | undefined> {
const extensionNames = new Set<string>();
const edits: WorkspaceEdit[] = [];
await emitter.fireAsync(data, token, async (thenable, listener) => {
@@ -204,25 +201,28 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
const result = await Promise.resolve(thenable);
if (result instanceof WorkspaceEdit) {
edits.push(result);
extensionNames.add((<IExtensionListener<E>>listener).extension.displayName ?? (<IExtensionListener<E>>listener).extension.identifier.value);
}
if (Date.now() - now > timeout) {
this._logService.warn('SLOW file-participant', (<IExtensionListener<E>>listener).extension?.identifier);
this._logService.warn('SLOW file-participant', (<IExtensionListener<E>>listener).extension.identifier);
}
});
if (token.isCancellationRequested) {
return;
return undefined;
}
if (edits.length > 0) {
// concat all WorkspaceEdits collected via waitUntil-call and apply them in one go.
const dto: IWorkspaceEditDto = { edits: [] };
for (let edit of edits) {
let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
dto.edits = dto.edits.concat(edits);
}
return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit(dto, undoRedoGroupId);
if (edits.length === 0) {
return undefined;
}
// concat all WorkspaceEdits collected via waitUntil-call and send them over to the renderer
const dto: IWorkspaceEditDto = { edits: [] };
for (let edit of edits) {
let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
dto.edits = dto.edits.concat(edits);
}
return { edit: dto, extensionNames: Array.from(extensionNames) };
}
}

View File

@@ -27,11 +27,12 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio
import { IURITransformer } from 'vs/base/common/uriIpc';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer';
import { encodeSemanticTokensDto } from 'vs/workbench/api/common/shared/semanticTokensDto';
import { encodeSemanticTokensDto } from 'vs/editor/common/services/semanticTokensDto';
import { IdGenerator } from 'vs/base/common/idGenerator';
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
import { Cache } from './cache';
import { StopWatch } from 'vs/base/common/stopwatch';
import { CancellationError } from 'vs/base/common/errors';
// --- adapter
@@ -1062,6 +1063,20 @@ class SignatureHelpAdapter {
}
}
class InlineHintsAdapter {
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.InlineHintsProvider,
) { }
provideInlineHints(resource: URI, range: IRange, token: CancellationToken): Promise<extHostProtocol.IInlineHintsDto | undefined> {
const doc = this._documents.getDocument(resource);
return asPromise(() => this._provider.provideInlineHints(doc, typeConvert.Range.to(range), token)).then(value => {
return value ? { hints: value.map(typeConvert.InlineHint.from) } : undefined;
});
}
}
class LinkProviderAdapter {
private _cache = new Cache<vscode.DocumentLink>('DocumentLink');
@@ -1320,7 +1335,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter
| TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter
| SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter | EvaluatableExpressionAdapter
| LinkedEditingRangeAdapter;
| LinkedEditingRangeAdapter | InlineHintsAdapter;
class AdapterData {
constructor(
@@ -1403,7 +1418,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
return ExtHostLanguageFeatures._handlePool++;
}
private _withAdapter<A, R>(handle: number, ctor: { new(...args: any[]): A; }, callback: (adapter: A, extension: IExtensionDescription | undefined) => Promise<R>, fallbackValue: R): Promise<R> {
private _withAdapter<A, R>(handle: number, ctor: { new(...args: any[]): A; }, callback: (adapter: A, extension: IExtensionDescription | undefined) => Promise<R>, fallbackValue: R, allowCancellationError: boolean = false): Promise<R> {
const data = this._adapter.get(handle);
if (!data) {
return Promise.resolve(fallbackValue);
@@ -1421,8 +1436,11 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
Promise.resolve(p).then(
() => this._logService.trace(`[${extension.identifier.value}] provider DONE after ${Date.now() - t1}ms`),
err => {
this._logService.error(`[${extension.identifier.value}] provider FAILED`);
this._logService.error(err);
const isExpectedError = allowCancellationError && (err instanceof CancellationError);
if (!isExpectedError) {
this._logService.error(`[${extension.identifier.value}] provider FAILED`);
this._logService.error(err);
}
}
);
}
@@ -1711,7 +1729,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
}
$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);
return this._withAdapter(handle, DocumentSemanticTokensAdapter, adapter => adapter.provideDocumentSemanticTokens(URI.revive(resource), previousResultId, token), null, true);
}
$releaseDocumentSemanticTokens(handle: number, semanticColoringResultId: number): void {
@@ -1725,7 +1743,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
}
$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);
return this._withAdapter(handle, DocumentRangeSemanticTokensAdapter, adapter => adapter.provideDocumentRangeSemanticTokens(URI.revive(resource), range, token), null, true);
}
//#endregion
@@ -1770,6 +1788,27 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.releaseSignatureHelp(id), undefined);
}
// --- inline hints
registerInlineHintsProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineHintsProvider): vscode.Disposable {
const eventHandle = typeof provider.onDidChangeInlineHints === 'function' ? this._nextHandle() : undefined;
const handle = this._addNewAdapter(new InlineHintsAdapter(this._documents, provider), extension);
this._proxy.$registerInlineHintsProvider(handle, this._transformDocumentSelector(selector), eventHandle);
let result = this._createDisposable(handle);
if (eventHandle !== undefined) {
const subscription = provider.onDidChangeInlineHints!(_ => this._proxy.$emitInlineHintsEvent(eventHandle));
result = Disposable.from(result, subscription);
}
return result;
}
$provideInlineHints(handle: number, resource: UriComponents, range: IRange, token: CancellationToken): Promise<extHostProtocol.IInlineHintsDto | undefined> {
return this._withAdapter(handle, InlineHintsAdapter, adapter => adapter.provideInlineHints(URI.revive(resource), range, token), undefined);
}
// --- links
registerDocumentLinkProvider(extension: IExtensionDescription | undefined, selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable {
@@ -1882,7 +1921,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
return {
beforeText: ExtHostLanguageFeatures._serializeRegExp(onEnterRule.beforeText),
afterText: onEnterRule.afterText ? ExtHostLanguageFeatures._serializeRegExp(onEnterRule.afterText) : undefined,
oneLineAboveText: onEnterRule.oneLineAboveText ? ExtHostLanguageFeatures._serializeRegExp(onEnterRule.oneLineAboveText) : undefined,
previousLineText: onEnterRule.previousLineText ? ExtHostLanguageFeatures._serializeRegExp(onEnterRule.previousLineText) : undefined,
action: onEnterRule.action
};
}

View File

@@ -8,6 +8,7 @@ import type * as vscode from 'vscode';
import { MainContext, MainThreadMessageServiceShape, MainThreadMessageOptions, IMainContext } from './extHost.protocol';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
function isMessageItem(item: any): item is vscode.MessageItem {
return item && item.title;
@@ -37,9 +38,14 @@ export class ExtHostMessageService {
items = [optionsOrFirstItem, ...rest];
} else {
options.modal = optionsOrFirstItem && optionsOrFirstItem.modal;
options.useCustom = optionsOrFirstItem && optionsOrFirstItem.useCustom;
items = rest;
}
if (options.useCustom) {
checkProposedApiEnabled(extension);
}
const commands: { title: string; isCloseAffordance: boolean; handle: number; }[] = [];
for (let handle = 0; handle < items.length; handle++) {

View File

@@ -78,8 +78,8 @@ export interface ExtHostNotebookOutputRenderingHandler {
}
export class ExtHostNotebookKernelProviderAdapter extends Disposable {
private _kernelToId = new Map<vscode.NotebookKernel, string>();
private _idToKernel = new Map<string, vscode.NotebookKernel>();
private _kernelToFriendlyId = new Map<vscode.NotebookKernel, string>();
private _friendlyIdToKernel = new Map<string, vscode.NotebookKernel>();
constructor(
private readonly _proxy: MainThreadNotebookShape,
private readonly _handle: number,
@@ -101,24 +101,25 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
const newMap = new Map<vscode.NotebookKernel, string>();
let kernel_unique_pool = 0;
const kernelIdCache = new Set<string>();
const kernelFriendlyIdCache = new Set<string>();
const transformedData: INotebookKernelInfoDto2[] = data.map(kernel => {
let id = this._kernelToId.get(kernel);
if (id === undefined) {
if (kernel.id && kernelIdCache.has(kernel.id)) {
id = `${this._extension.identifier.value}_${kernel.id}_${kernel_unique_pool++}`;
let friendlyId = this._kernelToFriendlyId.get(kernel);
if (friendlyId === undefined) {
if (kernel.id && kernelFriendlyIdCache.has(kernel.id)) {
friendlyId = `${this._extension.identifier.value}_${kernel.id}_${kernel_unique_pool++}`;
} else {
id = `${this._extension.identifier.value}_${kernel.id || UUID.generateUuid()}`;
friendlyId = `${this._extension.identifier.value}_${kernel.id || UUID.generateUuid()}`;
}
this._kernelToId.set(kernel, id);
this._kernelToFriendlyId.set(kernel, friendlyId);
}
newMap.set(kernel, id);
newMap.set(kernel, friendlyId);
return {
id,
id: kernel.id,
friendlyId: friendlyId,
label: kernel.label,
extension: this._extension.identifier,
extensionLocation: this._extension.extensionLocation,
@@ -129,22 +130,22 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
};
});
this._kernelToId = newMap;
this._kernelToFriendlyId = newMap;
this._idToKernel.clear();
this._kernelToId.forEach((value, key) => {
this._idToKernel.set(value, key);
this._friendlyIdToKernel.clear();
this._kernelToFriendlyId.forEach((value, key) => {
this._friendlyIdToKernel.set(value, key);
});
return transformedData;
}
getKernel(kernelId: string) {
return this._idToKernel.get(kernelId);
getKernelByFriendlyId(kernelId: string) {
return this._friendlyIdToKernel.get(kernelId);
}
async resolveNotebook(kernelId: string, document: ExtHostNotebookDocument, webview: vscode.NotebookCommunication, token: CancellationToken) {
const kernel = this._idToKernel.get(kernelId);
const kernel = this._friendlyIdToKernel.get(kernelId);
if (kernel && this._provider.resolveKernel) {
return this._provider.resolveKernel(kernel, document.notebookDocument, webview, token);
@@ -152,7 +153,7 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
}
async executeNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) {
const kernel = this._idToKernel.get(kernelId);
const kernel = this._friendlyIdToKernel.get(kernelId);
if (!kernel) {
return;
@@ -166,7 +167,7 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
}
async cancelNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) {
const kernel = this._idToKernel.get(kernelId);
const kernel = this._friendlyIdToKernel.get(kernelId);
if (!kernel) {
return;
@@ -190,21 +191,22 @@ async function withToken(cb: (token: CancellationToken) => any) {
}
}
export class NotebookEditorDecorationType implements vscode.NotebookEditorDecorationType {
export class NotebookEditorDecorationType {
private static readonly _Keys = new IdGenerator('NotebookEditorDecorationType');
private _proxy: MainThreadNotebookShape;
public key: string;
readonly value: vscode.NotebookEditorDecorationType;
constructor(proxy: MainThreadNotebookShape, options: vscode.NotebookDecorationRenderOptions) {
this.key = NotebookEditorDecorationType._Keys.nextId();
this._proxy = proxy;
this._proxy.$registerNotebookEditorDecorationType(this.key, typeConverters.NotebookDecorationRenderOptions.from(options));
}
const key = NotebookEditorDecorationType._Keys.nextId();
proxy.$registerNotebookEditorDecorationType(key, typeConverters.NotebookDecorationRenderOptions.from(options));
public dispose(): void {
this._proxy.$removeNotebookEditorDecorationType(this.key);
this.value = {
key,
dispose() {
proxy.$removeNotebookEditorDecorationType(key);
}
};
}
}
@@ -320,23 +322,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
this._notebookContentProviders.set(viewType, { extension, provider });
const listeners: vscode.Disposable[] = [];
listeners.push(provider.onDidChangeNotebook
? provider.onDidChangeNotebook(e => {
const document = this._documents.get(URI.revive(e.document.uri));
if (!document) {
throw new Error(`Notebook document ${e.document.uri.toString()} not found`);
}
if (isEditEvent(e)) {
const editId = document.addEdit(e);
this._proxy.$onUndoableContentChange(e.document.uri, viewType, editId, e.label);
} else {
this._proxy.$onContentChange(e.document.uri, viewType);
}
})
: Disposable.None);
listeners.push(provider.onDidChangeNotebookContentOptions
? provider.onDidChangeNotebookContentOptions(() => {
this._proxy.$updateNotebookProviderOptions(viewType, provider.options);
@@ -383,7 +368,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
}
createNotebookEditorDecorationType(options: vscode.NotebookDecorationRenderOptions): vscode.NotebookEditorDecorationType {
return new NotebookEditorDecorationType(this._proxy, options);
return new NotebookEditorDecorationType(this._proxy, options).value;
}
async openNotebookDocument(uriComponents: UriComponents, viewType?: string): Promise<vscode.NotebookDocument> {
@@ -543,26 +528,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
return false;
}
async $undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void> {
const document = this._documents.get(URI.revive(uri));
if (!document) {
return;
}
document.undo(editId, isDirty);
}
async $redoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void> {
const document = this._documents.get(URI.revive(uri));
if (!document) {
return;
}
document.redo(editId, isDirty);
}
async $backup(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string | undefined> {
const document = this._documents.get(URI.revive(uri));
const provider = this._notebookContentProviders.get(viewType);
@@ -580,10 +545,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
this._outputDisplayOrder = displayOrder;
}
$acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelId: string | undefined; }) {
$acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelFriendlyId: string | undefined; }) {
if (event.providerHandle !== undefined) {
this._withAdapter(event.providerHandle, event.uri, async (adapter, document) => {
const kernel = event.kernelId ? adapter.getKernel(event.kernelId) : undefined;
const kernel = event.kernelFriendlyId ? adapter.getKernelByFriendlyId(event.kernelFriendlyId) : undefined;
this._editors.forEach(editor => {
if (editor.editor.notebookData === document) {
editor.editor._acceptKernel(kernel);
@@ -891,11 +856,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
}
}
function isEditEvent(e: vscode.NotebookDocumentEditEvent | vscode.NotebookDocumentContentChangeEvent): e is vscode.NotebookDocumentEditEvent {
return typeof (e as vscode.NotebookDocumentEditEvent).undo === 'function'
&& typeof (e as vscode.NotebookDocumentEditEvent).redo === 'function';
}
export class NotebookCellStatusBarItemInternal extends Disposable {
private static NEXT_ID = 0;

View File

@@ -15,7 +15,6 @@ import { CellKind, INotebookDocumentPropertiesChangeData, IWorkspaceCellEditDto,
import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { CellEditType, CellOutputKind, diff, IMainCellDto, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import * as vscode from 'vscode';
import { Cache } from './cache';
interface IObservable<T> {
@@ -113,14 +112,17 @@ export class ExtHostCell extends Disposable {
get cell(): vscode.NotebookCell {
if (!this._cell) {
const that = this;
const document = this._extHostDocument.getDocument(this.uri)!.document;
const data = this._extHostDocument.getDocument(this.uri);
if (!data) {
throw new Error(`MISSING extHostDocument for notebook cell: ${this.uri}`);
}
this._cell = Object.freeze({
get index() { return that._notebook.getCellIndex(that); },
notebook: that._notebook.notebookDocument,
uri: that.uri,
cellKind: this._cellData.cellKind,
document,
get language() { return document.languageId; },
document: data.document,
get language() { return data!.document.languageId; },
get outputs() { return that._outputs; },
set outputs(value) { that._updateOutputs(value); },
get metadata() { return that._metadata; },
@@ -228,8 +230,6 @@ export class ExtHostNotebookDocument extends Disposable {
private _disposed = false;
private _languages: string[] = [];
private readonly _edits = new Cache<vscode.NotebookDocumentEditEvent>('notebook documents');
constructor(
private readonly _proxy: MainThreadNotebookShape,
private readonly _documentsAndEditors: ExtHostDocumentsAndEditors,
@@ -493,37 +493,4 @@ export class ExtHostNotebookDocument extends Disposable {
getCellIndex(cell: ExtHostCell): number {
return this._cells.indexOf(cell);
}
addEdit(item: vscode.NotebookDocumentEditEvent): number {
return this._edits.add([item]);
}
async undo(editId: number, isDirty: boolean): Promise<void> {
await this.getEdit(editId).undo();
// if (!isDirty) {
// this.disposeBackup();
// }
}
async redo(editId: number, isDirty: boolean): Promise<void> {
await this.getEdit(editId).redo();
// if (!isDirty) {
// this.disposeBackup();
// }
}
private getEdit(editId: number): vscode.NotebookDocumentEditEvent {
const edit = this._edits.get(editId, 0);
if (!edit) {
throw new Error('No edit found');
}
return edit;
}
disposeEdits(editIds: number[]): void {
for (const id of editIds) {
this._edits.delete(id);
}
}
}

View File

@@ -208,7 +208,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
if (prev.editType === CellEditType.Replace && editData.cellEdits[i].editType === CellEditType.Replace) {
const edit = editData.cellEdits[i];
if ((edit.editType !== CellEditType.DocumentMetadata && edit.editType !== CellEditType.Unknown) && prev.index === edit.index) {
if ((edit.editType !== CellEditType.DocumentMetadata) && prev.index === edit.index) {
prev.cells.push(...(editData.cellEdits[i] as ICellReplaceEdit).cells);
prev.count += (editData.cellEdits[i] as ICellReplaceEdit).count;
continue;

View File

@@ -233,6 +233,7 @@ class ExtHostQuickInput implements QuickInput {
private static _nextId = 1;
_id = ExtHostQuickPick._nextId++;
#proxy: MainThreadQuickOpenShape;
private _title: string | undefined;
private _steps: number | undefined;
private _totalSteps: number | undefined;
@@ -260,7 +261,8 @@ class ExtHostQuickInput implements QuickInput {
this._onDidChangeValueEmitter
];
constructor(protected _proxy: MainThreadQuickOpenShape, protected _extensionId: ExtensionIdentifier, private _onDidDispose: () => void) {
constructor(proxy: MainThreadQuickOpenShape, protected _extensionId: ExtensionIdentifier, private _onDidDispose: () => void) {
this.#proxy = proxy;
}
get title() {
@@ -409,7 +411,7 @@ class ExtHostQuickInput implements QuickInput {
this._updateTimeout = undefined;
}
this._onDidDispose();
this._proxy.$dispose(this._id);
this.#proxy.$dispose(this._id);
}
protected update(properties: Record<string, any>): void {
@@ -437,7 +439,7 @@ class ExtHostQuickInput implements QuickInput {
}
private dispatchUpdate() {
this._proxy.$createOrUpdate(this._pendingUpdate);
this.#proxy.$createOrUpdate(this._pendingUpdate);
this._pendingUpdate = { id: this._id };
}
}

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as performance from 'vs/base/common/performance';
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { MainThreadTelemetryShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
@@ -55,7 +56,9 @@ export abstract class RequireInterceptor {
this._installInterceptor();
performance.mark('code/extHost/willWaitForConfig');
const configProvider = await this._extHostConfiguration.getConfigProvider();
performance.mark('code/extHost/didWaitForConfig');
const extensionPaths = await this._extHostExtensionService.getExtensionPathIndex();
this.register(new VSCodeNodeModuleFactory(this._apiFactory.vscode, extensionPaths, this._extensionRegistry, configProvider, this._logService)); // {{SQL CARBON EDIT}} // add node module

View File

@@ -0,0 +1,43 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type * as vscode from 'vscode';
import { ExtHostSecretState } from 'vs/workbench/api/common/exHostSecretState';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { Emitter, Event } from 'vs/base/common/event';
export class ExtensionSecrets implements vscode.SecretStorage {
protected readonly _id: string;
readonly #secretState: ExtHostSecretState;
private _onDidChange = new Emitter<vscode.SecretStorageChangeEvent>();
readonly onDidChange: Event<vscode.SecretStorageChangeEvent> = this._onDidChange.event;
constructor(extensionDescription: IExtensionDescription, secretState: ExtHostSecretState) {
this._id = ExtensionIdentifier.toKey(extensionDescription.identifier);
this.#secretState = secretState;
this.#secretState.onDidChangePassword(e => {
if (e.extensionId === this._id) {
this._onDidChange.fire({ key: e.key });
}
});
}
get(key: string): Promise<string | undefined> {
return this.#secretState.get(this._id, key);
}
store(key: string, value: string): Promise<void> {
return this.#secretState.store(this._id, key, value);
}
delete(key: string): Promise<void> {
return this.#secretState.delete(this._id, key);
}
}

View File

@@ -10,12 +10,16 @@ import { MainContext, MainThreadStatusBarShape, IMainContext, ICommandDto } from
import { localize } from 'vs/nls';
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
private static ID_GEN = 0;
private static ALLOWED_BACKGROUND_COLORS = new Set<string>(['statusBarItem.errorBackground']);
private static ALLOWED_BACKGROUND_COLORS = new Map<string, ThemeColor>(
[['statusBarItem.errorBackground', new ThemeColor('statusBarItem.errorForeground')]]
);
#proxy: MainThreadStatusBarShape;
#commands: CommandsConverter;
private _id: number;
private _alignment: number;
@@ -37,21 +41,18 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
};
private _timeoutHandle: any;
private _proxy: MainThreadStatusBarShape;
private _commands: CommandsConverter;
private _accessibilityInformation?: vscode.AccessibilityInformation;
private _extension?: IExtensionDescription;
constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation, extension?: IExtensionDescription) {
constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation) {
this.#proxy = proxy;
this.#commands = commands;
this._id = ExtHostStatusBarEntry.ID_GEN++;
this._proxy = proxy;
this._commands = commands;
this._statusId = id;
this._statusName = name;
this._alignment = alignment;
this._priority = priority;
this._accessibilityInformation = accessibilityInformation;
this._extension = extension;
}
public get id(): number {
@@ -106,10 +107,6 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
}
public set backgroundColor(color: ThemeColor | undefined) {
if (this._extension) {
checkProposedApiEnabled(this._extension);
}
if (color && !ExtHostStatusBarEntry.ALLOWED_BACKGROUND_COLORS.has(color.id)) {
color = undefined;
}
@@ -127,12 +124,12 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
if (typeof command === 'string') {
this._command = {
fromApi: command,
internal: this._commands.toInternal({ title: '', command }, this._internalCommandRegistration),
internal: this.#commands.toInternal({ title: '', command }, this._internalCommandRegistration),
};
} else if (command) {
this._command = {
fromApi: command,
internal: this._commands.toInternal(command, this._internalCommandRegistration),
internal: this.#commands.toInternal(command, this._internalCommandRegistration),
};
} else {
this._command = undefined;
@@ -153,7 +150,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
public hide(): void {
clearTimeout(this._timeoutHandle);
this._visible = false;
this._proxy.$dispose(this.id);
this.#proxy.$dispose(this.id);
}
private update(): void {
@@ -167,9 +164,15 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
this._timeoutHandle = setTimeout(() => {
this._timeoutHandle = undefined;
// If a background color is set, the foreground is determined
let color = this._color;
if (this._backgroundColor) {
color = ExtHostStatusBarEntry.ALLOWED_BACKGROUND_COLORS.get(this._backgroundColor.id);
}
// Set to status bar
this._proxy.$setEntry(this.id, this._statusId, this._statusName, this.text, this.tooltip, this._command?.internal, this.color,
this.backgroundColor, this._alignment === ExtHostStatusBarAlignment.Left ? MainThreadStatusBarAlignment.LEFT : MainThreadStatusBarAlignment.RIGHT,
this.#proxy.$setEntry(this.id, this._statusId, this._statusName, this._text, this._tooltip, this._command?.internal, color,
this._backgroundColor, this._alignment === ExtHostStatusBarAlignment.Left ? MainThreadStatusBarAlignment.LEFT : MainThreadStatusBarAlignment.RIGHT,
this._priority, this._accessibilityInformation);
}, 0);
}
@@ -230,8 +233,8 @@ export class ExtHostStatusBar {
this._statusMessage = new StatusBarMessage(this);
}
createStatusBarEntry(id: string, name: string, alignment?: ExtHostStatusBarAlignment, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation, extension?: IExtensionDescription): vscode.StatusBarItem {
return new ExtHostStatusBarEntry(this._proxy, this._commands, id, name, alignment, priority, accessibilityInformation, extension);
createStatusBarEntry(id: string, name: string, alignment?: ExtHostStatusBarAlignment, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation): vscode.StatusBarItem {
return new ExtHostStatusBarEntry(this._proxy, this._commands, id, name, alignment, priority, accessibilityInformation);
}
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): Disposable {

View File

@@ -48,7 +48,7 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths {
const storageUri = URI.joinPath(this._environment.workspaceStorageHome, storageName);
try {
await this._extHostFileSystem.stat(storageUri);
await this._extHostFileSystem.value.stat(storageUri);
this._logService.trace('[ExtHostStorage] storage dir already exists', storageUri);
return storageUri;
} catch {
@@ -57,8 +57,8 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths {
try {
this._logService.trace('[ExtHostStorage] creating dir and metadata-file', storageUri);
await this._extHostFileSystem.createDirectory(storageUri);
await this._extHostFileSystem.writeFile(
await this._extHostFileSystem.value.createDirectory(storageUri);
await this._extHostFileSystem.value.writeFile(
URI.joinPath(storageUri, 'meta.json'),
new TextEncoder().encode(JSON.stringify({
id: this._workspace.id,

View File

@@ -341,7 +341,10 @@ export namespace TaskFilterDTO {
class TaskExecutionImpl implements vscode.TaskExecution {
constructor(private readonly _tasks: ExtHostTaskBase, readonly _id: string, private readonly _task: vscode.Task) {
readonly #tasks: ExtHostTaskBase;
constructor(tasks: ExtHostTaskBase, readonly _id: string, private readonly _task: vscode.Task) {
this.#tasks = tasks;
}
public get task(): vscode.Task {
@@ -349,7 +352,7 @@ class TaskExecutionImpl implements vscode.TaskExecution {
}
public terminate(): void {
this._tasks.terminateTask(this);
this.#tasks.terminateTask(this);
}
public fireDidStartProcess(value: tasks.TaskProcessStartedDTO): void {

View File

@@ -5,12 +5,11 @@
import type * as vscode from 'vscode';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ITerminalChildProcess, EXT_HOST_CREATION_DELAY, ITerminalLaunchError, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal';
import { timeout } from 'vs/base/common/async';
import { ITerminalChildProcess, ITerminalLaunchError, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
@@ -21,6 +20,7 @@ import { localize } from 'vs/nls';
import { NotSupportedError } from 'vs/base/common/errors';
import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { generateUuid } from 'vs/base/common/uuid';
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable {
@@ -47,63 +47,8 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, ID
export const IExtHostTerminalService = createDecorator<IExtHostTerminalService>('IExtHostTerminalService');
export class BaseExtHostTerminal {
public _id: number | undefined;
protected _idPromise: Promise<number>;
private _idPromiseComplete: ((value: number) => any) | undefined;
export class ExtHostTerminal {
private _disposed: boolean = false;
private _queuedRequests: ApiRequest[] = [];
constructor(
protected _proxy: MainThreadTerminalServiceShape,
id?: number
) {
this._idPromise = new Promise<number>(c => {
if (id !== undefined) {
this._id = id;
c(id);
} else {
this._idPromiseComplete = c;
}
});
}
public dispose(): void {
if (!this._disposed) {
this._disposed = true;
this._queueApiRequest(this._proxy.$dispose, []);
}
}
protected _checkDisposed() {
if (this._disposed) {
throw new Error('Terminal has already been disposed');
}
}
protected _queueApiRequest(callback: (...args: any[]) => void, args: any[]): void {
const request: ApiRequest = new ApiRequest(callback, args);
if (!this._id) {
this._queuedRequests.push(request);
return;
}
request.run(this._proxy, this._id);
}
public _runQueuedRequests(id: number): void {
this._id = id;
if (this._idPromiseComplete) {
this._idPromiseComplete(id);
this._idPromiseComplete = undefined;
}
this._queuedRequests.forEach((r) => {
r.run(this._proxy, id);
});
this._queuedRequests.length = 0;
}
}
export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Terminal {
private _pidPromise: Promise<number | undefined>;
private _cols: number | undefined;
private _pidPromiseComplete: ((value: number | undefined) => any) | undefined;
@@ -112,15 +57,59 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
public isOpen: boolean = false;
readonly value: vscode.Terminal;
constructor(
proxy: MainThreadTerminalServiceShape,
private _proxy: MainThreadTerminalServiceShape,
public _id: TerminalIdentifier,
private readonly _creationOptions: vscode.TerminalOptions | vscode.ExtensionTerminalOptions,
private _name?: string,
id?: number
) {
super(proxy, id);
this._creationOptions = Object.freeze(this._creationOptions);
this._pidPromise = new Promise<number | undefined>(c => this._pidPromiseComplete = c);
const that = this;
this.value = {
get name(): string {
return that._name || '';
},
get processId(): Promise<number | undefined> {
return that._pidPromise;
},
get creationOptions(): Readonly<vscode.TerminalOptions | vscode.ExtensionTerminalOptions> {
return that._creationOptions;
},
get exitStatus(): vscode.TerminalExitStatus | undefined {
return that._exitStatus;
},
sendText(text: string, addNewLine: boolean = true): void {
that._checkDisposed();
that._proxy.$sendText(that._id, text, addNewLine);
},
show(preserveFocus: boolean): void {
that._checkDisposed();
that._proxy.$show(that._id, preserveFocus);
},
hide(): void {
that._checkDisposed();
that._proxy.$hide(that._id);
},
dispose(): void {
if (!that._disposed) {
that._disposed = true;
that._proxy.$dispose(that._id);
}
},
get dimensions(): vscode.TerminalDimensions | undefined {
if (that._cols === undefined || that._rows === undefined) {
return undefined;
}
return {
columns: that._cols,
rows: that._rows
};
}
};
}
public async create(
@@ -133,40 +122,34 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
hideFromUser?: boolean,
isFeatureTerminal?: boolean
): Promise<void> {
const result = await this._proxy.$createTerminal({ name: this._name, shellPath, shellArgs, cwd, env, waitOnExit, strictEnv, hideFromUser, isFeatureTerminal });
this._name = result.name;
this._runQueuedRequests(result.id);
if (typeof this._id !== 'string') {
throw new Error('Terminal has already been created');
}
await this._proxy.$createTerminal(this._id, { name: this._name, shellPath, shellArgs, cwd, env, waitOnExit, strictEnv, hideFromUser, isFeatureTerminal });
}
public async createExtensionTerminal(): Promise<number> {
const result = await this._proxy.$createTerminal({ name: this._name, isExtensionTerminal: true });
this._name = result.name;
this._runQueuedRequests(result.id);
return result.id;
if (typeof this._id !== 'string') {
throw new Error('Terminal has already been created');
}
await this._proxy.$createTerminal(this._id, { name: this._name, isExtensionTerminal: true });
// At this point, the id has been set via `$acceptTerminalOpened`
if (typeof this._id === 'string') {
throw new Error('Terminal creation failed');
}
return this._id;
}
public get name(): string {
return this._name || '';
private _checkDisposed() {
if (this._disposed) {
throw new Error('Terminal has already been disposed');
}
}
public set name(name: string) {
this._name = name;
}
public get exitStatus(): vscode.TerminalExitStatus | undefined {
return this._exitStatus;
}
public get dimensions(): vscode.TerminalDimensions | undefined {
if (this._cols === undefined || this._rows === undefined) {
return undefined;
}
return {
columns: this._cols,
rows: this._rows
};
}
public setExitCode(code: number | undefined) {
this._exitStatus = Object.freeze({ code });
}
@@ -184,29 +167,6 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
return true;
}
public get processId(): Promise<number | undefined> {
return this._pidPromise;
}
public get creationOptions(): Readonly<vscode.TerminalOptions | vscode.ExtensionTerminalOptions> {
return this._creationOptions;
}
public sendText(text: string, addNewLine: boolean = true): void {
this._checkDisposed();
this._queueApiRequest(this._proxy.$sendText, [text, addNewLine]);
}
public show(preserveFocus: boolean): void {
this._checkDisposed();
this._queueApiRequest(this._proxy.$show, [preserveFocus]);
}
public hide(): void {
this._checkDisposed();
this._queueApiRequest(this._proxy.$hide, []);
}
public _setProcessId(processId: number | undefined): void {
// The event may fire 2 times when the panel is restored
if (this._pidPromiseComplete) {
@@ -223,20 +183,6 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
}
}
class ApiRequest {
private _callback: (...args: any[]) => void;
private _args: any[];
constructor(callback: (...args: any[]) => void, args: any[]) {
this._callback = callback;
this._args = args;
}
public run(proxy: MainThreadTerminalServiceShape, id: number) {
this._callback.apply(proxy, [id].concat(this._args));
}
}
export class ExtHostPseudoterminal implements ITerminalChildProcess {
private readonly _onProcessData = new Emitter<string>();
public readonly onProcessData: Event<string> = this._onProcessData.event;
@@ -271,6 +217,11 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
}
}
acknowledgeDataEvent(charCount: number): void {
// No-op, flow control is not supported in extension owned terminals. If this is ever
// implemented it will need new pause and resume VS Code APIs.
}
getInitialCwd(): Promise<string> {
return Promise.resolve('');
}
@@ -296,6 +247,11 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
}
this._pty.open(initialDimensions ? initialDimensions : undefined);
if (this._pty.setDimensions && initialDimensions) {
this._pty.setDimensions(initialDimensions);
}
this._onProcessReady.fire({ pid: -1, cwd: '' });
}
}
@@ -325,8 +281,8 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
private readonly _terminalLinkCache: Map<number, Map<number, ICachedLinkEntry>> = new Map();
private readonly _terminalLinkCancellationSource: Map<number, CancellationTokenSource> = new Map();
public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; }
public get terminals(): ExtHostTerminal[] { return this._terminals; }
public get activeTerminal(): vscode.Terminal | undefined { return this._activeTerminal?.value; }
public get terminals(): vscode.Terminal[] { return this._terminals.map(term => term.value); }
protected readonly _onDidCloseTerminal: Emitter<vscode.Terminal> = new Emitter<vscode.Terminal>();
public get onDidCloseTerminal(): Event<vscode.Terminal> { return this._onDidCloseTerminal && this._onDidCloseTerminal.event; }
@@ -370,18 +326,18 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
public abstract $acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
public createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal {
const terminal = new ExtHostTerminal(this._proxy, options, options.name);
const terminal = new ExtHostTerminal(this._proxy, generateUuid(), options, options.name);
const p = new ExtHostPseudoterminal(options.pty);
terminal.createExtensionTerminal().then(id => {
const disposable = this._setupExtHostProcessListeners(id, p);
this._terminalProcessDisposables[id] = disposable;
});
this._terminals.push(terminal);
return terminal;
return terminal.value;
}
public attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void {
const terminal = this._getTerminalByIdEventually(id);
const terminal = this._getTerminalById(id);
if (!terminal) {
throw new Error(`Cannot resolve terminal with id ${id} for virtual process`);
}
@@ -395,71 +351,71 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
if (id === null) {
this._activeTerminal = undefined;
if (original !== this._activeTerminal) {
this._onDidChangeActiveTerminal.fire(this._activeTerminal);
this._onDidChangeActiveTerminal.fire(this._activeTerminal.value);
}
return;
}
const terminal = await this._getTerminalByIdEventually(id);
const terminal = this._getTerminalById(id);
if (terminal) {
this._activeTerminal = terminal;
if (original !== this._activeTerminal) {
this._onDidChangeActiveTerminal.fire(this._activeTerminal);
this._onDidChangeActiveTerminal.fire(this._activeTerminal.value);
}
}
}
public async $acceptTerminalProcessData(id: number, data: string): Promise<void> {
const terminal = await this._getTerminalByIdEventually(id);
const terminal = this._getTerminalById(id);
if (terminal) {
this._onDidWriteTerminalData.fire({ terminal, data });
this._onDidWriteTerminalData.fire({ terminal: terminal.value, data });
}
}
public async $acceptTerminalDimensions(id: number, cols: number, rows: number): Promise<void> {
const terminal = await this._getTerminalByIdEventually(id);
const terminal = this._getTerminalById(id);
if (terminal) {
if (terminal.setDimensions(cols, rows)) {
this._onDidChangeTerminalDimensions.fire({
terminal: terminal,
dimensions: terminal.dimensions as vscode.TerminalDimensions
terminal: terminal.value,
dimensions: terminal.value.dimensions as vscode.TerminalDimensions
});
}
}
}
public async $acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): Promise<void> {
await this._getTerminalByIdEventually(id);
// Extension pty terminal only - when virtual process resize fires it means that the
// terminal's maximum dimensions changed
this._terminalProcesses.get(id)?.resize(cols, rows);
}
public async $acceptTerminalTitleChange(id: number, name: string): Promise<void> {
await this._getTerminalByIdEventually(id);
const extHostTerminal = this._getTerminalObjectById(this.terminals, id);
if (extHostTerminal) {
extHostTerminal.name = name;
const terminal = this._getTerminalById(id);
if (terminal) {
terminal.name = name;
}
}
public async $acceptTerminalClosed(id: number, exitCode: number | undefined): Promise<void> {
await this._getTerminalByIdEventually(id);
const index = this._getTerminalObjectIndexById(this.terminals, id);
const index = this._getTerminalObjectIndexById(this._terminals, id);
if (index !== null) {
const terminal = this._terminals.splice(index, 1)[0];
terminal.setExitCode(exitCode);
this._onDidCloseTerminal.fire(terminal);
this._onDidCloseTerminal.fire(terminal.value);
}
}
public $acceptTerminalOpened(id: number, name: string, shellLaunchConfigDto: IShellLaunchConfigDto): void {
const index = this._getTerminalObjectIndexById(this._terminals, id);
if (index !== null) {
// The terminal has already been created (via createTerminal*), only fire the event
this._onDidOpenTerminal.fire(this.terminals[index]);
this.terminals[index].isOpen = true;
return;
public $acceptTerminalOpened(id: number, extHostTerminalId: string | undefined, name: string, shellLaunchConfigDto: IShellLaunchConfigDto): void {
if (extHostTerminalId) {
// Resolve with the renderer generated id
const index = this._getTerminalObjectIndexById(this._terminals, extHostTerminalId);
if (index !== null) {
// The terminal has already been created (via createTerminal*), only fire the event
this._terminals[index]._id = id;
this._onDidOpenTerminal.fire(this.terminals[index]);
this._terminals[index].isOpen = true;
return;
}
}
const creationOptions: vscode.TerminalOptions = {
@@ -470,14 +426,14 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
env: shellLaunchConfigDto.env,
hideFromUser: shellLaunchConfigDto.hideFromUser
};
const terminal = new ExtHostTerminal(this._proxy, creationOptions, name, id);
const terminal = new ExtHostTerminal(this._proxy, id, creationOptions, name);
this._terminals.push(terminal);
this._onDidOpenTerminal.fire(terminal);
this._onDidOpenTerminal.fire(terminal.value);
terminal.isOpen = true;
}
public async $acceptTerminalProcessId(id: number, processId: number): Promise<void> {
const terminal = await this._getTerminalByIdEventually(id);
const terminal = this._getTerminalById(id);
if (terminal) {
terminal._setProcessId(processId);
}
@@ -486,7 +442,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
public async $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise<ITerminalLaunchError | undefined> {
// Make sure the ExtHostTerminal exists so onDidOpenTerminal has fired before we call
// Pseudoterminal.start
const terminal = await this._getTerminalByIdEventually(id);
const terminal = this._getTerminalById(id);
if (!terminal) {
return { message: localize('launchFail.idMissingOnExtHost', "Could not find the terminal with id {0} on the extension host", id) };
}
@@ -496,7 +452,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
await new Promise<void>(r => {
// Ensure open is called after onDidOpenTerminal
const listener = this.onDidOpenTerminal(async e => {
if (e === terminal) {
if (e === terminal.value) {
listener.dispose();
r();
}
@@ -539,6 +495,10 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
return disposables;
}
public $acceptProcessAckDataEvent(id: number, charCount: number): void {
this._terminalProcesses.get(id)?.acknowledgeDataEvent(charCount);
}
public $acceptProcessInput(id: number, data: string): void {
this._terminalProcesses.get(id)?.input(data);
}
@@ -601,7 +561,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
this._terminalLinkCancellationSource.set(terminalId, cancellationSource);
const result: ITerminalLinkDto[] = [];
const context: vscode.TerminalLinkContext = { terminal, line };
const context: vscode.TerminalLinkContext = { terminal: terminal.value, line };
const promises: vscode.ProviderResult<{ provider: vscode.TerminalLinkProvider, links: vscode.TerminalLink[] }>[] = [];
for (const provider of this._linkProviders) {
@@ -670,32 +630,6 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
this._proxy.$sendProcessExit(id, exitCode);
}
// TODO: This could be improved by using a single promise and resolve it when the terminal is ready
private _getTerminalByIdEventually(id: number, retries: number = 5): Promise<ExtHostTerminal | undefined> {
if (!this._getTerminalPromises[id]) {
this._getTerminalPromises[id] = this._createGetTerminalPromise(id, retries);
}
return this._getTerminalPromises[id];
}
private _createGetTerminalPromise(id: number, retries: number = 5): Promise<ExtHostTerminal | undefined> {
return new Promise(c => {
if (retries === 0) {
c(undefined);
return;
}
const terminal = this._getTerminalById(id);
if (terminal) {
c(terminal);
} else {
// This should only be needed immediately after createTerminalRenderer is called as
// the ExtHostTerminal has not yet been iniitalized
timeout(EXT_HOST_CREATION_DELAY * 2).then(() => c(this._createGetTerminalPromise(id, retries - 1)));
}
});
}
private _getTerminalById(id: number): ExtHostTerminal | null {
return this._getTerminalObjectById(this._terminals, id);
}
@@ -705,7 +639,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
return index !== null ? array[index] : null;
}
private _getTerminalObjectIndexById<T extends ExtHostTerminal>(array: T[], id: number): number | null {
private _getTerminalObjectIndexById<T extends ExtHostTerminal>(array: T[], id: TerminalIdentifier): number | null {
let index: number | null = null;
array.some((item, i) => {
const thisId = item._id;

View File

@@ -6,7 +6,6 @@
import { mapFind } from 'vs/base/common/arrays';
import { disposableTimeout } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { throttle } from 'vs/base/common/decorators';
import { Emitter } from 'vs/base/common/event';
import { once } from 'vs/base/common/functional';
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
@@ -14,25 +13,35 @@ import { isDefined } from 'vs/base/common/types';
import { URI, UriComponents } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { ExtHostTestingResource, ExtHostTestingShape, MainContext, MainThreadTestingShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { TestItem } from 'vs/workbench/api/common/extHostTypeConverters';
import { Disposable, RequiredTestItem } from 'vs/workbench/api/common/extHostTypes';
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { AbstractIncrementalTestCollection, EMPTY_TEST_RESULT, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, RunTestForProviderRequest, RunTestsResult, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { OwnedTestCollection, SingleUseTestCollection } from 'vs/workbench/contrib/testing/common/ownedTestCollection';
import { AbstractIncrementalTestCollection, EMPTY_TEST_RESULT, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, InternalTestItemWithChildren, InternalTestResults, RunTestForProviderRequest, RunTestsResult, TestDiffOpType, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import type * as vscode from 'vscode';
const getTestSubscriptionKey = (resource: ExtHostTestingResource, uri: URI) => `${resource}:${uri.toString()}`;
export class ExtHostTesting implements ExtHostTestingShape {
private readonly resultsChangedEmitter = new Emitter<void>();
private readonly providers = new Map<string, vscode.TestProvider>();
private readonly proxy: MainThreadTestingShape;
private readonly ownedTests = new OwnedTestCollection();
private readonly testSubscriptions = new Map<string, { collection: SingleUseTestCollection, store: IDisposable }>();
private readonly testSubscriptions = new Map<string, {
collection: SingleUseTestCollection;
store: IDisposable;
subscribeFn: (id: string, provider: vscode.TestProvider) => void;
}>();
private workspaceObservers: WorkspaceFolderTestObserverFactory;
private textDocumentObservers: TextDocumentTestObserverFactory;
public onLastResultsChanged = this.resultsChangedEmitter.event;
public lastResults?: vscode.TestResults;
constructor(@IExtHostRpcService rpc: IExtHostRpcService, @IExtHostDocumentsAndEditors private readonly documents: IExtHostDocumentsAndEditors, @IExtHostWorkspace private readonly workspace: IExtHostWorkspace) {
this.proxy = rpc.getProxy(MainContext.MainThreadTesting);
this.workspaceObservers = new WorkspaceFolderTestObserverFactory(this.proxy);
@@ -47,6 +56,14 @@ export class ExtHostTesting implements ExtHostTestingShape {
this.providers.set(providerId, provider);
this.proxy.$registerTestProvider(providerId);
// give the ext a moment to register things rather than synchronously invoking within activate()
const toSubscribe = [...this.testSubscriptions.keys()];
setTimeout(() => {
for (const subscription of toSubscribe) {
this.testSubscriptions.get(subscription)?.subscribeFn(providerId, provider);
}
}, 0);
return new Disposable(() => {
this.providers.delete(providerId);
this.proxy.$unregisterTestProvider(providerId);
@@ -70,7 +87,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
/**
* Implements vscode.test.runTests
*/
public async runTests(req: vscode.TestRunOptions<vscode.TestItem>) {
public async runTests(req: vscode.TestRunOptions<vscode.TestItem>, token = CancellationToken.None) {
await this.proxy.$runTests({
tests: req.tests
// Find workspace items first, then owned tests, then document tests.
@@ -82,14 +99,27 @@ export class ExtHostTesting implements ExtHostTestingShape {
.filter(isDefined)
.map(item => ({ providerId: item.providerId, testId: item.id })),
debug: req.debug
});
}, token);
}
/**
* Updates test results shown to extensions.
* @override
*/
public $publishTestResults(results: InternalTestResults): void {
const convert = (item: InternalTestItemWithChildren): vscode.RequiredTestItem =>
({ ...TestItem.toShallow(item.item), children: item.children.map(convert) });
this.lastResults = { tests: results.tests.map(convert) };
this.resultsChangedEmitter.fire();
}
/**
* Handles a request to read tests for a file, or workspace.
* @override
*/
public $subscribeToTests(resource: ExtHostTestingResource, uriComponents: UriComponents) {
public async $subscribeToTests(resource: ExtHostTestingResource, uriComponents: UriComponents) {
const uri = URI.revive(uriComponents);
const subscriptionKey = getTestSubscriptionKey(resource, uri);
if (this.testSubscriptions.has(subscriptionKey)) {
@@ -98,12 +128,29 @@ export class ExtHostTesting implements ExtHostTestingShape {
let method: undefined | ((p: vscode.TestProvider) => vscode.TestHierarchy<vscode.TestItem> | undefined);
if (resource === ExtHostTestingResource.TextDocument) {
const document = this.documents.getDocument(uri);
let document = this.documents.getDocument(uri);
// we can ask to subscribe to tests before the documents are populated in
// the extension host. Try to wait.
if (!document) {
const store = new DisposableStore();
document = await new Promise<ExtHostDocumentData | undefined>(resolve => {
store.add(disposableTimeout(() => resolve(undefined), 5000));
store.add(this.documents.onDidAddDocuments(e => {
const data = e.find(data => data.document.uri.toString() === uri.toString());
if (data) { resolve(data); }
}));
}).finally(() => store.dispose());
}
if (document) {
method = p => p.createDocumentTestHierarchy?.(document.document);
const folder = await this.workspace.getWorkspaceFolder2(uri, false);
method = p => p.createDocumentTestHierarchy
? p.createDocumentTestHierarchy(document!.document)
: this.createDefaultDocumentTestHierarchy(p, document!.document, folder);
}
} else {
const folder = this.workspace.getWorkspaceFolder(uri, false);
const folder = await this.workspace.getWorkspaceFolder2(uri, false);
if (folder) {
method = p => p.createWorkspaceTestHierarchy?.(folder);
}
@@ -113,24 +160,34 @@ export class ExtHostTesting implements ExtHostTestingShape {
return;
}
const disposable = new DisposableStore();
const collection = disposable.add(this.ownedTests.createForHierarchy(diff => this.proxy.$publishDiff(resource, uriComponents, diff)));
for (const [id, provider] of this.providers) {
const subscribeFn = (id: string, provider: vscode.TestProvider) => {
try {
const hierarchy = method(provider);
const hierarchy = method!(provider);
if (!hierarchy) {
continue;
return;
}
collection.pushDiff([TestDiffOpType.DeltaDiscoverComplete, 1]);
disposable.add(hierarchy);
collection.addRoot(hierarchy.root, id);
Promise.resolve(hierarchy.discoveredInitialTests).then(() => collection.pushDiff([TestDiffOpType.DeltaDiscoverComplete, -1]));
hierarchy.onDidChangeTest(e => collection.onItemChange(e, id));
} catch (e) {
console.error(e);
}
};
const disposable = new DisposableStore();
const collection = disposable.add(this.ownedTests.createForHierarchy(diff => this.proxy.$publishDiff(resource, uriComponents, diff)));
for (const [id, provider] of this.providers) {
subscribeFn(id, provider);
}
this.testSubscriptions.set(subscriptionKey, { store: disposable, collection });
// note: we don't increment the root count initially -- this is done by the
// main thread, incrementing once per extension host. We just push the
// diff to signal that roots have been discovered.
collection.pushDiff([TestDiffOpType.DeltaRootsComplete, -1]);
this.testSubscriptions.set(subscriptionKey, { store: disposable, collection, subscribeFn });
}
/**
@@ -162,217 +219,193 @@ export class ExtHostTesting implements ExtHostTestingShape {
* providers to be run.
* @override
*/
public async $runTestsForProvider(req: RunTestForProviderRequest): Promise<RunTestsResult> {
public async $runTestsForProvider(req: RunTestForProviderRequest, cancellation: CancellationToken): Promise<RunTestsResult> {
const provider = this.providers.get(req.providerId);
if (!provider || !provider.runTests) {
return EMPTY_TEST_RESULT;
}
const tests = req.ids.map(id => this.ownedTests.getTestById(id)?.actual).filter(isDefined);
const tests = req.ids.map(id => this.ownedTests.getTestById(id)?.actual)
.filter(isDefined)
// Only send the actual TestItem's to the user to run.
.map(t => t instanceof TestItemFilteredWrapper ? t.actual : t);
if (!tests.length) {
return EMPTY_TEST_RESULT;
}
await provider.runTests({ tests, debug: req.debug }, CancellationToken.None);
return EMPTY_TEST_RESULT;
try {
await provider.runTests({ tests, debug: req.debug }, cancellation);
for (const { collection } of this.testSubscriptions.values()) {
collection.flushDiff(); // ensure all states are updated
}
return EMPTY_TEST_RESULT;
} catch (e) {
console.error(e); // so it appears to attached debuggers
throw e;
}
}
public $lookupTest(req: TestIdWithProvider): Promise<InternalTestItem | undefined> {
const owned = this.ownedTests.getTestById(req.testId);
if (!owned) {
return Promise.resolve(undefined);
}
const { actual, previousChildren, previousEquals, ...item } = owned;
return Promise.resolve(item);
}
private createDefaultDocumentTestHierarchy(provider: vscode.TestProvider, document: vscode.TextDocument, folder: vscode.WorkspaceFolder | undefined): vscode.TestHierarchy<vscode.TestItem> | undefined {
if (!folder) {
return undefined;
}
const workspaceHierarchy = provider.createWorkspaceTestHierarchy?.(folder);
if (!workspaceHierarchy) {
return undefined;
}
const onDidChangeTest = new Emitter<vscode.TestItem>();
workspaceHierarchy.onDidChangeTest(node => {
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(node, document);
const previouslySeen = wrapper.hasNodeMatchingFilter;
if (previouslySeen) {
// reset cache and get whether you can currently see the TestItem.
wrapper.reset();
const currentlySeen = wrapper.hasNodeMatchingFilter;
if (currentlySeen) {
onDidChangeTest.fire(wrapper);
return;
}
// Fire the event to say that the current visible parent has changed.
onDidChangeTest.fire(wrapper.visibleParent);
return;
}
const previousParent = wrapper.visibleParent;
wrapper.reset();
const currentlySeen = wrapper.hasNodeMatchingFilter;
// It wasn't previously seen and isn't currently seen so
// nothing has actually changed.
if (!currentlySeen) {
return;
}
// The test is now visible so we need to refresh the cache
// of the previous visible parent and fire that it has changed.
previousParent.reset();
onDidChangeTest.fire(previousParent);
});
return {
root: TestItemFilteredWrapper.getWrapperForTestItem(workspaceHierarchy.root, document),
dispose: () => {
onDidChangeTest.dispose();
TestItemFilteredWrapper.removeFilter(document);
},
onDidChangeTest: onDidChangeTest.event
};
}
}
const keyMap: { [K in keyof Omit<RequiredTestItem, 'children'>]: null } = {
label: null,
location: null,
state: null,
debuggable: null,
description: null,
runnable: null
};
const simpleProps = Object.keys(keyMap) as ReadonlyArray<keyof typeof keyMap>;
const itemEqualityComparator = (a: vscode.TestItem) => {
const values: unknown[] = [];
for (const prop of simpleProps) {
values.push(a[prop]);
}
return (b: vscode.TestItem) => {
for (let i = 0; i < simpleProps.length; i++) {
if (values[i] !== b[simpleProps[i]]) {
return false;
}
}
return true;
};
};
/**
* @private
/*
* A class which wraps a vscode.TestItem that provides the ability to filter a TestItem's children
* to only the children that are located in a certain vscode.Uri.
*/
export interface OwnedCollectionTestItem extends InternalTestItem {
actual: vscode.TestItem;
previousChildren: Set<string>;
previousEquals: (v: vscode.TestItem) => boolean;
}
/**
* @private
*/
export class OwnedTestCollection {
protected readonly testIdToInternal = new Map<string, OwnedCollectionTestItem>();
/**
* Gets test information by ID, if it was defined and still exists in this
* extension host.
*/
public getTestById(id: string) {
return this.testIdToInternal.get(id);
export class TestItemFilteredWrapper implements vscode.TestItem {
private static wrapperMap = new WeakMap<vscode.TextDocument, WeakMap<vscode.TestItem, TestItemFilteredWrapper>>();
public static removeFilter(document: vscode.TextDocument): void {
this.wrapperMap.delete(document);
}
/**
* Creates a new test collection for a specific hierarchy for a workspace
* or document observation.
*/
public createForHierarchy(publishDiff: (diff: TestsDiff) => void = () => undefined) {
return new SingleUseTestCollection(this.testIdToInternal, publishDiff);
}
}
/**
* Maintains tests created and registered for a single set of hierarchies
* for a workspace or document.
* @private
*/
export class SingleUseTestCollection implements IDisposable {
protected readonly testItemToInternal = new Map<vscode.TestItem, OwnedCollectionTestItem>();
protected diff: TestsDiff = [];
private disposed = false;
constructor(private readonly testIdToInternal: Map<string, OwnedCollectionTestItem>, private readonly publishDiff: (diff: TestsDiff) => void) { }
/**
* Adds a new root node to the collection.
*/
public addRoot(item: vscode.TestItem, providerId: string) {
this.addItem(item, providerId, null);
this.throttleSendDiff();
}
/**
* Gets test information by its reference, if it was defined and still exists
* in this extension host.
*/
public getTestByReference(item: vscode.TestItem) {
return this.testItemToInternal.get(item);
}
/**
* Should be called when an item change is fired on the test provider.
*/
public onItemChange(item: vscode.TestItem, providerId: string) {
const existing = this.testItemToInternal.get(item);
if (!existing) {
if (!this.disposed) {
console.warn(`Received a TestProvider.onDidChangeTest for a test that wasn't seen before as a child.`);
}
return;
// Wraps the TestItem specified in a TestItemFilteredWrapper and pulls from a cache if it already exists.
public static getWrapperForTestItem(item: vscode.TestItem, filterDocument: vscode.TextDocument, parent?: TestItemFilteredWrapper): TestItemFilteredWrapper {
let innerMap = this.wrapperMap.get(filterDocument);
if (innerMap?.has(item)) {
return innerMap.get(item)!;
}
this.addItem(item, providerId, existing.parent);
this.throttleSendDiff();
}
if (!innerMap) {
innerMap = new WeakMap<vscode.TestItem, TestItemFilteredWrapper>();
this.wrapperMap.set(filterDocument, innerMap);
/**
* Gets a diff of all changes that have been made, and clears the diff queue.
*/
public collectDiff() {
const diff = this.diff;
this.diff = [];
return diff;
}
public dispose() {
for (const item of this.testItemToInternal.values()) {
this.testIdToInternal.delete(item.id);
}
this.testIdToInternal.clear();
this.diff = [];
this.disposed = true;
const w = new TestItemFilteredWrapper(item, filterDocument, parent);
innerMap.set(item, w);
return w;
}
protected getId(): string {
return generateUuid();
public get label() {
return this.actual.label;
}
private addItem(actual: vscode.TestItem, providerId: string, parent: string | null) {
let internal = this.testItemToInternal.get(actual);
if (!internal) {
internal = {
actual,
id: this.getId(),
parent,
item: TestItem.from(actual),
providerId,
previousChildren: new Set(),
previousEquals: itemEqualityComparator(actual),
};
this.testItemToInternal.set(actual, internal);
this.testIdToInternal.set(internal.id, internal);
this.diff.push([TestDiffOpType.Add, { id: internal.id, parent, providerId, item: internal.item }]);
} else if (!internal.previousEquals(actual)) {
internal.item = TestItem.from(actual);
internal.previousEquals = itemEqualityComparator(actual);
this.diff.push([TestDiffOpType.Update, { id: internal.id, parent, providerId, item: internal.item }]);
}
// If there are children, track which ones are deleted
// and recursively and/update them.
if (actual.children) {
const deletedChildren = internal.previousChildren;
const currentChildren = new Set<string>();
for (const child of actual.children) {
const c = this.addItem(child, providerId, internal.id);
deletedChildren.delete(c.id);
currentChildren.add(c.id);
}
for (const child of deletedChildren) {
this.removeItembyId(child);
}
internal.previousChildren = currentChildren;
}
return internal;
public get debuggable() {
return this.actual.debuggable;
}
private removeItembyId(id: string) {
this.diff.push([TestDiffOpType.Remove, id]);
const queue = [this.testIdToInternal.get(id)];
while (queue.length) {
const item = queue.pop();
if (!item) {
continue;
}
this.testIdToInternal.delete(item.id);
this.testItemToInternal.delete(item.actual);
for (const child of item.previousChildren) {
queue.push(this.testIdToInternal.get(child));
}
}
public get description() {
return this.actual.description;
}
@throttle(200)
protected throttleSendDiff() {
const diff = this.collectDiff();
if (diff.length) {
this.publishDiff(diff);
public get location() {
return this.actual.location;
}
public get runnable() {
return this.actual.runnable;
}
public get state() {
return this.actual.state;
}
public get children() {
// We only want children that match the filter.
return this.getWrappedChildren().filter(child => child.hasNodeMatchingFilter);
}
public get visibleParent(): TestItemFilteredWrapper {
return this.hasNodeMatchingFilter ? this : this.parent!.visibleParent;
}
private matchesFilter: boolean | undefined;
// Determines if the TestItem matches the filter. This would be true if:
// 1. We don't have a parent (because the root is the workspace root node)
// 2. The URI of the current node matches the filter URI
// 3. Some child of the current node matches the filter URI
public get hasNodeMatchingFilter(): boolean {
if (this.matchesFilter === undefined) {
this.matchesFilter = !this.parent
|| this.actual.location?.uri.toString() === this.filterDocument.uri.toString()
|| this.getWrappedChildren().some(child => child.hasNodeMatchingFilter);
}
return this.matchesFilter;
}
// Reset the cache of whether or not you can see a node from a particular node
// up to it's visible parent.
public reset(): void {
if (this !== this.visibleParent) {
this.parent?.reset();
}
this.matchesFilter = undefined;
}
private constructor(public readonly actual: vscode.TestItem, private filterDocument: vscode.TextDocument, private readonly parent?: TestItemFilteredWrapper) {
this.getWrappedChildren();
}
private getWrappedChildren() {
return this.actual.children?.map(t => TestItemFilteredWrapper.getWrapperForTestItem(t, this.filterDocument, this)) || [];
}
}
@@ -382,7 +415,7 @@ export class SingleUseTestCollection implements IDisposable {
interface MirroredCollectionTestItem extends IncrementalTestCollectionItem {
revived: vscode.TestItem;
depth: number;
wrapped?: vscode.TestItem;
wrapped?: vscode.RequiredTestItem;
}
class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollectionTestItem> {
@@ -411,7 +444,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
* @override
*/
public update(node: MirroredCollectionTestItem): void {
Object.assign(node.revived, TestItem.to(node.item));
Object.assign(node.revived, TestItem.toShallow(node.item));
if (!this.added.has(node)) {
this.updated.add(node);
}
@@ -545,8 +578,8 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
/**
* Translates the item IDs to TestItems for exposure to extensions.
*/
public getAllAsTestItem(itemIds: Iterable<string>): vscode.TestItem[] {
let output: vscode.TestItem[] = [];
public getAllAsTestItem(itemIds: Iterable<string>): vscode.RequiredTestItem[] {
let output: vscode.RequiredTestItem[] = [];
for (const itemId of itemIds) {
const item = this.items.get(itemId);
if (item) {
@@ -577,7 +610,7 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
* @override
*/
protected createItem(item: InternalTestItem, parent?: MirroredCollectionTestItem): MirroredCollectionTestItem {
return { ...item, revived: TestItem.to(item.item), depth: parent ? parent.depth + 1 : 0, children: new Set() };
return { ...item, revived: TestItem.toShallow(item.item), depth: parent ? parent.depth + 1 : 0, children: new Set() };
}
/**
@@ -590,9 +623,9 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
/**
* Gets the public test item instance for the given mirrored record.
*/
public getPublicTestItem(item: MirroredCollectionTestItem): vscode.TestItem {
public getPublicTestItem(item: MirroredCollectionTestItem): vscode.RequiredTestItem {
if (!item.wrapped) {
item.wrapped = new ExtHostTestItem(item, this);
item.wrapped = new TestItemFromMirror(item, this);
}
return item.wrapped;
@@ -605,10 +638,11 @@ const getMirroredItemId = (item: vscode.TestItem) => {
const MirroredItemId = Symbol('MirroredItemId');
class ExtHostTestItem implements vscode.TestItem, RequiredTestItem {
class TestItemFromMirror implements vscode.RequiredTestItem {
readonly #internal: MirroredCollectionTestItem;
readonly #collection: MirroredTestCollection;
public get id() { return this.#internal.revived.id!; }
public get label() { return this.#internal.revived.label; }
public get description() { return this.#internal.revived.description; }
public get state() { return this.#internal.revived.state; }
@@ -627,14 +661,18 @@ class ExtHostTestItem implements vscode.TestItem, RequiredTestItem {
}
public toJSON() {
const serialized: RequiredTestItem = {
const serialized: vscode.RequiredTestItem & TestIdWithProvider = {
id: this.id,
label: this.label,
description: this.description,
state: this.state,
location: this.location,
runnable: this.runnable,
debuggable: this.debuggable,
children: this.children.map(c => (c as ExtHostTestItem).toJSON()),
children: this.children.map(c => (c as TestItemFromMirror).toJSON()),
providerId: this.#internal.providerId,
testId: this.#internal.id,
};
return serialized;
@@ -655,6 +693,7 @@ abstract class AbstractTestObserverFactory {
const resourceKey = resourceUri.toString();
const resource = this.resources.get(resourceKey) ?? this.createObserverData(resourceUri);
resource.pendingDeletion?.dispose();
resource.observers++;
return {
@@ -778,16 +817,9 @@ class TextDocumentTestObserverFactory extends AbstractTestObserverFactory {
const uriString = resourceUri.toString();
this.diffListeners.set(uriString, onDiff);
const disposeListener = this.documents.onDidRemoveDocuments(evt => {
if (evt.some(delta => delta.document.uri.toString() === uriString)) {
this.unlisten(resourceUri);
}
});
this.proxy.$subscribeToDiffs(ExtHostTestingResource.TextDocument, resourceUri);
return new Disposable(() => {
this.proxy.$unsubscribeFromDiffs(ExtHostTestingResource.TextDocument, resourceUri);
disposeListener.dispose();
this.diffListeners.delete(uriString);
});
}

View File

@@ -10,28 +10,29 @@ import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { IRange } from 'vs/editor/common/core/range';
import { ISingleEditOperation } from 'vs/editor/common/model';
import { IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, MainThreadTextEditorsShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { EndOfLine, Position, Range, Selection, SnippetString, TextEditorLineNumbersStyle, TextEditorRevealType } from 'vs/workbench/api/common/extHostTypes';
import type * as vscode from 'vscode';
import { ILogService } from 'vs/platform/log/common/log';
import { Lazy } from 'vs/base/common/lazy';
export class TextEditorDecorationType implements vscode.TextEditorDecorationType {
export class TextEditorDecorationType {
private static readonly _Keys = new IdGenerator('TextEditorDecorationType');
private _proxy: MainThreadTextEditorsShape;
public key: string;
readonly value: vscode.TextEditorDecorationType;
constructor(proxy: MainThreadTextEditorsShape, options: vscode.DecorationRenderOptions) {
this.key = TextEditorDecorationType._Keys.nextId();
this._proxy = proxy;
this._proxy.$registerTextEditorDecorationType(this.key, TypeConverters.DecorationRenderOptions.from(options));
const key = TextEditorDecorationType._Keys.nextId();
proxy.$registerTextEditorDecorationType(key, TypeConverters.DecorationRenderOptions.from(options));
this.value = Object.freeze({
key,
dispose() {
proxy.$removeTextEditorDecorationType(key);
}
});
}
public dispose(): void {
this._proxy.$removeTextEditorDecorationType(this.key);
}
}
export interface ITextEditOperation {
@@ -134,36 +135,63 @@ export class TextEditorEdit {
}
}
export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
export class ExtHostTextEditorOptions {
private _proxy: MainThreadTextEditorsShape;
private _id: string;
private _logService: ILogService;
private _tabSize!: number;
private _indentSize!: number;
private _insertSpaces!: boolean;
private _cursorStyle!: TextEditorCursorStyle;
private _lineNumbers!: TextEditorLineNumbersStyle;
readonly value: vscode.TextEditorOptions;
constructor(proxy: MainThreadTextEditorsShape, id: string, source: IResolvedTextEditorConfiguration, logService: ILogService) {
this._proxy = proxy;
this._id = id;
this._accept(source);
this._logService = logService;
const that = this;
this.value = {
get tabSize(): number | string {
return that._tabSize;
},
set tabSize(value: number | string) {
that._setTabSize(value);
},
get insertSpaces(): boolean | string {
return that._insertSpaces;
},
set insertSpaces(value: boolean | string) {
that._setInsertSpaces(value);
},
get cursorStyle(): TextEditorCursorStyle {
return that._cursorStyle;
},
set cursorStyle(value: TextEditorCursorStyle) {
that._setCursorStyle(value);
},
get lineNumbers(): TextEditorLineNumbersStyle {
return that._lineNumbers;
},
set lineNumbers(value: TextEditorLineNumbersStyle) {
that._setLineNumbers(value);
}
};
}
public _accept(source: IResolvedTextEditorConfiguration): void {
this._tabSize = source.tabSize;
this._indentSize = source.indentSize;
this._insertSpaces = source.insertSpaces;
this._cursorStyle = source.cursorStyle;
this._lineNumbers = TypeConverters.TextEditorLineNumbersStyle.to(source.lineNumbers);
}
public get tabSize(): number | string {
return this._tabSize;
}
// --- internal: tabSize
private _validateTabSize(value: number | string): number | 'auto' | null {
if (value === 'auto') {
@@ -183,7 +211,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
return null;
}
public set tabSize(value: number | string) {
private _setTabSize(value: number | string) {
const tabSize = this._validateTabSize(value);
if (tabSize === null) {
// ignore invalid call
@@ -202,50 +230,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
}));
}
public get indentSize(): number | string {
return this._indentSize;
}
private _validateIndentSize(value: number | string): number | 'tabSize' | null {
if (value === 'tabSize') {
return 'tabSize';
}
if (typeof value === 'number') {
const r = Math.floor(value);
return (r > 0 ? r : null);
}
if (typeof value === 'string') {
const r = parseInt(value, 10);
if (isNaN(r)) {
return null;
}
return (r > 0 ? r : null);
}
return null;
}
public set indentSize(value: number | string) {
const indentSize = this._validateIndentSize(value);
if (indentSize === null) {
// ignore invalid call
return;
}
if (typeof indentSize === 'number') {
if (this._indentSize === indentSize) {
// nothing to do
return;
}
// reflect the new indentSize value immediately
this._indentSize = indentSize;
}
this._warnOnError(this._proxy.$trySetOptions(this._id, {
indentSize: indentSize
}));
}
public get insertSpaces(): boolean | string {
return this._insertSpaces;
}
// --- internal: insert spaces
private _validateInsertSpaces(value: boolean | string): boolean | 'auto' {
if (value === 'auto') {
@@ -254,7 +239,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
return (value === 'false' ? false : Boolean(value));
}
public set insertSpaces(value: boolean | string) {
private _setInsertSpaces(value: boolean | string) {
const insertSpaces = this._validateInsertSpaces(value);
if (typeof insertSpaces === 'boolean') {
if (this._insertSpaces === insertSpaces) {
@@ -269,11 +254,9 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
}));
}
public get cursorStyle(): TextEditorCursorStyle {
return this._cursorStyle;
}
// --- internal: cursor style
public set cursorStyle(value: TextEditorCursorStyle) {
private _setCursorStyle(value: TextEditorCursorStyle) {
if (this._cursorStyle === value) {
// nothing to do
return;
@@ -284,11 +267,9 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
}));
}
public get lineNumbers(): TextEditorLineNumbersStyle {
return this._lineNumbers;
}
// --- internal: line number
public set lineNumbers(value: TextEditorLineNumbersStyle) {
private _setLineNumbers(value: TextEditorLineNumbersStyle) {
if (this._lineNumbers === value) {
// nothing to do
return;
@@ -368,31 +349,170 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
}
}
export class ExtHostTextEditor implements vscode.TextEditor {
private readonly _documentData: ExtHostDocumentData;
export class ExtHostTextEditor {
private _selections: Selection[];
private _options: ExtHostTextEditorOptions;
private _visibleRanges: Range[];
private _viewColumn: vscode.ViewColumn | undefined;
private _disposed: boolean = false;
private _hasDecorationsForKey: { [key: string]: boolean; };
private _hasDecorationsForKey = new Set<string>();
readonly value: vscode.TextEditor;
constructor(
readonly id: string,
private readonly _proxy: MainThreadTextEditorsShape,
private readonly _logService: ILogService,
document: ExtHostDocumentData,
document: Lazy<vscode.TextDocument>,
selections: Selection[], options: IResolvedTextEditorConfiguration,
visibleRanges: Range[], viewColumn: vscode.ViewColumn | undefined
) {
this._documentData = document;
this._selections = selections;
this._options = new ExtHostTextEditorOptions(this._proxy, this.id, options, _logService);
this._visibleRanges = visibleRanges;
this._viewColumn = viewColumn;
this._hasDecorationsForKey = Object.create(null);
const that = this;
this.value = Object.freeze({
get document(): vscode.TextDocument {
return document.getValue();
},
set document(_value) {
throw readonly('document');
},
// --- selection
get selection(): Selection {
return that._selections && that._selections[0];
},
set selection(value: Selection) {
if (!(value instanceof Selection)) {
throw illegalArgument('selection');
}
that._selections = [value];
that._trySetSelection();
},
get selections(): Selection[] {
return that._selections;
},
set selections(value: Selection[]) {
if (!Array.isArray(value) || value.some(a => !(a instanceof Selection))) {
throw illegalArgument('selections');
}
that._selections = value;
that._trySetSelection();
},
// --- visible ranges
get visibleRanges(): Range[] {
return that._visibleRanges;
},
set visibleRanges(_value: Range[]) {
throw readonly('visibleRanges');
},
// --- options
get options(): vscode.TextEditorOptions {
return that._options.value;
},
set options(value: vscode.TextEditorOptions) {
if (!that._disposed) {
that._options.assign(value);
}
},
// --- view column
get viewColumn(): vscode.ViewColumn | undefined {
return that._viewColumn;
},
set viewColumn(_value) {
throw readonly('viewColumn');
},
// --- edit
edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
if (that._disposed) {
return Promise.reject(new Error('TextEditor#edit not possible on closed editors'));
}
const edit = new TextEditorEdit(document.getValue(), options);
callback(edit);
return that._applyEdit(edit);
},
// --- snippet edit
insertSnippet(snippet: SnippetString, where?: Position | readonly Position[] | Range | readonly Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
if (that._disposed) {
return Promise.reject(new Error('TextEditor#insertSnippet not possible on closed editors'));
}
let ranges: IRange[];
if (!where || (Array.isArray(where) && where.length === 0)) {
ranges = that._selections.map(range => TypeConverters.Range.from(range));
} else if (where instanceof Position) {
const { lineNumber, column } = TypeConverters.Position.from(where);
ranges = [{ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }];
} else if (where instanceof Range) {
ranges = [TypeConverters.Range.from(where)];
} else {
ranges = [];
for (const posOrRange of where) {
if (posOrRange instanceof Range) {
ranges.push(TypeConverters.Range.from(posOrRange));
} else {
const { lineNumber, column } = TypeConverters.Position.from(posOrRange);
ranges.push({ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column });
}
}
}
return _proxy.$tryInsertSnippet(id, snippet.value, ranges, options);
},
setDecorations(decorationType: vscode.TextEditorDecorationType, ranges: Range[] | vscode.DecorationOptions[]): void {
const willBeEmpty = (ranges.length === 0);
if (willBeEmpty && !that._hasDecorationsForKey.has(decorationType.key)) {
// avoid no-op call to the renderer
return;
}
if (willBeEmpty) {
that._hasDecorationsForKey.delete(decorationType.key);
} else {
that._hasDecorationsForKey.add(decorationType.key);
}
that._runOnProxy(() => {
if (TypeConverters.isDecorationOptionsArr(ranges)) {
return _proxy.$trySetDecorations(
id,
decorationType.key,
TypeConverters.fromRangeOrRangeWithMessage(ranges)
);
} else {
const _ranges: number[] = new Array<number>(4 * ranges.length);
for (let i = 0, len = ranges.length; i < len; i++) {
const range = ranges[i];
_ranges[4 * i] = range.start.line + 1;
_ranges[4 * i + 1] = range.start.character + 1;
_ranges[4 * i + 2] = range.end.line + 1;
_ranges[4 * i + 3] = range.end.character + 1;
}
return _proxy.$trySetDecorationsFast(
id,
decorationType.key,
_ranges
);
}
});
},
revealRange(range: Range, revealType: vscode.TextEditorRevealType): void {
that._runOnProxy(() => _proxy.$tryRevealRange(
id,
TypeConverters.Range.from(range),
(revealType || TextEditorRevealType.Default)
));
},
show(column: vscode.ViewColumn) {
_proxy.$tryShowEditor(id, TypeConverters.ViewColumn.from(column));
},
hide() {
_proxy.$tryHideEditor(id);
}
});
}
dispose() {
@@ -400,164 +520,32 @@ export class ExtHostTextEditor implements vscode.TextEditor {
this._disposed = true;
}
show(column: vscode.ViewColumn) {
this._proxy.$tryShowEditor(this.id, TypeConverters.ViewColumn.from(column));
}
hide() {
this._proxy.$tryHideEditor(this.id);
}
// ---- the document
get document(): vscode.TextDocument {
return this._documentData.document;
}
set document(value) {
throw readonly('document');
}
// ---- options
get options(): vscode.TextEditorOptions {
return this._options;
}
set options(value: vscode.TextEditorOptions) {
if (!this._disposed) {
this._options.assign(value);
}
}
// --- incoming: extension host MUST accept what the renderer says
_acceptOptions(options: IResolvedTextEditorConfiguration): void {
ok(!this._disposed);
this._options._accept(options);
}
// ---- visible ranges
get visibleRanges(): Range[] {
return this._visibleRanges;
}
set visibleRanges(value: Range[]) {
throw readonly('visibleRanges');
}
_acceptVisibleRanges(value: Range[]): void {
ok(!this._disposed);
this._visibleRanges = value;
}
// ---- view column
get viewColumn(): vscode.ViewColumn | undefined {
return this._viewColumn;
}
set viewColumn(value) {
throw readonly('viewColumn');
}
_acceptViewColumn(value: vscode.ViewColumn) {
ok(!this._disposed);
this._viewColumn = value;
}
// ---- selections
get selection(): Selection {
return this._selections && this._selections[0];
}
set selection(value: Selection) {
if (!(value instanceof Selection)) {
throw illegalArgument('selection');
}
this._selections = [value];
this._trySetSelection();
}
get selections(): Selection[] {
return this._selections;
}
set selections(value: Selection[]) {
if (!Array.isArray(value) || value.some(a => !(a instanceof Selection))) {
throw illegalArgument('selections');
}
this._selections = value;
this._trySetSelection();
}
setDecorations(decorationType: vscode.TextEditorDecorationType, ranges: Range[] | vscode.DecorationOptions[]): void {
const willBeEmpty = (ranges.length === 0);
if (willBeEmpty && !this._hasDecorationsForKey[decorationType.key]) {
// avoid no-op call to the renderer
return;
}
if (willBeEmpty) {
delete this._hasDecorationsForKey[decorationType.key];
} else {
this._hasDecorationsForKey[decorationType.key] = true;
}
this._runOnProxy(
() => {
if (TypeConverters.isDecorationOptionsArr(ranges)) {
return this._proxy.$trySetDecorations(
this.id,
decorationType.key,
TypeConverters.fromRangeOrRangeWithMessage(ranges)
);
} else {
const _ranges: number[] = new Array<number>(4 * ranges.length);
for (let i = 0, len = ranges.length; i < len; i++) {
const range = ranges[i];
_ranges[4 * i] = range.start.line + 1;
_ranges[4 * i + 1] = range.start.character + 1;
_ranges[4 * i + 2] = range.end.line + 1;
_ranges[4 * i + 3] = range.end.character + 1;
}
return this._proxy.$trySetDecorationsFast(
this.id,
decorationType.key,
_ranges
);
}
}
);
}
revealRange(range: Range, revealType: vscode.TextEditorRevealType): void {
this._runOnProxy(
() => this._proxy.$tryRevealRange(
this.id,
TypeConverters.Range.from(range),
(revealType || TextEditorRevealType.Default)
)
);
}
private _trySetSelection(): Promise<vscode.TextEditor | null | undefined> {
const selection = this._selections.map(TypeConverters.Selection.from);
return this._runOnProxy(() => this._proxy.$trySetSelections(this.id, selection));
}
_acceptSelections(selections: Selection[]): void {
ok(!this._disposed);
this._selections = selections;
}
// ---- editing
edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
if (this._disposed) {
return Promise.reject(new Error('TextEditor#edit not possible on closed editors'));
}
const edit = new TextEditorEdit(this._documentData.document, options);
callback(edit);
return this._applyEdit(edit);
private async _trySetSelection(): Promise<vscode.TextEditor | null | undefined> {
const selection = this._selections.map(TypeConverters.Selection.from);
await this._runOnProxy(() => this._proxy.$trySetSelections(this.id, selection));
return this.value;
}
private _applyEdit(editBuilder: TextEditorEdit): Promise<boolean> {
@@ -613,44 +601,12 @@ export class ExtHostTextEditor implements vscode.TextEditor {
undoStopAfter: editData.undoStopAfter
});
}
insertSnippet(snippet: SnippetString, where?: Position | readonly Position[] | Range | readonly Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
if (this._disposed) {
return Promise.reject(new Error('TextEditor#insertSnippet not possible on closed editors'));
}
let ranges: IRange[];
if (!where || (Array.isArray(where) && where.length === 0)) {
ranges = this._selections.map(range => TypeConverters.Range.from(range));
} else if (where instanceof Position) {
const { lineNumber, column } = TypeConverters.Position.from(where);
ranges = [{ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }];
} else if (where instanceof Range) {
ranges = [TypeConverters.Range.from(where)];
} else {
ranges = [];
for (const posOrRange of where) {
if (posOrRange instanceof Range) {
ranges.push(TypeConverters.Range.from(posOrRange));
} else {
const { lineNumber, column } = TypeConverters.Position.from(posOrRange);
ranges.push({ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column });
}
}
}
return this._proxy.$tryInsertSnippet(this.id, snippet.value, ranges, options);
}
// ---- util
private _runOnProxy(callback: () => Promise<any>): Promise<ExtHostTextEditor | undefined | null> {
if (this._disposed) {
this._logService.warn('TextEditor is closed/disposed');
return Promise.resolve(undefined);
}
return callback().then(() => this, err => {
if (!(err instanceof Error && err.name === 'DISPOSED')) {
this._logService.warn(err);
@@ -659,4 +615,3 @@ export class ExtHostTextEditor implements vscode.TextEditor {
});
}
}

View File

@@ -41,12 +41,17 @@ export class ExtHostEditors implements ExtHostEditorsShape {
this._extHostDocumentsAndEditors.onDidChangeActiveTextEditor(e => this._onDidChangeActiveTextEditor.fire(e));
}
getActiveTextEditor(): ExtHostTextEditor | undefined {
getActiveTextEditor(): vscode.TextEditor | undefined {
return this._extHostDocumentsAndEditors.activeEditor();
}
getVisibleTextEditors(): vscode.TextEditor[] {
return this._extHostDocumentsAndEditors.allEditors();
getVisibleTextEditors(): vscode.TextEditor[];
getVisibleTextEditors(internal: true): ExtHostTextEditor[];
getVisibleTextEditors(internal?: true): ExtHostTextEditor[] | vscode.TextEditor[] {
const editors = this._extHostDocumentsAndEditors.allEditors();
return internal
? editors
: editors.map(editor => editor.value);
}
showTextDocument(document: vscode.TextDocument, column: vscode.ViewColumn, preserveFocus: boolean): Promise<vscode.TextEditor>;
@@ -75,7 +80,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
const editorId = await this._proxy.$tryShowTextDocument(document.uri, options);
const editor = editorId && this._extHostDocumentsAndEditors.getEditor(editorId);
if (editor) {
return editor;
return editor.value;
}
// we have no editor... having an id means that we had an editor
// on the main side and that it isn't the current editor anymore...
@@ -87,7 +92,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
}
createTextEditorDecorationType(options: vscode.DecorationRenderOptions): vscode.TextEditorDecorationType {
return new TextEditorDecorationType(this._proxy, options);
return new TextEditorDecorationType(this._proxy, options).value;
}
// --- called from main thread
@@ -114,7 +119,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
// (2) fire change events
if (data.options) {
this._onDidChangeTextEditorOptions.fire({
textEditor: textEditor,
textEditor: textEditor.value,
options: { ...data.options, lineNumbers: TypeConverters.TextEditorLineNumbersStyle.to(data.options.lineNumbers) }
});
}
@@ -122,7 +127,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
const kind = TextEditorSelectionChangeKind.fromValue(data.selections.source);
const selections = data.selections.selections.map(TypeConverters.Selection.to);
this._onDidChangeTextEditorSelection.fire({
textEditor,
textEditor: textEditor.value,
selections,
kind
});
@@ -130,7 +135,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
if (data.visibleRanges) {
const visibleRanges = arrays.coalesce(data.visibleRanges.map(TypeConverters.Range.to));
this._onDidChangeTextEditorVisibleRanges.fire({
textEditor,
textEditor: textEditor.value,
visibleRanges
});
}
@@ -143,9 +148,9 @@ export class ExtHostEditors implements ExtHostEditorsShape {
throw new Error('Unknown text editor');
}
const viewColumn = TypeConverters.ViewColumn.to(data[id]);
if (textEditor.viewColumn !== viewColumn) {
if (textEditor.value.viewColumn !== viewColumn) {
textEditor._acceptViewColumn(viewColumn);
this._onDidChangeTextEditorViewColumn.fire({ textEditor, viewColumn });
this._onDidChangeTextEditorViewColumn.fire({ textEditor: textEditor.value, viewColumn });
}
}
}

View File

@@ -21,6 +21,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
import { MarkdownString } from 'vs/workbench/api/common/extHostTypeConverters';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { Command } from 'vs/editor/common/modes';
// {{SQL CARBON EDIT}}
import * as azdata from 'azdata';
@@ -134,12 +135,12 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
return treeView.hasResolve;
}
$resolve(treeViewId: string, treeItemHandle: string): Promise<ITreeItem | undefined> {
$resolve(treeViewId: string, treeItemHandle: string, token: vscode.CancellationToken): Promise<ITreeItem | undefined> {
const treeView = this.treeViews.get(treeViewId);
if (!treeView) {
throw new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId));
}
return treeView.resolveTreeItem(treeItemHandle);
return treeView.resolveTreeItem(treeItemHandle, token);
}
$setExpanded(treeViewId: string, treeItemHandle: string, expanded: boolean): void {
@@ -186,6 +187,7 @@ export interface TreeNode extends IDisposable { // {{SQL CARBON EDIT}} export in
extensionItem: vscode.TreeItem;
parent: TreeNode | Root;
children?: TreeNode[];
disposableStore: DisposableStore;
}
// {{SQL CARBON EDIT}}
@@ -377,7 +379,7 @@ export class ExtHostTreeView<T> extends Disposable {
return !!this.dataProvider.resolveTreeItem;
}
async resolveTreeItem(treeItemHandle: string): Promise<ITreeItem | undefined> {
async resolveTreeItem(treeItemHandle: string, token: vscode.CancellationToken): Promise<ITreeItem | undefined> {
if (!this.dataProvider.resolveTreeItem) {
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
}
@@ -385,9 +387,10 @@ export class ExtHostTreeView<T> extends Disposable {
if (element) {
const node = this.nodes.get(element);
if (node) {
const resolve = await this.dataProvider.resolveTreeItem(node.extensionItem, element) ?? node.extensionItem;
// Resolvable elements. Currently only tooltip.
const resolve = await this.dataProvider.resolveTreeItem(node.extensionItem, element, token) ?? node.extensionItem;
// Resolvable elements. Currently only tooltip and command.
node.item.tooltip = this.getTooltip(resolve.tooltip);
node.item.command = this.getCommand(node.disposableStore, resolve.command);
return node.item;
}
}
@@ -584,8 +587,12 @@ export class ExtHostTreeView<T> extends Disposable {
return tooltip;
}
private getCommand(disposable: DisposableStore, command?: vscode.Command): Command | undefined {
return command ? this.commands.toInternal(command, disposable) : undefined;
}
protected createTreeNode(element: T, extensionTreeItem: azdata.TreeItem, parent: TreeNode | Root): TreeNode { // {{SQL CARBON EDIT}} change to protected, change to azdata.TreeItem
const disposable = new DisposableStore();
const disposableStore = new DisposableStore();
const handle = this.createHandle(element, extensionTreeItem, parent);
const icon = this.getLightIconPath(extensionTreeItem);
// {{SQL CARBON EDIT}}
@@ -596,7 +603,7 @@ export class ExtHostTreeView<T> extends Disposable {
description: extensionTreeItem.description,
resourceUri: extensionTreeItem.resourceUri,
tooltip: this.getTooltip(extensionTreeItem.tooltip),
command: extensionTreeItem.command ? this.commands.toInternal(extensionTreeItem.command, disposable) : undefined,
command: this.getCommand(disposableStore, extensionTreeItem.command),
contextValue: extensionTreeItem.contextValue,
icon,
iconDark: this.getDarkIconPath(extensionTreeItem) || icon,
@@ -613,7 +620,8 @@ export class ExtHostTreeView<T> extends Disposable {
extensionItem: extensionTreeItem,
parent,
children: undefined,
dispose(): void { disposable.dispose(); }
disposableStore,
dispose(): void { disposableStore.dispose(); }
};
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtHostTunnelServiceShape, MainContext, MainThreadTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import * as vscode from 'vscode';
import { RemoteTunnel, TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel';
@@ -11,18 +11,27 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
export interface TunnelDto {
remoteAddress: { port: number, host: string };
localAddress: { port: number, host: string } | string;
public: boolean;
}
export namespace TunnelDto {
export function fromApiTunnel(tunnel: vscode.Tunnel): TunnelDto {
return { remoteAddress: tunnel.remoteAddress, localAddress: tunnel.localAddress };
return { remoteAddress: tunnel.remoteAddress, localAddress: tunnel.localAddress, public: !!tunnel.public };
}
export function fromServiceTunnel(tunnel: RemoteTunnel): TunnelDto {
return { remoteAddress: { host: tunnel.tunnelRemoteHost, port: tunnel.tunnelRemotePort }, localAddress: tunnel.localAddress };
return {
remoteAddress: {
host: tunnel.tunnelRemoteHost,
port: tunnel.tunnelRemotePort
},
localAddress: tunnel.localAddress,
public: tunnel.public
};
}
}
@@ -44,12 +53,13 @@ export const IExtHostTunnelService = createDecorator<IExtHostTunnelService>('IEx
export class ExtHostTunnelService implements IExtHostTunnelService {
declare readonly _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 $applyCandidateFilter(candidates: CandidatePort[]): Promise<CandidatePort[]> {
return candidates;
}
async openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise<vscode.Tunnel | undefined> {
@@ -59,10 +69,10 @@ export class ExtHostTunnelService implements IExtHostTunnelService {
return [];
}
async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> {
await this._proxy.$tunnelServiceReady();
return { dispose: () => { } };
}
$forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<TunnelDto> | undefined { return undefined; }
async $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<TunnelDto | undefined> { return undefined; }
async $closeTunnel(remote: { host: string, port: number }): Promise<void> { }
async $onDidTunnelsChange(): Promise<void> { }
async $registerCandidateFinder(): Promise<void> { }
}

View File

@@ -1015,6 +1015,29 @@ export namespace SignatureHelp {
}
}
export namespace InlineHint {
export function from(hint: vscode.InlineHint): modes.InlineHint {
return {
text: hint.text,
range: Range.from(hint.range),
description: hint.description && MarkdownString.fromStrict(hint.description),
whitespaceBefore: hint.whitespaceBefore,
whitespaceAfter: hint.whitespaceAfter
};
}
export function to(hint: modes.InlineHint): vscode.InlineHint {
return new types.InlineHint(
hint.text,
Range.to(hint.range),
htmlContent.isMarkdownString(hint.description) ? MarkdownString.to(hint.description) : hint.description,
hint.whitespaceBefore,
hint.whitespaceAfter
);
}
}
export namespace DocumentLink {
export function from(link: vscode.DocumentLink): modes.ILink {
@@ -1389,19 +1412,21 @@ export namespace TestState {
export namespace TestItem {
export function from(item: vscode.TestItem): ITestItem {
export function from(item: vscode.TestItem, parentExtId?: string): ITestItem {
return {
extId: item.id ?? (parentExtId ? `${parentExtId}\0${item.label}` : item.label),
label: item.label,
location: item.location ? location.from(item.location) : undefined,
debuggable: item.debuggable,
debuggable: item.debuggable ?? false,
description: item.description,
runnable: item.runnable,
runnable: item.runnable ?? true,
state: TestState.from(item.state),
};
}
export function to(item: ITestItem): vscode.TestItem {
export function toShallow(item: ITestItem): Omit<vscode.RequiredTestItem, 'children'> {
return {
id: item.extId,
label: item.label,
location: item.location && location.to({
range: item.location.range,

View File

@@ -13,7 +13,7 @@ import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files';
import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { addIdToOutput, CellEditType, ICellEditOperation, IDisplayOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { addIdToOutput, CellEditType, ICellEditOperation, ICellOutputEdit, ITransformedDisplayOutputDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import type * as vscode from 'vscode';
function es5ClassCompat(target: Function): any {
@@ -638,7 +638,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
// --- notebook
replaceNotebookMetadata(uri: URI, value: vscode.NotebookDocumentMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
this._edits.push({ _type: FileEditType.Cell, metadata, uri, notebookMetadata: value });
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.DocumentMetadata, metadata: { ...notebookDocumentMetadataDefaults, ...value } }, notebookMetadata: value });
}
replaceNotebookCells(uri: URI, start: number, end: number, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
@@ -648,17 +648,27 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
}
replaceNotebookCellOutput(uri: URI, index: number, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
this._edits.push({
_type: FileEditType.Cell, metadata, uri, edit: {
editType: CellEditType.Output, index, outputs: outputs.map(output => {
if (NotebookCellOutput.isNotebookCellOutput(output)) {
return addIdToOutput(output.toJSON());
} else {
return addIdToOutput(output);
}
})
}
});
this._editNotebookCellOutput(uri, index, false, outputs, metadata);
}
appendNotebookCellOutput(uri: URI, index: number, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
this._editNotebookCellOutput(uri, index, true, outputs, metadata);
}
private _editNotebookCellOutput(uri: URI, index: number, append: boolean, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void {
const edit: ICellOutputEdit = {
editType: CellEditType.Output,
index,
append,
outputs: outputs.map(output => {
if (NotebookCellOutput.isNotebookCellOutput(output)) {
return output.toJSON();
} else {
return addIdToOutput(output);
}
})
};
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit });
}
replaceNotebookCellMetadata(uri: URI, index: number, cellMetadata: vscode.NotebookCellMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
@@ -1373,6 +1383,23 @@ export enum SignatureHelpTriggerKind {
ContentChange = 3,
}
@es5ClassCompat
export class InlineHint {
text: string;
range: Range;
description?: string | vscode.MarkdownString;
whitespaceBefore?: boolean;
whitespaceAfter?: boolean;
constructor(text: string, range: Range, description?: string | vscode.MarkdownString, whitespaceBefore?: boolean, whitespaceAfter?: boolean) {
this.text = text;
this.range = range;
this.description = description;
this.whitespaceBefore = whitespaceBefore;
this.whitespaceAfter = whitespaceAfter;
}
}
export enum CompletionTriggerKind {
Invoke = 0,
TriggerCharacter = 1,
@@ -2298,9 +2325,6 @@ export class FunctionBreakpoint extends Breakpoint {
constructor(functionName: string, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) {
super(enabled, condition, hitCondition, logMessage);
if (!functionName) {
throw illegalArgument('functionName');
}
this.functionName = functionName;
}
}
@@ -2802,12 +2826,11 @@ export class NotebookCellOutput {
return obj instanceof NotebookCellOutput;
}
constructor(
readonly outputs: NotebookCellOutputItem[],
readonly metadata?: Record<string, string | number | boolean>
) { }
readonly id: string = generateUuid();
toJSON(): IDisplayOutput {
constructor(readonly outputs: NotebookCellOutputItem[]) { }
toJSON(): ITransformedDisplayOutputDto {
let data: { [key: string]: unknown; } = {};
let custom: { [key: string]: unknown; } = {};
let hasMetadata = false;
@@ -2820,6 +2843,7 @@ export class NotebookCellOutput {
}
}
return {
outputId: this.id,
outputKind: CellOutputKind.Rich,
data,
metadata: hasMetadata ? { custom } : undefined
@@ -2858,7 +2882,8 @@ export enum NotebookCellStatusBarAlignment {
export enum NotebookEditorRevealType {
Default = 0,
InCenter = 1,
InCenterIfOutsideViewport = 2
InCenterIfOutsideViewport = 2,
AtTop = 3
}
@@ -2924,11 +2949,12 @@ export class LinkedEditingRanges {
//#region Testing
export enum TestRunState {
Unset = 0,
Running = 1,
Passed = 2,
Failed = 3,
Skipped = 4,
Errored = 5
Queued = 1,
Running = 2,
Passed = 3,
Failed = 4,
Skipped = 5,
Errored = 6
}
export enum TestMessageSeverity {
@@ -2963,15 +2989,15 @@ export class TestState {
}
}
type AllowedUndefined = 'description' | 'location';
/**
* Test item without any optional properties. Only some properties are
* permitted to be undefined, but they must still exist.
*/
export type RequiredTestItem = {
[K in keyof Required<vscode.TestItem>]: K extends AllowedUndefined ? vscode.TestItem[K] : Required<vscode.TestItem>[K]
};
export type RequiredTestItem = vscode.RequiredTestItem;
export type TestItem = vscode.TestItem;
//#endregion
export enum ExternalUriOpenerPriority {
None = 0,
Option = 1,
Default = 2,
Preferred = 3,
}

View File

@@ -0,0 +1,74 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from 'vs/base/common/cancellation';
import { toDisposable } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as modes from 'vs/editor/common/modes';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import type * as vscode from 'vscode';
import { ExtHostUriOpenersShape, IMainContext, MainContext, MainThreadUriOpenersShape } from './extHost.protocol';
export class ExtHostUriOpeners implements ExtHostUriOpenersShape {
private static readonly supportedSchemes = new Set<string>([Schemas.http, Schemas.https]);
private readonly _proxy: MainThreadUriOpenersShape;
private readonly _openers = new Map<string, vscode.ExternalUriOpener>();
constructor(
mainContext: IMainContext,
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadUriOpeners);
}
registerExternalUriOpener(
extensionId: ExtensionIdentifier,
id: string,
opener: vscode.ExternalUriOpener,
metadata: vscode.ExternalUriOpenerMetadata,
): vscode.Disposable {
if (this._openers.has(id)) {
throw new Error(`Opener with id '${id}' already registered`);
}
const invalidScheme = metadata.schemes.find(scheme => !ExtHostUriOpeners.supportedSchemes.has(scheme));
if (invalidScheme) {
throw new Error(`Scheme '${invalidScheme}' is not supported. Only http and https are currently supported.`);
}
this._openers.set(id, opener);
this._proxy.$registerUriOpener(id, metadata.schemes, extensionId, metadata.label);
return toDisposable(() => {
this._openers.delete(id);
this._proxy.$unregisterUriOpener(id);
});
}
async $canOpenUri(id: string, uriComponents: UriComponents, token: CancellationToken): Promise<modes.ExternalUriOpenerPriority> {
const opener = this._openers.get(id);
if (!opener) {
throw new Error(`Unknown opener with id: ${id}`);
}
const uri = URI.revive(uriComponents);
return opener.canOpenExternalUri(uri, token);
}
async $openUri(id: string, context: { resolvedUri: UriComponents, sourceUri: UriComponents }, token: CancellationToken): Promise<void> {
const opener = this._openers.get(id);
if (!opener) {
throw new Error(`Unknown opener id: '${id}'`);
}
return opener.openExternalUri(URI.revive(context.resolvedUri), {
sourceUri: URI.revive(context.sourceUri)
}, token);
}
}

View File

@@ -287,8 +287,8 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
await serializer.deserializeWebviewPanel(revivedPanel, state);
}
public createNewWebviewPanel(webviewHandle: string, viewType: string, title: string, position: number, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, webview: ExtHostWebview) {
const panel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview);
public createNewWebviewPanel(webviewHandle: string, viewType: string, title: string, position: vscode.ViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, webview: ExtHostWebview) {
const panel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, position, options, webview);
this._webviewPanels.set(webviewHandle, panel);
return panel;
}

View File

@@ -10,7 +10,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import { Schemas } from 'vs/base/common/network';
import { Counter } from 'vs/base/common/numbers';
import { basename, basenameOrAuthority, dirname, isEqual, relativePath } from 'vs/base/common/resources';
import { basename, basenameOrAuthority, dirname, ExtUri, relativePath } from 'vs/base/common/resources';
import { compare } from 'vs/base/common/strings';
import { withUndefinedAsNull } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
@@ -37,27 +37,27 @@ export interface IExtHostWorkspaceProvider {
resolveProxy(url: string): Promise<string | undefined>;
}
function isFolderEqual(folderA: URI, folderB: URI): boolean {
return isEqual(folderA, folderB);
function isFolderEqual(folderA: URI, folderB: URI, extHostFileSystemInfo: IExtHostFileSystemInfo): boolean {
return new ExtUri(uri => ignorePathCasing(uri, extHostFileSystemInfo)).isEqual(folderA, folderB);
}
function compareWorkspaceFolderByUri(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder): number {
return isFolderEqual(a.uri, b.uri) ? 0 : compare(a.uri.toString(), b.uri.toString());
function compareWorkspaceFolderByUri(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder, extHostFileSystemInfo: IExtHostFileSystemInfo): number {
return isFolderEqual(a.uri, b.uri, extHostFileSystemInfo) ? 0 : compare(a.uri.toString(), b.uri.toString());
}
function compareWorkspaceFolderByUriAndNameAndIndex(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder): number {
function compareWorkspaceFolderByUriAndNameAndIndex(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder, extHostFileSystemInfo: IExtHostFileSystemInfo): number {
if (a.index !== b.index) {
return a.index < b.index ? -1 : 1;
}
return isFolderEqual(a.uri, b.uri) ? compare(a.name, b.name) : compare(a.uri.toString(), b.uri.toString());
return isFolderEqual(a.uri, b.uri, extHostFileSystemInfo) ? compare(a.name, b.name) : compare(a.uri.toString(), b.uri.toString());
}
function delta(oldFolders: vscode.WorkspaceFolder[], newFolders: vscode.WorkspaceFolder[], compare: (a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder) => number): { removed: vscode.WorkspaceFolder[], added: vscode.WorkspaceFolder[] } {
const oldSortedFolders = oldFolders.slice(0).sort(compare);
const newSortedFolders = newFolders.slice(0).sort(compare);
function delta(oldFolders: vscode.WorkspaceFolder[], newFolders: vscode.WorkspaceFolder[], compare: (a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder, extHostFileSystemInfo: IExtHostFileSystemInfo) => number, extHostFileSystemInfo: IExtHostFileSystemInfo): { removed: vscode.WorkspaceFolder[], added: vscode.WorkspaceFolder[] } {
const oldSortedFolders = oldFolders.slice(0).sort((a, b) => compare(a, b, extHostFileSystemInfo));
const newSortedFolders = newFolders.slice(0).sort((a, b) => compare(a, b, extHostFileSystemInfo));
return arrayDelta(oldSortedFolders, newSortedFolders, compare);
return arrayDelta(oldSortedFolders, newSortedFolders, (a, b) => compare(a, b, extHostFileSystemInfo));
}
function ignorePathCasing(uri: URI, extHostFileSystemInfo: IExtHostFileSystemInfo): boolean {
@@ -87,7 +87,7 @@ class ExtHostWorkspaceImpl extends Workspace {
if (previousConfirmedWorkspace) {
folders.forEach((folderData, index) => {
const folderUri = URI.revive(folderData.uri);
const existingFolder = ExtHostWorkspaceImpl._findFolder(previousUnconfirmedWorkspace || previousConfirmedWorkspace, folderUri);
const existingFolder = ExtHostWorkspaceImpl._findFolder(previousUnconfirmedWorkspace || previousConfirmedWorkspace, folderUri, extHostFileSystemInfo);
if (existingFolder) {
existingFolder.name = folderData.name;
@@ -106,15 +106,15 @@ class ExtHostWorkspaceImpl extends Workspace {
newWorkspaceFolders.sort((f1, f2) => f1.index < f2.index ? -1 : 1);
const workspace = new ExtHostWorkspaceImpl(id, name, newWorkspaceFolders, configuration ? URI.revive(configuration) : null, !!isUntitled, uri => ignorePathCasing(uri, extHostFileSystemInfo));
const { added, removed } = delta(oldWorkspace ? oldWorkspace.workspaceFolders : [], workspace.workspaceFolders, compareWorkspaceFolderByUri);
const { added, removed } = delta(oldWorkspace ? oldWorkspace.workspaceFolders : [], workspace.workspaceFolders, compareWorkspaceFolderByUri, extHostFileSystemInfo);
return { workspace, added, removed };
}
private static _findFolder(workspace: ExtHostWorkspaceImpl, folderUriToFind: URI): MutableWorkspaceFolder | undefined {
private static _findFolder(workspace: ExtHostWorkspaceImpl, folderUriToFind: URI, extHostFileSystemInfo: IExtHostFileSystemInfo): MutableWorkspaceFolder | undefined {
for (let i = 0; i < workspace.folders.length; i++) {
const folder = workspace.workspaceFolders[i];
if (isFolderEqual(folder.uri, folderUriToFind)) {
if (isFolderEqual(folder.uri, folderUriToFind, extHostFileSystemInfo)) {
return folder;
}
}
@@ -254,7 +254,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
const validatedDistinctWorkspaceFoldersToAdd: { uri: vscode.Uri, name?: string }[] = [];
if (Array.isArray(workspaceFoldersToAdd)) {
workspaceFoldersToAdd.forEach(folderToAdd => {
if (URI.isUri(folderToAdd.uri) && !validatedDistinctWorkspaceFoldersToAdd.some(f => isFolderEqual(f.uri, folderToAdd.uri))) {
if (URI.isUri(folderToAdd.uri) && !validatedDistinctWorkspaceFoldersToAdd.some(f => isFolderEqual(f.uri, folderToAdd.uri, this._extHostFileSystemInfo))) {
validatedDistinctWorkspaceFoldersToAdd.push({ uri: folderToAdd.uri, name: folderToAdd.name || basenameOrAuthority(folderToAdd.uri) });
}
});
@@ -283,13 +283,13 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
for (let i = 0; i < newWorkspaceFolders.length; i++) {
const folder = newWorkspaceFolders[i];
if (newWorkspaceFolders.some((otherFolder, index) => index !== i && isFolderEqual(folder.uri, otherFolder.uri))) {
if (newWorkspaceFolders.some((otherFolder, index) => index !== i && isFolderEqual(folder.uri, otherFolder.uri, this._extHostFileSystemInfo))) {
return false; // cannot add the same folder multiple times
}
}
newWorkspaceFolders.forEach((f, index) => f.index = index); // fix index
const { added, removed } = delta(currentWorkspaceFolders, newWorkspaceFolders, compareWorkspaceFolderByUriAndNameAndIndex);
const { added, removed } = delta(currentWorkspaceFolders, newWorkspaceFolders, compareWorkspaceFolderByUriAndNameAndIndex, this._extHostFileSystemInfo);
if (added.length === 0 && removed.length === 0) {
return false; // nothing actually changed
}

View File

@@ -1,152 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { VSBuffer } from 'vs/base/common/buffer';
import * as platform from 'vs/base/common/platform';
export interface IFullSemanticTokensDto {
id: number;
type: 'full';
data: Uint32Array;
}
export interface IDeltaSemanticTokensDto {
id: number;
type: 'delta';
deltas: { start: number; deleteCount: number; data?: Uint32Array; }[];
}
export type ISemanticTokensDto = IFullSemanticTokensDto | IDeltaSemanticTokensDto;
const enum EncodedSemanticTokensType {
Full = 1,
Delta = 2
}
function reverseEndianness(arr: Uint8Array): void {
for (let i = 0, len = arr.length; i < len; i += 4) {
// flip bytes 0<->3 and 1<->2
const b0 = arr[i + 0];
const b1 = arr[i + 1];
const b2 = arr[i + 2];
const b3 = arr[i + 3];
arr[i + 0] = b3;
arr[i + 1] = b2;
arr[i + 2] = b1;
arr[i + 3] = b0;
}
}
function toLittleEndianBuffer(arr: Uint32Array): VSBuffer {
const uint8Arr = new Uint8Array(arr.buffer, arr.byteOffset, arr.length * 4);
if (!platform.isLittleEndian()) {
// the byte order must be changed
reverseEndianness(uint8Arr);
}
return VSBuffer.wrap(uint8Arr);
}
function fromLittleEndianBuffer(buff: VSBuffer): Uint32Array {
const uint8Arr = buff.buffer;
if (!platform.isLittleEndian()) {
// the byte order must be changed
reverseEndianness(uint8Arr);
}
if (uint8Arr.byteOffset % 4 === 0) {
return new Uint32Array(uint8Arr.buffer, uint8Arr.byteOffset, uint8Arr.length / 4);
} else {
// unaligned memory access doesn't work on all platforms
const data = new Uint8Array(uint8Arr.byteLength);
data.set(uint8Arr);
return new Uint32Array(data.buffer, data.byteOffset, data.length / 4);
}
}
export function encodeSemanticTokensDto(semanticTokens: ISemanticTokensDto): VSBuffer {
const dest = new Uint32Array(encodeSemanticTokensDtoSize(semanticTokens));
let offset = 0;
dest[offset++] = semanticTokens.id;
if (semanticTokens.type === 'full') {
dest[offset++] = EncodedSemanticTokensType.Full;
dest[offset++] = semanticTokens.data.length;
dest.set(semanticTokens.data, offset); offset += semanticTokens.data.length;
} else {
dest[offset++] = EncodedSemanticTokensType.Delta;
dest[offset++] = semanticTokens.deltas.length;
for (const delta of semanticTokens.deltas) {
dest[offset++] = delta.start;
dest[offset++] = delta.deleteCount;
if (delta.data) {
dest[offset++] = delta.data.length;
dest.set(delta.data, offset); offset += delta.data.length;
} else {
dest[offset++] = 0;
}
}
}
return toLittleEndianBuffer(dest);
}
function encodeSemanticTokensDtoSize(semanticTokens: ISemanticTokensDto): number {
let result = 0;
result += (
+ 1 // id
+ 1 // type
);
if (semanticTokens.type === 'full') {
result += (
+ 1 // data length
+ semanticTokens.data.length
);
} else {
result += (
+ 1 // delta count
);
result += (
+ 1 // start
+ 1 // deleteCount
+ 1 // data length
) * semanticTokens.deltas.length;
for (const delta of semanticTokens.deltas) {
if (delta.data) {
result += delta.data.length;
}
}
}
return result;
}
export function decodeSemanticTokensDto(_buff: VSBuffer): ISemanticTokensDto {
const src = fromLittleEndianBuffer(_buff);
let offset = 0;
const id = src[offset++];
const type: EncodedSemanticTokensType = src[offset++];
if (type === EncodedSemanticTokensType.Full) {
const length = src[offset++];
const data = src.subarray(offset, offset + length); offset += length;
return {
id: id,
type: 'full',
data: data
};
}
const deltaCount = src[offset++];
let deltas: { start: number; deleteCount: number; data?: Uint32Array; }[] = [];
for (let i = 0; i < deltaCount; i++) {
const start = src[offset++];
const deleteCount = src[offset++];
const length = src[offset++];
let data: Uint32Array | undefined;
if (length > 0) {
data = src.subarray(offset, offset + length); offset += length;
}
deltas[i] = { start, deleteCount, data };
}
return {
id: id,
type: 'delta',
deltas: deltas
};
}

View File

@@ -119,8 +119,7 @@ export function checkGlobFileExists(
const queryBuilder = instantiationService.createInstance(QueryBuilder);
const query = queryBuilder.file(folders.map(folder => toWorkspaceFolder(URI.revive(folder))), {
_reason: 'checkExists',
includePattern: includes.join(', '),
expandPatterns: true,
includePattern: includes,
exists: true
});