mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-27 18:47:22 -05:00
Merge from vscode 27ada910e121e23a6d95ecca9cae595fb98ab568
This commit is contained in:
@@ -16,6 +16,7 @@ import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
|
||||
interface AllowedExtension {
|
||||
id: string;
|
||||
@@ -62,7 +63,8 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
private readonly _proxy: ExtHostAuthenticationShape,
|
||||
public readonly id: string,
|
||||
public readonly displayName: string,
|
||||
private readonly notificationService: INotificationService
|
||||
private readonly notificationService: INotificationService,
|
||||
private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -148,6 +150,8 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
||||
order: 3
|
||||
});
|
||||
|
||||
this.storageKeysSyncRegistryService.registerStorageKey({ key: `${this.id}-${session.account.displayName}`, version: 1 });
|
||||
|
||||
const manageCommand = CommandsRegistry.registerCommand({
|
||||
id: `configureSessions${session.id}`,
|
||||
handler: (accessor, args) => {
|
||||
@@ -285,14 +289,15 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@INotificationService private readonly notificationService: INotificationService
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
|
||||
) {
|
||||
super();
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication);
|
||||
}
|
||||
|
||||
async $registerAuthenticationProvider(id: string, displayName: string): Promise<void> {
|
||||
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, this.notificationService);
|
||||
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, this.notificationService, this.storageKeysSyncRegistryService);
|
||||
await provider.initialize();
|
||||
this.authenticationService.registerAuthenticationProvider(id, provider);
|
||||
}
|
||||
@@ -316,14 +321,14 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
|
||||
const { choice } = await this.dialogService.show(
|
||||
Severity.Info,
|
||||
nls.localize('confirmAuthenticationAccess', "The extension '{0}' is trying to access authentication information for the {1} account '{2}'.", extensionName, providerName, accountName),
|
||||
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow")],
|
||||
nls.localize('confirmAuthenticationAccess', "The extension '{0}' wants to access the {1} account '{2}'.", extensionName, providerName, accountName),
|
||||
[nls.localize('allow', "Allow"), nls.localize('cancel', "Cancel")],
|
||||
{
|
||||
cancelId: 0
|
||||
cancelId: 1
|
||||
}
|
||||
);
|
||||
|
||||
const allow = choice === 1;
|
||||
const allow = choice === 0;
|
||||
if (allow) {
|
||||
allowList.push({ id: extensionId, name: extensionName });
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
|
||||
@@ -336,13 +341,13 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
const { choice } = await this.dialogService.show(
|
||||
Severity.Info,
|
||||
nls.localize('confirmLogin', "The extension '{0}' wants to sign in using {1}.", extensionName, providerName),
|
||||
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow")],
|
||||
[nls.localize('allow', "Allow"), nls.localize('cancel', "Cancel")],
|
||||
{
|
||||
cancelId: 0
|
||||
cancelId: 1
|
||||
}
|
||||
);
|
||||
|
||||
return choice === 1;
|
||||
return choice === 0;
|
||||
}
|
||||
|
||||
async $setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise<void> {
|
||||
|
||||
@@ -177,6 +177,10 @@ export class MainThreadCommentController {
|
||||
this._reactions = reactions;
|
||||
}
|
||||
|
||||
get options() {
|
||||
return this._features.options;
|
||||
}
|
||||
|
||||
private readonly _threads: Map<number, MainThreadCommentThread> = new Map<number, MainThreadCommentThread>();
|
||||
public activeCommentThread?: MainThreadCommentThread;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import severity from 'vs/base/common/severity';
|
||||
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { convertToVSCPaths, convertToDAPaths } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
import { DebugConfigurationProviderScope } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadDebugService)
|
||||
export class MainThreadDebugService implements MainThreadDebugServiceShape, IDebugAdapterFactory {
|
||||
@@ -155,11 +155,11 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public $registerDebugConfigurationProvider(debugType: string, providerScope: DebugConfigurationProviderScope, hasProvide: boolean, hasResolve: boolean, hasResolve2: boolean, hasProvideDebugAdapter: boolean, handle: number): Promise<void> {
|
||||
public $registerDebugConfigurationProvider(debugType: string, providerTriggerKind: DebugConfigurationProviderTriggerKind, hasProvide: boolean, hasResolve: boolean, hasResolve2: boolean, hasProvideDebugAdapter: boolean, handle: number): Promise<void> {
|
||||
|
||||
const provider = <IDebugConfigurationProvider>{
|
||||
type: debugType,
|
||||
scope: providerScope
|
||||
triggerKind: providerTriggerKind
|
||||
};
|
||||
if (hasProvide) {
|
||||
provider.provideDebugConfigurations = (folder, token) => {
|
||||
|
||||
@@ -24,7 +24,9 @@ import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContex
|
||||
import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { DEFAULT_EDITOR_ID } from 'vs/workbench/contrib/files/common/files';
|
||||
import { openEditorWith } from 'vs/workbench/contrib/files/common/openWith';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
|
||||
export class MainThreadTextEditors implements MainThreadTextEditorsShape {
|
||||
|
||||
@@ -296,26 +298,17 @@ CommandsRegistry.registerCommand('_workbench.open', function (accessor: Services
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAccessor, args: [URI, string, ITextEditorOptions | undefined, EditorViewColumn | undefined]) => {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const editorGroupsService = accessor.get(IEditorGroupsService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
|
||||
const [resource, id, options, position] = args;
|
||||
|
||||
const group = editorGroupService.getGroup(viewColumnToEditorGroup(editorGroupService, position)) ?? editorGroupService.activeGroup;
|
||||
const group = editorGroupsService.getGroup(viewColumnToEditorGroup(editorGroupsService, position)) ?? editorGroupsService.activeGroup;
|
||||
const textOptions = options ? { ...options, ignoreOverrides: true } : { ignoreOverrides: true };
|
||||
|
||||
const fileEditorInput = editorService.createEditorInput({ resource, forceFile: true });
|
||||
if (id === DEFAULT_EDITOR_ID) {
|
||||
return editorService.openEditor(fileEditorInput, textOptions, position);
|
||||
}
|
||||
|
||||
const editors = editorService.getEditorOverrides(fileEditorInput, undefined, group);
|
||||
for (const [handler, data] of editors) {
|
||||
if (data.id === id) {
|
||||
return handler.open(fileEditorInput, options, group, id);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
const input = editorService.createEditorInput({ resource });
|
||||
return openEditorWith(input, id, textOptions, group, editorService, configurationService, quickInputService);
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -8,12 +8,13 @@ import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IEx
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/browser/notebookService';
|
||||
import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, CellKind, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, CellKind, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
|
||||
export class MainThreadNotebookDocument extends Disposable {
|
||||
private _textModel: NotebookTextModel;
|
||||
@@ -63,6 +64,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
@INotebookService private _notebookService: INotebookService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService
|
||||
|
||||
) {
|
||||
super();
|
||||
@@ -85,22 +87,25 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
this._proxy.$updateActiveEditor(e.viewType, e.uri);
|
||||
}));
|
||||
|
||||
let userOrder = this.configurationService.getValue<string[]>('notebook.displayOrder');
|
||||
this._proxy.$acceptDisplayOrder({
|
||||
defaultOrder: NOTEBOOK_DISPLAY_ORDER,
|
||||
userOrder: userOrder
|
||||
});
|
||||
const updateOrder = () => {
|
||||
let userOrder = this.configurationService.getValue<string[]>('notebook.displayOrder');
|
||||
this._proxy.$acceptDisplayOrder({
|
||||
defaultOrder: this.accessibilityService.isScreenReaderOptimized() ? ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER : NOTEBOOK_DISPLAY_ORDER,
|
||||
userOrder: userOrder
|
||||
});
|
||||
};
|
||||
|
||||
this.configurationService.onDidChangeConfiguration(e => {
|
||||
updateOrder();
|
||||
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectedKeys.indexOf('notebook.displayOrder') >= 0) {
|
||||
let userOrder = this.configurationService.getValue<string[]>('notebook.displayOrder');
|
||||
|
||||
this._proxy.$acceptDisplayOrder({
|
||||
defaultOrder: NOTEBOOK_DISPLAY_ORDER,
|
||||
userOrder: userOrder
|
||||
});
|
||||
updateOrder();
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => {
|
||||
updateOrder();
|
||||
}));
|
||||
}
|
||||
|
||||
async $registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, handle: number, preloads: UriComponents[]): Promise<void> {
|
||||
|
||||
@@ -309,8 +309,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
this.registerEditorProvider(ModelType.Text, extensionData, viewType, options, capabilities, true);
|
||||
}
|
||||
|
||||
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerResource: boolean): void {
|
||||
this.registerEditorProvider(ModelType.Custom, extensionData, viewType, options, {}, supportsMultipleEditorsPerResource);
|
||||
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void {
|
||||
this.registerEditorProvider(ModelType.Custom, extensionData, viewType, options, {}, supportsMultipleEditorsPerDocument);
|
||||
}
|
||||
|
||||
private registerEditorProvider(
|
||||
@@ -319,14 +319,14 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
viewType: string,
|
||||
options: modes.IWebviewPanelOptions,
|
||||
capabilities: extHostProtocol.CustomTextEditorCapabilities,
|
||||
supportsMultipleEditorsPerResource: boolean,
|
||||
supportsMultipleEditorsPerDocument: boolean,
|
||||
): DisposableStore {
|
||||
if (this._editorProviders.has(viewType)) {
|
||||
throw new Error(`Provider for ${viewType} already registered`);
|
||||
}
|
||||
|
||||
this._customEditorService.registerCustomEditorCapabilities(viewType, {
|
||||
supportsMultipleEditorsPerResource
|
||||
supportsMultipleEditorsPerDocument
|
||||
});
|
||||
|
||||
const extension = reviveWebviewExtension(extensionData);
|
||||
@@ -610,8 +610,9 @@ namespace HotExitState {
|
||||
|
||||
class MainThreadCustomEditorModel extends Disposable implements ICustomEditorModel, IWorkingCopy {
|
||||
|
||||
private _hotExitState: HotExitState.State = HotExitState.Allowed;
|
||||
private readonly _fromBackup: boolean = false;
|
||||
private _hotExitState: HotExitState.State = HotExitState.Allowed;
|
||||
private _backupId: string | undefined;
|
||||
|
||||
private _currentEditIndex: number = -1;
|
||||
private _savePoint: number = -1;
|
||||
@@ -716,6 +717,10 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
return this._viewType;
|
||||
}
|
||||
|
||||
public get backupId() {
|
||||
return this._backupId;
|
||||
}
|
||||
|
||||
public pushEdit(editId: number, label: string | undefined) {
|
||||
if (!this._editable) {
|
||||
throw new Error('Document is not editable');
|
||||
@@ -902,6 +907,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
if (this._hotExitState === pendingState) {
|
||||
this._hotExitState = HotExitState.Allowed;
|
||||
backupData.meta!.backupId = backupId;
|
||||
this._backupId = backupId;
|
||||
}
|
||||
} catch (e) {
|
||||
// Make sure state has not changed in the meantime
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.8394 20.337L14.9999 9.009V3.009H16.4999V1.509H14.9909V1.5L14.3069 1.5075H7.4999V3H8.9999V8.928L3.1589 20.3415C3.04535 20.57 2.99197 20.8237 3.00381 21.0786C3.01566 21.3335 3.09234 21.5812 3.22659 21.7982C3.36085 22.0152 3.54825 22.1944 3.77106 22.3187C3.99387 22.4431 4.24473 22.5086 4.4999 22.509H19.4999C19.7556 22.5087 20.0069 22.4431 20.2301 22.3184C20.4533 22.1937 20.6409 22.014 20.7751 21.7964C20.9093 21.5788 20.9856 21.3305 20.9969 21.075C21.0082 20.8196 20.9539 20.5656 20.8394 20.337ZM10.3394 9.612L10.4999 9.2895V3.054L13.4999 3.018V9.0105V9.3735L13.6664 9.696L16.4054 15.009H7.5734L10.3394 9.612ZM4.4999 21.0255L6.8114 16.509H17.1839L19.5044 21.009L4.4999 21.0255Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 810 B |
@@ -30,6 +30,7 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/wor
|
||||
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';
|
||||
|
||||
export interface IUserFriendlyViewsContainerDescriptor {
|
||||
id: string;
|
||||
@@ -53,7 +54,8 @@ const viewsContainerSchema: IJSONSchema = {
|
||||
description: localize('vscode.extension.contributes.views.containers.icon', "Path to the container icon. Icons are 24x24 centered on a 50x40 block and have a fill color of 'rgb(215, 218, 224)' or '#d7dae0'. It is recommended that icons be in SVG, though any image file type is accepted."),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ['id', 'title', 'icon']
|
||||
};
|
||||
|
||||
export const viewsContainersContribution: IJSONSchema = {
|
||||
@@ -255,7 +257,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
|
||||
private registerTestViewContainer(): void {
|
||||
const title = localize('test', "Test");
|
||||
const icon = URI.parse(require.toUrl('./media/test.svg'));
|
||||
const icon = Codicon.beaker.classNames;
|
||||
|
||||
this.registerCustomViewContainer(TEST_VIEW_CONTAINER_ID, title, icon, TEST_VIEW_CONTAINER_ORDER, undefined, ViewContainerLocation.Sidebar);
|
||||
}
|
||||
@@ -310,7 +312,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
return order;
|
||||
}
|
||||
|
||||
private registerCustomViewContainer(id: string, title: string, icon: URI, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer {
|
||||
private registerCustomViewContainer(id: string, title: string, icon: URI | string, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer {
|
||||
let viewContainer = this.viewContainersRegistry.get(id);
|
||||
|
||||
if (!viewContainer) {
|
||||
|
||||
@@ -72,6 +72,10 @@ const configurationEntrySchema: IJSONSchema = {
|
||||
deprecationMessage: {
|
||||
type: 'string',
|
||||
description: nls.localize('scope.deprecationMessage', 'If set, the property is marked as deprecated and the given message is shown as an explanation.')
|
||||
},
|
||||
markdownDeprecationMessage: {
|
||||
type: 'string',
|
||||
description: nls.localize('scope.markdownDeprecationMessage', 'If set, the property is marked as deprecated and the given message is shown as an explanation in the markdown format.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostAp
|
||||
import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication';
|
||||
import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline';
|
||||
import { ExtHostNotebookConcatDocument } from 'vs/workbench/api/common/extHostNotebookConcatDocument';
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
|
||||
export interface IExtensionApiFactory {
|
||||
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
|
||||
@@ -93,6 +94,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
const uriTransformer = accessor.get(IURITransformerService);
|
||||
const rpcProtocol = accessor.get(IExtHostRpcService);
|
||||
const extHostStorage = accessor.get(IExtHostStorage);
|
||||
const extensionStoragePaths = accessor.get(IExtensionStoragePaths);
|
||||
const extHostLogService = accessor.get(ILogService);
|
||||
const extHostTunnelService = accessor.get(IExtHostTunnelService);
|
||||
const extHostApiDeprecation = accessor.get(IExtHostApiDeprecationService);
|
||||
@@ -137,7 +139,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol));
|
||||
const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol));
|
||||
const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands));
|
||||
const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation, extHostDocuments));
|
||||
const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation, extHostDocuments, extensionStoragePaths));
|
||||
|
||||
// Check that no named customers are missing
|
||||
// {{SQL CARBON EDIT}} filter out the services we don't expose
|
||||
@@ -203,6 +205,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
login(providerId: string, scopes: string[]): Thenable<vscode.AuthenticationSession> {
|
||||
return extHostAuthentication.login(extension, providerId, scopes);
|
||||
},
|
||||
logout(providerId: string, sessionId: string): Thenable<void> {
|
||||
return extHostAuthentication.logout(providerId, sessionId);
|
||||
},
|
||||
get onDidChangeSessions(): Event<{ [providerId: string]: vscode.AuthenticationSessionsChangeEvent }> {
|
||||
return extHostAuthentication.onDidChangeSessions;
|
||||
},
|
||||
@@ -489,10 +494,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTerminalService.onDidWriteTerminalData(listener, thisArg, disposables);
|
||||
},
|
||||
getEnvironmentVariableCollection(persistent?: boolean): vscode.EnvironmentVariableCollection {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTerminalService.getEnvironmentVariableCollection(extension, persistent);
|
||||
},
|
||||
get state() {
|
||||
return extHostWindow.state;
|
||||
},
|
||||
@@ -589,7 +590,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions } = {}) => {
|
||||
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options);
|
||||
},
|
||||
registerCustomEditorProvider2: (viewType: string, provider: vscode.CustomEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerResource?: boolean } = {}) => {
|
||||
registerCustomEditorProvider2: (viewType: string, provider: vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options);
|
||||
},
|
||||
@@ -855,7 +856,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
extHostLogService.warn('Debug API is disabled in Azure Data Studio');
|
||||
return undefined!;
|
||||
},
|
||||
registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider, scope?: vscode.DebugConfigurationProviderScope) {
|
||||
registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider, triggerKind?: vscode.DebugConfigurationProviderTriggerKind) {
|
||||
extHostLogService.warn('Debug API is disabled in Azure Data Studio');
|
||||
return undefined!;
|
||||
},
|
||||
@@ -938,6 +939,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.activeNotebookEditor;
|
||||
},
|
||||
onDidChangeNotebookDocument(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeNotebookDocument(listener, thisArgs, disposables);
|
||||
},
|
||||
createConcatTextDocument(notebook, selector) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return new ExtHostNotebookConcatDocument(extHostNotebook, extHostDocuments, notebook, selector);
|
||||
@@ -1065,7 +1070,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall,
|
||||
CallHierarchyItem: extHostTypes.CallHierarchyItem,
|
||||
DebugConsoleMode: extHostTypes.DebugConsoleMode,
|
||||
DebugConfigurationProviderScope: extHostTypes.DebugConfigurationProviderScope,
|
||||
DebugConfigurationProviderTriggerKind: extHostTypes.DebugConfigurationProviderTriggerKind,
|
||||
Decoration: extHostTypes.Decoration,
|
||||
UIKind: UIKind,
|
||||
ColorThemeKind: extHostTypes.ColorThemeKind,
|
||||
|
||||
@@ -55,7 +55,7 @@ import { INotebookMimeTypeSelector, IOutput, INotebookDisplayOrder, NotebookCell
|
||||
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { Dto } from 'vs/base/common/types';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { DebugConfigurationProviderScope } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { ITreeItem as sqlITreeItem } from 'sql/workbench/common/views';
|
||||
@@ -139,6 +139,7 @@ export interface MainThreadCommandsShape extends IDisposable {
|
||||
export interface CommentProviderFeatures {
|
||||
reactionGroup?: modes.CommentReaction[];
|
||||
reactionHandler?: boolean;
|
||||
options?: modes.CommentOptions;
|
||||
}
|
||||
|
||||
export type CommentThreadChanges = Partial<{
|
||||
@@ -616,7 +617,7 @@ export interface MainThreadWebviewsShape extends IDisposable {
|
||||
$unregisterSerializer(viewType: string): void;
|
||||
|
||||
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void;
|
||||
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerResource: boolean): void;
|
||||
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void;
|
||||
$unregisterEditorProvider(viewType: string): void;
|
||||
|
||||
$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void;
|
||||
@@ -850,7 +851,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, scope: DebugConfigurationProviderScope, hasProvideMethod: boolean, hasResolveMethod: boolean, hasResolve2Method: boolean, hasProvideDaMethod: boolean, handle: number): Promise<void>;
|
||||
$registerDebugConfigurationProvider(type: string, triggerKind: DebugConfigurationProviderTriggerKind, hasProvideMethod: boolean, hasResolveMethod: boolean, hasResolve2Method: boolean, hasProvideDaMethod: boolean, handle: number): Promise<void>;
|
||||
$registerDebugAdapterDescriptorFactory(type: string, handle: number): Promise<void>;
|
||||
$unregisterDebugConfigurationProvider(handle: number): void;
|
||||
$unregisterDebugAdapterDescriptorFactory(handle: number): void;
|
||||
|
||||
@@ -143,22 +143,22 @@ const newCommands: ApiCommand[] = [
|
||||
new ApiCommand(
|
||||
'vscode.executeDefinitionProvider', '_executeDefinitionProvider', 'Execute all definition providers.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
|
||||
new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location-instances.', mapLocationOrLocationLink)
|
||||
new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location or LocationLink instances.', mapLocationOrLocationLink)
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.executeTypeDefinitionProvider', '_executeTypeDefinitionProvider', 'Execute all type definition providers.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
|
||||
new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location-instances.', mapLocationOrLocationLink)
|
||||
new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location or LocationLink instances.', mapLocationOrLocationLink)
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.executeDeclarationProvider', '_executeDeclarationProvider', 'Execute all declaration providers.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
|
||||
new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location-instances.', mapLocationOrLocationLink)
|
||||
new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location or LocationLink instances.', mapLocationOrLocationLink)
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.executeImplementationProvider', '_executeImplementationProvider', 'Execute all implementation providers.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
|
||||
new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location-instances.', mapLocationOrLocationLink)
|
||||
new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location or LocationLink instances.', mapLocationOrLocationLink)
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.executeReferenceProvider', '_executeReferenceProvider', 'Execute all reference providers.',
|
||||
|
||||
@@ -102,6 +102,15 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
};
|
||||
}
|
||||
|
||||
async logout(providerId: string, sessionId: string): Promise<void> {
|
||||
const provider = this._authenticationProviders.get(providerId);
|
||||
if (!provider) {
|
||||
throw new Error(`No authentication provider with id '${providerId}' is currently registered.`);
|
||||
}
|
||||
|
||||
return provider.logout(sessionId);
|
||||
}
|
||||
|
||||
registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable {
|
||||
if (this._authenticationProviders.get(provider.id)) {
|
||||
throw new Error(`An authentication provider with id '${provider.id}' is already registered.`);
|
||||
|
||||
@@ -451,6 +451,18 @@ class ExtHostCommentController implements vscode.CommentController {
|
||||
this._proxy.$updateCommentControllerFeatures(this.handle, { reactionHandler: !!handler });
|
||||
}
|
||||
|
||||
private _options: modes.CommentOptions | undefined;
|
||||
|
||||
get options() {
|
||||
return this._options;
|
||||
}
|
||||
|
||||
set options(options: modes.CommentOptions | undefined) {
|
||||
this._options = options;
|
||||
|
||||
this._proxy.$updateCommentControllerFeatures(this.handle, { options: this._options });
|
||||
}
|
||||
|
||||
constructor(
|
||||
private _extension: IExtensionDescription,
|
||||
private _handle: number,
|
||||
|
||||
@@ -51,7 +51,7 @@ export interface IExtHostDebugService extends ExtHostDebugServiceShape {
|
||||
addBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise<void>;
|
||||
removeBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise<void>;
|
||||
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, options: vscode.DebugSessionOptions): Promise<boolean>;
|
||||
registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider, scope: vscode.DebugConfigurationProviderScope): vscode.Disposable;
|
||||
registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider, trigger: vscode.DebugConfigurationProviderTriggerKind): vscode.Disposable;
|
||||
registerDebugAdapterDescriptorFactory(extension: IExtensionDescription, type: string, factory: vscode.DebugAdapterDescriptorFactory): vscode.Disposable;
|
||||
registerDebugAdapterTrackerFactory(type: string, factory: vscode.DebugAdapterTrackerFactory): vscode.Disposable;
|
||||
asDebugSourceUri(source: vscode.DebugProtocolSource, session?: vscode.DebugSession): vscode.Uri;
|
||||
@@ -299,7 +299,7 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
|
||||
});
|
||||
}
|
||||
|
||||
public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider, scope: vscode.DebugConfigurationProviderScope): vscode.Disposable {
|
||||
public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider, trigger: vscode.DebugConfigurationProviderTriggerKind): vscode.Disposable {
|
||||
|
||||
if (!provider) {
|
||||
return new Disposable(() => { });
|
||||
@@ -312,7 +312,7 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
|
||||
const handle = this._configProviderHandleCounter++;
|
||||
this._configProviders.push({ type, handle, provider });
|
||||
|
||||
this._debugServiceProxy.$registerDebugConfigurationProvider(type, scope,
|
||||
this._debugServiceProxy.$registerDebugConfigurationProvider(type, trigger,
|
||||
!!provider.provideDebugConfigurations,
|
||||
!!provider.resolveDebugConfiguration,
|
||||
!!provider.resolveDebugConfigurationWithSubstitutedVariables,
|
||||
|
||||
@@ -16,7 +16,7 @@ import { ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/co
|
||||
import { ActivatedExtension, EmptyExtension, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
import { ExtHostStorage, IExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
|
||||
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionActivationError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
@@ -33,6 +33,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
|
||||
interface ITestRunner {
|
||||
/** Old test runner API, as exported from `vscode/lib/testrunner` */
|
||||
@@ -78,6 +79,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
|
||||
protected readonly _extHostConfiguration: ExtHostConfiguration;
|
||||
protected readonly _logService: ILogService;
|
||||
protected readonly _extHostTunnelService: IExtHostTunnelService;
|
||||
protected readonly _extHostTerminalService: IExtHostTerminalService;
|
||||
|
||||
protected readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape;
|
||||
protected readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape;
|
||||
@@ -107,7 +109,8 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
|
||||
@ILogService logService: ILogService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
@IExtensionStoragePaths storagePath: IExtensionStoragePaths,
|
||||
@IExtHostTunnelService extHostTunnelService: IExtHostTunnelService
|
||||
@IExtHostTunnelService extHostTunnelService: IExtHostTunnelService,
|
||||
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
|
||||
) {
|
||||
this._hostUtils = hostUtils;
|
||||
this._extHostContext = extHostContext;
|
||||
@@ -117,6 +120,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
|
||||
this._extHostConfiguration = extHostConfiguration;
|
||||
this._logService = logService;
|
||||
this._extHostTunnelService = extHostTunnelService;
|
||||
this._extHostTerminalService = extHostTerminalService;
|
||||
this._disposables = new DisposableStore();
|
||||
|
||||
this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace);
|
||||
@@ -371,7 +375,11 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
|
||||
get storagePath() { return that._storagePath.workspaceValue(extensionDescription); },
|
||||
get globalStoragePath() { return that._storagePath.globalValue(extensionDescription); },
|
||||
asAbsolutePath(relativePath: string) { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
|
||||
get logPath() { return path.join(that._initData.logsLocation.fsPath, extensionDescription.identifier.value); }
|
||||
get logPath() { return path.join(that._initData.logsLocation.fsPath, extensionDescription.identifier.value); },
|
||||
get environmentVariableCollection() {
|
||||
checkProposedApiEnabled(extensionDescription);
|
||||
return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -449,10 +457,11 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
|
||||
const fileNames: string[] = [];
|
||||
const globPatterns: string[] = [];
|
||||
|
||||
const localWithRemote = !this._initData.remote.isRemote && !!this._initData.remote.authority;
|
||||
for (const activationEvent of activationEvents) {
|
||||
if (/^workspaceContains:/.test(activationEvent)) {
|
||||
const fileNameOrGlob = activationEvent.substr('workspaceContains:'.length);
|
||||
if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) {
|
||||
if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0 || localWithRemote) {
|
||||
globPatterns.push(fileNameOrGlob);
|
||||
} else {
|
||||
fileNames.push(fileNameOrGlob);
|
||||
|
||||
@@ -13,7 +13,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
|
||||
import { CellKind, CellOutputKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, MainThreadDocumentsShape, INotebookEditorPropertiesChangeData } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { CellEditType, CellUri, diff, ICellEditOperation, ICellInsertEdit, IErrorOutput, INotebookDisplayOrder, INotebookEditData, IOrderedMimeType, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellsChangedEvent, NotebookCellsSplice2, sortMimeTypes, ICellDeleteEdit, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellEditType, CellUri, diff, ICellEditOperation, ICellInsertEdit, IErrorOutput, INotebookDisplayOrder, INotebookEditData, IOrderedMimeType, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellsChangedEvent, NotebookCellsSplice2, sortMimeTypes, ICellDeleteEdit, notebookDocumentMetadataDefaults, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { Disposable as VSCodeDisposable } from './extHostTypes';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
|
||||
@@ -247,7 +247,18 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
get isDirty() { return false; }
|
||||
|
||||
accpetModelChanged(event: NotebookCellsChangedEvent) {
|
||||
this.$spliceNotebookCells(event.changes);
|
||||
if (event.kind === NotebookCellsChangeType.ModelChange) {
|
||||
this.$spliceNotebookCells(event.changes);
|
||||
} else if (event.kind === NotebookCellsChangeType.Move) {
|
||||
this.$moveCell(event.index, event.newIdx);
|
||||
} else if (event.kind === NotebookCellsChangeType.CellClearOutput) {
|
||||
this.$clearCellOutputs(event.index);
|
||||
} else if (event.kind === NotebookCellsChangeType.CellsClearOutput) {
|
||||
this.$clearAllCellOutputs();
|
||||
} else if (event.kind === NotebookCellsChangeType.ChangeLanguage) {
|
||||
this.$changeCellLanguage(event.index, event.language);
|
||||
}
|
||||
|
||||
this._versionId = event.versionId;
|
||||
}
|
||||
|
||||
@@ -289,6 +300,25 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
});
|
||||
}
|
||||
|
||||
private $moveCell(index: number, newIdx: number) {
|
||||
const cells = this.cells.splice(index, 1);
|
||||
this.cells.splice(newIdx, 0, ...cells);
|
||||
}
|
||||
|
||||
private $clearCellOutputs(index: number) {
|
||||
const cell = this.cells[index];
|
||||
cell.outputs = [];
|
||||
}
|
||||
|
||||
private $clearAllCellOutputs() {
|
||||
this.cells.forEach(cell => cell.outputs = []);
|
||||
}
|
||||
|
||||
private $changeCellLanguage(index: number, language: string) {
|
||||
const cell = this.cells[index];
|
||||
cell.language = language;
|
||||
}
|
||||
|
||||
eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice<vscode.CellOutput>[]) {
|
||||
let renderers = new Set<number>();
|
||||
let outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => {
|
||||
@@ -600,8 +630,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
private readonly _editors = new Map<string, { editor: ExtHostNotebookEditor, onDidReceiveMessage: Emitter<any> }>();
|
||||
private readonly _notebookOutputRenderers = new Map<number, ExtHostNotebookOutputRenderer>();
|
||||
|
||||
private readonly _onDidChangeNotebookDocument = new Emitter<{ document: ExtHostNotebookDocument, changes: NotebookCellsSplice2[] }>();
|
||||
readonly onDidChangeNotebookDocument: Event<{ document: ExtHostNotebookDocument, changes: NotebookCellsSplice2[] }> = this._onDidChangeNotebookDocument.event;
|
||||
private readonly _onDidChangeNotebookDocument = new Emitter<{ document: ExtHostNotebookDocument, changes: NotebookCellsChangedEvent[] }>();
|
||||
readonly onDidChangeNotebookDocument: Event<{ document: ExtHostNotebookDocument, changes: NotebookCellsChangedEvent[] }> = this._onDidChangeNotebookDocument.event;
|
||||
|
||||
private _outputDisplayOrder: INotebookDisplayOrder | undefined;
|
||||
|
||||
@@ -801,7 +831,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
editor.editor.document.accpetModelChanged(event);
|
||||
this._onDidChangeNotebookDocument.fire({
|
||||
document: editor.editor.document,
|
||||
changes: event.changes
|
||||
changes: [event]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -644,36 +644,36 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
|
||||
|
||||
export class EnvironmentVariableCollection implements vscode.EnvironmentVariableCollection {
|
||||
readonly map: Map<string, vscode.EnvironmentVariableMutator> = new Map();
|
||||
private _persistent: boolean = true;
|
||||
|
||||
private _disposed = false;
|
||||
public get persistent(): boolean { return this._persistent; }
|
||||
public set persistent(value: boolean) {
|
||||
this._persistent = value;
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
|
||||
protected readonly _onDidChangeCollection: Emitter<void> = new Emitter<void>();
|
||||
get onDidChangeCollection(): Event<void> { return this._onDidChangeCollection && this._onDidChangeCollection.event; }
|
||||
|
||||
constructor(
|
||||
readonly persistent: boolean,
|
||||
serialized?: ISerializableEnvironmentVariableCollection
|
||||
) {
|
||||
this.map = new Map(serialized);
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
this._checkDisposed();
|
||||
return this.map.size;
|
||||
}
|
||||
|
||||
replace(variable: string, value: string): void {
|
||||
this._checkDisposed();
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Replace });
|
||||
}
|
||||
|
||||
append(variable: string, value: string): void {
|
||||
this._checkDisposed();
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Append });
|
||||
}
|
||||
|
||||
prepend(variable: string, value: string): void {
|
||||
this._checkDisposed();
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Prepend });
|
||||
}
|
||||
|
||||
@@ -686,40 +686,22 @@ export class EnvironmentVariableCollection implements vscode.EnvironmentVariable
|
||||
}
|
||||
|
||||
get(variable: string): vscode.EnvironmentVariableMutator | undefined {
|
||||
this._checkDisposed();
|
||||
return this.map.get(variable);
|
||||
}
|
||||
|
||||
forEach(callback: (variable: string, mutator: vscode.EnvironmentVariableMutator, collection: vscode.EnvironmentVariableCollection) => any, thisArg?: any): void {
|
||||
this._checkDisposed();
|
||||
this.map.forEach((value, key) => callback.call(thisArg, key, value, this));
|
||||
}
|
||||
|
||||
delete(variable: string): void {
|
||||
this._checkDisposed();
|
||||
this.map.delete(variable);
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this._checkDisposed();
|
||||
this.map.clear();
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
if (!this._disposed) {
|
||||
this._disposed = true;
|
||||
this.map.clear();
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
}
|
||||
|
||||
protected _checkDisposed() {
|
||||
if (this._disposed) {
|
||||
throw new Error('EnvironmentVariableCollection has already been disposed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
|
||||
@@ -2652,18 +2652,13 @@ export enum DebugConsoleMode {
|
||||
MergeWithParent = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* VS Code can call the `provideDebugConfigurations` method of a `DebugConfigurationProvider` in two situations (aka 'scopes'):
|
||||
* to provide the initial debug configurations for a newly create launch.json or to provide debug configurations dynamically based on context.
|
||||
* A scope value is used when registering a `DebugConfigurationProvider` with `debug.registerDebugConfigurationProvider`.
|
||||
*/
|
||||
export enum DebugConfigurationProviderScope {
|
||||
export enum DebugConfigurationProviderTriggerKind {
|
||||
/**
|
||||
* The 'initial' scope denotes a context where all debug configurations for a newly created launch.json are needed.
|
||||
* `DebugConfigurationProvider.provideDebugConfigurations` is called to provide the initial debug configurations for a newly created launch.json.
|
||||
*/
|
||||
Initial = 1,
|
||||
/**
|
||||
* The 'dynamic' scope denotes a context where all debug configurations for the current context are needed.
|
||||
* `DebugConfigurationProvider.provideDebugConfigurations` is called to provide dynamically generated debug configurations when the user asks for them through the UI (e.g. via the "Select and Start Debugging" command).
|
||||
*/
|
||||
Dynamic = 2
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
@@ -13,6 +16,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
@@ -265,15 +269,17 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
||||
|
||||
class CustomDocumentStoreEntry {
|
||||
|
||||
private _backupCounter = 1;
|
||||
|
||||
constructor(
|
||||
public readonly document: vscode.CustomDocument,
|
||||
private readonly _storagePath: string,
|
||||
) { }
|
||||
|
||||
private readonly _edits = new Cache<vscode.CustomDocumentEditEvent>('custom documents');
|
||||
|
||||
private _backup?: vscode.CustomDocumentBackup;
|
||||
|
||||
|
||||
addEdit(item: vscode.CustomDocumentEditEvent): number {
|
||||
return this._edits.add([item]);
|
||||
}
|
||||
@@ -298,13 +304,17 @@ class CustomDocumentStoreEntry {
|
||||
}
|
||||
}
|
||||
|
||||
getNewBackupUri(): URI {
|
||||
return joinPath(URI.file(this._storagePath), hashPath(this.document.uri) + (this._backupCounter++));
|
||||
}
|
||||
|
||||
updateBackup(backup: vscode.CustomDocumentBackup): void {
|
||||
this._backup?.dispose();
|
||||
this._backup?.delete();
|
||||
this._backup = backup;
|
||||
}
|
||||
|
||||
disposeBackup(): void {
|
||||
this._backup?.dispose();
|
||||
this._backup?.delete();
|
||||
this._backup = undefined;
|
||||
}
|
||||
|
||||
@@ -324,12 +334,12 @@ class CustomDocumentStore {
|
||||
return this._documents.get(this.key(viewType, resource));
|
||||
}
|
||||
|
||||
public add(viewType: string, document: vscode.CustomDocument): CustomDocumentStoreEntry {
|
||||
public add(viewType: string, document: vscode.CustomDocument, storagePath: string): CustomDocumentStoreEntry {
|
||||
const key = this.key(viewType, document.uri);
|
||||
if (this._documents.has(key)) {
|
||||
throw new Error(`Document already exists for viewType:${viewType} resource:${document.uri}`);
|
||||
}
|
||||
const entry = new CustomDocumentStoreEntry(document);
|
||||
const entry = new CustomDocumentStoreEntry(document, storagePath);
|
||||
this._documents.set(key, entry);
|
||||
return entry;
|
||||
}
|
||||
@@ -357,7 +367,7 @@ type ProviderEntry = {
|
||||
} | {
|
||||
readonly extension: IExtensionDescription;
|
||||
readonly type: WebviewEditorType.Custom;
|
||||
readonly provider: vscode.CustomEditorProvider;
|
||||
readonly provider: vscode.CustomReadonlyEditorProvider;
|
||||
};
|
||||
|
||||
class EditorProviderStore {
|
||||
@@ -367,7 +377,7 @@ class EditorProviderStore {
|
||||
return this.add(WebviewEditorType.Text, viewType, extension, provider);
|
||||
}
|
||||
|
||||
public addCustomProvider(viewType: string, extension: IExtensionDescription, provider: vscode.CustomEditorProvider): vscode.Disposable {
|
||||
public addCustomProvider(viewType: string, extension: IExtensionDescription, provider: vscode.CustomReadonlyEditorProvider): vscode.Disposable {
|
||||
return this.add(WebviewEditorType.Custom, viewType, extension, provider);
|
||||
}
|
||||
|
||||
@@ -375,7 +385,7 @@ class EditorProviderStore {
|
||||
return this._providers.get(viewType);
|
||||
}
|
||||
|
||||
private add(type: WebviewEditorType, viewType: string, extension: IExtensionDescription, provider: vscode.CustomTextEditorProvider | vscode.CustomEditorProvider): vscode.Disposable {
|
||||
private add(type: WebviewEditorType, viewType: string, extension: IExtensionDescription, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider): vscode.Disposable {
|
||||
if (this._providers.has(viewType)) {
|
||||
throw new Error(`Provider for viewType:${viewType} already registered`);
|
||||
}
|
||||
@@ -409,6 +419,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
private readonly _logService: ILogService,
|
||||
private readonly _deprecationService: IExtHostApiDeprecationService,
|
||||
private readonly _extHostDocuments: ExtHostDocuments,
|
||||
private readonly _extensionStoragePaths?: IExtensionStoragePaths,
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews);
|
||||
}
|
||||
@@ -456,8 +467,8 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
public registerCustomEditorProvider(
|
||||
extension: IExtensionDescription,
|
||||
viewType: string,
|
||||
provider: vscode.CustomEditorProvider | vscode.CustomTextEditorProvider,
|
||||
options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerResource?: boolean },
|
||||
provider: vscode.CustomReadonlyEditorProvider | vscode.CustomTextEditorProvider,
|
||||
options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean },
|
||||
): vscode.Disposable {
|
||||
const disposables = new DisposableStore();
|
||||
if ('resolveCustomTextEditor' in provider) {
|
||||
@@ -467,7 +478,20 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
});
|
||||
} else {
|
||||
disposables.add(this._editorProviders.addCustomProvider(viewType, extension, provider));
|
||||
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerResource);
|
||||
|
||||
if (this.supportEditing(provider)) {
|
||||
disposables.add(provider.onDidChangeCustomDocument(e => {
|
||||
const entry = this.getCustomDocumentEntry(viewType, e.document.uri);
|
||||
if (isEditEvent(e)) {
|
||||
const editId = entry.addEdit(e);
|
||||
this._proxy.$onDidEdit(e.document.uri, viewType, editId, e.label);
|
||||
} else {
|
||||
this._proxy.$onContentChange((<any>e).document.uri, viewType); // {{SQL CARBON EDIT}} strict-null-checks
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerDocument);
|
||||
}
|
||||
|
||||
return extHostTypes.Disposable.from(
|
||||
@@ -567,20 +591,11 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
|
||||
const revivedResource = URI.revive(resource);
|
||||
const document = await entry.provider.openCustomDocument(revivedResource, { backupId }, cancellation);
|
||||
const documentEntry = this._documents.add(viewType, document);
|
||||
|
||||
if (this.isEditable(document)) {
|
||||
document.onDidChange(e => {
|
||||
if (isEditEvent(e)) {
|
||||
const editId = documentEntry.addEdit(e);
|
||||
this._proxy.$onDidEdit(document.uri, viewType, editId, e.label);
|
||||
} else {
|
||||
this._proxy.$onContentChange(document.uri, viewType);
|
||||
}
|
||||
});
|
||||
}
|
||||
const storageRoot = this._extensionStoragePaths?.workspaceValue(entry.extension) ?? this._extensionStoragePaths?.globalValue(entry.extension);
|
||||
this._documents.add(viewType, document, storageRoot!);
|
||||
|
||||
return { editable: this.isEditable(document) };
|
||||
return { editable: this.supportEditing(entry.provider) };
|
||||
}
|
||||
|
||||
async $disposeCustomDocument(resource: UriComponents, viewType: string): Promise<void> {
|
||||
@@ -674,29 +689,33 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
|
||||
async $revert(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const document = this.getEditableCustomDocument(viewType, resourceComponents);
|
||||
await document.revert(cancellation);
|
||||
const provider = this.getCustomEditorProvider(viewType);
|
||||
await provider.revertCustomDocument(entry.document, cancellation);
|
||||
entry.disposeBackup();
|
||||
}
|
||||
|
||||
async $onSave(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const document = this.getEditableCustomDocument(viewType, resourceComponents);
|
||||
await document.save(cancellation);
|
||||
const provider = this.getCustomEditorProvider(viewType);
|
||||
await provider.saveCustomDocument(entry.document, cancellation);
|
||||
entry.disposeBackup();
|
||||
}
|
||||
|
||||
async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents, cancellation: CancellationToken): Promise<void> {
|
||||
const document = this.getEditableCustomDocument(viewType, resourceComponents);
|
||||
return document.saveAs(URI.revive(targetResource), cancellation);
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const provider = this.getCustomEditorProvider(viewType);
|
||||
return provider.saveCustomDocumentAs(entry.document, URI.revive(targetResource), cancellation);
|
||||
}
|
||||
|
||||
async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<string> {
|
||||
const entry = this.getCustomDocumentEntry(viewType, resourceComponents);
|
||||
const document = this.getEditableCustomDocument(viewType, resourceComponents);
|
||||
const backup = await document.backup(cancellation);
|
||||
const provider = this.getCustomEditorProvider(viewType);
|
||||
|
||||
const backup = await provider.backupCustomDocument(entry.document, {
|
||||
destination: entry.getNewBackupUri(),
|
||||
}, cancellation);
|
||||
entry.updateBackup(backup);
|
||||
return backup.backupId;
|
||||
return backup.id;
|
||||
}
|
||||
|
||||
private getWebviewPanel(handle: extHostProtocol.WebviewPanelHandle): ExtHostWebviewEditor | undefined {
|
||||
@@ -711,16 +730,19 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
return entry;
|
||||
}
|
||||
|
||||
private isEditable(document: vscode.CustomDocument): document is vscode.EditableCustomDocument {
|
||||
return !!(document as vscode.EditableCustomDocument).onDidChange;
|
||||
}
|
||||
|
||||
private getEditableCustomDocument(viewType: string, resource: UriComponents): vscode.EditableCustomDocument {
|
||||
const { document } = this.getCustomDocumentEntry(viewType, resource);
|
||||
if (!this.isEditable(document)) {
|
||||
private getCustomEditorProvider(viewType: string): vscode.CustomEditorProvider {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
const provider = entry?.provider;
|
||||
if (!provider || !this.supportEditing(provider)) {
|
||||
throw new Error('Custom document is not editable');
|
||||
}
|
||||
return document;
|
||||
return provider;
|
||||
}
|
||||
|
||||
private supportEditing(
|
||||
provider: vscode.CustomTextEditorProvider | vscode.CustomEditorProvider | vscode.CustomReadonlyEditorProvider
|
||||
): provider is vscode.CustomEditorProvider {
|
||||
return !!(provider as vscode.CustomEditorProvider).onDidChangeCustomDocument;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -753,3 +775,8 @@ function isEditEvent(e: vscode.CustomDocumentContentChangeEvent | vscode.CustomD
|
||||
return typeof (e as vscode.CustomDocumentEditEvent).undo === 'function'
|
||||
&& typeof (e as vscode.CustomDocumentEditEvent).redo === 'function';
|
||||
}
|
||||
|
||||
function hashPath(resource: URI): string {
|
||||
const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString();
|
||||
return hash(str) + '';
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
cwd: args.cwd,
|
||||
name: args.title || nls.localize('debug.terminal.title', "debuggee"),
|
||||
};
|
||||
// @ts-ignore
|
||||
delete args.cwd;
|
||||
this._integratedTerminalInstance = this._terminalService.createTerminalFromOptions(options);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/term
|
||||
import { BaseExtHostTerminalService, ExtHostTerminal, EnvironmentVariableCollection } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection';
|
||||
@@ -227,22 +226,12 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
this._isWorkspaceShellAllowed = isAllowed;
|
||||
}
|
||||
|
||||
public getEnvironmentVariableCollection(extension: IExtensionDescription, persistent: boolean = false): vscode.EnvironmentVariableCollection {
|
||||
let collection: EnvironmentVariableCollection | undefined;
|
||||
if (persistent) {
|
||||
// If persistent is specified, return the current collection if it exists
|
||||
collection = this._environmentVariableCollections.get(extension.identifier.value);
|
||||
|
||||
// If persistence changed then create a new collection
|
||||
if (collection && !collection.persistent) {
|
||||
collection = undefined;
|
||||
}
|
||||
}
|
||||
public getEnvironmentVariableCollection(extension: IExtensionDescription): vscode.EnvironmentVariableCollection {
|
||||
let collection = this._environmentVariableCollections.get(extension.identifier.value);
|
||||
|
||||
if (!collection) {
|
||||
// If not persistent, clear out the current collection and create a new one
|
||||
dispose(this._environmentVariableCollections.get(extension.identifier.value));
|
||||
collection = new EnvironmentVariableCollection(persistent);
|
||||
// TODO: Disable dispose
|
||||
collection = new EnvironmentVariableCollection();
|
||||
this._setEnvironmentVariableCollection(extension.identifier.value, collection);
|
||||
}
|
||||
|
||||
@@ -257,7 +246,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
public $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void {
|
||||
collections.forEach(entry => {
|
||||
const extensionIdentifier = entry[0];
|
||||
const collection = new EnvironmentVariableCollection(true, entry[1]);
|
||||
const collection = new EnvironmentVariableCollection(entry[1]);
|
||||
this._setEnvironmentVariableCollection(extensionIdentifier, collection);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -498,6 +498,12 @@ export class ResetViewLocationsAction extends Action {
|
||||
this.viewDescriptorService.moveViewsToContainer([viewDescriptor], defaultContainer);
|
||||
}
|
||||
});
|
||||
|
||||
const defaultContainerLocation = this.viewDescriptorService.getDefaultViewContainerLocation(viewContainer);
|
||||
const currentContainerLocation = this.viewDescriptorService.getViewContainerLocation(viewContainer);
|
||||
if (defaultContainerLocation !== null && currentContainerLocation !== defaultContainerLocation) {
|
||||
this.viewDescriptorService.moveViewContainerToLocation(viewContainer, defaultContainerLocation);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorIsReadonlyContext, EditorAreaVisibleContext, DirtyWorkingCopiesContext, ActiveEditorAvailableEditorsContext } from 'vs/workbench/common/editor';
|
||||
import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorIsReadonlyContext, EditorAreaVisibleContext, DirtyWorkingCopiesContext, ActiveEditorAvailableEditorIdsContext } from 'vs/workbench/common/editor';
|
||||
import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom';
|
||||
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -41,7 +41,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
|
||||
private activeEditorContext: IContextKey<string | null>;
|
||||
private activeEditorIsReadonly: IContextKey<boolean>;
|
||||
private activeEditorAvailableEditors: IContextKey<string>;
|
||||
private activeEditorAvailableEditorIds: IContextKey<string>;
|
||||
|
||||
private activeEditorGroupEmpty: IContextKey<boolean>;
|
||||
private activeEditorGroupIndex: IContextKey<number>;
|
||||
@@ -92,7 +92,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
// Editors
|
||||
this.activeEditorContext = ActiveEditorContext.bindTo(this.contextKeyService);
|
||||
this.activeEditorIsReadonly = ActiveEditorIsReadonlyContext.bindTo(this.contextKeyService);
|
||||
this.activeEditorAvailableEditors = ActiveEditorAvailableEditorsContext.bindTo(this.contextKeyService);
|
||||
this.activeEditorAvailableEditorIds = ActiveEditorAvailableEditorIdsContext.bindTo(this.contextKeyService);
|
||||
this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService);
|
||||
this.textCompareEditorVisibleContext = TextCompareEditorVisibleContext.bindTo(this.contextKeyService);
|
||||
this.textCompareEditorActiveContext = TextCompareEditorActiveContext.bindTo(this.contextKeyService);
|
||||
@@ -210,12 +210,12 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
this.activeEditorContext.set(activeEditorPane.getId());
|
||||
this.activeEditorIsReadonly.set(activeEditorPane.input.isReadonly());
|
||||
|
||||
const editors = this.editorService.getEditorOverrides(activeEditorPane.input, undefined, activeGroup);
|
||||
this.activeEditorAvailableEditors.set(editors.map(([_, entry]) => entry.id).join(','));
|
||||
const editors = activeEditorPane.input.resource ? this.editorService.getEditorOverrides(activeEditorPane.input.resource, undefined, activeGroup) : [];
|
||||
this.activeEditorAvailableEditorIds.set(editors.map(([_, entry]) => entry.id).join(','));
|
||||
} else {
|
||||
this.activeEditorContext.reset();
|
||||
this.activeEditorIsReadonly.reset();
|
||||
this.activeEditorAvailableEditors.reset();
|
||||
this.activeEditorAvailableEditorIds.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { LifecyclePhase, StartupKind, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility } from 'vs/platform/windows/common/windows';
|
||||
import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, IPath } from 'vs/platform/windows/common/windows';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IEditor } from 'vs/editor/common/editorCommon';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
@@ -41,6 +41,8 @@ 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';
|
||||
|
||||
enum Settings {
|
||||
ACTIVITYBAR_VISIBLE = 'workbench.activityBar.visible',
|
||||
@@ -82,6 +84,21 @@ 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 {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
@@ -450,6 +467,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
}
|
||||
|
||||
private initLayoutState(lifecycleService: ILifecycleService, fileService: IFileService): void {
|
||||
this.applyDefaultLayout(this.environmentService, this.storageService);
|
||||
|
||||
// Fullscreen
|
||||
this.state.fullscreen = isFullscreen();
|
||||
@@ -471,7 +489,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
// Only restore last viewlet if window was reloaded or we are in development mode
|
||||
let viewletToRestore: string;
|
||||
if (!this.environmentService.isBuilt || lifecycleService.startupKind === StartupKind.ReloadedWindow) {
|
||||
if (!this.environmentService.isBuilt || lifecycleService.startupKind === StartupKind.ReloadedWindow || isWeb) {
|
||||
viewletToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, this.viewletService.getDefaultViewletId());
|
||||
} else {
|
||||
viewletToRestore = this.viewletService.getDefaultViewletId();
|
||||
@@ -527,18 +545,181 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
}
|
||||
|
||||
private applyDefaultLayout(environmentService: IWorkbenchEnvironmentService, storageService: IStorageService) {
|
||||
const defaultLayout = environmentService.options?.defaultLayout;
|
||||
if (!defaultLayout || !defaultLayout.firstRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
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 && (<any>container).visible !== undefined)) { // {{SQL CARBON EDIT}} strict-null-checks
|
||||
order = container.order ?? (order + 1);
|
||||
const state: SideBarActivityState = {
|
||||
id: viewletId,
|
||||
order: order,
|
||||
pinned: (container.active || (<any>container).visible) ?? true, // {{SQL CARBON EDIT}} strict-null-checks
|
||||
visible: (container.active || (<any>container).visible) ?? true // {{SQL CARBON EDIT}} strict-null-checks
|
||||
};
|
||||
|
||||
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_VIEWLETS, 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 && (<any>container).visible !== undefined)) { // {{SQL CARBON EDIT}} strict-null-checks
|
||||
order = container.order ?? (order + 1);
|
||||
const state: PanelActivityState = {
|
||||
id: panelId,
|
||||
name: name,
|
||||
order: order,
|
||||
pinned: (container.active || (<any>container).visible) ?? true, // {{SQL CARBON EDIT}} strict-null-checks
|
||||
visible: (container.active || (<any>container).visible) ?? true // {{SQL CARBON EDIT}} strict-null-checks
|
||||
};
|
||||
|
||||
panelState.push(state);
|
||||
}
|
||||
}
|
||||
|
||||
if (panelState.length) {
|
||||
storageService.store(PanelPart.PINNED_PANELS, JSON.stringify(panelState), StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private resolveEditorsToOpen(fileService: IFileService): Promise<IResourceEditorInputType[]> | IResourceEditorInputType[] {
|
||||
const configuration = this.environmentService.configuration;
|
||||
const hasInitialFilesToOpen = this.hasInitialFilesToOpen();
|
||||
const initialFilesToOpen = this.getInitialFilesToOpen();
|
||||
|
||||
// Only restore editors if we are not instructed to open files initially
|
||||
this.state.editor.restoreEditors = !hasInitialFilesToOpen;
|
||||
this.state.editor.restoreEditors = initialFilesToOpen === undefined;
|
||||
|
||||
// Files to open, diff or create
|
||||
if (hasInitialFilesToOpen) {
|
||||
if (initialFilesToOpen !== undefined) {
|
||||
|
||||
// Files to diff is exclusive
|
||||
return pathsToEditors(configuration.filesToDiff, fileService).then(filesToDiff => {
|
||||
return pathsToEditors(initialFilesToOpen.filesToDiff, fileService).then(filesToDiff => {
|
||||
if (filesToDiff?.length === 2) {
|
||||
return [{
|
||||
leftResource: filesToDiff[0].resource,
|
||||
@@ -549,7 +730,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
}
|
||||
|
||||
// Otherwise: Open/Create files
|
||||
return pathsToEditors(configuration.filesToOpenOrCreate, fileService);
|
||||
return pathsToEditors(initialFilesToOpen.filesToOpenOrCreate, fileService);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -571,13 +752,26 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
return [];
|
||||
}
|
||||
|
||||
private hasInitialFilesToOpen(): boolean {
|
||||
const configuration = this.environmentService.configuration;
|
||||
private getInitialFilesToOpen(): { filesToOpenOrCreate?: IPath[], filesToDiff?: IPath[] } | undefined {
|
||||
const defaultLayout = this.environmentService.options?.defaultLayout;
|
||||
if (defaultLayout?.firstRun && defaultLayout?.editors?.length) {
|
||||
//
|
||||
return {
|
||||
filesToOpenOrCreate: defaultLayout.editors
|
||||
.sort((a, b) => (a.active ? -1 : 1) - (b.active ? -1 : 1))
|
||||
.map(f => ({ fileUri: URI.file(f.path).with({ scheme: f.scheme }), inactive: !f.active }))
|
||||
};
|
||||
}
|
||||
|
||||
return !!(
|
||||
(configuration.filesToOpenOrCreate && configuration.filesToOpenOrCreate.length > 0) ||
|
||||
(configuration.filesToDiff && configuration.filesToDiff.length > 0)
|
||||
);
|
||||
const configuration = this.environmentService.configuration;
|
||||
if (configuration.filesToOpenOrCreate || configuration.filesToDiff) {
|
||||
return {
|
||||
filesToOpenOrCreate: configuration.filesToOpenOrCreate,
|
||||
filesToDiff: configuration.filesToDiff
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private updatePanelPosition() {
|
||||
|
||||
@@ -59,8 +59,8 @@ export class PaneComposite extends Composite implements IPaneComposite {
|
||||
return this.viewPaneContainer.getOptimalWidth();
|
||||
}
|
||||
|
||||
openView(id: string, focus?: boolean): IView {
|
||||
return this.viewPaneContainer.openView(id, focus);
|
||||
openView<T extends IView>(id: string, focus?: boolean): T | undefined {
|
||||
return this.viewPaneContainer.openView(id, focus) as T;
|
||||
}
|
||||
|
||||
getViewPaneContainer(): ViewPaneContainer {
|
||||
|
||||
@@ -73,7 +73,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private static readonly ACTION_HEIGHT = 48;
|
||||
private static readonly PINNED_VIEWLETS = 'workbench.activity.pinnedViewlets2';
|
||||
static readonly PINNED_VIEWLETS = 'workbench.activity.pinnedViewlets2';
|
||||
private static readonly PLACEHOLDER_VIEWLETS = 'workbench.activity.placeholderViewlets';
|
||||
|
||||
//#region IView
|
||||
@@ -87,6 +87,9 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
|
||||
private content: HTMLElement | undefined;
|
||||
|
||||
private homeBar: ActionBar | undefined;
|
||||
private homeBarContainer: HTMLElement | undefined;
|
||||
|
||||
private menuBar: CustomMenubarControl | undefined;
|
||||
private menuBarContainer: HTMLElement | undefined;
|
||||
|
||||
@@ -155,7 +158,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
(id: string, focus?: boolean) => this.viewletService.openViewlet(id, focus),
|
||||
(from: string, to: string, before?: Before2D) => this.compositeBar.move(from, to, before?.verticallyBefore)
|
||||
),
|
||||
compositeSize: 50,
|
||||
compositeSize: 52,
|
||||
colors: (theme: IColorTheme) => this.getActivitybarItemColors(theme),
|
||||
overflowActionSize: ActivitybarPart.ACTION_HEIGHT
|
||||
}));
|
||||
@@ -353,20 +356,20 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
}
|
||||
|
||||
private createHomeBar(command: string, title: string, icon: Codicon): void {
|
||||
const homeBarContainer = document.createElement('div');
|
||||
homeBarContainer.setAttribute('aria-label', nls.localize('homeIndicator', "Home"));
|
||||
homeBarContainer.setAttribute('role', 'toolbar');
|
||||
addClass(homeBarContainer, 'home-bar');
|
||||
this.homeBarContainer = document.createElement('div');
|
||||
this.homeBarContainer.setAttribute('aria-label', nls.localize('homeIndicator', "Home"));
|
||||
this.homeBarContainer.setAttribute('role', 'toolbar');
|
||||
addClass(this.homeBarContainer, 'home-bar');
|
||||
|
||||
const homeActionBar = this._register(new ActionBar(homeBarContainer, {
|
||||
this.homeBar = this._register(new ActionBar(this.homeBarContainer, {
|
||||
orientation: ActionsOrientation.VERTICAL,
|
||||
animated: false
|
||||
}));
|
||||
|
||||
homeActionBar.push(this._register(this.instantiationService.createInstance(HomeAction, command, title, icon)), { icon: true, label: false });
|
||||
this.homeBar.push(this._register(this.instantiationService.createInstance(HomeAction, command, title, icon)), { icon: true, label: false });
|
||||
|
||||
const content = assertIsDefined(this.content);
|
||||
content.prepend(homeBarContainer);
|
||||
content.prepend(this.homeBarContainer);
|
||||
}
|
||||
|
||||
updateStyles(): void {
|
||||
@@ -582,12 +585,15 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
|
||||
// Layout composite bar
|
||||
let availableHeight = contentAreaSize.height;
|
||||
if (this.globalActivityActionBar) {
|
||||
availableHeight -= (this.globalActivityActionBar.viewItems.length * ActivitybarPart.ACTION_HEIGHT); // adjust height for global actions showing
|
||||
if (this.homeBarContainer) {
|
||||
availableHeight -= this.homeBarContainer.clientHeight;
|
||||
}
|
||||
if (this.menuBarContainer) {
|
||||
availableHeight -= this.menuBarContainer.clientHeight;
|
||||
}
|
||||
if (this.globalActivityActionBar) {
|
||||
availableHeight -= (this.globalActivityActionBar.viewItems.length * ActivitybarPart.ACTION_HEIGHT); // adjust height for global actions showing
|
||||
}
|
||||
this.compositeBar.layout(new Dimension(width, availableHeight));
|
||||
}
|
||||
|
||||
@@ -640,10 +646,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
for (const { when } of viewContainerModel.allViewDescriptors) {
|
||||
views.push({ when: when ? when.serialize() : undefined });
|
||||
}
|
||||
const cacheIcon = URI.isUri(viewContainerModel.icon) ? viewContainerModel.icon.scheme === Schemas.file : true;
|
||||
state.push({
|
||||
id: compositeItem.id,
|
||||
name: viewContainerModel.title,
|
||||
icon: URI.isUri(viewContainerModel.icon) && viewContainerModel.icon.scheme === Schemas.file ? viewContainerModel.icon : viewContainerModel.icon,
|
||||
icon: cacheIcon ? viewContainerModel.icon : undefined,
|
||||
views,
|
||||
pinned: compositeItem.pinned,
|
||||
order: compositeItem.order,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
.monaco-workbench .part.activitybar {
|
||||
width: 48px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content {
|
||||
@@ -17,7 +18,7 @@
|
||||
/*
|
||||
* Fix menu jumping and background inheritance in Safari
|
||||
*/
|
||||
.monaco-workbench .activitybar > .content {
|
||||
.monaco-workbench.safari .activitybar > .content {
|
||||
position: absolute;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
@@ -59,19 +59,10 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
|
||||
return;
|
||||
}
|
||||
|
||||
this.viewDescriptorService.moveViewToLocation(viewsToMove[0], this.targetContainerLocation);
|
||||
const newContainer = this.viewDescriptorService.getViewContainerById(viewsToMove[0].id)!;
|
||||
this.moveComposite(newContainer.id, targetCompositeId, before);
|
||||
this.viewDescriptorService.moveViewContainerToLocation(currentContainer, this.targetContainerLocation);
|
||||
this.moveComposite(currentContainer.id, targetCompositeId, before);
|
||||
|
||||
if (viewsToMove.length > 1) {
|
||||
this.viewDescriptorService.moveViewsToContainer(viewsToMove.slice(1), newContainer);
|
||||
}
|
||||
|
||||
this.openComposite(newContainer.id, true).then(composite => {
|
||||
if (composite && viewsToMove.length === 1) {
|
||||
composite.openView(viewsToMove[0].id, true);
|
||||
}
|
||||
});
|
||||
this.openComposite(currentContainer.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,6 +231,9 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
onDragLeave: (e: IDraggedCompositeData) => {
|
||||
toggleClass(parent, 'dragged-over', false);
|
||||
},
|
||||
onDragEnd: (e: IDraggedCompositeData) => {
|
||||
toggleClass(parent, 'dragged-over', false);
|
||||
},
|
||||
onDrop: (e: IDraggedCompositeData) => {
|
||||
const pinnedItems = this.getPinnedComposites();
|
||||
this.options.dndHandler.drop(e.dragAndDropData, pinnedItems[pinnedItems.length - 1].id, e.eventData, { horizontallyBefore: false, verticallyBefore: false });
|
||||
|
||||
@@ -17,7 +17,7 @@ import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { DelayedDragHandler } from 'vs/base/browser/dnd';
|
||||
import { IActivity } from 'vs/workbench/common/activity';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { CompositeDragAndDropObserver, ICompositeDragAndDrop, Before2D } from 'vs/workbench/browser/dnd';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
@@ -477,32 +477,34 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
|
||||
CompositeActionViewItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction);
|
||||
}
|
||||
|
||||
this._register(compositeActivityAction.onDidChangeActivity(() => { this.compositeActivity = undefined; this.updateActivity(); }, this));
|
||||
this._register(compositeActivityAction.onDidChangeActivity(() => {
|
||||
this.compositeActivity = undefined;
|
||||
this.updateActivity();
|
||||
}, this));
|
||||
this._register(Event.any(
|
||||
compositeActivityAction.onDidChangeActivity,
|
||||
Event.filter(keybindingService.onDidUpdateKeybindings, () => this.compositeActivity!.name !== this.getActivtyName())
|
||||
)(() => {
|
||||
if (this.compositeActivity && this.compositeActivity.name !== this.getActivtyName()) {
|
||||
this.compositeActivity = undefined;
|
||||
this.updateActivity();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
protected get activity(): IActivity {
|
||||
if (!this.compositeActivity) {
|
||||
let activityName: string;
|
||||
const keybinding = typeof this.compositeActivityAction.activity.keybindingId === 'string' ? this.getKeybindingLabel(this.compositeActivityAction.activity.keybindingId) : null;
|
||||
if (keybinding) {
|
||||
activityName = nls.localize('titleKeybinding', "{0} ({1})", this.compositeActivityAction.activity.name, keybinding);
|
||||
} else {
|
||||
activityName = this.compositeActivityAction.activity.name;
|
||||
}
|
||||
|
||||
this.compositeActivity = { ...this.compositeActivityAction.activity, ... { name: activityName } };
|
||||
this.compositeActivity = {
|
||||
...this.compositeActivityAction.activity,
|
||||
... { name: this.getActivtyName() }
|
||||
};
|
||||
}
|
||||
|
||||
return this.compositeActivity;
|
||||
}
|
||||
|
||||
private getKeybindingLabel(id: string): string | null {
|
||||
const kb = this.keybindingService.lookupKeybinding(id);
|
||||
if (kb) {
|
||||
return kb.getLabel();
|
||||
}
|
||||
|
||||
return null;
|
||||
private getActivtyName(): string {
|
||||
const keybinding = this.compositeActivityAction.activity.keybindingId ? this.keybindingService.lookupKeybinding(this.compositeActivityAction.activity.keybindingId) : null;
|
||||
return keybinding ? nls.localize('titleKeybinding', "{0} ({1})", this.compositeActivityAction.activity.name, keybinding.getLabel()) : this.compositeActivityAction.activity.name;
|
||||
}
|
||||
|
||||
render(container: HTMLElement): void {
|
||||
|
||||
@@ -24,7 +24,6 @@ import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platf
|
||||
import { ResourceLabels, IResourceLabel, DEFAULT_LABELS_CONTAINER } from 'vs/workbench/browser/labels';
|
||||
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
|
||||
import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel';
|
||||
|
||||
import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, ITreeSorter } from 'vs/base/browser/ui/tree/tree';
|
||||
import { OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItemComparator, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineDataSource, OutlineSortOrder, OutlineFilter, OutlineAccessibilityProvider } from 'vs/editor/contrib/documentSymbols/outlineTree';
|
||||
import { IIdentityProvider, IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list';
|
||||
|
||||
@@ -52,9 +52,7 @@ export function getEditorPartOptions(config: IWorkbenchEditorConfiguration): IEd
|
||||
return options;
|
||||
}
|
||||
|
||||
if (typeof config.workbench.iconTheme === 'string') {
|
||||
options.iconTheme = config.workbench.iconTheme;
|
||||
}
|
||||
options.iconTheme = config.workbench.iconTheme;
|
||||
|
||||
if (config.workbench.editor) {
|
||||
Object.assign(options, config.workbench.editor);
|
||||
|
||||
@@ -21,6 +21,7 @@ import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/wo
|
||||
import { ItemActivation, IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { AllEditorsByMostRecentlyUsedQuickAccess, ActiveGroupEditorsByMostRecentlyUsedQuickAccess, AllEditorsByAppearanceQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
|
||||
|
||||
export class ExecuteCommandAction extends Action {
|
||||
|
||||
@@ -522,7 +523,8 @@ export abstract class BaseCloseAllAction extends Action {
|
||||
private workingCopyService: IWorkingCopyService,
|
||||
private fileDialogService: IFileDialogService,
|
||||
protected editorGroupService: IEditorGroupsService,
|
||||
private editorService: IEditorService
|
||||
private editorService: IEditorService,
|
||||
private filesConfigurationService: IFilesConfigurationService
|
||||
) {
|
||||
super(id, label, clazz);
|
||||
}
|
||||
@@ -562,31 +564,51 @@ export abstract class BaseCloseAllAction extends Action {
|
||||
}));
|
||||
|
||||
const dirtyEditorsToConfirm = new Set<string>();
|
||||
const dirtyEditorsToAutoSave = new Set<IEditorInput>();
|
||||
|
||||
for (const editor of this.editorService.editors) {
|
||||
if (!editor.isDirty() || editor.isSaving()) {
|
||||
continue; // only interested in dirty editors (unless in the process of saving)
|
||||
}
|
||||
|
||||
let name: string;
|
||||
if (editor instanceof SideBySideEditorInput) {
|
||||
name = editor.master.getName(); // prefer shorter names by using master's name in this case
|
||||
} else {
|
||||
name = editor.getName();
|
||||
// Auto-save on focus change: assume to Save unless the editor is untitled
|
||||
// because bringing up a dialog would save in this case anyway.
|
||||
if (this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.ON_FOCUS_CHANGE && !editor.isUntitled()) {
|
||||
dirtyEditorsToAutoSave.add(editor);
|
||||
}
|
||||
|
||||
dirtyEditorsToConfirm.add(name);
|
||||
// No auto-save on focus change: ask user
|
||||
else {
|
||||
let name: string;
|
||||
if (editor instanceof SideBySideEditorInput) {
|
||||
name = editor.master.getName(); // prefer shorter names by using master's name in this case
|
||||
} else {
|
||||
name = editor.getName();
|
||||
}
|
||||
|
||||
dirtyEditorsToConfirm.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
const confirm = await this.fileDialogService.showSaveConfirm(Array.from(dirtyEditorsToConfirm.values()));
|
||||
if (confirm === ConfirmResult.CANCEL) {
|
||||
let confirmation: ConfirmResult;
|
||||
let saveReason = SaveReason.EXPLICIT;
|
||||
if (dirtyEditorsToConfirm.size > 0) {
|
||||
confirmation = await this.fileDialogService.showSaveConfirm(Array.from(dirtyEditorsToConfirm.values()));
|
||||
} else if (dirtyEditorsToAutoSave.size > 0) {
|
||||
confirmation = ConfirmResult.SAVE;
|
||||
saveReason = SaveReason.FOCUS_CHANGE;
|
||||
} else {
|
||||
confirmation = ConfirmResult.DONT_SAVE;
|
||||
}
|
||||
|
||||
if (confirmation === ConfirmResult.CANCEL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (confirm === ConfirmResult.DONT_SAVE) {
|
||||
if (confirmation === ConfirmResult.DONT_SAVE) {
|
||||
await this.editorService.revertAll({ soft: true, includeUntitled: true });
|
||||
} else {
|
||||
await this.editorService.saveAll({ reason: SaveReason.EXPLICIT, includeUntitled: true });
|
||||
await this.editorService.saveAll({ reason: saveReason, includeUntitled: true });
|
||||
}
|
||||
|
||||
if (!this.workingCopyService.hasDirty) {
|
||||
@@ -608,9 +630,10 @@ export class CloseAllEditorsAction extends BaseCloseAllAction {
|
||||
@IWorkingCopyService workingCopyService: IWorkingCopyService,
|
||||
@IFileDialogService fileDialogService: IFileDialogService,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@IEditorService editorService: IEditorService
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IFilesConfigurationService filesConfigurationService: IFilesConfigurationService
|
||||
) {
|
||||
super(id, label, Codicon.closeAll.classNames, workingCopyService, fileDialogService, editorGroupService, editorService);
|
||||
super(id, label, Codicon.closeAll.classNames, workingCopyService, fileDialogService, editorGroupService, editorService, filesConfigurationService);
|
||||
}
|
||||
|
||||
protected async doCloseAll(): Promise<void> {
|
||||
@@ -629,9 +652,10 @@ export class CloseAllEditorGroupsAction extends BaseCloseAllAction {
|
||||
@IWorkingCopyService workingCopyService: IWorkingCopyService,
|
||||
@IFileDialogService fileDialogService: IFileDialogService,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@IEditorService editorService: IEditorService
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IFilesConfigurationService filesConfigurationService: IFilesConfigurationService
|
||||
) {
|
||||
super(id, label, undefined, workingCopyService, fileDialogService, editorGroupService, editorService);
|
||||
super(id, label, undefined, workingCopyService, fileDialogService, editorGroupService, editorService, filesConfigurationService);
|
||||
}
|
||||
|
||||
protected async doCloseAll(): Promise<void> {
|
||||
|
||||
@@ -52,6 +52,7 @@ import { EditorActivation, EditorOpenContext } from 'vs/platform/editor/common/e
|
||||
import { IDialogService, IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
|
||||
|
||||
export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
@@ -134,7 +135,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService,
|
||||
@IFileDialogService private readonly fileDialogService: IFileDialogService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IEditorService private readonly editorService: EditorServiceImpl
|
||||
@IEditorService private readonly editorService: EditorServiceImpl,
|
||||
@IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
@@ -1306,30 +1308,43 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
return false; // editor is still editable somewhere else
|
||||
}
|
||||
|
||||
// Switch to editor that we want to handle and confirm to save/revert
|
||||
await this.openEditor(editor);
|
||||
|
||||
let name: string;
|
||||
if (editor instanceof SideBySideEditorInput) {
|
||||
name = editor.master.getName(); // prefer shorter names by using master's name in this case
|
||||
} else {
|
||||
name = editor.getName();
|
||||
// Auto-save on focus change: assume to Save unless the editor is untitled
|
||||
// because bringing up a dialog would save in this case anyway.
|
||||
let confirmation: ConfirmResult;
|
||||
let saveReason = SaveReason.EXPLICIT;
|
||||
if (this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.ON_FOCUS_CHANGE && !editor.isUntitled()) {
|
||||
confirmation = ConfirmResult.SAVE;
|
||||
saveReason = SaveReason.FOCUS_CHANGE;
|
||||
}
|
||||
|
||||
const res = await this.fileDialogService.showSaveConfirm([name]);
|
||||
// No auto-save on focus change: ask user
|
||||
else {
|
||||
|
||||
// Switch to editor that we want to handle and confirm to save/revert
|
||||
await this.openEditor(editor);
|
||||
|
||||
let name: string;
|
||||
if (editor instanceof SideBySideEditorInput) {
|
||||
name = editor.master.getName(); // prefer shorter names by using master's name in this case
|
||||
} else {
|
||||
name = editor.getName();
|
||||
}
|
||||
|
||||
confirmation = await this.fileDialogService.showSaveConfirm([name]);
|
||||
}
|
||||
|
||||
// It could be that the editor saved meanwhile or is saving, so we check
|
||||
// again to see if anything needs to happen before closing for good.
|
||||
// This can happen for example if autoSave: onFocusChange is configured
|
||||
// so that the save happens when the dialog opens.
|
||||
if (!editor.isDirty() || editor.isSaving()) {
|
||||
return res === ConfirmResult.CANCEL ? true : false;
|
||||
return confirmation === ConfirmResult.CANCEL ? true : false;
|
||||
}
|
||||
|
||||
// Otherwise, handle accordingly
|
||||
switch (res) {
|
||||
switch (confirmation) {
|
||||
case ConfirmResult.SAVE:
|
||||
await editor.save(this.id, { reason: SaveReason.EXPLICIT });
|
||||
await editor.save(this.id, { reason: saveReason });
|
||||
|
||||
return editor.isDirty(); // veto if still dirty
|
||||
case ConfirmResult.DONT_SAVE:
|
||||
|
||||
@@ -444,7 +444,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
this.screenRedearModeElement.value = this.statusbarService.addEntry({
|
||||
text,
|
||||
ariaLabel: text,
|
||||
tooltip: nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\"."),
|
||||
command: 'showEditorScreenReaderNotification',
|
||||
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND),
|
||||
color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND)
|
||||
|
||||
@@ -334,23 +334,18 @@ export class TabsTitleControl extends TitleControl {
|
||||
}
|
||||
|
||||
// Shift-key enables or disables this behaviour depending on the setting
|
||||
if (this.accessor.partOptions.scrollToSwitchTabs === 'off') {
|
||||
if (!e.shiftKey) {
|
||||
return; // 'off': only enable this when Shift-key is pressed
|
||||
}
|
||||
} else {
|
||||
if (this.accessor.partOptions.scrollToSwitchTabs === true) {
|
||||
if (e.shiftKey) {
|
||||
return; // 'on': only enable this when Shift-key is not pressed
|
||||
}
|
||||
} else {
|
||||
if (!e.shiftKey) {
|
||||
return; // 'off': only enable this when Shift-key is pressed
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out scrolling direction
|
||||
let scrollingUp = e.deltaX < 0 || e.deltaY < 0;
|
||||
if (this.accessor.partOptions.scrollToSwitchTabs === 'reverse') {
|
||||
scrollingUp = !scrollingUp;
|
||||
}
|
||||
|
||||
const nextEditor = this.group.getEditorByIndex(this.group.getIndexOfEditor(activeEditor) + (scrollingUp ? -1 : 1));
|
||||
const nextEditor = this.group.getEditorByIndex(this.group.getIndexOfEditor(activeEditor) + (e.deltaX < 0 || e.deltaY < 0 /* scrolling up */ ? -1 : 1));
|
||||
if (!nextEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
|
||||
static readonly activePanelSettingsKey = 'workbench.panelpart.activepanelid';
|
||||
|
||||
private static readonly PINNED_PANELS = 'workbench.panel.pinnedPanels';
|
||||
static readonly PINNED_PANELS = 'workbench.panel.pinnedPanels';
|
||||
private static readonly MIN_COMPOSITE_BAR_WIDTH = 50;
|
||||
|
||||
_serviceBrand: undefined;
|
||||
@@ -222,14 +222,27 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
}
|
||||
}
|
||||
|
||||
private onDidDeregisterPanel(panelId: string): void {
|
||||
private async onDidDeregisterPanel(panelId: string): Promise<void> {
|
||||
const disposable = this.panelDisposables.get(panelId);
|
||||
if (disposable) {
|
||||
disposable.dispose();
|
||||
}
|
||||
|
||||
this.panelDisposables.delete(panelId);
|
||||
this.hideComposite(panelId);
|
||||
|
||||
const activeContainers = this.viewDescriptorService.getViewContainersByLocation(ViewContainerLocation.Panel)
|
||||
.filter(container => this.viewDescriptorService.getViewContainerModel(container).activeViewDescriptors.length > 0);
|
||||
|
||||
if (activeContainers.length) {
|
||||
if (this.getActivePanel()?.getId() === panelId) {
|
||||
const defaultPanelId = this.panelRegistry.getDefaultPanelId();
|
||||
const containerToOpen = activeContainers.filter(c => c.id === defaultPanelId)[0] || activeContainers[0];
|
||||
await this.openPanel(containerToOpen.id);
|
||||
}
|
||||
} else {
|
||||
this.layoutService.setPanelHidden(true);
|
||||
}
|
||||
|
||||
this.removeComposite(panelId);
|
||||
}
|
||||
|
||||
private updateActivity(viewContainer: ViewContainer, viewContainerModel: IViewContainerModel): void {
|
||||
@@ -287,6 +300,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
this.compositeBar.onDidChange(() => this.saveCachedPanels(), this, disposables);
|
||||
this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e), this, disposables);
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
private onDidRegisterExtensions(): void {
|
||||
@@ -537,6 +551,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
|
||||
protected removeComposite(compositeId: string): boolean {
|
||||
if (super.removeComposite(compositeId)) {
|
||||
this.compositeBar.removeComposite(compositeId);
|
||||
const compositeActions = this.compositeActions.get(compositeId);
|
||||
if (compositeActions) {
|
||||
compositeActions.activityAction.dispose();
|
||||
|
||||
@@ -34,6 +34,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { LayoutPriority } from 'vs/base/browser/ui/grid/grid';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { CompositeDragAndDropObserver } from 'vs/workbench/browser/dnd';
|
||||
import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
|
||||
export class SidebarPart extends CompositePart<Viewlet> implements IViewletService {
|
||||
|
||||
@@ -93,6 +94,7 @@ export class SidebarPart extends CompositePart<Viewlet> implements IViewletServi
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IExtensionService private readonly extensionService: IExtensionService
|
||||
) {
|
||||
@@ -134,9 +136,18 @@ export class SidebarPart extends CompositePart<Viewlet> implements IViewletServi
|
||||
|
||||
// Viewlet deregister
|
||||
this._register(this.registry.onDidDeregister(async (viewletDescriptor: ViewletDescriptor) => {
|
||||
const activeViewlet = this.getActiveViewlet();
|
||||
if (!activeViewlet || activeViewlet.getId() === viewletDescriptor.id) {
|
||||
await this.openViewlet(this.getDefaultViewletId());
|
||||
|
||||
const activeContainers = this.viewDescriptorService.getViewContainersByLocation(ViewContainerLocation.Sidebar)
|
||||
.filter(container => this.viewDescriptorService.getViewContainerModel(container).activeViewDescriptors.length > 0);
|
||||
|
||||
if (activeContainers.length) {
|
||||
if (this.getActiveComposite()?.getId() === viewletDescriptor.id) {
|
||||
const defaultViewletId = this.getDefaultViewletId();
|
||||
const containerToOpen = activeContainers.filter(c => c.id === defaultViewletId)[0] || activeContainers[0];
|
||||
await this.openViewlet(containerToOpen.id);
|
||||
}
|
||||
} else {
|
||||
this.layoutService.setSideBarHidden(true);
|
||||
}
|
||||
|
||||
this.removeComposite(viewletDescriptor.id);
|
||||
@@ -165,12 +176,7 @@ export class SidebarPart extends CompositePart<Viewlet> implements IViewletServi
|
||||
|
||||
const draggedItemProvider = (): { type: 'view' | 'composite', id: string } => {
|
||||
const activeViewlet = this.getActiveViewlet()!;
|
||||
const visibleViews = activeViewlet.getViewPaneContainer().views.filter(v => v.isVisible());
|
||||
if (visibleViews.length === 1) {
|
||||
return { type: 'view', id: visibleViews[0].id };
|
||||
} else {
|
||||
return { type: 'composite', id: activeViewlet.getId() };
|
||||
}
|
||||
return { type: 'composite', id: activeViewlet.getId() };
|
||||
};
|
||||
|
||||
this._register(CompositeDragAndDropObserver.INSTANCE.registerDraggable(this.titleLabelElement!, draggedItemProvider, {}));
|
||||
|
||||
@@ -695,6 +695,8 @@ class StatusbarEntryItem extends Disposable {
|
||||
}
|
||||
|
||||
if (!this.entry || entry.ariaLabel !== this.entry.ariaLabel) {
|
||||
// Set the aria label on both elements so screen readers would read the correct thing without duplication #96210
|
||||
this.container.setAttribute('aria-label', entry.ariaLabel);
|
||||
this.labelContainer.setAttribute('aria-label', entry.ariaLabel);
|
||||
}
|
||||
|
||||
@@ -703,7 +705,7 @@ class StatusbarEntryItem extends Disposable {
|
||||
if (entry.tooltip) {
|
||||
this.container.title = entry.tooltip;
|
||||
} else {
|
||||
delete this.container.title;
|
||||
this.container.title = '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,6 @@
|
||||
max-width: 260px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.monaco-workbench .pane > .pane-body .welcome-view-content {
|
||||
|
||||
@@ -458,7 +458,7 @@ export abstract class ViewPane extends Pane implements IView {
|
||||
|
||||
if (linkedText.nodes.length === 1 && typeof linkedText.nodes[0] !== 'string') {
|
||||
const node = linkedText.nodes[0];
|
||||
const button = new Button(this.viewWelcomeContainer, { title: node.title });
|
||||
const button = new Button(this.viewWelcomeContainer, { title: node.title, supportCodicons: true });
|
||||
button.label = node.label;
|
||||
button.onDidClick(_ => {
|
||||
this.telemetryService.publicLog2<{ viewId: string, uri: string }, WelcomeActionClassification>('views.welcomeAction', { viewId: this.id, uri: node.href });
|
||||
@@ -878,8 +878,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
}));
|
||||
|
||||
this._register(this.onDidSashChange(() => this.saveViewSizes()));
|
||||
this.viewContainerModel.onDidAddVisibleViewDescriptors(added => this.onDidAddViewDescriptors(added));
|
||||
this.viewContainerModel.onDidRemoveVisibleViewDescriptors(removed => this.onDidRemoveViewDescriptors(removed));
|
||||
this._register(this.viewContainerModel.onDidAddVisibleViewDescriptors(added => this.onDidAddViewDescriptors(added)));
|
||||
this._register(this.viewContainerModel.onDidRemoveVisibleViewDescriptors(removed => this.onDidRemoveViewDescriptors(removed)));
|
||||
const addedViews: IAddedViewDescriptorRef[] = this.viewContainerModel.visibleViewDescriptors.map((viewDescriptor, index) => {
|
||||
const size = this.viewContainerModel.getSize(viewDescriptor.id);
|
||||
const collapsed = this.viewContainerModel.isCollapsed(viewDescriptor.id);
|
||||
@@ -1140,15 +1140,17 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
});
|
||||
}
|
||||
|
||||
openView(id: string, focus?: boolean): IView {
|
||||
openView(id: string, focus?: boolean): IView | undefined {
|
||||
let view = this.getView(id);
|
||||
if (!view) {
|
||||
this.toggleViewVisibility(id);
|
||||
}
|
||||
view = this.getView(id)!;
|
||||
view.setExpanded(true);
|
||||
if (focus) {
|
||||
view.focus();
|
||||
view = this.getView(id);
|
||||
if (view) {
|
||||
view.setExpanded(true);
|
||||
if (focus) {
|
||||
view.focus();
|
||||
}
|
||||
}
|
||||
return view;
|
||||
}
|
||||
@@ -1199,17 +1201,19 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
panesToRemove.push(this.panes[index]);
|
||||
}
|
||||
this.removePanes(panesToRemove);
|
||||
dispose(panesToRemove);
|
||||
}
|
||||
|
||||
protected toggleViewVisibility(viewId: string): void {
|
||||
const visible = !this.viewContainerModel.isVisible(viewId);
|
||||
type ViewsToggleVisibilityClassification = {
|
||||
viewId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
visible: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
};
|
||||
this.telemetryService.publicLog2<{ viewId: String, visible: boolean }, ViewsToggleVisibilityClassification>('views.toggleVisibility', { viewId, visible });
|
||||
this.viewContainerModel.setVisible(viewId, visible);
|
||||
// Check if view is active
|
||||
if (this.viewContainerModel.activeViewDescriptors.some(viewDescriptor => viewDescriptor.id === viewId)) {
|
||||
const visible = !this.viewContainerModel.isVisible(viewId);
|
||||
type ViewsToggleVisibilityClassification = {
|
||||
viewId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
visible: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
};
|
||||
this.telemetryService.publicLog2<{ viewId: String, visible: boolean }, ViewsToggleVisibilityClassification>('views.toggleVisibility', { viewId, visible });
|
||||
this.viewContainerModel.setVisible(viewId, visible);
|
||||
}
|
||||
}
|
||||
|
||||
private addPane(pane: ViewPane, size: number, index = this.paneItems.length - 1): void {
|
||||
@@ -1234,8 +1238,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
leftBorder: PANEL_BORDER,
|
||||
dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND
|
||||
}, pane);
|
||||
const disposable = combinedDisposable(onDidFocus, onDidChangeTitleArea, paneStyler, onDidChange, onDidChangeVisibility);
|
||||
const paneItem: IViewPaneItem = { pane: pane, disposable };
|
||||
const disposable = combinedDisposable(pane, onDidFocus, onDidChangeTitleArea, paneStyler, onDidChange, onDidChangeVisibility);
|
||||
const paneItem: IViewPaneItem = { pane, disposable };
|
||||
|
||||
this.paneItems.splice(index, 0, paneItem);
|
||||
assertIsDefined(this.paneview).addPane(pane, size, index);
|
||||
|
||||
@@ -39,13 +39,13 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
|
||||
private readonly viewContainersRegistry: IViewContainersRegistry;
|
||||
private readonly viewDisposable: Map<IViewDescriptor, IDisposable>;
|
||||
private readonly viewPaneContainers: Map<string, { viewPaneContainer: ViewPaneContainer, disposable: IDisposable }>;
|
||||
|
||||
private readonly _onDidChangeViewVisibility: Emitter<{ id: string, visible: boolean }> = this._register(new Emitter<{ id: string, visible: boolean }>());
|
||||
readonly onDidChangeViewVisibility: Event<{ id: string, visible: boolean }> = this._onDidChangeViewVisibility.event;
|
||||
|
||||
private readonly visibleViewContextKeys: Map<string, IContextKey<boolean>>;
|
||||
|
||||
private readonly viewPaneContainers: Map<string, ViewPaneContainer>;
|
||||
|
||||
constructor(
|
||||
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
|
||||
@@ -59,7 +59,7 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
this.viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry);
|
||||
this.viewDisposable = new Map<IViewDescriptor, IDisposable>();
|
||||
this.visibleViewContextKeys = new Map<string, IContextKey<boolean>>();
|
||||
this.viewPaneContainers = new Map<string, ViewPaneContainer>();
|
||||
this.viewPaneContainers = new Map<string, { viewPaneContainer: ViewPaneContainer, disposable: IDisposable }>();
|
||||
|
||||
this._register(toDisposable(() => {
|
||||
this.viewDisposable.forEach(disposable => disposable.dispose());
|
||||
@@ -67,17 +67,27 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
}));
|
||||
|
||||
this.viewDescriptorService.getViewContainers().forEach(viewContainer => this.onDidRegisterViewContainer(viewContainer, this.viewDescriptorService.getViewContainerLocation(viewContainer)!));
|
||||
this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer, viewContainerLocation }) => this.onDidRegisterViewContainer(viewContainer, viewContainerLocation)));
|
||||
|
||||
this._register(this.viewContainersRegistry.onDidDeregister(e => this.viewPaneContainers.delete(e.viewContainer.id)));
|
||||
this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer }) => this.onDidRegisterViewContainer(viewContainer, this.viewDescriptorService.getViewContainerLocation(viewContainer)!)));
|
||||
this._register(this.viewContainersRegistry.onDidDeregister(e => this.deregisterViewPaneContainer(e.viewContainer.id)));
|
||||
this._register(this.viewDescriptorService.onDidChangeContainerLocation(({ viewContainer, from, to }) => this.onDidChangeContainerLocation(viewContainer, from, to)));
|
||||
}
|
||||
|
||||
private registerViewPaneContainer(viewPaneContainer: ViewPaneContainer): void {
|
||||
this._register(viewPaneContainer.onDidAddViews(views => this.onViewsAdded(views)));
|
||||
this._register(viewPaneContainer.onDidChangeViewVisibility(view => this.onViewsVisibilityChanged(view, view.isBodyVisible())));
|
||||
this._register(viewPaneContainer.onDidRemoveViews(views => this.onViewsRemoved(views)));
|
||||
const disposable = new DisposableStore();
|
||||
disposable.add(viewPaneContainer);
|
||||
disposable.add(viewPaneContainer.onDidAddViews(views => this.onViewsAdded(views)));
|
||||
disposable.add(viewPaneContainer.onDidChangeViewVisibility(view => this.onViewsVisibilityChanged(view, view.isBodyVisible())));
|
||||
disposable.add(viewPaneContainer.onDidRemoveViews(views => this.onViewsRemoved(views)));
|
||||
|
||||
this.viewPaneContainers.set(viewPaneContainer.getId(), viewPaneContainer);
|
||||
this.viewPaneContainers.set(viewPaneContainer.getId(), { viewPaneContainer, disposable });
|
||||
}
|
||||
|
||||
private deregisterViewPaneContainer(id: string): void {
|
||||
const viewPaneContainerItem = this.viewPaneContainers.get(id);
|
||||
if (viewPaneContainerItem) {
|
||||
viewPaneContainerItem.disposable.dispose();
|
||||
this.viewPaneContainers.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
private onViewsAdded(added: IView[]): void {
|
||||
@@ -107,6 +117,11 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
return contextKey;
|
||||
}
|
||||
|
||||
private onDidChangeContainerLocation(viewContainer: ViewContainer, from: ViewContainerLocation, to: ViewContainerLocation): void {
|
||||
this.deregisterViewletOrPanel(viewContainer, from);
|
||||
this.registerViewletOrPanel(viewContainer, to);
|
||||
}
|
||||
|
||||
private onDidRegisterViewContainer(viewContainer: ViewContainer, viewContainerLocation: ViewContainerLocation): void {
|
||||
this.registerViewletOrPanel(viewContainer, viewContainerLocation);
|
||||
const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer);
|
||||
@@ -134,6 +149,7 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
category: composite ? composite.name : localize('view category', "View"),
|
||||
menu: [{
|
||||
id: MenuId.CommandPalette,
|
||||
when: viewDescriptor.when,
|
||||
}],
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.has(`${viewDescriptor.id}.active`),
|
||||
@@ -228,16 +244,22 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
|
||||
async openView<T extends IView>(id: string, focus: boolean): Promise<T | null> {
|
||||
const viewContainer = this.viewDescriptorService.getViewContainerByViewId(id);
|
||||
if (viewContainer) {
|
||||
const location = this.viewDescriptorService.getViewContainerLocation(viewContainer);
|
||||
const compositeDescriptor = this.getComposite(viewContainer.id, location!);
|
||||
if (compositeDescriptor) {
|
||||
const paneComposite = await this.openComposite(compositeDescriptor.id, location!) as IPaneComposite | undefined;
|
||||
if (paneComposite && paneComposite.openView) {
|
||||
return paneComposite.openView(id, focus) as T;
|
||||
} else if (focus) {
|
||||
paneComposite?.focus();
|
||||
}
|
||||
if (!viewContainer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this.viewDescriptorService.getViewContainerModel(viewContainer).activeViewDescriptors.some(viewDescriptor => viewDescriptor.id === id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const location = this.viewDescriptorService.getViewContainerLocation(viewContainer);
|
||||
const compositeDescriptor = this.getComposite(viewContainer.id, location!);
|
||||
if (compositeDescriptor) {
|
||||
const paneComposite = await this.openComposite(compositeDescriptor.id, location!) as IPaneComposite | undefined;
|
||||
if (paneComposite && paneComposite.openView) {
|
||||
return paneComposite.openView<T>(id, focus) || null;
|
||||
} else if (focus) {
|
||||
paneComposite?.focus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +312,7 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const view = this.viewPaneContainers.get(viewContainer.id)?.getView(id);
|
||||
const view = this.viewPaneContainers.get(viewContainer.id)?.viewPaneContainer?.getView(id);
|
||||
return view?.getProgressIndicator();
|
||||
}
|
||||
|
||||
@@ -307,6 +329,19 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
}
|
||||
}
|
||||
|
||||
private deregisterViewletOrPanel(viewContainer: ViewContainer, viewContainerLocation: ViewContainerLocation): void {
|
||||
switch (viewContainerLocation) {
|
||||
case ViewContainerLocation.Panel:
|
||||
this.deregisterPanel(viewContainer);
|
||||
break;
|
||||
case ViewContainerLocation.Sidebar:
|
||||
if (viewContainer.ctorDescriptor) {
|
||||
this.deregisterViewlet(viewContainer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private registerPanel(viewContainer: ViewContainer): void {
|
||||
const that = this;
|
||||
class PaneContainerPanel extends PaneCompositePanel {
|
||||
@@ -329,12 +364,17 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
PaneContainerPanel,
|
||||
viewContainer.id,
|
||||
viewContainer.name,
|
||||
isString(viewContainer.icon) ? viewContainer.icon : undefined,
|
||||
undefined,
|
||||
viewContainer.order,
|
||||
viewContainer.focusCommand?.id,
|
||||
));
|
||||
}
|
||||
|
||||
private deregisterPanel(viewContainer: ViewContainer): void {
|
||||
this.deregisterViewPaneContainer(viewContainer.id);
|
||||
Registry.as<PanelRegistry>(PanelExtensions.Panels).deregisterPanel(viewContainer.id);
|
||||
}
|
||||
|
||||
private registerViewlet(viewContainer: ViewContainer): void {
|
||||
const that = this;
|
||||
class PaneContainerViewlet extends Viewlet {
|
||||
@@ -364,6 +404,11 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
viewContainer.icon instanceof URI ? viewContainer.icon : undefined
|
||||
));
|
||||
}
|
||||
|
||||
private deregisterViewlet(viewContainer: ViewContainer): void {
|
||||
this.deregisterViewPaneContainer(viewContainer.id);
|
||||
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).deregisterViewlet(viewContainer.id);
|
||||
}
|
||||
}
|
||||
|
||||
export function createFileIconThemableTreeContainerScope(container: HTMLElement, themeService: IThemeService): IDisposable {
|
||||
|
||||
@@ -38,7 +38,7 @@ import { FileUserDataProvider } from 'vs/workbench/services/userData/common/file
|
||||
import { BACKUPS } from 'vs/platform/environment/common/environment';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { BrowserStorageService } from 'vs/platform/storage/browser/storageService';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { getThemeTypeSelector, DARK, HIGH_CONTRAST, LIGHT } from 'vs/platform/theme/common/themeService';
|
||||
import { registerWindowDriver } from 'vs/platform/driver/browser/driver';
|
||||
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
|
||||
@@ -52,23 +52,6 @@ import { coalesce } from 'vs/base/common/arrays';
|
||||
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
|
||||
import { WebResourceIdentityService, IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { firstSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
interface PanelActivityState {
|
||||
id: string;
|
||||
name?: string;
|
||||
pinned: boolean;
|
||||
order: number;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
interface SideBarActivityState {
|
||||
id: string;
|
||||
pinned: boolean;
|
||||
order: number;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
|
||||
class BrowserMain extends Disposable {
|
||||
|
||||
@@ -82,6 +65,11 @@ class BrowserMain extends Disposable {
|
||||
async open(): Promise<IWorkbench> {
|
||||
const services = await this.initServices();
|
||||
|
||||
// const defaultLayout = this.configuration?.defaultLayout;
|
||||
// if (defaultLayout) {
|
||||
// defaultLayout.firstRun = services.storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL) === undefined;
|
||||
// }
|
||||
|
||||
await domContentLoaded();
|
||||
mark('willStartWorkbench');
|
||||
|
||||
@@ -98,8 +86,6 @@ class BrowserMain extends Disposable {
|
||||
// Listeners
|
||||
this.registerListeners(workbench, services.storageService);
|
||||
|
||||
this.applyDefaultLayout(services.storageService);
|
||||
|
||||
// Driver
|
||||
if (this.configuration.driver) {
|
||||
(async () => this._register(await registerWindowDriver()))();
|
||||
@@ -120,176 +106,6 @@ class BrowserMain extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
private applyDefaultLayout(storageService: BrowserStorageService) {
|
||||
const { defaultLayout } = this.configuration;
|
||||
if (!defaultLayout) {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstRun = storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL);
|
||||
if (firstRun !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { sidebar } = defaultLayout;
|
||||
if (sidebar) {
|
||||
if (sidebar.visible !== undefined) {
|
||||
if (sidebar.visible) {
|
||||
storageService.remove('workbench.sidebar.hidden', StorageScope.WORKSPACE);
|
||||
} else {
|
||||
storageService.store('workbench.sidebar.hidden', true, StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
if (sidebar.containers !== undefined) {
|
||||
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}`;
|
||||
}
|
||||
|
||||
order = container.order ?? (order + 1);
|
||||
const state: SideBarActivityState = {
|
||||
id: viewletId,
|
||||
order: order,
|
||||
pinned: true,
|
||||
visible: true
|
||||
};
|
||||
|
||||
if (container.active) {
|
||||
storageService.store('workbench.sidebar.activeviewletid', viewletId, StorageScope.WORKSPACE);
|
||||
} else {
|
||||
if ((<any>container).visible !== undefined) { // {{SQL CARBON EDIT}} strict-null-checks
|
||||
state.pinned = (<any>container).visible; // {{SQL CARBON EDIT}} strict-null-checks
|
||||
state.visible = (<any>container).visible; // {{SQL CARBON EDIT}} strict-null-checks
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
storageService.store('workbench.activity.pinnedViewlets2', JSON.stringify(sidebarState), StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
const { panel } = defaultLayout;
|
||||
if (panel) {
|
||||
if (panel.visible !== undefined) {
|
||||
if (panel.visible) {
|
||||
storageService.store('workbench.panel.hidden', false, StorageScope.WORKSPACE);
|
||||
} else {
|
||||
storageService.remove('workbench.panel.hidden', StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
if (panel.containers !== undefined) {
|
||||
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';
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
order = container.order ?? (order + 1);
|
||||
const state: PanelActivityState = {
|
||||
id: panelId,
|
||||
name: name,
|
||||
order: order,
|
||||
pinned: true,
|
||||
visible: true
|
||||
};
|
||||
|
||||
if (container.active) {
|
||||
storageService.store('workbench.panelpart.activepanelid', panelId, StorageScope.WORKSPACE);
|
||||
} else {
|
||||
if ((<any>container).visible !== undefined) { // {{SQL CARBON EDIT}} strict-null-checks
|
||||
state.pinned = (<any>container).visible; // {{SQL CARBON EDIT}} strict-null-checks
|
||||
state.visible = (<any>container).visible; // {{SQL CARBON EDIT}} strict-null-checks
|
||||
}
|
||||
}
|
||||
|
||||
panelState.push(state);
|
||||
}
|
||||
|
||||
storageService.store('workbench.panel.pinnedPanels', JSON.stringify(panelState), StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private registerListeners(workbench: Workbench, storageService: BrowserStorageService): void {
|
||||
|
||||
// Layout
|
||||
|
||||
@@ -33,15 +33,9 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio
|
||||
'default': true
|
||||
},
|
||||
'workbench.editor.scrollToSwitchTabs': {
|
||||
'type': 'string',
|
||||
'enum': ['off', 'natural', 'reverse'],
|
||||
'enumDescriptions': [
|
||||
nls.localize('workbench.editor.scrollToSwitchTabs.off', "Tabs will reveal when scrolling with the mouse but not open. You can press and hold the Shift-key to switch tabs while scrolling."),
|
||||
nls.localize('workbench.editor.scrollToSwitchTabs.natural', "Tabs will open when scrolling with the mouse in natural scrolling direction (scroll up to switch to the tab on the left and down for the tab on the right). You can press and hold the Shift-key to disable this behaviour for that duration."),
|
||||
nls.localize('workbench.editor.scrollToSwitchTabs.reverse', "Tabs will open when scrolling with the mouse in reverse scrolling direction (scroll down to switch to the tab on the left and up for the tab on the right). You can press and hold the Shift-key to disable this behaviour for that duration."),
|
||||
],
|
||||
'default': 'off',
|
||||
'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'scrollToSwitchTabs' }, "Controls wether scrolling over tabs will open them or not. By default tabs will only reveal upon scrolling, but not open. You can press and hold the Shift-key while scrolling to change this behaviour for that duration.")
|
||||
'type': 'boolean',
|
||||
'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'scrollToSwitchTabs' }, "Controls wether scrolling over tabs will open them or not. By default tabs will only reveal upon scrolling, but not open. You can press and hold the Shift-key while scrolling to change this behaviour for that duration."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.editor.highlightModifiedTabs': {
|
||||
'type': 'boolean',
|
||||
|
||||
@@ -32,7 +32,7 @@ import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/
|
||||
export const DirtyWorkingCopiesContext = new RawContextKey<boolean>('dirtyWorkingCopies', false);
|
||||
export const ActiveEditorContext = new RawContextKey<string | null>('activeEditor', null);
|
||||
export const ActiveEditorIsReadonlyContext = new RawContextKey<boolean>('activeEditorIsReadonly', false);
|
||||
export const ActiveEditorAvailableEditorsContext = new RawContextKey<string>('availableEditors', '');
|
||||
export const ActiveEditorAvailableEditorIdsContext = new RawContextKey<string>('activeEditorAvailableEditorIds', '');
|
||||
export const EditorsVisibleContext = new RawContextKey<boolean>('editorIsOpen', false);
|
||||
export const EditorPinnedContext = new RawContextKey<boolean>('editorPinned', false);
|
||||
export const EditorGroupActiveEditorDirtyContext = new RawContextKey<boolean>('groupActiveEditorDirty', false);
|
||||
@@ -1304,7 +1304,7 @@ export interface IWorkbenchEditorConfiguration {
|
||||
|
||||
interface IEditorPartConfiguration {
|
||||
showTabs?: boolean;
|
||||
scrollToSwitchTabs?: 'off' | 'natural' | 'reverse';
|
||||
scrollToSwitchTabs?: boolean;
|
||||
highlightModifiedTabs?: boolean;
|
||||
tabCloseButton?: 'left' | 'right' | 'off';
|
||||
tabSizing?: 'fit' | 'shrink';
|
||||
|
||||
@@ -384,7 +384,7 @@ export class EditorGroup extends Disposable {
|
||||
|
||||
moveEditor(candidate: EditorInput, toIndex: number): EditorInput | undefined {
|
||||
const index = this.indexOf(candidate);
|
||||
if (index < 0) {
|
||||
if (index < 0 || toIndex === index) {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
}
|
||||
|
||||
@@ -482,6 +482,10 @@ export class EditorGroup extends Disposable {
|
||||
isPinned(editor: EditorInput): boolean;
|
||||
isPinned(index: number): boolean;
|
||||
isPinned(arg1: EditorInput | number): boolean {
|
||||
if (!this.preview) {
|
||||
return true; // no preview editor
|
||||
}
|
||||
|
||||
let editor: EditorInput;
|
||||
let index: number;
|
||||
if (typeof arg1 === 'number') {
|
||||
@@ -496,10 +500,6 @@ export class EditorGroup extends Disposable {
|
||||
return false; // editor not found
|
||||
}
|
||||
|
||||
if (!this.preview) {
|
||||
return true; // no preview editor
|
||||
}
|
||||
|
||||
return !this.matches(this.preview, editor);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { IView, IViewPaneContainer } from 'vs/workbench/common/views';
|
||||
import { IComposite } from 'vs/workbench/common/composite';
|
||||
|
||||
export interface IPaneComposite extends IComposite {
|
||||
openView(id: string, focus?: boolean): IView;
|
||||
openView<T extends IView>(id: string, focus?: boolean): T | undefined;
|
||||
getViewPaneContainer(): IViewPaneContainer;
|
||||
saveState(): void;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import { UriComponents, URI } from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { RawContextKey, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IViewlet } from 'vs/workbench/common/viewlet';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
@@ -443,12 +442,6 @@ export interface IView {
|
||||
getProgressIndicator(): IProgressIndicator | undefined;
|
||||
}
|
||||
|
||||
export interface IViewsViewlet extends IViewlet {
|
||||
|
||||
openView(id: string, focus?: boolean): IView;
|
||||
|
||||
}
|
||||
|
||||
export const IViewsService = createDecorator<IViewsService>('viewsService');
|
||||
|
||||
export interface IViewsService {
|
||||
@@ -482,6 +475,7 @@ export interface IViewDescriptorService {
|
||||
|
||||
readonly onDidChangeContainer: Event<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }>;
|
||||
readonly onDidChangeLocation: Event<{ views: IViewDescriptor[], from: ViewContainerLocation, to: ViewContainerLocation }>;
|
||||
readonly onDidChangeContainerLocation: Event<{ viewContainer: ViewContainer, from: ViewContainerLocation, to: ViewContainerLocation }>;
|
||||
|
||||
moveViewContainerToLocation(viewContainer: ViewContainer, location: ViewContainerLocation): void;
|
||||
|
||||
@@ -499,6 +493,8 @@ export interface IViewDescriptorService {
|
||||
|
||||
getViewContainerLocation(viewContainer: ViewContainer): ViewContainerLocation | null;
|
||||
|
||||
getDefaultViewContainerLocation(viewContainer: ViewContainer): ViewContainerLocation | null;
|
||||
|
||||
getDefaultContainerById(id: string): ViewContainer | null;
|
||||
|
||||
getViewLocationById(id: string): ViewContainerLocation | null;
|
||||
@@ -631,7 +627,7 @@ export interface IEditableData {
|
||||
validationMessage: (value: string) => { content: string, severity: Severity } | null;
|
||||
placeholder?: string | null;
|
||||
startingValue?: string | null;
|
||||
onFinish: (value: string, success: boolean) => void;
|
||||
onFinish: (value: string, success: boolean) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface IViewPaneContainer {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
.tiw-metadata-value {
|
||||
font-family: var(--monaco-monospace-font);
|
||||
text-align: right;
|
||||
word-break: break-word;
|
||||
}
|
||||
.tiw-metadata-key {
|
||||
vertical-align: top;
|
||||
|
||||
@@ -27,7 +27,7 @@ import { ITextMateService, IGrammar, IToken, StackElement } from 'vs/workbench/s
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { ColorThemeData, TokenStyleDefinitions, TokenStyleDefinition, TextMateThemingRuleDefinitions } from 'vs/workbench/services/themes/common/colorThemeData';
|
||||
import { TokenStylingRule, TokenStyleData, TokenStyle } from 'vs/platform/theme/common/tokenClassificationRegistry';
|
||||
import { SemanticTokenRule, TokenStyleData, TokenStyle } from 'vs/platform/theme/common/tokenClassificationRegistry';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export interface IEditorSemanticHighlightingOptions {
|
||||
@@ -251,6 +251,7 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget {
|
||||
}
|
||||
let text = this._compute(grammar, semanticTokens, position);
|
||||
this._domNode.innerHTML = text;
|
||||
this._domNode.style.maxWidth = `${Math.max(this._editor.getLayoutInfo().width * 0.66, 500)}px`;
|
||||
this._editor.layoutContentWidget(this);
|
||||
}, (err) => {
|
||||
this._notificationService.warn(err);
|
||||
@@ -552,10 +553,11 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget {
|
||||
theme.resolveScopes(definition, scopesDefinition);
|
||||
const matchingRule = scopesDefinition[property];
|
||||
if (matchingRule && scopesDefinition.scope) {
|
||||
return `${escape(scopesDefinition.scope.join(' '))}<br><code class="tiw-theme-selector">${matchingRule.scope}\n${JSON.stringify(matchingRule.settings, null, '\t')}</code>`;
|
||||
const strScopes = Array.isArray(matchingRule.scope) ? matchingRule.scope.join(', ') : String(matchingRule.scope);
|
||||
return `${escape(scopesDefinition.scope.join(' '))}<br><code class="tiw-theme-selector">${strScopes}\n${JSON.stringify(matchingRule.settings, null, '\t')}</code>`;
|
||||
}
|
||||
return '';
|
||||
} else if (TokenStylingRule.is(definition)) {
|
||||
} else if (SemanticTokenRule.is(definition)) {
|
||||
const scope = theme.getTokenStylingRuleScope(definition);
|
||||
if (scope === 'setting') {
|
||||
return `User settings: ${definition.selector.id} - ${this._renderStyleProperty(definition.style, property)}`;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { IKeyMods, IQuickPickSeparator, IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IKeyMods, IQuickPickSeparator, IQuickInputService, IQuickPick } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IEditor } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
@@ -12,16 +12,19 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IQuickAccessRegistry, Extensions as QuickaccessExtensions } from 'vs/platform/quickinput/common/quickAccess';
|
||||
import { AbstractGotoSymbolQuickAccessProvider, IGotoSymbolQuickPickItem } from 'vs/editor/contrib/quickAccess/gotoSymbolQuickAccess';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
|
||||
import { IWorkbenchEditorConfiguration, IEditorPane } from 'vs/workbench/common/editor';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { prepareQuery } from 'vs/base/common/fuzzyScorer';
|
||||
import { SymbolKind } from 'vs/editor/common/modes';
|
||||
import { fuzzyScore, createMatches } from 'vs/base/common/filters';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccessProvider {
|
||||
|
||||
@@ -36,6 +39,8 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess
|
||||
});
|
||||
}
|
||||
|
||||
//#region DocumentSymbols (text editor required)
|
||||
|
||||
private get configuration() {
|
||||
const editorConfig = this.configurationService.getValue<IWorkbenchEditorConfiguration>().workbench.editor;
|
||||
|
||||
@@ -66,6 +71,7 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region public methods to use this picker from other pickers
|
||||
|
||||
@@ -98,6 +104,81 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
protected provideWithoutTextEditor(picker: IQuickPick<IGotoSymbolQuickPickItem>): IDisposable {
|
||||
const pane = this.editorService.activeEditorPane;
|
||||
if (!pane || !TableOfContentsProviderRegistry.has(pane.getId())) {
|
||||
//
|
||||
return super.provideWithoutTextEditor(picker);
|
||||
}
|
||||
|
||||
const provider = TableOfContentsProviderRegistry.get(pane.getId())!;
|
||||
const cts = new CancellationTokenSource();
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
disposables.add(toDisposable(() => cts.dispose(true)));
|
||||
|
||||
picker.busy = true;
|
||||
|
||||
provider.provideTableOfContents(pane, cts.token).then(entries => {
|
||||
|
||||
picker.busy = false;
|
||||
|
||||
if (cts.token.isCancellationRequested || !entries || entries.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const items: IGotoSymbolQuickPickItem[] = entries.map((entry, idx) => {
|
||||
return {
|
||||
kind: SymbolKind.File,
|
||||
index: idx,
|
||||
score: 0,
|
||||
label: entry.label,
|
||||
detail: entry.detail,
|
||||
description: entry.description,
|
||||
};
|
||||
});
|
||||
|
||||
disposables.add(picker.onDidAccept(() => {
|
||||
picker.hide();
|
||||
const [entry] = picker.selectedItems;
|
||||
entries[entry.index]?.reveal();
|
||||
}));
|
||||
|
||||
const updatePickerItems = () => {
|
||||
const filteredItems = items.filter(item => {
|
||||
if (picker.value === '@') {
|
||||
// default, no filtering, scoring...
|
||||
item.score = 0;
|
||||
item.highlights = undefined;
|
||||
return true;
|
||||
}
|
||||
const score = fuzzyScore(picker.value, picker.value.toLowerCase(), 1 /*@-character*/, item.label, item.label.toLowerCase(), 0, true);
|
||||
if (!score) {
|
||||
return false;
|
||||
}
|
||||
item.score = score[1];
|
||||
item.highlights = { label: createMatches(score) };
|
||||
return true;
|
||||
});
|
||||
if (filteredItems.length === 0) {
|
||||
const label = localize('empty', 'No matching entries');
|
||||
picker.items = [{ label, index: -1, kind: SymbolKind.String }];
|
||||
picker.ariaLabel = label;
|
||||
} else {
|
||||
picker.items = filteredItems;
|
||||
}
|
||||
};
|
||||
updatePickerItems();
|
||||
disposables.add(picker.onDidChangeValue(updatePickerItems));
|
||||
|
||||
}).catch(err => {
|
||||
onUnexpectedError(err);
|
||||
picker.hide();
|
||||
});
|
||||
|
||||
return disposables;
|
||||
}
|
||||
}
|
||||
|
||||
Registry.as<IQuickAccessRegistry>(QuickaccessExtensions.Quickaccess).registerQuickAccessProvider({
|
||||
@@ -132,3 +213,43 @@ export class GotoSymbolAction extends Action {
|
||||
Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions).registerWorkbenchAction(SyncActionDescriptor.from(GotoSymbolAction, {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O
|
||||
}), 'Go to Symbol in Editor...');
|
||||
|
||||
|
||||
//#region toc definition and logic
|
||||
|
||||
export interface ITableOfContentsEntry {
|
||||
label: string;
|
||||
detail?: string;
|
||||
description?: string;
|
||||
reveal(): any;
|
||||
}
|
||||
|
||||
export interface ITableOfContentsProvider<T extends IEditorPane = IEditorPane> {
|
||||
provideTableOfContents(editor: T, token: CancellationToken): Promise<ITableOfContentsEntry[] | undefined | null>;
|
||||
}
|
||||
|
||||
class ProviderRegistry {
|
||||
|
||||
private readonly _provider = new Map<string, ITableOfContentsProvider>();
|
||||
|
||||
register(type: string, provider: ITableOfContentsProvider): IDisposable {
|
||||
this._provider.set(type, provider);
|
||||
return toDisposable(() => {
|
||||
if (this._provider.get(type) === provider) {
|
||||
this._provider.delete(type);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get(type: string): ITableOfContentsProvider | undefined {
|
||||
return this._provider.get(type);
|
||||
}
|
||||
|
||||
has(type: string): boolean {
|
||||
return this._provider.has(type);
|
||||
}
|
||||
}
|
||||
|
||||
export const TableOfContentsProviderRegistry = new ProviderRegistry();
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -6,3 +6,4 @@
|
||||
import './inputClipboardActions';
|
||||
import './sleepResumeRepaintMinimap';
|
||||
import './selectionClipboard';
|
||||
import './startDebugTextMate';
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
|
||||
import { ITextMateService } from 'vs/workbench/services/textMate/common/textMateService';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { createRotatingLogger } from 'vs/platform/log/node/spdlogService';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
|
||||
class StartDebugTextMate extends Action {
|
||||
|
||||
private static resource = URI.parse(`inmemory:///tm-log.txt`);
|
||||
|
||||
public static readonly ID = 'editor.action.startDebugTextMate';
|
||||
public static readonly LABEL = nls.localize('startDebugTextMate', "Start Text Mate Syntax Grammar Logging");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@ITextMateService private readonly _textMateService: ITextMateService,
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
|
||||
@IHostService private readonly _hostService: IHostService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
private _getOrCreateModel(): ITextModel {
|
||||
const model = this._modelService.getModel(StartDebugTextMate.resource);
|
||||
if (model) {
|
||||
return model;
|
||||
}
|
||||
return this._modelService.createModel('', null, StartDebugTextMate.resource);
|
||||
}
|
||||
|
||||
private _append(model: ITextModel, str: string) {
|
||||
const lineCount = model.getLineCount();
|
||||
model.applyEdits([{
|
||||
range: new Range(lineCount, Constants.MAX_SAFE_SMALL_INTEGER, lineCount, Constants.MAX_SAFE_SMALL_INTEGER),
|
||||
text: str
|
||||
}]);
|
||||
}
|
||||
|
||||
public async run(): Promise<any> {
|
||||
const pathInTemp = path.join(os.tmpdir(), `vcode-tm-log-${generateUuid()}.txt`);
|
||||
const logger = createRotatingLogger(`tm-log`, pathInTemp, 1024 * 1024 * 30, 1);
|
||||
const model = this._getOrCreateModel();
|
||||
const append = (str: string) => {
|
||||
this._append(model, str + '\n');
|
||||
scrollEditor();
|
||||
logger.info(str);
|
||||
logger.flush();
|
||||
};
|
||||
await this._hostService.openWindow([{ fileUri: URI.file(pathInTemp) }], { forceNewWindow: true });
|
||||
const textEditorPane = await this._editorService.openEditor({
|
||||
resource: model.uri
|
||||
});
|
||||
if (!textEditorPane) {
|
||||
return;
|
||||
}
|
||||
const scrollEditor = () => {
|
||||
const editors = this._codeEditorService.listCodeEditors();
|
||||
for (const editor of editors) {
|
||||
if (editor.hasModel()) {
|
||||
if (editor.getModel().uri.toString() === StartDebugTextMate.resource.toString()) {
|
||||
editor.revealLine(editor.getModel().getLineCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
append(`// Open the file you want to test to the side and watch here`);
|
||||
append(`// Output mirrored at ${pathInTemp}`);
|
||||
|
||||
this._textMateService.startDebugMode(
|
||||
(str) => {
|
||||
this._append(model, str + '\n');
|
||||
scrollEditor();
|
||||
logger.info(str);
|
||||
logger.flush();
|
||||
},
|
||||
() => {
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(StartDebugTextMate), 'Start Text Mate Syntax Grammar Logging', nls.localize('developer', "Developer"));
|
||||
@@ -339,6 +339,11 @@ export class CommentNode extends Disposable {
|
||||
this._commentEditor.layout({ width: container.clientWidth - 14, height: 90 });
|
||||
this._commentEditor.focus();
|
||||
|
||||
dom.scheduleAtNextAnimationFrame(() => {
|
||||
this._commentEditor!.layout({ width: container.clientWidth - 14, height: 90 });
|
||||
this._commentEditor!.focus();
|
||||
});
|
||||
|
||||
const lastLine = this._commentEditorModel.getLineCount();
|
||||
const lastColumn = this._commentEditorModel.getLineContent(lastLine).length + 1;
|
||||
this._commentEditor.setSelection(new Selection(lastLine, lastColumn, lastLine, lastColumn));
|
||||
|
||||
@@ -101,6 +101,8 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
|
||||
private _commentMenus: CommentMenus;
|
||||
|
||||
private _commentOptions: modes.CommentOptions | undefined;
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
private _owner: string,
|
||||
@@ -133,6 +135,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
|
||||
if (controller) {
|
||||
commentControllerKey.set(controller.contextValue);
|
||||
this._commentOptions = controller.options;
|
||||
}
|
||||
|
||||
this._resizeObserver = null;
|
||||
@@ -718,9 +721,9 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
|
||||
private createReplyButton() {
|
||||
this._reviewThreadReplyButton = <HTMLButtonElement>dom.append(this._commentForm, dom.$('button.review-thread-reply-button'));
|
||||
this._reviewThreadReplyButton.title = nls.localize('reply', "Reply...");
|
||||
this._reviewThreadReplyButton.title = this._commentOptions?.prompt || nls.localize('reply', "Reply...");
|
||||
|
||||
this._reviewThreadReplyButton.textContent = nls.localize('reply', "Reply...");
|
||||
this._reviewThreadReplyButton.textContent = this._commentOptions?.prompt || nls.localize('reply', "Reply...");
|
||||
// bind click/escape actions for reviewThreadReplyButton and textArea
|
||||
this._disposables.add(dom.addDisposableListener(this._reviewThreadReplyButton, 'click', _ => this.expandReplyArea()));
|
||||
this._disposables.add(dom.addDisposableListener(this._reviewThreadReplyButton, 'focus', _ => this.expandReplyArea()));
|
||||
@@ -768,8 +771,8 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
const placeholder = valueLength > 0
|
||||
? ''
|
||||
: hasExistingComments
|
||||
? nls.localize('reply', "Reply...")
|
||||
: nls.localize('newComment', "Type a new comment");
|
||||
? (this._commentOptions?.placeHolder || nls.localize('reply', "Reply..."))
|
||||
: (this._commentOptions?.placeHolder || nls.localize('newComment', "Type a new comment"));
|
||||
const decorations = [{
|
||||
range: {
|
||||
startLineNumber: 0,
|
||||
|
||||
@@ -10,8 +10,7 @@ import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkey
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
|
||||
import { defaultCustomEditor } from 'vs/workbench/contrib/customEditor/common/contributedCustomEditors';
|
||||
import { CONTEXT_CUSTOM_EDITORS, CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor';
|
||||
import { CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE } from 'vs/workbench/contrib/customEditor/common/customEditor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
|
||||
@@ -67,51 +66,3 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
||||
}
|
||||
}).register();
|
||||
|
||||
(new class ToggleCustomEditorCommand extends Command {
|
||||
public static readonly ID = 'editor.action.customEditor.toggle';
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: ToggleCustomEditorCommand.ID,
|
||||
precondition: CONTEXT_CUSTOM_EDITORS,
|
||||
});
|
||||
}
|
||||
|
||||
public runCommand(accessor: ServicesAccessor): void {
|
||||
const editorService = accessor.get<IEditorService>(IEditorService);
|
||||
const activeEditorPane = editorService.activeEditorPane;
|
||||
if (!activeEditorPane) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeGroup = activeEditorPane.group;
|
||||
const activeEditor = activeEditorPane.input;
|
||||
const targetResource = activeEditor.resource;
|
||||
|
||||
if (!targetResource) {
|
||||
return;
|
||||
}
|
||||
|
||||
const customEditorService = accessor.get<ICustomEditorService>(ICustomEditorService);
|
||||
|
||||
let toggleView = defaultCustomEditor.id;
|
||||
if (!(activeEditor instanceof CustomEditorInput)) {
|
||||
const bestAvailableEditor = customEditorService.getContributedCustomEditors(targetResource).bestAvailableEditor;
|
||||
if (bestAvailableEditor) {
|
||||
toggleView = bestAvailableEditor.id;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const newEditorInput = customEditorService.createInput(targetResource, toggleView, activeGroup.id);
|
||||
|
||||
editorService.replaceEditors([{
|
||||
editor: activeEditor,
|
||||
replacement: newEditorInput,
|
||||
options: {
|
||||
ignoreOverrides: true,
|
||||
}
|
||||
}], activeGroup);
|
||||
}
|
||||
}).register();
|
||||
|
||||
@@ -29,7 +29,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
|
||||
private readonly _editorResource: URI;
|
||||
private _defaultDirtyState: boolean | undefined;
|
||||
|
||||
public readonly backupId: string | undefined;
|
||||
private readonly _backupId: string | undefined;
|
||||
|
||||
get resource() { return this._editorResource; }
|
||||
|
||||
@@ -54,7 +54,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
|
||||
super(id, viewType, '', webview, webviewService, webviewWorkbenchService);
|
||||
this._editorResource = resource;
|
||||
this._defaultDirtyState = options.startsDirty;
|
||||
this.backupId = options.backupId;
|
||||
this._backupId = options.backupId;
|
||||
}
|
||||
|
||||
public getTypeId(): string {
|
||||
@@ -213,7 +213,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
|
||||
this.viewType,
|
||||
this.id,
|
||||
new Lazy(() => undefined!),
|
||||
{ startsDirty: this._defaultDirtyState, backupId: this.backupId }); // this webview is replaced in the transfer call
|
||||
{ startsDirty: this._defaultDirtyState, backupId: this._backupId }); // this webview is replaced in the transfer call
|
||||
this.transfer(newEditor);
|
||||
newEditor.updateGroup(group);
|
||||
return newEditor;
|
||||
@@ -246,4 +246,11 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
|
||||
this._moveHandler = undefined;
|
||||
return other;
|
||||
}
|
||||
|
||||
get backupId(): string | undefined {
|
||||
if (this._modelRef) {
|
||||
return this._modelRef.object.backupId;
|
||||
}
|
||||
return this._backupId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,9 @@ import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { CONTEXT_CUSTOM_EDITORS, CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CustomEditorCapabilities, CustomEditorInfo, CustomEditorInfoCollection, CustomEditorPriority, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor';
|
||||
import { CustomEditorModelManager } from 'vs/workbench/contrib/customEditor/common/customEditorModelManager';
|
||||
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
|
||||
import { defaultEditorOverrideEntry } from 'vs/workbench/contrib/files/common/openWith';
|
||||
import { IWebviewService, webviewHasOwnEditFunctionsContext } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { CustomEditorAssociation, CustomEditorsAssociations, customEditorsAssociationsSettingId } from 'vs/workbench/services/editor/browser/editorAssociationsSetting';
|
||||
import { CustomEditorAssociation, CustomEditorsAssociations, customEditorsAssociationsSettingId } from 'vs/workbench/services/editor/common/editorAssociationsSetting';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ICustomEditorInfo, ICustomEditorViewTypesHandler, IEditorService, IOpenEditorOverride, IOpenEditorOverrideEntry } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ContributedCustomEditors, defaultCustomEditor } from '../common/contributedCustomEditors';
|
||||
@@ -170,7 +171,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
|
||||
// And persist the setting
|
||||
if (pick) {
|
||||
const newAssociation: CustomEditorAssociation = { viewType: pick, filenamePattern: '*' + resourceExt };
|
||||
const currentAssociations = [...this.configurationService.getValue<CustomEditorsAssociations>(customEditorsAssociationsSettingId)] || [];
|
||||
const currentAssociations = [...this.configurationService.getValue<CustomEditorsAssociations>(customEditorsAssociationsSettingId)];
|
||||
|
||||
// First try updating existing association
|
||||
for (let i = 0; i < currentAssociations.length; ++i) {
|
||||
@@ -207,7 +208,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
|
||||
}
|
||||
|
||||
const capabilities = this.getCustomEditorCapabilities(viewType) || {};
|
||||
if (!capabilities.supportsMultipleEditorsPerResource) {
|
||||
if (!capabilities.supportsMultipleEditorsPerDocument) {
|
||||
const movedEditor = await this.tryRevealExistingEditorForResourceInGroup(resource, viewType, options, group);
|
||||
if (movedEditor) {
|
||||
return movedEditor;
|
||||
@@ -426,21 +427,24 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo
|
||||
open: (editor, options, group, id) => {
|
||||
return this.onEditorOpening(editor, options, group, id);
|
||||
},
|
||||
getEditorOverrides: (editor: IEditorInput, _options: IEditorOptions | undefined, _group: IEditorGroup | undefined): IOpenEditorOverrideEntry[] => {
|
||||
const resource = editor.resource;
|
||||
if (!resource) {
|
||||
return [];
|
||||
}
|
||||
getEditorOverrides: (resource: URI, _options: IEditorOptions | undefined, group: IEditorGroup | undefined): IOpenEditorOverrideEntry[] => {
|
||||
const currentEditor = group?.editors.find(editor => isEqual(editor.resource, resource));
|
||||
|
||||
const customEditors = this.customEditorService.getAllCustomEditors(resource);
|
||||
return customEditors.allEditors.map(entry => {
|
||||
return {
|
||||
id: entry.id,
|
||||
active: editor instanceof CustomEditorInput && editor.viewType === entry.id,
|
||||
label: entry.displayName,
|
||||
detail: entry.providerDisplayName,
|
||||
};
|
||||
});
|
||||
return [
|
||||
{
|
||||
...defaultEditorOverrideEntry,
|
||||
active: currentEditor instanceof FileEditorInput,
|
||||
},
|
||||
...customEditors.allEditors.map(entry => {
|
||||
return {
|
||||
id: entry.id,
|
||||
active: currentEditor instanceof CustomEditorInput && currentEditor.viewType === entry.id,
|
||||
label: entry.displayName,
|
||||
detail: entry.providerDisplayName,
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ export const CONTEXT_CUSTOM_EDITORS = new RawContextKey<string>('customEditors',
|
||||
export const CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE = new RawContextKey<boolean>('focusedCustomEditorIsEditable', false);
|
||||
|
||||
export interface CustomEditorCapabilities {
|
||||
readonly supportsMultipleEditorsPerResource?: boolean;
|
||||
readonly supportsMultipleEditorsPerDocument?: boolean;
|
||||
}
|
||||
|
||||
export interface ICustomEditorService {
|
||||
@@ -56,6 +56,7 @@ export interface ICustomEditorModelManager {
|
||||
export interface ICustomEditorModel extends IDisposable {
|
||||
readonly viewType: string;
|
||||
readonly resource: URI;
|
||||
readonly backupId: string | undefined;
|
||||
|
||||
isReadonly(): boolean;
|
||||
|
||||
|
||||
@@ -54,6 +54,10 @@ export class CustomTextEditorModel extends Disposable implements ICustomEditorMo
|
||||
return this._model.object.isReadonly();
|
||||
}
|
||||
|
||||
public get backupId() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public isDirty(): boolean {
|
||||
return this.textFileService.isDirty(this.resource);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { IDebugService, State, IStackFrame, IDebugSession, IThread, CONTEXT_CALL
|
||||
import { Thread, StackFrame, ThreadAndSessionIds } from 'vs/workbench/contrib/debug/common/debugModel';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MenuId, IMenu, IMenuService } from 'vs/platform/actions/common/actions';
|
||||
import { MenuId, IMenu, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
@@ -21,7 +21,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c
|
||||
import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { createAndFillInContextMenuActions, createAndFillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { ITreeRenderer, ITreeNode, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
|
||||
import { TreeResourceNavigator, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
|
||||
@@ -40,6 +40,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -62,7 +63,7 @@ export function getContext(element: CallStackItem | null): any {
|
||||
export function getContextForContributedActions(element: CallStackItem | null): string | number {
|
||||
if (element instanceof StackFrame) {
|
||||
if (element.source.inMemory) {
|
||||
return element.source.raw.path || element.source.reference || '';
|
||||
return element.source.raw.path || element.source.reference || element.source.name;
|
||||
}
|
||||
|
||||
return element.source.uri.toString();
|
||||
@@ -87,7 +88,7 @@ export class CallStackView extends ViewPane {
|
||||
private callStackItemType: IContextKey<string>;
|
||||
private dataSource!: CallStackDataSource;
|
||||
private tree!: WorkbenchAsyncDataTree<CallStackItem | IDebugModel, CallStackItem, FuzzyScore>;
|
||||
private contributedContextMenu: IMenu;
|
||||
private menu: IMenu;
|
||||
private parentSessionToExpand = new Set<IDebugSession>();
|
||||
private selectionNeedsUpdate = false;
|
||||
|
||||
@@ -109,8 +110,8 @@ export class CallStackView extends ViewPane {
|
||||
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService);
|
||||
this.callStackItemType = CONTEXT_CALLSTACK_ITEM_TYPE.bindTo(contextKeyService);
|
||||
|
||||
this.contributedContextMenu = menuService.createMenu(MenuId.DebugCallStackContext, contextKeyService);
|
||||
this._register(this.contributedContextMenu);
|
||||
this.menu = menuService.createMenu(MenuId.DebugCallStackContext, contextKeyService);
|
||||
this._register(this.menu);
|
||||
|
||||
// Create scheduler to prevent unnecessary flashing of tree when reacting to changes
|
||||
this.onCallStackChangeScheduler = new RunOnceScheduler(() => {
|
||||
@@ -171,8 +172,9 @@ export class CallStackView extends ViewPane {
|
||||
const treeContainer = renderViewTree(container);
|
||||
|
||||
this.dataSource = new CallStackDataSource(this.debugService);
|
||||
const sessionsRenderer = this.instantiationService.createInstance(SessionsRenderer, this.menu);
|
||||
this.tree = <WorkbenchAsyncDataTree<CallStackItem | IDebugModel, CallStackItem, FuzzyScore>>this.instantiationService.createInstance(WorkbenchAsyncDataTree, 'CallStackView', treeContainer, new CallStackDelegate(), [
|
||||
new SessionsRenderer(this.instantiationService, this.debugService),
|
||||
sessionsRenderer,
|
||||
new ThreadsRenderer(this.instantiationService),
|
||||
this.instantiationService.createInstance(StackFramesRenderer),
|
||||
new ErrorsRenderer(),
|
||||
@@ -378,12 +380,15 @@ export class CallStackView extends ViewPane {
|
||||
this.callStackItemType.reset();
|
||||
}
|
||||
|
||||
const actions: IAction[] = [];
|
||||
const actionsDisposable = createAndFillInContextMenuActions(this.contributedContextMenu, { arg: getContextForContributedActions(element), shouldForwardArgs: true }, actions, this.contextMenuService);
|
||||
|
||||
const primary: IAction[] = [];
|
||||
const secondary: IAction[] = [];
|
||||
const result = { primary, secondary };
|
||||
const actionsDisposable = createAndFillInContextMenuActions(this.menu, { arg: getContextForContributedActions(element), shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g));
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => e.anchor,
|
||||
getActions: () => actions,
|
||||
getActions: () => result.secondary,
|
||||
getActionsContext: () => getContext(element),
|
||||
onHide: () => dispose(actionsDisposable)
|
||||
});
|
||||
@@ -406,6 +411,7 @@ interface ISessionTemplateData {
|
||||
stateLabel: HTMLSpanElement;
|
||||
label: HighlightedLabel;
|
||||
actionBar: ActionBar;
|
||||
elementDisposable: IDisposable[];
|
||||
}
|
||||
|
||||
interface IErrorTemplateData {
|
||||
@@ -430,8 +436,12 @@ class SessionsRenderer implements ITreeRenderer<IDebugSession, FuzzyScore, ISess
|
||||
static readonly ID = 'session';
|
||||
|
||||
constructor(
|
||||
private readonly instantiationService: IInstantiationService,
|
||||
private readonly debugService: IDebugService
|
||||
private menu: IMenu,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService
|
||||
) { }
|
||||
|
||||
get templateId(): string {
|
||||
@@ -445,9 +455,18 @@ class SessionsRenderer implements ITreeRenderer<IDebugSession, FuzzyScore, ISess
|
||||
const state = dom.append(session, $('.state'));
|
||||
const stateLabel = dom.append(state, $('span.label'));
|
||||
const label = new HighlightedLabel(name, false);
|
||||
const actionBar = new ActionBar(session);
|
||||
const actionBar = new ActionBar(session, {
|
||||
actionViewItemProvider: action => {
|
||||
if (action instanceof MenuItemAction) {
|
||||
// We need the MenuEntryActionViewItem so the icon would get rendered
|
||||
return new MenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService);
|
||||
}
|
||||
|
||||
return { session, name, state, stateLabel, label, actionBar };
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
return { session, name, state, stateLabel, label, actionBar, elementDisposable: [] };
|
||||
}
|
||||
|
||||
renderElement(element: ITreeNode<IDebugSession, FuzzyScore>, _: number, data: ISessionTemplateData): void {
|
||||
@@ -456,9 +475,19 @@ class SessionsRenderer implements ITreeRenderer<IDebugSession, FuzzyScore, ISess
|
||||
data.label.set(session.getLabel(), createMatches(element.filterData));
|
||||
const thread = session.getAllThreads().filter(t => t.stopped).pop();
|
||||
|
||||
data.actionBar.clear();
|
||||
const actions = getActions(this.instantiationService, element.element);
|
||||
data.actionBar.push(actions, { icon: true, label: false });
|
||||
const setActionBar = () => {
|
||||
const actions = getActions(this.instantiationService, element.element);
|
||||
|
||||
const primary: IAction[] = actions;
|
||||
const secondary: IAction[] = [];
|
||||
const result = { primary, secondary };
|
||||
data.elementDisposable.push(createAndFillInActionBarActions(this.menu, { arg: getContextForContributedActions(session), shouldForwardArgs: true }, result, g => /^inline/.test(g)));
|
||||
|
||||
data.actionBar.clear();
|
||||
data.actionBar.push(primary, { icon: true, label: false });
|
||||
};
|
||||
setActionBar();
|
||||
data.elementDisposable.push(this.menu.onDidChange(() => setActionBar()));
|
||||
data.stateLabel.hidden = false;
|
||||
|
||||
if (thread && thread.stoppedDetails) {
|
||||
@@ -476,6 +505,10 @@ class SessionsRenderer implements ITreeRenderer<IDebugSession, FuzzyScore, ISess
|
||||
disposeTemplate(templateData: ISessionTemplateData): void {
|
||||
templateData.actionBar.dispose();
|
||||
}
|
||||
|
||||
disposeElement(_element: ITreeNode<IDebugSession, FuzzyScore>, _: number, templateData: ISessionTemplateData): void {
|
||||
dispose(templateData.elementDisposable);
|
||||
}
|
||||
}
|
||||
|
||||
class ThreadsRenderer implements ITreeRenderer<IThread, FuzzyScore, IThreadTemplateData> {
|
||||
|
||||
@@ -91,6 +91,7 @@ const openPanelKb: IKeybindings = {
|
||||
const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).registerViewContainer({
|
||||
id: DEBUG_PANEL_ID,
|
||||
name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'),
|
||||
icon: 'codicon-debug-console',
|
||||
ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [DEBUG_PANEL_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]),
|
||||
storageId: DEBUG_PANEL_ID,
|
||||
focusCommand: {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { SelectBox, ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selec
|
||||
import { SelectActionViewItem, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IDebugService, IDebugSession, IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugService, IDebugSession, IDebugConfiguration, IConfig, ILaunch } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
@@ -36,6 +36,7 @@ export class StartDebugActionViewItem implements IActionViewItem {
|
||||
private options: { label: string, handler?: (() => boolean) }[] = [];
|
||||
private toDispose: IDisposable[];
|
||||
private selected = 0;
|
||||
private providers: { label: string, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[] = [];
|
||||
|
||||
constructor(
|
||||
private context: unknown,
|
||||
@@ -127,6 +128,12 @@ export class StartDebugActionViewItem implements IActionViewItem {
|
||||
this.container.style.border = colors.selectBorder ? `1px solid ${colors.selectBorder}` : '';
|
||||
selectBoxContainer.style.borderLeft = colors.selectBorder ? `1px solid ${colors.selectBorder}` : '';
|
||||
}));
|
||||
this.debugService.getConfigurationManager().getDynamicProviders().then(providers => {
|
||||
this.providers = providers;
|
||||
if (this.providers.length > 0) {
|
||||
this.updateOptions();
|
||||
}
|
||||
});
|
||||
|
||||
this.updateOptions();
|
||||
}
|
||||
@@ -155,7 +162,7 @@ export class StartDebugActionViewItem implements IActionViewItem {
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
|
||||
private async updateOptions(): Promise<void> {
|
||||
private updateOptions(): void {
|
||||
this.selected = 0;
|
||||
this.options = [];
|
||||
const manager = this.debugService.getConfigurationManager();
|
||||
@@ -191,8 +198,7 @@ export class StartDebugActionViewItem implements IActionViewItem {
|
||||
disabledIdxs.push(this.options.length - 1);
|
||||
}
|
||||
|
||||
const providers = await manager.getDynamicProviders();
|
||||
providers.forEach(p => {
|
||||
this.providers.forEach(p => {
|
||||
this.options.push({
|
||||
label: `${p.label}...`, handler: () => {
|
||||
StartAction.GET_CONFIG_AND_LAUNCH = p.pick;
|
||||
@@ -201,7 +207,7 @@ export class StartDebugActionViewItem implements IActionViewItem {
|
||||
});
|
||||
});
|
||||
|
||||
if (providers.length > 0) {
|
||||
if (this.providers.length > 0) {
|
||||
this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: undefined });
|
||||
disabledIdxs.push(this.options.length - 1);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import { sequence } from 'vs/base/common/async';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { first } from 'vs/base/common/arrays';
|
||||
import { getVisibleAndSorted } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
import { DebugConfigurationProviderScope } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes';
|
||||
|
||||
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
|
||||
jsonRegistry.registerSchema(launchSchemaId, launchSchema);
|
||||
@@ -195,14 +195,14 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* if scope is not specified,a value of DebugConfigurationProviderScope.Initialization is assumed.
|
||||
* if scope is not specified,a value of DebugConfigurationProvideTrigger.Initial is assumed.
|
||||
*/
|
||||
hasDebugConfigurationProvider(debugType: string, scope?: DebugConfigurationProviderScope): boolean {
|
||||
if (scope === undefined) {
|
||||
scope = DebugConfigurationProviderScope.Initial;
|
||||
hasDebugConfigurationProvider(debugType: string, triggerKind?: DebugConfigurationProviderTriggerKind): boolean {
|
||||
if (triggerKind === undefined) {
|
||||
triggerKind = DebugConfigurationProviderTriggerKind.Initial;
|
||||
}
|
||||
// check if there are providers for the given type that contribute a provideDebugConfigurations method
|
||||
const providers = this.configProviders.filter(p => p.provideDebugConfigurations && (p.type === debugType) && (p.scope === scope));
|
||||
const providers = this.configProviders.filter(p => p.provideDebugConfigurations && (p.type === debugType) && (p.triggerKind === triggerKind));
|
||||
return providers.length > 0;
|
||||
}
|
||||
|
||||
@@ -241,22 +241,28 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
|
||||
async provideDebugConfigurations(folderUri: uri | undefined, type: string, token: CancellationToken): Promise<any[]> {
|
||||
await this.activateDebuggers('onDebugInitialConfigurations');
|
||||
const results = await Promise.all(this.configProviders.filter(p => p.type === type && p.scope === DebugConfigurationProviderScope.Initial && p.provideDebugConfigurations).map(p => p.provideDebugConfigurations!(folderUri, token)));
|
||||
const results = await Promise.all(this.configProviders.filter(p => p.type === type && p.triggerKind === DebugConfigurationProviderTriggerKind.Initial && p.provideDebugConfigurations).map(p => p.provideDebugConfigurations!(folderUri, token)));
|
||||
|
||||
return results.reduce((first, second) => first.concat(second), []);
|
||||
}
|
||||
|
||||
async getDynamicProviders(): Promise<{ label: string, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[]> {
|
||||
await this.activateDebuggers('onDebugDynamicConfigurations');
|
||||
const dynamicProviders = this.configProviders.filter(p => p.scope === DebugConfigurationProviderScope.Dynamic && p.provideDebugConfigurations);
|
||||
return dynamicProviders.map(provider => {
|
||||
const extensions = await this.extensionService.getExtensions();
|
||||
const debugDynamicExtensions = extensions.filter(e => {
|
||||
return e.activationEvents && e.activationEvents.filter(e => e.includes('onDebugDynamicConfigurations')).length && e.contributes?.debuggers;
|
||||
});
|
||||
|
||||
return debugDynamicExtensions.map(e => {
|
||||
const type = e.contributes?.debuggers![0].type!;
|
||||
return {
|
||||
label: this.getDebuggerLabel(provider.type)!,
|
||||
label: this.getDebuggerLabel(type)!,
|
||||
pick: async () => {
|
||||
await this.activateDebuggers('onDebugDynamicConfigurations', type);
|
||||
const token = new CancellationTokenSource();
|
||||
const picks: Promise<{ label: string, launch: ILaunch, config: IConfig }[]>[] = [];
|
||||
const provider = this.configProviders.filter(p => p.type === type && p.triggerKind === DebugConfigurationProviderTriggerKind.Dynamic && p.provideDebugConfigurations)[0];
|
||||
this.getLaunches().forEach(launch => {
|
||||
if (launch.workspace) {
|
||||
if (launch.workspace && provider) {
|
||||
picks.push(provider.provideDebugConfigurations!(launch.workspace.uri, token.token).then(configurations => configurations.map(config => ({
|
||||
label: config.name,
|
||||
config,
|
||||
|
||||
@@ -281,16 +281,16 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
}
|
||||
|
||||
this.valueContainer.hidden = true;
|
||||
this.complexValueContainer.hidden = false;
|
||||
|
||||
await this.tree.setInput(expression);
|
||||
this.complexValueTitle.textContent = expression.value;
|
||||
this.complexValueTitle.title = expression.value;
|
||||
this.layoutTreeAndContainer();
|
||||
this.editor.layoutContentWidget(this);
|
||||
this.scrollbar.scanDomNode();
|
||||
await this.tree.setInput(expression);
|
||||
this.tree.scrollTop = 0;
|
||||
this.tree.scrollLeft = 0;
|
||||
this.complexValueContainer.hidden = false;
|
||||
|
||||
if (focus) {
|
||||
this.editor.render();
|
||||
|
||||
@@ -170,7 +170,7 @@ export class DebugService implements IDebugService {
|
||||
this.activity.dispose();
|
||||
}
|
||||
if (numberOfSessions > 0) {
|
||||
this.activity = this.activityService.showActivity(VIEWLET_ID, new NumberBadge(numberOfSessions, n => n === 1 ? nls.localize('1activeSession', "1 active session") : nls.localize('nActiveSessions', "{0} active sessions", n)));
|
||||
this.activity = this.activityService.showViewContainerActivity(VIEWLET_ID, { badge: new NumberBadge(numberOfSessions, n => n === 1 ? nls.localize('1activeSession', "1 active session") : nls.localize('nActiveSessions', "{0} active sessions", n)) });
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(this.model.onDidChangeBreakpoints(() => setBreakpointsExistContext()));
|
||||
|
||||
@@ -271,7 +271,6 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution {
|
||||
|
||||
if (this.$el) {
|
||||
this.$el.remove();
|
||||
delete this.$el;
|
||||
}
|
||||
if (this.disposeOnUpdate) {
|
||||
dispose(this.disposeOnUpdate);
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
font-size: 11px;
|
||||
margin-right: 0.3em;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
flex-shrink: 1;
|
||||
margin-top: 7px;
|
||||
}
|
||||
@@ -40,15 +41,23 @@
|
||||
}
|
||||
|
||||
.monaco-workbench .part > .title > .title-actions .start-debug-action-item .codicon {
|
||||
line-height: inherit;
|
||||
outline-offset: 0px;
|
||||
flex-shrink: 0;
|
||||
transition: transform 50ms ease;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-action-bar .start-debug-action-item .configuration.select-container {
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-action-bar .start-debug-action-item .configuration .monaco-select-box {
|
||||
border: none;
|
||||
margin-top: 0px;
|
||||
cursor: pointer;
|
||||
outline-offset: 2px;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
outline-offset: 0px;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-action-bar .start-debug-action-item .configuration.disabled .monaco-select-box {
|
||||
@@ -75,6 +84,11 @@
|
||||
cursor: initial;
|
||||
}
|
||||
|
||||
/* Make icons the same color as the list foreground on focus selection */
|
||||
.debug-pane .monaco-list:focus .monaco-list-row.selected.focused .codicon {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/* Call stack */
|
||||
|
||||
.debug-pane .debug-call-stack-title {
|
||||
@@ -145,7 +159,9 @@
|
||||
height: 100%;
|
||||
line-height: 22px;
|
||||
margin-right: 8px;
|
||||
vertical-align: text-top;
|
||||
background-size: 16px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.debug-pane .debug-call-stack .thread > .state > .label,
|
||||
|
||||
@@ -700,13 +700,13 @@ export class RawDebugSession implements IDisposable {
|
||||
"error" : { "classification": "CallstackOrException", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('debugProtocolErrorResponse', { error: telemetryMessage });
|
||||
this.telemetryService.publicLogError('debugProtocolErrorResponse', { error: telemetryMessage });
|
||||
if (this.customTelemetryService) {
|
||||
/* __GDPR__TODO__
|
||||
The message is sent in the name of the adapter but the adapter doesn't know about it.
|
||||
However, since adapters are an open-ended set, we can not declared the events statically either.
|
||||
*/
|
||||
this.customTelemetryService.publicLog('debugProtocolErrorResponse', { error: telemetryMessage }, true);
|
||||
this.customTelemetryService.publicLogError('debugProtocolErrorResponse', { error: telemetryMessage });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import { TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks';
|
||||
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { DebugConfigurationProviderScope } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes';
|
||||
|
||||
export const VIEWLET_ID = 'workbench.view.debug';
|
||||
|
||||
@@ -603,7 +603,7 @@ export interface IDebuggerContribution extends IPlatformSpecificAdapterContribut
|
||||
|
||||
export interface IDebugConfigurationProvider {
|
||||
readonly type: string;
|
||||
readonly scope: DebugConfigurationProviderScope;
|
||||
readonly triggerKind: DebugConfigurationProviderTriggerKind;
|
||||
resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise<IConfig | null | undefined>;
|
||||
resolveDebugConfigurationWithSubstitutedVariables?(folderUri: uri | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise<IConfig | null | undefined>;
|
||||
provideDebugConfigurations?(folderUri: uri | undefined, token: CancellationToken): Promise<IConfig[]>;
|
||||
|
||||
@@ -33,7 +33,7 @@ export class NodeDebugHelperService implements IDebugHelperService {
|
||||
const channel = client.getChannel('telemetryAppender');
|
||||
const appender = new TelemetryAppenderClient(channel);
|
||||
|
||||
return new TelemetryService({ appender }, configurationService);
|
||||
return new TelemetryService({ appender, sendErrorTelemetry: true }, configurationService);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -164,8 +164,8 @@ export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments
|
||||
case ShellType.bash:
|
||||
|
||||
quote = (s: string) => {
|
||||
s = s.replace(/(["';\\])/g, '\\$1');
|
||||
return (s.indexOf(' ') >= 0 || s.length === 0) ? `"${s}"` : s;
|
||||
s = s.replace(/(["'\\])/g, '\\$1');
|
||||
return (s.indexOf(' ') >= 0 || s.indexOf(';') >= 0 || s.length === 0) ? `"${s}"` : s;
|
||||
};
|
||||
|
||||
const hardQuote = (s: string) => {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/teleme
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { language } from 'vs/base/common/platform';
|
||||
import { language, OperatingSystem, OS } from 'vs/base/common/platform';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { match } from 'vs/base/common/glob';
|
||||
import { IRequestService, asJson } from 'vs/platform/request/common/request';
|
||||
@@ -93,7 +93,7 @@ interface IExperimentStorageState {
|
||||
* be incremented when adding a condition, otherwise experiments might activate
|
||||
* on older versions of VS Code where not intended.
|
||||
*/
|
||||
export const currentSchemaVersion = 3;
|
||||
export const currentSchemaVersion = 4;
|
||||
|
||||
interface IRawExperiment {
|
||||
id: string;
|
||||
@@ -111,6 +111,7 @@ interface IRawExperiment {
|
||||
uniqueDays?: number;
|
||||
minEvents: number;
|
||||
};
|
||||
os: OperatingSystem[];
|
||||
installedExtensions?: {
|
||||
excludes?: string[];
|
||||
includes?: string[];
|
||||
@@ -439,6 +440,10 @@ export class ExperimentService extends Disposable implements IExperimentService
|
||||
return Promise.resolve(ExperimentState.Run);
|
||||
}
|
||||
|
||||
if (experiment.condition?.os && !experiment.condition.os.includes(OS)) {
|
||||
return Promise.resolve(ExperimentState.NoRun);
|
||||
}
|
||||
|
||||
if (!this.checkExperimentDependencies(experiment)) {
|
||||
return Promise.resolve(ExperimentState.NoRun);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IWillActivateEvent, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { TestExtensionService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
import { OS } from 'vs/base/common/platform';
|
||||
|
||||
interface ExperimentSettings {
|
||||
enabled?: boolean;
|
||||
@@ -308,6 +309,44 @@ suite('Experiment Service', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Experiment with OS should be enabled on current OS', () => {
|
||||
experimentData = {
|
||||
experiments: [
|
||||
{
|
||||
id: 'experiment1',
|
||||
enabled: true,
|
||||
condition: {
|
||||
os: [OS],
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
testObject = instantiationService.createInstance(TestExperimentService);
|
||||
return testObject.getExperimentById('experiment1').then(result => {
|
||||
assert.equal(result.state, ExperimentState.Run);
|
||||
});
|
||||
});
|
||||
|
||||
test('Experiment with OS should be disabled on other OS', () => {
|
||||
experimentData = {
|
||||
experiments: [
|
||||
{
|
||||
id: 'experiment1',
|
||||
enabled: true,
|
||||
condition: {
|
||||
os: [OS - 1],
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
testObject = instantiationService.createInstance(TestExperimentService);
|
||||
return testObject.getExperimentById('experiment1').then(result => {
|
||||
assert.equal(result.state, ExperimentState.NoRun);
|
||||
});
|
||||
});
|
||||
|
||||
test('Activation event experiment with not enough events should be evaluating', () => {
|
||||
experimentData = {
|
||||
experiments: [
|
||||
|
||||
@@ -215,7 +215,7 @@ export class ExtensionEditor extends BaseEditor {
|
||||
|
||||
const details = append(header, $('.details'));
|
||||
const title = append(details, $('.title'));
|
||||
const name = append(title, $('span.name.clickable', { title: localize('name', "Extension name") }));
|
||||
const name = append(title, $('span.name.clickable', { title: localize('name', "Extension name"), role: 'heading', tabIndex: 0 }));
|
||||
const identifier = append(title, $('span.identifier', { title: localize('extension id', "Extension identifier") }));
|
||||
|
||||
const preview = append(title, $('span.preview', { title: localize('preview', "Preview") }));
|
||||
@@ -399,8 +399,8 @@ export class ExtensionEditor extends BaseEditor {
|
||||
}
|
||||
this.transientDisposables.add(this.onClick(template.publisher, () => {
|
||||
this.viewletService.openViewlet(VIEWLET_ID, true)
|
||||
.then(viewlet => viewlet?.getViewPaneContainer())
|
||||
.then((viewlet: IExtensionsViewPaneContainer) => {
|
||||
.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
|
||||
.then(viewlet => {
|
||||
viewlet.search(`publisher:"${extension.publisherDisplayName}"`);
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -643,7 +643,7 @@ export class StatusUpdater extends Disposable implements IWorkbenchContribution
|
||||
const outdated = this.extensionsWorkbenchService.outdated.reduce((r, e) => r + (this.extensionEnablementService.isEnabled(e.local!) ? 1 : 0), 0);
|
||||
if (outdated > 0) {
|
||||
const badge = new NumberBadge(outdated, n => localize('outdatedExtensions', '{0} Outdated Extensions', n));
|
||||
this.badgeHandle.value = this.activityService.showActivity(VIEWLET_ID, badge, 'extensions-badge count-badge');
|
||||
this.badgeHandle.value = this.activityService.showViewContainerActivity(VIEWLET_ID, { badge, clazz: 'extensions-badge count-badge' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import 'vs/css!./media/explorerviewlet';
|
||||
import { localize } from 'vs/nls';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { VIEWLET_ID, ExplorerViewletVisibleContext, IFilesConfiguration, OpenEditorsVisibleContext } from 'vs/workbench/contrib/files/common/files';
|
||||
import { VIEWLET_ID, ExplorerViewletVisibleContext, IFilesConfiguration, OpenEditorsVisibleContext, VIEW_ID } from 'vs/workbench/contrib/files/common/files';
|
||||
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { ExplorerView } from 'vs/workbench/contrib/files/browser/views/explorerView';
|
||||
@@ -136,7 +136,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor
|
||||
|
||||
private createExplorerViewDescriptor(): IViewDescriptor {
|
||||
return {
|
||||
id: ExplorerView.ID,
|
||||
id: VIEW_ID,
|
||||
name: localize('folders', "Folders"),
|
||||
containerIcon: Codicon.files.classNames,
|
||||
ctorDescriptor: new SyncDescriptor(ExplorerView),
|
||||
@@ -191,7 +191,7 @@ export class ExplorerViewPaneContainer extends ViewPaneContainer {
|
||||
}
|
||||
|
||||
protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewPane {
|
||||
if (viewDescriptor.id === ExplorerView.ID) {
|
||||
if (viewDescriptor.id === VIEW_ID) {
|
||||
// Create a delegating editor service for the explorer to be able to delay the refresh in the opened
|
||||
// editors view above. This is a workaround for being able to double click on a file to make it pinned
|
||||
// without causing the animation in the opened editors view to kick in and change scroll position.
|
||||
@@ -232,7 +232,7 @@ export class ExplorerViewPaneContainer extends ViewPaneContainer {
|
||||
}
|
||||
|
||||
public getExplorerView(): ExplorerView {
|
||||
return <ExplorerView>this.getView(ExplorerView.ID);
|
||||
return <ExplorerView>this.getView(VIEW_ID);
|
||||
}
|
||||
|
||||
public getOpenEditorsView(): OpenEditorsView {
|
||||
@@ -245,7 +245,7 @@ export class ExplorerViewPaneContainer extends ViewPaneContainer {
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
const explorerView = this.getView(ExplorerView.ID);
|
||||
const explorerView = this.getView(VIEW_ID);
|
||||
if (explorerView?.isExpanded()) {
|
||||
explorerView.focus();
|
||||
} else {
|
||||
|
||||
@@ -5,17 +5,17 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ToggleAutoSaveAction, GlobalNewUntitledFileAction, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, openFilePreserveFocusHandler, DOWNLOAD_LABEL, ShowOpenedFileInNewWindow, ReopenResourcesAction, GlobalNewUntitledPlainFileAction } from 'vs/workbench/contrib/files/browser/fileActions';
|
||||
import { ToggleAutoSaveAction, GlobalNewUntitledFileAction, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, openFilePreserveFocusHandler, DOWNLOAD_LABEL, ShowOpenedFileInNewWindow, ReopenResourcesAction, ToggleEditorTypeCommand as ToggleEditorTypeAction, GlobalNewUntitledPlainFileAction } from 'vs/workbench/contrib/files/browser/fileActions';
|
||||
import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTION_CONTEXT } from 'vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler';
|
||||
import { SyncActionDescriptor, MenuId, MenuRegistry, ILocalizedString } from 'vs/platform/actions/common/actions';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { openWindowCommand, COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, DirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, newWindowCommand, ReadonlyEditorContext } from 'vs/workbench/contrib/files/browser/fileCommands';
|
||||
import { openWindowCommand, COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, DirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, newWindowCommand, ReadonlyEditorContext, OPEN_WITH_EXPLORER_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileCommands';
|
||||
import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
|
||||
import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext, ExplorerResourceCut, IExplorerService, ExplorerResourceMoveableToTrash, ExplorerViewletVisibleContext } from 'vs/workbench/contrib/files/common/files';
|
||||
import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext, ExplorerResourceCut, IExplorerService, ExplorerResourceMoveableToTrash, ExplorerViewletVisibleContext, ExplorerResourceAvailableEditorIdsContext } from 'vs/workbench/contrib/files/common/files';
|
||||
import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL } from 'vs/workbench/browser/actions/workspaceCommands';
|
||||
import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { AutoSaveAfterShortDelayContext } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
|
||||
@@ -26,7 +26,7 @@ import { WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys';
|
||||
import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { OpenFileFolderAction, OpenFileAction, OpenFolderAction, OpenWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions';
|
||||
import { ActiveEditorIsReadonlyContext, DirtyWorkingCopiesContext, ActiveEditorContext, ActiveEditorAvailableEditorsContext } from 'vs/workbench/common/editor';
|
||||
import { ActiveEditorIsReadonlyContext, DirtyWorkingCopiesContext, ActiveEditorContext, ActiveEditorAvailableEditorIdsContext } from 'vs/workbench/common/editor';
|
||||
import { SidebarFocusContext } from 'vs/workbench/common/viewlet';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
@@ -36,7 +36,8 @@ const category = { value: nls.localize('filesCategory', "File"), original: 'File
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(SaveAllAction, { primary: undefined, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_S }, win: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_S) } }), 'File: Save All', category.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(GlobalCompareResourcesAction), 'File: Compare Active File With...', category.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(ReopenResourcesAction), 'File: Reopen With...', category.value, ActiveEditorAvailableEditorsContext);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(ReopenResourcesAction), 'File: Reopen With...', category.value, ActiveEditorAvailableEditorIdsContext);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleEditorTypeAction), 'File: Toggle Editor Type', category.value, ActiveEditorAvailableEditorIdsContext);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusFilesExplorer), 'File: Focus on Files Explorer', category.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowActiveFileInExplorer), 'File: Reveal Active File in Side Bar', category.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(CollapseExplorerView), 'File: Collapse Folders in Explorer', category.value);
|
||||
@@ -193,7 +194,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
},
|
||||
group: '6_reopen',
|
||||
order: 20,
|
||||
when: ActiveEditorAvailableEditorsContext,
|
||||
when: ActiveEditorAvailableEditorIdsContext,
|
||||
});
|
||||
|
||||
// Editor Title Menu for Conflict Resolution
|
||||
@@ -436,6 +437,16 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ResourceContextKey.HasResource)
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
group: 'navigation',
|
||||
order: 20,
|
||||
command: {
|
||||
id: OPEN_WITH_EXPLORER_COMMAND_ID,
|
||||
title: nls.localize('explorerOpenWith', "Open With..."),
|
||||
},
|
||||
when: ContextKeyExpr.and(ExplorerRootContext.toNegated(), ExplorerResourceAvailableEditorIdsContext),
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
group: '3_compare',
|
||||
order: 20,
|
||||
|
||||
@@ -13,12 +13,12 @@ import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { VIEWLET_ID, IExplorerService, IFilesConfiguration, DEFAULT_EDITOR_ID } from 'vs/workbench/contrib/files/common/files';
|
||||
import { VIEWLET_ID, IExplorerService, IFilesConfiguration, VIEW_ID } from 'vs/workbench/contrib/files/common/files';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { toResource, SideBySideEditor, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { toResource, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { ExplorerViewPaneContainer } from 'vs/workbench/contrib/files/browser/explorerViewlet';
|
||||
import { IQuickInputService, ItemActivation, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IQuickInputService, ItemActivation } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
@@ -34,7 +34,7 @@ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IDialogService, IConfirmationResult, getFileNamesMessage, IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IEditorService, IOpenEditorOverrideHandler } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
import { CLOSE_EDITORS_AND_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
@@ -49,9 +49,9 @@ import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/commo
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { CustomEditorsAssociations, customEditorsAssociationsSettingId, CustomEditorAssociation } from 'vs/workbench/services/editor/browser/editorAssociationsSetting';
|
||||
import { IViewsService } from 'vs/workbench/common/views';
|
||||
import { openEditorWith, getAllAvailableEditors } from 'vs/workbench/contrib/files/common/openWith';
|
||||
|
||||
export const NEW_FILE_COMMAND_ID = 'explorer.newFile';
|
||||
export const NEW_FILE_LABEL = nls.localize('newFile', "New File");
|
||||
@@ -81,10 +81,10 @@ function onError(notificationService: INotificationService, error: any): void {
|
||||
notificationService.error(toErrorMessage(error, false));
|
||||
}
|
||||
|
||||
function refreshIfSeparator(value: string, explorerService: IExplorerService): void {
|
||||
async function refreshIfSeparator(value: string, explorerService: IExplorerService): Promise<void> {
|
||||
if (value && ((value.indexOf('/') >= 0) || (value.indexOf('\\') >= 0))) {
|
||||
// New input contains separator, multiple resources will get created workaround for #68204
|
||||
explorerService.refresh();
|
||||
await explorerService.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,15 +94,10 @@ export class NewFileAction extends Action {
|
||||
static readonly LABEL = nls.localize('createNewFile', "New File");
|
||||
|
||||
constructor(
|
||||
@IExplorerService explorerService: IExplorerService,
|
||||
@ICommandService private commandService: ICommandService
|
||||
) {
|
||||
super('explorer.newFile', NEW_FILE_LABEL);
|
||||
this.class = 'explorer-action ' + Codicon.newFile.classNames;
|
||||
this._register(explorerService.onDidChangeEditable(e => {
|
||||
const elementIsBeingEdited = explorerService.isEditable(e);
|
||||
this.enabled = !elementIsBeingEdited;
|
||||
}));
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
@@ -116,15 +111,10 @@ export class NewFolderAction extends Action {
|
||||
static readonly LABEL = nls.localize('createNewFolder', "New Folder");
|
||||
|
||||
constructor(
|
||||
@IExplorerService explorerService: IExplorerService,
|
||||
@ICommandService private commandService: ICommandService
|
||||
) {
|
||||
super('explorer.newFolder', NEW_FOLDER_LABEL);
|
||||
this.class = 'explorer-action ' + Codicon.newFolder.classNames;
|
||||
this._register(explorerService.onDidChangeEditable(e => {
|
||||
const elementIsBeingEdited = explorerService.isEditable(e);
|
||||
this.enabled = !elementIsBeingEdited;
|
||||
}));
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
@@ -507,7 +497,7 @@ export class GlobalCompareResourcesAction extends Action {
|
||||
|
||||
// Compare with next editor that opens
|
||||
const toDispose = this.editorService.overrideOpenEditor({
|
||||
getEditorOverrides: (editor: IEditorInput, options: IEditorOptions | undefined, group: IEditorGroup | undefined) => {
|
||||
getEditorOverrides: (resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined) => {
|
||||
return [];
|
||||
},
|
||||
open: editor => {
|
||||
@@ -542,7 +532,6 @@ export class GlobalCompareResourcesAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
const builtinProviderDisplayName = nls.localize('builtinProviderDisplayName', "Built-in");
|
||||
export class ReopenResourcesAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.files.action.reopenWithEditor';
|
||||
@@ -571,98 +560,44 @@ export class ReopenResourcesAction extends Action {
|
||||
|
||||
const options = activeEditorPane.options;
|
||||
const group = activeEditorPane.group;
|
||||
const activeResource = activeInput.resource;
|
||||
if (!activeResource) {
|
||||
await openEditorWith(activeInput, undefined, options, group, this.editorService, this.configurationService, this.quickInputService);
|
||||
}
|
||||
}
|
||||
|
||||
export class ToggleEditorTypeCommand extends Action {
|
||||
|
||||
static readonly ID = 'workbench.files.action.toggleEditorType';
|
||||
static readonly LABEL = nls.localize('workbench.files.action.toggleEditorType', "Toggle Editor Type");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
const activeEditorPane = this.editorService.activeEditorPane;
|
||||
if (!activeEditorPane) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resourceExt = extname(activeResource.path);
|
||||
|
||||
const overrides = this.editorService.getEditorOverrides(activeInput, options, group);
|
||||
const items: (IQuickPickItem & { handler?: IOpenEditorOverrideHandler })[] = overrides.map((override) => {
|
||||
return {
|
||||
handler: override[0],
|
||||
id: override[1].id,
|
||||
label: override[1].label,
|
||||
description: override[1].active ? 'Currently Active' : undefined,
|
||||
detail: override[1].detail,
|
||||
buttons: resourceExt ? [{
|
||||
iconClass: 'codicon-settings-gear',
|
||||
tooltip: nls.localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt)
|
||||
}] : undefined
|
||||
};
|
||||
});
|
||||
|
||||
if (!items.length) {
|
||||
const input = activeEditorPane.input;
|
||||
if (!input.resource) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!items.find(item => item.id === DEFAULT_EDITOR_ID)) {
|
||||
items.unshift({
|
||||
id: DEFAULT_EDITOR_ID,
|
||||
label: nls.localize('promptOpenWith.defaultEditor.displayName', "Text Editor"),
|
||||
description: activeInput instanceof FileEditorInput ? 'Currently Active' : undefined,
|
||||
detail: builtinProviderDisplayName,
|
||||
buttons: resourceExt ? [{
|
||||
iconClass: 'codicon-settings-gear',
|
||||
tooltip: nls.localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt)
|
||||
}] : undefined
|
||||
});
|
||||
}
|
||||
const options = activeEditorPane.options;
|
||||
const group = activeEditorPane.group;
|
||||
|
||||
const picker = this.quickInputService.createQuickPick<(IQuickPickItem & { handler?: IOpenEditorOverrideHandler })>();
|
||||
picker.items = items;
|
||||
picker.placeholder = nls.localize('promptOpenWith.placeHolder', "Select editor to use for '{0}'...", resources.basename(activeResource));
|
||||
|
||||
const pickedItem = await new Promise<(IQuickPickItem & { handler?: IOpenEditorOverrideHandler }) | undefined>(resolve => {
|
||||
picker.onDidAccept(() => {
|
||||
resolve(picker.selectedItems.length === 1 ? picker.selectedItems[0] : undefined);
|
||||
picker.dispose();
|
||||
});
|
||||
|
||||
picker.onDidTriggerItemButton(e => {
|
||||
const pick = e.item;
|
||||
const id = pick.id;
|
||||
resolve(pick); // open the view
|
||||
picker.dispose();
|
||||
|
||||
// And persist the setting
|
||||
if (pick && id) {
|
||||
const newAssociation: CustomEditorAssociation = { viewType: id, filenamePattern: '*' + resourceExt };
|
||||
const currentAssociations = [...this.configurationService.getValue<CustomEditorsAssociations>(customEditorsAssociationsSettingId)] || [];
|
||||
|
||||
// First try updating existing association
|
||||
for (let i = 0; i < currentAssociations.length; ++i) {
|
||||
const existing = currentAssociations[i];
|
||||
if (existing.filenamePattern === newAssociation.filenamePattern) {
|
||||
currentAssociations.splice(i, 1, newAssociation);
|
||||
this.configurationService.updateValue(customEditorsAssociationsSettingId, currentAssociations);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, create a new one
|
||||
currentAssociations.unshift(newAssociation);
|
||||
this.configurationService.updateValue(customEditorsAssociationsSettingId, currentAssociations);
|
||||
}
|
||||
});
|
||||
|
||||
picker.show();
|
||||
});
|
||||
|
||||
if (!pickedItem) {
|
||||
const overrides = getAllAvailableEditors(input.resource, options, group, this.editorService);
|
||||
const firstNonActiveOverride = overrides.find(([_, entry]) => !entry.active);
|
||||
if (!firstNonActiveOverride) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pickedItem.id === DEFAULT_EDITOR_ID) {
|
||||
const fileEditorInput = this.editorService.createEditorInput({ resource: activeResource!, forceFile: true });
|
||||
const textOptions = options ? { ...options, ignoreOverrides: true } : { ignoreOverrides: true };
|
||||
|
||||
await this.editorService.openEditor(fileEditorInput, textOptions, group);
|
||||
return;
|
||||
}
|
||||
|
||||
await pickedItem.handler!.open(activeInput!, options, group, pickedItem.id);
|
||||
await firstNonActiveOverride[0].open(input, options, group, firstNonActiveOverride[1].id)?.override;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,10 +757,6 @@ export class CollapseExplorerView extends Action {
|
||||
@IExplorerService readonly explorerService: IExplorerService
|
||||
) {
|
||||
super(id, label, 'explorer-action ' + Codicon.collapseAll.classNames);
|
||||
this._register(explorerService.onDidChangeEditable(e => {
|
||||
const elementIsBeingEdited = explorerService.isEditable(e);
|
||||
this.enabled = !elementIsBeingEdited;
|
||||
}));
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
@@ -833,6 +764,11 @@ export class CollapseExplorerView extends Action {
|
||||
const explorerView = explorerViewlet.getExplorerView();
|
||||
if (explorerView) {
|
||||
explorerView.collapseAll();
|
||||
// If there is something being edited via input box make sure to close it #96198
|
||||
const editable = this.explorerService.getEditable();
|
||||
if (editable) {
|
||||
await this.explorerService.setEditable(editable.stat, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -849,15 +785,11 @@ export class RefreshExplorerView extends Action {
|
||||
@IExplorerService private readonly explorerService: IExplorerService
|
||||
) {
|
||||
super(id, label, 'explorer-action ' + Codicon.refresh.classNames);
|
||||
this._register(explorerService.onDidChangeEditable(e => {
|
||||
const elementIsBeingEdited = explorerService.isEditable(e);
|
||||
this.enabled = !elementIsBeingEdited;
|
||||
}));
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
await this.viewletService.openViewlet(VIEWLET_ID);
|
||||
this.explorerService.refresh();
|
||||
await this.explorerService.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1045,10 +977,10 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole
|
||||
const fileService = accessor.get(IFileService);
|
||||
const textFileService = accessor.get(ITextFileService);
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const viewletService = accessor.get(IViewletService);
|
||||
const viewsService = accessor.get(IViewsService);
|
||||
const notificationService = accessor.get(INotificationService);
|
||||
|
||||
await viewletService.openViewlet(VIEWLET_ID, true);
|
||||
await viewsService.openView(VIEW_ID, true);
|
||||
|
||||
const stats = explorerService.getContext(false);
|
||||
const stat = stats.length > 0 ? stats[0] : undefined;
|
||||
@@ -1064,15 +996,12 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole
|
||||
}
|
||||
|
||||
const newStat = new NewExplorerItem(fileService, folder, isFolder);
|
||||
const sortOrder = explorerService.sortOrder;
|
||||
await folder.fetchChildren(sortOrder);
|
||||
|
||||
folder.addChild(newStat);
|
||||
|
||||
const onSuccess = async (value: string): Promise<void> => {
|
||||
try {
|
||||
const created = isFolder ? await fileService.createFolder(resources.joinPath(folder.resource, value)) : await textFileService.create(resources.joinPath(folder.resource, value));
|
||||
refreshIfSeparator(value, explorerService);
|
||||
await refreshIfSeparator(value, explorerService);
|
||||
|
||||
isFolder ?
|
||||
await explorerService.select(created.resource, true) :
|
||||
@@ -1082,11 +1011,11 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole
|
||||
}
|
||||
};
|
||||
|
||||
explorerService.setEditable(newStat, {
|
||||
await explorerService.setEditable(newStat, {
|
||||
validationMessage: value => validateFileName(newStat, value),
|
||||
onFinish: (value, success) => {
|
||||
onFinish: async (value, success) => {
|
||||
folder.removeChild(newStat);
|
||||
explorerService.setEditable(newStat, null);
|
||||
await explorerService.setEditable(newStat, null);
|
||||
if (success) {
|
||||
onSuccess(value);
|
||||
}
|
||||
@@ -1108,7 +1037,7 @@ CommandsRegistry.registerCommand({
|
||||
}
|
||||
});
|
||||
|
||||
export const renameHandler = (accessor: ServicesAccessor) => {
|
||||
export const renameHandler = async (accessor: ServicesAccessor) => {
|
||||
const explorerService = accessor.get(IExplorerService);
|
||||
const workingCopyFileService = accessor.get(IWorkingCopyFileService);
|
||||
const notificationService = accessor.get(INotificationService);
|
||||
@@ -1119,7 +1048,7 @@ export const renameHandler = (accessor: ServicesAccessor) => {
|
||||
return;
|
||||
}
|
||||
|
||||
explorerService.setEditable(stat, {
|
||||
await explorerService.setEditable(stat, {
|
||||
validationMessage: value => validateFileName(stat, value),
|
||||
onFinish: async (value, success) => {
|
||||
if (success) {
|
||||
@@ -1128,13 +1057,13 @@ export const renameHandler = (accessor: ServicesAccessor) => {
|
||||
if (stat.resource.toString() !== targetResource.toString()) {
|
||||
try {
|
||||
await workingCopyFileService.move(stat.resource, targetResource);
|
||||
refreshIfSeparator(value, explorerService);
|
||||
await refreshIfSeparator(value, explorerService);
|
||||
} catch (e) {
|
||||
notificationService.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
explorerService.setEditable(stat, null);
|
||||
await explorerService.setEditable(stat, null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -40,12 +40,16 @@ import { coalesce } from 'vs/base/common/arrays';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { openEditorWith } from 'vs/workbench/contrib/files/common/openWith';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
|
||||
// Commands
|
||||
|
||||
export const REVEAL_IN_EXPLORER_COMMAND_ID = 'revealInExplorer';
|
||||
export const REVERT_FILE_COMMAND_ID = 'workbench.action.files.revert';
|
||||
export const OPEN_TO_SIDE_COMMAND_ID = 'explorer.openToSide';
|
||||
export const OPEN_WITH_EXPLORER_COMMAND_ID = 'explorer.openWith';
|
||||
export const SELECT_FOR_COMPARE_COMMAND_ID = 'selectForCompare';
|
||||
|
||||
export const COMPARE_SELECTED_COMMAND_ID = 'compareSelected';
|
||||
@@ -313,6 +317,22 @@ CommandsRegistry.registerCommand({
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: OPEN_WITH_EXPLORER_COMMAND_ID,
|
||||
handler: async (accessor, resource: URI | object) => {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorGroupsService = accessor.get(IEditorGroupsService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
|
||||
const uri = getResourceForCommand(resource, accessor.get(IListService), accessor.get(IEditorService));
|
||||
if (uri) {
|
||||
const input = editorService.createEditorInput({ resource: uri });
|
||||
openEditorWith(input, undefined, undefined, editorGroupsService.activeGroup, editorService, configurationService, quickInputService);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Save / Save As / Save All / Revert
|
||||
|
||||
async function saveSelectedEditors(accessor: ServicesAccessor, options?: ISaveEditorsOptions): Promise<void> {
|
||||
|
||||
@@ -321,6 +321,11 @@ configurationRegistry.registerConfiguration({
|
||||
'markdownDescription': nls.localize('maxMemoryForLargeFilesMB', "Controls the memory available to Azure Data Studio after restart when trying to open large files. Same effect as specifying `--max-memory=NEWSIZE` on the command line."), // {{SQL CARBON EDIT}} Change product name to ADS
|
||||
included: platform.isNative
|
||||
},
|
||||
'files.maxMemoryForClosedFilesUndoStackMB': {
|
||||
'type': 'number',
|
||||
'default': 20,
|
||||
'markdownDescription': nls.localize('maxMemoryForClosedFilesUndoStackMB', "Controls the maximum ammount of memory the undo stack should hold for files that have been closed.")
|
||||
},
|
||||
'files.saveConflictResolution': {
|
||||
'type': 'string',
|
||||
'enum': [
|
||||
|
||||
@@ -56,11 +56,6 @@ export class ExplorerDecorationsProvider implements IDecorationsProvider {
|
||||
this.toDispose.add(contextService.onDidChangeWorkspaceFolders(e => {
|
||||
this._onDidChange.fire(e.changed.concat(e.added).map(wf => wf.uri));
|
||||
}));
|
||||
this.toDispose.add(explorerService.onDidChangeItem(change => {
|
||||
if (change.item) {
|
||||
this._onDidChange.fire([change.item.resource]);
|
||||
}
|
||||
}));
|
||||
this.toDispose.add(explorerRootErrorEmitter.event((resource => {
|
||||
this._onDidChange.fire([resource]);
|
||||
})));
|
||||
|
||||
@@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
import * as perf from 'vs/base/common/performance';
|
||||
import { IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService, ExplorerResourceCut, ExplorerResourceMoveableToTrash, ExplorerCompressedFocusContext, ExplorerCompressedFirstFocusContext, ExplorerCompressedLastFocusContext } from 'vs/workbench/contrib/files/common/files';
|
||||
import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService, ExplorerResourceCut, ExplorerResourceMoveableToTrash, ExplorerCompressedFocusContext, ExplorerCompressedFirstFocusContext, ExplorerCompressedLastFocusContext, ExplorerResourceAvailableEditorIdsContext } from 'vs/workbench/contrib/files/common/files';
|
||||
import { NewFolderAction, NewFileAction, FileCopiedContext, RefreshExplorerView, CollapseExplorerView } from 'vs/workbench/contrib/files/browser/fileActions';
|
||||
import { toResource, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
@@ -123,7 +123,6 @@ export function getContext(focus: ExplorerItem[], selection: ExplorerItem[], res
|
||||
}
|
||||
|
||||
export class ExplorerView extends ViewPane {
|
||||
static readonly ID: string = 'workbench.explorer.fileView';
|
||||
static readonly TREE_VIEW_STATE_STORAGE_KEY: string = 'workbench.explorer.treeViewState';
|
||||
|
||||
private tree!: WorkbenchCompressibleAsyncDataTree<ExplorerItem | ExplorerItem[], ExplorerItem, FuzzyScore>;
|
||||
@@ -132,12 +131,15 @@ export class ExplorerView extends ViewPane {
|
||||
private resourceContext: ResourceContextKey;
|
||||
private folderContext: IContextKey<boolean>;
|
||||
private readonlyContext: IContextKey<boolean>;
|
||||
private availableEditorIdsContext: IContextKey<string>;
|
||||
|
||||
private rootContext: IContextKey<boolean>;
|
||||
private resourceMoveableToTrash: IContextKey<boolean>;
|
||||
|
||||
private renderer!: FilesRenderer;
|
||||
|
||||
private styleElement!: HTMLStyleElement;
|
||||
private treeContainer!: HTMLElement;
|
||||
private compressedFocusContext: IContextKey<boolean>;
|
||||
private compressedFocusFirstContext: IContextKey<boolean>;
|
||||
private compressedFocusLastContext: IContextKey<boolean>;
|
||||
@@ -179,13 +181,14 @@ export class ExplorerView extends ViewPane {
|
||||
|
||||
this.folderContext = ExplorerFolderContext.bindTo(contextKeyService);
|
||||
this.readonlyContext = ExplorerResourceReadonlyContext.bindTo(contextKeyService);
|
||||
this.availableEditorIdsContext = ExplorerResourceAvailableEditorIdsContext.bindTo(contextKeyService);
|
||||
this.rootContext = ExplorerRootContext.bindTo(contextKeyService);
|
||||
this.resourceMoveableToTrash = ExplorerResourceMoveableToTrash.bindTo(contextKeyService);
|
||||
this.compressedFocusContext = ExplorerCompressedFocusContext.bindTo(contextKeyService);
|
||||
this.compressedFocusFirstContext = ExplorerCompressedFirstFocusContext.bindTo(contextKeyService);
|
||||
this.compressedFocusLastContext = ExplorerCompressedLastFocusContext.bindTo(contextKeyService);
|
||||
|
||||
this.explorerService.registerContextProvider(this);
|
||||
this.explorerService.registerView(this);
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
@@ -245,48 +248,17 @@ export class ExplorerView extends ViewPane {
|
||||
renderBody(container: HTMLElement): void {
|
||||
super.renderBody(container);
|
||||
|
||||
const treeContainer = DOM.append(container, DOM.$('.explorer-folders-view'));
|
||||
this.treeContainer = DOM.append(container, DOM.$('.explorer-folders-view'));
|
||||
|
||||
this.styleElement = DOM.createStyleSheet(treeContainer);
|
||||
this.styleElement = DOM.createStyleSheet(this.treeContainer);
|
||||
attachStyler<IExplorerViewColors>(this.themeService, { listDropBackground }, this.styleListDropBackground.bind(this));
|
||||
|
||||
this.createTree(treeContainer);
|
||||
this.createTree(this.treeContainer);
|
||||
|
||||
this._register(this.labelService.onDidChangeFormatters(() => {
|
||||
this._onDidChangeTitleArea.fire();
|
||||
}));
|
||||
|
||||
this._register(this.explorerService.onDidChangeRoots(() => this.setTreeInput()));
|
||||
this._register(this.explorerService.onDidChangeItem(e => {
|
||||
if (this.explorerService.isEditable(undefined)) {
|
||||
this.tree.domFocus();
|
||||
}
|
||||
this.refresh(e.recursive, e.item);
|
||||
}));
|
||||
this._register(this.explorerService.onDidChangeEditable(async e => {
|
||||
const isEditing = !!this.explorerService.getEditableData(e);
|
||||
|
||||
if (isEditing) {
|
||||
if (e.parent !== this.tree.getInput()) {
|
||||
await this.tree.expand(e.parent!);
|
||||
this.tree.reveal(e.parent!);
|
||||
}
|
||||
} else {
|
||||
DOM.removeClass(treeContainer, 'highlight');
|
||||
}
|
||||
|
||||
await this.refresh(false, e.parent);
|
||||
|
||||
if (isEditing) {
|
||||
DOM.addClass(treeContainer, 'highlight');
|
||||
this.tree.reveal(e);
|
||||
} else {
|
||||
this.tree.domFocus();
|
||||
}
|
||||
}));
|
||||
this._register(this.explorerService.onDidSelectResource(e => this.onSelectResource(e.resource, e.reveal)));
|
||||
this._register(this.explorerService.onDidCopyItems(e => this.onCopyItems(e.items, e.cut, e.previouslyCutItems)));
|
||||
|
||||
// Update configuration
|
||||
const configuration = this.configurationService.getValue<IFilesConfiguration>();
|
||||
this.onConfigurationUpdated(configuration);
|
||||
@@ -338,6 +310,29 @@ export class ExplorerView extends ViewPane {
|
||||
return getContext(this.tree.getFocus(), this.tree.getSelection(), respectMultiSelection, this.renderer);
|
||||
}
|
||||
|
||||
async setEditable(stat: ExplorerItem, isEditing: boolean): Promise<void> {
|
||||
let shouldRefresh = true;
|
||||
if (isEditing) {
|
||||
if (stat.parent && stat.parent !== this.tree.getInput()) {
|
||||
shouldRefresh = stat.parent.isDirectoryResolved;
|
||||
await this.tree.expand(stat.parent);
|
||||
}
|
||||
} else {
|
||||
DOM.removeClass(this.treeContainer, 'highlight');
|
||||
}
|
||||
|
||||
if (shouldRefresh) {
|
||||
await this.refresh(false, stat.parent);
|
||||
}
|
||||
|
||||
if (isEditing) {
|
||||
DOM.addClass(this.treeContainer, 'highlight');
|
||||
this.tree.reveal(stat);
|
||||
} else {
|
||||
this.tree.domFocus();
|
||||
}
|
||||
}
|
||||
|
||||
private selectActiveFile(deselect?: boolean, reveal = this.autoReveal): void {
|
||||
if (this.autoReveal) {
|
||||
const activeFile = this.getActiveFile();
|
||||
@@ -443,10 +438,10 @@ export class ExplorerView extends ViewPane {
|
||||
|
||||
this._register(this.tree.onContextMenu(e => this.onContextMenu(e)));
|
||||
|
||||
this._register(this.tree.onDidScroll(e => {
|
||||
this._register(this.tree.onDidScroll(async e => {
|
||||
let editable = this.explorerService.getEditable();
|
||||
if (e.scrollTopChanged && editable && this.tree.getRelativeTop(editable.stat) === null) {
|
||||
editable.data.onFinish('', false);
|
||||
await editable.data.onFinish('', false);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -474,6 +469,13 @@ export class ExplorerView extends ViewPane {
|
||||
this.folderContext.set((isSingleFolder && !stat) || !!stat && stat.isDirectory);
|
||||
this.readonlyContext.set(!!stat && stat.isReadonly);
|
||||
this.rootContext.set(!stat || (stat && stat.isRoot));
|
||||
|
||||
if (resource) {
|
||||
const overrides = resource ? this.editorService.getEditorOverrides(resource, undefined, undefined) : [];
|
||||
this.availableEditorIdsContext.set(overrides.map(([, entry]) => entry.id).join(','));
|
||||
} else {
|
||||
this.availableEditorIdsContext.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private onContextMenu(e: ITreeContextMenuEvent<ExplorerItem>): void {
|
||||
@@ -556,19 +558,18 @@ export class ExplorerView extends ViewPane {
|
||||
* Refresh the contents of the explorer to get up to date data from the disk about the file structure.
|
||||
* If the item is passed we refresh only that level of the tree, otherwise we do a full refresh.
|
||||
*/
|
||||
private refresh(recursive: boolean, item?: ExplorerItem): Promise<void> {
|
||||
if (!this.tree || !this.isBodyVisible()) {
|
||||
refresh(recursive: boolean, item?: ExplorerItem): Promise<void> {
|
||||
if (!this.tree || !this.isBodyVisible() || (item && !this.tree.hasNode(item))) {
|
||||
// Tree node doesn't exist yet
|
||||
this.shouldRefresh = true;
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
// Tree node doesn't exist yet
|
||||
if (item && !this.tree.hasNode(item)) {
|
||||
return Promise.resolve(undefined);
|
||||
if (this.explorerService.isEditable(undefined)) {
|
||||
this.tree.domFocus();
|
||||
}
|
||||
|
||||
const toRefresh = item || this.tree.getInput();
|
||||
|
||||
return this.tree.updateChildren(toRefresh, recursive);
|
||||
}
|
||||
|
||||
@@ -579,7 +580,7 @@ export class ExplorerView extends ViewPane {
|
||||
return DOM.getLargestChildWidth(parentNode, childNodes);
|
||||
}
|
||||
|
||||
private async setTreeInput(): Promise<void> {
|
||||
async setTreeInput(): Promise<void> {
|
||||
if (!this.isBodyVisible()) {
|
||||
this.shouldRefresh = true;
|
||||
return Promise.resolve(undefined);
|
||||
@@ -647,7 +648,7 @@ export class ExplorerView extends ViewPane {
|
||||
return withNullAsUndefined(toResource(input, { supportSideBySide: SideBySideEditor.MASTER }));
|
||||
}
|
||||
|
||||
private async onSelectResource(resource: URI | undefined, reveal = this.autoReveal, retry = 0): Promise<void> {
|
||||
public async selectResource(resource: URI | undefined, reveal = this.autoReveal, retry = 0): Promise<void> {
|
||||
// do no retry more than once to prevent inifinite loops in cases of inconsistent model
|
||||
if (retry === 2) {
|
||||
return;
|
||||
@@ -664,10 +665,11 @@ export class ExplorerView extends ViewPane {
|
||||
.sort((first, second) => second.resource.path.length - first.resource.path.length)[0];
|
||||
|
||||
while (item && item.resource.toString() !== resource.toString()) {
|
||||
if (item.isDisposed) {
|
||||
return this.onSelectResource(resource, reveal, retry + 1);
|
||||
try {
|
||||
await this.tree.expand(item);
|
||||
} catch (e) {
|
||||
return this.selectResource(resource, reveal, retry + 1);
|
||||
}
|
||||
await this.tree.expand(item);
|
||||
item = first(values(item.children), i => isEqualOrParent(resource, i.resource, ignoreCase));
|
||||
}
|
||||
|
||||
@@ -680,10 +682,6 @@ export class ExplorerView extends ViewPane {
|
||||
|
||||
try {
|
||||
if (reveal) {
|
||||
if (item.isDisposed) {
|
||||
return this.onSelectResource(resource, reveal, retry + 1);
|
||||
}
|
||||
|
||||
// Don't scroll to the item if it's already visible
|
||||
if (this.tree.getRelativeTop(item) === null) {
|
||||
this.tree.reveal(item, 0.5);
|
||||
@@ -693,12 +691,13 @@ export class ExplorerView extends ViewPane {
|
||||
this.tree.setFocus([item]);
|
||||
this.tree.setSelection([item]);
|
||||
} catch (e) {
|
||||
// Element might not be in the tree, silently fail
|
||||
// Element might not be in the tree, try again and silently fail
|
||||
return this.selectResource(resource, reveal, retry + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onCopyItems(stats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void {
|
||||
itemsCopied(stats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void {
|
||||
this.fileCopiedContextKey.set(stats.length > 0);
|
||||
this.resourceCutContextKey.set(cut && stats.length > 0);
|
||||
if (previousCut) {
|
||||
|
||||
@@ -445,12 +445,15 @@ export class FilesRenderer implements ICompressibleTreeRenderer<ExplorerItem, Fu
|
||||
DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.KEY_UP, (e: IKeyboardEvent) => {
|
||||
showInputBoxNotification();
|
||||
}),
|
||||
DOM.addDisposableListener(inputBox.inputElement, DOM.EventType.BLUR, () => {
|
||||
done(inputBox.isInputValid(), true);
|
||||
}),
|
||||
label,
|
||||
styler
|
||||
];
|
||||
setTimeout(() => {
|
||||
// Do not react immediatly on blur events due to tree refresh potentially causing an early blur #96566
|
||||
toDispose.push(DOM.addDisposableListener(inputBox.inputElement, DOM.EventType.BLUR, () => {
|
||||
done(inputBox.isInputValid(), true);
|
||||
}));
|
||||
}, 100);
|
||||
|
||||
return toDisposable(() => {
|
||||
done(false, false);
|
||||
|
||||
@@ -55,10 +55,12 @@ export class DirtyFilesIndicator extends Disposable implements IWorkbenchContrib
|
||||
|
||||
// Indicate dirty count in badge if any
|
||||
if (dirtyCount > 0) {
|
||||
this.badgeHandle.value = this.activityService.showActivity(
|
||||
this.badgeHandle.value = this.activityService.showViewContainerActivity(
|
||||
VIEWLET_ID,
|
||||
new NumberBadge(dirtyCount, num => num === 1 ? nls.localize('dirtyFile', "1 unsaved file") : nls.localize('dirtyFiles', "{0} unsaved files", dirtyCount)),
|
||||
'explorer-viewlet-label'
|
||||
{
|
||||
badge: new NumberBadge(dirtyCount, num => num === 1 ? nls.localize('dirtyFile', "1 unsaved file") : nls.localize('dirtyFiles', "{0} unsaved files", dirtyCount)),
|
||||
clazz: 'explorer-viewlet-label'
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.badgeHandle.clear();
|
||||
|
||||
@@ -78,7 +78,6 @@ export class ExplorerModel implements IDisposable {
|
||||
|
||||
export class ExplorerItem {
|
||||
protected _isDirectoryResolved: boolean;
|
||||
private _isDisposed: boolean;
|
||||
public isError = false;
|
||||
private _isExcluded = false;
|
||||
|
||||
@@ -93,7 +92,6 @@ export class ExplorerItem {
|
||||
private _unknown = false
|
||||
) {
|
||||
this._isDirectoryResolved = false;
|
||||
this._isDisposed = false;
|
||||
}
|
||||
|
||||
get isExcluded(): boolean {
|
||||
@@ -111,10 +109,6 @@ export class ExplorerItem {
|
||||
this._isExcluded = value;
|
||||
}
|
||||
|
||||
get isDisposed(): boolean {
|
||||
return this._isDisposed;
|
||||
}
|
||||
|
||||
get isDirectoryResolved(): boolean {
|
||||
return this._isDirectoryResolved;
|
||||
}
|
||||
@@ -258,9 +252,11 @@ export class ExplorerItem {
|
||||
}
|
||||
});
|
||||
|
||||
for (let child of oldLocalChildren.values()) {
|
||||
child._dispose();
|
||||
}
|
||||
oldLocalChildren.forEach(oldChild => {
|
||||
if (oldChild instanceof NewExplorerItem) {
|
||||
local.addChild(oldChild);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,20 +306,10 @@ export class ExplorerItem {
|
||||
}
|
||||
|
||||
forgetChildren(): void {
|
||||
for (let c of this.children.values()) {
|
||||
c._dispose();
|
||||
}
|
||||
this.children.clear();
|
||||
this._isDirectoryResolved = false;
|
||||
}
|
||||
|
||||
private _dispose() {
|
||||
this._isDisposed = true;
|
||||
for (let child of this.children.values()) {
|
||||
child._dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private getPlatformAwareName(name: string): string {
|
||||
return this.fileService.hasCapability(this.resource, FileSystemProviderCapabilities.PathCaseSensitive) ? name : name.toLowerCase();
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IExplorerService, IFilesConfiguration, SortOrder, IContextProvider } from 'vs/workbench/contrib/files/common/files';
|
||||
import { IExplorerService, IFilesConfiguration, SortOrder, IExplorerView } from 'vs/workbench/contrib/files/common/files';
|
||||
import { ExplorerItem, ExplorerModel } from 'vs/workbench/contrib/files/common/explorerModel';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { FileOperationEvent, FileOperation, IFileStat, IFileService, FileChangesEvent, FILES_EXCLUDE_CONFIG, FileChangeType, IResolveFileOptions } from 'vs/platform/files/common/files';
|
||||
import { FileOperationEvent, FileOperation, IFileService, FileChangesEvent, FILES_EXCLUDE_CONFIG, FileChangeType, IResolveFileOptions } from 'vs/platform/files/common/files';
|
||||
import { dirname } from 'vs/base/common/resources';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { ResourceGlobMatcher } from 'vs/workbench/common/resources';
|
||||
@@ -32,16 +32,11 @@ export class ExplorerService implements IExplorerService {
|
||||
|
||||
private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first
|
||||
|
||||
private readonly _onDidChangeRoots = new Emitter<void>();
|
||||
private readonly _onDidChangeItem = new Emitter<{ item?: ExplorerItem, recursive: boolean }>();
|
||||
private readonly _onDidChangeEditable = new Emitter<ExplorerItem>();
|
||||
private readonly _onDidSelectResource = new Emitter<{ resource?: URI, reveal?: boolean }>();
|
||||
private readonly _onDidCopyItems = new Emitter<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>();
|
||||
private readonly disposables = new DisposableStore();
|
||||
private editable: { stat: ExplorerItem, data: IEditableData } | undefined;
|
||||
private _sortOrder: SortOrder;
|
||||
private cutItems: ExplorerItem[] | undefined;
|
||||
private contextProvider: IContextProvider | undefined;
|
||||
private view: IExplorerView | undefined;
|
||||
private model: ExplorerModel;
|
||||
|
||||
constructor(
|
||||
@@ -59,7 +54,7 @@ export class ExplorerService implements IExplorerService {
|
||||
this.disposables.add(this.fileService.onDidRunOperation(e => this.onDidRunOperation(e)));
|
||||
this.disposables.add(this.fileService.onDidFilesChange(e => this.onDidFilesChange(e)));
|
||||
this.disposables.add(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue<IFilesConfiguration>())));
|
||||
this.disposables.add(Event.any<{ scheme: string }>(this.fileService.onDidChangeFileSystemProviderRegistrations, this.fileService.onDidChangeFileSystemProviderCapabilities)(e => {
|
||||
this.disposables.add(Event.any<{ scheme: string }>(this.fileService.onDidChangeFileSystemProviderRegistrations, this.fileService.onDidChangeFileSystemProviderCapabilities)(async e => {
|
||||
let affected = false;
|
||||
this.model.roots.forEach(r => {
|
||||
if (r.resource.scheme === e.scheme) {
|
||||
@@ -68,50 +63,35 @@ export class ExplorerService implements IExplorerService {
|
||||
}
|
||||
});
|
||||
if (affected) {
|
||||
this._onDidChangeItem.fire({ recursive: true });
|
||||
if (this.view) {
|
||||
await this.view.refresh(true);
|
||||
}
|
||||
}
|
||||
}));
|
||||
this.disposables.add(this.model.onDidChangeRoots(() => {
|
||||
if (this.view) {
|
||||
this.view.setTreeInput();
|
||||
}
|
||||
}));
|
||||
this.disposables.add(this.model.onDidChangeRoots(() => this._onDidChangeRoots.fire()));
|
||||
}
|
||||
|
||||
get roots(): ExplorerItem[] {
|
||||
return this.model.roots;
|
||||
}
|
||||
|
||||
get onDidChangeRoots(): Event<void> {
|
||||
return this._onDidChangeRoots.event;
|
||||
}
|
||||
|
||||
get onDidChangeItem(): Event<{ item?: ExplorerItem, recursive: boolean }> {
|
||||
return this._onDidChangeItem.event;
|
||||
}
|
||||
|
||||
get onDidChangeEditable(): Event<ExplorerItem> {
|
||||
return this._onDidChangeEditable.event;
|
||||
}
|
||||
|
||||
get onDidSelectResource(): Event<{ resource?: URI, reveal?: boolean }> {
|
||||
return this._onDidSelectResource.event;
|
||||
}
|
||||
|
||||
get onDidCopyItems(): Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }> {
|
||||
return this._onDidCopyItems.event;
|
||||
}
|
||||
|
||||
get sortOrder(): SortOrder {
|
||||
return this._sortOrder;
|
||||
}
|
||||
|
||||
registerContextProvider(contextProvider: IContextProvider): void {
|
||||
this.contextProvider = contextProvider;
|
||||
registerView(contextProvider: IExplorerView): void {
|
||||
this.view = contextProvider;
|
||||
}
|
||||
|
||||
getContext(respectMultiSelection: boolean): ExplorerItem[] {
|
||||
if (!this.contextProvider) {
|
||||
if (!this.view) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.contextProvider.getContext(respectMultiSelection);
|
||||
return this.view.getContext(respectMultiSelection);
|
||||
}
|
||||
|
||||
// Memoized locals
|
||||
@@ -132,13 +112,18 @@ export class ExplorerService implements IExplorerService {
|
||||
return this.model.findClosest(resource);
|
||||
}
|
||||
|
||||
setEditable(stat: ExplorerItem, data: IEditableData | null): void {
|
||||
async setEditable(stat: ExplorerItem, data: IEditableData | null): Promise<void> {
|
||||
if (!this.view) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
this.editable = undefined;
|
||||
} else {
|
||||
this.editable = { stat, data };
|
||||
}
|
||||
this._onDidChangeEditable.fire(stat);
|
||||
const isEditing = this.isEditable(stat);
|
||||
await this.view.setEditable(stat, isEditing);
|
||||
}
|
||||
|
||||
setToCopy(items: ExplorerItem[], cut: boolean): void {
|
||||
@@ -146,7 +131,7 @@ export class ExplorerService implements IExplorerService {
|
||||
this.cutItems = cut ? items : undefined;
|
||||
this.clipboardService.writeResources(items.map(s => s.resource));
|
||||
|
||||
this._onDidCopyItems.fire({ items, cut, previouslyCutItems });
|
||||
this.view?.itemsCopied(items, cut, previouslyCutItems);
|
||||
}
|
||||
|
||||
isCut(item: ExplorerItem): boolean {
|
||||
@@ -166,9 +151,12 @@ export class ExplorerService implements IExplorerService {
|
||||
}
|
||||
|
||||
async select(resource: URI, reveal?: boolean): Promise<void> {
|
||||
if (!this.view) {
|
||||
return;
|
||||
}
|
||||
const fileStat = this.findClosest(resource);
|
||||
if (fileStat) {
|
||||
this._onDidSelectResource.fire({ resource: fileStat.resource, reveal });
|
||||
await this.view.selectResource(fileStat.resource, reveal);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
@@ -190,31 +178,33 @@ export class ExplorerService implements IExplorerService {
|
||||
// Update Input with disk Stat
|
||||
ExplorerItem.mergeLocalWithDisk(modelStat, root);
|
||||
const item = root.find(resource);
|
||||
this._onDidChangeItem.fire({ item: root, recursive: true });
|
||||
await this.view.refresh(true, root);
|
||||
|
||||
// Select and Reveal
|
||||
this._onDidSelectResource.fire({ resource: item ? item.resource : undefined, reveal });
|
||||
await this.view.selectResource(item ? item.resource : undefined, reveal);
|
||||
} catch (error) {
|
||||
root.isError = true;
|
||||
this._onDidChangeItem.fire({ item: root, recursive: false });
|
||||
await this.view.refresh(false, root);
|
||||
}
|
||||
}
|
||||
|
||||
refresh(): void {
|
||||
async refresh(reveal = true): Promise<void> {
|
||||
this.model.roots.forEach(r => r.forgetChildren());
|
||||
this._onDidChangeItem.fire({ recursive: true });
|
||||
const resource = this.editorService.activeEditor ? this.editorService.activeEditor.resource : undefined;
|
||||
const autoReveal = this.configurationService.getValue<IFilesConfiguration>().explorer.autoReveal;
|
||||
if (this.view) {
|
||||
await this.view.refresh(true);
|
||||
const resource = this.editorService.activeEditor ? this.editorService.activeEditor.resource : undefined;
|
||||
const autoReveal = this.configurationService.getValue<IFilesConfiguration>().explorer.autoReveal;
|
||||
|
||||
if (resource && autoReveal) {
|
||||
// We did a top level refresh, reveal the active file #67118
|
||||
this.select(resource, true);
|
||||
if (reveal && resource && autoReveal) {
|
||||
// We did a top level refresh, reveal the active file #67118
|
||||
this.select(resource, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// File events
|
||||
|
||||
private onDidRunOperation(e: FileOperationEvent): void {
|
||||
private async onDidRunOperation(e: FileOperationEvent): Promise<void> {
|
||||
// Add
|
||||
if (e.isOperation(FileOperation.CREATE) || e.isOperation(FileOperation.COPY)) {
|
||||
const addedElement = e.target;
|
||||
@@ -224,23 +214,23 @@ export class ExplorerService implements IExplorerService {
|
||||
if (parents.length) {
|
||||
|
||||
// Add the new file to its parent (Model)
|
||||
parents.forEach(p => {
|
||||
parents.forEach(async p => {
|
||||
// We have to check if the parent is resolved #29177
|
||||
const resolveMetadata = this.sortOrder === `modified`;
|
||||
const thenable: Promise<IFileStat | undefined> = p.isDirectoryResolved ? Promise.resolve(undefined) : this.fileService.resolve(p.resource, { resolveMetadata });
|
||||
thenable.then(stat => {
|
||||
if (!p.isDirectoryResolved) {
|
||||
const stat = await this.fileService.resolve(p.resource, { resolveMetadata });
|
||||
if (stat) {
|
||||
const modelStat = ExplorerItem.create(this.fileService, stat, p.parent);
|
||||
ExplorerItem.mergeLocalWithDisk(modelStat, p);
|
||||
}
|
||||
}
|
||||
|
||||
const childElement = ExplorerItem.create(this.fileService, addedElement, p.parent);
|
||||
// Make sure to remove any previous version of the file if any
|
||||
p.removeChild(childElement);
|
||||
p.addChild(childElement);
|
||||
// Refresh the Parent (View)
|
||||
this._onDidChangeItem.fire({ item: p, recursive: false });
|
||||
});
|
||||
const childElement = ExplorerItem.create(this.fileService, addedElement, p.parent);
|
||||
// Make sure to remove any previous version of the file if any
|
||||
p.removeChild(childElement);
|
||||
p.addChild(childElement);
|
||||
// Refresh the Parent (View)
|
||||
await this.view?.refresh(false, p);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -255,10 +245,10 @@ export class ExplorerService implements IExplorerService {
|
||||
// Handle Rename
|
||||
if (oldParentResource.toString() === newParentResource.toString()) {
|
||||
const modelElements = this.model.findAll(oldResource);
|
||||
modelElements.forEach(modelElement => {
|
||||
modelElements.forEach(async modelElement => {
|
||||
// Rename File (Model)
|
||||
modelElement.rename(newElement);
|
||||
this._onDidChangeItem.fire({ item: modelElement.parent, recursive: false });
|
||||
await this.view?.refresh(false, modelElement.parent);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -269,11 +259,11 @@ export class ExplorerService implements IExplorerService {
|
||||
|
||||
if (newParents.length && modelElements.length) {
|
||||
// Move in Model
|
||||
modelElements.forEach((modelElement, index) => {
|
||||
modelElements.forEach(async (modelElement, index) => {
|
||||
const oldParent = modelElement.parent;
|
||||
modelElement.move(newParents[index]);
|
||||
this._onDidChangeItem.fire({ item: oldParent, recursive: false });
|
||||
this._onDidChangeItem.fire({ item: newParents[index], recursive: false });
|
||||
await this.view?.refresh(false, oldParent);
|
||||
await this.view?.refresh(false, newParents[index]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -282,13 +272,13 @@ export class ExplorerService implements IExplorerService {
|
||||
// Delete
|
||||
else if (e.isOperation(FileOperation.DELETE)) {
|
||||
const modelElements = this.model.findAll(e.resource);
|
||||
modelElements.forEach(element => {
|
||||
modelElements.forEach(async element => {
|
||||
if (element.parent) {
|
||||
const parent = element.parent;
|
||||
// Remove Element from Parent (Model)
|
||||
parent.removeChild(element);
|
||||
// Refresh Parent (View)
|
||||
this._onDidChangeItem.fire({ item: parent, recursive: false });
|
||||
await this.view?.refresh(false, parent);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -298,7 +288,7 @@ export class ExplorerService implements IExplorerService {
|
||||
// Check if an explorer refresh is necessary (delayed to give internal events a chance to react first)
|
||||
// Note: there is no guarantee when the internal events are fired vs real ones. Code has to deal with the fact that one might
|
||||
// be fired first over the other or not at all.
|
||||
setTimeout(() => {
|
||||
setTimeout(async () => {
|
||||
// Filter to the ones we care
|
||||
const shouldRefresh = () => {
|
||||
e = this.filterToViewRelevantEvents(e);
|
||||
@@ -365,8 +355,7 @@ export class ExplorerService implements IExplorerService {
|
||||
};
|
||||
|
||||
if (shouldRefresh()) {
|
||||
this.roots.forEach(r => r.forgetChildren());
|
||||
this._onDidChangeItem.fire({ recursive: true });
|
||||
await this.refresh(false);
|
||||
}
|
||||
}, ExplorerService.EXPLORER_FILE_CHANGES_REACT_DELAY);
|
||||
}
|
||||
@@ -389,13 +378,13 @@ export class ExplorerService implements IExplorerService {
|
||||
}));
|
||||
}
|
||||
|
||||
private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void {
|
||||
const configSortOrder = configuration?.explorer?.sortOrder || SortOrder.Default; // {{SQL CARBON EDIT}} strict-null-checks?
|
||||
private async onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): Promise<void> {
|
||||
const configSortOrder = configuration?.explorer?.sortOrder || 'default';
|
||||
if (this._sortOrder !== configSortOrder) {
|
||||
const shouldRefresh = this._sortOrder !== undefined;
|
||||
this._sortOrder = configSortOrder;
|
||||
this._sortOrder = configSortOrder as SortOrder; // {{SQL CARBON EDIT}} strict-null-checks
|
||||
if (shouldRefresh) {
|
||||
this.refresh();
|
||||
await this.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/con
|
||||
import { ITextModelContentProvider } from 'vs/editor/common/services/resolverService';
|
||||
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
@@ -29,6 +28,11 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
||||
*/
|
||||
export const VIEWLET_ID = 'workbench.view.explorer';
|
||||
|
||||
/**
|
||||
* Explorer file view id.
|
||||
*/
|
||||
export const VIEW_ID = 'workbench.explorer.fileView';
|
||||
|
||||
/**
|
||||
* Id of the default editor for open with.
|
||||
*/
|
||||
@@ -38,20 +42,15 @@ export interface IExplorerService {
|
||||
_serviceBrand: undefined;
|
||||
readonly roots: ExplorerItem[];
|
||||
readonly sortOrder: SortOrder;
|
||||
readonly onDidChangeRoots: Event<void>;
|
||||
readonly onDidChangeItem: Event<{ item?: ExplorerItem, recursive: boolean }>;
|
||||
readonly onDidChangeEditable: Event<ExplorerItem>;
|
||||
readonly onDidSelectResource: Event<{ resource?: URI, reveal?: boolean }>;
|
||||
readonly onDidCopyItems: Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>;
|
||||
|
||||
getContext(respectMultiSelection: boolean): ExplorerItem[];
|
||||
setEditable(stat: ExplorerItem, data: IEditableData | null): void;
|
||||
setEditable(stat: ExplorerItem, data: IEditableData | null): Promise<void>;
|
||||
getEditable(): { stat: ExplorerItem, data: IEditableData } | undefined;
|
||||
getEditableData(stat: ExplorerItem): IEditableData | undefined;
|
||||
// If undefined is passed checks if any element is currently being edited.
|
||||
isEditable(stat: ExplorerItem | undefined): boolean;
|
||||
findClosest(resource: URI): ExplorerItem | null;
|
||||
refresh(): void;
|
||||
refresh(): Promise<void>;
|
||||
setToCopy(stats: ExplorerItem[], cut: boolean): void;
|
||||
isCut(stat: ExplorerItem): boolean;
|
||||
|
||||
@@ -61,11 +60,16 @@ export interface IExplorerService {
|
||||
*/
|
||||
select(resource: URI, reveal?: boolean): Promise<void>;
|
||||
|
||||
registerContextProvider(contextProvider: IContextProvider): void;
|
||||
registerView(contextAndRefreshProvider: IExplorerView): void;
|
||||
}
|
||||
|
||||
export interface IContextProvider {
|
||||
export interface IExplorerView {
|
||||
getContext(respectMultiSelection: boolean): ExplorerItem[];
|
||||
refresh(recursive: boolean, item?: ExplorerItem): Promise<void>;
|
||||
selectResource(resource: URI | undefined, reveal?: boolean): Promise<void>;
|
||||
setTreeInput(): Promise<void>;
|
||||
itemsCopied(tats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void;
|
||||
setEditable(stat: ExplorerItem, isEditing: boolean): Promise<void>;
|
||||
}
|
||||
|
||||
export const IExplorerService = createDecorator<IExplorerService>('explorerService');
|
||||
@@ -77,6 +81,10 @@ export const ExplorerViewletVisibleContext = new RawContextKey<boolean>('explore
|
||||
export const ExplorerFolderContext = new RawContextKey<boolean>('explorerResourceIsFolder', false);
|
||||
export const ExplorerResourceReadonlyContext = new RawContextKey<boolean>('explorerResourceReadonly', false);
|
||||
export const ExplorerResourceNotReadonlyContext = ExplorerResourceReadonlyContext.toNegated();
|
||||
/**
|
||||
* Comma separated list of editor ids that can be used for the selected explorer resource.
|
||||
*/
|
||||
export const ExplorerResourceAvailableEditorIdsContext = new RawContextKey<string>('explorerResourceAvailableEditorIds', '');
|
||||
export const ExplorerRootContext = new RawContextKey<boolean>('explorerResourceIsRoot', false);
|
||||
export const ExplorerResourceCut = new RawContextKey<boolean>('explorerResourceCut', false);
|
||||
export const ExplorerResourceMoveableToTrash = new RawContextKey<boolean>('explorerResourceMoveableToTrash', false);
|
||||
|
||||
150
src/vs/workbench/contrib/files/common/openWith.ts
Normal file
150
src/vs/workbench/contrib/files/common/openWith.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { basename, extname, isEqual } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IEditorInput, IEditorPane } from 'vs/workbench/common/editor';
|
||||
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
|
||||
import { DEFAULT_EDITOR_ID } from 'vs/workbench/contrib/files/common/files';
|
||||
import { CustomEditorAssociation, CustomEditorsAssociations, customEditorsAssociationsSettingId } from 'vs/workbench/services/editor/common/editorAssociationsSetting';
|
||||
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService, IOpenEditorOverrideEntry, IOpenEditorOverrideHandler } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
const builtinProviderDisplayName = nls.localize('builtinProviderDisplayName', "Built-in");
|
||||
|
||||
/**
|
||||
* Try to open an resource with a given editor.
|
||||
*
|
||||
* @param input Resource to open.
|
||||
* @param id Id of the editor to use. If not provided, the user is prompted for which editor to use.
|
||||
*/
|
||||
export async function openEditorWith(
|
||||
input: IEditorInput,
|
||||
id: string | undefined,
|
||||
options: IEditorOptions | ITextEditorOptions | undefined,
|
||||
group: IEditorGroup,
|
||||
editorService: IEditorService,
|
||||
configurationService: IConfigurationService,
|
||||
quickInputService: IQuickInputService,
|
||||
): Promise<IEditorPane | undefined> {
|
||||
const resource = input.resource;
|
||||
if (!resource) {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
|
||||
}
|
||||
|
||||
const allEditorOverrides = getAllAvailableEditors(resource, options, group, editorService);
|
||||
if (!allEditorOverrides.length) {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
|
||||
}
|
||||
|
||||
const overrideToUse = typeof id === 'string' && allEditorOverrides.find(([_, entry]) => entry.id === id);
|
||||
if (overrideToUse) {
|
||||
return overrideToUse[0].open(input, options, group, id)?.override;
|
||||
}
|
||||
|
||||
// Prompt
|
||||
const resourceExt = extname(resource);
|
||||
|
||||
const items: (IQuickPickItem & { handler: IOpenEditorOverrideHandler })[] = allEditorOverrides.map((override) => {
|
||||
return {
|
||||
handler: override[0],
|
||||
id: override[1].id,
|
||||
label: override[1].label,
|
||||
description: override[1].active ? nls.localize('promptOpenWith.currentlyActive', 'Currently Active') : undefined,
|
||||
detail: override[1].detail,
|
||||
buttons: resourceExt ? [{
|
||||
iconClass: 'codicon-settings-gear',
|
||||
tooltip: nls.localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt)
|
||||
}] : undefined
|
||||
};
|
||||
});
|
||||
|
||||
const picker = quickInputService.createQuickPick<(IQuickPickItem & { handler: IOpenEditorOverrideHandler })>();
|
||||
picker.items = items;
|
||||
if (items.length) {
|
||||
picker.selectedItems = [items[0]];
|
||||
}
|
||||
picker.placeholder = nls.localize('promptOpenWith.placeHolder', "Select editor for '{0}'", basename(resource));
|
||||
|
||||
const pickedItem = await new Promise<(IQuickPickItem & { handler: IOpenEditorOverrideHandler }) | undefined>(resolve => {
|
||||
picker.onDidAccept(() => {
|
||||
resolve(picker.selectedItems.length === 1 ? picker.selectedItems[0] : undefined);
|
||||
picker.dispose();
|
||||
});
|
||||
|
||||
picker.onDidTriggerItemButton(e => {
|
||||
const pick = e.item;
|
||||
const id = pick.id;
|
||||
resolve(pick); // open the view
|
||||
picker.dispose();
|
||||
|
||||
// And persist the setting
|
||||
if (pick && id) {
|
||||
const newAssociation: CustomEditorAssociation = { viewType: id, filenamePattern: '*' + resourceExt };
|
||||
const currentAssociations = [...configurationService.getValue<CustomEditorsAssociations>(customEditorsAssociationsSettingId)];
|
||||
|
||||
// First try updating existing association
|
||||
for (let i = 0; i < currentAssociations.length; ++i) {
|
||||
const existing = currentAssociations[i];
|
||||
if (existing.filenamePattern === newAssociation.filenamePattern) {
|
||||
currentAssociations.splice(i, 1, newAssociation);
|
||||
configurationService.updateValue(customEditorsAssociationsSettingId, currentAssociations);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, create a new one
|
||||
currentAssociations.unshift(newAssociation);
|
||||
configurationService.updateValue(customEditorsAssociationsSettingId, currentAssociations);
|
||||
}
|
||||
});
|
||||
|
||||
picker.show();
|
||||
});
|
||||
|
||||
return pickedItem?.handler.open(input!, options, group, pickedItem.id)?.override;
|
||||
}
|
||||
|
||||
export const defaultEditorOverrideEntry = Object.freeze({
|
||||
id: DEFAULT_EDITOR_ID,
|
||||
label: nls.localize('promptOpenWith.defaultEditor.displayName', "Text Editor"),
|
||||
detail: builtinProviderDisplayName,
|
||||
});
|
||||
|
||||
/**
|
||||
* Get a list of all available editors, including the default text editor.
|
||||
*/
|
||||
export function getAllAvailableEditors(
|
||||
resource: URI,
|
||||
options: IEditorOptions | ITextEditorOptions | undefined,
|
||||
group: IEditorGroup,
|
||||
editorService: IEditorService,
|
||||
): Array<[IOpenEditorOverrideHandler, IOpenEditorOverrideEntry]> {
|
||||
const overrides = editorService.getEditorOverrides(resource, options, group);
|
||||
if (!overrides.some(([_, entry]) => entry.id === DEFAULT_EDITOR_ID)) {
|
||||
overrides.unshift([
|
||||
{
|
||||
open: (input: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup) => {
|
||||
if (!input.resource) {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
|
||||
}
|
||||
|
||||
const fileEditorInput = editorService.createEditorInput({ resource: input.resource, forceFile: true });
|
||||
const textOptions = options ? { ...options, ignoreOverrides: true } : { ignoreOverrides: true };
|
||||
return { override: editorService.openEditor(fileEditorInput, textOptions, group) };
|
||||
}
|
||||
},
|
||||
{
|
||||
...defaultEditorOverrideEntry,
|
||||
active: editorService.activeEditor instanceof FileEditorInput && isEqual(editorService.activeEditor.resource, resource),
|
||||
}]);
|
||||
}
|
||||
return overrides;
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ class ToggleMarkersPanelAction extends ToggleViewAction {
|
||||
const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({
|
||||
id: Constants.MARKERS_CONTAINER_ID,
|
||||
name: Messages.MARKERS_PANEL_TITLE_PROBLEMS,
|
||||
icon: Codicon.warning.classNames,
|
||||
hideIfEmpty: true,
|
||||
order: 0,
|
||||
ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [Constants.MARKERS_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]),
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MarkersModel, compareMarkersByUri } from './markersModel';
|
||||
import { Disposable, MutableDisposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IMarkerService, MarkerSeverity, IMarker } from 'vs/platform/markers/common/markers';
|
||||
import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { NumberBadge, ViewContaierActivityByView } from 'vs/workbench/services/activity/common/activity';
|
||||
import { localize } from 'vs/nls';
|
||||
import Constants from './constants';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -55,21 +55,22 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb
|
||||
|
||||
export class ActivityUpdater extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
private readonly activity = this._register(new MutableDisposable<IDisposable>());
|
||||
private readonly activity: ViewContaierActivityByView;
|
||||
|
||||
constructor(
|
||||
@IActivityService private readonly activityService: IActivityService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IMarkerService private readonly markerService: IMarkerService
|
||||
) {
|
||||
super();
|
||||
this._register(this.markerService.onMarkerChanged(() => this.updateBadge()));
|
||||
this.updateBadge();
|
||||
this.activity = this._register(instantiationService.createInstance(ViewContaierActivityByView, Constants.MARKERS_VIEW_ID));
|
||||
this._register(this.markerService.onMarkerChanged(() => this.updateActivity()));
|
||||
this.updateActivity();
|
||||
}
|
||||
|
||||
private updateBadge(): void {
|
||||
private updateActivity(): void {
|
||||
const { errors, warnings, infos } = this.markerService.getStatistics();
|
||||
const total = errors + warnings + infos;
|
||||
const message = localize('totalProblems', 'Total {0} Problems', total);
|
||||
this.activity.value = this.activityService.showActivity(Constants.MARKERS_CONTAINER_ID, new NumberBadge(total, () => message));
|
||||
this.activity.setActivity({ badge: new NumberBadge(total, () => message) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,8 +114,13 @@ export class MarkersView extends ViewPane implements IMarkerFilterController {
|
||||
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService);
|
||||
this.smallLayoutContextKey = Constants.MarkersViewSmallLayoutContextKey.bindTo(this.contextKeyService);
|
||||
this.panelState = new Memento(Constants.MARKERS_VIEW_STORAGE_ID, storageService).getMemento(StorageScope.WORKSPACE);
|
||||
|
||||
this.markersViewModel = this._register(instantiationService.createInstance(MarkersViewModel, this.panelState['multiline']));
|
||||
for (const resourceMarker of this.markersWorkbenchService.markersModel.resourceMarkers) {
|
||||
resourceMarker.markers.forEach(marker => this.markersViewModel.add(marker));
|
||||
}
|
||||
this._register(this.markersViewModel.onDidChange(marker => this.onDidChangeViewState(marker)));
|
||||
|
||||
this.setCurrentActiveEditor();
|
||||
|
||||
this.filter = new Filter(new FilterOptions());
|
||||
@@ -860,6 +865,10 @@ export class MarkersView extends ViewPane implements IMarkerFilterController {
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MarkersTree extends WorkbenchObjectTree<TreeElement, FilterData> {
|
||||
|
||||
@@ -335,6 +335,10 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem {
|
||||
this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.KEY_DOWN, (e: any) => this.onInputKeyDown(e, this.filterInputBox!)));
|
||||
this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_DOWN, this.handleKeyboardEvent));
|
||||
this._register(DOM.addStandardDisposableListener(container, DOM.EventType.KEY_UP, this.handleKeyboardEvent));
|
||||
this._register(DOM.addStandardDisposableListener(this.filterInputBox.inputElement, DOM.EventType.CLICK, (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}));
|
||||
|
||||
const focusTracker = this._register(DOM.trackFocus(this.filterInputBox.inputElement));
|
||||
this._register(focusTracker.onDidFocus(() => this.focusContextKey.set(true)));
|
||||
|
||||
@@ -5,27 +5,15 @@
|
||||
|
||||
// Cell sizing related
|
||||
export const CELL_MARGIN = 20;
|
||||
export const CELL_RUN_GUTTER = 32; // TODO should be dynamic based on execution order width, and runnable enablement
|
||||
export const CELL_RUN_GUTTER = 32;
|
||||
|
||||
export const EDITOR_TOOLBAR_HEIGHT = 0;
|
||||
export const BOTTOM_CELL_TOOLBAR_HEIGHT = 32;
|
||||
export const BOTTOM_CELL_TOOLBAR_HEIGHT = 36;
|
||||
export const CELL_STATUSBAR_HEIGHT = 22;
|
||||
|
||||
// Top margin of editor
|
||||
export const EDITOR_TOP_MARGIN = 16;
|
||||
export const EDITOR_TOP_MARGIN = 0;
|
||||
|
||||
// Top and bottom padding inside the monaco editor in a cell, which are included in `cell.editorHeight`
|
||||
export const EDITOR_TOP_PADDING = 12;
|
||||
export const EDITOR_BOTTOM_PADDING = 12;
|
||||
|
||||
// Cell context keys
|
||||
|
||||
export const NOTEBOOK_VIEW_TYPE = 'notebookViewType';
|
||||
export const NOTEBOOK_CELL_TYPE_CONTEXT_KEY = 'notebookCellType'; // code, markdown
|
||||
export const NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY = 'notebookCellEditable'; // bool
|
||||
export const NOTEBOOK_CELL_MARKDOWN_EDIT_MODE_CONTEXT_KEY = 'notebookCellMarkdownEditMode'; // bool
|
||||
export const NOTEBOOK_CELL_RUN_STATE_CONTEXT_KEY = 'notebookCellRunState'; // idle, running
|
||||
|
||||
// Notebook context keys
|
||||
export const NOTEBOOK_EDITABLE_CONTEXT_KEY = 'notebookEditable';
|
||||
export const NOTEBOOK_EXECUTING_KEY = 'notebookExecuting';
|
||||
|
||||
@@ -12,37 +12,59 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo
|
||||
import { InputFocusedContext, InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE_CONTEXT_KEY, NOTEBOOK_CELL_TYPE_CONTEXT_KEY, NOTEBOOK_EDITABLE_CONTEXT_KEY, NOTEBOOK_EXECUTING_KEY } from 'vs/workbench/contrib/notebook/browser/constants';
|
||||
import { BaseCellRenderTemplate, CellEditState, CellRunState, ICellViewModel, INotebookEditor, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { BaseCellRenderTemplate, CellEditState, CellRunState, ICellViewModel, INotebookEditor, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_RUNNABLE, NOTEBOOK_CELL_TYPE, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_EDITABLE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellKind, NOTEBOOK_EDITOR_CURSOR_BOUNDARY } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/browser/notebookService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
|
||||
import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
const INSERT_CODE_CELL_ABOVE_COMMAND_ID = 'workbench.notebook.code.insertCellAbove';
|
||||
const INSERT_CODE_CELL_BELOW_COMMAND_ID = 'workbench.notebook.code.insertCellBelow';
|
||||
const INSERT_MARKDOWN_CELL_ABOVE_COMMAND_ID = 'workbench.notebook.markdown.insertCellAbove';
|
||||
const INSERT_MARKDOWN_CELL_BELOW_COMMAND_ID = 'workbench.notebook.markdown.insertCellBelow';
|
||||
// Notebook Commands
|
||||
const EXECUTE_NOTEBOOK_COMMAND_ID = 'notebook.execute';
|
||||
const CANCEL_NOTEBOOK_COMMAND_ID = 'notebook.cancelExecution';
|
||||
const NOTEBOOK_FOCUS_TOP = 'notebook.focusTop';
|
||||
const NOTEBOOK_FOCUS_BOTTOM = 'notebook.focusBottom';
|
||||
const NOTEBOOK_REDO = 'notebook.redo';
|
||||
const NOTEBOOK_UNDO = 'notebook.undo';
|
||||
const NOTEBOOK_CURSOR_UP = 'notebook.cursorUp';
|
||||
const NOTEBOOK_CURSOR_DOWN = 'notebook.cursorDown';
|
||||
const CLEAR_ALL_CELLS_OUTPUTS_COMMAND_ID = 'notebook.clearAllCellsOutputs';
|
||||
|
||||
const EDIT_CELL_COMMAND_ID = 'workbench.notebook.cell.edit';
|
||||
const SAVE_CELL_COMMAND_ID = 'workbench.notebook.cell.save';
|
||||
const DELETE_CELL_COMMAND_ID = 'workbench.notebook.cell.delete';
|
||||
// Cell Commands
|
||||
const INSERT_CODE_CELL_ABOVE_COMMAND_ID = 'notebook.cell.insertCodeCellAbove';
|
||||
const INSERT_CODE_CELL_BELOW_COMMAND_ID = 'notebook.cell.insertCodeCellBelow';
|
||||
const INSERT_MARKDOWN_CELL_ABOVE_COMMAND_ID = 'notebook.cell.insertMarkdownCellAbove';
|
||||
const INSERT_MARKDOWN_CELL_BELOW_COMMAND_ID = 'notebook.cell.insertMarkdownCellBelow';
|
||||
const CHANGE_CELL_TO_CODE_COMMAND_ID = 'notebook.cell.changeToCode';
|
||||
const CHANGE_CELL_TO_MARKDOWN_COMMAND_ID = 'notebook.cell.changeToMarkdown';
|
||||
|
||||
const MOVE_CELL_UP_COMMAND_ID = 'workbench.notebook.cell.moveUp';
|
||||
const MOVE_CELL_DOWN_COMMAND_ID = 'workbench.notebook.cell.moveDown';
|
||||
const COPY_CELL_COMMAND_ID = 'workbench.notebook.cell.copy';
|
||||
const CUT_CELL_COMMAND_ID = 'workbench.notebook.cell.cut';
|
||||
const PASTE_CELL_COMMAND_ID = 'workbench.notebook.cell.paste';
|
||||
const PASTE_CELL_ABOVE_COMMAND_ID = 'workbench.notebook.cell.pasteAbove';
|
||||
const COPY_CELL_UP_COMMAND_ID = 'workbench.notebook.cell.copyUp';
|
||||
const COPY_CELL_DOWN_COMMAND_ID = 'workbench.notebook.cell.copyDown';
|
||||
const EDIT_CELL_COMMAND_ID = 'notebook.cell.edit';
|
||||
const QUIT_EDIT_CELL_COMMAND_ID = 'notebook.cell.quitEdit';
|
||||
const SAVE_CELL_COMMAND_ID = 'notebook.cell.save';
|
||||
const DELETE_CELL_COMMAND_ID = 'notebook.cell.delete';
|
||||
|
||||
const EXECUTE_CELL_COMMAND_ID = 'workbench.notebook.cell.execute';
|
||||
const CANCEL_CELL_COMMAND_ID = 'workbench.notebook.cell.cancelExecution';
|
||||
const EXECUTE_NOTEBOOK_COMMAND_ID = 'workbench.notebook.executeNotebook';
|
||||
const CANCEL_NOTEBOOK_COMMAND_ID = 'workbench.notebook.cancelExecution';
|
||||
const MOVE_CELL_UP_COMMAND_ID = 'notebook.cell.moveUp';
|
||||
const MOVE_CELL_DOWN_COMMAND_ID = 'notebook.cell.moveDown';
|
||||
const COPY_CELL_COMMAND_ID = 'notebook.cell.copy';
|
||||
const CUT_CELL_COMMAND_ID = 'notebook.cell.cut';
|
||||
const PASTE_CELL_COMMAND_ID = 'notebook.cell.paste';
|
||||
const PASTE_CELL_ABOVE_COMMAND_ID = 'notebook.cell.pasteAbove';
|
||||
const COPY_CELL_UP_COMMAND_ID = 'notebook.cell.copyUp';
|
||||
const COPY_CELL_DOWN_COMMAND_ID = 'notebook.cell.copyDown';
|
||||
|
||||
const NOTEBOOK_ACTIONS_CATEGORY = localize('notebookActions.category', "Notebook");
|
||||
const EXECUTE_CELL_COMMAND_ID = 'notebook.cell.execute';
|
||||
const CANCEL_CELL_COMMAND_ID = 'notebook.cell.cancelExecution';
|
||||
const EXECUTE_CELL_SELECT_BELOW = 'notebook.cell.executeAndSelectBelow';
|
||||
const EXECUTE_CELL_INSERT_BELOW = 'notebook.cell.executeAndInsertBelow';
|
||||
const CLEAR_CELL_OUTPUTS_COMMAND_ID = 'notebook.cell.clearOutputs';
|
||||
const CHANGE_CELL_LANGUAGE = 'notebook.cell.changeLanguage';
|
||||
|
||||
|
||||
export const NOTEBOOK_ACTIONS_CATEGORY = localize('notebookActions.category', "Notebook");
|
||||
|
||||
const EDITOR_WIDGET_ACTION_WEIGHT = KeybindingWeight.EditorContrib; // smaller than Suggest Widget, etc
|
||||
|
||||
@@ -51,6 +73,7 @@ const enum CellToolbarOrder {
|
||||
MoveCellDown,
|
||||
EditCell,
|
||||
SaveCell,
|
||||
ClearCellOutput,
|
||||
InsertCell,
|
||||
DeleteCell
|
||||
}
|
||||
@@ -62,7 +85,7 @@ registerAction2(class extends Action2 {
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
title: localize('notebookActions.execute', "Execute Cell"),
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext),
|
||||
when: NOTEBOOK_EDITOR_FOCUSED,
|
||||
primary: KeyMod.WinCtrl | KeyCode.Enter,
|
||||
win: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter
|
||||
@@ -82,7 +105,7 @@ registerAction2(class extends Action2 {
|
||||
}
|
||||
}
|
||||
|
||||
runCell(context);
|
||||
return runCell(context);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -149,10 +172,11 @@ export class CancelCellAction extends MenuItemAction {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.executeNotebookCellSelectBelow',
|
||||
id: EXECUTE_CELL_SELECT_BELOW,
|
||||
title: localize('notebookActions.executeAndSelectBelow', "Execute Notebook Cell and Select Below"),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext),
|
||||
when: NOTEBOOK_EDITOR_FOCUSED,
|
||||
primary: KeyMod.Shift | KeyCode.Enter,
|
||||
weight: EDITOR_WIDGET_ACTION_WEIGHT
|
||||
}
|
||||
@@ -182,7 +206,9 @@ registerAction2(class extends Action2 {
|
||||
editor.focusNotebookCell(nextCell, activeCell.editState === CellEditState.Editing);
|
||||
} else {
|
||||
const newCell = editor.insertNotebookCell(activeCell, CellKind.Code, 'below');
|
||||
editor.focusNotebookCell(newCell, true);
|
||||
if (newCell) {
|
||||
editor.focusNotebookCell(newCell, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -190,10 +216,11 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.executeNotebookCellInsertBelow',
|
||||
id: EXECUTE_CELL_INSERT_BELOW,
|
||||
title: localize('notebookActions.executeAndInsertBelow', "Execute Notebook Cell and Insert Below"),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext),
|
||||
when: NOTEBOOK_EDITOR_FOCUSED,
|
||||
primary: KeyMod.Alt | KeyCode.Enter,
|
||||
weight: EDITOR_WIDGET_ACTION_WEIGHT
|
||||
}
|
||||
@@ -213,7 +240,9 @@ registerAction2(class extends Action2 {
|
||||
}
|
||||
|
||||
const newCell = editor.insertNotebookCell(activeCell, CellKind.Code, 'below');
|
||||
editor.focusNotebookCell(newCell, true);
|
||||
if (newCell) {
|
||||
editor.focusNotebookCell(newCell, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -262,8 +291,9 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.quitNotebookEdit',
|
||||
id: QUIT_EDIT_CELL_COMMAND_ID,
|
||||
title: localize('notebookActions.quitEditing', "Quit Notebook Cell Editing"),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext),
|
||||
primary: KeyCode.Escape,
|
||||
@@ -295,17 +325,19 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
command: {
|
||||
id: EXECUTE_NOTEBOOK_COMMAND_ID,
|
||||
title: localize('notebookActions.menu.executeNotebook', "Execute Notebook (Run all cells)"),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
icon: { id: 'codicon/run-all' }
|
||||
},
|
||||
order: -1,
|
||||
group: 'navigation',
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(NOTEBOOK_EXECUTING_KEY))
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK.toNegated(), NOTEBOOK_EDITOR_RUNNABLE)
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
command: {
|
||||
id: CANCEL_NOTEBOOK_COMMAND_ID,
|
||||
title: localize('notebookActions.menu.cancelNotebook', "Stop Notebook Execution"),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
icon: { id: 'codicon/primitive-square' }
|
||||
},
|
||||
order: -1,
|
||||
@@ -318,17 +350,18 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
command: {
|
||||
id: EXECUTE_CELL_COMMAND_ID,
|
||||
title: localize('notebookActions.menu.execute', "Execute Notebook Cell"),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
icon: { id: 'codicon/run' }
|
||||
},
|
||||
order: 0,
|
||||
group: 'navigation',
|
||||
when: NOTEBOOK_EDITOR_FOCUSED
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_CELL_RUNNABLE)
|
||||
});
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.changeCellToCode',
|
||||
id: CHANGE_CELL_TO_CODE_COMMAND_ID,
|
||||
title: localize('notebookActions.changeCellToCode', "Change Cell to Code"),
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
|
||||
@@ -348,7 +381,7 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.changeCellToMarkdown',
|
||||
id: CHANGE_CELL_TO_MARKDOWN_COMMAND_ID,
|
||||
title: localize('notebookActions.changeCellToMarkdown', "Change Cell to Markdown"),
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
|
||||
@@ -407,30 +440,46 @@ async function changeActiveCellToKind(kind: CellKind, accessor: ServicesAccessor
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeCell.cellKind === kind) {
|
||||
return;
|
||||
changeCellToKind(kind, { cell: activeCell, notebookEditor: editor });
|
||||
}
|
||||
|
||||
export async function changeCellToKind(kind: CellKind, context: INotebookCellActionContext, language?: string): Promise<ICellViewModel | null> {
|
||||
const { cell, notebookEditor } = context;
|
||||
|
||||
if (cell.cellKind === kind) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const text = activeCell.getText();
|
||||
editor.insertNotebookCell(activeCell, kind, 'below', text);
|
||||
const idx = editor.viewModel?.getCellIndex(activeCell);
|
||||
const text = cell.getText();
|
||||
if (!notebookEditor.insertNotebookCell(cell, kind, 'below', text)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const idx = notebookEditor.viewModel?.getCellIndex(cell);
|
||||
if (typeof idx !== 'number') {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
const newCell = editor.viewModel?.viewCells[idx + 1];
|
||||
const newCell = notebookEditor.viewModel?.viewCells[idx + 1];
|
||||
if (!newCell) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
editor.focusNotebookCell(newCell, activeCell.editState === CellEditState.Editing);
|
||||
editor.deleteNotebookCell(activeCell);
|
||||
if (language) {
|
||||
newCell.model.language = language;
|
||||
}
|
||||
|
||||
notebookEditor.focusNotebookCell(newCell, cell.editState === CellEditState.Editing);
|
||||
notebookEditor.deleteNotebookCell(cell);
|
||||
|
||||
return newCell;
|
||||
}
|
||||
|
||||
export interface INotebookCellActionContext {
|
||||
cellTemplate?: BaseCellRenderTemplate;
|
||||
cell: ICellViewModel;
|
||||
notebookEditor: INotebookEditor;
|
||||
ui?: boolean;
|
||||
}
|
||||
|
||||
function isCellActionContext(context: any): context is INotebookCellActionContext {
|
||||
@@ -473,8 +522,10 @@ abstract class InsertCellCommand extends Action2 {
|
||||
}
|
||||
}
|
||||
|
||||
const newCell = context.notebookEditor.insertNotebookCell(context.cell, this.kind, this.direction);
|
||||
context.notebookEditor.focusNotebookCell(newCell, true);
|
||||
const newCell = context.notebookEditor.insertNotebookCell(context.cell, this.kind, this.direction, undefined, context.ui);
|
||||
if (newCell) {
|
||||
context.notebookEditor.focusNotebookCell(newCell, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,9 +634,9 @@ registerAction2(class extends Action2 {
|
||||
menu: {
|
||||
id: MenuId.NotebookCellTitle,
|
||||
when: ContextKeyExpr.and(
|
||||
ContextKeyExpr.equals(NOTEBOOK_CELL_TYPE_CONTEXT_KEY, 'markdown'),
|
||||
ContextKeyExpr.equals(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE_CONTEXT_KEY, false),
|
||||
ContextKeyExpr.equals(NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, true)),
|
||||
NOTEBOOK_CELL_TYPE.isEqualTo('markdown'),
|
||||
NOTEBOOK_CELL_MARKDOWN_EDIT_MODE.toNegated(),
|
||||
NOTEBOOK_CELL_EDITABLE),
|
||||
order: CellToolbarOrder.EditCell
|
||||
},
|
||||
icon: { id: 'codicon/pencil' }
|
||||
@@ -613,9 +664,9 @@ registerAction2(class extends Action2 {
|
||||
menu: {
|
||||
id: MenuId.NotebookCellTitle,
|
||||
when: ContextKeyExpr.and(
|
||||
ContextKeyExpr.equals(NOTEBOOK_CELL_TYPE_CONTEXT_KEY, 'markdown'),
|
||||
ContextKeyExpr.equals(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE_CONTEXT_KEY, true),
|
||||
ContextKeyExpr.equals(NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, true)),
|
||||
NOTEBOOK_CELL_TYPE.isEqualTo('markdown'),
|
||||
NOTEBOOK_CELL_MARKDOWN_EDIT_MODE,
|
||||
NOTEBOOK_CELL_EDITABLE),
|
||||
order: CellToolbarOrder.SaveCell
|
||||
},
|
||||
icon: { id: 'codicon/check' }
|
||||
@@ -645,7 +696,7 @@ registerAction2(class extends Action2 {
|
||||
menu: {
|
||||
id: MenuId.NotebookCellTitle,
|
||||
order: CellToolbarOrder.DeleteCell,
|
||||
when: ContextKeyExpr.equals(NOTEBOOK_EDITABLE_CONTEXT_KEY, true)
|
||||
when: NOTEBOOK_EDITOR_EDITABLE
|
||||
},
|
||||
keybinding: {
|
||||
primary: KeyCode.Delete,
|
||||
@@ -679,7 +730,9 @@ registerAction2(class extends Action2 {
|
||||
} else {
|
||||
// No cells left, insert a new empty one
|
||||
const newCell = context.notebookEditor.insertNotebookCell(undefined, context.cell.cellKind);
|
||||
context.notebookEditor.focusNotebookCell(newCell, true);
|
||||
if (newCell) {
|
||||
context.notebookEditor.focusNotebookCell(newCell, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -687,8 +740,8 @@ registerAction2(class extends Action2 {
|
||||
|
||||
async function moveCell(context: INotebookCellActionContext, direction: 'up' | 'down'): Promise<void> {
|
||||
const result = direction === 'up' ?
|
||||
context.notebookEditor.moveCellUp(context.cell) :
|
||||
context.notebookEditor.moveCellDown(context.cell);
|
||||
await context.notebookEditor.moveCellUp(context.cell) :
|
||||
await context.notebookEditor.moveCellDown(context.cell);
|
||||
|
||||
if (result) {
|
||||
// move cell command only works when the cell container has focus
|
||||
@@ -700,7 +753,9 @@ async function copyCell(context: INotebookCellActionContext, direction: 'up' | '
|
||||
const text = context.cell.getText();
|
||||
const newCellDirection = direction === 'up' ? 'above' : 'below';
|
||||
const newCell = context.notebookEditor.insertNotebookCell(context.cell, context.cell.cellKind, newCellDirection, text);
|
||||
context.notebookEditor.focusNotebookCell(newCell, false);
|
||||
if (newCell) {
|
||||
context.notebookEditor.focusNotebookCell(newCell, false);
|
||||
}
|
||||
}
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
@@ -952,8 +1007,9 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.notebook.cursorDown',
|
||||
title: 'Notebook Cursor Move Down',
|
||||
id: NOTEBOOK_CURSOR_DOWN,
|
||||
title: localize('cursorMoveDown', 'Cursor Move Down'),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.has(InputFocusedContextKey), EditorContextKeys.editorTextFocus, NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('top'), NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none')),
|
||||
primary: KeyCode.DownArrow,
|
||||
@@ -991,8 +1047,9 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.notebook.cursorUp',
|
||||
title: 'Notebook Cursor Move Up',
|
||||
id: NOTEBOOK_CURSOR_UP,
|
||||
title: localize('cursorMoveUp', 'Cursor Move Up'),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.has(InputFocusedContextKey), EditorContextKeys.editorTextFocus, NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('bottom'), NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none')),
|
||||
primary: KeyCode.UpArrow,
|
||||
@@ -1035,8 +1092,9 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.notebook.undo',
|
||||
title: 'Notebook Undo',
|
||||
id: NOTEBOOK_UNDO,
|
||||
title: localize('undo', 'Undo'),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_Z,
|
||||
@@ -1066,8 +1124,9 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.notebook.redo',
|
||||
title: 'Notebook Redo',
|
||||
id: NOTEBOOK_REDO,
|
||||
title: localize('redo', 'Redo'),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z,
|
||||
@@ -1097,8 +1156,9 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.notebook.focusTop',
|
||||
title: 'Notebook Focus First Cell',
|
||||
id: NOTEBOOK_FOCUS_TOP,
|
||||
title: localize('focusFirstCell', 'Focus First Cell'),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Home,
|
||||
@@ -1130,8 +1190,9 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.notebook.focusBottom',
|
||||
title: 'Notebook Focus Last Cell',
|
||||
id: NOTEBOOK_FOCUS_BOTTOM,
|
||||
title: localize('focusLastCell', 'Focus Last Cell'),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
|
||||
primary: KeyMod.CtrlCmd | KeyCode.End,
|
||||
@@ -1159,3 +1220,179 @@ registerAction2(class extends Action2 {
|
||||
editor.focusNotebookCell(firstCell, false);
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: CLEAR_CELL_OUTPUTS_COMMAND_ID,
|
||||
title: localize('clearActiveCellOutputs', 'Clear Active Cell Outputs'),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
menu: {
|
||||
id: MenuId.NotebookCellTitle,
|
||||
when: ContextKeyExpr.and(NOTEBOOK_CELL_TYPE.isEqualTo('code'), NOTEBOOK_EDITOR_RUNNABLE),
|
||||
order: CellToolbarOrder.ClearCellOutput
|
||||
},
|
||||
icon: { id: 'codicon/clear-all' },
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise<void> {
|
||||
if (!isCellActionContext(context)) {
|
||||
context = getActiveCellContext(accessor);
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const editor = context.notebookEditor;
|
||||
if (!editor.viewModel || !editor.viewModel.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
editor.viewModel.notebookDocument.clearCellOutput(context.cell.handle);
|
||||
}
|
||||
});
|
||||
|
||||
interface ILanguagePickInput extends IQuickPickItem {
|
||||
languageId: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export class ChangeCellLanguageAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: CHANGE_CELL_LANGUAGE,
|
||||
title: localize('changeLanguage', 'Change Cell Language'),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise<void> {
|
||||
if (!isCellActionContext(context)) {
|
||||
context = getActiveCellContext(accessor);
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.showLanguagePicker(accessor, context);
|
||||
}
|
||||
|
||||
private async showLanguagePicker(accessor: ServicesAccessor, context: INotebookCellActionContext) {
|
||||
const topItems: ILanguagePickInput[] = [];
|
||||
const mainItems: ILanguagePickInput[] = [];
|
||||
|
||||
const modeService = accessor.get(IModeService);
|
||||
const modelService = accessor.get(IModelService);
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
|
||||
const providerLanguages = [...context.notebookEditor.viewModel!.notebookDocument.languages, 'markdown'];
|
||||
providerLanguages.forEach(languageId => {
|
||||
let description: string;
|
||||
if (languageId === context.cell.language) {
|
||||
description = localize('languageDescription', "({0}) - Current Language", languageId);
|
||||
} else {
|
||||
description = localize('languageDescriptionConfigured', "({0})", languageId);
|
||||
}
|
||||
|
||||
const languageName = modeService.getLanguageName(languageId);
|
||||
if (!languageName) {
|
||||
// Notebook has unrecognized language
|
||||
return;
|
||||
}
|
||||
|
||||
const item = <ILanguagePickInput>{
|
||||
label: languageName,
|
||||
iconClasses: getIconClasses(modelService, modeService, this.getFakeResource(languageName, modeService)),
|
||||
description,
|
||||
languageId
|
||||
};
|
||||
|
||||
if (languageId === 'markdown' || languageId === context.cell.language) {
|
||||
topItems.push(item);
|
||||
} else {
|
||||
mainItems.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
mainItems.sort((a, b) => {
|
||||
return a.description.localeCompare(b.description);
|
||||
});
|
||||
|
||||
const picks: QuickPickInput[] = [
|
||||
...topItems,
|
||||
{ type: 'separator' },
|
||||
...mainItems
|
||||
];
|
||||
|
||||
const selection = await quickInputService.pick(picks, { placeHolder: localize('pickLanguageToConfigure', "Select Language Mode") }) as ILanguagePickInput | undefined;
|
||||
if (selection && selection.languageId) {
|
||||
if (selection.languageId === 'markdown' && context.cell?.language !== 'markdown') {
|
||||
const newCell = await changeCellToKind(CellKind.Markdown, { cell: context.cell, notebookEditor: context.notebookEditor });
|
||||
if (newCell) {
|
||||
context.notebookEditor.focusNotebookCell(newCell, true);
|
||||
}
|
||||
} else if (selection.languageId !== 'markdown' && context.cell?.language === 'markdown') {
|
||||
await changeCellToKind(CellKind.Code, { cell: context.cell, notebookEditor: context.notebookEditor }, selection.languageId);
|
||||
} else {
|
||||
context.notebookEditor.viewModel!.notebookDocument.changeCellLanguage(context.cell.handle, selection.languageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied from editorStatus.ts
|
||||
*/
|
||||
private getFakeResource(lang: string, modeService: IModeService): URI | undefined {
|
||||
let fakeResource: URI | undefined;
|
||||
|
||||
const extensions = modeService.getExtensions(lang);
|
||||
if (extensions?.length) {
|
||||
fakeResource = URI.file(extensions[0]);
|
||||
} else {
|
||||
const filenames = modeService.getFilenames(lang);
|
||||
if (filenames?.length) {
|
||||
fakeResource = URI.file(filenames[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return fakeResource;
|
||||
}
|
||||
}
|
||||
registerAction2(ChangeCellLanguageAction);
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: CLEAR_ALL_CELLS_OUTPUTS_COMMAND_ID,
|
||||
title: localize('clearAllCellsOutputs', 'Clear All Cells Outputs'),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
menu: {
|
||||
id: MenuId.EditorTitle,
|
||||
when: NOTEBOOK_EDITOR_FOCUSED,
|
||||
group: 'navigation',
|
||||
order: 0
|
||||
},
|
||||
icon: { id: 'codicon/clear-all' },
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise<void> {
|
||||
if (!isCellActionContext(context)) {
|
||||
context = getActiveCellContext(accessor);
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const editor = context.notebookEditor;
|
||||
if (!editor.viewModel || !editor.viewModel.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
editor.viewModel.notebookDocument.clearAllCellOutputs();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -254,7 +254,7 @@ registerNotebookContribution(NotebookFindWidget.id, NotebookFindWidget);
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.notebook.hideFind',
|
||||
id: 'notebook.hideFind',
|
||||
title: localize('notebookActions.hideFind', "Hide Find in Notebook"),
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED),
|
||||
@@ -281,7 +281,7 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.notebook.find',
|
||||
id: 'notebook.find',
|
||||
title: localize('notebookActions.findInNotebook', "Find in Notebook"),
|
||||
keybinding: {
|
||||
when: NOTEBOOK_EDITOR_FOCUSED,
|
||||
|
||||
@@ -16,7 +16,8 @@ import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { getActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
|
||||
import { getActiveNotebookEditor, NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export class FoldingController extends Disposable implements INotebookEditorContribution {
|
||||
static id: string = 'workbench.notebook.findController';
|
||||
@@ -132,8 +133,9 @@ registerNotebookContribution(FoldingController.id, FoldingController);
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.notebook.fold',
|
||||
title: 'Notebook Fold Cell',
|
||||
id: 'notebook.fold',
|
||||
title: localize('fold.cell', 'Fold Cell'),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
|
||||
primary: KeyCode.LeftArrow,
|
||||
@@ -169,8 +171,9 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.notebook.unfold',
|
||||
title: 'Notebook Unfold Cell',
|
||||
id: 'notebook.unfold',
|
||||
title: localize('unfold.cell', 'Unfold Cell'),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
|
||||
primary: KeyCode.RightArrow,
|
||||
|
||||
@@ -44,6 +44,32 @@ export class FoldingModel extends Disposable {
|
||||
this.recompute();
|
||||
}));
|
||||
|
||||
this._viewModelStore.add(this._viewModel.onDidChangeSelection(() => {
|
||||
const selectionHandles = this._viewModel!.selectionHandles;
|
||||
const indexes = selectionHandles.map(handle =>
|
||||
this._viewModel!.getCellIndex(this._viewModel!.getCellByHandle(handle)!)
|
||||
);
|
||||
|
||||
let changed = false;
|
||||
|
||||
indexes.forEach(index => {
|
||||
let regionIndex = this.regions.findRange(index + 1);
|
||||
|
||||
while (regionIndex !== -1) {
|
||||
if (this._regions.isCollapsed(regionIndex) && index > this._regions.getStartLineNumber(regionIndex) - 1) {
|
||||
this._regions.setCollapsed(regionIndex, false);
|
||||
changed = true;
|
||||
}
|
||||
regionIndex = this._regions.getParentIndex(regionIndex);
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
this._onDidFoldingRegionChanges.fire();
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
this.recompute();
|
||||
}
|
||||
|
||||
|
||||
@@ -320,4 +320,92 @@ suite('Notebook Folding', () => {
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('View Index', function () {
|
||||
withTestNotebook(
|
||||
instantiationService,
|
||||
blukEditService,
|
||||
undoRedoService,
|
||||
[
|
||||
[['# header 1'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['body'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['body 2'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['body 3'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['## header 2.2'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['var e = 7;'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['body 2'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['body 3'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['## header 2.2'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['var e = 7;'], 'markdown', CellKind.Markdown, [], {}],
|
||||
],
|
||||
(editor, viewModel) => {
|
||||
const foldingModel = new FoldingModel();
|
||||
foldingModel.attachViewModel(viewModel);
|
||||
foldingModel.applyMemento([{ start: 2, end: 6 }]);
|
||||
viewModel.updateFoldingRanges(foldingModel.regions);
|
||||
|
||||
// Note that hidden ranges !== folding ranges
|
||||
assert.deepEqual(viewModel.getHiddenRanges(), [
|
||||
{ start: 3, end: 6 }
|
||||
]);
|
||||
|
||||
assert.equal(viewModel.getNextVisibleCellIndex(1), 2);
|
||||
assert.equal(viewModel.getNextVisibleCellIndex(2), 7);
|
||||
assert.equal(viewModel.getNextVisibleCellIndex(3), 7);
|
||||
assert.equal(viewModel.getNextVisibleCellIndex(4), 7);
|
||||
assert.equal(viewModel.getNextVisibleCellIndex(5), 7);
|
||||
assert.equal(viewModel.getNextVisibleCellIndex(6), 7);
|
||||
assert.equal(viewModel.getNextVisibleCellIndex(7), 8);
|
||||
}
|
||||
);
|
||||
|
||||
withTestNotebook(
|
||||
instantiationService,
|
||||
blukEditService,
|
||||
undoRedoService,
|
||||
[
|
||||
[['# header 1'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['body'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['body 2'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['body 3'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['## header 2.2'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['var e = 7;'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['# header 2.1\n'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['body 2'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['body 3'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['## header 2.2'], 'markdown', CellKind.Markdown, [], {}],
|
||||
[['var e = 7;'], 'markdown', CellKind.Markdown, [], {}],
|
||||
],
|
||||
(editor, viewModel) => {
|
||||
const foldingModel = new FoldingModel();
|
||||
foldingModel.attachViewModel(viewModel);
|
||||
foldingModel.applyMemento([
|
||||
{ start: 5, end: 6 },
|
||||
{ start: 10, end: 11 },
|
||||
]);
|
||||
|
||||
viewModel.updateFoldingRanges(foldingModel.regions);
|
||||
|
||||
// Note that hidden ranges !== folding ranges
|
||||
assert.deepEqual(viewModel.getHiddenRanges(), [
|
||||
{ start: 6, end: 6 },
|
||||
{ start: 11, end: 11 }
|
||||
]);
|
||||
|
||||
// folding ranges
|
||||
// [5, 6]
|
||||
// [10, 11]
|
||||
assert.equal(viewModel.getNextVisibleCellIndex(4), 5);
|
||||
assert.equal(viewModel.getNextVisibleCellIndex(5), 7);
|
||||
assert.equal(viewModel.getNextVisibleCellIndex(6), 7);
|
||||
|
||||
assert.equal(viewModel.getNextVisibleCellIndex(9), 10);
|
||||
assert.equal(viewModel.getNextVisibleCellIndex(10), 12);
|
||||
assert.equal(viewModel.getNextVisibleCellIndex(11), 12);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { registerAction2, Action2 } from 'vs/platform/actions/common/actions';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { localize } from 'vs/nls';
|
||||
import { NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { getActiveNotebookEditor, NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { getDocumentFormattingEditsUntilResult } from 'vs/editor/contrib/format/format';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
|
||||
import { WorkspaceTextEdit } from 'vs/editor/common/modes';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'notebook.format',
|
||||
title: localize('format.title', 'Format Notebook'),
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
precondition: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR),
|
||||
keybinding: {
|
||||
when: EditorContextKeys.editorTextFocus.toNegated(),
|
||||
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F,
|
||||
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I },
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const textModelService = accessor.get(ITextModelService);
|
||||
const editorWorkerService = accessor.get(IEditorWorkerService);
|
||||
const bulkEditService = accessor.get(IBulkEditService);
|
||||
|
||||
const editor = getActiveNotebookEditor(editorService);
|
||||
if (!editor || !editor.viewModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const notebook = editor.viewModel.notebookDocument;
|
||||
const dispoables = new DisposableStore();
|
||||
try {
|
||||
|
||||
const edits: WorkspaceTextEdit[] = [];
|
||||
|
||||
for (let cell of notebook.cells) {
|
||||
|
||||
const ref = await textModelService.createModelReference(cell.uri);
|
||||
dispoables.add(ref);
|
||||
|
||||
const model = ref.object.textEditorModel;
|
||||
|
||||
const formatEdits = await getDocumentFormattingEditsUntilResult(
|
||||
editorWorkerService, model,
|
||||
model.getOptions(), CancellationToken.None
|
||||
);
|
||||
|
||||
if (formatEdits) {
|
||||
formatEdits.forEach(edit => edits.push({
|
||||
edit,
|
||||
resource: model.uri,
|
||||
modelVersionId: model.getVersionId()
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
await bulkEditService.apply(
|
||||
{ edits },
|
||||
{ label: localize('label', "Format Notebook") }
|
||||
);
|
||||
|
||||
} finally {
|
||||
dispoables.dispose();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user