Update to VS Code 1.52.1
This commit is contained in:
@@ -65,6 +65,7 @@ import 'vs/server/browser/mainThreadNodeProxy';
|
||||
import './mainThreadTunnelService';
|
||||
import './mainThreadAuthentication';
|
||||
import './mainThreadTimeline';
|
||||
import './mainThreadTesting';
|
||||
import 'vs/workbench/api/common/apiCommands';
|
||||
|
||||
export class ExtensionPoints implements IWorkbenchContribution {
|
||||
|
||||
@@ -10,11 +10,10 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { IAuthenticationService, AllowedExtension, readAllowedExtensions, getAuthenticationProviderActivationEvent } from 'vs/workbench/services/authentication/browser/authenticationService';
|
||||
import { ExtHostAuthenticationShape, ExtHostContext, IExtHostContext, MainContext, MainThreadAuthenticationShape } from '../common/extHost.protocol';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { fromNow } from 'vs/base/common/date';
|
||||
import { ActivationKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
@@ -23,7 +22,7 @@ import { IEncryptionService } from 'vs/workbench/services/encryption/common/encr
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials';
|
||||
|
||||
const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git', 'ms-vsonline.vsonline', 'vscode.github-browser', 'ms-vscode.github-browser'];
|
||||
const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git', 'ms-vsonline.vsonline', 'vscode.github-browser', 'ms-vscode.github-browser', 'github.codespaces'];
|
||||
|
||||
interface IAccountUsage {
|
||||
extensionId: string;
|
||||
@@ -70,7 +69,7 @@ function addAccountUsage(storageService: IStorageService, providerId: string, ac
|
||||
});
|
||||
}
|
||||
|
||||
storageService.store(accountKey, JSON.stringify(usages), StorageScope.GLOBAL);
|
||||
storageService.store(accountKey, JSON.stringify(usages), StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
}
|
||||
|
||||
export class MainThreadAuthenticationProvider extends Disposable {
|
||||
@@ -83,7 +82,6 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
public readonly label: string,
|
||||
public readonly supportsMultipleAccounts: boolean,
|
||||
private readonly notificationService: INotificationService,
|
||||
private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
|
||||
private readonly storageService: IStorageService,
|
||||
private readonly quickInputService: IQuickInputService,
|
||||
private readonly dialogService: IDialogService
|
||||
@@ -100,9 +98,15 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
}
|
||||
|
||||
public manageTrustedExtensions(accountName: string) {
|
||||
const allowedExtensions = readAllowedExtensions(this.storageService, this.id, accountName);
|
||||
|
||||
if (!allowedExtensions.length) {
|
||||
this.dialogService.show(Severity.Info, nls.localize('noTrustedExtensions', "This account has not been used by any extensions."), []);
|
||||
return;
|
||||
}
|
||||
|
||||
const quickPick = this.quickInputService.createQuickPick<{ label: string, description: string, extension: AllowedExtension }>();
|
||||
quickPick.canSelectMany = true;
|
||||
const allowedExtensions = readAllowedExtensions(this.storageService, this.id, accountName);
|
||||
const usages = readAccountUsages(this.storageService, this.id, accountName);
|
||||
const items = allowedExtensions.map(extension => {
|
||||
const usage = usages.find(usage => extension.id === usage.extensionId);
|
||||
@@ -122,7 +126,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
|
||||
quickPick.onDidAccept(() => {
|
||||
const updatedAllowedList = quickPick.selectedItems.map(item => item.extension);
|
||||
this.storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL);
|
||||
this.storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL, StorageTarget.USER);
|
||||
|
||||
quickPick.dispose();
|
||||
});
|
||||
@@ -153,8 +157,6 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
} else {
|
||||
this._accounts.set(session.account.label, [session.id]);
|
||||
}
|
||||
|
||||
this.storageKeysSyncRegistryService.registerStorageKey({ key: `${this.id}-${session.account.label}`, version: 1 });
|
||||
}
|
||||
|
||||
async signOut(accountName: string): Promise<void> {
|
||||
@@ -171,6 +173,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
if (result.confirmed) {
|
||||
sessionsForAccount?.forEach(sessionId => this.logout(sessionId));
|
||||
removeAccountUsage(this.storageService, this.id, accountName);
|
||||
this.storageService.remove(`${this.id}-${accountName}`, StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +223,6 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
|
||||
@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IExtensionService private readonly extensionService: IExtensionService,
|
||||
@@ -259,7 +261,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
}
|
||||
|
||||
async $registerAuthenticationProvider(id: string, label: string, supportsMultipleAccounts: boolean): Promise<void> {
|
||||
const provider = new MainThreadAuthenticationProvider(this._proxy, id, label, supportsMultipleAccounts, this.notificationService, this.storageKeysSyncRegistryService, this.storageService, this.quickInputService, this.dialogService);
|
||||
const provider = new MainThreadAuthenticationProvider(this._proxy, id, label, supportsMultipleAccounts, this.notificationService, this.storageService, this.quickInputService, this.dialogService);
|
||||
await provider.initialize();
|
||||
this.authenticationService.registerAuthenticationProvider(id, provider);
|
||||
}
|
||||
@@ -383,10 +385,10 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
const allowList = readAllowedExtensions(this.storageService, providerId, accountName);
|
||||
if (!allowList.find(allowed => allowed.id === extensionId)) {
|
||||
allowList.push({ id: extensionId, name: extensionName });
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER);
|
||||
}
|
||||
|
||||
this.storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL);
|
||||
this.storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
|
||||
quickPick.dispose();
|
||||
resolve(session);
|
||||
@@ -435,7 +437,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
if (allow) {
|
||||
addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName);
|
||||
allowList.push({ id: extensionId, name: extensionName });
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER);
|
||||
}
|
||||
|
||||
return allow;
|
||||
@@ -458,10 +460,11 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
const allowList = readAllowedExtensions(this.storageService, providerId, accountName);
|
||||
if (!allowList.find(allowed => allowed.id === extensionId)) {
|
||||
allowList.push({ id: extensionId, name: extensionName });
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER);
|
||||
}
|
||||
|
||||
this.storageService.store(`${extensionName}-${providerId}`, sessionId, StorageScope.GLOBAL);
|
||||
this.storageService.store(`${extensionName}-${providerId}`, sessionId, StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName);
|
||||
}
|
||||
|
||||
private getFullKey(extensionId: string): string {
|
||||
|
||||
@@ -37,8 +37,14 @@ export class MainThreadBulkEdits implements MainThreadBulkEditsShape {
|
||||
|
||||
dispose(): void { }
|
||||
|
||||
$tryApplyWorkspaceEdit(dto: IWorkspaceEditDto): Promise<boolean> {
|
||||
$tryApplyWorkspaceEdit(dto: IWorkspaceEditDto, undoRedoGroupId?: number): Promise<boolean> {
|
||||
const edits = reviveWorkspaceEditDto2(dto);
|
||||
return this._bulkEditService.apply(edits).then(() => true, _err => false);
|
||||
return this._bulkEditService.apply(edits, {
|
||||
// having a undoRedoGroupId means that this is a nested workspace edit,
|
||||
// e.g one from a onWill-handler and for now we need to forcefully suppress
|
||||
// refactor previewing, see: https://github.com/microsoft/vscode/issues/111873#issuecomment-738739852
|
||||
undoRedoGroupId,
|
||||
suppressPreview: typeof undoRedoGroupId === 'number' ? true : undefined
|
||||
}).then(() => true, _err => false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
|
||||
// todo@joh move these things back into something like contrib/insets
|
||||
// todo@jrieken move these things back into something like contrib/insets
|
||||
class EditorWebviewZone implements IViewZone {
|
||||
|
||||
readonly domNode: HTMLElement;
|
||||
@@ -73,7 +73,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
|
||||
async $createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void> {
|
||||
|
||||
let editor: IActiveCodeEditor | undefined;
|
||||
id = id.substr(0, id.indexOf(',')); //todo@joh HACK
|
||||
id = id.substr(0, id.indexOf(',')); //todo@jrieken HACK
|
||||
|
||||
for (const candidate of this._editorService.listCodeEditors()) {
|
||||
if (candidate.getId() === id && candidate.hasModel() && isEqual(candidate.getModel().uri, URI.revive(uri))) {
|
||||
|
||||
@@ -34,23 +34,23 @@ export class MainThreadCommands implements MainThreadCommandsShape {
|
||||
this._generateCommandsDocumentationRegistration.dispose();
|
||||
}
|
||||
|
||||
private _generateCommandsDocumentation(): Promise<void> {
|
||||
return this._proxy.$getContributedCommandHandlerDescriptions().then(result => {
|
||||
// add local commands
|
||||
const commands = CommandsRegistry.getCommands();
|
||||
for (const [id, command] of commands) {
|
||||
if (command.description) {
|
||||
result[id] = command.description;
|
||||
}
|
||||
}
|
||||
private async _generateCommandsDocumentation(): Promise<void> {
|
||||
const result = await this._proxy.$getContributedCommandHandlerDescriptions();
|
||||
|
||||
// print all as markdown
|
||||
const all: string[] = [];
|
||||
for (let id in result) {
|
||||
all.push('`' + id + '` - ' + _generateMarkdown(result[id]));
|
||||
// add local commands
|
||||
const commands = CommandsRegistry.getCommands();
|
||||
for (const [id, command] of commands) {
|
||||
if (command.description) {
|
||||
result[id] = command.description;
|
||||
}
|
||||
console.log(all.join('\n'));
|
||||
});
|
||||
}
|
||||
|
||||
// print all as markdown
|
||||
const all: string[] = [];
|
||||
for (let id in result) {
|
||||
all.push('`' + id + '` - ' + _generateMarkdown(result[id]));
|
||||
}
|
||||
console.log(all.join('\n'));
|
||||
}
|
||||
|
||||
$registerCommand(id: string): void {
|
||||
|
||||
@@ -21,6 +21,8 @@ import { ViewContainer, IViewContainersRegistry, Extensions as ViewExtensions, V
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
|
||||
export class MainThreadCommentThread implements modes.CommentThread {
|
||||
@@ -351,6 +353,9 @@ export class MainThreadCommentController {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const commentsViewIcon = registerIcon('comments-view-icon', Codicon.commentDiscussion, localize('commentsViewIcon', 'View icon of the comments view.'));
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadComments)
|
||||
export class MainThreadComments extends Disposable implements MainThreadCommentsShape {
|
||||
private readonly _proxy: ExtHostCommentsShape;
|
||||
@@ -472,7 +477,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
|
||||
ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]),
|
||||
storageId: COMMENTS_VIEW_TITLE,
|
||||
hideIfEmpty: true,
|
||||
icon: Codicon.commentDiscussion.classNames,
|
||||
icon: commentsViewIcon,
|
||||
order: 10,
|
||||
}, ViewContainerLocation.Panel);
|
||||
|
||||
@@ -482,7 +487,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
|
||||
canToggleVisibility: false,
|
||||
ctorDescriptor: new SyncDescriptor(CommentsPanel),
|
||||
canMoveView: true,
|
||||
containerIcon: Codicon.commentDiscussion.classNames,
|
||||
containerIcon: commentsViewIcon,
|
||||
focusCommand: {
|
||||
id: 'workbench.action.focusCommentsPanel'
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@ import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/comm
|
||||
import { MainThreadWebviewPanels } from 'vs/workbench/api/browser/mainThreadWebviewPanels';
|
||||
import { MainThreadWebviews, reviveWebviewExtension } from 'vs/workbench/api/browser/mainThreadWebviews';
|
||||
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
import { IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor';
|
||||
import { editorGroupToViewColumn, IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor';
|
||||
import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
|
||||
import { CustomDocumentBackupData } from 'vs/workbench/contrib/customEditor/browser/customEditorInputFactory';
|
||||
import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor';
|
||||
@@ -604,6 +603,10 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
this._proxy.$backup(this._editorResource.toJSON(), this.viewType, token)));
|
||||
this._hotExitState = pendingState;
|
||||
|
||||
token.onCancellationRequested(() => {
|
||||
pendingState.operation.cancel();
|
||||
});
|
||||
|
||||
try {
|
||||
const backupId = await pendingState.operation;
|
||||
// Make sure state has not changed in the meantime
|
||||
|
||||
@@ -82,7 +82,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
// RPC methods (MainThreadDebugServiceShape)
|
||||
|
||||
public $registerDebugTypes(debugTypes: string[]) {
|
||||
this._toDispose.add(this.debugService.getConfigurationManager().registerDebugAdapterFactory(debugTypes, this));
|
||||
this._toDispose.add(this.debugService.getAdapterManager().registerDebugAdapterFactory(debugTypes, this));
|
||||
}
|
||||
|
||||
public $startBreakpointEvents(): void {
|
||||
@@ -155,7 +155,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public $registerDebugConfigurationProvider(debugType: string, providerTriggerKind: DebugConfigurationProviderTriggerKind, hasProvide: boolean, hasResolve: boolean, hasResolve2: boolean, hasProvideDebugAdapter: boolean, handle: number): Promise<void> {
|
||||
public $registerDebugConfigurationProvider(debugType: string, providerTriggerKind: DebugConfigurationProviderTriggerKind, hasProvide: boolean, hasResolve: boolean, hasResolve2: boolean, handle: number): Promise<void> {
|
||||
|
||||
const provider = <IDebugConfigurationProvider>{
|
||||
type: debugType,
|
||||
@@ -176,12 +176,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
return this._proxy.$resolveDebugConfigurationWithSubstitutedVariables(handle, folder, config, token);
|
||||
};
|
||||
}
|
||||
if (hasProvideDebugAdapter) {
|
||||
console.info('DebugConfigurationProvider.debugAdapterExecutable is deprecated and will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.');
|
||||
provider.debugAdapterExecutable = (folder) => {
|
||||
return this._proxy.$legacyDebugAdapterExecutable(handle, folder);
|
||||
};
|
||||
}
|
||||
this._debugConfigurationProviders.set(handle, provider);
|
||||
this._toDispose.add(this.debugService.getConfigurationManager().registerDebugConfigurationProvider(provider));
|
||||
|
||||
@@ -205,7 +199,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
}
|
||||
};
|
||||
this._debugAdapterDescriptorFactories.set(handle, provider);
|
||||
this._toDispose.add(this.debugService.getConfigurationManager().registerDebugAdapterDescriptorFactory(provider));
|
||||
this._toDispose.add(this.debugService.getAdapterManager().registerDebugAdapterDescriptorFactory(provider));
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
@@ -214,7 +208,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
const provider = this._debugAdapterDescriptorFactories.get(handle);
|
||||
if (provider) {
|
||||
this._debugAdapterDescriptorFactories.delete(handle);
|
||||
this.debugService.getConfigurationManager().unregisterDebugAdapterDescriptorFactory(provider);
|
||||
this.debugService.getAdapterManager().unregisterDebugAdapterDescriptorFactory(provider);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,12 +23,20 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape {
|
||||
//
|
||||
}
|
||||
|
||||
$showOpenDialog(options?: MainThreadDialogOpenOptions): Promise<URI[] | undefined> {
|
||||
return Promise.resolve(this._fileDialogService.showOpenDialog(MainThreadDialogs._convertOpenOptions(options)));
|
||||
async $showOpenDialog(options?: MainThreadDialogOpenOptions): Promise<URI[] | undefined> {
|
||||
const convertedOptions = MainThreadDialogs._convertOpenOptions(options);
|
||||
if (!convertedOptions.defaultUri) {
|
||||
convertedOptions.defaultUri = await this._fileDialogService.defaultFilePath();
|
||||
}
|
||||
return Promise.resolve(this._fileDialogService.showOpenDialog(convertedOptions));
|
||||
}
|
||||
|
||||
$showSaveDialog(options?: MainThreadDialogSaveOptions): Promise<URI | undefined> {
|
||||
return Promise.resolve(this._fileDialogService.showSaveDialog(MainThreadDialogs._convertSaveOptions(options)));
|
||||
async $showSaveDialog(options?: MainThreadDialogSaveOptions): Promise<URI | undefined> {
|
||||
const convertedOptions = MainThreadDialogs._convertSaveOptions(options);
|
||||
if (!convertedOptions.defaultUri) {
|
||||
convertedOptions.defaultUri = await this._fileDialogService.defaultFilePath();
|
||||
}
|
||||
return Promise.resolve(this._fileDialogService.showSaveDialog(convertedOptions));
|
||||
}
|
||||
|
||||
private static _convertOpenOptions(options?: MainThreadDialogOpenOptions): IOpenDialogOptions {
|
||||
@@ -38,7 +46,8 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape {
|
||||
canSelectFolders: options?.canSelectFolders,
|
||||
canSelectMany: options?.canSelectMany,
|
||||
defaultUri: options?.defaultUri ? URI.revive(options.defaultUri) : undefined,
|
||||
title: options?.title || undefined
|
||||
title: options?.title || undefined,
|
||||
availableFileSystems: []
|
||||
};
|
||||
if (options?.filters) {
|
||||
result.filters = [];
|
||||
|
||||
@@ -19,9 +19,8 @@ import { MainThreadDocuments } from 'vs/workbench/api/browser/mainThreadDocument
|
||||
import { MainThreadTextEditor } from 'vs/workbench/api/browser/mainThreadEditor';
|
||||
import { MainThreadTextEditors } from 'vs/workbench/api/browser/mainThreadEditors';
|
||||
import { ExtHostContext, ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, IExtHostContext, IModelAddedData, ITextEditorAddData, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { EditorViewColumn, editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
|
||||
import { IEditorPane } from 'vs/workbench/common/editor';
|
||||
import { editorGroupToViewColumn, EditorGroupColumn, IEditorPane } from 'vs/workbench/common/editor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
@@ -31,41 +30,8 @@ import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/commo
|
||||
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
import { diffSets, diffMaps } from 'vs/base/common/collections';
|
||||
|
||||
namespace delta {
|
||||
|
||||
export function ofSets<T>(before: Set<T>, after: Set<T>): { removed: T[], added: T[] } {
|
||||
const removed: T[] = [];
|
||||
const added: T[] = [];
|
||||
for (let element of before) {
|
||||
if (!after.has(element)) {
|
||||
removed.push(element);
|
||||
}
|
||||
}
|
||||
for (let element of after) {
|
||||
if (!before.has(element)) {
|
||||
added.push(element);
|
||||
}
|
||||
}
|
||||
return { removed, added };
|
||||
}
|
||||
|
||||
export function ofMaps<K, V>(before: Map<K, V>, after: Map<K, V>): { removed: V[], added: V[] } {
|
||||
const removed: V[] = [];
|
||||
const added: V[] = [];
|
||||
for (let [index, value] of before) {
|
||||
if (!after.has(index)) {
|
||||
removed.push(value);
|
||||
}
|
||||
}
|
||||
for (let [index, value] of after) {
|
||||
if (!before.has(index)) {
|
||||
added.push(value);
|
||||
}
|
||||
}
|
||||
return { removed, added };
|
||||
}
|
||||
}
|
||||
|
||||
class TextEditorSnapshot {
|
||||
|
||||
@@ -118,8 +84,8 @@ class DocumentAndEditorState {
|
||||
undefined, after.activeEditor
|
||||
);
|
||||
}
|
||||
const documentDelta = delta.ofSets(before.documents, after.documents);
|
||||
const editorDelta = delta.ofMaps(before.textEditors, after.textEditors);
|
||||
const documentDelta = diffSets(before.documents, after.documents);
|
||||
const editorDelta = diffMaps(before.textEditors, after.textEditors);
|
||||
const oldActiveEditor = before.activeEditor !== after.activeEditor ? before.activeEditor : undefined;
|
||||
const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined;
|
||||
|
||||
@@ -443,7 +409,7 @@ export class MainThreadDocumentsAndEditors {
|
||||
};
|
||||
}
|
||||
|
||||
private _findEditorPosition(editor: MainThreadTextEditor): EditorViewColumn | undefined {
|
||||
private _findEditorPosition(editor: MainThreadTextEditor): EditorGroupColumn | undefined {
|
||||
for (const editorPane of this._editorService.visibleEditorPanes) {
|
||||
if (editor.matches(editorPane)) {
|
||||
return editorGroupToViewColumn(this._editorGroupService, editorPane.group);
|
||||
|
||||
@@ -14,18 +14,14 @@ import { ISelection } from 'vs/editor/common/core/selection';
|
||||
import { IDecorationOptions, IDecorationRenderOptions, ILineChange } from 'vs/editor/common/editorCommon';
|
||||
import { ISingleEditOperation } from 'vs/editor/common/model';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IEditorOptions, ITextEditorOptions, IResourceEditorInput, EditorActivation } from 'vs/platform/editor/common/editor';
|
||||
import { ITextEditorOptions, IResourceEditorInput, EditorActivation } from 'vs/platform/editor/common/editor';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors';
|
||||
import { MainThreadTextEditor } from 'vs/workbench/api/browser/mainThreadEditor';
|
||||
import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContext, ITextDocumentShowOptions, ITextEditorConfigurationUpdate, ITextEditorPositionData, IUndoStopOptions, MainThreadTextEditorsShape, TextEditorRevealType, IWorkspaceEditDto, WorkspaceEditType } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
|
||||
import { editorGroupToViewColumn, EditorGroupColumn, viewColumnToEditorGroup } from 'vs/workbench/common/editor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
@@ -161,7 +157,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
|
||||
return this._documentsAndEditors.findTextEditorIdFor(editor);
|
||||
}
|
||||
|
||||
async $tryShowEditor(id: string, position?: EditorViewColumn): Promise<void> {
|
||||
async $tryShowEditor(id: string, position?: EditorGroupColumn): Promise<void> {
|
||||
const mainThreadEditor = this._documentsAndEditors.getEditor(id);
|
||||
if (mainThreadEditor) {
|
||||
const model = mainThreadEditor.getModel();
|
||||
@@ -297,59 +293,6 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
|
||||
|
||||
// --- commands
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.open', async function (accessor: ServicesAccessor, args: [URI, IEditorOptions, EditorViewColumn, string?]) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const openerService = accessor.get(IOpenerService);
|
||||
|
||||
const [resource, options, position, label] = args;
|
||||
|
||||
if (options || typeof position === 'number') {
|
||||
// use editor options or editor view column as a hint to use the editor service for opening
|
||||
await editorService.openEditor({ resource, options, label }, viewColumnToEditorGroup(editorGroupService, position));
|
||||
return;
|
||||
}
|
||||
|
||||
if (resource && resource.scheme === 'command') {
|
||||
// do not allow to execute commands from here
|
||||
return;
|
||||
|
||||
}
|
||||
// finally, delegate to opener service
|
||||
await openerService.open(resource);
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAccessor, args: [URI, string, ITextEditorOptions | undefined, EditorViewColumn | undefined]) => {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorGroupsService = accessor.get(IEditorGroupsService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
|
||||
const [resource, id, options, position] = args;
|
||||
|
||||
const group = editorGroupsService.getGroup(viewColumnToEditorGroup(editorGroupsService, position)) ?? editorGroupsService.activeGroup;
|
||||
const textOptions: ITextEditorOptions = options ? { ...options, override: false } : { override: false };
|
||||
|
||||
const input = editorService.createEditorInput({ resource });
|
||||
return openEditorWith(input, id, textOptions, group, editorService, configurationService, quickInputService);
|
||||
});
|
||||
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.diff', async function (accessor: ServicesAccessor, args: [URI, URI, string, string, IEditorOptions, EditorViewColumn]) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
|
||||
let [leftResource, rightResource, label, description, options, position] = args;
|
||||
|
||||
if (!options || typeof options !== 'object') {
|
||||
options = {
|
||||
preserveFocus: false
|
||||
};
|
||||
}
|
||||
|
||||
await editorService.openEditor({ leftResource, rightResource, label, description, options }, viewColumnToEditorGroup(editorGroupService, position));
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.revertAllDirty', async function (accessor: ServicesAccessor) {
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
if (!environmentService.extensionTestsLocationURI) {
|
||||
|
||||
@@ -54,11 +54,13 @@ export class MainThreadFileSystemEventService {
|
||||
|
||||
|
||||
// BEFORE file operation
|
||||
workingCopyFileService.addFileOperationParticipant({
|
||||
participate: (files, operation, progress, timeout, token) => {
|
||||
return proxy.$onWillRunFileOperation(operation, files, timeout, token);
|
||||
this._listener.add(workingCopyFileService.addFileOperationParticipant({
|
||||
participate: async (files, operation, undoRedoGroupId, isUndoing, _progress, timeout, token) => {
|
||||
if (!isUndoing) {
|
||||
return proxy.$onWillRunFileOperation(operation, files, undoRedoGroupId, timeout, token);
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
// AFTER file operation
|
||||
this._listener.add(workingCopyFileService.onDidRunWorkingCopyFileOperation(e => proxy.$onDidRunFileOperation(e.operation, e.files)));
|
||||
@@ -75,7 +77,7 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
|
||||
properties: {
|
||||
'files.participants.timeout': {
|
||||
type: 'number',
|
||||
default: 5000,
|
||||
default: 60000,
|
||||
markdownDescription: localize('files.participants.timeout', "Timeout in milliseconds after which file participants for create, rename, and delete are cancelled. Use `0` to disable participants."),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,16 +165,15 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
$registerCodeLensSupport(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void {
|
||||
|
||||
const provider = <modes.CodeLensProvider>{
|
||||
provideCodeLenses: (model: ITextModel, token: CancellationToken): Promise<modes.CodeLensList | undefined> => {
|
||||
return this._proxy.$provideCodeLenses(handle, model.uri, token).then(listDto => {
|
||||
if (!listDto) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
lenses: listDto.lenses,
|
||||
dispose: () => listDto.cacheId && this._proxy.$releaseCodeLenses(handle, listDto.cacheId)
|
||||
};
|
||||
});
|
||||
provideCodeLenses: async (model: ITextModel, token: CancellationToken): Promise<modes.CodeLensList | undefined> => {
|
||||
const listDto = await this._proxy.$provideCodeLenses(handle, model.uri, token);
|
||||
if (!listDto) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
lenses: listDto.lenses,
|
||||
dispose: () => listDto.cacheId && this._proxy.$releaseCodeLenses(handle, listDto.cacheId)
|
||||
};
|
||||
},
|
||||
resolveCodeLens: (_model: ITextModel, codeLens: modes.CodeLens, token: CancellationToken): Promise<modes.CodeLens | undefined> => {
|
||||
return this._proxy.$resolveCodeLens(handle, codeLens, token);
|
||||
@@ -261,14 +260,12 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
}));
|
||||
}
|
||||
|
||||
// --- on type rename
|
||||
// --- linked editing
|
||||
|
||||
$registerOnTypeRenameProvider(handle: number, selector: IDocumentFilterDto[], wordPattern?: IRegExpDto): void {
|
||||
const revivedWordPattern = wordPattern ? MainThreadLanguageFeatures._reviveRegExp(wordPattern) : undefined;
|
||||
this._registrations.set(handle, modes.OnTypeRenameProviderRegistry.register(selector, <modes.OnTypeRenameProvider>{
|
||||
wordPattern: revivedWordPattern,
|
||||
provideOnTypeRenameRanges: async (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: RegExp; } | undefined> => {
|
||||
const res = await this._proxy.$provideOnTypeRenameRanges(handle, model.uri, position, token);
|
||||
$registerLinkedEditingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void {
|
||||
this._registrations.set(handle, modes.LinkedEditingRangeProviderRegistry.register(selector, <modes.LinkedEditingRangeProvider>{
|
||||
provideLinkedEditingRanges: async (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<modes.LinkedEditingRanges | undefined> => {
|
||||
const res = await this._proxy.$provideLinkedEditingRanges(handle, model.uri, position, token);
|
||||
if (res) {
|
||||
return {
|
||||
ranges: res.ranges,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { diffMaps, diffSets } from 'vs/base/common/collections';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable, IReference } from 'vs/base/common/lifecycle';
|
||||
@@ -14,8 +15,11 @@ import { IExtUri } from 'vs/base/common/resources';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { EditorActivation, ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { viewColumnToEditorGroup } from 'vs/workbench/common/editor';
|
||||
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
@@ -23,44 +27,14 @@ import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/com
|
||||
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookEditorModel, INotebookExclusiveDocumentFilter, NotebookCellOutputsSplice, NotebookCellsChangeType, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
|
||||
import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroup, IEditorGroupsService, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith';
|
||||
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol';
|
||||
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol';
|
||||
|
||||
class DocumentAndEditorState {
|
||||
static ofSets<T>(before: Set<T>, after: Set<T>): { removed: T[], added: T[] } {
|
||||
const removed: T[] = [];
|
||||
const added: T[] = [];
|
||||
before.forEach(element => {
|
||||
if (!after.has(element)) {
|
||||
removed.push(element);
|
||||
}
|
||||
});
|
||||
after.forEach(element => {
|
||||
if (!before.has(element)) {
|
||||
added.push(element);
|
||||
}
|
||||
});
|
||||
return { removed, added };
|
||||
}
|
||||
|
||||
static ofMaps<K, V>(before: Map<K, V>, after: Map<K, V>): { removed: V[], added: V[] } {
|
||||
const removed: V[] = [];
|
||||
const added: V[] = [];
|
||||
before.forEach((value, index) => {
|
||||
if (!after.has(index)) {
|
||||
removed.push(value);
|
||||
}
|
||||
});
|
||||
after.forEach((value, index) => {
|
||||
if (!before.has(index)) {
|
||||
added.push(value);
|
||||
}
|
||||
});
|
||||
return { removed, added };
|
||||
}
|
||||
|
||||
static compute(before: DocumentAndEditorState | undefined, after: DocumentAndEditorState): INotebookDocumentsAndEditorsDelta {
|
||||
if (!before) {
|
||||
const apiEditors = [];
|
||||
@@ -75,8 +49,8 @@ class DocumentAndEditorState {
|
||||
visibleEditors: [...after.visibleEditors].map(editor => editor[0])
|
||||
};
|
||||
}
|
||||
const documentDelta = DocumentAndEditorState.ofSets(before.documents, after.documents);
|
||||
const editorDelta = DocumentAndEditorState.ofMaps(before.textEditors, after.textEditors);
|
||||
const documentDelta = diffSets(before.documents, after.documents);
|
||||
const editorDelta = diffMaps(before.textEditors, after.textEditors);
|
||||
const addedAPIEditors = editorDelta.added.map(add => ({
|
||||
id: add.getId(),
|
||||
documentUri: add.uri!,
|
||||
@@ -89,7 +63,7 @@ class DocumentAndEditorState {
|
||||
// const oldActiveEditor = before.activeEditor !== after.activeEditor ? before.activeEditor : undefined;
|
||||
const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined;
|
||||
|
||||
const visibleEditorDelta = DocumentAndEditorState.ofMaps(before.visibleEditors, after.visibleEditors);
|
||||
const visibleEditorDelta = diffMaps(before.visibleEditors, after.visibleEditors);
|
||||
|
||||
return {
|
||||
addedDocuments: documentDelta.added.map((e: NotebookTextModel): INotebookModelAddedData => {
|
||||
@@ -152,7 +126,10 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
@INotebookService private _notebookService: INotebookService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService,
|
||||
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@INotebookCellStatusBarService private readonly cellStatusBarService: INotebookCellStatusBarService,
|
||||
@IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService,
|
||||
@@ -171,7 +148,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
if (!textModel) {
|
||||
return false;
|
||||
}
|
||||
this._notebookService.transformEditsOutputs(textModel, cellEdits);
|
||||
return textModel.applyEdits(modelVersionId, cellEdits, true, undefined, () => undefined, undefined);
|
||||
}
|
||||
|
||||
@@ -413,9 +389,9 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
|
||||
const editors = new Map<string, IEditor>();
|
||||
this._notebookService.listNotebookEditors().forEach(editor => {
|
||||
if (editor.hasModel()) {
|
||||
if (editor.textModel) {
|
||||
editors.set(editor.getId(), editor);
|
||||
documentEditorsMap.set(editor.textModel!.uri.toString(), editor);
|
||||
documentEditorsMap.set(editor.textModel.uri.toString(), editor);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -434,7 +410,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
documents.add(document);
|
||||
});
|
||||
|
||||
if (!activeEditor && focusedNotebookEditor && focusedNotebookEditor.hasModel()) {
|
||||
if (!activeEditor && focusedNotebookEditor && focusedNotebookEditor.textModel) {
|
||||
activeEditor = focusedNotebookEditor.getId();
|
||||
}
|
||||
|
||||
@@ -479,8 +455,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
const edits: ICellEditOperation[] = [
|
||||
{ editType: CellEditType.Replace, index: 0, count: mainthreadTextModel.cells.length, cells: data.cells }
|
||||
];
|
||||
|
||||
this._notebookService.transformEditsOutputs(mainthreadTextModel, edits);
|
||||
await new Promise(resolve => {
|
||||
DOM.scheduleAtNextAnimationFrame(() => {
|
||||
const ret = mainthreadTextModel!.applyEdits(mainthreadTextModel!.versionId, edits, true, undefined, () => undefined, undefined);
|
||||
@@ -606,7 +580,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
return;
|
||||
}
|
||||
|
||||
this._notebookService.transformSpliceOutputs(textModel, splices);
|
||||
const cell = textModel.cells.find(cell => cell.handle === cellHandle);
|
||||
|
||||
if (!cell) {
|
||||
@@ -662,8 +635,11 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
const editor = this._notebookService.listNotebookEditors().find(editor => editor.getId() === id);
|
||||
if (editor && editor.isNotebookEditor) {
|
||||
const notebookEditor = editor as INotebookEditor;
|
||||
if (!notebookEditor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const viewModel = notebookEditor.viewModel;
|
||||
const cell = viewModel?.viewCells[range.start];
|
||||
const cell = viewModel.viewCells[range.start];
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
@@ -726,6 +702,51 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
async $tryShowNotebookDocument(resource: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise<string> {
|
||||
const editorOptions: ITextEditorOptions = {
|
||||
preserveFocus: options.preserveFocus,
|
||||
pinned: options.pinned,
|
||||
// selection: options.selection,
|
||||
// preserve pre 1.38 behaviour to not make group active when preserveFocus: true
|
||||
// but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633
|
||||
activation: options.preserveFocus ? EditorActivation.RESTORE : undefined,
|
||||
override: false,
|
||||
};
|
||||
|
||||
const columnArg = viewColumnToEditorGroup(this._editorGroupService, options.position);
|
||||
|
||||
let group: IEditorGroup | undefined = undefined;
|
||||
|
||||
if (columnArg === SIDE_GROUP) {
|
||||
const direction = preferredSideBySideGroupDirection(this.configurationService);
|
||||
|
||||
let neighbourGroup = this.editorGroupsService.findGroup({ direction });
|
||||
if (!neighbourGroup) {
|
||||
neighbourGroup = this.editorGroupsService.addGroup(this.editorGroupsService.activeGroup, direction);
|
||||
}
|
||||
group = neighbourGroup;
|
||||
} else {
|
||||
group = this.editorGroupsService.getGroup(viewColumnToEditorGroup(this.editorGroupsService, columnArg)) ?? this.editorGroupsService.activeGroup;
|
||||
}
|
||||
|
||||
const input = this.editorService.createEditorInput({ resource: URI.revive(resource), options: editorOptions });
|
||||
|
||||
// TODO: handle options.selection
|
||||
const editorPane = await openEditorWith(input, viewType, options, group, this.editorService, this.configurationService, this.quickInputService);
|
||||
const notebookEditor = (editorPane as unknown as { isNotebookEditor?: boolean })?.isNotebookEditor ? (editorPane!.getControl() as INotebookEditor) : undefined;
|
||||
|
||||
if (notebookEditor) {
|
||||
if (notebookEditor.viewModel && options.selection && notebookEditor.viewModel.viewCells[options.selection.start]) {
|
||||
const focusedCell = notebookEditor.viewModel.viewCells[options.selection.start];
|
||||
notebookEditor.revealInCenterIfOutsideViewport(focusedCell);
|
||||
notebookEditor.selectElement(focusedCell);
|
||||
}
|
||||
return notebookEditor.getId();
|
||||
} else {
|
||||
throw new Error(`Notebook Editor creation failure for documenet ${resource}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -68,7 +68,8 @@ class MainThreadSCMResource implements ISCMResource {
|
||||
readonly sourceUri: URI,
|
||||
readonly resourceGroup: ISCMResourceGroup,
|
||||
readonly decorations: ISCMResourceDecorations,
|
||||
readonly contextValue: string | undefined
|
||||
readonly contextValue: string | undefined,
|
||||
readonly command: Command | undefined
|
||||
) { }
|
||||
|
||||
open(preserveFocus: boolean): Promise<void> {
|
||||
@@ -201,7 +202,7 @@ class MainThreadSCMProvider implements ISCMProvider {
|
||||
|
||||
for (const [start, deleteCount, rawResources] of groupSlices) {
|
||||
const resources = rawResources.map(rawResource => {
|
||||
const [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue] = rawResource;
|
||||
const [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue, command] = rawResource;
|
||||
const icon = icons[0];
|
||||
const iconDark = icons[1] || icon;
|
||||
const decorations = {
|
||||
@@ -220,7 +221,8 @@ class MainThreadSCMProvider implements ISCMProvider {
|
||||
URI.revive(sourceUri),
|
||||
group,
|
||||
decorations,
|
||||
contextValue || undefined
|
||||
contextValue || undefined,
|
||||
command
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
|
||||
this.entries.clear();
|
||||
}
|
||||
|
||||
$setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation): void {
|
||||
$setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation): void {
|
||||
// if there are icons in the text use the tooltip for the aria label
|
||||
let ariaLabel: string;
|
||||
let role: string | undefined = undefined;
|
||||
@@ -37,7 +37,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
|
||||
} else {
|
||||
ariaLabel = text ? text.replace(MainThreadStatusBar.CODICON_REGEXP, (_match, codiconName) => codiconName) : '';
|
||||
}
|
||||
const entry: IStatusbarEntry = { text, tooltip, command, color, ariaLabel, role };
|
||||
const entry: IStatusbarEntry = { text, tooltip, command, color, backgroundColor, ariaLabel, role };
|
||||
|
||||
if (typeof priority === 'undefined') {
|
||||
priority = 0;
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { MainThreadStorageShape, MainContext, IExtHostContext, ExtHostStorageShape, ExtHostContext } from '../common/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IExtensionIdWithVersion, IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
import { IExtensionIdWithVersion, IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadStorage)
|
||||
export class MainThreadStorage implements MainThreadStorageShape {
|
||||
|
||||
private readonly _storageService: IStorageService;
|
||||
private readonly _storageKeysSyncRegistryService: IStorageKeysSyncRegistryService;
|
||||
private readonly _extensionsStorageSyncService: IExtensionsStorageSyncService;
|
||||
private readonly _proxy: ExtHostStorageShape;
|
||||
private readonly _storageListener: IDisposable;
|
||||
private readonly _sharedStorageKeysToWatch: Map<string, boolean> = new Map<string, boolean>();
|
||||
@@ -21,13 +21,13 @@ export class MainThreadStorage implements MainThreadStorageShape {
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
|
||||
@IExtensionsStorageSyncService extensionsStorageSyncService: IExtensionsStorageSyncService,
|
||||
) {
|
||||
this._storageService = storageService;
|
||||
this._storageKeysSyncRegistryService = storageKeysSyncRegistryService;
|
||||
this._extensionsStorageSyncService = extensionsStorageSyncService;
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostStorage);
|
||||
|
||||
this._storageListener = this._storageService.onDidChangeStorage(e => {
|
||||
this._storageListener = this._storageService.onDidChangeValue(e => {
|
||||
const shared = e.scope === StorageScope.GLOBAL;
|
||||
if (shared && this._sharedStorageKeysToWatch.has(e.key)) {
|
||||
try {
|
||||
@@ -66,7 +66,12 @@ export class MainThreadStorage implements MainThreadStorageShape {
|
||||
let jsonValue: string;
|
||||
try {
|
||||
jsonValue = JSON.stringify(value);
|
||||
<<<<<<< HEAD
|
||||
await this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE);
|
||||
=======
|
||||
// Extension state is synced separately through extensions
|
||||
this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE, StorageTarget.MACHINE);
|
||||
>>>>>>> e4a830e9b7ca039c7c70697786d29f5b6679d775
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
@@ -74,6 +79,6 @@ export class MainThreadStorage implements MainThreadStorageShape {
|
||||
}
|
||||
|
||||
$registerExtensionStorageKeysToSync(extension: IExtensionIdWithVersion, keys: string[]): void {
|
||||
this._storageKeysSyncRegistryService.registerExtensionStorageKeys(extension, keys);
|
||||
this._extensionsStorageSyncService.setKeysForSync(extension, keys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace TaskProcessStartedDTO {
|
||||
}
|
||||
|
||||
namespace TaskProcessEndedDTO {
|
||||
export function from(value: TaskExecution, exitCode: number): TaskProcessEndedDTO {
|
||||
export function from(value: TaskExecution, exitCode: number | undefined): TaskProcessEndedDTO {
|
||||
return {
|
||||
id: value.id,
|
||||
exitCode
|
||||
@@ -429,7 +429,7 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
} else if (event.kind === TaskEventKind.ProcessStarted) {
|
||||
this._proxy.$onDidStartTaskProcess(TaskProcessStartedDTO.from(task.getTaskExecution(), event.processId!));
|
||||
} else if (event.kind === TaskEventKind.ProcessEnded) {
|
||||
this._proxy.$onDidEndTaskProcess(TaskProcessEndedDTO.from(task.getTaskExecution(), event.exitCode!));
|
||||
this._proxy.$onDidEndTaskProcess(TaskProcessEndedDTO.from(task.getTaskExecution(), event.exitCode));
|
||||
} else if (event.kind === TaskEventKind.End) {
|
||||
this._proxy.$OnDidEndTask(TaskExecutionDTO.from(task.getTaskExecution()));
|
||||
}
|
||||
|
||||
77
lib/vscode/src/vs/workbench/api/browser/mainThreadTesting.ts
Normal file
77
lib/vscode/src/vs/workbench/api/browser/mainThreadTesting.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { getTestSubscriptionKey, RunTestsRequest, RunTestsResult, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { ITestService } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ExtHostContext, ExtHostTestingResource, ExtHostTestingShape, IExtHostContext, MainContext, MainThreadTestingShape } from '../common/extHost.protocol';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTesting)
|
||||
export class MainThreadTesting extends Disposable implements MainThreadTestingShape {
|
||||
private readonly proxy: ExtHostTestingShape;
|
||||
private readonly testSubscriptions = new Map<string, IDisposable>();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@ITestService private readonly testService: ITestService,
|
||||
) {
|
||||
super();
|
||||
this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostTesting);
|
||||
this._register(this.testService.onShouldSubscribe(args => this.proxy.$subscribeToTests(args.resource, args.uri)));
|
||||
this._register(this.testService.onShouldUnsubscribe(args => this.proxy.$unsubscribeFromTests(args.resource, args.uri)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $registerTestProvider(id: string) {
|
||||
this.testService.registerTestController(id, {
|
||||
runTests: req => this.proxy.$runTestsForProvider(req),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $unregisterTestProvider(id: string) {
|
||||
this.testService.unregisterTestController(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$subscribeToDiffs(resource: ExtHostTestingResource, uriComponents: UriComponents): void {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const disposable = this.testService.subscribeToDiffs(resource, uri,
|
||||
diff => this.proxy.$acceptDiff(resource, uriComponents, diff));
|
||||
this.testSubscriptions.set(getTestSubscriptionKey(resource, uri), disposable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $unsubscribeFromDiffs(resource: ExtHostTestingResource, uriComponents: UriComponents): void {
|
||||
const key = getTestSubscriptionKey(resource, URI.revive(uriComponents));
|
||||
this.testSubscriptions.get(key)?.dispose();
|
||||
this.testSubscriptions.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void {
|
||||
this.testService.publishDiff(resource, URI.revive(uri), diff);
|
||||
}
|
||||
|
||||
public $runTests(req: RunTestsRequest): Promise<RunTestsResult> {
|
||||
return this.testService.runTests(req);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
@@ -52,14 +52,14 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
|
||||
});
|
||||
}
|
||||
|
||||
$reveal(treeViewId: string, item: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise<void> {
|
||||
this.logService.trace('MainThreadTreeViews#$reveal', treeViewId, item, parentChain, options);
|
||||
$reveal(treeViewId: string, itemInfo: { item: ITreeItem, parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise<void> {
|
||||
this.logService.trace('MainThreadTreeViews#$reveal', treeViewId, itemInfo?.item, itemInfo?.parentChain, options);
|
||||
|
||||
return this.viewsService.openView(treeViewId, options.focus)
|
||||
.then(() => {
|
||||
const viewer = this.getTreeView(treeViewId);
|
||||
if (viewer) {
|
||||
return this.reveal(viewer, this._dataProviders.get(treeViewId)!, item, parentChain, options);
|
||||
if (viewer && itemInfo) {
|
||||
return this.reveal(viewer, this._dataProviders.get(treeViewId)!, itemInfo.item, itemInfo.parentChain, options);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
@@ -235,10 +235,14 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
|
||||
private updateTreeItem(current: ITreeItem, treeItem: ITreeItem): void {
|
||||
treeItem.children = treeItem.children ? treeItem.children : undefined;
|
||||
if (current) {
|
||||
const properties = distinct([...Object.keys(current), ...Object.keys(treeItem)]);
|
||||
const properties = distinct([...Object.keys(current instanceof ResolvableTreeItem ? current.asTreeItem() : current),
|
||||
...Object.keys(treeItem)]);
|
||||
for (const property of properties) {
|
||||
(<any>current)[property] = (<any>treeItem)[property];
|
||||
}
|
||||
if (current instanceof ResolvableTreeItem) {
|
||||
current.resetResolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
import { MainThreadTunnelServiceShape, IExtHostContext, MainContext, ExtHostContext, ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { IRemoteExplorerService, MakeAddress } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ITunnelProvider, ITunnelService, TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { IRemoteExplorerService, makeAddress } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import type { TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
|
||||
@@ -26,8 +26,8 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
this._register(tunnelService.onTunnelClosed(() => this._proxy.$onDidTunnelsChange()));
|
||||
}
|
||||
|
||||
async $openTunnel(tunnelOptions: TunnelOptions): Promise<TunnelDto | undefined> {
|
||||
const tunnel = await this.remoteExplorerService.forward(tunnelOptions.remoteAddress, tunnelOptions.localAddressPort, tunnelOptions.label);
|
||||
async $openTunnel(tunnelOptions: TunnelOptions, source: string): Promise<TunnelDto | undefined> {
|
||||
const tunnel = await this.remoteExplorerService.forward(tunnelOptions.remoteAddress, tunnelOptions.localAddressPort, tunnelOptions.label, source);
|
||||
if (tunnel) {
|
||||
return TunnelDto.fromServiceTunnel(tunnel);
|
||||
}
|
||||
@@ -47,8 +47,8 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
});
|
||||
}
|
||||
|
||||
async $registerCandidateFinder(): Promise<void> {
|
||||
this.remoteExplorerService.registerCandidateFinder(() => this._proxy.$findCandidatePorts());
|
||||
async $onFoundNewCandidates(candidates: { host: string, port: number, detail: string }[]): Promise<void> {
|
||||
this.remoteExplorerService.onFoundNewCandidates(candidates);
|
||||
}
|
||||
|
||||
async $tunnelServiceReady(): Promise<void> {
|
||||
@@ -57,19 +57,17 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
|
||||
async $setTunnelProvider(): Promise<void> {
|
||||
const tunnelProvider: ITunnelProvider = {
|
||||
forwardPort: (tunnelOptions: TunnelOptions) => {
|
||||
const forward = this._proxy.$forwardPort(tunnelOptions);
|
||||
forwardPort: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => {
|
||||
const forward = this._proxy.$forwardPort(tunnelOptions, tunnelCreationOptions);
|
||||
if (forward) {
|
||||
return forward.then(tunnel => {
|
||||
return {
|
||||
tunnelRemotePort: tunnel.remoteAddress.port,
|
||||
tunnelRemoteHost: tunnel.remoteAddress.host,
|
||||
localAddress: typeof tunnel.localAddress === 'string' ? tunnel.localAddress : MakeAddress(tunnel.localAddress.host, tunnel.localAddress.port),
|
||||
localAddress: typeof tunnel.localAddress === 'string' ? tunnel.localAddress : makeAddress(tunnel.localAddress.host, tunnel.localAddress.port),
|
||||
tunnelLocalPort: typeof tunnel.localAddress !== 'string' ? tunnel.localAddress.port : undefined,
|
||||
dispose: (silent: boolean) => {
|
||||
if (!silent) {
|
||||
this._proxy.$closeTunnel({ host: tunnel.remoteAddress.host, port: tunnel.remoteAddress.port });
|
||||
}
|
||||
dispose: (silent?: boolean) => {
|
||||
this._proxy.$closeTunnel({ host: tunnel.remoteAddress.host, port: tunnel.remoteAddress.port }, silent);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -80,22 +78,6 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
this.tunnelService.setTunnelProvider(tunnelProvider);
|
||||
}
|
||||
|
||||
async $setCandidateFilter(): Promise<void> {
|
||||
this._register(this.remoteExplorerService.setCandidateFilter(async (candidates: { host: string, port: number, detail: string }[]): Promise<{ host: string, port: number, detail: string }[]> => {
|
||||
const filters: boolean[] = await this._proxy.$filterCandidates(candidates);
|
||||
const filteredCandidates: { host: string, port: number, detail: string }[] = [];
|
||||
if (filters.length !== candidates.length) {
|
||||
return candidates;
|
||||
}
|
||||
for (let i = 0; i < candidates.length; i++) {
|
||||
if (filters[i]) {
|
||||
filteredCandidates.push(candidates[i]);
|
||||
}
|
||||
}
|
||||
return filteredCandidates;
|
||||
}));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
}
|
||||
|
||||
@@ -9,8 +9,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { MainThreadWebviews, reviveWebviewExtension, reviveWebviewOptions } from 'vs/workbench/api/browser/mainThreadWebviews';
|
||||
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
|
||||
import { IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { editorGroupToViewColumn, EditorGroupColumn, IEditorInput, viewColumnToEditorGroup } from 'vs/workbench/common/editor';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput';
|
||||
@@ -150,7 +149,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
handle: extHostProtocol.WebviewHandle,
|
||||
viewType: string,
|
||||
title: string,
|
||||
showOptions: { viewColumn?: EditorViewColumn, preserveFocus?: boolean; },
|
||||
showOptions: { viewColumn?: EditorGroupColumn, preserveFocus?: boolean; },
|
||||
options: WebviewInputOptions
|
||||
): void {
|
||||
const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null);
|
||||
|
||||
@@ -8,8 +8,8 @@ import { forEach } from 'vs/base/common/collections';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor, ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { TreeViewPane } from 'vs/workbench/browser/parts/views/treeView';
|
||||
import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor, ViewContainerLocation, testViewIcon } from 'vs/workbench/common/views';
|
||||
import { CustomTreeView, TreeViewPane } from 'vs/workbench/browser/parts/views/treeView';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { coalesce, } from 'vs/base/common/arrays';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
@@ -30,9 +30,8 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions, CATEGORIES }
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { CustomTreeView } from 'vs/workbench/contrib/views/browser/treeView';
|
||||
import { WebviewViewPane } from 'vs/workbench/contrib/webviewView/browser/webviewViewPane';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
export interface IUserFriendlyViewsContainerDescriptor {
|
||||
id: string;
|
||||
@@ -321,7 +320,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
|
||||
private registerTestViewContainer(): void {
|
||||
const title = localize('test', "Test");
|
||||
const icon = Codicon.beaker.classNames;
|
||||
const icon = testViewIcon;
|
||||
|
||||
this.registerCustomViewContainer(TEST_VIEW_CONTAINER_ID, title, icon, TEST_VIEW_CONTAINER_ORDER, undefined, ViewContainerLocation.Sidebar);
|
||||
}
|
||||
@@ -356,7 +355,9 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
|
||||
private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number, existingViewContainers: ViewContainer[], location: ViewContainerLocation): number {
|
||||
containers.forEach(descriptor => {
|
||||
const icon = resources.joinPath(extension.extensionLocation, descriptor.icon);
|
||||
const themeIcon = ThemeIcon.fromString(descriptor.icon);
|
||||
|
||||
const icon = themeIcon || resources.joinPath(extension.extensionLocation, descriptor.icon);
|
||||
const id = `workbench.view.extension.${descriptor.id}`;
|
||||
const viewContainer = this.registerCustomViewContainer(id, descriptor.title, icon, order++, extension.identifier, location);
|
||||
|
||||
@@ -376,7 +377,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
return order;
|
||||
}
|
||||
|
||||
private registerCustomViewContainer(id: string, title: string, icon: URI | string, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer {
|
||||
private registerCustomViewContainer(id: string, title: string, icon: URI | ThemeIcon, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer {
|
||||
let viewContainer = this.viewContainersRegistry.get(id);
|
||||
|
||||
if (!viewContainer) {
|
||||
@@ -470,7 +471,11 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
? container.viewOrderDelegate.getOrder(item.group)
|
||||
: undefined;
|
||||
|
||||
const icon = item.icon ? resources.joinPath(extension.description.extensionLocation, item.icon) : undefined;
|
||||
let icon: ThemeIcon | URI | undefined;
|
||||
if (typeof item.icon === 'string') {
|
||||
icon = ThemeIcon.fromString(item.icon) || resources.joinPath(extension.description.extensionLocation, item.icon);
|
||||
}
|
||||
|
||||
const initialVisibility = this.convertInitialVisibility(item.visibility);
|
||||
|
||||
const type = this.getViewType(item.type);
|
||||
|
||||
@@ -3,20 +3,16 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { URI, UriComponents } 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 { ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
|
||||
import { IWorkspacesService, hasWorkspaceFileExtension, IRecent } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
|
||||
import { IWorkspacesService, IRecent } from 'vs/platform/workspaces/common/workspaces';
|
||||
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.
|
||||
@@ -35,41 +31,6 @@ function adjustHandler(handler: (executor: ICommandsExecutor, ...args: any[]) =>
|
||||
};
|
||||
}
|
||||
|
||||
interface IOpenFolderAPICommandOptions {
|
||||
forceNewWindow?: boolean;
|
||||
forceReuseWindow?: boolean;
|
||||
noRecentEntry?: boolean;
|
||||
}
|
||||
|
||||
export class OpenFolderAPICommand {
|
||||
public static readonly ID = 'vscode.openFolder';
|
||||
public static execute(executor: ICommandsExecutor, uri?: URI, forceNewWindow?: boolean): Promise<any>;
|
||||
public static execute(executor: ICommandsExecutor, uri?: URI, options?: IOpenFolderAPICommandOptions): Promise<any>;
|
||||
public static execute(executor: ICommandsExecutor, uri?: URI, arg: boolean | IOpenFolderAPICommandOptions = {}): Promise<any> {
|
||||
if (typeof arg === 'boolean') {
|
||||
arg = { forceNewWindow: arg };
|
||||
}
|
||||
if (!uri) {
|
||||
return executor.executeCommand('_files.pickFolderAndOpen', { forceNewWindow: arg.forceNewWindow });
|
||||
}
|
||||
const options: IOpenWindowOptions = { forceNewWindow: arg.forceNewWindow, forceReuseWindow: arg.forceReuseWindow, noRecentEntry: arg.noRecentEntry };
|
||||
uri = URI.revive(uri);
|
||||
const uriToOpen: IWindowOpenable = (hasWorkspaceFileExtension(uri) || uri.scheme === Schemas.untitled) ? { workspaceUri: uri } : { folderUri: uri };
|
||||
return executor.executeCommand('_files.windowOpen', [uriToOpen], options);
|
||||
}
|
||||
}
|
||||
CommandsRegistry.registerCommand({
|
||||
id: OpenFolderAPICommand.ID,
|
||||
handler: adjustHandler(OpenFolderAPICommand.execute),
|
||||
description: {
|
||||
description: 'Open a folder or workspace in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder/workspace unless the newWindow parameter is set to true.',
|
||||
args: [
|
||||
{ name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: (value: any) => value === undefined || value instanceof URI },
|
||||
{ name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Wheter the opened URI will appear in the \'Open Recent\' list. Defaults to true. Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' }
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
interface INewWindowAPICommandOptions {
|
||||
reuseWindow?: boolean;
|
||||
remoteAuthority?: string;
|
||||
@@ -96,67 +57,6 @@ CommandsRegistry.registerCommand({
|
||||
}
|
||||
});
|
||||
|
||||
export class DiffAPICommand {
|
||||
public static readonly ID = 'vscode.diff';
|
||||
public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: typeConverters.TextEditorOpenOptions): Promise<any> {
|
||||
return executor.executeCommand('_workbench.diff', [
|
||||
left, right,
|
||||
label,
|
||||
undefined,
|
||||
typeConverters.TextEditorOpenOptions.from(options),
|
||||
options ? typeConverters.ViewColumn.from(options.viewColumn) : undefined
|
||||
]);
|
||||
}
|
||||
}
|
||||
CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand.execute));
|
||||
|
||||
export class OpenAPICommand {
|
||||
public static readonly ID = 'vscode.open';
|
||||
public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | typeConverters.TextEditorOpenOptions, label?: string): Promise<any> {
|
||||
let options: ITextEditorOptions | undefined;
|
||||
let position: EditorViewColumn | undefined;
|
||||
|
||||
if (columnOrOptions) {
|
||||
if (typeof columnOrOptions === 'number') {
|
||||
position = typeConverters.ViewColumn.from(columnOrOptions);
|
||||
} else {
|
||||
options = typeConverters.TextEditorOpenOptions.from(columnOrOptions);
|
||||
position = typeConverters.ViewColumn.from(columnOrOptions.viewColumn);
|
||||
}
|
||||
}
|
||||
|
||||
return executor.executeCommand('_workbench.open', [
|
||||
resource,
|
||||
options,
|
||||
position,
|
||||
label
|
||||
]);
|
||||
}
|
||||
}
|
||||
CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute));
|
||||
|
||||
export class OpenWithAPICommand {
|
||||
public static readonly ID = 'vscode.openWith';
|
||||
public static execute(executor: ICommandsExecutor, resource: URI, viewType: string, columnOrOptions?: vscode.ViewColumn | typeConverters.TextEditorOpenOptions): Promise<any> {
|
||||
let options: ITextEditorOptions | undefined;
|
||||
let position: EditorViewColumn | undefined;
|
||||
|
||||
if (typeof columnOrOptions === 'number') {
|
||||
position = typeConverters.ViewColumn.from(columnOrOptions);
|
||||
} else if (typeof columnOrOptions !== 'undefined') {
|
||||
options = typeConverters.TextEditorOpenOptions.from(columnOrOptions);
|
||||
}
|
||||
|
||||
return executor.executeCommand('_workbench.openWith', [
|
||||
resource,
|
||||
viewType,
|
||||
options,
|
||||
position
|
||||
]);
|
||||
}
|
||||
}
|
||||
CommandsRegistry.registerCommand(OpenWithAPICommand.ID, adjustHandler(OpenWithAPICommand.execute));
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, uri: URI) {
|
||||
const workspacesService = accessor.get(IWorkspacesService);
|
||||
return workspacesService.removeRecentlyOpened([uri]);
|
||||
@@ -219,38 +119,6 @@ CommandsRegistry.registerCommand('_workbench.getRecentlyOpened', async function
|
||||
return workspacesService.getRecentlyOpened();
|
||||
});
|
||||
|
||||
export class SetEditorLayoutAPICommand {
|
||||
public static readonly ID = 'vscode.setEditorLayout';
|
||||
public static execute(executor: ICommandsExecutor, layout: EditorGroupLayout): Promise<any> {
|
||||
return executor.executeCommand('layoutEditorGroups', layout);
|
||||
}
|
||||
}
|
||||
CommandsRegistry.registerCommand({
|
||||
id: SetEditorLayoutAPICommand.ID,
|
||||
handler: adjustHandler(SetEditorLayoutAPICommand.execute),
|
||||
description: {
|
||||
description: 'Set Editor Layout',
|
||||
args: [{
|
||||
name: 'args',
|
||||
schema: {
|
||||
'type': 'object',
|
||||
'required': ['groups'],
|
||||
'properties': {
|
||||
'orientation': {
|
||||
'type': 'number',
|
||||
'default': 0,
|
||||
'enum': [0, 1]
|
||||
},
|
||||
'groups': {
|
||||
'$ref': '#/definitions/editorGroupsSchema', // defined in keybindingService.ts ...
|
||||
'default': [{}, {}],
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand('_extensionTests.setLogLevel', function (accessor: ServicesAccessor, level: number) {
|
||||
const logService = accessor.get(ILogService);
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
@@ -260,6 +128,13 @@ 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);
|
||||
|
||||
@@ -304,3 +179,31 @@ CommandsRegistry.registerCommand({
|
||||
args: []
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// The following commands are registered on the renderer but as API
|
||||
// command. DO NOT USE this unless you have understood what this
|
||||
// means
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
|
||||
class OpenAPICommand {
|
||||
public static readonly ID = 'vscode.open';
|
||||
public static execute(executor: ICommandsExecutor, resource: URI): Promise<any> {
|
||||
|
||||
return executor.executeCommand('_workbench.open', resource);
|
||||
}
|
||||
}
|
||||
CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute));
|
||||
|
||||
class DiffAPICommand {
|
||||
public static readonly ID = 'vscode.diff';
|
||||
public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: typeConverters.TextEditorOpenOptions): Promise<any> {
|
||||
return executor.executeCommand('_workbench.diff', [
|
||||
left, right,
|
||||
label,
|
||||
]);
|
||||
}
|
||||
}
|
||||
CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand.execute));
|
||||
|
||||
@@ -28,6 +28,10 @@ const configurationEntrySchema: IJSONSchema = {
|
||||
properties: {
|
||||
description: nls.localize('vscode.extension.contributes.configuration.properties', 'Description of the configuration properties.'),
|
||||
type: 'object',
|
||||
propertyNames: {
|
||||
pattern: '\\S+',
|
||||
patternErrorMessage: nls.localize('vscode.extension.contributes.configuration.property.empty', 'Property should not be empty.'),
|
||||
},
|
||||
additionalProperties: {
|
||||
anyOf: [
|
||||
{ $ref: 'http://json-schema.org/draft-07/schema#' },
|
||||
|
||||
@@ -51,7 +51,7 @@ import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { throwProposedApiError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import * as vscode from 'vscode';
|
||||
import type * as vscode from 'vscode';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { originalFSPath } from 'vs/base/common/resources';
|
||||
import { values } from 'vs/base/common/collections';
|
||||
@@ -82,6 +82,7 @@ import { ExtHostCustomEditors } from 'vs/workbench/api/common/extHostCustomEdito
|
||||
import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels';
|
||||
import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits';
|
||||
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
|
||||
import { ExtHostTesting } from 'vs/workbench/api/common/extHostTesting';
|
||||
|
||||
export interface IExtensionApiFactory {
|
||||
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
|
||||
@@ -155,6 +156,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
const extHostWebviewPanels = rpcProtocol.set(ExtHostContext.ExtHostWebviewPanels, new ExtHostWebviewPanels(rpcProtocol, extHostWebviews, extHostWorkspace));
|
||||
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));
|
||||
|
||||
// Check that no named customers are missing
|
||||
const expected: ProxyIdentifier<any>[] = values(ExtHostContext);
|
||||
@@ -204,40 +206,50 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
})();
|
||||
|
||||
const authentication: typeof vscode.authentication = {
|
||||
registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable {
|
||||
return extHostAuthentication.registerAuthenticationProvider(provider);
|
||||
},
|
||||
get onDidChangeAuthenticationProviders(): Event<vscode.AuthenticationProvidersChangeEvent> {
|
||||
return extHostAuthentication.onDidChangeAuthenticationProviders;
|
||||
},
|
||||
getProviderIds(): Thenable<ReadonlyArray<string>> {
|
||||
return extHostAuthentication.getProviderIds();
|
||||
},
|
||||
get providerIds(): string[] {
|
||||
return extHostAuthentication.providerIds;
|
||||
},
|
||||
get providers(): ReadonlyArray<vscode.AuthenticationProviderInformation> {
|
||||
return extHostAuthentication.providers;
|
||||
},
|
||||
getSession(providerId: string, scopes: string[], options?: vscode.AuthenticationGetSessionOptions) {
|
||||
return extHostAuthentication.getSession(extension, providerId, scopes, options as any);
|
||||
},
|
||||
logout(providerId: string, sessionId: string): Thenable<void> {
|
||||
return extHostAuthentication.logout(providerId, sessionId);
|
||||
},
|
||||
get onDidChangeSessions(): Event<vscode.AuthenticationSessionsChangeEvent> {
|
||||
return extHostAuthentication.onDidChangeSessions;
|
||||
},
|
||||
registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostAuthentication.registerAuthenticationProvider(provider);
|
||||
},
|
||||
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;
|
||||
},
|
||||
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;
|
||||
}
|
||||
};
|
||||
@@ -296,14 +308,6 @@ 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 logLevel() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return typeConverters.LogLevel.to(extHostLogService.getLevel());
|
||||
},
|
||||
get onDidChangeLogLevel() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return Event.map(extHostLogService.onDidChangeLogLevel, l => typeConverters.LogLevel.to(l));
|
||||
},
|
||||
get clipboard(): vscode.Clipboard {
|
||||
return extHostClipboard;
|
||||
},
|
||||
@@ -336,6 +340,25 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
? extHostTypes.ExtensionKind.Workspace
|
||||
: extHostTypes.ExtensionKind.UI;
|
||||
|
||||
const test: typeof vscode.test = {
|
||||
registerTestProvider(provider) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.registerTestProvider(provider);
|
||||
},
|
||||
createDocumentTestObserver(document) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.createTextDocumentTestObserver(document);
|
||||
},
|
||||
createWorkspaceTestObserver(workspaceFolder) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.createWorkspaceTestObserver(workspaceFolder);
|
||||
},
|
||||
runTests(provider) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.runTests(provider);
|
||||
},
|
||||
};
|
||||
|
||||
// namespace: extensions
|
||||
const extensions: typeof vscode.extensions = {
|
||||
getExtension(extensionId: string): Extension<any> | undefined {
|
||||
@@ -400,9 +423,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider);
|
||||
},
|
||||
registerOnTypeRenameProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeRenameProvider, stopPattern?: RegExp): vscode.Disposable {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostLanguageFeatures.registerOnTypeRenameProvider(extension, checkSelector(selector), provider, stopPattern);
|
||||
registerLinkedEditingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.LinkedEditingRangeProvider): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerLinkedEditingRangeProvider(extension, checkSelector(selector), provider);
|
||||
},
|
||||
registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerReferenceProvider(extension, checkSelector(selector), provider);
|
||||
@@ -572,7 +594,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
priority = priority;
|
||||
}
|
||||
|
||||
return extHostStatusBar.createStatusBarEntry(id, name, alignment, priority, accessibilityInformation);
|
||||
return extHostStatusBar.createStatusBarEntry(id, name, alignment, priority, accessibilityInformation, extension);
|
||||
},
|
||||
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): vscode.Disposable {
|
||||
return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable);
|
||||
@@ -620,9 +642,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => {
|
||||
return extHostCustomEditors.registerCustomEditorProvider(extension, viewType, provider, options);
|
||||
},
|
||||
registerDecorationProvider(provider: vscode.FileDecorationProvider) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostDecorations.registerDecorationProvider(provider, extension.identifier);
|
||||
registerFileDecorationProvider(provider: vscode.FileDecorationProvider) {
|
||||
return extHostDecorations.registerFileDecorationProvider(provider, extension.identifier);
|
||||
},
|
||||
registerUriHandler(handler: vscode.UriHandler) {
|
||||
return extHostUrls.registerUriHandler(extension.identifier, handler);
|
||||
@@ -670,6 +691,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeNotebookEditorVisibleRanges(listener, thisArgs, disposables);
|
||||
},
|
||||
showNotebookDocument(document, options?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.showNotebookDocument(document, options);
|
||||
}
|
||||
};
|
||||
|
||||
// namespace: workspace
|
||||
@@ -838,7 +863,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
},
|
||||
openTunnel: (forward: vscode.TunnelOptions) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTunnelService.openTunnel(forward).then(value => {
|
||||
return extHostTunnelService.openTunnel(extension, forward).then(value => {
|
||||
if (!value) {
|
||||
throw new Error('cannot open tunnel');
|
||||
}
|
||||
@@ -873,14 +898,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
}
|
||||
};
|
||||
|
||||
const comment: typeof vscode.comments = {
|
||||
// namespace: comments
|
||||
const comments: typeof vscode.comments = {
|
||||
createCommentController(id: string, label: string) {
|
||||
return extHostComment.createCommentController(extension, id, label);
|
||||
}
|
||||
};
|
||||
|
||||
const comments = comment;
|
||||
|
||||
// namespace: debug
|
||||
const debug: typeof vscode.debug = {
|
||||
get activeDebugSession() {
|
||||
@@ -1017,6 +1041,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
return extHostNotebook.registerNotebookKernelProvider(extension, selector, provider);
|
||||
},
|
||||
createNotebookEditorDecorationType(options: vscode.NotebookDecorationRenderOptions): vscode.NotebookEditorDecorationType {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.createNotebookEditorDecorationType(options);
|
||||
},
|
||||
get activeNotebookEditor(): vscode.NotebookEditor | undefined {
|
||||
@@ -1070,40 +1095,46 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
// namespaces
|
||||
authentication,
|
||||
commands,
|
||||
comments,
|
||||
debug,
|
||||
env,
|
||||
extensions,
|
||||
languages,
|
||||
scm,
|
||||
comment,
|
||||
comments,
|
||||
tasks,
|
||||
notebook,
|
||||
scm,
|
||||
tasks,
|
||||
test,
|
||||
window,
|
||||
workspace,
|
||||
// types
|
||||
Breakpoint: extHostTypes.Breakpoint,
|
||||
CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall,
|
||||
CallHierarchyItem: extHostTypes.CallHierarchyItem,
|
||||
CallHierarchyOutgoingCall: extHostTypes.CallHierarchyOutgoingCall,
|
||||
CancellationTokenSource: CancellationTokenSource,
|
||||
CodeAction: extHostTypes.CodeAction,
|
||||
CodeActionKind: extHostTypes.CodeActionKind,
|
||||
CodeActionTrigger: extHostTypes.CodeActionTrigger,
|
||||
CodeLens: extHostTypes.CodeLens,
|
||||
CodeInset: extHostTypes.CodeInset,
|
||||
Color: extHostTypes.Color,
|
||||
ColorInformation: extHostTypes.ColorInformation,
|
||||
ColorPresentation: extHostTypes.ColorPresentation,
|
||||
CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState,
|
||||
ColorThemeKind: extHostTypes.ColorThemeKind,
|
||||
CommentMode: extHostTypes.CommentMode,
|
||||
CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState,
|
||||
CompletionItem: extHostTypes.CompletionItem,
|
||||
CompletionItemKind: extHostTypes.CompletionItemKind,
|
||||
CompletionItemTag: extHostTypes.CompletionItemTag,
|
||||
CompletionList: extHostTypes.CompletionList,
|
||||
CompletionTriggerKind: extHostTypes.CompletionTriggerKind,
|
||||
ConfigurationTarget: extHostTypes.ConfigurationTarget,
|
||||
CustomExecution: extHostTypes.CustomExecution,
|
||||
DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable,
|
||||
DebugAdapterServer: extHostTypes.DebugAdapterServer,
|
||||
DebugAdapterNamedPipeServer: extHostTypes.DebugAdapterNamedPipeServer,
|
||||
DebugAdapterInlineImplementation: extHostTypes.DebugAdapterInlineImplementation,
|
||||
DebugAdapterNamedPipeServer: extHostTypes.DebugAdapterNamedPipeServer,
|
||||
DebugAdapterServer: extHostTypes.DebugAdapterServer,
|
||||
DebugConfigurationProviderTriggerKind: extHostTypes.DebugConfigurationProviderTriggerKind,
|
||||
DebugConsoleMode: extHostTypes.DebugConsoleMode,
|
||||
DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior,
|
||||
Diagnostic: extHostTypes.Diagnostic,
|
||||
DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation,
|
||||
@@ -1120,9 +1151,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
EventEmitter: Emitter,
|
||||
ExtensionKind: extHostTypes.ExtensionKind,
|
||||
ExtensionMode: extHostTypes.ExtensionMode,
|
||||
ExtensionRuntime: extHostTypes.ExtensionRuntime,
|
||||
CustomExecution: extHostTypes.CustomExecution,
|
||||
FileChangeType: extHostTypes.FileChangeType,
|
||||
FileDecoration: extHostTypes.FileDecoration,
|
||||
FileSystemError: extHostTypes.FileSystemError,
|
||||
FileType: files.FileType,
|
||||
FoldingRange: extHostTypes.FoldingRange,
|
||||
@@ -1131,7 +1161,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
Hover: extHostTypes.Hover,
|
||||
IndentAction: languageConfiguration.IndentAction,
|
||||
Location: extHostTypes.Location,
|
||||
LogLevel: extHostTypes.LogLevel,
|
||||
MarkdownString: extHostTypes.MarkdownString,
|
||||
OverviewRulerLane: OverviewRulerLane,
|
||||
ParameterInformation: extHostTypes.ParameterInformation,
|
||||
@@ -1141,23 +1170,20 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
QuickInputButtons: extHostTypes.QuickInputButtons,
|
||||
Range: extHostTypes.Range,
|
||||
RelativePattern: extHostTypes.RelativePattern,
|
||||
ResolvedAuthority: extHostTypes.ResolvedAuthority,
|
||||
RemoteAuthorityResolverError: extHostTypes.RemoteAuthorityResolverError,
|
||||
SemanticTokensLegend: extHostTypes.SemanticTokensLegend,
|
||||
SemanticTokensBuilder: extHostTypes.SemanticTokensBuilder,
|
||||
SemanticTokens: extHostTypes.SemanticTokens,
|
||||
SemanticTokensEdits: extHostTypes.SemanticTokensEdits,
|
||||
SemanticTokensEdit: extHostTypes.SemanticTokensEdit,
|
||||
Selection: extHostTypes.Selection,
|
||||
SelectionRange: extHostTypes.SelectionRange,
|
||||
SemanticTokens: extHostTypes.SemanticTokens,
|
||||
SemanticTokensBuilder: extHostTypes.SemanticTokensBuilder,
|
||||
SemanticTokensEdit: extHostTypes.SemanticTokensEdit,
|
||||
SemanticTokensEdits: extHostTypes.SemanticTokensEdits,
|
||||
SemanticTokensLegend: extHostTypes.SemanticTokensLegend,
|
||||
ShellExecution: extHostTypes.ShellExecution,
|
||||
ShellQuoting: extHostTypes.ShellQuoting,
|
||||
SignatureHelpTriggerKind: extHostTypes.SignatureHelpTriggerKind,
|
||||
SignatureHelp: extHostTypes.SignatureHelp,
|
||||
SignatureHelpTriggerKind: extHostTypes.SignatureHelpTriggerKind,
|
||||
SignatureInformation: extHostTypes.SignatureInformation,
|
||||
SnippetString: extHostTypes.SnippetString,
|
||||
SourceBreakpoint: extHostTypes.SourceBreakpoint,
|
||||
SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType,
|
||||
StandardTokenType: extHostTypes.StandardTokenType,
|
||||
StatusBarAlignment: extHostTypes.StatusBarAlignment,
|
||||
SymbolInformation: extHostTypes.SymbolInformation,
|
||||
@@ -1177,29 +1203,80 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
ThemeColor: extHostTypes.ThemeColor,
|
||||
ThemeIcon: extHostTypes.ThemeIcon,
|
||||
TreeItem: extHostTypes.TreeItem,
|
||||
TreeItem2: extHostTypes.TreeItem,
|
||||
TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState,
|
||||
UIKind: UIKind,
|
||||
Uri: URI,
|
||||
ViewColumn: extHostTypes.ViewColumn,
|
||||
WorkspaceEdit: extHostTypes.WorkspaceEdit,
|
||||
// proposed
|
||||
CallHierarchyOutgoingCall: extHostTypes.CallHierarchyOutgoingCall,
|
||||
CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall,
|
||||
CallHierarchyItem: extHostTypes.CallHierarchyItem,
|
||||
DebugConsoleMode: extHostTypes.DebugConsoleMode,
|
||||
DebugConfigurationProviderTriggerKind: extHostTypes.DebugConfigurationProviderTriggerKind,
|
||||
FileDecoration: extHostTypes.FileDecoration,
|
||||
UIKind: UIKind,
|
||||
ColorThemeKind: extHostTypes.ColorThemeKind,
|
||||
TimelineItem: extHostTypes.TimelineItem,
|
||||
CellKind: extHostTypes.CellKind,
|
||||
CellOutputKind: extHostTypes.CellOutputKind,
|
||||
NotebookCellRunState: extHostTypes.NotebookCellRunState,
|
||||
NotebookRunState: extHostTypes.NotebookRunState,
|
||||
NotebookCellStatusBarAlignment: extHostTypes.NotebookCellStatusBarAlignment,
|
||||
NotebookEditorRevealType: extHostTypes.NotebookEditorRevealType,
|
||||
NotebookCellOutput: extHostTypes.NotebookCellOutput,
|
||||
NotebookCellOutputItem: extHostTypes.NotebookCellOutputItem,
|
||||
// proposed api types
|
||||
get RemoteAuthorityResolverError() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.RemoteAuthorityResolverError;
|
||||
},
|
||||
get ResolvedAuthority() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.ResolvedAuthority;
|
||||
},
|
||||
get SourceControlInputBoxValidationType() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.SourceControlInputBoxValidationType;
|
||||
},
|
||||
get ExtensionRuntime() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.ExtensionRuntime;
|
||||
},
|
||||
get TimelineItem() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.TimelineItem;
|
||||
},
|
||||
get CellKind() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.CellKind;
|
||||
},
|
||||
get CellOutputKind() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.CellOutputKind;
|
||||
},
|
||||
get NotebookCellRunState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookCellRunState;
|
||||
},
|
||||
get NotebookRunState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookRunState;
|
||||
},
|
||||
get NotebookCellStatusBarAlignment() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookCellStatusBarAlignment;
|
||||
},
|
||||
get NotebookEditorRevealType() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookEditorRevealType;
|
||||
},
|
||||
get NotebookCellOutput() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookCellOutput;
|
||||
},
|
||||
get NotebookCellOutputItem() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookCellOutputItem;
|
||||
},
|
||||
get LinkedEditingRanges() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.LinkedEditingRanges;
|
||||
},
|
||||
get TestRunState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.TestRunState;
|
||||
},
|
||||
get TestMessageSeverity() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.TestMessageSeverity;
|
||||
},
|
||||
get TestState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.TestState;
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import * as statusbar from 'vs/workbench/services/statusbar/common/statusbar';
|
||||
import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings';
|
||||
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ThemeColor } from 'vs/platform/theme/common/themeService';
|
||||
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
import * as tasks from 'vs/workbench/api/common/shared/tasks';
|
||||
import { IRevealOptions, ITreeItem } from 'vs/workbench/common/views';
|
||||
import { IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug';
|
||||
@@ -45,10 +44,10 @@ import { ITerminalDimensions, IShellLaunchConfig, ITerminalLaunchError } from 'v
|
||||
import { ActivationKind, ExtensionActivationError } 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 { SaveReason } from 'vs/workbench/common/editor';
|
||||
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 { TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { TunnelCreationOptions, 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 +56,8 @@ import { Dto } from 'vs/base/common/types';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync';
|
||||
import { RunTestForProviderRequest, RunTestsRequest, RunTestsResult, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
|
||||
export interface IEnvironment {
|
||||
isExtensionDevelopmentDebug: boolean;
|
||||
@@ -263,21 +263,21 @@ export interface IApplyEditsOptions extends IUndoStopOptions {
|
||||
}
|
||||
|
||||
export interface ITextDocumentShowOptions {
|
||||
position?: EditorViewColumn;
|
||||
position?: EditorGroupColumn;
|
||||
preserveFocus?: boolean;
|
||||
pinned?: boolean;
|
||||
selection?: IRange;
|
||||
}
|
||||
|
||||
export interface MainThreadBulkEditsShape extends IDisposable {
|
||||
$tryApplyWorkspaceEdit(workspaceEditDto: IWorkspaceEditDto): Promise<boolean>;
|
||||
$tryApplyWorkspaceEdit(workspaceEditDto: IWorkspaceEditDto, undoRedoGroupId?: number): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface MainThreadTextEditorsShape extends IDisposable {
|
||||
$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise<string | undefined>;
|
||||
$registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void;
|
||||
$removeTextEditorDecorationType(key: string): void;
|
||||
$tryShowEditor(id: string, position: EditorViewColumn): Promise<void>;
|
||||
$tryShowEditor(id: string, position: EditorGroupColumn): Promise<void>;
|
||||
$tryHideEditor(id: string): Promise<void>;
|
||||
$trySetOptions(id: string, options: ITextEditorConfigurationUpdate): Promise<void>;
|
||||
$trySetDecorations(id: string, key: string, ranges: editorCommon.IDecorationOptions[]): Promise<void>;
|
||||
@@ -292,7 +292,7 @@ export interface MainThreadTextEditorsShape extends IDisposable {
|
||||
export interface MainThreadTreeViewsShape extends IDisposable {
|
||||
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean; }): void;
|
||||
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem; }): Promise<void>;
|
||||
$reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise<void>;
|
||||
$reveal(treeViewId: string, itemInfo: { item: ITreeItem, parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise<void>;
|
||||
$setMessage(treeViewId: string, message: string): void;
|
||||
$setTitle(treeViewId: string, title: string, description: string | undefined): void;
|
||||
}
|
||||
@@ -384,7 +384,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
|
||||
$registerHoverProvider(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerOnTypeRenameProvider(handle: number, selector: IDocumentFilterDto[], stopPattern: IRegExpDto | undefined): void;
|
||||
$registerLinkedEditingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto, displayName: string, supportsResolve: boolean): void;
|
||||
$registerDocumentFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void;
|
||||
@@ -565,7 +565,7 @@ export interface MainThreadQuickOpenShape extends IDisposable {
|
||||
}
|
||||
|
||||
export interface MainThreadStatusBarShape extends IDisposable {
|
||||
$setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: ICommandDto | undefined, color: string | ThemeColor | undefined, alignment: statusbar.StatusbarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): void;
|
||||
$setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: ICommandDto | undefined, color: string | ThemeColor | undefined, backgroundColor: string | ThemeColor | undefined, alignment: statusbar.StatusbarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): void;
|
||||
$dispose(id: number): void;
|
||||
}
|
||||
|
||||
@@ -597,7 +597,7 @@ export interface ExtHostEditorInsetsShape {
|
||||
export type WebviewHandle = string;
|
||||
|
||||
export interface WebviewPanelShowOptions {
|
||||
readonly viewColumn?: EditorViewColumn;
|
||||
readonly viewColumn?: EditorGroupColumn;
|
||||
readonly preserveFocus?: boolean;
|
||||
}
|
||||
|
||||
@@ -661,7 +661,7 @@ export interface WebviewPanelViewStateData {
|
||||
[handle: string]: {
|
||||
readonly active: boolean;
|
||||
readonly visible: boolean;
|
||||
readonly position: EditorViewColumn;
|
||||
readonly position: EditorGroupColumn;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -673,11 +673,11 @@ export interface ExtHostWebviewsShape {
|
||||
export interface ExtHostWebviewPanelsShape {
|
||||
$onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void;
|
||||
$onDidDisposeWebviewPanel(handle: WebviewHandle): Promise<void>;
|
||||
$deserializeWebviewPanel(newWebviewHandle: WebviewHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
$deserializeWebviewPanel(newWebviewHandle: WebviewHandle, viewType: string, title: string, state: any, position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
}
|
||||
|
||||
export interface ExtHostCustomEditorsShape {
|
||||
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken): Promise<void>;
|
||||
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewHandle, viewType: string, title: string, position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken): Promise<void>;
|
||||
$createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>;
|
||||
$disposeCustomDocument(resource: UriComponents, viewType: string): Promise<void>;
|
||||
|
||||
@@ -741,6 +741,13 @@ export enum NotebookEditorRevealType {
|
||||
InCenterIfOutsideViewport = 2,
|
||||
}
|
||||
|
||||
export interface INotebookDocumentShowOptions {
|
||||
position?: EditorGroupColumn;
|
||||
preserveFocus?: boolean;
|
||||
pinned?: boolean;
|
||||
selection?: ICellRange;
|
||||
}
|
||||
|
||||
export type INotebookCellStatusBarEntryDto = Dto<INotebookCellStatusBarEntry>;
|
||||
|
||||
export interface MainThreadNotebookShape extends IDisposable {
|
||||
@@ -760,6 +767,7 @@ export interface MainThreadNotebookShape extends IDisposable {
|
||||
$postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise<boolean>;
|
||||
$setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise<void>;
|
||||
$tryOpenDocument(uriComponents: UriComponents, viewType?: string): Promise<URI>;
|
||||
$tryShowNotebookDocument(uriComponents: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise<string>;
|
||||
$tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType): Promise<void>;
|
||||
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void;
|
||||
$removeNotebookEditorDecorationType(key: string): void;
|
||||
@@ -877,7 +885,8 @@ export type SCMRawResource = [
|
||||
string /*tooltip*/,
|
||||
boolean /*strike through*/,
|
||||
boolean /*faded*/,
|
||||
string /*context value*/
|
||||
string /*context value*/,
|
||||
ICommandDto | undefined /*command*/
|
||||
];
|
||||
|
||||
export type SCMRawResourceSplice = [
|
||||
@@ -931,7 +940,7 @@ export interface MainThreadDebugServiceShape extends IDisposable {
|
||||
$acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void;
|
||||
$acceptDAError(handle: number, name: string, message: string, stack: string | undefined): void;
|
||||
$acceptDAExit(handle: number, code: number | undefined, signal: string | undefined): void;
|
||||
$registerDebugConfigurationProvider(type: string, triggerKind: DebugConfigurationProviderTriggerKind, hasProvideMethod: boolean, hasResolveMethod: boolean, hasResolve2Method: boolean, hasProvideDaMethod: boolean, handle: number): Promise<void>;
|
||||
$registerDebugConfigurationProvider(type: string, triggerKind: DebugConfigurationProviderTriggerKind, hasProvideMethod: boolean, hasResolveMethod: boolean, hasResolve2Method: boolean, handle: number): Promise<void>;
|
||||
$registerDebugAdapterDescriptorFactory(type: string, handle: number): Promise<void>;
|
||||
$unregisterDebugConfigurationProvider(handle: number): void;
|
||||
$unregisterDebugAdapterDescriptorFactory(handle: number): void;
|
||||
@@ -957,13 +966,12 @@ export interface MainThreadWindowShape extends IDisposable {
|
||||
}
|
||||
|
||||
export interface MainThreadTunnelServiceShape extends IDisposable {
|
||||
$openTunnel(tunnelOptions: TunnelOptions): Promise<TunnelDto | undefined>;
|
||||
$openTunnel(tunnelOptions: TunnelOptions, source: string | undefined): Promise<TunnelDto | undefined>;
|
||||
$closeTunnel(remote: { host: string, port: number }): Promise<void>;
|
||||
$getTunnels(): Promise<TunnelDescription[]>;
|
||||
$registerCandidateFinder(): Promise<void>;
|
||||
$setTunnelProvider(): Promise<void>;
|
||||
$setCandidateFilter(): Promise<void>;
|
||||
$tunnelServiceReady(): Promise<void>;
|
||||
$onFoundNewCandidates(candidates: { host: string, port: number, detail: string }[]): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadTimelineShape extends IDisposable {
|
||||
@@ -1017,10 +1025,10 @@ export interface ITextEditorAddData {
|
||||
options: IResolvedTextEditorConfiguration;
|
||||
selections: ISelection[];
|
||||
visibleRanges: IRange[];
|
||||
editorPosition: EditorViewColumn | undefined;
|
||||
editorPosition: EditorGroupColumn | undefined;
|
||||
}
|
||||
export interface ITextEditorPositionData {
|
||||
[id: string]: EditorViewColumn;
|
||||
[id: string]: EditorGroupColumn;
|
||||
}
|
||||
export interface IEditorPropertiesChangeData {
|
||||
options: IResolvedTextEditorConfiguration | null;
|
||||
@@ -1150,7 +1158,7 @@ export interface SourceTargetPair {
|
||||
|
||||
export interface ExtHostFileSystemEventServiceShape {
|
||||
$onFileEvent(events: FileSystemEvents): void;
|
||||
$onWillRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise<any>;
|
||||
$onWillRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[], undoRedoGroupId: number | undefined, timeout: number, token: CancellationToken): Promise<any>;
|
||||
$onDidRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[]): void;
|
||||
}
|
||||
|
||||
@@ -1318,7 +1326,7 @@ export interface IWorkspaceCellEditDto {
|
||||
export interface IWorkspaceEditDto {
|
||||
edits: Array<IWorkspaceFileEditDto | IWorkspaceTextEditDto | IWorkspaceCellEditDto>;
|
||||
|
||||
// todo@joh reject should go into rename
|
||||
// todo@jrieken reject should go into rename
|
||||
rejectReason?: string;
|
||||
}
|
||||
|
||||
@@ -1406,6 +1414,11 @@ export interface ILanguageWordDefinitionDto {
|
||||
regexFlags: string
|
||||
}
|
||||
|
||||
export interface ILinkedEditingRangesDto {
|
||||
ranges: IRange[];
|
||||
wordPattern?: IRegExpDto;
|
||||
}
|
||||
|
||||
export interface ExtHostLanguageFeaturesShape {
|
||||
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined>;
|
||||
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<ICodeLensListDto | undefined>;
|
||||
@@ -1418,7 +1431,7 @@ export interface ExtHostLanguageFeaturesShape {
|
||||
$provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.Hover | undefined>;
|
||||
$provideEvaluatableExpression(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.EvaluatableExpression | undefined>;
|
||||
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.DocumentHighlight[] | undefined>;
|
||||
$provideOnTypeRenameRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: IRegExpDto; } | undefined>;
|
||||
$provideLinkedEditingRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<ILinkedEditingRangesDto | undefined>;
|
||||
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise<ILocationDto[] | undefined>;
|
||||
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<ICodeActionListDto | undefined>;
|
||||
$resolveCodeAction(handle: number, id: ChainedCacheId, token: CancellationToken): Promise<IWorkspaceEditDto | undefined>;
|
||||
@@ -1613,7 +1626,6 @@ export interface ExtHostDebugServiceShape {
|
||||
$resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise<IConfig | null | undefined>;
|
||||
$resolveDebugConfigurationWithSubstitutedVariables(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise<IConfig | null | undefined>;
|
||||
$provideDebugConfigurations(handle: number, folder: UriComponents | undefined, token: CancellationToken): Promise<IConfig[]>;
|
||||
$legacyDebugAdapterExecutable(handle: number, folderUri: UriComponents | undefined): Promise<IAdapterDescriptor>; // TODO@AW legacy
|
||||
$provideDebugAdapter(handle: number, session: IDebugSessionDto): Promise<IAdapterDescriptor>;
|
||||
$acceptDebugSessionStarted(session: IDebugSessionDto): void;
|
||||
$acceptDebugSessionTerminated(session: IDebugSessionDto): void;
|
||||
@@ -1720,7 +1732,6 @@ export interface ExtHostNotebookShape {
|
||||
$resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise<void>;
|
||||
$executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void>;
|
||||
$cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void>;
|
||||
$executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise<void>;
|
||||
$saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise<boolean>;
|
||||
$saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise<boolean>;
|
||||
$backup(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string | undefined>;
|
||||
@@ -1749,10 +1760,8 @@ export interface MainThreadThemingShape extends IDisposable {
|
||||
}
|
||||
|
||||
export interface ExtHostTunnelServiceShape {
|
||||
$findCandidatePorts(): Promise<{ host: string, port: number, detail: string }[]>;
|
||||
$filterCandidates(candidates: { host: string, port: number, detail: string }[]): Promise<boolean[]>;
|
||||
$forwardPort(tunnelOptions: TunnelOptions): Promise<TunnelDto> | undefined;
|
||||
$closeTunnel(remote: { host: string, port: number }): Promise<void>;
|
||||
$forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<TunnelDto> | undefined;
|
||||
$closeTunnel(remote: { host: string, port: number }, silent?: boolean): Promise<void>;
|
||||
$onDidTunnelsChange(): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1760,6 +1769,28 @@ export interface ExtHostTimelineShape {
|
||||
$getTimeline(source: string, uri: UriComponents, options: TimelineOptions, token: CancellationToken, internalOptions?: InternalTimelineOptions): Promise<Timeline | undefined>;
|
||||
}
|
||||
|
||||
export const enum ExtHostTestingResource {
|
||||
Workspace,
|
||||
TextDocument
|
||||
}
|
||||
|
||||
export interface ExtHostTestingShape {
|
||||
$runTestsForProvider(req: RunTestForProviderRequest): Promise<RunTestsResult>;
|
||||
$subscribeToTests(resource: ExtHostTestingResource, uri: UriComponents): void;
|
||||
$unsubscribeFromTests(resource: ExtHostTestingResource, uri: UriComponents): void;
|
||||
|
||||
$acceptDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
|
||||
}
|
||||
|
||||
export interface MainThreadTestingShape {
|
||||
$registerTestProvider(id: string): void;
|
||||
$unregisterTestProvider(id: string): void;
|
||||
$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>;
|
||||
}
|
||||
|
||||
// --- proxy identifiers
|
||||
|
||||
export const MainContext = {
|
||||
@@ -1810,7 +1841,8 @@ export const MainContext = {
|
||||
MainThreadNodeProxy: createMainId<MainThreadNodeProxyShape>('MainThreadNodeProxy'),
|
||||
MainThreadTheming: createMainId<MainThreadThemingShape>('MainThreadTheming'),
|
||||
MainThreadTunnelService: createMainId<MainThreadTunnelServiceShape>('MainThreadTunnelService'),
|
||||
MainThreadTimeline: createMainId<MainThreadTimelineShape>('MainThreadTimeline')
|
||||
MainThreadTimeline: createMainId<MainThreadTimelineShape>('MainThreadTimeline'),
|
||||
MainThreadTesting: createMainId<MainThreadTestingShape>('MainThreadTesting'),
|
||||
};
|
||||
|
||||
export const ExtHostContext = {
|
||||
@@ -1854,5 +1886,6 @@ export const ExtHostContext = {
|
||||
ExtHostTheming: createMainId<ExtHostThemingShape>('ExtHostTheming'),
|
||||
ExtHostTunnelService: createMainId<ExtHostTunnelServiceShape>('ExtHostTunnelService'),
|
||||
ExtHostAuthentication: createMainId<ExtHostAuthenticationShape>('ExtHostAuthentication'),
|
||||
ExtHostTimeline: createMainId<ExtHostTimelineShape>('ExtHostTimeline')
|
||||
ExtHostTimeline: createMainId<ExtHostTimelineShape>('ExtHostTimeline'),
|
||||
ExtHostTesting: createMainId<ExtHostTestingShape>('ExtHostTesting'),
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import type * as vscode from 'vscode';
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import * as types from 'vs/workbench/api/common/extHostTypes';
|
||||
@@ -12,77 +12,17 @@ import { IRawColorInfo, IWorkspaceEditDto, ICallHierarchyItemDto, IIncomingCallD
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import * as search from 'vs/workbench/contrib/search/common/search';
|
||||
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { CustomCodeAction } from 'vs/workbench/api/common/extHostLanguageFeatures';
|
||||
import { ICommandsExecutor, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand, OpenIssueReporter, OpenIssueReporterArgs } from './apiCommands';
|
||||
import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ICommandsExecutor, RemoveFromRecentlyOpenedAPICommand, OpenIssueReporter, OpenIssueReporterArgs } from './apiCommands';
|
||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
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';
|
||||
|
||||
//#region --- NEW world
|
||||
|
||||
export class ApiCommandArgument<V, O = V> {
|
||||
|
||||
static readonly Uri = new ApiCommandArgument<URI>('uri', 'Uri of a text document', v => URI.isUri(v), v => v);
|
||||
static readonly Position = new ApiCommandArgument<types.Position, IPosition>('position', 'A position in a text document', v => types.Position.isPosition(v), typeConverters.Position.from);
|
||||
static readonly Range = new ApiCommandArgument<types.Range, IRange>('range', 'A range in a text document', v => types.Range.isRange(v), typeConverters.Range.from);
|
||||
|
||||
static readonly CallHierarchyItem = new ApiCommandArgument('item', 'A call hierarchy item', v => v instanceof types.CallHierarchyItem, typeConverters.CallHierarchyItem.to);
|
||||
|
||||
constructor(
|
||||
readonly name: string,
|
||||
readonly description: string,
|
||||
readonly validate: (v: V) => boolean,
|
||||
readonly convert: (v: V) => O
|
||||
) { }
|
||||
}
|
||||
|
||||
export class ApiCommandResult<V, O = V> {
|
||||
|
||||
constructor(
|
||||
readonly description: string,
|
||||
readonly convert: (v: V, apiArgs: any[]) => O
|
||||
) { }
|
||||
}
|
||||
|
||||
export class ApiCommand {
|
||||
|
||||
constructor(
|
||||
readonly id: string,
|
||||
readonly internalId: string,
|
||||
readonly description: string,
|
||||
readonly args: ApiCommandArgument<any, any>[],
|
||||
readonly result: ApiCommandResult<any, any>
|
||||
) { }
|
||||
|
||||
register(commands: ExtHostCommands): IDisposable {
|
||||
|
||||
return commands.registerCommand(false, this.id, async (...apiArgs) => {
|
||||
|
||||
const internalArgs = this.args.map((arg, i) => {
|
||||
if (!arg.validate(apiArgs[i])) {
|
||||
throw new Error(`Invalid argument '${arg.name}' when running '${this.id}', receieved: ${apiArgs[i]}`);
|
||||
}
|
||||
return arg.convert(apiArgs[i]);
|
||||
});
|
||||
|
||||
const internalResult = await commands.executeCommand(this.internalId, ...internalArgs);
|
||||
return this.result.convert(internalResult, apiArgs);
|
||||
}, undefined, this._getCommandHandlerDesc());
|
||||
}
|
||||
|
||||
private _getCommandHandlerDesc(): ICommandHandlerDescription {
|
||||
return {
|
||||
description: this.description,
|
||||
args: this.args,
|
||||
returns: this.result.description
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const newCommands: ApiCommand[] = [
|
||||
// -- document highlights
|
||||
new ApiCommand(
|
||||
@@ -189,7 +129,7 @@ const newCommands: ApiCommand[] = [
|
||||
// -- symbol search
|
||||
new ApiCommand(
|
||||
'vscode.executeWorkspaceSymbolProvider', '_executeWorkspaceSymbolProvider', 'Execute all workspace symbol providers.',
|
||||
[new ApiCommandArgument('query', 'Search string', v => typeof v === 'string', v => v)],
|
||||
[ApiCommandArgument.String.with('query', 'Search string')],
|
||||
new ApiCommandResult<[search.IWorkspaceSymbolProvider, search.IWorkspaceSymbol[]][], types.SymbolInformation[]>('A promise that resolves to an array of SymbolInformation-instances.', value => {
|
||||
const result: types.SymbolInformation[] = [];
|
||||
if (Array.isArray(value)) {
|
||||
@@ -219,7 +159,7 @@ const newCommands: ApiCommand[] = [
|
||||
// --- rename
|
||||
new ApiCommand(
|
||||
'vscode.executeDocumentRenameProvider', '_executeDocumentRenameProvider', 'Execute rename provider.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position, new ApiCommandArgument('newName', 'The new symbol name', v => typeof v === 'string', v => v)],
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position, ApiCommandArgument.String.with('newName', 'The new symbol name')],
|
||||
new ApiCommandResult<IWorkspaceEditDto, types.WorkspaceEdit | undefined>('A promise that resolves to a WorkspaceEdit.', value => {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
@@ -233,9 +173,169 @@ const newCommands: ApiCommand[] = [
|
||||
// --- links
|
||||
new ApiCommand(
|
||||
'vscode.executeLinkProvider', '_executeLinkProvider', 'Execute document link provider.',
|
||||
[ApiCommandArgument.Uri, new ApiCommandArgument('linkResolveCount', '(optional) Number of links that should be resolved, only when links are unresolved.', v => typeof v === 'number' || typeof v === 'undefined', v => v)],
|
||||
[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))
|
||||
)
|
||||
),
|
||||
// --- completions
|
||||
new ApiCommand(
|
||||
'vscode.executeCompletionItemProvider', '_executeCompletionItemProvider', 'Execute completion item provider.',
|
||||
[
|
||||
ApiCommandArgument.Uri,
|
||||
ApiCommandArgument.Position,
|
||||
ApiCommandArgument.String.with('triggerCharacter', 'Trigger completion when the user types the character, like `,` or `(`').optional(),
|
||||
ApiCommandArgument.Number.with('itemResolveCount', 'Number of completions to resolve (too large numbers slow down completions)').optional()
|
||||
],
|
||||
new ApiCommandResult<modes.CompletionList, vscode.CompletionList>('A promise that resolves to a CompletionList-instance.', (value, _args, converter) => {
|
||||
if (!value) {
|
||||
return new types.CompletionList([]);
|
||||
}
|
||||
const items = value.suggestions.map(suggestion => typeConverters.CompletionItem.to(suggestion, converter));
|
||||
return new types.CompletionList(items, value.incomplete);
|
||||
})
|
||||
),
|
||||
// --- signature help
|
||||
new ApiCommand(
|
||||
'vscode.executeSignatureHelpProvider', '_executeSignatureHelpProvider', 'Execute signature help provider.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position, ApiCommandArgument.String.with('triggerCharacter', 'Trigger signature help when the user types the character, like `,` or `(`').optional()],
|
||||
new ApiCommandResult<modes.SignatureHelp, vscode.SignatureHelp | undefined>('A promise that resolves to SignatureHelp.', value => {
|
||||
if (value) {
|
||||
return typeConverters.SignatureHelp.to(value);
|
||||
}
|
||||
return undefined;
|
||||
})
|
||||
),
|
||||
// --- code lens
|
||||
new ApiCommand(
|
||||
'vscode.executeCodeLensProvider', '_executeCodeLensProvider', 'Execute code lens provider.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Number.with('itemResolveCount', 'Number of lenses that should be resolved and returned. Will only return resolved lenses, will impact performance)').optional()],
|
||||
new ApiCommandResult<modes.CodeLens[], vscode.CodeLens[] | undefined>('A promise that resolves to an array of CodeLens-instances.', (value, _args, converter) => {
|
||||
return tryMapWith<modes.CodeLens, vscode.CodeLens>(item => {
|
||||
return new types.CodeLens(typeConverters.Range.to(item.range), item.command && converter.fromInternal(item.command));
|
||||
})(value);
|
||||
})
|
||||
),
|
||||
// --- code actions
|
||||
new ApiCommand(
|
||||
'vscode.executeCodeActionProvider', '_executeCodeActionProvider', 'Execute code action provider.',
|
||||
[
|
||||
ApiCommandArgument.Uri,
|
||||
new ApiCommandArgument('rangeOrSelection', 'Range in a text document. Some refactoring provider requires Selection object.', v => types.Range.isRange(v), v => types.Selection.isSelection(v) ? typeConverters.Selection.from(v) : typeConverters.Range.from(v)),
|
||||
ApiCommandArgument.String.with('kind', 'Code action kind to return code actions for').optional(),
|
||||
ApiCommandArgument.Number.with('itemResolveCount', 'Number of code actions to resolve (too large numbers slow down code actions)').optional()
|
||||
],
|
||||
new ApiCommandResult<CustomCodeAction[], (vscode.CodeAction | vscode.Command | undefined)[] | undefined>('A promise that resolves to an array of Command-instances.', (value, _args, converter) => {
|
||||
return tryMapWith<CustomCodeAction, vscode.CodeAction | vscode.Command | undefined>((codeAction) => {
|
||||
if (codeAction._isSynthetic) {
|
||||
if (!codeAction.command) {
|
||||
throw new Error('Synthetic code actions must have a command');
|
||||
}
|
||||
return converter.fromInternal(codeAction.command);
|
||||
} else {
|
||||
const ret = new types.CodeAction(
|
||||
codeAction.title,
|
||||
codeAction.kind ? new types.CodeActionKind(codeAction.kind) : undefined
|
||||
);
|
||||
if (codeAction.edit) {
|
||||
ret.edit = typeConverters.WorkspaceEdit.to(codeAction.edit);
|
||||
}
|
||||
if (codeAction.command) {
|
||||
ret.command = converter.fromInternal(codeAction.command);
|
||||
}
|
||||
ret.isPreferred = codeAction.isPreferred;
|
||||
return ret;
|
||||
}
|
||||
})(value);
|
||||
})
|
||||
),
|
||||
// --- colors
|
||||
new ApiCommand(
|
||||
'vscode.executeDocumentColorProvider', '_executeDocumentColorProvider', 'Execute document color provider.',
|
||||
[ApiCommandArgument.Uri],
|
||||
new ApiCommandResult<IRawColorInfo[], vscode.ColorInformation[]>('A promise that resolves to an array of ColorInformation objects.', result => {
|
||||
if (result) {
|
||||
return result.map(ci => new types.ColorInformation(typeConverters.Range.to(ci.range), typeConverters.Color.to(ci.color)));
|
||||
}
|
||||
return [];
|
||||
})
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.executeColorPresentationProvider', '_executeColorPresentationProvider', 'Execute color presentation provider.',
|
||||
[
|
||||
new ApiCommandArgument<types.Color, [number, number, number, number]>('color', 'The color to show and insert', v => v instanceof types.Color, typeConverters.Color.from),
|
||||
new ApiCommandArgument<{ uri: URI, range: types.Range; }, { uri: URI, range: IRange; }>('context', 'Context object with uri and range', _v => true, v => ({ uri: v.uri, range: typeConverters.Range.from(v.range) })),
|
||||
],
|
||||
new ApiCommandResult<modes.IColorPresentation[], types.ColorPresentation[]>('A promise that resolves to an array of ColorPresentation objects.', result => {
|
||||
if (result) {
|
||||
return result.map(typeConverters.ColorPresentation.to);
|
||||
}
|
||||
return [];
|
||||
})
|
||||
),
|
||||
// --- 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 ApiCommandResult<{
|
||||
viewType: string;
|
||||
displayName: string;
|
||||
options: { transientOutputs: boolean; transientMetadata: TransientMetadata };
|
||||
filenamePattern: (string | types.RelativePattern | { include: string | types.RelativePattern, exclude: string | types.RelativePattern })[]
|
||||
}[], {
|
||||
viewType: string;
|
||||
displayName: string;
|
||||
filenamePattern: vscode.NotebookFilenamePattern[];
|
||||
options: vscode.NotebookDocumentContentOptions;
|
||||
}[] | undefined>('A promise that resolves to an array of NotebookContentProvider static info objects.', tryMapWith(item => {
|
||||
return {
|
||||
viewType: item.viewType,
|
||||
displayName: item.displayName,
|
||||
options: { transientOutputs: item.options.transientOutputs, transientMetadata: item.options.transientMetadata },
|
||||
filenamePattern: item.filenamePattern.map(pattern => typeConverters.NotebookExclusiveDocumentPattern.to(pattern))
|
||||
};
|
||||
}))
|
||||
),
|
||||
// --- open'ish commands
|
||||
new ApiCommand(
|
||||
'vscode.open', '_workbench.open', 'Opens the provided resource in the editor. Can be a text or binary file, or a http(s) url. If you need more control over the options for opening a text file, use vscode.window.showTextDocument instead.',
|
||||
[
|
||||
ApiCommandArgument.Uri,
|
||||
new ApiCommandArgument<vscode.ViewColumn | typeConverters.TextEditorOpenOptions | undefined, [number?, ITextEditorOptions?] | undefined>('columnOrOptions', 'Either the column in which to open or editor options, see vscode.TextDocumentShowOptions',
|
||||
v => v === undefined || typeof v === 'number' || typeof v === 'object',
|
||||
v => !v ? v : typeof v === 'number' ? [v, undefined] : [typeConverters.ViewColumn.from(v.viewColumn), typeConverters.TextEditorOpenOptions.from(v)]
|
||||
).optional(),
|
||||
ApiCommandArgument.String.with('label', '').optional()
|
||||
],
|
||||
ApiCommandResult.Void
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.openWith', '_workbench.openWith', 'Opens the provided resource with a specific editor.',
|
||||
[
|
||||
ApiCommandArgument.Uri.with('resource', 'Resource to open'),
|
||||
ApiCommandArgument.String.with('viewId', 'Custom editor view id or \'default\' to use VS Code\'s default editor'),
|
||||
new ApiCommandArgument<vscode.ViewColumn | typeConverters.TextEditorOpenOptions | undefined, [number?, ITextEditorOptions?] | undefined>('columnOrOptions', 'Either the column in which to open or editor options, see vscode.TextDocumentShowOptions',
|
||||
v => v === undefined || typeof v === 'number' || typeof v === 'object',
|
||||
v => !v ? v : typeof v === 'number' ? [v, undefined] : [typeConverters.ViewColumn.from(v.viewColumn), typeConverters.TextEditorOpenOptions.from(v)],
|
||||
).optional()
|
||||
],
|
||||
ApiCommandResult.Void
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.diff', '_workbench.diff', 'Opens the provided resources in the diff editor to compare their contents.',
|
||||
[
|
||||
ApiCommandArgument.Uri.with('left', 'Left-hand side resource of the diff editor'),
|
||||
ApiCommandArgument.Uri.with('right', 'Rigth-hand side resource of the diff editor'),
|
||||
ApiCommandArgument.String.with('title', 'Human readable title for the diff editor').optional(),
|
||||
new ApiCommandArgument<typeConverters.TextEditorOpenOptions | undefined, [number?, ITextEditorOptions?] | undefined>('columnOrOptions', 'Either the column in which to open or editor options, see vscode.TextDocumentShowOptions',
|
||||
v => v === undefined || typeof v === 'object',
|
||||
v => v && [typeConverters.ViewColumn.from(v.viewColumn), typeConverters.TextEditorOpenOptions.from(v)]
|
||||
).optional(),
|
||||
],
|
||||
ApiCommandResult.Void
|
||||
),
|
||||
];
|
||||
|
||||
//#endregion
|
||||
@@ -246,7 +346,7 @@ const newCommands: ApiCommand[] = [
|
||||
export class ExtHostApiCommands {
|
||||
|
||||
static register(commands: ExtHostCommands) {
|
||||
newCommands.forEach(command => command.register(commands));
|
||||
newCommands.forEach(commands.registerApiCommand, commands);
|
||||
return new ExtHostApiCommands(commands).registerCommands();
|
||||
}
|
||||
|
||||
@@ -258,66 +358,10 @@ export class ExtHostApiCommands {
|
||||
}
|
||||
|
||||
registerCommands() {
|
||||
this._register('vscode.executeSignatureHelpProvider', this._executeSignatureHelpProvider, {
|
||||
description: 'Execute signature help provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
|
||||
{ name: 'triggerCharacter', description: '(optional) Trigger signature help when the user types the character, like `,` or `(`', constraint: (value: any) => value === undefined || typeof value === 'string' }
|
||||
],
|
||||
returns: 'A promise that resolves to SignatureHelp.'
|
||||
});
|
||||
this._register('vscode.executeCompletionItemProvider', this._executeCompletionItemProvider, {
|
||||
description: 'Execute completion item provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
|
||||
{ name: 'triggerCharacter', description: '(optional) Trigger completion when the user types the character, like `,` or `(`', constraint: (value: any) => value === undefined || typeof value === 'string' },
|
||||
{ name: 'itemResolveCount', description: '(optional) Number of completions to resolve (too large numbers slow down completions)', constraint: (value: any) => value === undefined || typeof value === 'number' }
|
||||
],
|
||||
returns: 'A promise that resolves to a CompletionList-instance.'
|
||||
});
|
||||
this._register('vscode.executeCodeActionProvider', this._executeCodeActionProvider, {
|
||||
description: 'Execute code action provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'rangeOrSelection', description: 'Range in a text document. Some refactoring provider requires Selection object.', constraint: types.Range },
|
||||
{ name: 'kind', description: '(optional) Code action kind to return code actions for', constraint: (value: any) => !value || typeof value.value === 'string' },
|
||||
{ name: 'itemResolveCount', description: '(optional) Number of code actions to resolve (too large numbers slow down code actions)', constraint: (value: any) => value === undefined || typeof value === 'number' }
|
||||
|
||||
],
|
||||
returns: 'A promise that resolves to an array of Command-instances.'
|
||||
});
|
||||
this._register('vscode.executeCodeLensProvider', this._executeCodeLensProvider, {
|
||||
description: 'Execute CodeLens provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'itemResolveCount', description: '(optional) Number of lenses that should be resolved and returned. Will only return resolved lenses, will impact performance)', constraint: (value: any) => value === undefined || typeof value === 'number' }
|
||||
],
|
||||
returns: 'A promise that resolves to an array of CodeLens-instances.'
|
||||
});
|
||||
|
||||
this._register('vscode.executeDocumentColorProvider', this._executeDocumentColorProvider, {
|
||||
description: 'Execute document color provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
],
|
||||
returns: 'A promise that resolves to an array of ColorInformation objects.'
|
||||
});
|
||||
this._register('vscode.executeColorPresentationProvider', this._executeColorPresentationProvider, {
|
||||
description: 'Execute color presentation provider.',
|
||||
args: [
|
||||
{ name: 'color', description: 'The color to show and insert', constraint: types.Color },
|
||||
{ name: 'context', description: 'Context object with uri and range' }
|
||||
],
|
||||
returns: 'A promise that resolves to an array of ColorPresentation objects.'
|
||||
});
|
||||
|
||||
this._register('vscode.resolveNotebookContentProviders', this._resolveNotebookContentProviders, {
|
||||
description: 'Resolve Notebook Content Providers',
|
||||
args: [],
|
||||
returns: 'A promise that resolves to an array of NotebookContentProvider static info objects.'
|
||||
});
|
||||
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// The following commands are registered on both sides separately.
|
||||
@@ -333,32 +377,6 @@ export class ExtHostApiCommands {
|
||||
};
|
||||
};
|
||||
|
||||
this._register(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute), {
|
||||
description: 'Open a folder or workspace in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder/workspace unless the newWindow parameter is set to true.',
|
||||
args: [
|
||||
{ name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: (value: any) => value === undefined || URI.isUri(value) },
|
||||
{ name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Whether the opened URI will appear in the \'Open Recent\' list. Defaults to true. Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' }
|
||||
]
|
||||
});
|
||||
|
||||
this._register(DiffAPICommand.ID, adjustHandler(DiffAPICommand.execute), {
|
||||
description: 'Opens the provided resources in the diff editor to compare their contents.',
|
||||
args: [
|
||||
{ name: 'left', description: 'Left-hand side resource of the diff editor', constraint: URI },
|
||||
{ name: 'right', description: 'Right-hand side resource of the diff editor', constraint: URI },
|
||||
{ name: 'title', description: '(optional) Human readable title for the diff editor', constraint: (v: any) => v === undefined || typeof v === 'string' },
|
||||
{ name: 'options', description: '(optional) Editor options, see vscode.TextDocumentShowOptions' }
|
||||
]
|
||||
});
|
||||
|
||||
this._register(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute), {
|
||||
description: 'Opens the provided resource in the editor. Can be a text or binary file, or a http(s) url. If you need more control over the options for opening a text file, use vscode.window.showTextDocument instead.',
|
||||
args: [
|
||||
{ name: 'resource', description: 'Resource to open', constraint: URI },
|
||||
{ name: 'columnOrOptions', description: '(optional) Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', constraint: (v: any) => v === undefined || typeof v === 'number' || typeof v === 'object' }
|
||||
]
|
||||
});
|
||||
|
||||
this._register(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute), {
|
||||
description: 'Removes an entry with the given path from the recently opened list.',
|
||||
args: [
|
||||
@@ -366,13 +384,6 @@ export class ExtHostApiCommands {
|
||||
]
|
||||
});
|
||||
|
||||
this._register(SetEditorLayoutAPICommand.ID, adjustHandler(SetEditorLayoutAPICommand.execute), {
|
||||
description: 'Sets the editor layout. The layout is described as object with an initial (optional) orientation (0 = horizontal, 1 = vertical) and an array of editor groups within. Each editor group can have a size and another array of editor groups that will be laid out orthogonal to the orientation. If editor group sizes are provided, their sum must be 1 to be applied per row or column. Example for a 2x2 grid: `{ orientation: 0, groups: [{ groups: [{}, {}], size: 0.5 }, { groups: [{}, {}], size: 0.5 }] }`',
|
||||
args: [
|
||||
{ name: 'layout', description: 'The editor layout to set.', constraint: (value: EditorGroupLayout) => typeof value === 'object' && Array.isArray(value.groups) }
|
||||
]
|
||||
});
|
||||
|
||||
this._register(OpenIssueReporter.ID, adjustHandler(OpenIssueReporter.execute), {
|
||||
description: 'Opens the issue reporter with the provided extension id as the selected source',
|
||||
args: [
|
||||
@@ -383,132 +394,16 @@ export class ExtHostApiCommands {
|
||||
|
||||
// --- command impl
|
||||
|
||||
/**
|
||||
* @deprecated use the ApiCommand instead
|
||||
*/
|
||||
private _register(id: string, handler: (...args: any[]) => any, description?: ICommandHandlerDescription): void {
|
||||
const disposable = this._commands.registerCommand(false, id, handler, this, description);
|
||||
this._disposables.add(disposable);
|
||||
}
|
||||
|
||||
private _executeSignatureHelpProvider(resource: URI, position: types.Position, triggerCharacter: string): Promise<types.SignatureHelp | undefined> {
|
||||
const args = {
|
||||
resource,
|
||||
position: position && typeConverters.Position.from(position),
|
||||
triggerCharacter
|
||||
};
|
||||
return this._commands.executeCommand<modes.SignatureHelp>('_executeSignatureHelpProvider', args).then(value => {
|
||||
if (value) {
|
||||
return typeConverters.SignatureHelp.to(value);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeCompletionItemProvider(resource: URI, position: types.Position, triggerCharacter: string, maxItemsToResolve: number): Promise<types.CompletionList | undefined> {
|
||||
const args = {
|
||||
resource,
|
||||
position: position && typeConverters.Position.from(position),
|
||||
triggerCharacter,
|
||||
maxItemsToResolve
|
||||
};
|
||||
return this._commands.executeCommand<modes.CompletionList>('_executeCompletionItemProvider', args).then(result => {
|
||||
if (result) {
|
||||
const items = result.suggestions.map(suggestion => typeConverters.CompletionItem.to(suggestion, this._commands.converter));
|
||||
return new types.CompletionList(items, result.incomplete);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeDocumentColorProvider(resource: URI): Promise<types.ColorInformation[]> {
|
||||
const args = {
|
||||
resource
|
||||
};
|
||||
return this._commands.executeCommand<IRawColorInfo[]>('_executeDocumentColorProvider', args).then(result => {
|
||||
if (result) {
|
||||
return result.map(ci => ({ range: typeConverters.Range.to(ci.range), color: typeConverters.Color.to(ci.color) }));
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
private _executeColorPresentationProvider(color: types.Color, context: { uri: URI, range: types.Range; }): Promise<types.ColorPresentation[]> {
|
||||
const args = {
|
||||
resource: context.uri,
|
||||
color: typeConverters.Color.from(color),
|
||||
range: typeConverters.Range.from(context.range),
|
||||
};
|
||||
return this._commands.executeCommand<modes.IColorPresentation[]>('_executeColorPresentationProvider', args).then(result => {
|
||||
if (result) {
|
||||
return result.map(typeConverters.ColorPresentation.to);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private _executeCodeActionProvider(resource: URI, rangeOrSelection: types.Range | types.Selection, kind?: string, itemResolveCount?: number): Promise<(vscode.CodeAction | vscode.Command | undefined)[] | undefined> {
|
||||
const args = {
|
||||
resource,
|
||||
rangeOrSelection: types.Selection.isSelection(rangeOrSelection)
|
||||
? typeConverters.Selection.from(rangeOrSelection)
|
||||
: typeConverters.Range.from(rangeOrSelection),
|
||||
kind,
|
||||
itemResolveCount,
|
||||
};
|
||||
return this._commands.executeCommand<CustomCodeAction[]>('_executeCodeActionProvider', args)
|
||||
.then(tryMapWith(codeAction => {
|
||||
if (codeAction._isSynthetic) {
|
||||
if (!codeAction.command) {
|
||||
throw new Error('Synthetic code actions must have a command');
|
||||
}
|
||||
return this._commands.converter.fromInternal(codeAction.command);
|
||||
} else {
|
||||
const ret = new types.CodeAction(
|
||||
codeAction.title,
|
||||
codeAction.kind ? new types.CodeActionKind(codeAction.kind) : undefined
|
||||
);
|
||||
if (codeAction.edit) {
|
||||
ret.edit = typeConverters.WorkspaceEdit.to(codeAction.edit);
|
||||
}
|
||||
if (codeAction.command) {
|
||||
ret.command = this._commands.converter.fromInternal(codeAction.command);
|
||||
}
|
||||
ret.isPreferred = codeAction.isPreferred;
|
||||
return ret;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private _executeCodeLensProvider(resource: URI, itemResolveCount: number): Promise<vscode.CodeLens[] | undefined> {
|
||||
const args = { resource, itemResolveCount };
|
||||
return this._commands.executeCommand<modes.CodeLens[]>('_executeCodeLensProvider', args)
|
||||
.then(tryMapWith(item => {
|
||||
return new types.CodeLens(
|
||||
typeConverters.Range.to(item.range),
|
||||
item.command ? this._commands.converter.fromInternal(item.command) : undefined);
|
||||
}));
|
||||
}
|
||||
|
||||
private _resolveNotebookContentProviders(): Promise<{
|
||||
viewType: string;
|
||||
displayName: string;
|
||||
filenamePattern: vscode.NotebookFilenamePattern[];
|
||||
options: vscode.NotebookDocumentContentOptions;
|
||||
}[] | undefined> {
|
||||
return this._commands.executeCommand<{
|
||||
viewType: string;
|
||||
displayName: string;
|
||||
options: { transientOutputs: boolean; transientMetadata: TransientMetadata };
|
||||
filenamePattern: (string | types.RelativePattern | { include: string | types.RelativePattern, exclude: string | types.RelativePattern })[]
|
||||
}[]>('_resolveNotebookContentProvider')
|
||||
.then(tryMapWith(item => {
|
||||
return {
|
||||
viewType: item.viewType,
|
||||
displayName: item.displayName,
|
||||
options: { transientOutputs: item.options.transientOutputs, transientMetadata: item.options.transientMetadata },
|
||||
filenamePattern: item.filenamePattern.map(pattern => typeConverters.NotebookExclusiveDocumentPattern.to(pattern))
|
||||
};
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function tryMapWith<T, R>(f: (x: T) => R) {
|
||||
|
||||
@@ -14,12 +14,13 @@ import * as modes from 'vs/editor/common/modes';
|
||||
import type * as vscode from 'vscode';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { ISelection } from 'vs/editor/common/core/selection';
|
||||
|
||||
interface CommandHandler {
|
||||
callback: Function;
|
||||
@@ -36,18 +37,32 @@ export class ExtHostCommands implements ExtHostCommandsShape {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _commands = new Map<string, CommandHandler>();
|
||||
private readonly _apiCommands = new Map<string, ApiCommand>();
|
||||
|
||||
private readonly _proxy: MainThreadCommandsShape;
|
||||
private readonly _converter: CommandsConverter;
|
||||
private readonly _logService: ILogService;
|
||||
private readonly _argumentProcessors: ArgumentProcessor[];
|
||||
|
||||
readonly converter: CommandsConverter;
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@ILogService logService: ILogService
|
||||
) {
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadCommands);
|
||||
this._logService = logService;
|
||||
this._converter = new CommandsConverter(this, logService);
|
||||
this.converter = new CommandsConverter(
|
||||
this,
|
||||
id => {
|
||||
// API commands that have no return type (void) can be
|
||||
// converted to their internal command and don't need
|
||||
// any indirection commands
|
||||
const candidate = this._apiCommands.get(id);
|
||||
return candidate?.result === ApiCommandResult.Void
|
||||
? candidate : undefined;
|
||||
},
|
||||
logService
|
||||
);
|
||||
this._argumentProcessors = [
|
||||
{
|
||||
processArgument(a) {
|
||||
@@ -77,14 +92,38 @@ export class ExtHostCommands implements ExtHostCommandsShape {
|
||||
];
|
||||
}
|
||||
|
||||
get converter(): CommandsConverter {
|
||||
return this._converter;
|
||||
}
|
||||
|
||||
registerArgumentProcessor(processor: ArgumentProcessor): void {
|
||||
this._argumentProcessors.push(processor);
|
||||
}
|
||||
|
||||
registerApiCommand(apiCommand: ApiCommand): extHostTypes.Disposable {
|
||||
|
||||
|
||||
const registration = this.registerCommand(false, apiCommand.id, async (...apiArgs) => {
|
||||
|
||||
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]}`);
|
||||
}
|
||||
return arg.convert(apiArgs[i]);
|
||||
});
|
||||
|
||||
const internalResult = await this.executeCommand(apiCommand.internalId, ...internalArgs);
|
||||
return apiCommand.result.convert(internalResult, apiArgs, this.converter);
|
||||
}, undefined, {
|
||||
description: apiCommand.description,
|
||||
args: apiCommand.args,
|
||||
returns: apiCommand.result.description
|
||||
});
|
||||
|
||||
this._apiCommands.set(apiCommand.id, apiCommand);
|
||||
|
||||
return new extHostTypes.Disposable(() => {
|
||||
registration.dispose();
|
||||
this._apiCommands.delete(apiCommand.id);
|
||||
});
|
||||
}
|
||||
|
||||
registerCommand(global: boolean, id: string, callback: <T>(...args: any[]) => T | Thenable<T>, thisArg?: any, description?: ICommandHandlerDescription): extHostTypes.Disposable {
|
||||
this._logService.trace('ExtHostCommands#registerCommand', id);
|
||||
|
||||
@@ -214,6 +253,8 @@ export class ExtHostCommands implements ExtHostCommandsShape {
|
||||
}
|
||||
}
|
||||
|
||||
export interface IExtHostCommands extends ExtHostCommands { }
|
||||
export const IExtHostCommands = createDecorator<IExtHostCommands>('IExtHostCommands');
|
||||
|
||||
export class CommandsConverter {
|
||||
|
||||
@@ -224,6 +265,7 @@ export class CommandsConverter {
|
||||
// --- conversion between internal and api commands
|
||||
constructor(
|
||||
private readonly _commands: ExtHostCommands,
|
||||
private readonly _lookupApiCommand: (id: string) => ApiCommand | undefined,
|
||||
private readonly _logService: ILogService
|
||||
) {
|
||||
this._delegatingCommandId = `_vscode_delegate_cmd_${Date.now().toString(36)}`;
|
||||
@@ -245,7 +287,20 @@ export class CommandsConverter {
|
||||
tooltip: command.tooltip
|
||||
};
|
||||
|
||||
if (command.command && isNonEmptyArray(command.arguments)) {
|
||||
if (!command.command) {
|
||||
// falsy command id -> return converted command but don't attempt any
|
||||
// argument or API-command dance since this command won't run anyways
|
||||
return result;
|
||||
}
|
||||
|
||||
const apiCommand = this._lookupApiCommand(command.command);
|
||||
if (apiCommand) {
|
||||
// API command with return-value can be converted inplace
|
||||
result.id = apiCommand.internalId;
|
||||
result.arguments = apiCommand.args.map((arg, i) => arg.convert(command.arguments && command.arguments[i]));
|
||||
|
||||
|
||||
} else if (isNonEmptyArray(command.arguments)) {
|
||||
// we have a contributed command with arguments. that
|
||||
// means we don't want to send the arguments around
|
||||
|
||||
@@ -293,5 +348,55 @@ export class CommandsConverter {
|
||||
|
||||
}
|
||||
|
||||
export interface IExtHostCommands extends ExtHostCommands { }
|
||||
export const IExtHostCommands = createDecorator<IExtHostCommands>('IExtHostCommands');
|
||||
|
||||
export class ApiCommandArgument<V, O = V> {
|
||||
|
||||
static readonly Uri = new ApiCommandArgument<URI>('uri', 'Uri of a text document', v => URI.isUri(v), v => v);
|
||||
static readonly Position = new ApiCommandArgument<extHostTypes.Position, IPosition>('position', 'A position in a text document', v => extHostTypes.Position.isPosition(v), extHostTypeConverter.Position.from);
|
||||
static readonly Range = new ApiCommandArgument<extHostTypes.Range, IRange>('range', 'A range in a text document', v => extHostTypes.Range.isRange(v), extHostTypeConverter.Range.from);
|
||||
static readonly Selection = new ApiCommandArgument<extHostTypes.Selection, ISelection>('selection', 'A selection in a text document', v => extHostTypes.Selection.isSelection(v), extHostTypeConverter.Selection.from);
|
||||
static readonly Number = new ApiCommandArgument<number>('number', '', v => typeof v === 'number', v => v);
|
||||
static readonly String = new ApiCommandArgument<string>('string', '', v => typeof v === 'string', v => v);
|
||||
|
||||
static readonly CallHierarchyItem = new ApiCommandArgument('item', 'A call hierarchy item', v => v instanceof extHostTypes.CallHierarchyItem, extHostTypeConverter.CallHierarchyItem.to);
|
||||
|
||||
constructor(
|
||||
readonly name: string,
|
||||
readonly description: string,
|
||||
readonly validate: (v: V) => boolean,
|
||||
readonly convert: (v: V) => O
|
||||
) { }
|
||||
|
||||
optional(): ApiCommandArgument<V | undefined | null, O | undefined | null> {
|
||||
return new ApiCommandArgument(
|
||||
this.name, `(optional) ${this.description}`,
|
||||
value => value === undefined || value === null || this.validate(value),
|
||||
value => value === undefined ? undefined : value === null ? null : this.convert(value)
|
||||
);
|
||||
}
|
||||
|
||||
with(name: string | undefined, description: string | undefined): ApiCommandArgument<V, O> {
|
||||
return new ApiCommandArgument(name ?? this.name, description ?? this.description, this.validate, this.convert);
|
||||
}
|
||||
}
|
||||
|
||||
export class ApiCommandResult<V, O = V> {
|
||||
|
||||
static readonly Void = new ApiCommandResult<void, void>('no result', v => v);
|
||||
|
||||
constructor(
|
||||
readonly description: string,
|
||||
readonly convert: (v: V, apiArgs: any[], cmdConverter: CommandsConverter) => O
|
||||
) { }
|
||||
}
|
||||
|
||||
export class ApiCommand {
|
||||
|
||||
constructor(
|
||||
readonly id: string,
|
||||
readonly internalId: string,
|
||||
readonly description: string,
|
||||
readonly args: ApiCommandArgument<any, any>[],
|
||||
readonly result: ApiCommandResult<any, any>
|
||||
) { }
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import { ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels';
|
||||
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
import { EditorGroupColumn } from 'vs/workbench/common/editor';
|
||||
import type * as vscode from 'vscode';
|
||||
import { Cache } from './cache';
|
||||
import * as extHostProtocol from './extHost.protocol';
|
||||
@@ -252,7 +252,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
|
||||
handle: extHostProtocol.WebviewHandle,
|
||||
viewType: string,
|
||||
title: string,
|
||||
position: EditorViewColumn,
|
||||
position: EditorGroupColumn,
|
||||
options: modes.IWebviewOptions & modes.IWebviewPanelOptions,
|
||||
cancellation: CancellationToken,
|
||||
): Promise<void> {
|
||||
|
||||
@@ -23,7 +23,6 @@ import { ExtHostConfigProvider, IExtHostConfiguration } from '../common/extHostC
|
||||
import { convertToVSCPaths, convertToDAPaths, isDebuggerMainContribution } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import { ISignService } from 'vs/platform/sign/common/sign';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
@@ -97,7 +96,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
|
||||
private readonly _onDidChangeBreakpoints: Emitter<vscode.BreakpointsChangeEvent>;
|
||||
|
||||
private _aexCommands: Map<string, string>;
|
||||
private _debugAdapters: Map<number, IDebugAdapter>;
|
||||
private _debugAdaptersTrackers: Map<number, vscode.DebugAdapterTracker>;
|
||||
|
||||
@@ -105,14 +103,12 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
|
||||
private _signService: ISignService | undefined;
|
||||
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpcService: IExtHostRpcService,
|
||||
@IExtHostWorkspace private _workspaceService: IExtHostWorkspace,
|
||||
@IExtHostWorkspace protected _workspaceService: IExtHostWorkspace,
|
||||
@IExtHostExtensionService private _extensionService: IExtHostExtensionService,
|
||||
@IExtHostDocumentsAndEditors private _editorsService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration protected _configurationService: IExtHostConfiguration,
|
||||
@IExtHostCommands private _commandService: IExtHostCommands
|
||||
) {
|
||||
this._configProviderHandleCounter = 0;
|
||||
this._configProviders = [];
|
||||
@@ -123,7 +119,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
this._trackerFactoryHandleCounter = 0;
|
||||
this._trackerFactories = [];
|
||||
|
||||
this._aexCommands = new Map();
|
||||
this._debugAdapters = new Map();
|
||||
this._debugAdaptersTrackers = new Map();
|
||||
|
||||
@@ -182,7 +177,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
private registerAllDebugTypes(extensionRegistry: ExtensionDescriptionRegistry) {
|
||||
|
||||
const debugTypes: string[] = [];
|
||||
this._aexCommands.clear();
|
||||
|
||||
for (const ed of extensionRegistry.getAllExtensionDescriptions()) {
|
||||
if (ed.contributes) {
|
||||
@@ -191,9 +185,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
for (const dbg of debuggers) {
|
||||
if (isDebuggerMainContribution(dbg)) {
|
||||
debugTypes.push(dbg.type);
|
||||
if (dbg.adapterExecutableCommand) {
|
||||
this._aexCommands.set(dbg.type, dbg.adapterExecutableCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,10 +303,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
return new Disposable(() => { });
|
||||
}
|
||||
|
||||
if (provider.debugAdapterExecutable) {
|
||||
console.error('DebugConfigurationProvider.debugAdapterExecutable is deprecated and will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.');
|
||||
}
|
||||
|
||||
const handle = this._configProviderHandleCounter++;
|
||||
this._configProviders.push({ type, handle, provider });
|
||||
|
||||
@@ -323,7 +310,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
!!provider.provideDebugConfigurations,
|
||||
!!provider.resolveDebugConfiguration,
|
||||
!!provider.resolveDebugConfigurationWithSubstitutedVariables,
|
||||
!!provider.debugAdapterExecutable, // TODO@AW: deprecated
|
||||
handle);
|
||||
|
||||
return new Disposable(() => {
|
||||
@@ -651,26 +637,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
});
|
||||
}
|
||||
|
||||
// TODO@AW deprecated and legacy
|
||||
public $legacyDebugAdapterExecutable(configProviderHandle: number, folderUri: UriComponents | undefined): Promise<IAdapterDescriptor> {
|
||||
return asPromise(async () => {
|
||||
const provider = this.getConfigProviderByHandle(configProviderHandle);
|
||||
if (!provider) {
|
||||
throw new Error('no DebugConfigurationProvider found');
|
||||
}
|
||||
if (!provider.debugAdapterExecutable) {
|
||||
throw new Error('DebugConfigurationProvider has no method debugAdapterExecutable');
|
||||
}
|
||||
const folder = await this.getFolder(folderUri);
|
||||
return provider.debugAdapterExecutable(folder, CancellationToken.None);
|
||||
}).then(executable => {
|
||||
if (!executable) {
|
||||
throw new Error('nothing returned from DebugConfigurationProvider.debugAdapterExecutable');
|
||||
}
|
||||
return this.convertToDto(executable);
|
||||
});
|
||||
}
|
||||
|
||||
public async $provideDebugAdapter(adapterFactoryHandle: number, sessionDto: IDebugSessionDto): Promise<IAdapterDescriptor> {
|
||||
const adapterDescriptorFactory = this.getAdapterDescriptorFactoryByHandle(adapterFactoryHandle);
|
||||
if (!adapterDescriptorFactory) {
|
||||
@@ -830,18 +796,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
return Promise.resolve(new DebugAdapterServer(serverPort));
|
||||
}
|
||||
|
||||
// TODO@AW legacy
|
||||
const pair = this._configProviders.filter(p => p.type === session.type).pop();
|
||||
if (pair && pair.provider.debugAdapterExecutable) {
|
||||
const func = pair.provider.debugAdapterExecutable;
|
||||
return asPromise(() => func(session.workspaceFolder, CancellationToken.None)).then(executable => {
|
||||
if (executable) {
|
||||
return executable;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
if (adapterDescriptorFactory) {
|
||||
const extensionRegistry = await this._extensionService.getExtensionRegistry();
|
||||
return asPromise(() => adapterDescriptorFactory.createDebugAdapterDescriptor(session, this.daExecutableFromPackage(session, extensionRegistry))).then(daDescriptor => {
|
||||
@@ -852,17 +806,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
});
|
||||
}
|
||||
|
||||
// try deprecated command based extension API "adapterExecutableCommand" to determine the executable
|
||||
// TODO@AW legacy
|
||||
const aex = this._aexCommands.get(session.type);
|
||||
if (aex) {
|
||||
const folder = session.workspaceFolder;
|
||||
const rootFolder = folder ? folder.uri.toString() : undefined;
|
||||
return this._commandService.executeCommand(aex, rootFolder).then((ae: any) => {
|
||||
return new DebugAdapterExecutable(ae.command, ae.args || []);
|
||||
});
|
||||
}
|
||||
|
||||
// fallback: use executable information from package.json
|
||||
const extensionRegistry = await this._extensionService.getExtensionRegistry();
|
||||
return Promise.resolve(this.daExecutableFromPackage(session, extensionRegistry));
|
||||
@@ -987,7 +930,7 @@ export class ExtHostDebugConsole implements vscode.DebugConsole {
|
||||
|
||||
export class ExtHostVariableResolverService extends AbstractVariableResolverService {
|
||||
|
||||
constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment) {
|
||||
constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment, workspaceService?: IExtHostWorkspace) {
|
||||
super({
|
||||
getFolderUri: (folderName: string): URI | undefined => {
|
||||
const found = folders.filter(f => f.name === folderName);
|
||||
@@ -999,7 +942,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
|
||||
getWorkspaceFolderCount: (): number => {
|
||||
return folders.length;
|
||||
},
|
||||
getConfigurationValue: (folderUri: URI, section: string): string | undefined => {
|
||||
getConfigurationValue: (folderUri: URI | undefined, section: string): string | undefined => {
|
||||
return configurationService.getConfiguration(undefined, folderUri).get<string>(section);
|
||||
},
|
||||
getExecPath: (): string | undefined => {
|
||||
@@ -1014,6 +957,18 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
getWorkspaceFolderPathForFile: (): string | undefined => {
|
||||
if (editorService && workspaceService) {
|
||||
const activeEditor = editorService.activeEditor();
|
||||
if (activeEditor) {
|
||||
const ws = workspaceService.getWorkspaceFolder(activeEditor.document.uri);
|
||||
if (ws) {
|
||||
return path.normalize(ws.uri.fsPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
getSelectedText: (): string | undefined => {
|
||||
if (editorService) {
|
||||
const activeEditor = editorService.activeEditor();
|
||||
@@ -1032,7 +987,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}, undefined, env, !editorService);
|
||||
}, undefined, env);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1118,10 +1073,9 @@ export class WorkerExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
|
||||
@IExtHostExtensionService extensionService: IExtHostExtensionService,
|
||||
@IExtHostDocumentsAndEditors editorsService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||
@IExtHostCommands commandService: IExtHostCommands
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration
|
||||
) {
|
||||
super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService, commandService);
|
||||
super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService);
|
||||
}
|
||||
|
||||
protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService {
|
||||
|
||||
@@ -37,12 +37,12 @@ export class ExtHostDecorations implements ExtHostDecorationsShape {
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadDecorations);
|
||||
}
|
||||
|
||||
registerDecorationProvider(provider: vscode.FileDecorationProvider, extensionId: ExtensionIdentifier): vscode.Disposable {
|
||||
registerFileDecorationProvider(provider: vscode.FileDecorationProvider, extensionId: ExtensionIdentifier): vscode.Disposable {
|
||||
const handle = ExtHostDecorations._handlePool++;
|
||||
this._provider.set(handle, { provider, extensionId });
|
||||
this._proxy.$registerDecorationProvider(handle, extensionId.value);
|
||||
|
||||
const listener = provider.onDidChange(e => {
|
||||
const listener = provider.onDidChangeFileDecorations && provider.onDidChangeFileDecorations(e => {
|
||||
if (!e) {
|
||||
this._proxy.$onDidChange(handle, null);
|
||||
return;
|
||||
@@ -75,7 +75,7 @@ export class ExtHostDecorations implements ExtHostDecorationsShape {
|
||||
});
|
||||
|
||||
return new Disposable(() => {
|
||||
listener.dispose();
|
||||
listener?.dispose();
|
||||
this._proxy.$unregisterDecorationProvider(handle);
|
||||
this._provider.delete(handle);
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { splitLines } from 'vs/base/common/strings';
|
||||
|
||||
export class ExtHostDocumentContentProvider implements ExtHostDocumentContentProvidersShape {
|
||||
|
||||
@@ -61,7 +62,7 @@ export class ExtHostDocumentContentProvider implements ExtHostDocumentContentPro
|
||||
}
|
||||
|
||||
// create lines and compare
|
||||
const lines = value.split(/\r\n|\r|\n/);
|
||||
const lines = splitLines(value);
|
||||
|
||||
// broadcast event when content changed
|
||||
if (!document.equalLines(lines)) {
|
||||
|
||||
@@ -178,23 +178,23 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
|
||||
};
|
||||
}
|
||||
|
||||
async $onWillRunFileOperation(operation: FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise<any> {
|
||||
async $onWillRunFileOperation(operation: FileOperation, files: SourceTargetPair[], undoRedoGroupId: number | undefined, timeout: number, token: CancellationToken): Promise<any> {
|
||||
switch (operation) {
|
||||
case FileOperation.MOVE:
|
||||
await this._fireWillEvent(this._onWillRenameFile, { files: files.map(f => ({ oldUri: URI.revive(f.source!), newUri: URI.revive(f.target) })) }, timeout, token);
|
||||
await this._fireWillEvent(this._onWillRenameFile, { files: files.map(f => ({ oldUri: URI.revive(f.source!), newUri: URI.revive(f.target) })) }, undoRedoGroupId, timeout, token);
|
||||
break;
|
||||
case FileOperation.DELETE:
|
||||
await this._fireWillEvent(this._onWillDeleteFile, { files: files.map(f => URI.revive(f.target)) }, timeout, token);
|
||||
await this._fireWillEvent(this._onWillDeleteFile, { files: files.map(f => URI.revive(f.target)) }, undoRedoGroupId, timeout, token);
|
||||
break;
|
||||
case FileOperation.CREATE:
|
||||
await this._fireWillEvent(this._onWillCreateFile, { files: files.map(f => URI.revive(f.target)) }, timeout, token);
|
||||
await this._fireWillEvent(this._onWillCreateFile, { files: files.map(f => URI.revive(f.target)) }, undoRedoGroupId, timeout, token);
|
||||
break;
|
||||
default:
|
||||
//ignore, dont send
|
||||
}
|
||||
}
|
||||
|
||||
private async _fireWillEvent<E extends IWaitUntil>(emitter: AsyncEmitter<E>, data: Omit<E, 'waitUntil'>, timeout: number, token: CancellationToken): Promise<any> {
|
||||
private async _fireWillEvent<E extends IWaitUntil>(emitter: AsyncEmitter<E>, data: Omit<E, 'waitUntil'>, undoRedoGroupId: number | undefined, timeout: number, token: CancellationToken): Promise<any> {
|
||||
|
||||
const edits: WorkspaceEdit[] = [];
|
||||
|
||||
@@ -222,7 +222,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
|
||||
let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
|
||||
dto.edits = dto.edits.concat(edits);
|
||||
}
|
||||
return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit(dto);
|
||||
return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit(dto, undoRedoGroupId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ 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 { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
||||
// --- adapter
|
||||
|
||||
@@ -117,65 +116,57 @@ class CodeLensAdapter {
|
||||
private readonly _provider: vscode.CodeLensProvider
|
||||
) { }
|
||||
|
||||
provideCodeLenses(resource: URI, token: CancellationToken): Promise<extHostProtocol.ICodeLensListDto | undefined> {
|
||||
async provideCodeLenses(resource: URI, token: CancellationToken): Promise<extHostProtocol.ICodeLensListDto | undefined> {
|
||||
const doc = this._documents.getDocument(resource);
|
||||
|
||||
return asPromise(() => this._provider.provideCodeLenses(doc, token)).then(lenses => {
|
||||
|
||||
if (!lenses || token.isCancellationRequested) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const cacheId = this._cache.add(lenses);
|
||||
const disposables = new DisposableStore();
|
||||
this._disposables.set(cacheId, disposables);
|
||||
|
||||
const result: extHostProtocol.ICodeLensListDto = {
|
||||
cacheId,
|
||||
lenses: [],
|
||||
};
|
||||
|
||||
for (let i = 0; i < lenses.length; i++) {
|
||||
result.lenses.push({
|
||||
cacheId: [cacheId, i],
|
||||
range: typeConvert.Range.from(lenses[i].range),
|
||||
command: this._commands.toInternal(lenses[i].command, disposables)
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
const lenses = await this._provider.provideCodeLenses(doc, token);
|
||||
if (!lenses || token.isCancellationRequested) {
|
||||
return undefined;
|
||||
}
|
||||
const cacheId = this._cache.add(lenses);
|
||||
const disposables = new DisposableStore();
|
||||
this._disposables.set(cacheId, disposables);
|
||||
const result: extHostProtocol.ICodeLensListDto = {
|
||||
cacheId,
|
||||
lenses: [],
|
||||
};
|
||||
for (let i = 0; i < lenses.length; i++) {
|
||||
result.lenses.push({
|
||||
cacheId: [cacheId, i],
|
||||
range: typeConvert.Range.from(lenses[i].range),
|
||||
command: this._commands.toInternal(lenses[i].command, disposables)
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
resolveCodeLens(symbol: extHostProtocol.ICodeLensDto, token: CancellationToken): Promise<extHostProtocol.ICodeLensDto | undefined> {
|
||||
async resolveCodeLens(symbol: extHostProtocol.ICodeLensDto, token: CancellationToken): Promise<extHostProtocol.ICodeLensDto | undefined> {
|
||||
|
||||
const lens = symbol.cacheId && this._cache.get(...symbol.cacheId);
|
||||
if (!lens) {
|
||||
return Promise.resolve(undefined);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let resolve: Promise<vscode.CodeLens | undefined | null>;
|
||||
let resolvedLens: vscode.CodeLens | undefined | null;
|
||||
if (typeof this._provider.resolveCodeLens !== 'function' || lens.isResolved) {
|
||||
resolve = Promise.resolve(lens);
|
||||
resolvedLens = lens;
|
||||
} else {
|
||||
resolve = asPromise(() => this._provider.resolveCodeLens!(lens, token));
|
||||
resolvedLens = await this._provider.resolveCodeLens(lens, token);
|
||||
}
|
||||
if (!resolvedLens) {
|
||||
resolvedLens = lens;
|
||||
}
|
||||
|
||||
return resolve.then(newLens => {
|
||||
if (token.isCancellationRequested) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const disposables = symbol.cacheId && this._disposables.get(symbol.cacheId[0]);
|
||||
if (!disposables) {
|
||||
// We've already been disposed of
|
||||
return undefined;
|
||||
}
|
||||
|
||||
newLens = newLens || lens;
|
||||
symbol.command = this._commands.toInternal(newLens.command || CodeLensAdapter._badCmd, disposables);
|
||||
return symbol;
|
||||
});
|
||||
if (token.isCancellationRequested) {
|
||||
return undefined;
|
||||
}
|
||||
const disposables = symbol.cacheId && this._disposables.get(symbol.cacheId[0]);
|
||||
if (!disposables) {
|
||||
// disposed in the meantime
|
||||
return undefined;
|
||||
}
|
||||
symbol.command = this._commands.toInternal(resolvedLens.command ?? CodeLensAdapter._badCmd, disposables);
|
||||
return symbol;
|
||||
}
|
||||
|
||||
releaseCodeLenses(cachedId: number): void {
|
||||
@@ -320,18 +311,18 @@ class DocumentHighlightAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
class OnTypeRenameAdapter {
|
||||
class LinkedEditingRangeAdapter {
|
||||
constructor(
|
||||
private readonly _documents: ExtHostDocuments,
|
||||
private readonly _provider: vscode.OnTypeRenameProvider
|
||||
private readonly _provider: vscode.LinkedEditingRangeProvider
|
||||
) { }
|
||||
|
||||
provideOnTypeRenameRanges(resource: URI, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: RegExp; } | undefined> {
|
||||
provideLinkedEditingRanges(resource: URI, position: IPosition, token: CancellationToken): Promise<modes.LinkedEditingRanges | undefined> {
|
||||
|
||||
const doc = this._documents.getDocument(resource);
|
||||
const pos = typeConvert.Position.to(position);
|
||||
|
||||
return asPromise(() => this._provider.provideOnTypeRenameRanges(doc, pos, token)).then(value => {
|
||||
return asPromise(() => this._provider.provideLinkedEditingRanges(doc, pos, token)).then(value => {
|
||||
if (value && Array.isArray(value.ranges)) {
|
||||
return {
|
||||
ranges: coalesce(value.ranges.map(typeConvert.Range.from)),
|
||||
@@ -1329,7 +1320,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov
|
||||
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter
|
||||
| TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter
|
||||
| SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter | EvaluatableExpressionAdapter
|
||||
| OnTypeRenameAdapter;
|
||||
| LinkedEditingRangeAdapter;
|
||||
|
||||
class AdapterData {
|
||||
constructor(
|
||||
@@ -1571,18 +1562,17 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
||||
return this._withAdapter(handle, DocumentHighlightAdapter, adapter => adapter.provideDocumentHighlights(URI.revive(resource), position, token), undefined);
|
||||
}
|
||||
|
||||
// --- on type rename
|
||||
// --- linked editing
|
||||
|
||||
registerOnTypeRenameProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.OnTypeRenameProvider, wordPattern?: RegExp): vscode.Disposable {
|
||||
const handle = this._addNewAdapter(new OnTypeRenameAdapter(this._documents, provider), extension);
|
||||
const serializedWordPattern = wordPattern ? ExtHostLanguageFeatures._serializeRegExp(wordPattern) : undefined;
|
||||
this._proxy.$registerOnTypeRenameProvider(handle, this._transformDocumentSelector(selector), serializedWordPattern);
|
||||
registerLinkedEditingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.LinkedEditingRangeProvider): vscode.Disposable {
|
||||
const handle = this._addNewAdapter(new LinkedEditingRangeAdapter(this._documents, provider), extension);
|
||||
this._proxy.$registerLinkedEditingRangeProvider(handle, this._transformDocumentSelector(selector));
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
|
||||
$provideOnTypeRenameRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: extHostProtocol.IRegExpDto; } | undefined> {
|
||||
return this._withAdapter(handle, OnTypeRenameAdapter, async adapter => {
|
||||
const res = await adapter.provideOnTypeRenameRanges(URI.revive(resource), position, token);
|
||||
$provideLinkedEditingRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<extHostProtocol.ILinkedEditingRangesDto | undefined> {
|
||||
return this._withAdapter(handle, LinkedEditingRangeAdapter, async adapter => {
|
||||
const res = await adapter.provideLinkedEditingRanges(URI.revive(resource), position, token);
|
||||
if (res) {
|
||||
return {
|
||||
ranges: res.ranges,
|
||||
@@ -1814,7 +1804,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
||||
return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo, token), undefined);
|
||||
}
|
||||
|
||||
registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider2): vscode.Disposable {
|
||||
registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable {
|
||||
const handle = this._nextHandle();
|
||||
const eventHandle = typeof provider.onDidChangeFoldingRanges === 'function' ? this._nextHandle() : undefined;
|
||||
|
||||
@@ -1823,8 +1813,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
||||
let result = this._createDisposable(handle);
|
||||
|
||||
if (eventHandle !== undefined) {
|
||||
checkProposedApiEnabled(extension);
|
||||
const subscription = provider.onDidChangeFoldingRanges!(_ => this._proxy.$emitFoldingRangeEvent(eventHandle));
|
||||
const subscription = provider.onDidChangeFoldingRanges!(() => this._proxy.$emitFoldingRangeEvent(eventHandle));
|
||||
result = Disposable.from(result, subscription);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as UUID from 'vs/base/common/uuid';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadBulkEditsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorPropertiesChangeData, MainContext, MainThreadBulkEditsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
@@ -214,7 +214,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
private readonly _proxy: MainThreadNotebookShape;
|
||||
private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape;
|
||||
private readonly _notebookContentProviders = new Map<string, { readonly provider: vscode.NotebookContentProvider, readonly extension: IExtensionDescription; }>();
|
||||
private readonly _notebookKernels = new Map<string, { readonly kernel: vscode.NotebookKernel, readonly extension: IExtensionDescription; }>();
|
||||
private readonly _notebookKernelProviders = new Map<number, ExtHostNotebookKernelProviderAdapter>();
|
||||
private readonly _documents = new ResourceMap<ExtHostNotebookDocument>();
|
||||
private readonly _editors = new Map<string, { editor: ExtHostNotebookEditor; }>();
|
||||
@@ -414,6 +413,35 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
return callback(provider, document);
|
||||
}
|
||||
|
||||
async showNotebookDocument(notebookDocument: vscode.NotebookDocument, options?: vscode.NotebookDocumentShowOptions): Promise<vscode.NotebookEditor> {
|
||||
let resolvedOptions: INotebookDocumentShowOptions;
|
||||
if (typeof options === 'object') {
|
||||
resolvedOptions = {
|
||||
position: typeConverters.ViewColumn.from(options.viewColumn),
|
||||
preserveFocus: options.preserveFocus,
|
||||
selection: options.selection,
|
||||
pinned: typeof options.preview === 'boolean' ? !options.preview : undefined
|
||||
};
|
||||
} else {
|
||||
resolvedOptions = {
|
||||
preserveFocus: false
|
||||
};
|
||||
}
|
||||
|
||||
const editorId = await this._proxy.$tryShowNotebookDocument(notebookDocument.uri, notebookDocument.viewType, resolvedOptions);
|
||||
const editor = editorId && this._editors.get(editorId)?.editor;
|
||||
|
||||
if (editor) {
|
||||
return editor;
|
||||
}
|
||||
|
||||
if (editorId) {
|
||||
throw new Error(`Could NOT open editor for "${notebookDocument.toString()}" because another editor opened in the meantime.`);
|
||||
} else {
|
||||
throw new Error(`Could NOT open editor for "${notebookDocument.toString()}".`);
|
||||
}
|
||||
}
|
||||
|
||||
async $provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise<INotebookKernelInfoDto2[]> {
|
||||
return this._withAdapter<INotebookKernelInfoDto2[]>(handle, uri, (adapter, document) => {
|
||||
return adapter.provideKernels(document, token);
|
||||
@@ -487,28 +515,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
});
|
||||
}
|
||||
|
||||
async $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise<void> {
|
||||
const document = this._documents.get(URI.revive(uri));
|
||||
|
||||
if (!document || document.notebookDocument.viewType !== viewType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const kernelInfo = this._notebookKernels.get(kernelId);
|
||||
|
||||
if (!kernelInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
|
||||
|
||||
if (cell) {
|
||||
return withToken(token => (kernelInfo!.kernel.executeCell as any)(document.notebookDocument, cell.cell, token));
|
||||
} else {
|
||||
return withToken(token => (kernelInfo!.kernel.executeAllCells as any)(document.notebookDocument, token));
|
||||
}
|
||||
}
|
||||
|
||||
async $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise<boolean> {
|
||||
const document = this._documents.get(URI.revive(uri));
|
||||
if (!document) {
|
||||
|
||||
@@ -448,7 +448,11 @@ function getIconUris(iconPath: QuickInputButton['iconPath']): { dark: URI, light
|
||||
}
|
||||
const dark = getDarkIconUri(iconPath as URI | { light: URI; dark: URI; });
|
||||
const light = getLightIconUri(iconPath as URI | { light: URI; dark: URI; });
|
||||
return { dark, light };
|
||||
// Tolerate strings: https://github.com/microsoft/vscode/issues/110432#issuecomment-726144556
|
||||
return {
|
||||
dark: typeof dark === 'string' ? URI.file(dark) : dark,
|
||||
light: typeof light === 'string' ? URI.file(light) : light
|
||||
};
|
||||
}
|
||||
|
||||
function getLightIconUri(iconPath: URI | { light: URI; dark: URI; }) {
|
||||
|
||||
@@ -17,6 +17,7 @@ import { ISplice } from 'vs/base/common/sequence';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
||||
type ProviderHandle = number;
|
||||
type GroupHandle = number;
|
||||
@@ -90,6 +91,49 @@ function compareResourceStatesDecorations(a: vscode.SourceControlResourceDecorat
|
||||
return result;
|
||||
}
|
||||
|
||||
function compareCommands(a: vscode.Command, b: vscode.Command): number {
|
||||
if (a.command !== b.command) {
|
||||
return a.command < b.command ? -1 : 1;
|
||||
}
|
||||
|
||||
if (a.title !== b.title) {
|
||||
return a.title < b.title ? -1 : 1;
|
||||
}
|
||||
|
||||
if (a.tooltip !== b.tooltip) {
|
||||
if (a.tooltip !== undefined && b.tooltip !== undefined) {
|
||||
return a.tooltip < b.tooltip ? -1 : 1;
|
||||
} else if (a.tooltip !== undefined) {
|
||||
return 1;
|
||||
} else if (b.tooltip !== undefined) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (a.arguments === b.arguments) {
|
||||
return 0;
|
||||
} else if (!a.arguments) {
|
||||
return -1;
|
||||
} else if (!b.arguments) {
|
||||
return 1;
|
||||
} else if (a.arguments.length !== b.arguments.length) {
|
||||
return a.arguments.length - b.arguments.length;
|
||||
}
|
||||
|
||||
for (let i = 0; i < a.arguments.length; i++) {
|
||||
const aArg = a.arguments[i];
|
||||
const bArg = b.arguments[i];
|
||||
|
||||
if (aArg === bArg) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return aArg < bArg ? -1 : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function compareResourceStates(a: vscode.SourceControlResourceState, b: vscode.SourceControlResourceState): number {
|
||||
let result = comparePaths(a.resourceUri.fsPath, b.resourceUri.fsPath, true);
|
||||
|
||||
@@ -97,6 +141,18 @@ function compareResourceStates(a: vscode.SourceControlResourceState, b: vscode.S
|
||||
return result;
|
||||
}
|
||||
|
||||
if (a.command && b.command) {
|
||||
result = compareCommands(a.command, b.command);
|
||||
} else if (a.command) {
|
||||
return 1;
|
||||
} else if (b.command) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (result !== 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (a.decorations && b.decorations) {
|
||||
result = compareResourceStatesDecorations(a.decorations, b.decorations);
|
||||
} else if (a.decorations) {
|
||||
@@ -166,17 +222,13 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox {
|
||||
private _validateInput: IValidateInput | undefined;
|
||||
|
||||
get validateInput(): IValidateInput | undefined {
|
||||
if (!this._extension.enableProposedApi) {
|
||||
throw new Error(`[${this._extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.identifier.value}`);
|
||||
}
|
||||
checkProposedApiEnabled(this._extension);
|
||||
|
||||
return this._validateInput;
|
||||
}
|
||||
|
||||
set validateInput(fn: IValidateInput | undefined) {
|
||||
if (!this._extension.enableProposedApi) {
|
||||
throw new Error(`[${this._extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.identifier.value}`);
|
||||
}
|
||||
checkProposedApiEnabled(this._extension);
|
||||
|
||||
if (fn && typeof fn !== 'function') {
|
||||
throw new Error(`[${this._extension.identifier.value}]: Invalid SCM input box validation function`);
|
||||
@@ -223,8 +275,9 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
|
||||
private _resourceHandlePool: number = 0;
|
||||
private _resourceStates: vscode.SourceControlResourceState[] = [];
|
||||
|
||||
private _resourceStatesMap: Map<ResourceStateHandle, vscode.SourceControlResourceState> = new Map<ResourceStateHandle, vscode.SourceControlResourceState>();
|
||||
private _resourceStatesCommandsMap: Map<ResourceStateHandle, vscode.Command> = new Map<ResourceStateHandle, vscode.Command>();
|
||||
private _resourceStatesMap = new Map<ResourceStateHandle, vscode.SourceControlResourceState>();
|
||||
private _resourceStatesCommandsMap = new Map<ResourceStateHandle, vscode.Command>();
|
||||
private _resourceStatesDisposablesMap = new Map<ResourceStateHandle, IDisposable>();
|
||||
|
||||
private readonly _onDidUpdateResourceStates = new Emitter<void>();
|
||||
readonly onDidUpdateResourceStates = this._onDidUpdateResourceStates.event;
|
||||
@@ -302,9 +355,16 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
|
||||
const lightIconUri = r.decorations && getIconResource(r.decorations.light) || iconUri;
|
||||
const darkIconUri = r.decorations && getIconResource(r.decorations.dark) || iconUri;
|
||||
const icons: UriComponents[] = [];
|
||||
let command: ICommandDto | undefined;
|
||||
|
||||
if (r.command) {
|
||||
this._resourceStatesCommandsMap.set(handle, r.command);
|
||||
if (r.command.command === 'vscode.open' || r.command.command === 'vscode.diff') {
|
||||
const disposables = new DisposableStore();
|
||||
command = this._commands.converter.toInternal(r.command, disposables);
|
||||
this._resourceStatesDisposablesMap.set(handle, disposables);
|
||||
} else {
|
||||
this._resourceStatesCommandsMap.set(handle, r.command);
|
||||
}
|
||||
}
|
||||
|
||||
if (lightIconUri) {
|
||||
@@ -320,7 +380,7 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
|
||||
const faded = r.decorations && !!r.decorations.faded;
|
||||
const contextValue = r.contextValue || '';
|
||||
|
||||
const rawResource = [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue] as SCMRawResource;
|
||||
const rawResource = [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue, command] as SCMRawResource;
|
||||
|
||||
return { rawResource, handle };
|
||||
});
|
||||
@@ -340,6 +400,8 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
|
||||
for (const handle of handlesToDelete) {
|
||||
this._resourceStatesMap.delete(handle);
|
||||
this._resourceStatesCommandsMap.delete(handle);
|
||||
this._resourceStatesDisposablesMap.get(handle)?.dispose();
|
||||
this._resourceStatesDisposablesMap.delete(handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,21 @@ 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 = (() => {
|
||||
const map = new Map<string, ThemeColor>();
|
||||
|
||||
// https://github.com/microsoft/vscode/issues/110214
|
||||
map.set('statusBarItem.errorBackground', new ThemeColor('statusBarItem.errorForeground'));
|
||||
|
||||
return map;
|
||||
})();
|
||||
|
||||
private _id: number;
|
||||
private _alignment: number;
|
||||
private _priority?: number;
|
||||
@@ -26,6 +37,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
|
||||
private _text: string = '';
|
||||
private _tooltip?: string;
|
||||
private _color?: string | ThemeColor;
|
||||
private _backgroundColor?: ThemeColor;
|
||||
private readonly _internalCommandRegistration = new DisposableStore();
|
||||
private _command?: {
|
||||
readonly fromApi: string | vscode.Command,
|
||||
@@ -36,8 +48,9 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
|
||||
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) {
|
||||
constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation, extension?: IExtensionDescription) {
|
||||
this._id = ExtHostStatusBarEntry.ID_GEN++;
|
||||
this._proxy = proxy;
|
||||
this._commands = commands;
|
||||
@@ -46,6 +59,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
|
||||
this._alignment = alignment;
|
||||
this._priority = priority;
|
||||
this._accessibilityInformation = accessibilityInformation;
|
||||
this._extension = extension;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
@@ -72,6 +86,14 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
|
||||
return this._color;
|
||||
}
|
||||
|
||||
public get backgroundColor(): ThemeColor | undefined {
|
||||
if (this._extension) {
|
||||
checkProposedApiEnabled(this._extension);
|
||||
}
|
||||
|
||||
return this._backgroundColor;
|
||||
}
|
||||
|
||||
public get command(): string | vscode.Command | undefined {
|
||||
return this._command?.fromApi;
|
||||
}
|
||||
@@ -95,6 +117,19 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
|
||||
this.update();
|
||||
}
|
||||
|
||||
public set backgroundColor(color: ThemeColor | undefined) {
|
||||
if (this._extension) {
|
||||
checkProposedApiEnabled(this._extension);
|
||||
}
|
||||
|
||||
if (color && !ExtHostStatusBarEntry.ALLOWED_BACKGROUND_COLORS.has(color.id)) {
|
||||
color = undefined;
|
||||
}
|
||||
|
||||
this._backgroundColor = color;
|
||||
this.update();
|
||||
}
|
||||
|
||||
public set command(command: string | vscode.Command | undefined) {
|
||||
if (this._command?.fromApi === command) {
|
||||
return;
|
||||
@@ -144,9 +179,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._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);
|
||||
}
|
||||
@@ -207,8 +248,8 @@ export class ExtHostStatusBar {
|
||||
this._statusMessage = new StatusBarMessage(this);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): Disposable {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { MainContext, MainThreadStorageShape, ExtHostStorageShape } from './extH
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync';
|
||||
|
||||
export interface IStorageChangeEvent {
|
||||
shared: boolean;
|
||||
|
||||
@@ -188,6 +188,10 @@ export namespace CustomExecutionDTO {
|
||||
customExecution: 'customExecution'
|
||||
};
|
||||
}
|
||||
|
||||
export function to(taskId: string, providedCustomExeutions: Map<string, types.CustomExecution>): types.CustomExecution | undefined {
|
||||
return providedCustomExeutions.get(taskId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -274,15 +278,17 @@ export namespace TaskDTO {
|
||||
};
|
||||
return result;
|
||||
}
|
||||
export async function to(value: tasks.TaskDTO | undefined, workspace: IExtHostWorkspaceProvider): Promise<types.Task | undefined> {
|
||||
export async function to(value: tasks.TaskDTO | undefined, workspace: IExtHostWorkspaceProvider, providedCustomExeutions: Map<string, types.CustomExecution>): Promise<types.Task | undefined> {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
let execution: types.ShellExecution | types.ProcessExecution | undefined;
|
||||
let execution: types.ShellExecution | types.ProcessExecution | types.CustomExecution | undefined;
|
||||
if (ProcessExecutionDTO.is(value.execution)) {
|
||||
execution = ProcessExecutionDTO.to(value.execution);
|
||||
} else if (ShellExecutionDTO.is(value.execution)) {
|
||||
execution = ShellExecutionDTO.to(value.execution);
|
||||
} else if (CustomExecutionDTO.is(value.execution)) {
|
||||
execution = CustomExecutionDTO.to(value._id, providedCustomExeutions);
|
||||
}
|
||||
const definition: vscode.TaskDefinition | undefined = TaskDefinitionDTO.to(value.definition);
|
||||
let scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined;
|
||||
@@ -354,13 +360,6 @@ class TaskExecutionImpl implements vscode.TaskExecution {
|
||||
}
|
||||
|
||||
export namespace TaskExecutionDTO {
|
||||
export async function to(value: tasks.TaskExecutionDTO, tasks: ExtHostTaskBase, workspaceProvider: IExtHostWorkspaceProvider): Promise<vscode.TaskExecution> {
|
||||
const task = await TaskDTO.to(value.task, workspaceProvider);
|
||||
if (!task) {
|
||||
throw new Error('Unexpected: Task cannot be created.');
|
||||
}
|
||||
return new TaskExecutionImpl(tasks, value.id, task);
|
||||
}
|
||||
export function from(value: vscode.TaskExecution): tasks.TaskExecutionDTO {
|
||||
return {
|
||||
id: (value as TaskExecutionImpl)._id,
|
||||
@@ -447,7 +446,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask
|
||||
return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then(async (values) => {
|
||||
const result: vscode.Task[] = [];
|
||||
for (let value of values) {
|
||||
const task = await TaskDTO.to(value, this._workspaceProvider);
|
||||
const task = await TaskDTO.to(value, this._workspaceProvider, this._providedCustomExecutions2);
|
||||
if (task) {
|
||||
result.push(task);
|
||||
}
|
||||
@@ -573,7 +572,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask
|
||||
throw new Error(`Unexpected: Task of type [${taskDTO.definition.type}] cannot be resolved by provider of type [${handler.type}].`);
|
||||
}
|
||||
|
||||
const task = await TaskDTO.to(taskDTO, this._workspaceProvider);
|
||||
const task = await TaskDTO.to(taskDTO, this._workspaceProvider, this._providedCustomExecutions2);
|
||||
if (!task) {
|
||||
throw new Error('Unexpected: Task cannot be resolved.');
|
||||
}
|
||||
@@ -631,7 +630,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask
|
||||
return result;
|
||||
}
|
||||
const createdResult: Promise<TaskExecutionImpl> = new Promise(async (resolve, reject) => {
|
||||
const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider);
|
||||
const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider, this._providedCustomExecutions2);
|
||||
if (!taskToCreate) {
|
||||
reject('Unexpected: Task does not exist.');
|
||||
} else {
|
||||
@@ -705,6 +704,10 @@ export class WorkerExtHostTask extends ExtHostTaskBase {
|
||||
}
|
||||
|
||||
public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution> {
|
||||
if (!task.execution) {
|
||||
throw new Error('Tasks to execute must include an execution');
|
||||
}
|
||||
|
||||
const dto = TaskDTO.from(task, extension);
|
||||
if (dto === undefined) {
|
||||
throw new Error('Task is not valid');
|
||||
|
||||
794
lib/vscode/src/vs/workbench/api/common/extHostTesting.ts
Normal file
794
lib/vscode/src/vs/workbench/api/common/extHostTesting.ts
Normal file
@@ -0,0 +1,794 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
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';
|
||||
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 { 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 { 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 type * as vscode from 'vscode';
|
||||
|
||||
const getTestSubscriptionKey = (resource: ExtHostTestingResource, uri: URI) => `${resource}:${uri.toString()}`;
|
||||
|
||||
export class ExtHostTesting implements ExtHostTestingShape {
|
||||
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 workspaceObservers: WorkspaceFolderTestObserverFactory;
|
||||
private textDocumentObservers: TextDocumentTestObserverFactory;
|
||||
|
||||
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);
|
||||
this.textDocumentObservers = new TextDocumentTestObserverFactory(this.proxy, documents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements vscode.test.registerTestProvider
|
||||
*/
|
||||
public registerTestProvider<T extends vscode.TestItem>(provider: vscode.TestProvider<T>): vscode.Disposable {
|
||||
const providerId = generateUuid();
|
||||
this.providers.set(providerId, provider);
|
||||
this.proxy.$registerTestProvider(providerId);
|
||||
|
||||
return new Disposable(() => {
|
||||
this.providers.delete(providerId);
|
||||
this.proxy.$unregisterTestProvider(providerId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements vscode.test.createTextDocumentTestObserver
|
||||
*/
|
||||
public createTextDocumentTestObserver(document: vscode.TextDocument) {
|
||||
return this.textDocumentObservers.checkout(document.uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements vscode.test.createWorkspaceTestObserver
|
||||
*/
|
||||
public createWorkspaceTestObserver(workspaceFolder: vscode.WorkspaceFolder) {
|
||||
return this.workspaceObservers.checkout(workspaceFolder.uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements vscode.test.runTests
|
||||
*/
|
||||
public async runTests(req: vscode.TestRunOptions<vscode.TestItem>) {
|
||||
await this.proxy.$runTests({
|
||||
tests: req.tests
|
||||
// Find workspace items first, then owned tests, then document tests.
|
||||
// If a test instance exists in both the workspace and document, prefer
|
||||
// the workspace because it's less ephemeral.
|
||||
.map(test => this.workspaceObservers.getMirroredTestDataByReference(test)
|
||||
?? mapFind(this.testSubscriptions.values(), c => c.collection.getTestByReference(test))
|
||||
?? this.textDocumentObservers.getMirroredTestDataByReference(test))
|
||||
.filter(isDefined)
|
||||
.map(item => ({ providerId: item.providerId, testId: item.id })),
|
||||
debug: req.debug
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a request to read tests for a file, or workspace.
|
||||
* @override
|
||||
*/
|
||||
public $subscribeToTests(resource: ExtHostTestingResource, uriComponents: UriComponents) {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const subscriptionKey = getTestSubscriptionKey(resource, uri);
|
||||
if (this.testSubscriptions.has(subscriptionKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let method: undefined | ((p: vscode.TestProvider) => vscode.TestHierarchy<vscode.TestItem> | undefined);
|
||||
if (resource === ExtHostTestingResource.TextDocument) {
|
||||
const document = this.documents.getDocument(uri);
|
||||
if (document) {
|
||||
method = p => p.createDocumentTestHierarchy?.(document.document);
|
||||
}
|
||||
} else {
|
||||
const folder = this.workspace.getWorkspaceFolder(uri, false);
|
||||
if (folder) {
|
||||
method = p => p.createWorkspaceTestHierarchy?.(folder);
|
||||
}
|
||||
}
|
||||
|
||||
if (!method) {
|
||||
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) {
|
||||
try {
|
||||
const hierarchy = method(provider);
|
||||
if (!hierarchy) {
|
||||
continue;
|
||||
}
|
||||
|
||||
disposable.add(hierarchy);
|
||||
collection.addRoot(hierarchy.root, id);
|
||||
hierarchy.onDidChangeTest(e => collection.onItemChange(e, id));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
this.testSubscriptions.set(subscriptionKey, { store: disposable, collection });
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes of a previous subscription to tests.
|
||||
* @override
|
||||
*/
|
||||
public $unsubscribeFromTests(resource: ExtHostTestingResource, uriComponents: UriComponents) {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const subscriptionKey = getTestSubscriptionKey(resource, uri);
|
||||
this.testSubscriptions.get(subscriptionKey)?.store.dispose();
|
||||
this.testSubscriptions.delete(subscriptionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a test update from the main thread. Called (eventually) whenever
|
||||
* tests change.
|
||||
* @override
|
||||
*/
|
||||
public $acceptDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void {
|
||||
if (resource === ExtHostTestingResource.TextDocument) {
|
||||
this.textDocumentObservers.acceptDiff(URI.revive(uri), diff);
|
||||
} else {
|
||||
this.workspaceObservers.acceptDiff(URI.revive(uri), diff);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs tests with the given set of IDs. Allows for test from multiple
|
||||
* providers to be run.
|
||||
* @override
|
||||
*/
|
||||
public async $runTestsForProvider(req: RunTestForProviderRequest): 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);
|
||||
if (!tests.length) {
|
||||
return EMPTY_TEST_RESULT;
|
||||
}
|
||||
|
||||
await provider.runTests({ tests, debug: req.debug }, CancellationToken.None);
|
||||
return EMPTY_TEST_RESULT;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
this.addItem(item, providerId, existing.parent);
|
||||
this.throttleSendDiff();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
protected getId(): string {
|
||||
return generateUuid();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@throttle(200)
|
||||
protected throttleSendDiff() {
|
||||
const diff = this.collectDiff();
|
||||
if (diff.length) {
|
||||
this.publishDiff(diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
interface MirroredCollectionTestItem extends IncrementalTestCollectionItem {
|
||||
revived: vscode.TestItem;
|
||||
depth: number;
|
||||
wrapped?: vscode.TestItem;
|
||||
}
|
||||
|
||||
class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollectionTestItem> {
|
||||
private readonly added = new Set<MirroredCollectionTestItem>();
|
||||
private readonly updated = new Set<MirroredCollectionTestItem>();
|
||||
private readonly removed = new Set<MirroredCollectionTestItem>();
|
||||
|
||||
private readonly alreadyRemoved = new Set<string>();
|
||||
|
||||
public get isEmpty() {
|
||||
return this.added.size === 0 && this.removed.size === 0 && this.updated.size === 0;
|
||||
}
|
||||
|
||||
constructor(private readonly collection: MirroredTestCollection, private readonly emitter: Emitter<vscode.TestChangeEvent>) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public add(node: MirroredCollectionTestItem): void {
|
||||
this.added.add(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public update(node: MirroredCollectionTestItem): void {
|
||||
Object.assign(node.revived, TestItem.to(node.item));
|
||||
if (!this.added.has(node)) {
|
||||
this.updated.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public remove(node: MirroredCollectionTestItem): void {
|
||||
if (this.added.has(node)) {
|
||||
this.added.delete(node);
|
||||
return;
|
||||
}
|
||||
|
||||
this.updated.delete(node);
|
||||
|
||||
if (node.parent && this.alreadyRemoved.has(node.parent)) {
|
||||
this.alreadyRemoved.add(node.id);
|
||||
return;
|
||||
}
|
||||
|
||||
this.removed.add(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getChangeEvent(): vscode.TestChangeEvent {
|
||||
const { collection, added, updated, removed } = this;
|
||||
return {
|
||||
get added() { return [...added].map(collection.getPublicTestItem, collection); },
|
||||
get updated() { return [...updated].map(collection.getPublicTestItem, collection); },
|
||||
get removed() { return [...removed].map(collection.getPublicTestItem, collection); },
|
||||
get commonChangeAncestor() {
|
||||
let ancestorPath: MirroredCollectionTestItem[] | undefined;
|
||||
const buildAncestorPath = (node: MirroredCollectionTestItem | undefined) => {
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// add the node and all its parents to the list of ancestors. If
|
||||
// the node is detached, do not return a path (its parent will
|
||||
// also have been passed to remove() and be present)
|
||||
const path: MirroredCollectionTestItem[] = new Array(node.depth + 1);
|
||||
for (let i = node.depth; i >= 0; i--) {
|
||||
if (!node) {
|
||||
return undefined; // detached child
|
||||
}
|
||||
|
||||
path[node.depth] = node;
|
||||
node = node.parent ? collection.getMirroredTestDataById(node.parent) : undefined;
|
||||
}
|
||||
|
||||
return path;
|
||||
};
|
||||
|
||||
const addAncestorPath = (node: MirroredCollectionTestItem) => {
|
||||
// fast path: if the common ancestor is already the root, no more work to do
|
||||
if (ancestorPath && ancestorPath.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const thisPath = buildAncestorPath(node);
|
||||
if (!thisPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ancestorPath) {
|
||||
ancestorPath = thisPath;
|
||||
return;
|
||||
}
|
||||
|
||||
// removes node from the path to the ancestor that don't match
|
||||
// the corresponding node in *this* path.
|
||||
for (let i = ancestorPath.length - 1; i >= 0; i--) {
|
||||
if (ancestorPath[i] !== thisPath[i]) {
|
||||
ancestorPath.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addParentAncestor = (node: MirroredCollectionTestItem) => {
|
||||
if (ancestorPath && ancestorPath.length === 0) {
|
||||
// no-op
|
||||
} else if (node.parent === null) {
|
||||
ancestorPath = [];
|
||||
} else {
|
||||
const parent = collection.getMirroredTestDataById(node.parent);
|
||||
if (parent) {
|
||||
addAncestorPath(parent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (const node of added) { addParentAncestor(node); }
|
||||
for (const node of updated) { addAncestorPath(node); }
|
||||
for (const node of removed) { addParentAncestor(node); }
|
||||
|
||||
const ancestor = ancestorPath && ancestorPath[ancestorPath.length - 1];
|
||||
return ancestor ? collection.getPublicTestItem(ancestor) : null;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public complete() {
|
||||
if (!this.isEmpty) {
|
||||
this.emitter.fire(this.getChangeEvent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maintains tests in this extension host sent from the main thread.
|
||||
* @private
|
||||
*/
|
||||
export class MirroredTestCollection extends AbstractIncrementalTestCollection<MirroredCollectionTestItem> {
|
||||
private changeEmitter = new Emitter<vscode.TestChangeEvent>();
|
||||
|
||||
/**
|
||||
* Change emitter that fires with the same sematics as `TestObserver.onDidChangeTests`.
|
||||
*/
|
||||
public readonly onDidChangeTests = this.changeEmitter.event;
|
||||
|
||||
/**
|
||||
* Gets a list of root test items.
|
||||
*/
|
||||
public get rootTestItems() {
|
||||
return this.getAllAsTestItem([...this.roots]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the item IDs to TestItems for exposure to extensions.
|
||||
*/
|
||||
public getAllAsTestItem(itemIds: Iterable<string>): vscode.TestItem[] {
|
||||
let output: vscode.TestItem[] = [];
|
||||
for (const itemId of itemIds) {
|
||||
const item = this.items.get(itemId);
|
||||
if (item) {
|
||||
output.push(this.getPublicTestItem(item));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* If the test ID exists, returns its underlying ID.
|
||||
*/
|
||||
public getMirroredTestDataById(itemId: string) {
|
||||
return this.items.get(itemId);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the test item is a mirrored test item, returns its underlying ID.
|
||||
*/
|
||||
public getMirroredTestDataByReference(item: vscode.TestItem) {
|
||||
const id = getMirroredItemId(item);
|
||||
return id ? this.items.get(id) : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected createItem(item: InternalTestItem, parent?: MirroredCollectionTestItem): MirroredCollectionTestItem {
|
||||
return { ...item, revived: TestItem.to(item.item), depth: parent ? parent.depth + 1 : 0, children: new Set() };
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected createChangeCollector() {
|
||||
return new MirroredChangeCollector(this, this.changeEmitter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the public test item instance for the given mirrored record.
|
||||
*/
|
||||
public getPublicTestItem(item: MirroredCollectionTestItem): vscode.TestItem {
|
||||
if (!item.wrapped) {
|
||||
item.wrapped = new ExtHostTestItem(item, this);
|
||||
}
|
||||
|
||||
return item.wrapped;
|
||||
}
|
||||
}
|
||||
|
||||
const getMirroredItemId = (item: vscode.TestItem) => {
|
||||
return (item as any)[MirroredItemId] as string | undefined;
|
||||
};
|
||||
|
||||
const MirroredItemId = Symbol('MirroredItemId');
|
||||
|
||||
class ExtHostTestItem implements vscode.TestItem, RequiredTestItem {
|
||||
readonly #internal: MirroredCollectionTestItem;
|
||||
readonly #collection: MirroredTestCollection;
|
||||
|
||||
public get label() { return this.#internal.revived.label; }
|
||||
public get description() { return this.#internal.revived.description; }
|
||||
public get state() { return this.#internal.revived.state; }
|
||||
public get location() { return this.#internal.revived.location; }
|
||||
public get runnable() { return this.#internal.revived.runnable ?? true; }
|
||||
public get debuggable() { return this.#internal.revived.debuggable ?? false; }
|
||||
public get children() {
|
||||
return this.#collection.getAllAsTestItem(this.#internal.children);
|
||||
}
|
||||
|
||||
get [MirroredItemId]() { return this.#internal.id; }
|
||||
|
||||
constructor(internal: MirroredCollectionTestItem, collection: MirroredTestCollection) {
|
||||
this.#internal = internal;
|
||||
this.#collection = collection;
|
||||
}
|
||||
|
||||
public toJSON() {
|
||||
const serialized: RequiredTestItem = {
|
||||
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()),
|
||||
};
|
||||
|
||||
return serialized;
|
||||
}
|
||||
}
|
||||
|
||||
interface IObserverData {
|
||||
observers: number;
|
||||
tests: MirroredTestCollection;
|
||||
listener: IDisposable;
|
||||
pendingDeletion?: IDisposable;
|
||||
}
|
||||
|
||||
abstract class AbstractTestObserverFactory {
|
||||
private readonly resources = new Map<string /* uri */, IObserverData>();
|
||||
|
||||
public checkout(resourceUri: URI): vscode.TestObserver {
|
||||
const resourceKey = resourceUri.toString();
|
||||
const resource = this.resources.get(resourceKey) ?? this.createObserverData(resourceUri);
|
||||
|
||||
resource.observers++;
|
||||
|
||||
return {
|
||||
onDidChangeTest: resource.tests.onDidChangeTests,
|
||||
onDidDiscoverInitialTests: new Emitter<void>().event, // todo@connor4312
|
||||
get tests() {
|
||||
return resource.tests.rootTestItems;
|
||||
},
|
||||
dispose: once(() => {
|
||||
if (!--resource.observers) {
|
||||
resource.pendingDeletion = this.eventuallyDispose(resourceUri);
|
||||
}
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the internal test data by its reference, in any observer.
|
||||
*/
|
||||
public getMirroredTestDataByReference(ref: vscode.TestItem) {
|
||||
for (const { tests } of this.resources.values()) {
|
||||
const v = tests.getMirroredTestDataByReference(ref);
|
||||
if (v) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when no observers are listening for the resource any more. Should
|
||||
* defer unlistening on the resource, and return a disposiable
|
||||
* to halt the process in case new listeners come in.
|
||||
*/
|
||||
protected eventuallyDispose(resourceUri: URI) {
|
||||
return disposableTimeout(() => this.unlisten(resourceUri), 10 * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts listening to test information for the given resource.
|
||||
*/
|
||||
protected abstract listen(resourceUri: URI, onDiff: (diff: TestsDiff) => void): Disposable;
|
||||
|
||||
private createObserverData(resourceUri: URI): IObserverData {
|
||||
const tests = new MirroredTestCollection();
|
||||
const listener = this.listen(resourceUri, diff => tests.apply(diff));
|
||||
const data: IObserverData = { observers: 0, tests, listener };
|
||||
this.resources.set(resourceUri.toString(), data);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a resource is no longer in use.
|
||||
*/
|
||||
protected unlisten(resourceUri: URI) {
|
||||
const key = resourceUri.toString();
|
||||
const resource = this.resources.get(key);
|
||||
if (resource) {
|
||||
resource.observers = -1;
|
||||
resource.pendingDeletion?.dispose();
|
||||
resource.listener.dispose();
|
||||
this.resources.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WorkspaceFolderTestObserverFactory extends AbstractTestObserverFactory {
|
||||
private diffListeners = new Map<string, (diff: TestsDiff) => void>();
|
||||
|
||||
constructor(private readonly proxy: MainThreadTestingShape) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishees the diff for the workspace folder with the given uri.
|
||||
*/
|
||||
public acceptDiff(resourceUri: URI, diff: TestsDiff) {
|
||||
this.diffListeners.get(resourceUri.toString())?.(diff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public listen(resourceUri: URI, onDiff: (diff: TestsDiff) => void) {
|
||||
this.proxy.$subscribeToDiffs(ExtHostTestingResource.Workspace, resourceUri);
|
||||
|
||||
const uriString = resourceUri.toString();
|
||||
this.diffListeners.set(uriString, onDiff);
|
||||
|
||||
return new Disposable(() => {
|
||||
this.proxy.$unsubscribeFromDiffs(ExtHostTestingResource.Workspace, resourceUri);
|
||||
this.diffListeners.delete(uriString);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class TextDocumentTestObserverFactory extends AbstractTestObserverFactory {
|
||||
private diffListeners = new Map<string, (diff: TestsDiff) => void>();
|
||||
|
||||
constructor(private readonly proxy: MainThreadTestingShape, private documents: IExtHostDocumentsAndEditors) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishees the diff for the document with the given uri.
|
||||
*/
|
||||
public acceptDiff(resourceUri: URI, diff: TestsDiff) {
|
||||
this.diffListeners.get(resourceUri.toString())?.(diff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public listen(resourceUri: URI, onDiff: (diff: TestsDiff) => void) {
|
||||
const document = this.documents.getDocument(resourceUri);
|
||||
if (!document) {
|
||||
return new Disposable(() => undefined);
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ import { TreeItemCollapsibleState, ThemeIcon, MarkdownString as MarkdownStringTy
|
||||
import { isUndefinedOrNull, isString } from 'vs/base/common/types';
|
||||
import { equals, coalesce } from 'vs/base/common/arrays';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { MarkdownString } from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
@@ -33,7 +32,6 @@ function toTreeItemLabel(label: any, extension: IExtensionDescription): ITreeIte
|
||||
if (label
|
||||
&& typeof label === 'object'
|
||||
&& typeof label.label === 'string') {
|
||||
checkProposedApiEnabled(extension);
|
||||
let highlights: [number, number][] | undefined = undefined;
|
||||
if (Array.isArray(label.highlights)) {
|
||||
highlights = (<[number, number][]>label.highlights).filter((highlight => highlight.length === 2 && typeof highlight[0] === 'number' && typeof highlight[1] === 'number'));
|
||||
@@ -183,7 +181,7 @@ type TreeData<T> = { message: boolean, element: T | Root | false };
|
||||
|
||||
interface TreeNode extends IDisposable {
|
||||
item: ITreeItem;
|
||||
extensionItem: vscode.TreeItem2;
|
||||
extensionItem: vscode.TreeItem;
|
||||
parent: TreeNode | Root;
|
||||
children?: TreeNode[];
|
||||
}
|
||||
@@ -293,7 +291,7 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
return this.elements.get(treeItemHandle);
|
||||
}
|
||||
|
||||
reveal(element: T, options?: IRevealOptions): Promise<void> {
|
||||
reveal(element: T | undefined, options?: IRevealOptions): Promise<void> {
|
||||
options = options ? options : { select: true, focus: false };
|
||||
const select = isUndefinedOrNull(options.select) ? true : options.select;
|
||||
const focus = isUndefinedOrNull(options.focus) ? false : options.focus;
|
||||
@@ -302,10 +300,15 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
if (typeof this.dataProvider.getParent !== 'function') {
|
||||
return Promise.reject(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' method`));
|
||||
}
|
||||
return this.refreshPromise
|
||||
.then(() => this.resolveUnknownParentChain(element))
|
||||
.then(parentChain => this.resolveTreeNode(element, parentChain[parentChain.length - 1])
|
||||
.then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus, expand })), error => this.logService.error(error));
|
||||
|
||||
if (element) {
|
||||
return this.refreshPromise
|
||||
.then(() => this.resolveUnknownParentChain(element))
|
||||
.then(parentChain => this.resolveTreeNode(element, parentChain[parentChain.length - 1])
|
||||
.then(treeNode => this.proxy.$reveal(this.viewId, { item: treeNode.item, parentChain: parentChain.map(p => p.item) }, { select, focus, expand })), error => this.logService.error(error));
|
||||
} else {
|
||||
return this.proxy.$reveal(this.viewId, undefined, { select, focus, expand });
|
||||
}
|
||||
}
|
||||
|
||||
private _message: string = '';
|
||||
@@ -375,7 +378,7 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
if (element) {
|
||||
const node = this.nodes.get(element);
|
||||
if (node) {
|
||||
const resolve = await this.dataProvider.resolveTreeItem(element, node.extensionItem);
|
||||
const resolve = await this.dataProvider.resolveTreeItem(node.extensionItem, element) ?? node.extensionItem;
|
||||
// Resolvable elements. Currently only tooltip.
|
||||
node.item.tooltip = this.getTooltip(resolve.tooltip);
|
||||
return node.item;
|
||||
@@ -565,13 +568,12 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
|
||||
private getTooltip(tooltip?: string | vscode.MarkdownString): string | IMarkdownString | undefined {
|
||||
if (MarkdownStringType.isMarkdownString(tooltip)) {
|
||||
checkProposedApiEnabled(this.extension);
|
||||
return MarkdownString.from(tooltip);
|
||||
}
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem2, parent: TreeNode | Root): TreeNode {
|
||||
private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode | Root): TreeNode {
|
||||
const disposable = new DisposableStore();
|
||||
const handle = this.createHandle(element, extensionTreeItem, parent);
|
||||
const icon = this.getLightIconPath(extensionTreeItem);
|
||||
@@ -600,7 +602,7 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
};
|
||||
}
|
||||
|
||||
private getThemeIcon(extensionTreeItem: vscode.TreeItem2): ThemeIcon | undefined {
|
||||
private getThemeIcon(extensionTreeItem: vscode.TreeItem): ThemeIcon | undefined {
|
||||
return extensionTreeItem.iconPath instanceof ThemeIcon ? extensionTreeItem.iconPath : undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
import { ExtHostTunnelServiceShape, MainContext, MainThreadTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import * as vscode from 'vscode';
|
||||
import { RemoteTunnel, TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { RemoteTunnel, TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
export interface TunnelDto {
|
||||
remoteAddress: { port: number, host: string };
|
||||
@@ -32,7 +33,7 @@ export interface Tunnel extends vscode.Disposable {
|
||||
|
||||
export interface IExtHostTunnelService extends ExtHostTunnelServiceShape {
|
||||
readonly _serviceBrand: undefined;
|
||||
openTunnel(forward: TunnelOptions): Promise<vscode.Tunnel | undefined>;
|
||||
openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise<vscode.Tunnel | undefined>;
|
||||
getTunnels(): Promise<vscode.TunnelDescription[]>;
|
||||
onDidChangeTunnels: vscode.Event<void>;
|
||||
setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable>;
|
||||
@@ -51,23 +52,17 @@ export class ExtHostTunnelService implements IExtHostTunnelService {
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTunnelService);
|
||||
}
|
||||
|
||||
async openTunnel(forward: TunnelOptions): Promise<vscode.Tunnel | undefined> {
|
||||
async openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise<vscode.Tunnel | undefined> {
|
||||
return undefined;
|
||||
}
|
||||
async getTunnels(): Promise<vscode.TunnelDescription[]> {
|
||||
return [];
|
||||
}
|
||||
async $findCandidatePorts(): Promise<{ host: string, port: number; detail: string; }[]> {
|
||||
return [];
|
||||
}
|
||||
async $filterCandidates(candidates: { host: string, port: number, detail: string }[]): Promise<boolean[]> {
|
||||
return candidates.map(() => true);
|
||||
}
|
||||
async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> {
|
||||
await this._proxy.$tunnelServiceReady();
|
||||
return { dispose: () => { } };
|
||||
}
|
||||
$forwardPort(tunnelOptions: TunnelOptions): Promise<TunnelDto> | undefined { return undefined; }
|
||||
$forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<TunnelDto> | undefined { return undefined; }
|
||||
async $closeTunnel(remote: { host: string, port: number }): Promise<void> { }
|
||||
async $onDidTunnelsChange(): Promise<void> { }
|
||||
}
|
||||
|
||||
@@ -7,13 +7,12 @@ import * as modes from 'vs/editor/common/modes';
|
||||
import * as types from './extHostTypes';
|
||||
import * as search from 'vs/workbench/contrib/search/common/search';
|
||||
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
import { IDecorationOptions, IThemeDecorationRenderOptions, IDecorationRenderOptions, IContentDecorationRenderOptions } from 'vs/editor/common/editorCommon';
|
||||
import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import type * as vscode from 'vscode';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import * as editorRange from 'vs/editor/common/core/range';
|
||||
import { ISelection } from 'vs/editor/common/core/selection';
|
||||
@@ -33,6 +32,7 @@ import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
|
||||
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { CellOutputKind, IDisplayOutput, INotebookDecorationRenderOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ITestItem, ITestState } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
|
||||
export interface PositionLike {
|
||||
line: number;
|
||||
@@ -220,7 +220,7 @@ export namespace DiagnosticSeverity {
|
||||
}
|
||||
|
||||
export namespace ViewColumn {
|
||||
export function from(column?: vscode.ViewColumn): EditorViewColumn {
|
||||
export function from(column?: vscode.ViewColumn): EditorGroupColumn {
|
||||
if (typeof column === 'number' && column >= types.ViewColumn.One) {
|
||||
return column - 1; // adjust zero index (ViewColumn.ONE => 0)
|
||||
}
|
||||
@@ -232,12 +232,12 @@ export namespace ViewColumn {
|
||||
return ACTIVE_GROUP; // default is always the active group
|
||||
}
|
||||
|
||||
export function to(position: EditorViewColumn): vscode.ViewColumn {
|
||||
export function to(position: EditorGroupColumn): vscode.ViewColumn {
|
||||
if (typeof position === 'number' && position >= 0) {
|
||||
return position + 1; // adjust to index (ViewColumn.ONE => 1)
|
||||
}
|
||||
|
||||
throw new Error(`invalid 'EditorViewColumn'`);
|
||||
throw new Error(`invalid 'EditorGroupColumn'`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,8 +274,8 @@ export namespace MarkdownString {
|
||||
if (isCodeblock(markup)) {
|
||||
const { language, value } = markup;
|
||||
res = { value: '```' + language + '\n' + value + '\n```\n' };
|
||||
} else if (htmlContent.isMarkdownString(markup)) {
|
||||
res = markup;
|
||||
} else if (types.MarkdownString.isMarkdownString(markup)) {
|
||||
res = { value: markup.value, isTrusted: markup.isTrusted, supportThemeIcons: markup.supportThemeIcons };
|
||||
} else if (typeof markup === 'string') {
|
||||
res = { value: markup };
|
||||
} else {
|
||||
@@ -343,7 +343,7 @@ export namespace MarkdownString {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function fromStrict(value: string | types.MarkdownString): undefined | string | htmlContent.IMarkdownString {
|
||||
export function fromStrict(value: string | vscode.MarkdownString): undefined | string | htmlContent.IMarkdownString {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -1183,6 +1183,7 @@ export namespace FoldingRangeKind {
|
||||
|
||||
export interface TextEditorOpenOptions extends vscode.TextDocumentShowOptions {
|
||||
background?: boolean;
|
||||
override?: boolean;
|
||||
}
|
||||
|
||||
export namespace TextEditorOpenOptions {
|
||||
@@ -1194,6 +1195,7 @@ export namespace TextEditorOpenOptions {
|
||||
inactive: options.background,
|
||||
preserveFocus: options.preserveFocus,
|
||||
selection: typeof options.selection === 'object' ? Range.from(options.selection) : undefined,
|
||||
override: typeof options.override === 'boolean' ? false : undefined
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1253,50 +1255,6 @@ export namespace LanguageSelector {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace LogLevel {
|
||||
export function from(extLevel: types.LogLevel): _MainLogLevel {
|
||||
switch (extLevel) {
|
||||
case types.LogLevel.Trace:
|
||||
return _MainLogLevel.Trace;
|
||||
case types.LogLevel.Debug:
|
||||
return _MainLogLevel.Debug;
|
||||
case types.LogLevel.Info:
|
||||
return _MainLogLevel.Info;
|
||||
case types.LogLevel.Warning:
|
||||
return _MainLogLevel.Warning;
|
||||
case types.LogLevel.Error:
|
||||
return _MainLogLevel.Error;
|
||||
case types.LogLevel.Critical:
|
||||
return _MainLogLevel.Critical;
|
||||
case types.LogLevel.Off:
|
||||
return _MainLogLevel.Off;
|
||||
default:
|
||||
return _MainLogLevel.Info;
|
||||
}
|
||||
}
|
||||
|
||||
export function to(mainLevel: _MainLogLevel): types.LogLevel {
|
||||
switch (mainLevel) {
|
||||
case _MainLogLevel.Trace:
|
||||
return types.LogLevel.Trace;
|
||||
case _MainLogLevel.Debug:
|
||||
return types.LogLevel.Debug;
|
||||
case _MainLogLevel.Info:
|
||||
return types.LogLevel.Info;
|
||||
case _MainLogLevel.Warning:
|
||||
return types.LogLevel.Warning;
|
||||
case _MainLogLevel.Error:
|
||||
return types.LogLevel.Error;
|
||||
case _MainLogLevel.Critical:
|
||||
return types.LogLevel.Critical;
|
||||
case _MainLogLevel.Off:
|
||||
return types.LogLevel.Off;
|
||||
default:
|
||||
return types.LogLevel.Info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellOutput {
|
||||
export function from(output: types.NotebookCellOutput): IDisplayOutput {
|
||||
return output.toJSON();
|
||||
@@ -1395,3 +1353,64 @@ export namespace NotebookDecorationRenderOptions {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TestState {
|
||||
export function from(item: vscode.TestState): ITestState {
|
||||
return {
|
||||
runState: item.runState,
|
||||
duration: item.duration,
|
||||
messages: item.messages.map(message => ({
|
||||
message: MarkdownString.fromStrict(message.message) || '',
|
||||
severity: message.severity,
|
||||
expectedOutput: message.expectedOutput,
|
||||
actualOutput: message.actualOutput,
|
||||
location: message.location ? location.from(message.location) : undefined,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
export function to(item: ITestState): vscode.TestState {
|
||||
return new types.TestState(
|
||||
item.runState,
|
||||
item.messages.map(message => ({
|
||||
message: typeof message.message === 'string' ? message.message : MarkdownString.to(message.message),
|
||||
severity: message.severity,
|
||||
expectedOutput: message.expectedOutput,
|
||||
actualOutput: message.actualOutput,
|
||||
location: message.location && location.to({
|
||||
range: message.location.range,
|
||||
uri: URI.revive(message.location.uri)
|
||||
}),
|
||||
})),
|
||||
item.duration,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export namespace TestItem {
|
||||
export function from(item: vscode.TestItem): ITestItem {
|
||||
return {
|
||||
label: item.label,
|
||||
location: item.location ? location.from(item.location) : undefined,
|
||||
debuggable: item.debuggable,
|
||||
description: item.description,
|
||||
runnable: item.runnable,
|
||||
state: TestState.from(item.state),
|
||||
};
|
||||
}
|
||||
|
||||
export function to(item: ITestItem): vscode.TestItem {
|
||||
return {
|
||||
label: item.label,
|
||||
location: item.location && location.to({
|
||||
range: item.location.range,
|
||||
uri: URI.revive(item.location.uri)
|
||||
}),
|
||||
debuggable: item.debuggable,
|
||||
description: item.description,
|
||||
runnable: item.runnable,
|
||||
state: TestState.to(item.state),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { coalesceInPlace, equals } from 'vs/base/common/arrays';
|
||||
import { escapeCodicons } from 'vs/base/common/codicons';
|
||||
import { illegalArgument } from 'vs/base/common/errors';
|
||||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import { isMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { isMarkdownString, MarkdownString as BaseMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { isStringArray } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -1276,54 +1275,10 @@ export class CodeLens {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class CodeInset {
|
||||
|
||||
range: Range;
|
||||
height?: number;
|
||||
|
||||
constructor(range: Range, height?: number) {
|
||||
this.range = range;
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@es5ClassCompat
|
||||
export class MarkdownString {
|
||||
export class MarkdownString implements vscode.MarkdownString {
|
||||
|
||||
value: string;
|
||||
isTrusted?: boolean;
|
||||
readonly supportThemeIcons?: boolean;
|
||||
|
||||
constructor(value?: string, supportThemeIcons: boolean = false) {
|
||||
this.value = value ?? '';
|
||||
this.supportThemeIcons = supportThemeIcons;
|
||||
}
|
||||
|
||||
appendText(value: string): MarkdownString {
|
||||
// escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
|
||||
this.value += (this.supportThemeIcons ? escapeCodicons(value) : value)
|
||||
.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&')
|
||||
.replace(/\n/, '\n\n');
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
appendMarkdown(value: string): MarkdownString {
|
||||
this.value += value;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
appendCodeblock(code: string, language: string = ''): MarkdownString {
|
||||
this.value += '\n```';
|
||||
this.value += language;
|
||||
this.value += '\n';
|
||||
this.value += code;
|
||||
this.value += '\n```\n';
|
||||
return this;
|
||||
}
|
||||
readonly #delegate: BaseMarkdownString;
|
||||
|
||||
static isMarkdownString(thing: any): thing is vscode.MarkdownString {
|
||||
if (thing instanceof MarkdownString) {
|
||||
@@ -1331,15 +1286,55 @@ export class MarkdownString {
|
||||
}
|
||||
return thing && thing.appendCodeblock && thing.appendMarkdown && thing.appendText && (thing.value !== undefined);
|
||||
}
|
||||
|
||||
constructor(value?: string, supportThemeIcons: boolean = false) {
|
||||
this.#delegate = new BaseMarkdownString(value, { supportThemeIcons });
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.#delegate.value;
|
||||
}
|
||||
set value(value: string) {
|
||||
this.#delegate.value = value;
|
||||
}
|
||||
|
||||
get isTrusted(): boolean | undefined {
|
||||
return this.#delegate.isTrusted;
|
||||
}
|
||||
|
||||
set isTrusted(value: boolean | undefined) {
|
||||
this.#delegate.isTrusted = value;
|
||||
}
|
||||
|
||||
get supportThemeIcons(): boolean | undefined {
|
||||
return this.#delegate.supportThemeIcons;
|
||||
}
|
||||
|
||||
appendText(value: string): vscode.MarkdownString {
|
||||
this.#delegate.appendText(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
appendMarkdown(value: string): vscode.MarkdownString {
|
||||
this.#delegate.appendMarkdown(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
appendCodeblock(value: string, language?: string): vscode.MarkdownString {
|
||||
this.#delegate.appendCodeblock(language ?? '', value);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class ParameterInformation {
|
||||
|
||||
label: string | [number, number];
|
||||
documentation?: string | MarkdownString;
|
||||
documentation?: string | vscode.MarkdownString;
|
||||
|
||||
constructor(label: string | [number, number], documentation?: string | MarkdownString) {
|
||||
constructor(label: string | [number, number], documentation?: string | vscode.MarkdownString) {
|
||||
this.label = label;
|
||||
this.documentation = documentation;
|
||||
}
|
||||
@@ -1349,11 +1344,11 @@ export class ParameterInformation {
|
||||
export class SignatureInformation {
|
||||
|
||||
label: string;
|
||||
documentation?: string | MarkdownString;
|
||||
documentation?: string | vscode.MarkdownString;
|
||||
parameters: ParameterInformation[];
|
||||
activeParameter?: number;
|
||||
|
||||
constructor(label: string, documentation?: string | MarkdownString) {
|
||||
constructor(label: string, documentation?: string | vscode.MarkdownString) {
|
||||
this.label = label;
|
||||
this.documentation = documentation;
|
||||
this.parameters = [];
|
||||
@@ -1439,7 +1434,7 @@ export class CompletionItem implements vscode.CompletionItem {
|
||||
kind?: CompletionItemKind;
|
||||
tags?: CompletionItemTag[];
|
||||
detail?: string;
|
||||
documentation?: string | MarkdownString;
|
||||
documentation?: string | vscode.MarkdownString;
|
||||
sortText?: string;
|
||||
filterText?: string;
|
||||
preselect?: boolean;
|
||||
@@ -2218,13 +2213,18 @@ export enum ConfigurationTarget {
|
||||
@es5ClassCompat
|
||||
export class RelativePattern implements IRelativePattern {
|
||||
base: string;
|
||||
baseFolder?: URI;
|
||||
|
||||
pattern: string;
|
||||
|
||||
constructor(base: vscode.WorkspaceFolder | string, pattern: string) {
|
||||
// expose a `baseFolder: URI` property as a workaround for the short-coming
|
||||
// of `IRelativePattern` only supporting `base: string` which always translates
|
||||
// to a `file://` URI. With `baseFolder` we can support non-file based folders
|
||||
// in searches
|
||||
// (https://github.com/microsoft/vscode/commit/6326543b11cf4998c8fd1564cab5c429a2416db3)
|
||||
readonly baseFolder?: URI;
|
||||
|
||||
constructor(base: vscode.WorkspaceFolder | URI | string, pattern: string) {
|
||||
if (typeof base !== 'string') {
|
||||
if (!base || !URI.isUri(base.uri)) {
|
||||
if (!base || !URI.isUri(base) && !URI.isUri(base.uri)) {
|
||||
throw illegalArgument('base');
|
||||
}
|
||||
}
|
||||
@@ -2234,7 +2234,11 @@ export class RelativePattern implements IRelativePattern {
|
||||
}
|
||||
|
||||
if (typeof base === 'string') {
|
||||
this.baseFolder = URI.file(base);
|
||||
this.base = base;
|
||||
} else if (URI.isUri(base)) {
|
||||
this.baseFolder = base;
|
||||
this.base = base.fsPath;
|
||||
} else {
|
||||
this.baseFolder = base.uri;
|
||||
this.base = base.uri.fsPath;
|
||||
@@ -2369,16 +2373,6 @@ export class EvaluatableExpression implements vscode.EvaluatableExpression {
|
||||
}
|
||||
}
|
||||
|
||||
export enum LogLevel {
|
||||
Trace = 1,
|
||||
Debug = 2,
|
||||
Info = 3,
|
||||
Warning = 4,
|
||||
Error = 5,
|
||||
Critical = 6,
|
||||
Off = 7
|
||||
}
|
||||
|
||||
//#region file api
|
||||
|
||||
export enum FileChangeType {
|
||||
@@ -2749,8 +2743,8 @@ export enum ExtensionKind {
|
||||
export class FileDecoration {
|
||||
|
||||
static validate(d: FileDecoration): void {
|
||||
if (d.badge && d.badge.length !== 1) {
|
||||
throw new Error(`The 'badge'-property must be undefined or a single character`);
|
||||
if (d.badge && d.badge.length !== 1 && d.badge.length !== 2) {
|
||||
throw new Error(`The 'badge'-property must be undefined or a short character`);
|
||||
}
|
||||
if (!d.color && !d.badge && !d.tooltip) {
|
||||
throw new Error(`The decoration is empty`);
|
||||
@@ -2920,3 +2914,64 @@ export enum StandardTokenType {
|
||||
String = 2,
|
||||
RegEx = 4
|
||||
}
|
||||
|
||||
|
||||
export class LinkedEditingRanges {
|
||||
constructor(public readonly ranges: Range[], public readonly wordPattern?: RegExp) {
|
||||
}
|
||||
}
|
||||
|
||||
//#region Testing
|
||||
export enum TestRunState {
|
||||
Unset = 0,
|
||||
Running = 1,
|
||||
Passed = 2,
|
||||
Failed = 3,
|
||||
Skipped = 4,
|
||||
Errored = 5
|
||||
}
|
||||
|
||||
export enum TestMessageSeverity {
|
||||
Error = 0,
|
||||
Warning = 1,
|
||||
Information = 2,
|
||||
Hint = 3
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class TestState {
|
||||
#runState: TestRunState;
|
||||
#duration?: number;
|
||||
#messages: ReadonlyArray<Readonly<vscode.TestMessage>>;
|
||||
|
||||
public get runState() {
|
||||
return this.#runState;
|
||||
}
|
||||
|
||||
public get duration() {
|
||||
return this.#duration;
|
||||
}
|
||||
|
||||
public get messages() {
|
||||
return this.#messages;
|
||||
}
|
||||
|
||||
constructor(runState: TestRunState, messages: vscode.TestMessage[] = [], duration?: number) {
|
||||
this.#runState = runState;
|
||||
this.#messages = Object.freeze(messages.map(m => Object.freeze(m)));
|
||||
this.#duration = duration;
|
||||
}
|
||||
}
|
||||
|
||||
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]
|
||||
};
|
||||
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -12,7 +12,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { convertWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
import { EditorGroupColumn } from 'vs/workbench/common/editor';
|
||||
import type * as vscode from 'vscode';
|
||||
import * as extHostProtocol from './extHost.protocol';
|
||||
import * as extHostTypes from './extHostTypes';
|
||||
@@ -273,7 +273,7 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
|
||||
viewType: string,
|
||||
title: string,
|
||||
state: any,
|
||||
position: EditorViewColumn,
|
||||
position: EditorGroupColumn,
|
||||
options: modes.IWebviewOptions & modes.IWebviewPanelOptions
|
||||
): Promise<void> {
|
||||
const entry = this._serializers.get(viewType);
|
||||
|
||||
@@ -10,17 +10,18 @@ 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 { isLinux } from 'vs/base/common/platform';
|
||||
import { basename, basenameOrAuthority, dirname, isEqual, 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';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Severity } from 'vs/platform/notification/common/notification';
|
||||
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { Range, RelativePattern } from 'vs/workbench/api/common/extHostTypes';
|
||||
@@ -59,6 +60,11 @@ function delta(oldFolders: vscode.WorkspaceFolder[], newFolders: vscode.Workspac
|
||||
return arrayDelta(oldSortedFolders, newSortedFolders, compare);
|
||||
}
|
||||
|
||||
function ignorePathCasing(uri: URI, extHostFileSystemInfo: IExtHostFileSystemInfo): boolean {
|
||||
const capabilities = extHostFileSystemInfo.getCapabilities(uri.scheme);
|
||||
return !(capabilities && (capabilities & FileSystemProviderCapabilities.PathCaseSensitive));
|
||||
}
|
||||
|
||||
interface MutableWorkspaceFolder extends vscode.WorkspaceFolder {
|
||||
name: string;
|
||||
index: number;
|
||||
@@ -66,7 +72,7 @@ interface MutableWorkspaceFolder extends vscode.WorkspaceFolder {
|
||||
|
||||
class ExtHostWorkspaceImpl extends Workspace {
|
||||
|
||||
static toExtHostWorkspace(data: IWorkspaceData | null, previousConfirmedWorkspace?: ExtHostWorkspaceImpl, previousUnconfirmedWorkspace?: ExtHostWorkspaceImpl): { workspace: ExtHostWorkspaceImpl | null, added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[] } {
|
||||
static toExtHostWorkspace(data: IWorkspaceData | null, previousConfirmedWorkspace: ExtHostWorkspaceImpl | undefined, previousUnconfirmedWorkspace: ExtHostWorkspaceImpl | undefined, extHostFileSystemInfo: IExtHostFileSystemInfo): { workspace: ExtHostWorkspaceImpl | null, added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[] } {
|
||||
if (!data) {
|
||||
return { workspace: null, added: [], removed: [] };
|
||||
}
|
||||
@@ -99,7 +105,7 @@ class ExtHostWorkspaceImpl extends Workspace {
|
||||
// make sure to restore sort order based on index
|
||||
newWorkspaceFolders.sort((f1, f2) => f1.index < f2.index ? -1 : 1);
|
||||
|
||||
const workspace = new ExtHostWorkspaceImpl(id, name, newWorkspaceFolders, configuration ? URI.revive(configuration) : null, !!isUntitled);
|
||||
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);
|
||||
|
||||
return { workspace, added, removed };
|
||||
@@ -117,10 +123,11 @@ class ExtHostWorkspaceImpl extends Workspace {
|
||||
}
|
||||
|
||||
private readonly _workspaceFolders: vscode.WorkspaceFolder[] = [];
|
||||
private readonly _structure = TernarySearchTree.forUris<vscode.WorkspaceFolder>(!isLinux);
|
||||
private readonly _structure: TernarySearchTree<URI, vscode.WorkspaceFolder>;
|
||||
|
||||
constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[], configuration: URI | null, private _isUntitled: boolean) {
|
||||
super(id, folders.map(f => new WorkspaceFolder(f)), configuration);
|
||||
constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[], configuration: URI | null, private _isUntitled: boolean, ignorePathCasing: (key: URI) => boolean) {
|
||||
super(id, folders.map(f => new WorkspaceFolder(f)), configuration, ignorePathCasing);
|
||||
this._structure = TernarySearchTree.forUris<vscode.WorkspaceFolder>(ignorePathCasing);
|
||||
|
||||
// setup the workspace folder data structure
|
||||
folders.forEach(folder => {
|
||||
@@ -170,22 +177,25 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
|
||||
private readonly _proxy: MainThreadWorkspaceShape;
|
||||
private readonly _messageService: MainThreadMessageServiceShape;
|
||||
private readonly _extHostFileSystemInfo: IExtHostFileSystemInfo;
|
||||
|
||||
private readonly _activeSearchCallbacks: ((match: IRawFileMatch2) => any)[] = [];
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
@IExtHostFileSystemInfo extHostFileSystemInfo: IExtHostFileSystemInfo,
|
||||
@ILogService logService: ILogService,
|
||||
) {
|
||||
this._logService = logService;
|
||||
this._extHostFileSystemInfo = extHostFileSystemInfo;
|
||||
this._requestIdProvider = new Counter();
|
||||
this._barrier = new Barrier();
|
||||
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadWorkspace);
|
||||
this._messageService = extHostRpc.getProxy(MainContext.MainThreadMessageService);
|
||||
const data = initData.workspace;
|
||||
this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, [], data.configuration ? URI.revive(data.configuration) : null, !!data.isUntitled) : undefined;
|
||||
this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, [], data.configuration ? URI.revive(data.configuration) : null, !!data.isUntitled, uri => ignorePathCasing(uri, extHostFileSystemInfo)) : undefined;
|
||||
}
|
||||
|
||||
$initializeWorkspace(data: IWorkspaceData | null): void {
|
||||
@@ -391,13 +401,13 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
configuration: this._actualWorkspace.configuration,
|
||||
folders,
|
||||
isUntitled: this._actualWorkspace.isUntitled
|
||||
} as IWorkspaceData, this._actualWorkspace).workspace || undefined;
|
||||
} as IWorkspaceData, this._actualWorkspace, undefined, this._extHostFileSystemInfo).workspace || undefined;
|
||||
}
|
||||
}
|
||||
|
||||
$acceptWorkspaceData(data: IWorkspaceData | null): void {
|
||||
|
||||
const { workspace, added, removed } = ExtHostWorkspaceImpl.toExtHostWorkspace(data, this._confirmedWorkspace, this._unconfirmedWorkspace);
|
||||
const { workspace, added, removed } = ExtHostWorkspaceImpl.toExtHostWorkspace(data, this._confirmedWorkspace, this._unconfirmedWorkspace, this._extHostFileSystemInfo);
|
||||
|
||||
// Update our workspace object. We have a confirmed workspace, so we drop our
|
||||
// unconfirmed workspace.
|
||||
@@ -457,17 +467,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
} :
|
||||
options.previewOptions;
|
||||
|
||||
let includePattern: string | undefined;
|
||||
let folder: URI | undefined;
|
||||
if (options.include) {
|
||||
if (typeof options.include === 'string') {
|
||||
includePattern = options.include;
|
||||
} else {
|
||||
includePattern = options.include.pattern;
|
||||
folder = (options.include as RelativePattern).baseFolder || URI.file(options.include.base);
|
||||
}
|
||||
}
|
||||
|
||||
const { includePattern, folder } = parseSearchInclude(options.include);
|
||||
const excludePattern = (typeof options.exclude === 'string') ? options.exclude :
|
||||
options.exclude ? options.exclude.pattern : undefined;
|
||||
const queryOptions: ITextQueryBuilderOptions = {
|
||||
@@ -562,14 +562,12 @@ function parseSearchInclude(include: RelativePattern | string | undefined): { in
|
||||
includePattern = include;
|
||||
} else {
|
||||
includePattern = include.pattern;
|
||||
|
||||
// include.base must be an absolute path
|
||||
includeFolder = include.baseFolder || URI.file(include.base);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
includePattern: includePattern,
|
||||
includePattern,
|
||||
folder: includeFolder
|
||||
};
|
||||
}
|
||||
|
||||
@@ -73,19 +73,19 @@ const apiMenus: IAPIMenu[] = [
|
||||
id: MenuId.DebugToolBar,
|
||||
description: localize('menus.debugToolBar', "The debug toolbar menu")
|
||||
},
|
||||
{
|
||||
key: 'menuBar/webNavigation',
|
||||
id: MenuId.MenubarWebNavigationMenu,
|
||||
description: localize('menus.webNavigation', "The top level navigational menu (web only)"),
|
||||
proposed: true,
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'menuBar/file',
|
||||
id: MenuId.MenubarFileMenu,
|
||||
description: localize('menus.file', "The top level file menu"),
|
||||
proposed: true
|
||||
},
|
||||
{
|
||||
key: 'menuBar/home',
|
||||
id: MenuId.MenubarHomeMenu,
|
||||
description: localize('menus.home', "The home indicator context menu (web only)"),
|
||||
proposed: true,
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'scm/title',
|
||||
id: MenuId.SCMTitle,
|
||||
@@ -461,7 +461,7 @@ namespace schema {
|
||||
type: 'string'
|
||||
},
|
||||
enablement: {
|
||||
description: localize('vscode.extension.contributes.commandType.precondition', '(Optional) Condition which must be true to enable the command'),
|
||||
description: localize('vscode.extension.contributes.commandType.precondition', '(Optional) Condition which must be true to enable the command in the UI (menu and keybindings). Does not prevent executing the command by other means, like the `executeCommand`-api.'),
|
||||
type: 'string'
|
||||
},
|
||||
icon: {
|
||||
@@ -586,6 +586,10 @@ submenusExtensionPoint.setHandler(extensions => {
|
||||
collector.warn(localize('submenuId.invalid.id', "`{0}` is not a valid submenu identifier", entry.value.id));
|
||||
return;
|
||||
}
|
||||
if (_submenus.has(entry.value.id)) {
|
||||
collector.warn(localize('submenuId.duplicate.id', "The `{0}` submenu was already previously registered.", entry.value.id));
|
||||
return;
|
||||
}
|
||||
if (!entry.value.label) {
|
||||
collector.warn(localize('submenuId.invalid.label', "`{0}` is not a valid submenu label", entry.value.label));
|
||||
return;
|
||||
@@ -616,6 +620,7 @@ submenusExtensionPoint.setHandler(extensions => {
|
||||
|
||||
const _apiMenusByKey = new Map(Iterable.map(Iterable.from(apiMenus), menu => ([menu.key, menu])));
|
||||
const _menuRegistrations = new DisposableStore();
|
||||
const _submenuMenuItems = new Map<number /* menu id */, Set<number /* submenu id */>>();
|
||||
|
||||
const menusExtensionPoint = ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: (schema.IUserFriendlyMenuItem | schema.IUserFriendlySubmenuItem)[] }>({
|
||||
extensionPoint: 'menus',
|
||||
@@ -627,6 +632,7 @@ menusExtensionPoint.setHandler(extensions => {
|
||||
|
||||
// remove all previous menu registrations
|
||||
_menuRegistrations.clear();
|
||||
_submenuMenuItems.clear();
|
||||
|
||||
const items: { id: MenuId, item: IMenuItem | ISubmenuItem }[] = [];
|
||||
|
||||
@@ -694,6 +700,20 @@ menusExtensionPoint.setHandler(extensions => {
|
||||
continue;
|
||||
}
|
||||
|
||||
let submenuRegistrations = _submenuMenuItems.get(menu.id.id);
|
||||
|
||||
if (!submenuRegistrations) {
|
||||
submenuRegistrations = new Set();
|
||||
_submenuMenuItems.set(menu.id.id, submenuRegistrations);
|
||||
}
|
||||
|
||||
if (submenuRegistrations.has(submenu.id.id)) {
|
||||
collector.warn(localize('submenuItem.duplicate', "The `{0}` submenu was already contributed to the `{1}` menu.", menuItem.submenu, entry.key));
|
||||
continue;
|
||||
}
|
||||
|
||||
submenuRegistrations.add(submenu.id.id);
|
||||
|
||||
item = { submenu: submenu.id, icon: submenu.icon, title: submenu.label, group: undefined, order: undefined, when: undefined };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IEditorGroupsService, IEditorGroup, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { GroupIdentifier } from 'vs/workbench/common/editor';
|
||||
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
export type EditorViewColumn = number;
|
||||
|
||||
export function viewColumnToEditorGroup(editorGroupService: IEditorGroupsService, position?: EditorViewColumn): GroupIdentifier {
|
||||
if (typeof position !== 'number' || position === ACTIVE_GROUP) {
|
||||
return ACTIVE_GROUP; // prefer active group when position is undefined or passed in as such
|
||||
}
|
||||
|
||||
const groups = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE);
|
||||
|
||||
let candidate = groups[position];
|
||||
if (candidate) {
|
||||
return candidate.id; // found direct match
|
||||
}
|
||||
|
||||
let firstGroup = groups[0];
|
||||
if (groups.length === 1 && firstGroup.count === 0) {
|
||||
return firstGroup.id; // first editor should always open in first group independent from position provided
|
||||
}
|
||||
|
||||
return SIDE_GROUP; // open to the side if group not found or we are instructed to
|
||||
}
|
||||
|
||||
export function editorGroupToViewColumn(editorGroupService: IEditorGroupsService, editorGroup: IEditorGroup | GroupIdentifier): EditorViewColumn {
|
||||
const group = (typeof editorGroup === 'number') ? editorGroupService.getGroup(editorGroup) : editorGroup;
|
||||
if (!group) {
|
||||
throw new Error('Invalid group provided');
|
||||
}
|
||||
|
||||
return editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE).indexOf(group);
|
||||
}
|
||||
@@ -113,7 +113,7 @@ export interface TaskProcessStartedDTO {
|
||||
|
||||
export interface TaskProcessEndedDTO {
|
||||
id: string;
|
||||
exitCode: number;
|
||||
exitCode: number | undefined;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { tmpdir } from 'os';
|
||||
export interface OpenCommandPipeArgs {
|
||||
type: 'open';
|
||||
fileURIs?: string[];
|
||||
folderURIs: string[];
|
||||
folderURIs?: string[];
|
||||
forceNewWindow?: boolean;
|
||||
diffMode?: boolean;
|
||||
addMode?: boolean;
|
||||
@@ -26,6 +26,11 @@ export interface OpenCommandPipeArgs {
|
||||
waitMarkerFilePath?: string;
|
||||
}
|
||||
|
||||
export interface OpenExternalCommandPipeArgs {
|
||||
type: 'openExternal';
|
||||
uris: string[];
|
||||
}
|
||||
|
||||
export interface StatusPipeArgs {
|
||||
type: 'status';
|
||||
}
|
||||
@@ -36,6 +41,8 @@ export interface RunCommandPipeArgs {
|
||||
args: any[];
|
||||
}
|
||||
|
||||
export type PipeCommand = OpenCommandPipeArgs | StatusPipeArgs | RunCommandPipeArgs | OpenExternalCommandPipeArgs;
|
||||
|
||||
export interface ICommandsExecuter {
|
||||
executeCommand<T>(id: string, ...args: any[]): Promise<T>;
|
||||
}
|
||||
@@ -80,11 +87,14 @@ export class CLIServerBase {
|
||||
req.setEncoding('utf8');
|
||||
req.on('data', (d: string) => chunks.push(d));
|
||||
req.on('end', () => {
|
||||
const data: OpenCommandPipeArgs | StatusPipeArgs | RunCommandPipeArgs | any = JSON.parse(chunks.join(''));
|
||||
const data: PipeCommand | any = JSON.parse(chunks.join(''));
|
||||
switch (data.type) {
|
||||
case 'open':
|
||||
this.open(data, res);
|
||||
break;
|
||||
case 'openExternal':
|
||||
this.openExternal(data, res);
|
||||
break;
|
||||
case 'status':
|
||||
this.getStatus(data, res);
|
||||
break;
|
||||
@@ -140,6 +150,14 @@ export class CLIServerBase {
|
||||
res.end();
|
||||
}
|
||||
|
||||
private openExternal(data: OpenExternalCommandPipeArgs, res: http.ServerResponse) {
|
||||
for (const uri of data.uris) {
|
||||
this._commands.executeCommand('_workbench.openExternal', URI.parse(uri), { allowTunneling: true });
|
||||
}
|
||||
res.writeHead(200);
|
||||
res.end();
|
||||
}
|
||||
|
||||
private async getStatus(data: StatusPipeArgs, res: http.ServerResponse) {
|
||||
try {
|
||||
const status = await this._commands.executeCommand('_issues.getSystemStatus');
|
||||
|
||||
@@ -14,7 +14,6 @@ import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensi
|
||||
import { IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IAdapterDescriptor } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IExtHostConfiguration, ExtHostConfigProvider } from '../common/extHostConfiguration';
|
||||
import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
@@ -24,13 +23,14 @@ import { SignService } from 'vs/platform/sign/node/signService';
|
||||
import { hasChildProcesses, prepareCommand, runInExternalTerminal } from 'vs/workbench/contrib/debug/node/terminals';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver';
|
||||
import { createCancelablePromise, firstParallel } from 'vs/base/common/async';
|
||||
|
||||
|
||||
export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
private _integratedTerminalInstance?: vscode.Terminal;
|
||||
private _integratedTerminalInstances = new DebugTerminalCollection();
|
||||
private _terminalDisposedListener: IDisposable | undefined;
|
||||
|
||||
constructor(
|
||||
@@ -39,10 +39,9 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
@IExtHostExtensionService extensionService: IExtHostExtensionService,
|
||||
@IExtHostDocumentsAndEditors editorsService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||
@IExtHostTerminalService private _terminalService: IExtHostTerminalService,
|
||||
@IExtHostCommands commandService: IExtHostCommands
|
||||
@IExtHostTerminalService private _terminalService: IExtHostTerminalService
|
||||
) {
|
||||
super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService, commandService);
|
||||
super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService);
|
||||
}
|
||||
|
||||
protected createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined {
|
||||
@@ -76,40 +75,44 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
if (!this._terminalDisposedListener) {
|
||||
// React on terminal disposed and check if that is the debug terminal #12956
|
||||
this._terminalDisposedListener = this._terminalService.onDidCloseTerminal(terminal => {
|
||||
if (this._integratedTerminalInstance && this._integratedTerminalInstance === terminal) {
|
||||
this._integratedTerminalInstance = undefined;
|
||||
}
|
||||
this._integratedTerminalInstances.onTerminalClosed(terminal);
|
||||
});
|
||||
}
|
||||
|
||||
let needNewTerminal = true; // be pessimistic
|
||||
if (this._integratedTerminalInstance) {
|
||||
const pid = await this._integratedTerminalInstance.processId;
|
||||
needNewTerminal = await hasChildProcesses(pid); // if no processes running in terminal reuse terminal
|
||||
}
|
||||
|
||||
const configProvider = await this._configurationService.getConfigProvider();
|
||||
const shell = this._terminalService.getDefaultShell(true, configProvider);
|
||||
const shellArgs = this._terminalService.getDefaultShellArgs(true, configProvider);
|
||||
|
||||
const shellConfig = JSON.stringify({ shell, shellArgs });
|
||||
let terminal = await this._integratedTerminalInstances.checkout(shellConfig);
|
||||
|
||||
let cwdForPrepareCommand: string | undefined;
|
||||
let giveShellTimeToInitialize = false;
|
||||
|
||||
if (needNewTerminal || !this._integratedTerminalInstance) {
|
||||
|
||||
if (!terminal) {
|
||||
const options: vscode.TerminalOptions = {
|
||||
shellPath: shell,
|
||||
// shellArgs: this._terminalService._getDefaultShellArgs(configProvider),
|
||||
shellArgs: shellArgs,
|
||||
cwd: args.cwd,
|
||||
name: args.title || nls.localize('debug.terminal.title', "debuggee"),
|
||||
};
|
||||
this._integratedTerminalInstance = this._terminalService.createTerminalFromOptions(options, true);
|
||||
giveShellTimeToInitialize = true;
|
||||
terminal = this._terminalService.createTerminalFromOptions(options, true);
|
||||
this._integratedTerminalInstances.insert(terminal, shellConfig);
|
||||
|
||||
} else {
|
||||
cwdForPrepareCommand = args.cwd;
|
||||
}
|
||||
|
||||
const terminal = this._integratedTerminalInstance;
|
||||
|
||||
terminal.show();
|
||||
|
||||
const shellProcessId = await this._integratedTerminalInstance.processId;
|
||||
const shellProcessId = await terminal.processId;
|
||||
|
||||
if (giveShellTimeToInitialize) {
|
||||
// give a new terminal some time to initialize the shell
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
const command = prepareCommand(shell, args.args, cwdForPrepareCommand, args.env);
|
||||
terminal.sendText(command, true);
|
||||
|
||||
@@ -123,6 +126,49 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
}
|
||||
|
||||
protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService {
|
||||
return new ExtHostVariableResolverService(folders, editorService, configurationService, process.env as env.IProcessEnvironment);
|
||||
return new ExtHostVariableResolverService(folders, editorService, configurationService, process.env as env.IProcessEnvironment, this._workspaceService);
|
||||
}
|
||||
}
|
||||
|
||||
class DebugTerminalCollection {
|
||||
/**
|
||||
* Delay before a new terminal is a candidate for reuse. See #71850
|
||||
*/
|
||||
private static minUseDelay = 1000;
|
||||
|
||||
private _terminalInstances = new Map<vscode.Terminal, { lastUsedAt: number, config: string }>();
|
||||
|
||||
public async checkout(config: string) {
|
||||
const entries = [...this._terminalInstances.keys()];
|
||||
const promises = entries.map((terminal) => createCancelablePromise(async ct => {
|
||||
const pid = await terminal.processId;
|
||||
if (await hasChildProcesses(pid)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// important: date check and map operations must be synchronous
|
||||
const now = Date.now();
|
||||
const termInfo = this._terminalInstances.get(terminal);
|
||||
if (!termInfo || termInfo.lastUsedAt + DebugTerminalCollection.minUseDelay > now || ct.isCancellationRequested) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (termInfo.config !== config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
termInfo.lastUsedAt = now;
|
||||
return terminal;
|
||||
}));
|
||||
|
||||
return await firstParallel(promises, (t): t is vscode.Terminal => !!t);
|
||||
}
|
||||
|
||||
public insert(terminal: vscode.Terminal, termConfig: string) {
|
||||
this._terminalInstances.set(terminal, { lastUsedAt: Date.now(), config: termConfig });
|
||||
}
|
||||
|
||||
public onTerminalClosed(terminal: vscode.Terminal) {
|
||||
this._terminalInstances.delete(terminal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,10 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||
}
|
||||
|
||||
public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution> {
|
||||
if (!task.execution) {
|
||||
throw new Error('Tasks to execute must include an execution');
|
||||
}
|
||||
|
||||
const tTask = (task as types.Task);
|
||||
// We have a preserved ID. So the task didn't change.
|
||||
if (tTask._id !== undefined) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import { BaseExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/co
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
|
||||
export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
|
||||
@@ -56,7 +57,15 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
public createTerminalFromOptions(options: vscode.TerminalOptions, isFeatureTerminal?: boolean): vscode.Terminal {
|
||||
const terminal = new ExtHostTerminal(this._proxy, options, options.name);
|
||||
this._terminals.push(terminal);
|
||||
terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env, /*options.waitOnExit*/ undefined, options.strictEnv, options.hideFromUser, isFeatureTerminal);
|
||||
terminal.create(
|
||||
withNullAsUndefined(options.shellPath),
|
||||
withNullAsUndefined(options.shellArgs),
|
||||
withNullAsUndefined(options.cwd),
|
||||
withNullAsUndefined(options.env),
|
||||
/*options.waitOnExit*/ undefined,
|
||||
withNullAsUndefined(options.strictEnv),
|
||||
withNullAsUndefined(options.hideFromUser),
|
||||
withNullAsUndefined(isFeatureTerminal));
|
||||
return terminal;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ import { isLinux } from 'vs/base/common/platform';
|
||||
import { IExtHostTunnelService, TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { asPromise } from 'vs/base/common/async';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { TunnelOptions, TunnelCreationOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
class ExtensionTunnel implements vscode.Tunnel {
|
||||
private _onDispose: Emitter<void> = new Emitter();
|
||||
@@ -36,9 +37,9 @@ class ExtensionTunnel implements vscode.Tunnel {
|
||||
export class ExtHostTunnelService extends Disposable implements IExtHostTunnelService {
|
||||
readonly _serviceBrand: undefined;
|
||||
private readonly _proxy: MainThreadTunnelServiceShape;
|
||||
private _forwardPortProvider: ((tunnelOptions: TunnelOptions) => Thenable<vscode.Tunnel> | undefined) | undefined;
|
||||
private _forwardPortProvider: ((tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => Thenable<vscode.Tunnel> | undefined) | undefined;
|
||||
private _showCandidatePort: (host: string, port: number, detail: string) => Thenable<boolean> = () => { return Promise.resolve(true); };
|
||||
private _extensionTunnels: Map<string, Map<number, vscode.Tunnel>> = new Map();
|
||||
private _extensionTunnels: Map<string, Map<number, { tunnel: vscode.Tunnel, disposeListener: IDisposable }>> = new Map();
|
||||
private _onDidChangeTunnels: Emitter<void> = new Emitter<void>();
|
||||
onDidChangeTunnels: vscode.Event<void> = this._onDidChangeTunnels.event;
|
||||
|
||||
@@ -53,8 +54,8 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
}
|
||||
}
|
||||
|
||||
async openTunnel(forward: TunnelOptions): Promise<vscode.Tunnel | undefined> {
|
||||
const tunnel = await this._proxy.$openTunnel(forward);
|
||||
async openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise<vscode.Tunnel | undefined> {
|
||||
const tunnel = await this._proxy.$openTunnel(forward, extension.displayName);
|
||||
if (tunnel) {
|
||||
const disposableTunnel: vscode.Tunnel = new ExtensionTunnel(tunnel.remoteAddress, tunnel.localAddress, () => {
|
||||
return this._proxy.$closeTunnel(tunnel.remoteAddress);
|
||||
@@ -69,21 +70,25 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
return this._proxy.$getTunnels();
|
||||
}
|
||||
|
||||
registerCandidateFinder(): Promise<void> {
|
||||
return this._proxy.$registerCandidateFinder();
|
||||
}
|
||||
|
||||
$filterCandidates(candidates: { host: string, port: number, detail: string }[]): Promise<boolean[]> {
|
||||
return Promise.all(candidates.map(candidate => {
|
||||
return this._showCandidatePort(candidate.host, candidate.port, candidate.detail);
|
||||
}));
|
||||
registerCandidateFinder(): void {
|
||||
// Every two seconds, scan to see if the candidate ports have changed;
|
||||
if (isLinux) {
|
||||
let oldPorts: { host: string, port: number, detail: string }[] | undefined = undefined;
|
||||
setInterval(async () => {
|
||||
const newPorts = await this.findCandidatePorts();
|
||||
if (!oldPorts || (JSON.stringify(oldPorts) !== JSON.stringify(newPorts))) {
|
||||
oldPorts = newPorts;
|
||||
this._proxy.$onFoundNewCandidates(oldPorts.filter(async (candidate) => await this._showCandidatePort(candidate.host, candidate.port, candidate.detail)));
|
||||
return;
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> {
|
||||
if (provider) {
|
||||
if (provider.showCandidatePort) {
|
||||
this._showCandidatePort = provider.showCandidatePort;
|
||||
await this._proxy.$setCandidateFilter();
|
||||
}
|
||||
if (provider.tunnelFactory) {
|
||||
this._forwardPortProvider = provider.tunnelFactory;
|
||||
@@ -98,11 +103,14 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
});
|
||||
}
|
||||
|
||||
async $closeTunnel(remote: { host: string, port: number }): Promise<void> {
|
||||
async $closeTunnel(remote: { host: string, port: number }, silent?: boolean): Promise<void> {
|
||||
if (this._extensionTunnels.has(remote.host)) {
|
||||
const hostMap = this._extensionTunnels.get(remote.host)!;
|
||||
if (hostMap.has(remote.port)) {
|
||||
hostMap.get(remote.port)!.dispose();
|
||||
if (silent) {
|
||||
hostMap.get(remote.port)!.disposeListener.dispose();
|
||||
}
|
||||
hostMap.get(remote.port)!.tunnel.dispose();
|
||||
hostMap.delete(remote.port);
|
||||
}
|
||||
}
|
||||
@@ -112,16 +120,16 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
this._onDidChangeTunnels.fire();
|
||||
}
|
||||
|
||||
$forwardPort(tunnelOptions: TunnelOptions): Promise<TunnelDto> | undefined {
|
||||
$forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<TunnelDto> | undefined {
|
||||
if (this._forwardPortProvider) {
|
||||
const providedPort = this._forwardPortProvider!(tunnelOptions);
|
||||
const providedPort = this._forwardPortProvider(tunnelOptions, tunnelCreationOptions);
|
||||
if (providedPort !== undefined) {
|
||||
return asPromise(() => providedPort).then(tunnel => {
|
||||
if (!this._extensionTunnels.has(tunnelOptions.remoteAddress.host)) {
|
||||
this._extensionTunnels.set(tunnelOptions.remoteAddress.host, new Map());
|
||||
}
|
||||
this._extensionTunnels.get(tunnelOptions.remoteAddress.host)!.set(tunnelOptions.remoteAddress.port, tunnel);
|
||||
this._register(tunnel.onDidDispose(() => this._proxy.$closeTunnel(tunnel.remoteAddress)));
|
||||
const disposeListener = this._register(tunnel.onDidDispose(() => this._proxy.$closeTunnel(tunnel.remoteAddress)));
|
||||
this._extensionTunnels.get(tunnelOptions.remoteAddress.host)!.set(tunnelOptions.remoteAddress.port, { tunnel, disposeListener });
|
||||
return Promise.resolve(TunnelDto.fromApiTunnel(tunnel));
|
||||
});
|
||||
}
|
||||
@@ -130,11 +138,7 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
}
|
||||
|
||||
|
||||
async $findCandidatePorts(): Promise<{ host: string, port: number, detail: string }[]> {
|
||||
if (!isLinux) {
|
||||
return [];
|
||||
}
|
||||
|
||||
async findCandidatePorts(): Promise<{ host: string, port: number, detail: string }[]> {
|
||||
const ports: { host: string, port: number, detail: string }[] = [];
|
||||
let tcp: string = '';
|
||||
let tcp6: string = '';
|
||||
@@ -189,15 +193,19 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
return ports;
|
||||
}
|
||||
|
||||
private getSockets(stdout: string) {
|
||||
private getSockets(stdout: string): { pid: number, socket: number }[] {
|
||||
const lines = stdout.trim().split('\n');
|
||||
return lines.map(line => {
|
||||
const mapped: { pid: number, socket: number }[] = [];
|
||||
lines.forEach(line => {
|
||||
const match = /\/proc\/(\d+)\/fd\/\d+ -> socket:\[(\d+)\]/.exec(line)!;
|
||||
return {
|
||||
pid: parseInt(match[1], 10),
|
||||
socket: parseInt(match[2], 10)
|
||||
};
|
||||
if (match && match.length >= 3) {
|
||||
mapped.push({
|
||||
pid: parseInt(match[1], 10),
|
||||
socket: parseInt(match[2], 10)
|
||||
});
|
||||
}
|
||||
});
|
||||
return mapped;
|
||||
}
|
||||
|
||||
private loadListeningPorts(...stdouts: string[]): { socket: number, ip: string, port: number }[] {
|
||||
|
||||
Reference in New Issue
Block a user