mirror of
https://github.com/coder/code-server.git
synced 2026-05-07 12:57:26 +02:00
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 }[] {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { SyncActionDescriptor, MenuId, MenuRegistry, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
|
||||
import { IWorkbenchActionRegistry, Extensions as WorkbenchExtensions, CATEGORIES } from 'vs/workbench/common/actions';
|
||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IEditorGroupsService, GroupOrientation } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -21,7 +21,6 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co
|
||||
import { InEditorZenModeContext, IsCenteredLayoutContext, EditorAreaVisibleContext } from 'vs/workbench/common/editor';
|
||||
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { SideBarVisibleContext } from 'vs/workbench/common/viewlet';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IViewDescriptorService, IViewsService, FocusedViewContext, ViewContainerLocation, IViewDescriptor } from 'vs/workbench/common/views';
|
||||
import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
@@ -76,7 +75,7 @@ export class ToggleActivityBarVisibilityAction extends Action2 {
|
||||
const visibility = layoutService.isVisible(Parts.ACTIVITYBAR_PART);
|
||||
const newVisibilityValue = !visibility;
|
||||
|
||||
configurationService.updateValue(ToggleActivityBarVisibilityAction.activityBarVisibleKey, newVisibilityValue, ConfigurationTarget.USER);
|
||||
configurationService.updateValue(ToggleActivityBarVisibilityAction.activityBarVisibleKey, newVisibilityValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +195,7 @@ export class ToggleSidebarPositionAction extends Action {
|
||||
const position = this.layoutService.getSideBarPosition();
|
||||
const newPositionValue = (position === Position.LEFT) ? 'right' : 'left';
|
||||
|
||||
return this.configurationService.updateValue(ToggleSidebarPositionAction.sidebarPositionConfigurationKey, newPositionValue, ConfigurationTarget.USER);
|
||||
return this.configurationService.updateValue(ToggleSidebarPositionAction.sidebarPositionConfigurationKey, newPositionValue);
|
||||
}
|
||||
|
||||
static getLabel(layoutService: IWorkbenchLayoutService): string {
|
||||
@@ -317,7 +316,7 @@ export class ToggleStatusbarVisibilityAction extends Action {
|
||||
const visibility = this.layoutService.isVisible(Parts.STATUSBAR_PART);
|
||||
const newVisibilityValue = !visibility;
|
||||
|
||||
return this.configurationService.updateValue(ToggleStatusbarVisibilityAction.statusbarVisibleKey, newVisibilityValue, ConfigurationTarget.USER);
|
||||
return this.configurationService.updateValue(ToggleStatusbarVisibilityAction.statusbarVisibleKey, newVisibilityValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,14 +418,13 @@ export class ToggleMenuBarAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
let currentVisibilityValue = getMenuBarVisibility(this.configurationService, this.environmentService);
|
||||
let currentVisibilityValue = getMenuBarVisibility(this.configurationService);
|
||||
if (typeof currentVisibilityValue !== 'string') {
|
||||
currentVisibilityValue = 'default';
|
||||
}
|
||||
|
||||
@@ -356,19 +356,13 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
const list = focused;
|
||||
|
||||
list.focusPreviousPage();
|
||||
list.reveal(list.getFocus()[0]);
|
||||
focused.focusPreviousPage();
|
||||
}
|
||||
|
||||
// Tree
|
||||
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const list = focused;
|
||||
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
list.focusPreviousPage(fakeKeyboardEvent);
|
||||
list.reveal(list.getFocus()[0]);
|
||||
focused.focusPreviousPage(fakeKeyboardEvent);
|
||||
}
|
||||
|
||||
// Ensure DOM Focus
|
||||
@@ -386,19 +380,13 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
const list = focused;
|
||||
|
||||
list.focusNextPage();
|
||||
list.reveal(list.getFocus()[0]);
|
||||
focused.focusNextPage();
|
||||
}
|
||||
|
||||
// Tree
|
||||
else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) {
|
||||
const list = focused;
|
||||
|
||||
const fakeKeyboardEvent = new KeyboardEvent('keydown');
|
||||
list.focusNextPage(fakeKeyboardEvent);
|
||||
list.reveal(list.getFocus()[0]);
|
||||
focused.focusNextPage(fakeKeyboardEvent);
|
||||
}
|
||||
|
||||
// Ensure DOM Focus
|
||||
|
||||
@@ -12,13 +12,10 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IViewlet } from 'vs/workbench/common/viewlet';
|
||||
import { IPanel } from 'vs/workbench/common/panel';
|
||||
import { Action2, MenuId, registerAction2, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { IWorkbenchActionRegistry, Extensions, CATEGORIES } from 'vs/workbench/common/actions';
|
||||
import { Direction } from 'vs/base/browser/ui/grid/grid';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { isAncestor } from 'vs/base/browser/dom';
|
||||
|
||||
@@ -275,29 +272,6 @@ export class FocusPreviousPart extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
class GoHomeContributor implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
const homeIndicator = environmentService.options?.homeIndicator;
|
||||
if (homeIndicator) {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: `workbench.actions.goHome`,
|
||||
title: nls.localize('goHome', "Go Home"),
|
||||
menu: { id: MenuId.MenubarWebNavigationMenu }
|
||||
});
|
||||
}
|
||||
async run(): Promise<void> {
|
||||
window.location.href = homeIndicator.href;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Actions Registration
|
||||
|
||||
const actionsRegistry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
|
||||
@@ -308,9 +282,12 @@ actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateLeftAc
|
||||
actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateRightAction, undefined), 'View: Navigate to the View on the Right', CATEGORIES.View.value);
|
||||
actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusNextPart, { primary: KeyCode.F6 }), 'View: Focus Next Part', CATEGORIES.View.value);
|
||||
actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPreviousPart, { primary: KeyMod.Shift | KeyCode.F6 }), 'View: Focus Previous Part', CATEGORIES.View.value);
|
||||
<<<<<<< HEAD
|
||||
|
||||
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
|
||||
workbenchRegistry.registerWorkbenchContribution(GoHomeContributor, LifecyclePhase.Ready);
|
||||
export const _1 = workbenchRegistry;
|
||||
export const _2 = GoHomeContributor;
|
||||
export const _3 = LifecyclePhase.Ready;
|
||||
=======
|
||||
>>>>>>> e4a830e9b7ca039c7c70697786d29f5b6679d775
|
||||
|
||||
@@ -141,7 +141,7 @@ export class OpenWorkspaceConfigFileAction extends Action {
|
||||
async run(): Promise<void> {
|
||||
const configuration = this.workspaceContextService.getWorkspace().configuration;
|
||||
if (configuration) {
|
||||
await this.editorService.openEditor({ resource: configuration });
|
||||
await this.editorService.openEditor({ resource: configuration, options: { pinned: true } });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/commo
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
@@ -18,6 +18,10 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IOpenWindowOptions, IWindowOpenable } from 'vs/platform/windows/common/windows';
|
||||
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
|
||||
export const ADD_ROOT_FOLDER_COMMAND_ID = 'addRootFolder';
|
||||
export const ADD_ROOT_FOLDER_LABEL = nls.localize('addFolderToWorkspace', "Add Folder to Workspace...");
|
||||
@@ -61,7 +65,7 @@ CommandsRegistry.registerCommand({
|
||||
title: nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace"),
|
||||
canSelectFolders: true,
|
||||
canSelectMany: true,
|
||||
defaultUri: dialogsService.defaultFolderPath()
|
||||
defaultUri: await dialogsService.defaultFolderPath()
|
||||
});
|
||||
|
||||
if (!folders || !folders.length) {
|
||||
@@ -116,3 +120,46 @@ CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, async functio
|
||||
|
||||
return;
|
||||
});
|
||||
|
||||
// API Command registration
|
||||
|
||||
interface IOpenFolderAPICommandOptions {
|
||||
forceNewWindow?: boolean;
|
||||
forceReuseWindow?: boolean;
|
||||
noRecentEntry?: boolean;
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: 'vscode.openFolder',
|
||||
handler: (accessor: ServicesAccessor, uri?: URI, arg?: boolean | IOpenFolderAPICommandOptions) => {
|
||||
const commandService = accessor.get(ICommandService);
|
||||
|
||||
// Be compatible to previous args by converting to options
|
||||
if (typeof arg === 'boolean') {
|
||||
arg = { forceNewWindow: arg };
|
||||
}
|
||||
|
||||
// Without URI, ask to pick a folder or workpsace to open
|
||||
if (!uri) {
|
||||
return commandService.executeCommand('_files.pickFolderAndOpen', { forceNewWindow: arg?.forceNewWindow });
|
||||
}
|
||||
|
||||
uri = URI.revive(uri);
|
||||
|
||||
const options: IOpenWindowOptions = {
|
||||
forceNewWindow: arg?.forceNewWindow,
|
||||
forceReuseWindow: arg?.forceReuseWindow,
|
||||
noRecentEntry: arg?.noRecentEntry
|
||||
};
|
||||
|
||||
const uriToOpen: IWindowOpenable = (hasWorkspaceFileExtension(uri) || uri.scheme === Schemas.untitled) ? { workspaceUri: uri } : { folderUri: uri };
|
||||
return commandService.executeCommand('_files.windowOpen', [uriToOpen], options);
|
||||
},
|
||||
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' }
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd';
|
||||
import { DragMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { normalizeDriveLetter } from 'vs/base/common/labels';
|
||||
import { MIME_BINARY } from 'vs/base/common/mime';
|
||||
import { isWindows, isWeb } from 'vs/base/common/platform';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IEditorIdentifier, GroupIdentifier } from 'vs/workbench/common/editor';
|
||||
@@ -27,7 +27,6 @@ import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsSe
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { isStandalone } from 'vs/base/browser/browser';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
@@ -320,8 +319,7 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources:
|
||||
event.dataTransfer.setData(DataTransfers.TEXT, sources.map(source => source.resource.scheme === Schemas.file ? normalize(normalizeDriveLetter(source.resource.fsPath)) : source.resource.toString()).join(lineDelimiter));
|
||||
|
||||
// Download URL: enables support to drag a tab as file to desktop (only single file supported)
|
||||
// Disabled for PWA web due to: https://github.com/microsoft/vscode/issues/83441
|
||||
if (!sources[0].isDirectory && (!isWeb || !isStandalone)) {
|
||||
if (!sources[0].isDirectory) {
|
||||
event.dataTransfer.setData(DataTransfers.DOWNLOAD_URL, [MIME_BINARY, basename(sources[0].resource), FileAccess.asBrowserUri(sources[0].resource).toString()].join(':'));
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { EventType, addDisposableListener, isAncestor, getClientArea, Dimension, position, size, IDimension } from 'vs/base/browser/dom';
|
||||
import { EventType, addDisposableListener, getClientArea, Dimension, position, size, IDimension, isAncestorUsingFlowTo } from 'vs/base/browser/dom';
|
||||
import { onDidChangeFullscreen, isFullscreen } from 'vs/base/browser/browser';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
@@ -16,7 +16,7 @@ import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart';
|
||||
import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel';
|
||||
import { Position, Parts, PanelOpensMaximizedOptions, IWorkbenchLayoutService, positionFromString, positionToString, panelOpensMaximizedFromString } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
@@ -41,7 +41,6 @@ import { INotificationService, NotificationsFilter } from 'vs/platform/notificat
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { WINDOW_ACTIVE_BORDER, WINDOW_INACTIVE_BORDER } from 'vs/workbench/common/theme';
|
||||
import { LineNumbersType } from 'vs/editor/common/config/editorOptions';
|
||||
import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activitybarPart';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
@@ -91,21 +90,6 @@ enum Classes {
|
||||
WINDOW_BORDER = 'border'
|
||||
}
|
||||
|
||||
interface PanelActivityState {
|
||||
id: string;
|
||||
name?: string;
|
||||
pinned: boolean;
|
||||
order: number;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
interface SideBarActivityState {
|
||||
id: string;
|
||||
pinned: boolean;
|
||||
order: number;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
export abstract class Layout extends Disposable implements IWorkbenchLayoutService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
@@ -319,7 +303,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this._register(addDisposableListener(this.container, EventType.SCROLL, () => this.container.scrollTop = 0));
|
||||
|
||||
// Menubar visibility changes
|
||||
if ((isWindows || isLinux || isWeb) && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') {
|
||||
if ((isWindows || isLinux || isWeb) && getTitleBarStyle(this.configurationService) === 'custom') {
|
||||
this._register(this.titleService.onMenubarVisibilityChange(visible => this.onMenubarToggled(visible)));
|
||||
}
|
||||
|
||||
@@ -357,8 +341,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
}
|
||||
}
|
||||
|
||||
// Change edge snapping accordingly
|
||||
this.workbenchGrid.edgeSnapping = this.state.fullscreen;
|
||||
|
||||
// Changing fullscreen state of the window has an impact on custom title bar visibility, so we need to update
|
||||
if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') {
|
||||
if (getTitleBarStyle(this.configurationService) === 'custom') {
|
||||
// Propagate to grid
|
||||
this.workbenchGrid.setViewVisible(this.titleBarPartView, this.isVisible(Parts.TITLEBAR_PART));
|
||||
|
||||
@@ -407,7 +394,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
}
|
||||
|
||||
// Menubar visibility
|
||||
const newMenubarVisibility = getMenuBarVisibility(this.configurationService, this.environmentService);
|
||||
const newMenubarVisibility = getMenuBarVisibility(this.configurationService);
|
||||
this.setMenubarVisibility(newMenubarVisibility, !!skipLayout);
|
||||
|
||||
// Centered Layout
|
||||
@@ -451,7 +438,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
}
|
||||
|
||||
private updateWindowBorder(skipLayout: boolean = false) {
|
||||
if (isWeb || getTitleBarStyle(this.configurationService, this.environmentService) !== 'custom') {
|
||||
if (isWeb || getTitleBarStyle(this.configurationService) !== 'custom') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -495,7 +482,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.state.fullscreen = isFullscreen();
|
||||
|
||||
// Menubar visibility
|
||||
this.state.menuBar.visibility = getMenuBarVisibility(this.configurationService, this.environmentService);
|
||||
this.state.menuBar.visibility = getMenuBarVisibility(this.configurationService);
|
||||
|
||||
// Activity bar visibility
|
||||
this.state.activityBar.hidden = !this.configurationService.getValue<string>(Settings.ACTIVITYBAR_VISIBLE);
|
||||
@@ -582,167 +569,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
const { views } = defaultLayout;
|
||||
if (views?.length) {
|
||||
this.state.views.defaults = views.map(v => v.id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO@eamodio Everything below here is deprecated and will be removed once Codespaces migrates
|
||||
|
||||
const { sidebar } = defaultLayout;
|
||||
if (sidebar) {
|
||||
if (sidebar.visible !== undefined) {
|
||||
if (sidebar.visible) {
|
||||
storageService.remove(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE);
|
||||
} else {
|
||||
storageService.store(Storage.SIDEBAR_HIDDEN, true, StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
if (sidebar.containers?.length) {
|
||||
const sidebarState: SideBarActivityState[] = [];
|
||||
|
||||
let order = -1;
|
||||
for (const container of sidebar.containers.sort((a, b) => (a.order ?? 1) - (b.order ?? 1))) {
|
||||
let viewletId;
|
||||
switch (container.id) {
|
||||
case 'explorer':
|
||||
viewletId = 'workbench.view.explorer';
|
||||
break;
|
||||
case 'run':
|
||||
viewletId = 'workbench.view.debug';
|
||||
break;
|
||||
case 'scm':
|
||||
viewletId = 'workbench.view.scm';
|
||||
break;
|
||||
case 'search':
|
||||
viewletId = 'workbench.view.search';
|
||||
break;
|
||||
case 'extensions':
|
||||
viewletId = 'workbench.view.extensions';
|
||||
break;
|
||||
case 'remote':
|
||||
viewletId = 'workbench.view.remote';
|
||||
break;
|
||||
default:
|
||||
viewletId = `workbench.view.extension.${container.id}`;
|
||||
}
|
||||
|
||||
if (container.active) {
|
||||
storageService.store(SidebarPart.activeViewletSettingsKey, viewletId, StorageScope.WORKSPACE);
|
||||
}
|
||||
|
||||
if (container.order !== undefined || (container.active === undefined && container.visible !== undefined)) {
|
||||
order = container.order ?? (order + 1);
|
||||
const state: SideBarActivityState = {
|
||||
id: viewletId,
|
||||
order: order,
|
||||
pinned: (container.active || container.visible) ?? true,
|
||||
visible: (container.active || container.visible) ?? true
|
||||
};
|
||||
|
||||
sidebarState.push(state);
|
||||
}
|
||||
|
||||
if (container.views !== undefined) {
|
||||
const viewsState: { id: string, isHidden?: boolean, order?: number }[] = [];
|
||||
const viewsWorkspaceState: { [id: string]: { collapsed: boolean, isHidden?: boolean, size?: number } } = {};
|
||||
|
||||
for (const view of container.views) {
|
||||
if (view.order !== undefined || view.visible !== undefined) {
|
||||
viewsState.push({
|
||||
id: view.id,
|
||||
isHidden: view.visible === undefined ? undefined : !view.visible,
|
||||
order: view.order === undefined ? undefined : view.order
|
||||
});
|
||||
}
|
||||
|
||||
if (view.collapsed !== undefined) {
|
||||
viewsWorkspaceState[view.id] = {
|
||||
collapsed: view.collapsed,
|
||||
isHidden: view.visible === undefined ? undefined : !view.visible,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
storageService.store(`${viewletId}.state.hidden`, JSON.stringify(viewsState), StorageScope.GLOBAL);
|
||||
storageService.store(`${viewletId}.state`, JSON.stringify(viewsWorkspaceState), StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
if (sidebarState.length) {
|
||||
storageService.store(ActivitybarPart.PINNED_VIEW_CONTAINERS, JSON.stringify(sidebarState), StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { panel } = defaultLayout;
|
||||
if (panel) {
|
||||
if (panel.visible !== undefined) {
|
||||
if (panel.visible) {
|
||||
storageService.store(Storage.PANEL_HIDDEN, false, StorageScope.WORKSPACE);
|
||||
} else {
|
||||
storageService.remove(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
if (panel.containers?.length) {
|
||||
const panelState: PanelActivityState[] = [];
|
||||
|
||||
let order = -1;
|
||||
for (const container of panel.containers.sort((a, b) => (a.order ?? 1) - (b.order ?? 1))) {
|
||||
let name;
|
||||
let panelId = container.id;
|
||||
switch (panelId) {
|
||||
case 'terminal':
|
||||
name = 'Terminal';
|
||||
panelId = 'workbench.panel.terminal';
|
||||
break;
|
||||
case 'debug':
|
||||
name = 'Debug Console';
|
||||
panelId = 'workbench.panel.repl';
|
||||
break;
|
||||
case 'problems':
|
||||
name = 'Problems';
|
||||
panelId = 'workbench.panel.markers';
|
||||
break;
|
||||
case 'output':
|
||||
name = 'Output';
|
||||
panelId = 'workbench.panel.output';
|
||||
break;
|
||||
case 'comments':
|
||||
name = 'Comments';
|
||||
panelId = 'workbench.panel.comments';
|
||||
break;
|
||||
case 'refactor':
|
||||
name = 'Refactor Preview';
|
||||
panelId = 'refactorPreview';
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (container.active) {
|
||||
storageService.store(PanelPart.activePanelSettingsKey, panelId, StorageScope.WORKSPACE);
|
||||
}
|
||||
|
||||
if (container.order !== undefined || (container.active === undefined && container.visible !== undefined)) {
|
||||
order = container.order ?? (order + 1);
|
||||
const state: PanelActivityState = {
|
||||
id: panelId,
|
||||
name: name,
|
||||
order: order,
|
||||
pinned: (container.active || container.visible) ?? true,
|
||||
visible: (container.active || container.visible) ?? true
|
||||
};
|
||||
|
||||
panelState.push(state);
|
||||
}
|
||||
}
|
||||
|
||||
if (panelState.length) {
|
||||
storageService.store(PanelPart.PINNED_PANELS, JSON.stringify(panelState), StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,7 +576,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
const initialFilesToOpen = this.getInitialFilesToOpen();
|
||||
|
||||
// Only restore editors if we are not instructed to open files initially
|
||||
this.state.editor.restoreEditors = initialFilesToOpen === undefined;
|
||||
// or when `window.restoreWindows` setting is explicitly set to `preserve`
|
||||
const forceRestoreEditors = this.configurationService.getValue<string>('window.restoreWindows') === 'preserve';
|
||||
this.state.editor.restoreEditors = !!forceRestoreEditors || initialFilesToOpen === undefined;
|
||||
|
||||
// Files to open, diff or create
|
||||
if (initialFilesToOpen !== undefined) {
|
||||
@@ -996,7 +824,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
const container = this.getContainer(part);
|
||||
|
||||
return !!container && isAncestor(activeElement, container);
|
||||
return !!container && isAncestorUsingFlowTo(activeElement, container);
|
||||
}
|
||||
|
||||
focusPart(part: Parts): void {
|
||||
@@ -1050,7 +878,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
isVisible(part: Parts): boolean {
|
||||
switch (part) {
|
||||
case Parts.TITLEBAR_PART:
|
||||
if (getTitleBarStyle(this.configurationService, this.environmentService) === 'native') {
|
||||
if (getTitleBarStyle(this.configurationService) === 'native') {
|
||||
return false;
|
||||
} else if (!this.state.fullscreen && !isWeb) {
|
||||
return true;
|
||||
@@ -1246,7 +1074,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
// State
|
||||
if (this.state.zenMode.active) {
|
||||
this.storageService.store(Storage.ZEN_MODE_ENABLED, true, StorageScope.WORKSPACE);
|
||||
this.storageService.store(Storage.ZEN_MODE_ENABLED, true, StorageScope.WORKSPACE, StorageTarget.USER);
|
||||
|
||||
// Exit zen mode on shutdown unless configured to keep
|
||||
this.state.zenMode.transitionDisposables.add(this.storageService.onWillSaveState(e => {
|
||||
@@ -1310,6 +1138,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.container.prepend(workbenchGrid.element);
|
||||
this.container.setAttribute('role', 'application');
|
||||
this.workbenchGrid = workbenchGrid;
|
||||
this.workbenchGrid.edgeSnapping = this.state.fullscreen;
|
||||
|
||||
[titleBar, editorPart, activityBar, panelPart, sideBar, statusBar].forEach((part: Part) => {
|
||||
this._register(part.onDidVisibilityChange((visible) => {
|
||||
@@ -1331,18 +1160,18 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
? grid.getViewCachedVisibleSize(this.sideBarPartView)
|
||||
: grid.getViewSize(this.sideBarPartView).width;
|
||||
|
||||
this.storageService.store(Storage.SIDEBAR_SIZE, sideBarSize, StorageScope.GLOBAL);
|
||||
this.storageService.store(Storage.SIDEBAR_SIZE, sideBarSize, StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
|
||||
const panelSize = this.state.panel.hidden
|
||||
? grid.getViewCachedVisibleSize(this.panelPartView)
|
||||
: (this.state.panel.position === Position.BOTTOM ? grid.getViewSize(this.panelPartView).height : grid.getViewSize(this.panelPartView).width);
|
||||
|
||||
this.storageService.store(Storage.PANEL_SIZE, panelSize, StorageScope.GLOBAL);
|
||||
this.storageService.store(Storage.PANEL_DIMENSION, positionToString(this.state.panel.position), StorageScope.GLOBAL);
|
||||
this.storageService.store(Storage.PANEL_SIZE, panelSize, StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
this.storageService.store(Storage.PANEL_DIMENSION, positionToString(this.state.panel.position), StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
|
||||
const gridSize = grid.getViewSize();
|
||||
this.storageService.store(Storage.GRID_WIDTH, gridSize.width, StorageScope.GLOBAL);
|
||||
this.storageService.store(Storage.GRID_HEIGHT, gridSize.height, StorageScope.GLOBAL);
|
||||
this.storageService.store(Storage.GRID_WIDTH, gridSize.width, StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
this.storageService.store(Storage.GRID_HEIGHT, gridSize.height, StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1373,7 +1202,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
centerEditorLayout(active: boolean, skipLayout?: boolean): void {
|
||||
this.state.editor.centered = active;
|
||||
|
||||
this.storageService.store(Storage.CENTERED_LAYOUT_ENABLED, active, StorageScope.WORKSPACE);
|
||||
this.storageService.store(Storage.CENTERED_LAYOUT_ENABLED, active, StorageScope.WORKSPACE, StorageTarget.USER);
|
||||
|
||||
let smartActive = active;
|
||||
const activeEditor = this.editorService.activeEditor;
|
||||
@@ -1486,7 +1315,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
// Remember in settings
|
||||
if (hidden) {
|
||||
this.storageService.store(Storage.EDITOR_HIDDEN, true, StorageScope.WORKSPACE);
|
||||
this.storageService.store(Storage.EDITOR_HIDDEN, true, StorageScope.WORKSPACE, StorageTarget.USER);
|
||||
} else {
|
||||
this.storageService.remove(Storage.EDITOR_HIDDEN, StorageScope.WORKSPACE);
|
||||
}
|
||||
@@ -1547,7 +1376,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
// Remember in settings
|
||||
const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY;
|
||||
if (hidden !== defaultHidden) {
|
||||
this.storageService.store(Storage.SIDEBAR_HIDDEN, hidden ? 'true' : 'false', StorageScope.WORKSPACE);
|
||||
this.storageService.store(Storage.SIDEBAR_HIDDEN, hidden ? 'true' : 'false', StorageScope.WORKSPACE, StorageTarget.USER);
|
||||
} else {
|
||||
this.storageService.remove(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE);
|
||||
}
|
||||
@@ -1612,14 +1441,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
}
|
||||
// Remember in settings
|
||||
if (!hidden) {
|
||||
this.storageService.store(Storage.PANEL_HIDDEN, 'false', StorageScope.WORKSPACE);
|
||||
this.storageService.store(Storage.PANEL_HIDDEN, 'false', StorageScope.WORKSPACE, StorageTarget.USER);
|
||||
}
|
||||
else {
|
||||
this.storageService.remove(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE);
|
||||
|
||||
// Remember this setting only when panel is hiding
|
||||
if (this.state.panel.wasLastMaximized) {
|
||||
this.storageService.store(Storage.PANEL_LAST_IS_MAXIMIZED, true, StorageScope.WORKSPACE);
|
||||
this.storageService.store(Storage.PANEL_LAST_IS_MAXIMIZED, true, StorageScope.WORKSPACE, StorageTarget.USER);
|
||||
}
|
||||
else {
|
||||
this.storageService.remove(Storage.PANEL_LAST_IS_MAXIMIZED, StorageScope.WORKSPACE);
|
||||
@@ -1637,10 +1466,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
if (!this.state.panel.hidden) {
|
||||
if (this.state.panel.position === Position.BOTTOM) {
|
||||
this.state.panel.lastNonMaximizedHeight = size.height;
|
||||
this.storageService.store(Storage.PANEL_LAST_NON_MAXIMIZED_HEIGHT, this.state.panel.lastNonMaximizedHeight, StorageScope.GLOBAL);
|
||||
this.storageService.store(Storage.PANEL_LAST_NON_MAXIMIZED_HEIGHT, this.state.panel.lastNonMaximizedHeight, StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
} else {
|
||||
this.state.panel.lastNonMaximizedWidth = size.width;
|
||||
this.storageService.store(Storage.PANEL_LAST_NON_MAXIMIZED_WIDTH, this.state.panel.lastNonMaximizedWidth, StorageScope.GLOBAL);
|
||||
this.storageService.store(Storage.PANEL_LAST_NON_MAXIMIZED_WIDTH, this.state.panel.lastNonMaximizedWidth, StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1715,7 +1544,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.state.panel.position = position;
|
||||
|
||||
// Save panel position
|
||||
this.storageService.store(Storage.PANEL_POSITION, newPositionValue, StorageScope.WORKSPACE);
|
||||
this.storageService.store(Storage.PANEL_POSITION, newPositionValue, StorageScope.WORKSPACE, StorageTarget.USER);
|
||||
|
||||
// Adjust CSS
|
||||
const panelContainer = assertIsDefined(panelPart.getContainer());
|
||||
|
||||
@@ -220,6 +220,10 @@ body.web {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.monaco-workbench input[type="checkbox"]:focus {
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.monaco-workbench [tabindex="0"]:active,
|
||||
.monaco-workbench [tabindex="-1"]:active,
|
||||
.monaco-workbench select:active,
|
||||
|
||||
@@ -10,15 +10,14 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
|
||||
import { Action, IAction, Separator, SubmenuAction } from 'vs/base/common/actions';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { SyncActionDescriptor, IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IMenuService, MenuId, IMenu, registerAction2, Action2, IAction2Options } from 'vs/platform/actions/common/actions';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { ActivityAction, ActivityActionViewItem, ICompositeBar, ICompositeBarColors, ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositeBarActions';
|
||||
import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
|
||||
import { CATEGORIES } from 'vs/workbench/common/actions';
|
||||
import { IActivity } from 'vs/workbench/common/activity';
|
||||
import { ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_ACTIVE_FOCUS_BORDER, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
|
||||
@@ -26,44 +25,31 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { isMacintosh, isWeb } from 'vs/base/common/platform';
|
||||
import { getCurrentAuthenticationSessionInfo, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService';
|
||||
import { AuthenticationSession } from 'vs/editor/common/modes';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { AnchorAlignment, AnchorAxisAlignment } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { getTitleBarStyle } from 'vs/platform/windows/common/windows';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export class ViewContainerActivityAction extends ActivityAction {
|
||||
|
||||
private static readonly preventDoubleClickDelay = 300;
|
||||
|
||||
private readonly viewletService: IViewletService;
|
||||
private readonly layoutService: IWorkbenchLayoutService;
|
||||
private readonly telemetryService: ITelemetryService;
|
||||
private readonly configurationService: IConfigurationService;
|
||||
|
||||
private lastRun: number;
|
||||
private lastRun = 0;
|
||||
|
||||
constructor(
|
||||
activity: IActivity,
|
||||
@IViewletService viewletService: IViewletService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
) {
|
||||
super(activity);
|
||||
|
||||
this.lastRun = 0;
|
||||
this.viewletService = viewletService;
|
||||
this.layoutService = layoutService;
|
||||
this.telemetryService = telemetryService;
|
||||
this.configurationService = configurationService;
|
||||
}
|
||||
|
||||
updateActivity(activity: IActivity): void {
|
||||
@@ -105,6 +91,7 @@ export class ViewContainerActivityAction extends ActivityAction {
|
||||
|
||||
this.logAction('show');
|
||||
await this.viewletService.openViewlet(this.activity.id, true);
|
||||
|
||||
return this.activate();
|
||||
}
|
||||
|
||||
@@ -117,21 +104,18 @@ export class ViewContainerActivityAction extends ActivityAction {
|
||||
}
|
||||
}
|
||||
|
||||
export const ACCOUNTS_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showAccounts';
|
||||
class MenuActivityActionViewItem extends ActivityActionViewItem {
|
||||
|
||||
export class AccountsActionViewItem extends ActivityActionViewItem {
|
||||
constructor(
|
||||
private readonly menuId: MenuId,
|
||||
action: ActivityAction,
|
||||
colors: (theme: IColorTheme) => ICompositeBarColors,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextMenuService protected contextMenuService: IContextMenuService,
|
||||
@IMenuService protected menuService: IMenuService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IMenuService protected readonly menuService: IMenuService,
|
||||
@IContextMenuService protected readonly contextMenuService: IContextMenuService,
|
||||
@IContextKeyService protected readonly contextKeyService: IContextKeyService,
|
||||
@IConfigurationService protected readonly configurationService: IConfigurationService,
|
||||
@IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
super(action, { draggable: false, colors, icon: true }, themeService);
|
||||
}
|
||||
@@ -161,12 +145,103 @@ export class AccountsActionViewItem extends ActivityActionViewItem {
|
||||
}));
|
||||
}
|
||||
|
||||
private async getActions(accountsMenu: IMenu) {
|
||||
protected async showContextMenu(e?: MouseEvent): Promise<void> {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
const menu = disposables.add(this.menuService.createMenu(this.menuId, this.contextKeyService));
|
||||
const actions = await this.resolveActions(menu, disposables);
|
||||
|
||||
const isUsingCustomMenu = isWeb || (getTitleBarStyle(this.configurationService) !== 'native' && !isMacintosh); // see #40262
|
||||
const position = this.configurationService.getValue('workbench.sideBar.location');
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => isUsingCustomMenu ? this.container : e || this.container,
|
||||
anchorAlignment: isUsingCustomMenu ? (position === 'left' ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT) : undefined,
|
||||
anchorAxisAlignment: isUsingCustomMenu ? AnchorAxisAlignment.HORIZONTAL : AnchorAxisAlignment.VERTICAL,
|
||||
getActions: () => actions,
|
||||
onHide: () => disposables.dispose()
|
||||
});
|
||||
}
|
||||
|
||||
protected async resolveActions(menu: IMenu, disposables: DisposableStore): Promise<IAction[]> {
|
||||
const actions: IAction[] = [];
|
||||
|
||||
disposables.add(createAndFillInActionBarActions(menu, undefined, { primary: [], secondary: actions }));
|
||||
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
|
||||
export class HomeActivityActionViewItem extends MenuActivityActionViewItem {
|
||||
|
||||
static readonly HOME_BAR_VISIBILITY_PREFERENCE = 'workbench.activity.showHomeIndicator';
|
||||
|
||||
constructor(
|
||||
private readonly goHomeHref: string,
|
||||
action: ActivityAction,
|
||||
colors: (theme: IColorTheme) => ICompositeBarColors,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@IStorageService private readonly storageService: IStorageService
|
||||
) {
|
||||
super(MenuId.MenubarHomeMenu, action, colors, themeService, menuService, contextMenuService, contextKeyService, configurationService, environmentService);
|
||||
}
|
||||
|
||||
protected async resolveActions(homeMenu: IMenu, disposables: DisposableStore): Promise<IAction[]> {
|
||||
const actions = [];
|
||||
|
||||
// Go Home
|
||||
actions.push(disposables.add(new Action('goHome', nls.localize('goHome', "Go Home"), undefined, true, async () => window.location.href = this.goHomeHref)));
|
||||
actions.push(disposables.add(new Separator()));
|
||||
|
||||
// Contributed
|
||||
const contributedActions = await super.resolveActions(homeMenu, disposables);
|
||||
actions.push(...contributedActions);
|
||||
|
||||
// Hide
|
||||
if (contributedActions.length > 0) {
|
||||
actions.push(disposables.add(new Separator()));
|
||||
}
|
||||
actions.push(disposables.add(new Action('hide', nls.localize('hide', "Hide"), undefined, true, async () => {
|
||||
this.storageService.store(HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE, false, StorageScope.GLOBAL, StorageTarget.USER);
|
||||
})));
|
||||
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
|
||||
export class AccountsActivityActionViewItem extends MenuActivityActionViewItem {
|
||||
|
||||
static readonly ACCOUNTS_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showAccounts';
|
||||
|
||||
constructor(
|
||||
action: ActivityAction,
|
||||
colors: (theme: IColorTheme) => ICompositeBarColors,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
) {
|
||||
super(MenuId.AccountsContext, action, colors, themeService, menuService, contextMenuService, contextKeyService, configurationService, environmentService);
|
||||
}
|
||||
|
||||
protected async resolveActions(accountsMenu: IMenu, disposables: DisposableStore): Promise<IAction[]> {
|
||||
await super.resolveActions(accountsMenu, disposables);
|
||||
|
||||
const otherCommands = accountsMenu.getActions();
|
||||
const providers = this.authenticationService.getProviderIds();
|
||||
const allSessions = providers.map(async id => {
|
||||
const allSessions = providers.map(async providerId => {
|
||||
try {
|
||||
const sessions = await this.authenticationService.getSessions(id);
|
||||
const sessions = await this.authenticationService.getSessions(providerId);
|
||||
|
||||
const groupedSessions: { [label: string]: AuthenticationSession[] } = {};
|
||||
sessions.forEach(session => {
|
||||
@@ -177,14 +252,9 @@ export class AccountsActionViewItem extends ActivityActionViewItem {
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
providerId: id,
|
||||
sessions: groupedSessions
|
||||
};
|
||||
return { providerId, sessions: groupedSessions };
|
||||
} catch {
|
||||
return {
|
||||
providerId: id
|
||||
};
|
||||
return { providerId };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -196,132 +266,67 @@ export class AccountsActionViewItem extends ActivityActionViewItem {
|
||||
|
||||
if (sessionInfo.sessions) {
|
||||
Object.keys(sessionInfo.sessions).forEach(accountName => {
|
||||
const hasEmbedderAccountSession = sessionInfo.sessions[accountName].some(session => session.id === (authenticationSession?.id));
|
||||
const manageExtensionsAction = new Action(`configureSessions${accountName}`, nls.localize('manageTrustedExtensions', "Manage Trusted Extensions"), '', true, _ => {
|
||||
const manageExtensionsAction = disposables.add(new Action(`configureSessions${accountName}`, nls.localize('manageTrustedExtensions', "Manage Trusted Extensions"), '', true, () => {
|
||||
return this.authenticationService.manageTrustedExtensionsForAccount(sessionInfo.providerId, accountName);
|
||||
});
|
||||
const signOutAction = new Action('signOut', nls.localize('signOut', "Sign Out"), '', true, _ => {
|
||||
return this.authenticationService.signOutOfAccount(sessionInfo.providerId, accountName);
|
||||
});
|
||||
}));
|
||||
|
||||
const actions = [manageExtensionsAction];
|
||||
const signOutAction = disposables.add(new Action('signOut', nls.localize('signOut', "Sign Out"), '', true, () => {
|
||||
return this.authenticationService.signOutOfAccount(sessionInfo.providerId, accountName);
|
||||
}));
|
||||
|
||||
const providerSubMenuActions = [manageExtensionsAction];
|
||||
|
||||
const hasEmbedderAccountSession = sessionInfo.sessions[accountName].some(session => session.id === (authenticationSession?.id));
|
||||
if (!hasEmbedderAccountSession || authenticationSession?.canSignOut) {
|
||||
actions.push(signOutAction);
|
||||
providerSubMenuActions.push(signOutAction);
|
||||
}
|
||||
|
||||
const menu = new SubmenuAction('activitybar.submenu', `${accountName} (${providerDisplayName})`, actions);
|
||||
menus.push(menu);
|
||||
const providerSubMenu = disposables.add(new SubmenuAction('activitybar.submenu', `${accountName} (${providerDisplayName})`, providerSubMenuActions));
|
||||
menus.push(providerSubMenu);
|
||||
});
|
||||
} else {
|
||||
const menu = new Action('providerUnavailable', nls.localize('authProviderUnavailable', '{0} is currently unavailable', providerDisplayName));
|
||||
menus.push(menu);
|
||||
const providerUnavailableAction = disposables.add(new Action('providerUnavailable', nls.localize('authProviderUnavailable', '{0} is currently unavailable', providerDisplayName)));
|
||||
menus.push(providerUnavailableAction);
|
||||
}
|
||||
});
|
||||
|
||||
if (menus.length && otherCommands.length) {
|
||||
menus.push(new Separator());
|
||||
menus.push(disposables.add(new Separator()));
|
||||
}
|
||||
|
||||
otherCommands.forEach((group, i) => {
|
||||
const actions = group[1];
|
||||
menus = menus.concat(actions);
|
||||
if (i !== otherCommands.length - 1) {
|
||||
menus.push(new Separator());
|
||||
menus.push(disposables.add(new Separator()));
|
||||
}
|
||||
});
|
||||
|
||||
if (menus.length) {
|
||||
menus.push(new Separator());
|
||||
menus.push(disposables.add(new Separator()));
|
||||
}
|
||||
|
||||
menus.push(new Action('hide', nls.localize('hide', "Hide"), undefined, true, _ => {
|
||||
this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, false, StorageScope.GLOBAL);
|
||||
return Promise.resolve();
|
||||
}));
|
||||
menus.push(disposables.add(new Action('hide', nls.localize('hide', "Hide"), undefined, true, async () => {
|
||||
this.storageService.store(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER);
|
||||
})));
|
||||
|
||||
return menus;
|
||||
}
|
||||
|
||||
private async showContextMenu(e?: MouseEvent): Promise<void> {
|
||||
const accountsActions: IAction[] = [];
|
||||
const accountsMenu = this.menuService.createMenu(MenuId.AccountsContext, this.contextKeyService);
|
||||
const actionsDisposable = createAndFillInActionBarActions(accountsMenu, undefined, { primary: [], secondary: accountsActions });
|
||||
const native = getTitleBarStyle(this.configurationService, this.environmentService) === 'native';
|
||||
const position = this.configurationService.getValue('workbench.sideBar.location');
|
||||
|
||||
const containerPosition = DOM.getDomNodePagePosition(this.container);
|
||||
const location = { x: containerPosition.left + (position === 'left' ? containerPosition.width : 0), y: containerPosition.top };
|
||||
const actions = await this.getActions(accountsMenu);
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => !native ? location : e || this.container,
|
||||
anchorAlignment: !native ? (position === 'left' ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT) : undefined,
|
||||
getActions: () => actions,
|
||||
onHide: () => {
|
||||
accountsMenu.dispose();
|
||||
dispose(actionsDisposable);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class GlobalActivityActionViewItem extends ActivityActionViewItem {
|
||||
export class GlobalActivityActionViewItem extends MenuActivityActionViewItem {
|
||||
|
||||
constructor(
|
||||
action: ActivityAction,
|
||||
colors: (theme: IColorTheme) => ICompositeBarColors,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IMenuService private readonly menuService: IMenuService,
|
||||
@IContextMenuService protected readonly contextMenuService: IContextMenuService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
super(action, { draggable: false, colors, icon: true }, themeService);
|
||||
}
|
||||
|
||||
render(container: HTMLElement): void {
|
||||
super.render(container);
|
||||
|
||||
// Context menus are triggered on mouse down so that an item can be picked
|
||||
// and executed with releasing the mouse over it
|
||||
|
||||
this._register(DOM.addDisposableListener(this.container, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
this.showContextMenu(e);
|
||||
}));
|
||||
|
||||
this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
|
||||
let event = new StandardKeyboardEvent(e);
|
||||
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
this.showContextMenu();
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(DOM.addDisposableListener(this.container, TouchEventType.Tap, (e: GestureEvent) => {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
this.showContextMenu();
|
||||
}));
|
||||
}
|
||||
|
||||
private showContextMenu(e?: MouseEvent): void {
|
||||
const globalActivityActions: IAction[] = [];
|
||||
const globalActivityMenu = this.menuService.createMenu(MenuId.GlobalActivity, this.contextKeyService);
|
||||
const actionsDisposable = createAndFillInActionBarActions(globalActivityMenu, undefined, { primary: [], secondary: globalActivityActions });
|
||||
const native = getTitleBarStyle(this.configurationService, this.environmentService) === 'native';
|
||||
const position = this.configurationService.getValue('workbench.sideBar.location');
|
||||
|
||||
const containerPosition = DOM.getDomNodePagePosition(this.container);
|
||||
const location = { x: containerPosition.left + (position === 'left' ? containerPosition.width : 0), y: containerPosition.top + containerPosition.height };
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => !native ? location : e || this.container,
|
||||
anchorAlignment: !native ? (position === 'left' ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT) : undefined,
|
||||
getActions: () => globalActivityActions,
|
||||
onHide: () => {
|
||||
globalActivityMenu.dispose();
|
||||
dispose(actionsDisposable);
|
||||
}
|
||||
});
|
||||
super(MenuId.GlobalActivity, action, colors, themeService, menuService, contextMenuService, contextKeyService, configurationService, environmentService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,106 +343,62 @@ export class PlaceHolderToggleCompositePinnedAction extends ToggleCompositePinne
|
||||
}
|
||||
}
|
||||
|
||||
class SwitchSideBarViewAction extends Action {
|
||||
class SwitchSideBarViewAction extends Action2 {
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
name: string,
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@IActivityBarService private readonly activityBarService: IActivityBarService
|
||||
desc: Readonly<IAction2Options>,
|
||||
private readonly offset: number
|
||||
) {
|
||||
super(id, name);
|
||||
super(desc);
|
||||
}
|
||||
|
||||
async run(offset: number): Promise<void> {
|
||||
const visibleViewletIds = this.activityBarService.getVisibleViewContainerIds();
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const activityBarService = accessor.get(IActivityBarService);
|
||||
const viewletService = accessor.get(IViewletService);
|
||||
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
const visibleViewletIds = activityBarService.getVisibleViewContainerIds();
|
||||
|
||||
const activeViewlet = viewletService.getActiveViewlet();
|
||||
if (!activeViewlet) {
|
||||
return;
|
||||
}
|
||||
let targetViewletId: string | undefined;
|
||||
for (let i = 0; i < visibleViewletIds.length; i++) {
|
||||
if (visibleViewletIds[i] === activeViewlet.getId()) {
|
||||
targetViewletId = visibleViewletIds[(i + visibleViewletIds.length + offset) % visibleViewletIds.length];
|
||||
targetViewletId = visibleViewletIds[(i + visibleViewletIds.length + this.offset) % visibleViewletIds.length];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await this.viewletService.openViewlet(targetViewletId, true);
|
||||
await viewletService.openViewlet(targetViewletId, true);
|
||||
}
|
||||
}
|
||||
|
||||
export class PreviousSideBarViewAction extends SwitchSideBarViewAction {
|
||||
|
||||
static readonly ID = 'workbench.action.previousSideBarView';
|
||||
static readonly LABEL = nls.localize('previousSideBarView', 'Previous Side Bar View');
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
name: string,
|
||||
@IViewletService viewletService: IViewletService,
|
||||
@IActivityBarService activityBarService: IActivityBarService
|
||||
) {
|
||||
super(id, name, viewletService, activityBarService);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
return super.run(-1);
|
||||
}
|
||||
}
|
||||
|
||||
export class NextSideBarViewAction extends SwitchSideBarViewAction {
|
||||
|
||||
static readonly ID = 'workbench.action.nextSideBarView';
|
||||
static readonly LABEL = nls.localize('nextSideBarView', 'Next Side Bar View');
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
name: string,
|
||||
@IViewletService viewletService: IViewletService,
|
||||
@IActivityBarService activityBarService: IActivityBarService
|
||||
) {
|
||||
super(id, name, viewletService, activityBarService);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
return super.run(1);
|
||||
}
|
||||
}
|
||||
|
||||
export class HomeAction extends Action {
|
||||
|
||||
constructor(
|
||||
private readonly href: string,
|
||||
name: string,
|
||||
icon: Codicon
|
||||
) {
|
||||
super('workbench.action.home', name, icon.classNames);
|
||||
}
|
||||
|
||||
async run(event: MouseEvent): Promise<void> {
|
||||
let openInNewWindow = false;
|
||||
if (isMacintosh) {
|
||||
openInNewWindow = event.metaKey;
|
||||
} else {
|
||||
openInNewWindow = event.ctrlKey;
|
||||
}
|
||||
|
||||
if (openInNewWindow) {
|
||||
DOM.windowOpenNoOpener(this.href);
|
||||
} else {
|
||||
window.location.href = this.href;
|
||||
registerAction2(
|
||||
class PreviousSideBarViewAction extends SwitchSideBarViewAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.previousSideBarView',
|
||||
title: { value: nls.localize('previousSideBarView', "Previous Side Bar View"), original: 'Previous Side Bar View' },
|
||||
category: CATEGORIES.View,
|
||||
f1: true
|
||||
}, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export class HomeActionViewItem extends ActionViewItem {
|
||||
|
||||
constructor(action: IAction) {
|
||||
super(undefined, action, { icon: true, label: false, useEventAsContext: true });
|
||||
registerAction2(
|
||||
class NextSideBarViewAction extends SwitchSideBarViewAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.nextSideBarView',
|
||||
title: { value: nls.localize('nextSideBarView', "Next Side Bar View"), original: 'Next Side Bar View' },
|
||||
category: CATEGORIES.View,
|
||||
f1: true
|
||||
}, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
|
||||
const activityBarBackgroundColor = theme.getColor(ACTIVITY_BAR_BACKGROUND);
|
||||
@@ -510,6 +471,7 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
|
||||
left: 9px;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active:before,
|
||||
@@ -549,7 +511,3 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(PreviousSideBarViewAction), 'View: Previous Side Bar View', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(NextSideBarViewAction), 'View: Next Side Bar View', CATEGORIES.View.value);
|
||||
|
||||
@@ -8,24 +8,24 @@ import * as nls from 'vs/nls';
|
||||
import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { GLOBAL_ACTIVITY_ID, IActivity, ACCOUNTS_ACTIVITY_ID } from 'vs/workbench/common/activity';
|
||||
import { Part } from 'vs/workbench/browser/part';
|
||||
import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, AccountsActionViewItem, HomeAction, HomeActionViewItem, ACCOUNTS_VISIBILITY_PREFERENCE_KEY } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
|
||||
import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, AccountsActivityActionViewItem, HomeActivityActionViewItem } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
|
||||
import { IBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ToggleActivityBarVisibilityAction, ToggleMenuBarAction, ToggleSidebarPositionAction } from 'vs/workbench/browser/actions/layoutActions';
|
||||
import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService';
|
||||
import { IThemeService, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BORDER } from 'vs/workbench/common/theme';
|
||||
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbench/browser/parts/compositeBar';
|
||||
import { Dimension, createCSSRule, asCSSUrl, addDisposableListener, EventType } from 'vs/base/browser/dom';
|
||||
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ToggleCompositePinnedAction, ICompositeBarColors, ActivityAction, ICompositeActivity } from 'vs/workbench/browser/parts/compositeBarActions';
|
||||
import { IViewDescriptorService, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewContainerModel, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views';
|
||||
import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { isUndefinedOrNull, assertIsDefined, isString } from 'vs/base/common/types';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
@@ -34,7 +34,6 @@ import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menuba
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { getMenuBarVisibility } from 'vs/platform/windows/common/windows';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
import { Before2D } from 'vs/workbench/browser/dnd';
|
||||
import { Codicon, iconRegistry } from 'vs/base/common/codicons';
|
||||
import { Action, Separator } from 'vs/base/common/actions';
|
||||
@@ -43,41 +42,45 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { CATEGORIES } from 'vs/workbench/common/actions';
|
||||
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
|
||||
|
||||
interface IPlaceholderViewContainer {
|
||||
id: string;
|
||||
name?: string;
|
||||
iconUrl?: UriComponents;
|
||||
iconCSS?: string;
|
||||
views?: { when?: string }[];
|
||||
readonly id: string;
|
||||
readonly name?: string;
|
||||
readonly iconUrl?: UriComponents;
|
||||
readonly themeIcon?: ThemeIcon;
|
||||
readonly views?: { when?: string }[];
|
||||
}
|
||||
|
||||
interface IPinnedViewContainer {
|
||||
id: string;
|
||||
pinned: boolean;
|
||||
order?: number;
|
||||
visible: boolean;
|
||||
readonly id: string;
|
||||
readonly pinned: boolean;
|
||||
readonly order?: number;
|
||||
readonly visible: boolean;
|
||||
}
|
||||
|
||||
interface ICachedViewContainer {
|
||||
id: string;
|
||||
readonly id: string;
|
||||
name?: string;
|
||||
icon?: URI | string;
|
||||
pinned: boolean;
|
||||
order?: number;
|
||||
icon?: URI | ThemeIcon;
|
||||
readonly pinned: boolean;
|
||||
readonly order?: number;
|
||||
visible: boolean;
|
||||
views?: { when?: string }[];
|
||||
}
|
||||
|
||||
const settingsViewBarIcon = registerIcon('settings-view-bar-icon', Codicon.settingsGear, nls.localize('settingsViewBarIcon', 'Settings icon in the view bar.'));
|
||||
const accountsViewBarIcon = registerIcon('accounts-view-bar-icon', Codicon.account, nls.localize('accountsViewBarIcon', 'Accounts icon in the view bar.'));
|
||||
|
||||
export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private static readonly ACTION_HEIGHT = 48;
|
||||
static readonly PINNED_VIEW_CONTAINERS = 'workbench.activity.pinnedViewlets2';
|
||||
private static readonly PINNED_VIEW_CONTAINERS = 'workbench.activity.pinnedViewlets2';
|
||||
private static readonly PLACEHOLDER_VIEW_CONTAINERS = 'workbench.activity.placeholderViewlets';
|
||||
private static readonly HOME_BAR_VISIBILITY_PREFERENCE = 'workbench.activity.showHomeIndicator';
|
||||
private static readonly ACTION_HEIGHT = 48;
|
||||
private static readonly ACCOUNTS_ACTION_INDEX = 0;
|
||||
|
||||
//#region IView
|
||||
|
||||
readonly minimumWidth: number = 48;
|
||||
@@ -100,17 +103,17 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
|
||||
private globalActivityAction: ActivityAction | undefined;
|
||||
private globalActivityActionBar: ActionBar | undefined;
|
||||
private readonly globalActivity: ICompositeActivity[] = [];
|
||||
private globalActivitiesContainer: HTMLElement | undefined;
|
||||
private readonly globalActivity: ICompositeActivity[] = [];
|
||||
|
||||
private accountsActivityAction: ActivityAction | undefined;
|
||||
|
||||
private accountsActivity: ICompositeActivity[] = [];
|
||||
private readonly accountsActivity: ICompositeActivity[] = [];
|
||||
|
||||
private readonly compositeActions = new Map<string, { activityAction: ViewContainerActivityAction, pinnedAction: ToggleCompositePinnedAction }>();
|
||||
private readonly viewContainerDisposables = new Map<string, IDisposable>();
|
||||
|
||||
private readonly keyboardNavigationDisposables = new DisposableStore();
|
||||
private readonly keyboardNavigationDisposables = this._register(new DisposableStore());
|
||||
|
||||
private readonly location = ViewContainerLocation.Sidebar;
|
||||
|
||||
@@ -125,27 +128,36 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
|
||||
) {
|
||||
super(Parts.ACTIVITYBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
|
||||
|
||||
storageKeysSyncRegistryService.registerStorageKey({ key: ActivitybarPart.PINNED_VIEW_CONTAINERS, version: 1 });
|
||||
storageKeysSyncRegistryService.registerStorageKey({ key: ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, version: 1 });
|
||||
storageKeysSyncRegistryService.registerStorageKey({ key: ACCOUNTS_VISIBILITY_PREFERENCE_KEY, version: 1 });
|
||||
|
||||
this.migrateFromOldCachedViewContainersValue();
|
||||
|
||||
for (const cachedViewContainer of this.cachedViewContainers) {
|
||||
if (environmentService.remoteAuthority // In remote window, hide activity bar entries until registered.
|
||||
|| this.shouldBeHidden(cachedViewContainer.id, cachedViewContainer)
|
||||
if (
|
||||
environmentService.remoteAuthority || // In remote window, hide activity bar entries until registered
|
||||
this.shouldBeHidden(cachedViewContainer.id, cachedViewContainer)
|
||||
) {
|
||||
cachedViewContainer.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.compositeBar = this.createCompositeBar();
|
||||
|
||||
this.onDidRegisterViewContainers(this.getViewContainers());
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private createCompositeBar() {
|
||||
const cachedItems = this.cachedViewContainers
|
||||
.map(v => ({ id: v.id, name: v.name, visible: v.visible, order: v.order, pinned: v.pinned }));
|
||||
this.compositeBar = this._register(this.instantiationService.createInstance(CompositeBar, cachedItems, {
|
||||
.map(container => ({
|
||||
id: container.id,
|
||||
name: container.name,
|
||||
visible: container.visible,
|
||||
order: container.order,
|
||||
pinned: container.pinned
|
||||
}));
|
||||
|
||||
return this._register(this.instantiationService.createInstance(CompositeBar, cachedItems, {
|
||||
icon: true,
|
||||
orientation: ActionsOrientation.VERTICAL,
|
||||
preventLoopNavigation: true,
|
||||
@@ -154,8 +166,9 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction,
|
||||
getOnCompositeClickAction: (compositeId: string) => new Action(compositeId, '', '', true, () => this.viewsService.isViewContainerVisible(compositeId) ? Promise.resolve(this.viewsService.closeViewContainer(compositeId)) : this.viewsService.openViewContainer(compositeId)),
|
||||
getContextMenuActions: () => {
|
||||
const menuBarVisibility = getMenuBarVisibility(this.configurationService, this.environmentService);
|
||||
const actions = [];
|
||||
|
||||
// Home
|
||||
if (this.homeBarContainer) {
|
||||
actions.push(new Action(
|
||||
'toggleHomeBarAction',
|
||||
@@ -166,22 +179,26 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
));
|
||||
}
|
||||
|
||||
// Menu
|
||||
const menuBarVisibility = getMenuBarVisibility(this.configurationService);
|
||||
if (menuBarVisibility === 'compact' || (menuBarVisibility === 'hidden' && isWeb)) {
|
||||
actions.push(this.instantiationService.createInstance(ToggleMenuBarAction, ToggleMenuBarAction.ID, menuBarVisibility === 'compact' ? nls.localize('hideMenu', "Hide Menu") : nls.localize('showMenu', "Show Menu")));
|
||||
}
|
||||
|
||||
const toggleAccountsVisibilityAction = new Action(
|
||||
// Accounts
|
||||
actions.push(new Action(
|
||||
'toggleAccountsVisibility',
|
||||
this.accountsVisibilityPreference ? nls.localize('hideAccounts', "Hide Accounts") : nls.localize('showAccounts', "Show Accounts"),
|
||||
undefined,
|
||||
true,
|
||||
async () => { this.accountsVisibilityPreference = !this.accountsVisibilityPreference; }
|
||||
);
|
||||
|
||||
actions.push(toggleAccountsVisibilityAction);
|
||||
));
|
||||
actions.push(new Separator());
|
||||
|
||||
// Toggle Sidebar
|
||||
actions.push(this.instantiationService.createInstance(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.getLabel(this.layoutService)));
|
||||
|
||||
// Toggle Activity Bar
|
||||
actions.push(new Action(
|
||||
ToggleActivityBarVisibilityAction.ID,
|
||||
nls.localize('hideActivitBar', "Hide Activity Bar"),
|
||||
@@ -204,19 +221,12 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
colors: (theme: IColorTheme) => this.getActivitybarItemColors(theme),
|
||||
overflowActionSize: ActivitybarPart.ACTION_HEIGHT
|
||||
}));
|
||||
|
||||
this.onDidRegisterViewContainers(this.getViewContainers());
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
focusActivityBar(): void {
|
||||
this.compositeBar.focus();
|
||||
}
|
||||
|
||||
private getContextMenuActionsForComposite(compositeId: string): Action[] {
|
||||
const viewContainer = this.viewDescriptorService.getViewContainerById(compositeId)!;
|
||||
|
||||
const actions = [];
|
||||
|
||||
const viewContainer = this.viewDescriptorService.getViewContainerById(compositeId)!;
|
||||
const defaultLocation = this.viewDescriptorService.getDefaultViewContainerLocation(viewContainer)!;
|
||||
if (defaultLocation !== this.viewDescriptorService.getViewContainerLocation(viewContainer)) {
|
||||
actions.push(new Action('resetLocationAction', nls.localize('resetLocation', "Reset Location"), undefined, true, async () => {
|
||||
@@ -253,13 +263,13 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
disposables.clear();
|
||||
this.onDidRegisterExtensions();
|
||||
this.compositeBar.onDidChange(() => this.saveCachedViewContainers(), this, disposables);
|
||||
this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e), this, disposables);
|
||||
this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e), this, disposables);
|
||||
}));
|
||||
|
||||
// Register for configuration changes
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('window.menuBarVisibility')) {
|
||||
if (getMenuBarVisibility(this.configurationService, this.environmentService) === 'compact') {
|
||||
if (getMenuBarVisibility(this.configurationService) === 'compact') {
|
||||
this.installMenubar();
|
||||
} else {
|
||||
this.uninstallMenubar();
|
||||
@@ -277,6 +287,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
if (from === this.location) {
|
||||
this.onDidDeregisterViewContainer(container);
|
||||
}
|
||||
|
||||
if (to === this.location) {
|
||||
this.onDidRegisterViewContainers([container]);
|
||||
}
|
||||
@@ -306,6 +317,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
private onDidViewContainerVisible(id: string): void {
|
||||
const viewContainer = this.getViewContainer(id);
|
||||
if (viewContainer) {
|
||||
|
||||
// Update the composite bar by adding
|
||||
this.compositeBar.addComposite(viewContainer);
|
||||
this.compositeBar.activateComposite(viewContainer.id);
|
||||
@@ -313,7 +325,8 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
if (viewContainer.hideIfEmpty) {
|
||||
const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer);
|
||||
if (viewContainerModel.activeViewDescriptors.length === 0) {
|
||||
this.hideComposite(viewContainer.id); // Update the composite bar by hiding
|
||||
// Update the composite bar by hiding
|
||||
this.hideComposite(viewContainer.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,6 +352,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
if (typeof priority !== 'number') {
|
||||
priority = 0;
|
||||
}
|
||||
|
||||
const activity: ICompositeActivity = { badge, clazz, priority };
|
||||
const activityCache = activityId === GLOBAL_ACTIVITY_ID ? this.globalActivity : this.accountsActivity;
|
||||
|
||||
@@ -387,16 +401,18 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
|
||||
private getCumulativeNumberBadge(activityCache: ICompositeActivity[], priority: number): NumberBadge {
|
||||
const numberActivities = activityCache.filter(activity => activity.badge instanceof NumberBadge && activity.priority === priority);
|
||||
let number = numberActivities.reduce((result, activity) => { return result + (<NumberBadge>activity.badge).number; }, 0);
|
||||
let descriptorFn = (): string => {
|
||||
const number = numberActivities.reduce((result, activity) => { return result + (<NumberBadge>activity.badge).number; }, 0);
|
||||
const descriptorFn = (): string => {
|
||||
return numberActivities.reduce((result, activity, index) => {
|
||||
result = result + (<NumberBadge>activity.badge).getDescription();
|
||||
if (index < numberActivities.length - 1) {
|
||||
result = result + '\n';
|
||||
result = `${result}\n`;
|
||||
}
|
||||
|
||||
return result;
|
||||
}, '');
|
||||
};
|
||||
|
||||
return new NumberBadge(number, descriptorFn);
|
||||
}
|
||||
|
||||
@@ -414,11 +430,19 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
}
|
||||
|
||||
private installMenubar() {
|
||||
if (this.menuBar) {
|
||||
return; // prevent menu bar from installing twice #110720
|
||||
}
|
||||
|
||||
this.menuBarContainer = document.createElement('div');
|
||||
this.menuBarContainer.classList.add('menubar');
|
||||
|
||||
const content = assertIsDefined(this.content);
|
||||
content.prepend(this.menuBarContainer);
|
||||
if (this.homeBarContainer) {
|
||||
content.insertBefore(this.menuBarContainer, this.homeBarContainer.nextSibling);
|
||||
} else {
|
||||
content.prepend(this.menuBarContainer);
|
||||
}
|
||||
|
||||
// Menubar: install a custom menu bar depending on configuration
|
||||
this.menuBar = this._register(this.instantiationService.createInstance(CustomMenubarControl));
|
||||
@@ -442,12 +466,12 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
codicon = Codicon.code;
|
||||
}
|
||||
|
||||
this.createHomeBar(homeIndicator.href, homeIndicator.title, codicon);
|
||||
this.createHomeBar(homeIndicator.href, codicon);
|
||||
this.onDidChangeHomeBarVisibility();
|
||||
}
|
||||
|
||||
// Install menubar if compact
|
||||
if (getMenuBarVisibility(this.configurationService, this.environmentService) === 'compact') {
|
||||
if (getMenuBarVisibility(this.configurationService) === 'compact') {
|
||||
this.installMenubar();
|
||||
}
|
||||
|
||||
@@ -456,11 +480,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
|
||||
// Global action bar
|
||||
this.globalActivitiesContainer = document.createElement('div');
|
||||
this.globalActivitiesContainer.classList.add('global-activity');
|
||||
this.content.appendChild(this.globalActivitiesContainer);
|
||||
|
||||
this.createGlobalActivityActionBar(this.globalActivitiesContainer);
|
||||
|
||||
// Keyboard Navigation
|
||||
this.registerKeyboardNavigationListeners();
|
||||
|
||||
return this.content;
|
||||
@@ -528,23 +552,19 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private createHomeBar(href: string, title: string, icon: Codicon): void {
|
||||
private createHomeBar(href: string, icon: Codicon): void {
|
||||
this.homeBarContainer = document.createElement('div');
|
||||
this.homeBarContainer.setAttribute('aria-label', nls.localize('homeIndicator', "Home"));
|
||||
this.homeBarContainer.setAttribute('role', 'toolbar');
|
||||
this.homeBarContainer.classList.add('home-bar');
|
||||
|
||||
this.homeBar = this._register(new ActionBar(this.homeBarContainer, {
|
||||
actionViewItemProvider: action => this.instantiationService.createInstance(HomeActivityActionViewItem, href, action as ActivityAction, (theme: IColorTheme) => this.getActivitybarItemColors(theme)),
|
||||
orientation: ActionsOrientation.VERTICAL,
|
||||
animated: false,
|
||||
ariaLabel: nls.localize('home', "Home"),
|
||||
actionViewItemProvider: action => new HomeActionViewItem(action),
|
||||
allowContextMenu: true,
|
||||
animated: false,
|
||||
preventLoopNavigation: true,
|
||||
ignoreOrientationForPreviousAndNextKey: true
|
||||
}));
|
||||
@@ -552,35 +572,15 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
const homeBarIconBadge = document.createElement('div');
|
||||
homeBarIconBadge.classList.add('home-bar-icon-badge');
|
||||
this.homeBarContainer.appendChild(homeBarIconBadge);
|
||||
this.homeBar.push(this._register(this.instantiationService.createInstance(HomeAction, href, title, icon)));
|
||||
|
||||
this.homeBar.push(this._register(new ActivityAction({
|
||||
id: 'workbench.actions.home',
|
||||
name: nls.localize('home', "Home"),
|
||||
cssClass: icon.classNames
|
||||
})));
|
||||
|
||||
const content = assertIsDefined(this.content);
|
||||
content.prepend(this.homeBarContainer);
|
||||
}
|
||||
|
||||
updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
const container = assertIsDefined(this.getContainer());
|
||||
const background = this.getColor(ACTIVITY_BAR_BACKGROUND) || '';
|
||||
container.style.backgroundColor = background;
|
||||
|
||||
const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder) || '';
|
||||
container.classList.toggle('bordered', !!borderColor);
|
||||
container.style.borderColor = borderColor ? borderColor : '';
|
||||
}
|
||||
|
||||
private getActivitybarItemColors(theme: IColorTheme): ICompositeBarColors {
|
||||
return {
|
||||
activeForegroundColor: theme.getColor(ACTIVITY_BAR_FOREGROUND),
|
||||
inactiveForegroundColor: theme.getColor(ACTIVITY_BAR_INACTIVE_FOREGROUND),
|
||||
activeBorderColor: theme.getColor(ACTIVITY_BAR_ACTIVE_BORDER),
|
||||
activeBackground: theme.getColor(ACTIVITY_BAR_ACTIVE_BACKGROUND),
|
||||
badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND),
|
||||
badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND),
|
||||
dragAndDropBorder: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BORDER),
|
||||
activeBackgroundColor: undefined, inactiveBackgroundColor: undefined, activeBorderBottomColor: undefined,
|
||||
};
|
||||
content.appendChild(this.homeBarContainer);
|
||||
}
|
||||
|
||||
private createGlobalActivityActionBar(container: HTMLElement): void {
|
||||
@@ -591,7 +591,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
}
|
||||
|
||||
if (action.id === 'workbench.actions.accounts') {
|
||||
return this.instantiationService.createInstance(AccountsActionViewItem, action as ActivityAction, (theme: IColorTheme) => this.getActivitybarItemColors(theme));
|
||||
return this.instantiationService.createInstance(AccountsActivityActionViewItem, action as ActivityAction, (theme: IColorTheme) => this.getActivitybarItemColors(theme));
|
||||
}
|
||||
|
||||
throw new Error(`No view item for action '${action.id}'`);
|
||||
@@ -603,18 +603,18 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
ignoreOrientationForPreviousAndNextKey: true
|
||||
}));
|
||||
|
||||
this.globalActivityAction = new ActivityAction({
|
||||
this.globalActivityAction = this._register(new ActivityAction({
|
||||
id: 'workbench.actions.manage',
|
||||
name: nls.localize('manage', "Manage"),
|
||||
cssClass: Codicon.settingsGear.classNames
|
||||
});
|
||||
cssClass: ThemeIcon.asClassName(settingsViewBarIcon)
|
||||
}));
|
||||
|
||||
if (this.accountsVisibilityPreference) {
|
||||
this.accountsActivityAction = new ActivityAction({
|
||||
this.accountsActivityAction = this._register(new ActivityAction({
|
||||
id: 'workbench.actions.accounts',
|
||||
name: nls.localize('accounts', "Accounts"),
|
||||
cssClass: Codicon.account.classNames
|
||||
});
|
||||
cssClass: ThemeIcon.asClassName(accountsViewBarIcon)
|
||||
}));
|
||||
|
||||
this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX });
|
||||
}
|
||||
@@ -628,11 +628,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
this.globalActivityActionBar.pull(ActivitybarPart.ACCOUNTS_ACTION_INDEX);
|
||||
this.accountsActivityAction = undefined;
|
||||
} else {
|
||||
this.accountsActivityAction = new ActivityAction({
|
||||
this.accountsActivityAction = this._register(new ActivityAction({
|
||||
id: 'workbench.actions.accounts',
|
||||
name: nls.localize('accounts', "Accounts"),
|
||||
cssClass: Codicon.account.classNames
|
||||
});
|
||||
}));
|
||||
this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX });
|
||||
}
|
||||
}
|
||||
@@ -723,7 +723,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
return ActivitybarPart.toActivity(id, name, icon, focusCommand?.id || id);
|
||||
}
|
||||
|
||||
private static toActivity(id: string, name: string, icon: URI | string | undefined, keybindingId: string | undefined): IActivity {
|
||||
private static toActivity(id: string, name: string, icon: URI | ThemeIcon | undefined, keybindingId: string | undefined): IActivity {
|
||||
let cssClass: string | undefined = undefined;
|
||||
let iconUrl: URI | undefined = undefined;
|
||||
if (URI.isUri(icon)) {
|
||||
@@ -736,9 +736,10 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
-webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%;
|
||||
-webkit-mask-size: 24px;
|
||||
`);
|
||||
} else if (isString(icon)) {
|
||||
cssClass = icon;
|
||||
} else if (ThemeIcon.isThemeIcon(icon)) {
|
||||
cssClass = ThemeIcon.asClassName(icon);
|
||||
}
|
||||
|
||||
return { id, name, cssClass, iconUrl, keybindingId };
|
||||
}
|
||||
|
||||
@@ -810,6 +811,35 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
.map(v => v.id);
|
||||
}
|
||||
|
||||
focusActivityBar(): void {
|
||||
this.compositeBar.focus();
|
||||
}
|
||||
|
||||
updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
const container = assertIsDefined(this.getContainer());
|
||||
const background = this.getColor(ACTIVITY_BAR_BACKGROUND) || '';
|
||||
container.style.backgroundColor = background;
|
||||
|
||||
const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder) || '';
|
||||
container.classList.toggle('bordered', !!borderColor);
|
||||
container.style.borderColor = borderColor ? borderColor : '';
|
||||
}
|
||||
|
||||
private getActivitybarItemColors(theme: IColorTheme): ICompositeBarColors {
|
||||
return {
|
||||
activeForegroundColor: theme.getColor(ACTIVITY_BAR_FOREGROUND),
|
||||
inactiveForegroundColor: theme.getColor(ACTIVITY_BAR_INACTIVE_FOREGROUND),
|
||||
activeBorderColor: theme.getColor(ACTIVITY_BAR_ACTIVE_BORDER),
|
||||
activeBackground: theme.getColor(ACTIVITY_BAR_ACTIVE_BACKGROUND),
|
||||
badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND),
|
||||
badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND),
|
||||
dragAndDropBorder: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BORDER),
|
||||
activeBackgroundColor: undefined, inactiveBackgroundColor: undefined, activeBorderBottomColor: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
layout(width: number, height: number): void {
|
||||
if (!this.layoutService.isVisible(Parts.ACTIVITYBAR_PART)) {
|
||||
return;
|
||||
@@ -834,6 +864,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
|
||||
private getViewContainer(id: string): ViewContainer | undefined {
|
||||
const viewContainer = this.viewDescriptorService.getViewContainerById(id);
|
||||
|
||||
return viewContainer && this.viewDescriptorService.getViewContainerLocation(viewContainer) === this.location ? viewContainer : undefined;
|
||||
}
|
||||
|
||||
@@ -841,7 +872,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
return this.viewDescriptorService.getViewContainersByLocation(this.location);
|
||||
}
|
||||
|
||||
private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void {
|
||||
private onDidStorageValueChange(e: IStorageValueChangeEvent): void {
|
||||
if (e.key === ActivitybarPart.PINNED_VIEW_CONTAINERS && e.scope === StorageScope.GLOBAL
|
||||
&& this.pinnedViewContainersValue !== this.getStoredPinnedViewContainersValue() /* This checks if current window changed the value or not */) {
|
||||
this._pinnedViewContainersValue = undefined;
|
||||
@@ -870,11 +901,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
this.compositeBar.setCompositeBarItems(newCompositeItems);
|
||||
}
|
||||
|
||||
if (e.key === ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE && e.scope === StorageScope.GLOBAL) {
|
||||
if (e.key === HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE && e.scope === StorageScope.GLOBAL) {
|
||||
this.onDidChangeHomeBarVisibility();
|
||||
}
|
||||
|
||||
if (e.key === ACCOUNTS_VISIBILITY_PREFERENCE_KEY && e.scope === StorageScope.GLOBAL) {
|
||||
if (e.key === AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY && e.scope === StorageScope.GLOBAL) {
|
||||
this.toggleAccountsActivity();
|
||||
}
|
||||
}
|
||||
@@ -917,12 +948,13 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
const cachedViewContainer = this._cachedViewContainers.filter(cached => cached.id === placeholderViewContainer.id)[0];
|
||||
if (cachedViewContainer) {
|
||||
cachedViewContainer.name = placeholderViewContainer.name;
|
||||
cachedViewContainer.icon = placeholderViewContainer.iconCSS ? placeholderViewContainer.iconCSS :
|
||||
cachedViewContainer.icon = placeholderViewContainer.themeIcon ? ThemeIcon.revive(placeholderViewContainer.themeIcon) :
|
||||
placeholderViewContainer.iconUrl ? URI.revive(placeholderViewContainer.iconUrl) : undefined;
|
||||
cachedViewContainer.views = placeholderViewContainer.views;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this._cachedViewContainers;
|
||||
}
|
||||
|
||||
@@ -933,10 +965,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
visible,
|
||||
order
|
||||
})));
|
||||
|
||||
this.setPlaceholderViewContainers(cachedViewContainers.map(({ id, icon, name, views }) => (<IPlaceholderViewContainer>{
|
||||
id,
|
||||
iconUrl: URI.isUri(icon) ? icon : undefined,
|
||||
iconCSS: isString(icon) ? icon : undefined,
|
||||
themeIcon: ThemeIcon.isThemeIcon(icon) ? icon : undefined,
|
||||
name,
|
||||
views
|
||||
})));
|
||||
@@ -971,7 +1004,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
}
|
||||
|
||||
private setStoredPinnedViewContainersValue(value: string): void {
|
||||
this.storageService.store(ActivitybarPart.PINNED_VIEW_CONTAINERS, value, StorageScope.GLOBAL);
|
||||
this.storageService.store(ActivitybarPart.PINNED_VIEW_CONTAINERS, value, StorageScope.GLOBAL, StorageTarget.USER);
|
||||
}
|
||||
|
||||
private getPlaceholderViewContainers(): IPlaceholderViewContainer[] {
|
||||
@@ -1003,37 +1036,23 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
}
|
||||
|
||||
private setStoredPlaceholderViewContainersValue(value: string): void {
|
||||
this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.GLOBAL);
|
||||
this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
}
|
||||
|
||||
private get homeBarVisibilityPreference(): boolean {
|
||||
return this.storageService.getBoolean(ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, StorageScope.GLOBAL, true);
|
||||
return this.storageService.getBoolean(HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE, StorageScope.GLOBAL, true);
|
||||
}
|
||||
|
||||
private set homeBarVisibilityPreference(value: boolean) {
|
||||
this.storageService.store(ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, value, StorageScope.GLOBAL);
|
||||
this.storageService.store(HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE, value, StorageScope.GLOBAL, StorageTarget.USER);
|
||||
}
|
||||
|
||||
private get accountsVisibilityPreference(): boolean {
|
||||
return this.storageService.getBoolean(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, StorageScope.GLOBAL, true);
|
||||
return this.storageService.getBoolean(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, StorageScope.GLOBAL, true);
|
||||
}
|
||||
|
||||
private set accountsVisibilityPreference(value: boolean) {
|
||||
this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, value, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
private migrateFromOldCachedViewContainersValue(): void {
|
||||
const value = this.storageService.get('workbench.activity.pinnedViewlets', StorageScope.GLOBAL);
|
||||
if (value !== undefined) {
|
||||
const storedStates: Array<string | ICachedViewContainer> = JSON.parse(value);
|
||||
const cachedViewContainers = storedStates.map(c => {
|
||||
const serialized: ICachedViewContainer = typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true, order: undefined, visible: true, name: undefined, icon: undefined, views: undefined } : c;
|
||||
serialized.visible = isUndefinedOrNull(serialized.visible) ? true : serialized.visible;
|
||||
return serialized;
|
||||
});
|
||||
this.storeCachedViewContainersState(cachedViewContainers);
|
||||
this.storageService.remove('workbench.activity.pinnedViewlets', StorageScope.GLOBAL);
|
||||
}
|
||||
this.storageService.store(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, value, StorageScope.GLOBAL, StorageTarget.USER);
|
||||
}
|
||||
|
||||
toJSON(): object {
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content > .home-bar > .monaco-action-bar .action-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content .composite-bar > .monaco-action-bar .action-item::before,
|
||||
.monaco-workbench .activitybar > .content .composite-bar > .monaco-action-bar .action-item::after {
|
||||
position: absolute;
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
<<<<<<< HEAD
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -59,6 +60,8 @@
|
||||
/* NOTE@coder: Hide since it doesn't seem to do anything when used with
|
||||
code-server except open the VS Code repository. */
|
||||
display: none !important;
|
||||
=======
|
||||
>>>>>>> e4a830e9b7ca039c7c70697786d29f5b6679d775
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content > .home-bar > .home-bar-icon-badge {
|
||||
@@ -92,10 +95,6 @@
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content > .composite-bar-excess {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/** Menu Bar */
|
||||
|
||||
.monaco-workbench .activitybar .menubar {
|
||||
|
||||
@@ -18,7 +18,7 @@ import { Composite, CompositeRegistry } from 'vs/workbench/browser/composite';
|
||||
import { IComposite } from 'vs/workbench/common/composite';
|
||||
import { CompositeProgressIndicator } from 'vs/workbench/services/progress/browser/progressIndicator';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
@@ -199,7 +199,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
// Store in preferences
|
||||
const id = this.activeComposite.getId();
|
||||
if (id !== this.defaultCompositeId) {
|
||||
this.storageService.store(this.activeCompositeSettingsKey, id, StorageScope.WORKSPACE);
|
||||
this.storageService.store(this.activeCompositeSettingsKey, id, StorageScope.WORKSPACE, StorageTarget.USER);
|
||||
} else {
|
||||
this.storageService.remove(this.activeCompositeSettingsKey, StorageScope.WORKSPACE);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IDialogHandler, IDialogResult, IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
import { IDialogsModel, IDialogViewItem } from 'vs/workbench/common/dialogs';
|
||||
import { BrowserDialogHandler } from 'vs/workbench/browser/parts/dialogs/dialogHandler';
|
||||
import { DialogService } from 'vs/workbench/services/dialogs/common/dialogService';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export class DialogHandlerContribution extends Disposable implements IWorkbenchContribution {
|
||||
private impl: IDialogHandler;
|
||||
|
||||
private model: IDialogsModel;
|
||||
private currentDialog: IDialogViewItem | undefined;
|
||||
|
||||
constructor(
|
||||
@IDialogService private dialogService: IDialogService,
|
||||
@ILogService logService: ILogService,
|
||||
@ILayoutService layoutService: ILayoutService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IProductService productService: IProductService,
|
||||
@IClipboardService clipboardService: IClipboardService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.impl = new BrowserDialogHandler(logService, layoutService, themeService, keybindingService, productService, clipboardService);
|
||||
|
||||
this.model = (this.dialogService as DialogService).model;
|
||||
|
||||
this._register(this.model.onDidShowDialog(() => {
|
||||
if (!this.currentDialog) {
|
||||
this.processDialogs();
|
||||
}
|
||||
}));
|
||||
|
||||
this.processDialogs();
|
||||
}
|
||||
|
||||
private async processDialogs(): Promise<void> {
|
||||
while (this.model.dialogs.length) {
|
||||
this.currentDialog = this.model.dialogs[0];
|
||||
|
||||
let result: IDialogResult | undefined = undefined;
|
||||
if (this.currentDialog.args.confirmArgs) {
|
||||
const args = this.currentDialog.args.confirmArgs;
|
||||
result = await this.impl.confirm(args.confirmation);
|
||||
} else if (this.currentDialog.args.inputArgs) {
|
||||
const args = this.currentDialog.args.inputArgs;
|
||||
result = await this.impl.input(args.severity, args.message, args.buttons, args.inputs, args.options);
|
||||
} else if (this.currentDialog.args.showArgs) {
|
||||
const args = this.currentDialog.args.showArgs;
|
||||
result = await this.impl.show(args.severity, args.message, args.buttons, args.options);
|
||||
} else {
|
||||
await this.impl.about();
|
||||
}
|
||||
|
||||
this.currentDialog.close(result);
|
||||
this.currentDialog = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
|
||||
workbenchRegistry.registerWorkbenchContribution(DialogHandlerContribution, LifecyclePhase.Starting);
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { IDialogService, IDialogOptions, IConfirmation, IConfirmationResult, DialogType, IShowResult, IInputResult, ICheckbox, IInput } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IDialogOptions, IConfirmation, IConfirmationResult, DialogType, IShowResult, IInputResult, ICheckbox, IInput, IDialogHandler } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
@@ -17,12 +17,9 @@ import { EventHelper } from 'vs/base/browser/dom';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { fromNow } from 'vs/base/common/date';
|
||||
|
||||
export class DialogService implements IDialogService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
export class BrowserDialogHandler implements IDialogHandler {
|
||||
|
||||
private static readonly ALLOWABLE_COMMANDS = [
|
||||
'copy',
|
||||
@@ -91,7 +88,7 @@ export class DialogService implements IDialogService {
|
||||
keyEventProcessor: (event: StandardKeyboardEvent) => {
|
||||
const resolved = this.keybindingService.softDispatch(event, this.layoutService.container);
|
||||
if (resolved && resolved.commandId) {
|
||||
if (DialogService.ALLOWABLE_COMMANDS.indexOf(resolved.commandId) === -1) {
|
||||
if (BrowserDialogHandler.ALLOWABLE_COMMANDS.indexOf(resolved.commandId) === -1) {
|
||||
EventHelper.stop(event, true);
|
||||
}
|
||||
}
|
||||
@@ -145,5 +142,3 @@ export class DialogService implements IDialogService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IDialogService, DialogService, true);
|
||||
@@ -20,7 +20,7 @@ import { dispose, IDisposable, Disposable, DisposableStore } from 'vs/base/commo
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { assertIsDefined, assertAllDefined } from 'vs/base/common/types';
|
||||
import { BinarySize } from 'vs/platform/files/common/files';
|
||||
import { ByteSize } from 'vs/platform/files/common/files';
|
||||
|
||||
export interface IOpenCallbacks {
|
||||
openInternal: (input: EditorInput, options: EditorOptions | undefined) => Promise<void>;
|
||||
@@ -182,7 +182,7 @@ interface ResourceViewerDelegate {
|
||||
|
||||
class ResourceViewer {
|
||||
|
||||
private static readonly MAX_OPEN_INTERNAL_SIZE = BinarySize.MB * 200; // max size until we offer an action to open internally
|
||||
private static readonly MAX_OPEN_INTERNAL_SIZE = ByteSize.MB * 200; // max size until we offer an action to open internally
|
||||
|
||||
static show(
|
||||
descriptor: IResourceDescriptor,
|
||||
@@ -211,7 +211,7 @@ class FileTooLargeFileView {
|
||||
scrollbar: DomScrollableElement,
|
||||
delegate: ResourceViewerDelegate
|
||||
) {
|
||||
const size = BinarySize.formatSize(descriptorSize);
|
||||
const size = ByteSize.formatSize(descriptorSize);
|
||||
delegate.metadataClb(size);
|
||||
|
||||
clearNode(container);
|
||||
@@ -233,7 +233,7 @@ class FileSeemsBinaryFileView {
|
||||
scrollbar: DomScrollableElement,
|
||||
delegate: ResourceViewerDelegate
|
||||
) {
|
||||
delegate.metadataClb(typeof descriptor.size === 'number' ? BinarySize.formatSize(descriptor.size) : '');
|
||||
delegate.metadataClb(typeof descriptor.size === 'number' ? ByteSize.formatSize(descriptor.size) : '');
|
||||
|
||||
clearNode(container);
|
||||
|
||||
|
||||
@@ -491,7 +491,7 @@ export class BreadcrumbsControl {
|
||||
if (element instanceof FileElement) {
|
||||
if (element.kind === FileKind.FILE) {
|
||||
// open file in any editor
|
||||
this._editorService.openEditor({ resource: element.uri, options: { pinned: pinned } }, group);
|
||||
this._editorService.openEditor({ resource: element.uri, options: { pinned } }, group);
|
||||
} else {
|
||||
// show next picker
|
||||
let items = this._widget.getItems();
|
||||
@@ -508,7 +508,8 @@ export class BreadcrumbsControl {
|
||||
resource: model.uri,
|
||||
options: {
|
||||
selection: Range.collapseToStart(element.symbol.selectionRange),
|
||||
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport
|
||||
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport,
|
||||
pinned
|
||||
}
|
||||
}, withUndefinedAsNull(this._getActiveCodeEditor()), group === SIDE_GROUP);
|
||||
}
|
||||
@@ -753,13 +754,14 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
// open symbol in editor
|
||||
return editors.openEditor({
|
||||
resource: outlineElement.uri,
|
||||
options: { selection: Range.collapseToStart(element.symbol.selectionRange) }
|
||||
options: { selection: Range.collapseToStart(element.symbol.selectionRange), pinned: true }
|
||||
}, SIDE_GROUP);
|
||||
|
||||
} else if (element && URI.isUri(element.resource)) {
|
||||
// open file in editor
|
||||
return editors.openEditor({
|
||||
resource: element.resource,
|
||||
options: { pinned: true }
|
||||
}, SIDE_GROUP);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -99,7 +99,7 @@ export abstract class BreadcrumbsPicker {
|
||||
this._treeContainer = document.createElement('div');
|
||||
this._treeContainer.style.background = color ? color.toString() : '';
|
||||
this._treeContainer.style.paddingTop = '2px';
|
||||
this._treeContainer.style.boxShadow = `0px 5px 8px ${this._themeService.getColorTheme().getColor(widgetShadow)}`;
|
||||
this._treeContainer.style.boxShadow = `0 0 8px 2px ${this._themeService.getColorTheme().getColor(widgetShadow)}`;
|
||||
this._domNode.appendChild(this._treeContainer);
|
||||
|
||||
this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 };
|
||||
@@ -436,7 +436,6 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
|
||||
}
|
||||
|
||||
protected _getTargetFromEvent(element: any): any | undefined {
|
||||
// todo@joh
|
||||
if (element && !IWorkspaceFolder.isIWorkspaceFolder(element) && !(element as IFileStat).isDirectory) {
|
||||
return new FileElement((element as IFileStat).resource, FileKind.FILE);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,8 @@ import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/pl
|
||||
import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess, AllEditorsByAppearanceQuickAccess, AllEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess';
|
||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
import { FileAccess } from 'vs/base/common/network';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
|
||||
|
||||
// Register String Editor
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
||||
@@ -238,27 +240,27 @@ export abstract class AbstractSideBySideEditorInputFactory implements IEditorInp
|
||||
const secondaryInput = secondaryInputFactory.deserialize(instantiationService, deserialized.secondarySerialized);
|
||||
|
||||
if (primaryInput && secondaryInput) {
|
||||
return this.createEditorInput(deserialized.name, deserialized.description, secondaryInput, primaryInput);
|
||||
return this.createEditorInput(instantiationService, deserialized.name, deserialized.description, secondaryInput, primaryInput);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected abstract createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput;
|
||||
protected abstract createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput;
|
||||
}
|
||||
|
||||
class SideBySideEditorInputFactory extends AbstractSideBySideEditorInputFactory {
|
||||
|
||||
protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
|
||||
protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
|
||||
return new SideBySideEditorInput(name, description, secondaryInput, primaryInput);
|
||||
}
|
||||
}
|
||||
|
||||
class DiffEditorInputFactory extends AbstractSideBySideEditorInputFactory {
|
||||
|
||||
protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
|
||||
return new DiffEditorInput(name, description, secondaryInput, primaryInput);
|
||||
protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
|
||||
return instantiationService.createInstance(DiffEditorInput, name, description, secondaryInput, primaryInput, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,6 +464,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.SHOW_EDITORS_IN_GROUP, title: nls.localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_KEEP_EDITORS_COMMAND_ID, title: nls.localize('toggleKeepEditors', "Keep Editors Open"), toggled: ContextKeyExpr.not('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 });
|
||||
|
||||
interface IEditorToolItem { id: string; title: string; icon?: { dark?: URI; light?: URI; } | ThemeIcon; }
|
||||
|
||||
@@ -494,14 +497,14 @@ appendEditorToolItem(
|
||||
{
|
||||
id: SplitEditorAction.ID,
|
||||
title: nls.localize('splitEditorRight', "Split Editor Right"),
|
||||
icon: { id: 'codicon/split-horizontal' }
|
||||
icon: Codicon.splitHorizontal
|
||||
},
|
||||
ContextKeyExpr.not('splitEditorsVertically'),
|
||||
100000, // towards the end
|
||||
{
|
||||
id: editorCommands.SPLIT_EDITOR_DOWN,
|
||||
title: nls.localize('splitEditorDown', "Split Editor Down"),
|
||||
icon: { id: 'codicon/split-vertical' }
|
||||
icon: Codicon.splitVertical
|
||||
}
|
||||
);
|
||||
|
||||
@@ -509,14 +512,14 @@ appendEditorToolItem(
|
||||
{
|
||||
id: SplitEditorAction.ID,
|
||||
title: nls.localize('splitEditorDown', "Split Editor Down"),
|
||||
icon: { id: 'codicon/split-vertical' }
|
||||
icon: Codicon.splitVertical
|
||||
},
|
||||
ContextKeyExpr.has('splitEditorsVertically'),
|
||||
100000, // towards the end
|
||||
{
|
||||
id: editorCommands.SPLIT_EDITOR_RIGHT,
|
||||
title: nls.localize('splitEditorRight', "Split Editor Right"),
|
||||
icon: { id: 'codicon/split-horizontal' }
|
||||
icon: Codicon.splitHorizontal
|
||||
}
|
||||
);
|
||||
|
||||
@@ -525,14 +528,14 @@ appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.CLOSE_EDITOR_COMMAND_ID,
|
||||
title: nls.localize('close', "Close"),
|
||||
icon: { id: 'codicon/close' }
|
||||
icon: Codicon.close
|
||||
},
|
||||
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext.toNegated(), ActiveEditorStickyContext.toNegated()),
|
||||
1000000, // towards the far end
|
||||
{
|
||||
id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
|
||||
title: nls.localize('closeAll', "Close All"),
|
||||
icon: { id: 'codicon/close-all' }
|
||||
icon: Codicon.closeAll
|
||||
}
|
||||
);
|
||||
|
||||
@@ -541,14 +544,14 @@ appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.CLOSE_EDITOR_COMMAND_ID,
|
||||
title: nls.localize('close', "Close"),
|
||||
icon: { id: 'codicon/close-dirty' }
|
||||
icon: Codicon.closeDirty
|
||||
},
|
||||
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext, ActiveEditorStickyContext.toNegated()),
|
||||
1000000, // towards the far end
|
||||
{
|
||||
id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
|
||||
title: nls.localize('closeAll', "Close All"),
|
||||
icon: { id: 'codicon/close-all' }
|
||||
icon: Codicon.closeAll
|
||||
}
|
||||
);
|
||||
|
||||
@@ -557,14 +560,14 @@ appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.UNPIN_EDITOR_COMMAND_ID,
|
||||
title: nls.localize('unpin', "Unpin"),
|
||||
icon: { id: 'codicon/pinned' }
|
||||
icon: Codicon.pinned
|
||||
},
|
||||
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext.toNegated(), ActiveEditorStickyContext),
|
||||
1000000, // towards the far end
|
||||
{
|
||||
id: editorCommands.CLOSE_EDITOR_COMMAND_ID,
|
||||
title: nls.localize('close', "Close"),
|
||||
icon: { id: 'codicon/close' }
|
||||
icon: Codicon.close
|
||||
}
|
||||
);
|
||||
|
||||
@@ -573,23 +576,28 @@ appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.UNPIN_EDITOR_COMMAND_ID,
|
||||
title: nls.localize('unpin', "Unpin"),
|
||||
icon: { id: 'codicon/pinned-dirty' }
|
||||
icon: Codicon.pinnedDirty
|
||||
},
|
||||
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext, ActiveEditorStickyContext),
|
||||
1000000, // towards the far end
|
||||
{
|
||||
id: editorCommands.CLOSE_EDITOR_COMMAND_ID,
|
||||
title: nls.localize('close', "Close"),
|
||||
icon: { id: 'codicon/close' }
|
||||
icon: Codicon.close
|
||||
}
|
||||
);
|
||||
|
||||
const previousChangeIcon = registerIcon('diff-editor-previous-change', Codicon.arrowUp, nls.localize('previousChangeIcon', 'Icon for the previous change action in the diff editor.'));
|
||||
const nextChangeIcon = registerIcon('diff-editor-next-change', Codicon.arrowDown, nls.localize('nextChangeIcon', 'Icon for the next change action in the diff editor.'));
|
||||
const toggleWhitespace = registerIcon('diff-editor-toggle-whitespace', Codicon.whitespace, nls.localize('toggleWhitespace', 'Icon for the toggle whitespace action in the diff editor.'));
|
||||
|
||||
|
||||
// Diff Editor Title Menu: Previous Change
|
||||
appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.GOTO_PREVIOUS_CHANGE,
|
||||
title: nls.localize('navigate.prev.label', "Previous Change"),
|
||||
icon: { id: 'codicon/arrow-up' }
|
||||
icon: previousChangeIcon
|
||||
},
|
||||
TextCompareEditorActiveContext,
|
||||
10
|
||||
@@ -600,7 +608,7 @@ appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.GOTO_NEXT_CHANGE,
|
||||
title: nls.localize('navigate.next.label', "Next Change"),
|
||||
icon: { id: 'codicon/arrow-down' }
|
||||
icon: nextChangeIcon
|
||||
},
|
||||
TextCompareEditorActiveContext,
|
||||
11
|
||||
@@ -611,7 +619,7 @@ appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE,
|
||||
title: nls.localize('ignoreTrimWhitespace.label', "Ignore Leading/Trailing Whitespace Differences"),
|
||||
icon: { id: 'codicon/whitespace' }
|
||||
icon: toggleWhitespace
|
||||
},
|
||||
ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', true)),
|
||||
20
|
||||
@@ -622,7 +630,7 @@ appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE,
|
||||
title: nls.localize('showTrimWhitespace.label', "Show Leading/Trailing Whitespace Differences"),
|
||||
icon: { id: 'codicon/whitespace~disabled' }
|
||||
icon: ThemeIcon.modify(toggleWhitespace, 'disabled')
|
||||
},
|
||||
ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', false)),
|
||||
20
|
||||
|
||||
@@ -38,7 +38,8 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = {
|
||||
openSideBySideDirection: 'right',
|
||||
closeEmptyGroups: true,
|
||||
labelFormat: 'default',
|
||||
splitSizing: 'distribute'
|
||||
splitSizing: 'distribute',
|
||||
splitOnDragAndDrop: true
|
||||
};
|
||||
|
||||
export function impactsEditorPartOptions(event: IConfigurationChangeEvent): boolean {
|
||||
@@ -64,12 +65,12 @@ export interface IEditorOpeningEvent extends IEditorIdentifier {
|
||||
/**
|
||||
* The options used when opening the editor.
|
||||
*/
|
||||
options?: IEditorOptions;
|
||||
readonly options?: IEditorOptions;
|
||||
|
||||
/**
|
||||
* Context indicates how the editor open event is initialized.
|
||||
*/
|
||||
context?: OpenEditorContext;
|
||||
readonly context?: OpenEditorContext;
|
||||
|
||||
/**
|
||||
* Allows to prevent the opening of an editor by providing a callback
|
||||
|
||||
@@ -7,22 +7,26 @@ import * as nls from 'vs/nls';
|
||||
import { isObject, isString, isUndefined, isNumber, withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { TextCompareEditorVisibleContext, EditorInput, IEditorIdentifier, IEditorCommandsContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, CloseDirection, IEditorInput, IVisibleEditorPane, ActiveEditorStickyContext, EditorsOrder } from 'vs/workbench/common/editor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { TextCompareEditorVisibleContext, EditorInput, IEditorIdentifier, IEditorCommandsContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, CloseDirection, IEditorInput, IVisibleEditorPane, ActiveEditorStickyContext, EditorsOrder, viewColumnToEditorGroup, EditorGroupColumn } from 'vs/workbench/common/editor';
|
||||
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor';
|
||||
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IListService, IOpenEvent } from 'vs/platform/list/browser/listService';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { distinct, coalesce } from 'vs/base/common/arrays';
|
||||
import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredSideBySideGroupDirection, EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith';
|
||||
|
||||
export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors';
|
||||
export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup';
|
||||
@@ -36,6 +40,7 @@ export const CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeOt
|
||||
export const MOVE_ACTIVE_EDITOR_COMMAND_ID = 'moveActiveEditor';
|
||||
export const LAYOUT_EDITOR_GROUPS_COMMAND_ID = 'layoutEditorGroups';
|
||||
export const KEEP_EDITOR_COMMAND_ID = 'workbench.action.keepEditor';
|
||||
export const TOGGLE_KEEP_EDITORS_COMMAND_ID = 'workbench.action.toggleKeepEditors';
|
||||
export const SHOW_EDITORS_IN_GROUP = 'workbench.action.showEditorsInGroup';
|
||||
|
||||
export const PIN_EDITOR_COMMAND_ID = 'workbench.action.pinEditor';
|
||||
@@ -44,6 +49,9 @@ export const UNPIN_EDITOR_COMMAND_ID = 'workbench.action.unpinEditor';
|
||||
export const TOGGLE_DIFF_SIDE_BY_SIDE = 'toggle.diff.renderSideBySide';
|
||||
export const GOTO_NEXT_CHANGE = 'workbench.action.compareEditor.nextChange';
|
||||
export const GOTO_PREVIOUS_CHANGE = 'workbench.action.compareEditor.previousChange';
|
||||
export const DIFF_FOCUS_PRIMARY_SIDE = 'workbench.action.compareEditor.focusPrimarySide';
|
||||
export const DIFF_FOCUS_SECONDARY_SIDE = 'workbench.action.compareEditor.focusSecondarySide';
|
||||
export const DIFF_FOCUS_OTHER_SIDE = 'workbench.action.compareEditor.focusOtherSide';
|
||||
export const TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE = 'toggle.diff.ignoreTrimWhitespace';
|
||||
|
||||
export const SPLIT_EDITOR_UP = 'workbench.action.splitEditorUp';
|
||||
@@ -58,6 +66,10 @@ export const FOCUS_BELOW_GROUP_WITHOUT_WRAP_COMMAND_ID = 'workbench.action.focus
|
||||
|
||||
export const OPEN_EDITOR_AT_INDEX_COMMAND_ID = 'workbench.action.openEditorAtIndex';
|
||||
|
||||
export const API_OPEN_EDITOR_COMMAND_ID = '_workbench.open';
|
||||
export const API_OPEN_DIFF_EDITOR_COMMAND_ID = '_workbench.diff';
|
||||
export const API_OPEN_WITH_EDITOR_COMMAND_ID = '_workbench.openWith';
|
||||
|
||||
export interface ActiveEditorMoveArguments {
|
||||
to: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next';
|
||||
by: 'tab' | 'group';
|
||||
@@ -227,13 +239,45 @@ function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IVisi
|
||||
}
|
||||
|
||||
function registerEditorGroupsLayoutCommand(): void {
|
||||
CommandsRegistry.registerCommand(LAYOUT_EDITOR_GROUPS_COMMAND_ID, (accessor: ServicesAccessor, args: EditorGroupLayout) => {
|
||||
if (!args || typeof args !== 'object') {
|
||||
|
||||
function applyEditorLayout(accessor: ServicesAccessor, layout: EditorGroupLayout): void {
|
||||
if (!layout || typeof layout !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
editorGroupService.applyLayout(args);
|
||||
editorGroupService.applyLayout(layout);
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand(LAYOUT_EDITOR_GROUPS_COMMAND_ID, (accessor: ServicesAccessor, args: EditorGroupLayout) => {
|
||||
applyEditorLayout(accessor, args);
|
||||
});
|
||||
|
||||
// API Command
|
||||
CommandsRegistry.registerCommand({
|
||||
id: 'vscode.setEditorLayout',
|
||||
handler: (accessor: ServicesAccessor, args: EditorGroupLayout) => applyEditorLayout(accessor, args),
|
||||
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',
|
||||
'default': [{}, {}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -265,30 +309,68 @@ function registerDiffEditorCommands(): void {
|
||||
handler: accessor => navigateInDiffEditor(accessor, false)
|
||||
});
|
||||
|
||||
function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void {
|
||||
function getActiveTextDiffEditor(accessor: ServicesAccessor): TextDiffEditor | undefined {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const candidates = [editorService.activeEditorPane, ...editorService.visibleEditorPanes].filter(editor => editor instanceof TextDiffEditor);
|
||||
|
||||
if (candidates.length > 0) {
|
||||
const navigator = (<TextDiffEditor>candidates[0]).getDiffNavigator();
|
||||
for (const editor of [editorService.activeEditorPane, ...editorService.visibleEditorPanes]) {
|
||||
if (editor instanceof TextDiffEditor) {
|
||||
return editor;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void {
|
||||
const activeTextDiffEditor = getActiveTextDiffEditor(accessor);
|
||||
|
||||
if (activeTextDiffEditor) {
|
||||
const navigator = activeTextDiffEditor.getDiffNavigator();
|
||||
if (navigator) {
|
||||
next ? navigator.next() : navigator.previous();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum FocusTextDiffEditorMode {
|
||||
Original,
|
||||
Modified,
|
||||
Toggle
|
||||
}
|
||||
|
||||
function focusInDiffEditor(accessor: ServicesAccessor, mode: FocusTextDiffEditorMode): void {
|
||||
const activeTextDiffEditor = getActiveTextDiffEditor(accessor);
|
||||
|
||||
if (activeTextDiffEditor) {
|
||||
switch (mode) {
|
||||
case FocusTextDiffEditorMode.Original:
|
||||
activeTextDiffEditor.getControl()?.getOriginalEditor().focus();
|
||||
break;
|
||||
case FocusTextDiffEditorMode.Modified:
|
||||
activeTextDiffEditor.getControl()?.getModifiedEditor().focus();
|
||||
break;
|
||||
case FocusTextDiffEditorMode.Toggle:
|
||||
if (activeTextDiffEditor.getControl()?.getModifiedEditor().hasWidgetFocus()) {
|
||||
return focusInDiffEditor(accessor, FocusTextDiffEditorMode.Original);
|
||||
} else {
|
||||
return focusInDiffEditor(accessor, FocusTextDiffEditorMode.Modified);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleDiffSideBySide(accessor: ServicesAccessor): void {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
|
||||
const newValue = !configurationService.getValue<boolean>('diffEditor.renderSideBySide');
|
||||
configurationService.updateValue('diffEditor.renderSideBySide', newValue, ConfigurationTarget.USER);
|
||||
configurationService.updateValue('diffEditor.renderSideBySide', newValue);
|
||||
}
|
||||
|
||||
function toggleDiffIgnoreTrimWhitespace(accessor: ServicesAccessor): void {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
|
||||
const newValue = !configurationService.getValue<boolean>('diffEditor.ignoreTrimWhitespace');
|
||||
configurationService.updateValue('diffEditor.ignoreTrimWhitespace', newValue, ConfigurationTarget.USER);
|
||||
configurationService.updateValue('diffEditor.ignoreTrimWhitespace', newValue);
|
||||
}
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
@@ -299,6 +381,30 @@ function registerDiffEditorCommands(): void {
|
||||
handler: accessor => toggleDiffSideBySide(accessor)
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: DIFF_FOCUS_PRIMARY_SIDE,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: undefined,
|
||||
primary: undefined,
|
||||
handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Modified)
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: DIFF_FOCUS_SECONDARY_SIDE,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: undefined,
|
||||
primary: undefined,
|
||||
handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Original)
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: DIFF_FOCUS_OTHER_SIDE,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: undefined,
|
||||
primary: undefined,
|
||||
handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Toggle)
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: TOGGLE_DIFF_SIDE_BY_SIDE,
|
||||
@@ -320,6 +426,88 @@ function registerDiffEditorCommands(): void {
|
||||
});
|
||||
}
|
||||
|
||||
function registerOpenEditorAPICommands(): void {
|
||||
|
||||
function mixinContext(context: IOpenEvent<unknown> | undefined, options: ITextEditorOptions | undefined, column: EditorGroupColumn | undefined): [ITextEditorOptions | undefined, EditorGroupColumn | undefined] {
|
||||
if (!context) {
|
||||
return [options, column];
|
||||
}
|
||||
|
||||
return [
|
||||
{ ...context.editorOptions, ...(options ?? Object.create(null)) },
|
||||
context.sideBySide ? SIDE_GROUP : column
|
||||
];
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand(API_OPEN_EDITOR_COMMAND_ID, async function (accessor: ServicesAccessor, resourceArg: UriComponents, columnAndOptions?: [EditorGroupColumn?, ITextEditorOptions?], label?: string, context?: IOpenEvent<unknown>) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const openerService = accessor.get(IOpenerService);
|
||||
|
||||
const resource = URI.revive(resourceArg);
|
||||
const [columnArg, optionsArg] = columnAndOptions ?? [];
|
||||
|
||||
// use editor options or editor view column as a hint to use the editor service for opening
|
||||
if (optionsArg || typeof columnArg === 'number') {
|
||||
const [options, column] = mixinContext(context, optionsArg, columnArg);
|
||||
|
||||
await editorService.openEditor({ resource, options, label }, viewColumnToEditorGroup(editorGroupService, column));
|
||||
}
|
||||
|
||||
// do not allow to execute commands from here
|
||||
else if (resource.scheme === 'command') {
|
||||
return;
|
||||
}
|
||||
|
||||
// finally, delegate to opener service
|
||||
else {
|
||||
await openerService.open(resource, { openToSide: context?.sideBySide, editorOptions: context?.editorOptions });
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand(API_OPEN_DIFF_EDITOR_COMMAND_ID, async function (accessor: ServicesAccessor, leftResource: UriComponents, rightResource: UriComponents, label?: string, columnAndOptions?: [EditorGroupColumn?, ITextEditorOptions?], context?: IOpenEvent<unknown>) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
|
||||
const [columnArg, optionsArg] = columnAndOptions ?? [];
|
||||
const [options, column] = mixinContext(context, optionsArg, columnArg);
|
||||
|
||||
await editorService.openEditor({
|
||||
leftResource: URI.revive(leftResource),
|
||||
rightResource: URI.revive(rightResource),
|
||||
label,
|
||||
options
|
||||
}, viewColumnToEditorGroup(editorGroupService, column));
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand(API_OPEN_WITH_EDITOR_COMMAND_ID, (accessor: ServicesAccessor, resource: UriComponents, id: string, columnAndOptions?: [EditorGroupColumn?, ITextEditorOptions?]) => {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorGroupsService = accessor.get(IEditorGroupsService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
|
||||
const [columnArg, optionsArg] = columnAndOptions ?? [];
|
||||
let group: IEditorGroup | undefined = undefined;
|
||||
|
||||
if (columnArg === SIDE_GROUP) {
|
||||
const direction = preferredSideBySideGroupDirection(configurationService);
|
||||
|
||||
let neighbourGroup = editorGroupsService.findGroup({ direction });
|
||||
if (!neighbourGroup) {
|
||||
neighbourGroup = editorGroupsService.addGroup(editorGroupsService.activeGroup, direction);
|
||||
}
|
||||
group = neighbourGroup;
|
||||
} else {
|
||||
group = editorGroupsService.getGroup(viewColumnToEditorGroup(editorGroupsService, columnArg)) ?? editorGroupsService.activeGroup;
|
||||
}
|
||||
|
||||
const textOptions: ITextEditorOptions = optionsArg ? { ...optionsArg, override: false } : { override: false };
|
||||
|
||||
const input = editorService.createEditorInput({ resource: URI.revive(resource) });
|
||||
return openEditorWith(input, id, textOptions, group, editorService, configurationService, quickInputService);
|
||||
});
|
||||
}
|
||||
|
||||
function registerOpenEditorAtIndexCommands(): void {
|
||||
const openEditorAtIndex: ICommandHandler = (accessor: ServicesAccessor, editorIndex: number): void => {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
@@ -710,6 +898,31 @@ function registerOtherEditorCommands(): void {
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: TOGGLE_KEEP_EDITORS_COMMAND_ID,
|
||||
handler: accessor => {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
const notificationService = accessor.get(INotificationService);
|
||||
const openerService = accessor.get(IOpenerService);
|
||||
|
||||
// Update setting
|
||||
const currentSetting = configurationService.getValue<boolean>('workbench.editor.enablePreview');
|
||||
const newSetting = currentSetting === true ? false : true;
|
||||
configurationService.updateValue('workbench.editor.enablePreview', newSetting);
|
||||
|
||||
// Inform user
|
||||
notificationService.prompt(
|
||||
Severity.Info,
|
||||
newSetting ?
|
||||
nls.localize('enablePreview', "Preview editors have been enabled in settings.") :
|
||||
nls.localize('disablePreview', "Preview editors have been disabled in settings."),
|
||||
[{
|
||||
label: nls.localize('learnMode', "Learn More"), run: () => openerService.open('https://go.microsoft.com/fwlink/?linkid=2147473')
|
||||
}]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: PIN_EDITOR_COMMAND_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
@@ -877,6 +1090,7 @@ export function setup(): void {
|
||||
registerActiveEditorMoveCommand();
|
||||
registerEditorGroupsLayoutCommand();
|
||||
registerDiffEditorCommands();
|
||||
registerOpenEditorAPICommands();
|
||||
registerOpenEditorAtIndexCommands();
|
||||
registerCloseEditorCommands();
|
||||
registerOtherEditorCommands();
|
||||
|
||||
@@ -12,7 +12,7 @@ import { IThemeService, Themable } from 'vs/platform/theme/common/themeService';
|
||||
import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IEditorIdentifier, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { isMacintosh, isWeb } from 'vs/base/common/platform';
|
||||
import { GroupDirection, MergeGroupMode, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { GroupDirection, IEditorGroupsService, MergeGroupMode, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
@@ -25,6 +25,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
||||
import { assertIsDefined, assertAllDefined } from 'vs/base/common/types';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ByteSize } from 'vs/platform/files/common/files';
|
||||
|
||||
interface IDropOperation {
|
||||
splitDirection?: GroupDirection;
|
||||
@@ -34,7 +35,7 @@ class DropOverlay extends Themable {
|
||||
|
||||
private static readonly OVERLAY_ID = 'monaco-workbench-editor-drop-overlay';
|
||||
|
||||
private static readonly MAX_FILE_UPLOAD_SIZE = 100 * 1024 * 1024; // 100mb
|
||||
private static readonly MAX_FILE_UPLOAD_SIZE = 100 * ByteSize.MB;
|
||||
|
||||
private container: HTMLElement | undefined;
|
||||
private overlay: HTMLElement | undefined;
|
||||
@@ -54,7 +55,8 @@ class DropOverlay extends Themable {
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IFileDialogService private readonly fileDialogService: IFileDialogService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@INotificationService private readonly notificationService: INotificationService
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
@@ -143,8 +145,14 @@ class DropOverlay extends Themable {
|
||||
}
|
||||
}
|
||||
|
||||
// Position overlay
|
||||
this.positionOverlay(e.offsetX, e.offsetY, isDraggingGroup);
|
||||
// Position overlay and conditionally enable or disable
|
||||
// editor group splitting support based on setting and
|
||||
// keymodifiers used.
|
||||
let splitOnDragAndDrop = !!this.editorGroupService.partOptions.splitOnDragAndDrop;
|
||||
if (this.isToggleSplitOperation(e)) {
|
||||
splitOnDragAndDrop = !splitOnDragAndDrop;
|
||||
}
|
||||
this.positionOverlay(e.offsetX, e.offsetY, isDraggingGroup, splitOnDragAndDrop);
|
||||
|
||||
// Make sure to stop any running cleanup scheduler to remove the overlay
|
||||
if (this.cleanupOverlayScheduler.isScheduled()) {
|
||||
@@ -319,7 +327,7 @@ class DropOverlay extends Themable {
|
||||
// Try to come up with a good file path for the untitled
|
||||
// editor by asking the file dialog service for the default
|
||||
let proposedFilePath: URI | undefined = undefined;
|
||||
const defaultFilePath = this.fileDialogService.defaultFilePath();
|
||||
const defaultFilePath = await this.fileDialogService.defaultFilePath();
|
||||
if (defaultFilePath) {
|
||||
proposedFilePath = joinPath(defaultFilePath, name);
|
||||
}
|
||||
@@ -362,24 +370,33 @@ class DropOverlay extends Themable {
|
||||
return (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh);
|
||||
}
|
||||
|
||||
private positionOverlay(mousePosX: number, mousePosY: number, isDraggingGroup: boolean): void {
|
||||
private isToggleSplitOperation(e: DragEvent): boolean {
|
||||
return (e.altKey && !isMacintosh) || (e.shiftKey && isMacintosh);
|
||||
}
|
||||
|
||||
private positionOverlay(mousePosX: number, mousePosY: number, isDraggingGroup: boolean, enableSplitting: boolean): void {
|
||||
const preferSplitVertically = this.accessor.partOptions.openSideBySideDirection === 'right';
|
||||
|
||||
const editorControlWidth = this.groupView.element.clientWidth;
|
||||
const editorControlHeight = this.groupView.element.clientHeight - this.getOverlayOffsetHeight();
|
||||
|
||||
let edgeWidthThresholdFactor: number;
|
||||
if (isDraggingGroup) {
|
||||
edgeWidthThresholdFactor = preferSplitVertically ? 0.3 : 0.1; // give larger threshold when dragging group depending on preferred split direction
|
||||
} else {
|
||||
edgeWidthThresholdFactor = 0.1; // 10% threshold to split if dragging editors
|
||||
}
|
||||
|
||||
let edgeHeightThresholdFactor: number;
|
||||
if (isDraggingGroup) {
|
||||
edgeHeightThresholdFactor = preferSplitVertically ? 0.1 : 0.3; // give larger threshold when dragging group depending on preferred split direction
|
||||
if (enableSplitting) {
|
||||
if (isDraggingGroup) {
|
||||
edgeWidthThresholdFactor = preferSplitVertically ? 0.3 : 0.1; // give larger threshold when dragging group depending on preferred split direction
|
||||
} else {
|
||||
edgeWidthThresholdFactor = 0.1; // 10% threshold to split if dragging editors
|
||||
}
|
||||
|
||||
if (isDraggingGroup) {
|
||||
edgeHeightThresholdFactor = preferSplitVertically ? 0.1 : 0.3; // give larger threshold when dragging group depending on preferred split direction
|
||||
} else {
|
||||
edgeHeightThresholdFactor = 0.1; // 10% threshold to split if dragging editors
|
||||
}
|
||||
} else {
|
||||
edgeHeightThresholdFactor = 0.1; // 10% threshold to split if dragging editors
|
||||
edgeWidthThresholdFactor = 0;
|
||||
edgeHeightThresholdFactor = 0;
|
||||
}
|
||||
|
||||
const edgeWidthThreshold = editorControlWidth * edgeWidthThresholdFactor;
|
||||
|
||||
@@ -1742,28 +1742,17 @@ class EditorOpeningEvent implements IEditorOpeningEvent {
|
||||
private override: (() => Promise<IEditorPane | undefined>) | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
private _group: GroupIdentifier,
|
||||
private _editor: EditorInput,
|
||||
public readonly groupId: GroupIdentifier,
|
||||
public readonly editor: EditorInput,
|
||||
private _options: EditorOptions | undefined,
|
||||
private _context: OpenEditorContext | undefined
|
||||
public readonly context: OpenEditorContext | undefined
|
||||
) {
|
||||
}
|
||||
|
||||
get groupId(): GroupIdentifier {
|
||||
return this._group;
|
||||
}
|
||||
|
||||
get editor(): EditorInput {
|
||||
return this._editor;
|
||||
}
|
||||
|
||||
get options(): EditorOptions | undefined {
|
||||
return this._options;
|
||||
}
|
||||
|
||||
get context(): OpenEditorContext | undefined {
|
||||
return this._context;
|
||||
}
|
||||
|
||||
prevent(callback: () => Promise<IEditorPane | undefined>): void {
|
||||
this.override = callback;
|
||||
@@ -1775,9 +1764,9 @@ class EditorOpeningEvent implements IEditorOpeningEvent {
|
||||
}
|
||||
|
||||
export interface EditorReplacement {
|
||||
editor: EditorInput;
|
||||
replacement: EditorInput;
|
||||
options?: EditorOptions;
|
||||
readonly editor: EditorInput;
|
||||
readonly replacement: EditorInput;
|
||||
readonly options?: EditorOptions;
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme, collector, environment) => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IEditorGroup, IEditorGroupsService, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { LRUCache, Touch } from 'vs/base/common/map';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
@@ -156,7 +156,7 @@ export abstract class EditorPane extends Composite implements IEditorPane {
|
||||
|
||||
let editorMemento = EditorPane.EDITOR_MEMENTOS.get(mementoKey);
|
||||
if (!editorMemento) {
|
||||
editorMemento = new EditorMemento(this.getId(), key, this.getMemento(StorageScope.WORKSPACE), limit, editorGroupService);
|
||||
editorMemento = new EditorMemento(this.getId(), key, this.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE), limit, editorGroupService);
|
||||
EditorPane.EDITOR_MEMENTOS.set(mementoKey, editorMemento);
|
||||
}
|
||||
|
||||
@@ -220,18 +220,7 @@ export class EditorMemento<T> implements IEditorMemento<T> {
|
||||
|
||||
// Automatically clear when editor input gets disposed if any
|
||||
if (resourceOrEditor instanceof EditorInput) {
|
||||
const editor = resourceOrEditor;
|
||||
|
||||
if (!this.editorDisposables) {
|
||||
this.editorDisposables = new Map<EditorInput, IDisposable>();
|
||||
}
|
||||
|
||||
if (!this.editorDisposables.has(editor)) {
|
||||
this.editorDisposables.set(editor, Event.once(resourceOrEditor.onDispose)(() => {
|
||||
this.clearEditorState(resource);
|
||||
this.editorDisposables?.delete(editor);
|
||||
}));
|
||||
}
|
||||
this.clearEditorStateOnDispose(resource, resourceOrEditor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +229,7 @@ export class EditorMemento<T> implements IEditorMemento<T> {
|
||||
loadEditorState(group: IEditorGroup, resourceOrEditor: URI | EditorInput, fallbackToOtherGroupState?: boolean): T | undefined {
|
||||
const resource = this.doGetResource(resourceOrEditor);
|
||||
if (!resource || !group) {
|
||||
return undefined; // we are not in a good state to load any state for a resource
|
||||
return; // we are not in a good state to load any state for a resource
|
||||
}
|
||||
|
||||
const cache = this.doLoad();
|
||||
@@ -261,12 +250,16 @@ export class EditorMemento<T> implements IEditorMemento<T> {
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
clearEditorState(resource: URI, group?: IEditorGroup): void;
|
||||
clearEditorState(editor: EditorInput, group?: IEditorGroup): void;
|
||||
clearEditorState(resourceOrEditor: URI | EditorInput, group?: IEditorGroup): void {
|
||||
if (resourceOrEditor instanceof EditorInput) {
|
||||
this.editorDisposables?.delete(resourceOrEditor);
|
||||
}
|
||||
|
||||
const resource = this.doGetResource(resourceOrEditor);
|
||||
if (resource) {
|
||||
const cache = this.doLoad();
|
||||
@@ -286,6 +279,19 @@ export class EditorMemento<T> implements IEditorMemento<T> {
|
||||
}
|
||||
}
|
||||
|
||||
clearEditorStateOnDispose(resource: URI, editor: EditorInput): void {
|
||||
if (!this.editorDisposables) {
|
||||
this.editorDisposables = new Map<EditorInput, IDisposable>();
|
||||
}
|
||||
|
||||
if (!this.editorDisposables.has(editor)) {
|
||||
this.editorDisposables.set(editor, Event.once(editor.onDispose)(() => {
|
||||
this.clearEditorState(resource);
|
||||
this.editorDisposables?.delete(editor);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
moveEditorState(source: URI, target: URI, comparer: IExtUri): void {
|
||||
const cache = this.doLoad();
|
||||
|
||||
@@ -342,7 +348,7 @@ export class EditorMemento<T> implements IEditorMemento<T> {
|
||||
saveState(): void {
|
||||
const cache = this.doLoad();
|
||||
|
||||
// Cleanup once during shutdown
|
||||
// Cleanup once during session
|
||||
if (!this.cleanedUp) {
|
||||
this.cleanUp();
|
||||
this.cleanedUp = true;
|
||||
|
||||
@@ -19,7 +19,7 @@ import { IEditorGroupsAccessor, IEditorGroupView, getEditorPartOptions, impactsE
|
||||
import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView';
|
||||
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
|
||||
import { EditorDropTarget, IEditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget';
|
||||
import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService';
|
||||
@@ -148,8 +148,8 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
|
||||
this.gridWidgetView = new GridWidgetView<IEditorGroupView>();
|
||||
|
||||
this.workspaceMemento = this.getMemento(StorageScope.WORKSPACE);
|
||||
this.globalMemento = this.getMemento(StorageScope.GLOBAL);
|
||||
this.workspaceMemento = this.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE);
|
||||
this.globalMemento = this.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
|
||||
this._whenRestored = new Promise(resolve => (this.whenRestoredResolve = resolve));
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import 'vs/css!./media/editorstatus';
|
||||
import * as nls from 'vs/nls';
|
||||
import { runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
|
||||
import { format, compare } from 'vs/base/common/strings';
|
||||
import { format, compare, splitLines } from 'vs/base/common/strings';
|
||||
import { extname, basename, isEqual } from 'vs/base/common/resources';
|
||||
import { areFunctions, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -346,12 +346,12 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
[{
|
||||
label: nls.localize('screenReaderDetectedExplanation.answerYes', "Yes"),
|
||||
run: () => {
|
||||
this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
|
||||
this.configurationService.updateValue('editor.accessibilitySupport', 'on');
|
||||
}
|
||||
}, {
|
||||
label: nls.localize('screenReaderDetectedExplanation.answerNo', "No"),
|
||||
run: () => {
|
||||
this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER);
|
||||
this.configurationService.updateValue('editor.accessibilitySupport', 'off');
|
||||
}
|
||||
}],
|
||||
{ sticky: true }
|
||||
@@ -918,7 +918,7 @@ class ShowCurrentMarkerInStatusbarContribution extends Disposable {
|
||||
this.currentMarker = this.getMarker();
|
||||
if (this.hasToUpdateStatus(previousMarker, this.currentMarker)) {
|
||||
if (this.currentMarker) {
|
||||
const line = this.currentMarker.message.split(/\r\n|\r|\n/g)[0];
|
||||
const line = splitLines(this.currentMarker.message)[0];
|
||||
const text = `${this.getType(this.currentMarker)} ${line}`;
|
||||
if (!this.statusBarEntryAccessor.value) {
|
||||
this.statusBarEntryAccessor.value = this.statusbarService.addEntry({ text: '', ariaLabel: '' }, 'statusbar.currentProblem', nls.localize('currentProblem', "Current Problem"), StatusbarAlignment.LEFT);
|
||||
@@ -1046,12 +1046,19 @@ export class ChangeModeAction extends Action {
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IPreferencesService private readonly preferencesService: IPreferencesService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService
|
||||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
@ICommandService private readonly commandService: ICommandService
|
||||
) {
|
||||
super(actionId, actionLabel);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
const activeEditorPane = this.editorService.activeEditorPane as unknown as { isNotebookEditor?: boolean } | undefined;
|
||||
if (activeEditorPane?.isNotebookEditor) {
|
||||
// it's inside notebook editor
|
||||
return this.commandService.executeCommand('notebook.cell.changeLanguage');
|
||||
}
|
||||
|
||||
const activeTextEditorControl = getCodeEditor(this.editorService.activeTextEditorControl);
|
||||
if (!activeTextEditorControl) {
|
||||
await this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
@@ -1143,7 +1150,7 @@ export class ChangeModeAction extends Action {
|
||||
|
||||
// User decided to configure settings for current language
|
||||
if (pick === configureModeSettings) {
|
||||
this.preferencesService.openGlobalSettings(true, { editSetting: `[${withUndefinedAsNull(currentModeId)}]` });
|
||||
this.preferencesService.openGlobalSettings(true, { revealSetting: { key: `[${withUndefinedAsNull(currentModeId)}]`, edit: true } });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget {
|
||||
super();
|
||||
|
||||
this._domNode = $('.floating-click-widget');
|
||||
this._domNode.style.padding = '10px';
|
||||
this._domNode.style.cursor = 'pointer';
|
||||
|
||||
if (keyBindingAction) {
|
||||
const keybinding = keybindingService.lookupKeybinding(keyBindingAction);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { IEditorInput, IEditorInputFactoryRegistry, IEditorIdentifier, GroupIdentifier, Extensions, IEditorPartOptionsChangeEvent, EditorsOrder, EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IEditorGroupsService, IEditorGroup, GroupChangeKind, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
@@ -339,7 +339,7 @@ export class EditorsObserver extends Disposable {
|
||||
if (this.mostRecentEditorsMap.isEmpty()) {
|
||||
this.storageService.remove(EditorsObserver.STORAGE_KEY, StorageScope.WORKSPACE);
|
||||
} else {
|
||||
this.storageService.store(EditorsObserver.STORAGE_KEY, JSON.stringify(this.serialize()), StorageScope.WORKSPACE);
|
||||
this.storageService.store(EditorsObserver.STORAGE_KEY, JSON.stringify(this.serialize()), StorageScope.WORKSPACE, StorageTarget.MACHINE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
/* todo@joh move somewhere else */
|
||||
/* breadcrumbs-picker-style */
|
||||
|
||||
.monaco-workbench .monaco-breadcrumbs-picker .arrow {
|
||||
position: absolute;
|
||||
|
||||
@@ -589,8 +589,8 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
const tabActionRunner = new EditorCommandsContextActionRunner({ groupId: this.group.id, editorIndex: index });
|
||||
|
||||
const tabActionBar = new ActionBar(tabActionsContainer, { ariaLabel: localize('ariaLabelTabActions', "Tab actions"), actionRunner: tabActionRunner, });
|
||||
tabActionBar.onDidBeforeRun(e => {
|
||||
const tabActionBar = new ActionBar(tabActionsContainer, { ariaLabel: localize('ariaLabelTabActions', "Tab actions"), actionRunner: tabActionRunner });
|
||||
tabActionBar.onBeforeRun(e => {
|
||||
if (e.action.id === this.closeEditorAction.id) {
|
||||
this.blockRevealActiveTabOnce();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import { ScrollType, IDiffEditorViewState, IDiffEditorModel } from 'vs/editor/co
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
@@ -32,6 +31,7 @@ import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/edit
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { multibyteAwareBtoa } from 'vs/base/browser/dom';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
|
||||
/**
|
||||
* The text editor that leverages the diff text editor for the editing experience.
|
||||
@@ -62,9 +62,28 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan
|
||||
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@IFileService private readonly fileService: IFileService
|
||||
) {
|
||||
super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService);
|
||||
|
||||
// Listen to file system provider changes
|
||||
this._register(this.fileService.onDidChangeFileSystemProviderCapabilities(e => this.onDidFileSystemProviderChange(e.scheme)));
|
||||
this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(e => this.onDidFileSystemProviderChange(e.scheme)));
|
||||
}
|
||||
|
||||
private onDidFileSystemProviderChange(scheme: string): void {
|
||||
const control = this.getControl();
|
||||
const input = this.input;
|
||||
|
||||
if (control && input instanceof DiffEditorInput) {
|
||||
if (input.originalInput.resource?.scheme === scheme || input.modifiedInput.resource?.scheme === scheme) {
|
||||
control.updateOptions({
|
||||
readOnly: input.modifiedInput.isReadonly(),
|
||||
originalEditable: !input.originalInput.isReadonly()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected onWillCloseEditorInGroup(editor: IEditorInput): void {
|
||||
@@ -175,7 +194,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan
|
||||
const originalInput = input.originalInput;
|
||||
const modifiedInput = input.modifiedInput;
|
||||
|
||||
const binaryDiffInput = new DiffEditorInput(input.getName(), input.getDescription(), originalInput, modifiedInput, true);
|
||||
const binaryDiffInput = this.instantiationService.createInstance(DiffEditorInput, input.getName(), input.getDescription(), originalInput, modifiedInput, true);
|
||||
|
||||
// Forward binary flag to input if supported
|
||||
const fileEditorInputFactory = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).getFileEditorInputFactory();
|
||||
@@ -217,16 +236,17 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan
|
||||
|
||||
// Handle diff editor specially by merging in diffEditor configuration
|
||||
if (isObject(configuration.diffEditor)) {
|
||||
// User settings defines `diffEditor.codeLens`, but there is also `editor.codeLens`.
|
||||
// Due to the mixin, the two settings cannot be distinguished anymore.
|
||||
//
|
||||
// So we map `diffEditor.codeLens` to `diffEditor.originalCodeLens` and `diffEditor.modifiedCodeLens`.
|
||||
const diffEditorConfiguration = <IDiffEditorOptions>objects.deepClone(configuration.diffEditor);
|
||||
diffEditorConfiguration.originalCodeLens = diffEditorConfiguration.codeLens;
|
||||
diffEditorConfiguration.modifiedCodeLens = diffEditorConfiguration.codeLens;
|
||||
|
||||
// User settings defines `diffEditor.codeLens`, but here we rename that to `diffEditor.diffCodeLens` to avoid collisions with `editor.codeLens`.
|
||||
diffEditorConfiguration.diffCodeLens = diffEditorConfiguration.codeLens;
|
||||
delete diffEditorConfiguration.codeLens;
|
||||
|
||||
objects.mixin(editorConfiguration, diffEditorConfiguration);
|
||||
// User settings defines `diffEditor.wordWrap`, but here we rename that to `diffEditor.diffWordWrap` to avoid collisions with `editor.wordWrap`.
|
||||
diffEditorConfiguration.diffWordWrap = <'off' | 'on' | 'inherit' | undefined>diffEditorConfiguration.wordWrap;
|
||||
delete diffEditorConfiguration.wordWrap;
|
||||
|
||||
Object.assign(editorConfiguration, diffEditorConfiguration);
|
||||
}
|
||||
|
||||
return editorConfiguration;
|
||||
@@ -309,12 +329,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan
|
||||
|
||||
// Otherwise save it
|
||||
else {
|
||||
super.saveTextEditorViewState(resource);
|
||||
|
||||
// Make sure to clean up when the input gets disposed
|
||||
Event.once(input.onDispose)(() => {
|
||||
super.clearTextEditorViewState([resource], this.group);
|
||||
});
|
||||
super.saveTextEditorViewState(resource, input);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa
|
||||
@IEditorGroupsService protected editorGroupService: IEditorGroupsService
|
||||
) {
|
||||
super(id, telemetryService, themeService, storageService);
|
||||
|
||||
this._instantiationService = instantiationService;
|
||||
|
||||
this.editorMemento = this.getEditorMemento<IEditorViewState>(editorGroupService, BaseTextEditor.TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100);
|
||||
@@ -223,13 +224,27 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa
|
||||
return this.editorControl;
|
||||
}
|
||||
|
||||
protected saveTextEditorViewState(resource: URI): void {
|
||||
getViewState(): IEditorViewState | undefined {
|
||||
const resource = this.input?.resource;
|
||||
if (resource) {
|
||||
return withNullAsUndefined(this.retrieveTextEditorViewState(resource));
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
protected saveTextEditorViewState(resource: URI, cleanUpOnDispose?: IEditorInput): void {
|
||||
const editorViewState = this.retrieveTextEditorViewState(resource);
|
||||
if (!editorViewState || !this.group) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.editorMemento.saveEditorState(this.group, resource, editorViewState);
|
||||
|
||||
if (cleanUpOnDispose) {
|
||||
this.editorMemento.clearEditorStateOnDispose(resource, cleanUpOnDispose);
|
||||
}
|
||||
}
|
||||
|
||||
protected shouldRestoreTextEditorViewState(editor: IEditorInput, context?: IEditorOpenContext): boolean {
|
||||
@@ -243,15 +258,6 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa
|
||||
return true;
|
||||
}
|
||||
|
||||
getViewState(): IEditorViewState | undefined {
|
||||
const resource = this.input?.resource;
|
||||
if (resource) {
|
||||
return withNullAsUndefined(this.retrieveTextEditorViewState(resource));
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected retrieveTextEditorViewState(resource: URI): IEditorViewState | null {
|
||||
const control = this.getControl();
|
||||
if (!isCodeEditor(control)) {
|
||||
@@ -284,9 +290,9 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa
|
||||
}
|
||||
|
||||
protected clearTextEditorViewState(resources: URI[], group?: IEditorGroup): void {
|
||||
resources.forEach(resource => {
|
||||
for (const resource of resources) {
|
||||
this.editorMemento.clearEditorState(resource, group);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private updateEditorConfiguration(configuration?: IEditorConfiguration): void {
|
||||
|
||||
@@ -16,7 +16,6 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ScrollType, IEditor } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
@@ -158,12 +157,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
|
||||
// Otherwise save it
|
||||
else {
|
||||
super.saveTextEditorViewState(resource);
|
||||
|
||||
// Make sure to clean up when the input gets disposed
|
||||
Event.once(input.onDispose)(() => {
|
||||
super.clearTextEditorViewState([resource]);
|
||||
});
|
||||
super.saveTextEditorViewState(resource, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,18 +105,27 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container > .monaco-button-dropdown,
|
||||
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container > .monaco-button {
|
||||
margin: 4px 5px; /* allows button focus outline to be visible */
|
||||
}
|
||||
|
||||
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-button {
|
||||
outline-offset: 2px !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-text-button {
|
||||
width: fit-content;
|
||||
width: -moz-fit-content;
|
||||
padding: 5px 10px;
|
||||
margin: 4px 5px; /* allows button focus outline to be visible */
|
||||
display: inline-block; /* to enable ellipsis in text overflow */
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-text-button {
|
||||
display: inline-block; /* to enable ellipsis in text overflow */
|
||||
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-dropdown-button {
|
||||
padding: 5px
|
||||
}
|
||||
|
||||
/** Notification: Progress */
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
}
|
||||
|
||||
.monaco-workbench > .notifications-toasts .notification-toast-container > .notification-toast {
|
||||
margin: 5px; /* enables separation and drop shadows around toasts */
|
||||
margin: 8px; /* enables separation and drop shadows around toasts */
|
||||
transform: translate3d(0px, 100%, 0px); /* move the notification 50px to the bottom (to prevent bleed through) */
|
||||
opacity: 0; /* fade the toast in */
|
||||
transition: transform 300ms ease-out, opacity 300ms ease-out;
|
||||
|
||||
@@ -12,14 +12,16 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import { CLEAR_NOTIFICATION, EXPAND_NOTIFICATION, COLLAPSE_NOTIFICATION, CLEAR_ALL_NOTIFICATIONS, HIDE_NOTIFICATIONS_CENTER } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { Codicon, registerIcon } from 'vs/base/common/codicons';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
const clearIcon = registerIcon('notifications-clear', Codicon.close);
|
||||
const clearAllIcon = registerIcon('notifications-clear-all', Codicon.clearAll);
|
||||
const hideIcon = registerIcon('notifications-hide', Codicon.chevronDown);
|
||||
const expandIcon = registerIcon('notifications-expand', Codicon.chevronUp);
|
||||
const collapseIcon = registerIcon('notifications-collapse', Codicon.chevronDown);
|
||||
const configureIcon = registerIcon('notifications-configure', Codicon.gear);
|
||||
const clearIcon = registerIcon('notifications-clear', Codicon.close, localize('clearIcon', 'Icon for the clear action in notifications.'));
|
||||
const clearAllIcon = registerIcon('notifications-clear-all', Codicon.clearAll, localize('clearAllIcon', 'Icon for the clear all action in notifications.'));
|
||||
const hideIcon = registerIcon('notifications-hide', Codicon.chevronDown, localize('hideIcon', 'Icon for the hide action in notifications.'));
|
||||
const expandIcon = registerIcon('notifications-expand', Codicon.chevronUp, localize('expandIcon', 'Icon for the expand action in notifications.'));
|
||||
const collapseIcon = registerIcon('notifications-collapse', Codicon.chevronDown, localize('collapseIcon', 'Icon for the collapse action in notifications.'));
|
||||
const configureIcon = registerIcon('notifications-configure', Codicon.gear, localize('configureIcon', 'Icon for the configure action in notifications.'));
|
||||
|
||||
export class ClearNotificationAction extends Action {
|
||||
|
||||
@@ -31,7 +33,7 @@ export class ClearNotificationAction extends Action {
|
||||
label: string,
|
||||
@ICommandService private readonly commandService: ICommandService
|
||||
) {
|
||||
super(id, label, clearIcon.classNames);
|
||||
super(id, label, ThemeIcon.asClassName(clearIcon));
|
||||
}
|
||||
|
||||
async run(notification: INotificationViewItem): Promise<void> {
|
||||
@@ -49,7 +51,7 @@ export class ClearAllNotificationsAction extends Action {
|
||||
label: string,
|
||||
@ICommandService private readonly commandService: ICommandService
|
||||
) {
|
||||
super(id, label, clearAllIcon.classNames);
|
||||
super(id, label, ThemeIcon.asClassName(clearAllIcon));
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
@@ -67,7 +69,7 @@ export class HideNotificationsCenterAction extends Action {
|
||||
label: string,
|
||||
@ICommandService private readonly commandService: ICommandService
|
||||
) {
|
||||
super(id, label, hideIcon.classNames);
|
||||
super(id, label, ThemeIcon.asClassName(hideIcon));
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
@@ -85,7 +87,7 @@ export class ExpandNotificationAction extends Action {
|
||||
label: string,
|
||||
@ICommandService private readonly commandService: ICommandService
|
||||
) {
|
||||
super(id, label, expandIcon.classNames);
|
||||
super(id, label, ThemeIcon.asClassName(expandIcon));
|
||||
}
|
||||
|
||||
async run(notification: INotificationViewItem): Promise<void> {
|
||||
@@ -103,7 +105,7 @@ export class CollapseNotificationAction extends Action {
|
||||
label: string,
|
||||
@ICommandService private readonly commandService: ICommandService
|
||||
) {
|
||||
super(id, label, collapseIcon.classNames);
|
||||
super(id, label, ThemeIcon.asClassName(collapseIcon));
|
||||
}
|
||||
|
||||
async run(notification: INotificationViewItem): Promise<void> {
|
||||
@@ -121,7 +123,7 @@ export class ConfigureNotificationAction extends Action {
|
||||
label: string,
|
||||
public readonly configurationActions: ReadonlyArray<IAction>
|
||||
) {
|
||||
super(id, label, configureIcon.classNames);
|
||||
super(id, label, ThemeIcon.asClassName(configureIcon));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente
|
||||
protected updateStyles(): void {
|
||||
if (this.notificationsCenterContainer && this.notificationsCenterHeader) {
|
||||
const widgetShadowColor = this.getColor(widgetShadow);
|
||||
this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : '';
|
||||
this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 0 8px 2px ${widgetShadowColor}` : '';
|
||||
|
||||
const borderColor = this.getColor(NOTIFICATIONS_CENTER_BORDER);
|
||||
this.notificationsCenterContainer.style.border = borderColor ? `1px solid ${borderColor}` : '';
|
||||
|
||||
@@ -15,20 +15,20 @@ import { IListService, WorkbenchList } from 'vs/platform/list/browser/listServic
|
||||
// Center
|
||||
export const SHOW_NOTIFICATIONS_CENTER = 'notifications.showList';
|
||||
export const HIDE_NOTIFICATIONS_CENTER = 'notifications.hideList';
|
||||
export const TOGGLE_NOTIFICATIONS_CENTER = 'notifications.toggleList';
|
||||
const TOGGLE_NOTIFICATIONS_CENTER = 'notifications.toggleList';
|
||||
|
||||
// Toasts
|
||||
export const HIDE_NOTIFICATION_TOAST = 'notifications.hideToasts';
|
||||
export const FOCUS_NOTIFICATION_TOAST = 'notifications.focusToasts';
|
||||
export const FOCUS_NEXT_NOTIFICATION_TOAST = 'notifications.focusNextToast';
|
||||
export const FOCUS_PREVIOUS_NOTIFICATION_TOAST = 'notifications.focusPreviousToast';
|
||||
export const FOCUS_FIRST_NOTIFICATION_TOAST = 'notifications.focusFirstToast';
|
||||
export const FOCUS_LAST_NOTIFICATION_TOAST = 'notifications.focusLastToast';
|
||||
const HIDE_NOTIFICATION_TOAST = 'notifications.hideToasts';
|
||||
const FOCUS_NOTIFICATION_TOAST = 'notifications.focusToasts';
|
||||
const FOCUS_NEXT_NOTIFICATION_TOAST = 'notifications.focusNextToast';
|
||||
const FOCUS_PREVIOUS_NOTIFICATION_TOAST = 'notifications.focusPreviousToast';
|
||||
const FOCUS_FIRST_NOTIFICATION_TOAST = 'notifications.focusFirstToast';
|
||||
const FOCUS_LAST_NOTIFICATION_TOAST = 'notifications.focusLastToast';
|
||||
|
||||
// Notification
|
||||
export const COLLAPSE_NOTIFICATION = 'notification.collapse';
|
||||
export const EXPAND_NOTIFICATION = 'notification.expand';
|
||||
export const TOGGLE_NOTIFICATION = 'notification.toggle';
|
||||
const TOGGLE_NOTIFICATION = 'notification.toggle';
|
||||
export const CLEAR_NOTIFICATION = 'notification.clear';
|
||||
export const CLEAR_ALL_NOTIFICATIONS = 'notifications.clearAll';
|
||||
|
||||
@@ -159,12 +159,20 @@ export function registerNotificationCommands(center: INotificationsCenterControl
|
||||
});
|
||||
|
||||
// Hide Toasts
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
CommandsRegistry.registerCommand(HIDE_NOTIFICATION_TOAST, accessor => toasts.hide());
|
||||
|
||||
KeybindingsRegistry.registerKeybindingRule({
|
||||
id: HIDE_NOTIFICATION_TOAST,
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
weight: KeybindingWeight.WorkbenchContrib - 50, // lower when not focused (e.g. let editor suggest win over this command)
|
||||
when: NotificationsToastsVisibleContext,
|
||||
primary: KeyCode.Escape,
|
||||
handler: accessor => toasts.hide()
|
||||
primary: KeyCode.Escape
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerKeybindingRule({
|
||||
id: HIDE_NOTIFICATION_TOAST,
|
||||
weight: KeybindingWeight.WorkbenchContrib + 100, // higher when focused
|
||||
when: ContextKeyExpr.and(NotificationsToastsVisibleContext, NotificationFocusedContext),
|
||||
primary: KeyCode.Escape
|
||||
});
|
||||
|
||||
// Focus Toasts
|
||||
|
||||
@@ -482,7 +482,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast
|
||||
toast.style.background = backgroundColor ? backgroundColor : '';
|
||||
|
||||
const widgetShadowColor = this.getColor(widgetShadow);
|
||||
toast.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : '';
|
||||
toast.style.boxShadow = widgetShadowColor ? `0 0 8px 2px ${widgetShadowColor}` : '';
|
||||
|
||||
const borderColor = this.getColor(NOTIFICATIONS_TOAST_BORDER);
|
||||
toast.style.border = borderColor ? `1px solid ${borderColor}` : '';
|
||||
|
||||
@@ -8,11 +8,11 @@ import { clearNode, addDisposableListener, EventType, EventHelper, $ } from 'vs/
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ButtonGroup } from 'vs/base/browser/ui/button/button';
|
||||
import { ButtonBar } from 'vs/base/browser/ui/button/button';
|
||||
import { attachButtonStyler, attachProgressBarStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IAction, IActionRunner } from 'vs/base/common/actions';
|
||||
import { ActionRunner, ActionWithMenuAction, IAction, IActionRunner } from 'vs/base/common/actions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { dispose, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
@@ -285,7 +285,8 @@ export class NotificationTemplateRenderer extends Disposable {
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -441,27 +442,40 @@ export class NotificationTemplateRenderer extends Disposable {
|
||||
|
||||
const primaryActions = notification.actions ? notification.actions.primary : undefined;
|
||||
if (notification.expanded && isNonEmptyArray(primaryActions)) {
|
||||
const buttonGroup = new ButtonGroup(this.template.buttonsContainer, primaryActions.length, { title: true /* assign titles to buttons in case they overflow */ });
|
||||
buttonGroup.buttons.forEach((button, index) => {
|
||||
const action = primaryActions[index];
|
||||
button.label = action.label;
|
||||
|
||||
this.inputDisposables.add(button.onDidClick(e => {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
const that = this;
|
||||
const actionRunner: IActionRunner = new class extends ActionRunner {
|
||||
protected async runAction(action: IAction): Promise<void> {
|
||||
// Run action
|
||||
this.actionRunner.run(action, notification);
|
||||
that.actionRunner.run(action, notification);
|
||||
|
||||
// Hide notification (unless explicitly prevented)
|
||||
if (!(action instanceof ChoiceAction) || !action.keepOpen) {
|
||||
notification.close();
|
||||
}
|
||||
}
|
||||
}();
|
||||
const buttonToolbar = this.inputDisposables.add(new ButtonBar(this.template.buttonsContainer));
|
||||
for (const action of primaryActions) {
|
||||
const buttonOptions = { title: true, /* assign titles to buttons in case they overflow */ };
|
||||
const dropdownActions = action instanceof ChoiceAction ? action.menu
|
||||
: action instanceof ActionWithMenuAction ? action.actions : undefined;
|
||||
const button = this.inputDisposables.add(
|
||||
dropdownActions
|
||||
? buttonToolbar.addButtonWithDropdown({
|
||||
...buttonOptions,
|
||||
contextMenuProvider: this.contextMenuService,
|
||||
actions: dropdownActions,
|
||||
actionRunner
|
||||
})
|
||||
: buttonToolbar.addButton(buttonOptions));
|
||||
button.label = action.label;
|
||||
this.inputDisposables.add(button.onDidClick(e => {
|
||||
EventHelper.stop(e, true);
|
||||
actionRunner.run(action);
|
||||
}));
|
||||
|
||||
this.inputDisposables.add(attachButtonStyler(button, this.themeService));
|
||||
});
|
||||
|
||||
this.inputDisposables.add(buttonGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,10 +84,6 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.panel > .composite.title > .composite-bar-excess {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.panel > .composite.title> .panel-switcher-container > .monaco-action-bar {
|
||||
line-height: 27px; /* matches panel titles in settings */
|
||||
height: 35px;
|
||||
|
||||
@@ -18,11 +18,13 @@ import { IActivity } from 'vs/workbench/common/activity';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ActivePanelContext, PanelPositionContext } from 'vs/workbench/common/panel';
|
||||
import { ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Codicon, registerIcon } from 'vs/base/common/codicons';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
const maximizeIcon = registerIcon('panel-maximize', Codicon.chevronUp);
|
||||
const restoreIcon = registerIcon('panel-restore', Codicon.chevronDown);
|
||||
const closeIcon = registerIcon('panel-close', Codicon.close);
|
||||
const maximizeIcon = registerIcon('panel-maximize', Codicon.chevronUp, nls.localize('maximizeIcon', 'Icon to maximize a panel.'));
|
||||
const restoreIcon = registerIcon('panel-restore', Codicon.chevronDown, nls.localize('restoreIcon', 'Icon to restore a panel.'));
|
||||
const closeIcon = registerIcon('panel-close', Codicon.close, nls.localize('closeIcon', 'Icon to close a panel.'));
|
||||
|
||||
export class ClosePanelAction extends Action {
|
||||
|
||||
@@ -34,7 +36,7 @@ export class ClosePanelAction extends Action {
|
||||
name: string,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, name, closeIcon.classNames);
|
||||
super(id, name, ThemeIcon.asClassName(closeIcon));
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
@@ -106,11 +108,11 @@ export class ToggleMaximizedPanelAction extends Action {
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IEditorGroupsService editorGroupsService: IEditorGroupsService
|
||||
) {
|
||||
super(id, label, layoutService.isPanelMaximized() ? restoreIcon.classNames : maximizeIcon.classNames);
|
||||
super(id, label, layoutService.isPanelMaximized() ? ThemeIcon.asClassName(restoreIcon) : ThemeIcon.asClassName(maximizeIcon));
|
||||
|
||||
this.toDispose.add(editorGroupsService.onDidLayout(() => {
|
||||
const maximized = this.layoutService.isPanelMaximized();
|
||||
this.class = maximized ? restoreIcon.classNames : maximizeIcon.classNames;
|
||||
this.class = maximized ? ThemeIcon.asClassName(restoreIcon) : ThemeIcon.asClassName(maximizeIcon);
|
||||
this.label = maximized ? ToggleMaximizedPanelAction.RESTORE_LABEL : ToggleMaximizedPanelAction.MAXIMIZE_LABEL;
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { CompositePart, ICompositeTitleLabel } from 'vs/workbench/browser/parts/
|
||||
import { Panel, PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel';
|
||||
import { IPanelService, IPanelIdentifier } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
@@ -37,7 +37,6 @@ import { ViewContainer, IViewDescriptorService, IViewContainerModel, ViewContain
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { ViewMenuActions, ViewContainerMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions';
|
||||
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
|
||||
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
import { Before2D, CompositeDragAndDropObserver, ICompositeDragAndDrop, toggleDropEffect } from 'vs/workbench/browser/dnd';
|
||||
import { IActivity } from 'vs/workbench/common/activity';
|
||||
|
||||
@@ -118,7 +117,6 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IExtensionService private readonly extensionService: IExtensionService,
|
||||
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
|
||||
) {
|
||||
super(
|
||||
notificationService,
|
||||
@@ -140,7 +138,6 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
);
|
||||
|
||||
this.panelRegistry = Registry.as<PanelRegistry>(PanelExtensions.Panels);
|
||||
storageKeysSyncRegistryService.registerStorageKey({ key: PanelPart.PINNED_PANELS, version: 1 });
|
||||
|
||||
this.dndHandler = new CompositeDragAndDrop(this.viewDescriptorService, ViewContainerLocation.Panel,
|
||||
(id: string, focus?: boolean) => (this.openPanel(id, focus) as Promise<IPaneComposite | undefined>).then(panel => panel || null),
|
||||
@@ -338,7 +335,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
disposables.clear();
|
||||
this.onDidRegisterExtensions();
|
||||
this.compositeBar.onDidChange(() => this.saveCachedPanels(), this, disposables);
|
||||
this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e), this, disposables);
|
||||
this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e), this, disposables);
|
||||
}));
|
||||
|
||||
}
|
||||
@@ -670,7 +667,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
return this.toolBar.getItemsWidth();
|
||||
}
|
||||
|
||||
private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void {
|
||||
private onDidStorageValueChange(e: IStorageValueChangeEvent): void {
|
||||
if (e.key === PanelPart.PINNED_PANELS && e.scope === StorageScope.GLOBAL
|
||||
&& this.cachedPanelsValue !== this.getStoredCachedPanelsValue() /* This checks if current window changed the value or not */) {
|
||||
this._cachedPanelsValue = undefined;
|
||||
@@ -760,7 +757,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
}
|
||||
|
||||
private setStoredCachedViewletsValue(value: string): void {
|
||||
this.storageService.store(PanelPart.PINNED_PANELS, value, StorageScope.GLOBAL);
|
||||
this.storageService.store(PanelPart.PINNED_PANELS, value, StorageScope.GLOBAL, StorageTarget.USER);
|
||||
}
|
||||
|
||||
private getPlaceholderViewContainers(): IPlaceholderViewContainer[] {
|
||||
@@ -792,7 +789,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
}
|
||||
|
||||
private setStoredPlaceholderViewContainersValue(value: string): void {
|
||||
this.storageService.store(PanelPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.WORKSPACE);
|
||||
this.storageService.store(PanelPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.WORKSPACE, StorageTarget.MACHINE);
|
||||
}
|
||||
|
||||
private getViewContainer(panelId: string): ViewContainer | undefined {
|
||||
|
||||
@@ -23,7 +23,7 @@ import { isThemeColor } from 'vs/editor/common/editorCommon';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { EventHelper, createStyleSheet, addDisposableListener, EventType, hide, show, isAncestor, appendChildren } from 'vs/base/browser/dom';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
@@ -32,7 +32,6 @@ import { ToggleStatusbarVisibilityAction } from 'vs/workbench/browser/actions/la
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Command } from 'vs/editor/common/modes';
|
||||
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
@@ -102,10 +101,10 @@ class StatusbarViewModel extends Disposable {
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
|
||||
this._register(this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e)));
|
||||
}
|
||||
|
||||
private onDidStorageChange(event: IWorkspaceStorageChangeEvent): void {
|
||||
private onDidStorageValueChange(event: IStorageValueChangeEvent): void {
|
||||
if (event.key === StatusbarViewModel.HIDDEN_ENTRIES_KEY && event.scope === StorageScope.GLOBAL) {
|
||||
|
||||
// Keep current hidden entries
|
||||
@@ -275,7 +274,7 @@ class StatusbarViewModel extends Disposable {
|
||||
|
||||
private saveState(): void {
|
||||
if (this.hidden.size > 0) {
|
||||
this.storageService.store(StatusbarViewModel.HIDDEN_ENTRIES_KEY, JSON.stringify(Array.from(this.hidden.values())), StorageScope.GLOBAL);
|
||||
this.storageService.store(StatusbarViewModel.HIDDEN_ENTRIES_KEY, JSON.stringify(Array.from(this.hidden.values())), StorageScope.GLOBAL, StorageTarget.USER);
|
||||
} else {
|
||||
this.storageService.remove(StatusbarViewModel.HIDDEN_ENTRIES_KEY, StorageScope.GLOBAL);
|
||||
}
|
||||
@@ -405,12 +404,9 @@ export class StatusbarPart extends Part implements IStatusbarService {
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
|
||||
) {
|
||||
super(Parts.STATUSBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
|
||||
|
||||
storageKeysSyncRegistryService.registerStorageKey({ key: StatusbarViewModel.HIDDEN_ENTRIES_KEY, version: 1 });
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user