Merge from vscode 27ada910e121e23a6d95ecca9cae595fb98ab568

This commit is contained in:
ADS Merger
2020-04-30 00:53:43 +00:00
parent 87e5239713
commit 93f35ca321
413 changed files with 7190 additions and 8756 deletions

View File

@@ -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> {

View File

@@ -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;

View File

@@ -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) => {

View File

@@ -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);
});

View File

@@ -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> {

View File

@@ -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

View File

@@ -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

View File

@@ -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) {

View File

@@ -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.')
}
}
}

View File

@@ -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,

View File

@@ -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;

View File

@@ -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.',

View File

@@ -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.`);

View File

@@ -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,

View File

@@ -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,

View File

@@ -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);

View File

@@ -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]
});
}

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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) + '';
}

View File

@@ -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);
}

View File

@@ -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);
});
}

View File

@@ -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);
}
});
}
}

View File

@@ -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();
}
}

View File

@@ -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() {

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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 });

View File

@@ -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 {

View File

@@ -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';

View File

@@ -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);

View File

@@ -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> {

View File

@@ -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:

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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, {}));

View File

@@ -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 = '';
}
}

View File

@@ -66,7 +66,6 @@
max-width: 260px;
margin-left: auto;
margin-right: auto;
display: block;
}
.monaco-workbench .pane > .pane-body .welcome-view-content {

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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

View File

@@ -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',

View File

@@ -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';

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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)}`;

View File

@@ -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

View File

@@ -6,3 +6,4 @@
import './inputClipboardActions';
import './sleepResumeRepaintMinimap';
import './selectionClipboard';
import './startDebugTextMate';

View File

@@ -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"));

View File

@@ -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));

View File

@@ -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,

View File

@@ -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();

View File

@@ -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;
}
}

View File

@@ -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,
};
})
];
}
}));

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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> {

View File

@@ -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: {

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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();

View File

@@ -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()));

View File

@@ -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);

View File

@@ -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,

View File

@@ -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 });
}
}

View File

@@ -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[]>;

View File

@@ -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);
}
}

View File

@@ -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) => {

View File

@@ -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);
}

View File

@@ -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: [

View File

@@ -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}"`);
});
}));

View File

@@ -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' });
}
}
}

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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);
}
});
};

View File

@@ -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> {

View File

@@ -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': [

View File

@@ -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]);
})));

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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();

View File

@@ -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();
}

View File

@@ -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();
}
}
}

View File

@@ -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);

View 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;
}

View File

@@ -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 }]),

View File

@@ -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) });
}
}

View File

@@ -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> {

View File

@@ -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)));

View File

@@ -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';

View File

@@ -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();
}
});

View File

@@ -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,

View File

@@ -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,

View File

@@ -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();
}

View File

@@ -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);
}
);
});
});

View File

@@ -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