diff --git a/extensions/json-language-features/package.nls.json b/extensions/json-language-features/package.nls.json index 69a8c0eea8..28a21fe9f9 100644 --- a/extensions/json-language-features/package.nls.json +++ b/extensions/json-language-features/package.nls.json @@ -3,7 +3,7 @@ "description": "Provides rich language support for JSON files.", "json.schemas.desc": "Associate schemas to JSON files in the current project", "json.schemas.url.desc": "A URL to a schema or a relative path to a schema in the current directory", - "json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas. `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there at least one matching pattern and the last matching pattern is not an exclusion pattern.", + "json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas. `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern.", "json.schemas.fileMatch.item.desc": "A file pattern that can contain '*' to match against when resolving JSON files to schemas.", "json.schemas.schema.desc": "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.", "json.format.enable.desc": "Enable/disable default JSON formatter", diff --git a/extensions/json-language-features/server/README.md b/extensions/json-language-features/server/README.md index edf55ce63f..d04ff913e9 100644 --- a/extensions/json-language-features/server/README.md +++ b/extensions/json-language-features/server/README.md @@ -63,10 +63,10 @@ The server supports the following settings: - `format` - `enable`: Whether the server should register the formatting support. This option is only applicable if the client supports *dynamicRegistration* for *rangeFormatting* and `initializationOptions.provideFormatter` is not defined. - `schemas`: Configures association of file names to schema URL or schemas and/or associations of schema URL to schema content. - - `fileMatch`: an array of file names or paths (separated by `/`). `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there at least one matching pattern and the last matching pattern is not an exclusion pattern. + - `fileMatch`: an array of file names or paths (separated by `/`). `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern. - `url`: The URL of the schema, optional when also a schema is provided. - `schema`: The schema content. - - `resultLimit`: The max number foldig ranges and otline symbols to be computed (for performance reasons) + - `resultLimit`: The max number folding ranges and outline symbols to be computed (for performance reasons) ```json { @@ -160,7 +160,7 @@ Notification: ### Item Limit If the setting `resultLimit` is set, the JSON language server will limit the number of folding ranges and document symbols computed. -When the limit is reached, a notification `json/resultLimitReached` is sent that can be shown that camn be shown to the user. +When the limit is reached, a notification `json/resultLimitReached` is sent that can be shown that can be shown to the user. Notification: - method: 'json/resultLimitReached' @@ -180,7 +180,7 @@ For that, install the `json-language-server` npm module: `npm install -g json-language-server` -Start the language server with the `json-language-server` command. Use a command line argument to specify the prefered communication channel: +Start the language server with the `json-language-server` command. Use a command line argument to specify the preferred communication channel: ``` json-language-server --node-ipc diff --git a/extensions/theme-defaults/themes/dark_plus.json b/extensions/theme-defaults/themes/dark_plus.json index fdadd4e5c3..4fd8979321 100644 --- a/extensions/theme-defaults/themes/dark_plus.json +++ b/extensions/theme-defaults/themes/dark_plus.json @@ -190,5 +190,11 @@ "foreground": "#C8C8C8" } } - ] + ], + "semanticTokenColors": { + "newOperator":"#C586C0", + "stringLiteral":"#ce9178", + "customLiteral": "#DCDCAA", + "numberLiteral": "#b5cea8", + } } diff --git a/extensions/theme-defaults/themes/dark_vs.json b/extensions/theme-defaults/themes/dark_vs.json index 6e5f4c25f8..1b4cf8b967 100644 --- a/extensions/theme-defaults/themes/dark_vs.json +++ b/extensions/theme-defaults/themes/dark_vs.json @@ -45,7 +45,6 @@ { "scope": [ "constant.numeric", - "entity.name.operator.custom-literal.number", "variable.other.enummember", "keyword.operator.plus.exponent", "keyword.operator.minus.exponent" @@ -226,7 +225,6 @@ { "scope": [ "string", - "entity.name.operator.custom-literal.string", "meta.embedded.assembly" ], "settings": { @@ -364,5 +362,11 @@ "foreground": "#569cd6" } } - ] + ], + "semanticTokenColors": { + "newOperator": "#d4d4d4", + "stringLiteral": "#ce9178", + "customLiteral": "#D4D4D4", + "numberLiteral": "#b5cea8", + } } diff --git a/extensions/theme-defaults/themes/light_plus.json b/extensions/theme-defaults/themes/light_plus.json index a5ea49d4d3..b743b1b998 100644 --- a/extensions/theme-defaults/themes/light_plus.json +++ b/extensions/theme-defaults/themes/light_plus.json @@ -190,5 +190,11 @@ "foreground": "#000000" } } - ] + ], + "semanticTokenColors": { + "newOperator": "#AF00DB", + "stringLiteral": "#a31515", + "customLiteral": "#795E26", + "numberLiteral": "#098658", + } } diff --git a/extensions/theme-defaults/themes/light_vs.json b/extensions/theme-defaults/themes/light_vs.json index cbf19b2298..3410551898 100644 --- a/extensions/theme-defaults/themes/light_vs.json +++ b/extensions/theme-defaults/themes/light_vs.json @@ -45,7 +45,6 @@ { "scope": [ "constant.numeric", - "entity.name.operator.custom-literal.number", "variable.other.enummember", "keyword.operator.plus.exponent", "keyword.operator.minus.exponent" @@ -218,7 +217,6 @@ { "scope": [ "string", - "entity.name.operator.custom-literal.string", "meta.embedded.assembly" ], "settings": { @@ -388,5 +386,11 @@ "foreground": "#0000ff" } } - ] + ], + "semanticTokenColors": { + "newOperator": "#0000ff", + "stringLiteral": "#a31515", + "customLiteral": "#000000", + "numberLiteral": "#098658", + } } diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index 0cd3192493..fe6584234c 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -41,8 +41,8 @@ const defaultOpts = { export class CheckboxActionViewItem extends BaseActionViewItem { - private checkbox: Checkbox | undefined; - private readonly disposables = new DisposableStore(); + protected checkbox: Checkbox | undefined; + protected readonly disposables = new DisposableStore(); render(container: HTMLElement): void { this.element = container; diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index c530869902..51e7290dbe 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -56,6 +56,7 @@ bootstrapWindow.load([ ], function (workbench, configuration) { perf.mark('didLoadWorkbenchMain'); + performance.mark('workbench-start'); return process['lazyEnv'].then(function () { perf.mark('main/startup'); diff --git a/src/vs/platform/browser/checkbox.ts b/src/vs/platform/browser/checkbox.ts new file mode 100644 index 0000000000..009cf20994 --- /dev/null +++ b/src/vs/platform/browser/checkbox.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionbar'; +import { CheckboxActionViewItem } from 'vs/base/browser/ui/checkbox/checkbox'; +import { IAction } from 'vs/base/common/actions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachCheckboxStyler } from 'vs/platform/theme/common/styler'; + +export class ThemableCheckboxActionViewItem extends CheckboxActionViewItem { + + constructor(context: any, action: IAction, options: IBaseActionViewItemOptions | undefined, private readonly themeService: IThemeService) { + super(context, action, options); + } + + render(container: HTMLElement): void { + super.render(container); + if (this.checkbox) { + this.disposables.add(attachCheckboxStyler(this.checkbox, this.themeService)); + } + } + +} + diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 61b243a602..0548ea63d2 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -151,7 +151,7 @@ export const OPTIONS: OptionDescriptions> = { 'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") }, 'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") }, 'category': { type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extension.") }, - 'install-extension': { type: 'string[]', cat: 'e', args: 'extension-id | path-to-vsix', description: localize('installExtension', "Installs or updates the extension. Use `--force` argument to avoid prompts.") }, + 'install-extension': { type: 'string[]', cat: 'e', args: 'extension-id[@version] | path-to-vsix', description: localize('installExtension', "Installs or updates the extension. Use `--force` argument to avoid prompts. The identifier of an extension is always `${publisher}.${name}`. To install a specific version provide `@${version}`. For example: 'vscode.csharp@1.2.3'.") }, 'uninstall-extension': { type: 'string[]', cat: 'e', args: 'extension-id', description: localize('uninstallExtension', "Uninstalls an extension.") }, 'enable-proposed-api': { type: 'string[]', cat: 'e', args: 'extension-id', description: localize('experimentalApis', "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.") }, diff --git a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts index 1909b0e3d8..4e579ee84c 100644 --- a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts +++ b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts @@ -53,7 +53,7 @@ export class MockContextKeyService implements IContextKeyService { public get onDidChangeContext(): Event { return Event.None; } - public bufferChangeEvents() { } + public bufferChangeEvents(callback: () => void) { callback(); } public getContextKeyValue(key: string) { const value = this._keys.get(key); if (value) { diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 8d3f107c10..4be7ba2c78 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -21,6 +21,7 @@ import { IRelativePattern } from 'vs/base/common/glob'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; export class MainThreadNotebookDocument extends Disposable { private _textModel: NotebookTextModel; @@ -36,12 +37,13 @@ export class MainThreadNotebookDocument extends Disposable { public supportBackup: boolean, public uri: URI, @INotebookService readonly notebookService: INotebookService, - @IUndoRedoService readonly undoRedoService: IUndoRedoService + @IUndoRedoService readonly undoRedoService: IUndoRedoService, + @ITextModelService modelService: ITextModelService ) { super(); - this._textModel = new NotebookTextModel(handle, viewType, supportBackup, uri, undoRedoService); + this._textModel = new NotebookTextModel(handle, viewType, supportBackup, uri, undoRedoService, modelService); this._register(this._textModel.onDidModelChangeProxy(e => { this._proxy.$acceptModelChanged(this.uri, e); this._proxy.$acceptEditorPropertiesChanged(uri, { selections: { selections: this._textModel.selections }, metadata: null }); diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 41f05b9866..3de74ce80b 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -227,7 +227,7 @@ export class TreeViewDataProvider implements ITreeViewDataProvider { const resolvable = new ResolvableTreeItem(element, hasResolve ? () => { return this._proxy.$resolve(this.treeViewId, element.handle); } : undefined); - this.itemsMap.set(element.handle, element); + this.itemsMap.set(element.handle, resolvable); result.push(resolvable); } } diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 6ff9829ea0..77263bfa7e 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -210,7 +210,7 @@ export class WorkbenchContextKeysHandler extends Disposable { this.activeEditorContext.set(activeEditorPane.getId()); this.activeEditorIsReadonly.set(activeEditorPane.input.isReadonly()); - const editors = activeEditorPane.input.resource ? this.editorService.getEditorOverrides(activeEditorPane.input.resource, undefined, activeGroup) : []; + const editors = activeEditorPane.input.resource ? this.editorService.getEditorOverrides(activeEditorPane.input.resource, undefined, activeGroup, undefined) : []; this.activeEditorAvailableEditorIds.set(editors.map(([_, entry]) => entry.id).join(',')); } else { this.activeEditorContext.reset(); diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 7b95537ccd..d9bf6a23f7 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -457,6 +457,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCo MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_DOWN, title: nls.localize('splitDown', "Split Down") }, group: '5_split', order: 20 }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_LEFT, title: nls.localize('splitLeft', "Split Left") }, group: '5_split', order: 30 }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_RIGHT, title: nls.localize('splitRight', "Split Right") }, group: '5_split', order: 40 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: ReopenResourcesAction.ID, title: ReopenResourcesAction.LABEL }, group: '6_reopen', order: 20, when: ActiveEditorAvailableEditorIdsContext }); // Editor Title Menu MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_DIFF_SIDE_BY_SIDE, title: nls.localize('toggleInlineView', "Toggle Inline View") }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') }); diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 029e36aafa..de43eb5c67 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -1836,7 +1836,7 @@ export class ToggleEditorTypeAction extends Action { const options = activeEditorPane.options; const group = activeEditorPane.group; - const overrides = getAllAvailableEditors(input.resource, options, group, this.editorService); + const overrides = getAllAvailableEditors(input.resource, undefined, options, group, this.editorService); const firstNonActiveOverride = overrides.find(([_, entry]) => !entry.active); if (!firstNonActiveOverride) { return; diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css index d888e31c76..7f8b46ddc2 100644 --- a/src/vs/workbench/browser/parts/panel/media/panelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css @@ -50,10 +50,15 @@ outline-offset: -2px; } -.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.clicked:focus .action-label { +.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.clicked:not(.checked):focus .action-label { border-bottom-color: transparent !important; /* hides border on clicked state */ } +.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.checked.clicked:focus .action-label, +.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.checked.active:focus .action-label { + border-bottom-color: currentColor !important; +} + /** Panel Switcher */ .monaco-workbench .part.panel > .composite.title > .panel-switcher-container.composite-bar > .monaco-action-bar .action-label.codicon-more { diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 6442a54a11..14cd0f6ce1 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -430,7 +430,8 @@ export class CustomMenubarControl extends MenubarControl { primary: KeyCode.F10, weight: KeybindingWeight.WorkbenchContrib, when: IsWebContext - } + }, + f1: true }); } diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index a03725594e..dea5198c21 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -416,6 +416,10 @@ export class Workbench extends Layout { // Telemetry: startup metrics mark('didStartWorkbench'); + + // Perf reporting (devtools) + performance.mark('workbench-end'); + performance.measure('perf: workbench create & restore', 'workbench-start', 'workbench-end'); } } } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index ed7ccba168..c040f27b5c 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -451,29 +451,45 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo open: (editor, options, group) => { return this.onEditorOpening(editor, options, group); }, - getEditorOverrides: (resource: URI, _options: IEditorOptions | undefined, group: IEditorGroup | undefined): IOpenEditorOverrideEntry[] => { + getEditorOverrides: (resource: URI, _options: IEditorOptions | undefined, group: IEditorGroup | undefined, id: string | undefined): IOpenEditorOverrideEntry[] => { const currentEditor = group?.editors.find(editor => isEqual(editor.resource, resource)); + const defaultEditorOverride: IOpenEditorOverrideEntry = { + ...defaultEditorOverrideEntry, + active: this._fileEditorInputFactory.isFileEditorInput(currentEditor), + }; + + const toOverride = (entry: CustomEditorInfo): IOpenEditorOverrideEntry => { + return { + id: entry.id, + active: currentEditor instanceof CustomEditorInput && currentEditor.viewType === entry.id, + label: entry.displayName, + detail: entry.providerDisplayName, + }; + }; + + if (typeof id === 'string') { + // A specific override was requested. Only return it. + + if (id === defaultEditorOverride.id) { + return [defaultEditorOverride]; + } + + const matchingEditor = this.customEditorService.getCustomEditor(id); + return matchingEditor ? [toOverride(matchingEditor)] : []; + } + + // Otherwise, return all potential overrides. const customEditors = this.customEditorService.getAllCustomEditors(resource); if (!customEditors.length) { return []; } return [ - { - ...defaultEditorOverrideEntry, - active: this._fileEditorInputFactory.isFileEditorInput(currentEditor), - }, + defaultEditorOverride, ...customEditors.allEditors .filter(entry => entry.id !== defaultCustomEditor.id) - .map(entry => { - return { - id: entry.id, - active: currentEditor instanceof CustomEditorInput && currentEditor.viewType === entry.id, - label: entry.displayName, - detail: entry.providerDisplayName, - }; - }) + .map(toOverride) ]; } })); diff --git a/src/vs/workbench/contrib/debug/browser/debugActions.ts b/src/vs/workbench/contrib/debug/browser/debugActions.ts index 793f5b9d9a..08fb9f950b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActions.ts @@ -98,7 +98,10 @@ export class ConfigureAction extends AbstractDebugAction { launch = launches[0]; } else { const picks = launches.map(l => ({ label: l.name, launch: l })); - const picked = await this.quickInputService.pick<{ label: string, launch: ILaunch }>(picks, { activeItem: picks[0], placeHolder: nls.localize('selectWorkspaceFolder', "Select a workspace folder to create a launch.json file in or add it to the workspace config file") }); + const picked = await this.quickInputService.pick<{ label: string, launch: ILaunch }>(picks, { + activeItem: picks[0], + placeHolder: nls.localize({ key: 'selectWorkspaceFolder', comment: ['User picks a workspace folder or a workspace configuration file here. Workspace configuration files can contain settings and thus a launch.json configuration can be written into one.'] }, "Select a workspace folder to create a launch.json file in or add it to the workspace config file") + }); if (picked) { launch = picked.launch; } diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index a53d44beb1..5c75fb445c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -247,7 +247,7 @@ class ShowDebugHoverAction extends EditorAction { class StepIntoTargetsAction extends EditorAction { public static readonly ID = 'editor.debug.action.stepIntoTargets'; - public static readonly LABEL = nls.localize('stepIntoTargets', "Step Into Targets..."); + public static readonly LABEL = nls.localize({ key: 'stepIntoTargets', comment: ['Step Into Targets lets the user step into an exact function he or she is interested in.'] }, "Step Into Targets..."); constructor() { super({ diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 7a88a26fd2..43ad6b8efe 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -61,11 +61,11 @@ export class DebugService implements IDebugService { private taskRunner: DebugTaskRunner; private configurationManager: ConfigurationManager; private toDispose: IDisposable[]; - private debugType: IContextKey; - private debugState: IContextKey; - private inDebugMode: IContextKey; - private debugUx: IContextKey; - private breakpointsExist: IContextKey; + private debugType!: IContextKey; + private debugState!: IContextKey; + private inDebugMode!: IContextKey; + private debugUx!: IContextKey; + private breakpointsExist!: IContextKey; private breakpointsToSendOnResourceSaved: Set; private initializing = false; private previousState: State | undefined; @@ -80,7 +80,7 @@ export class DebugService implements IDebugService { @IDialogService private readonly dialogService: IDialogService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IContextKeyService contextKeyService: IContextKeyService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, @ILifecycleService private readonly lifecycleService: ILifecycleService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IExtensionService private readonly extensionService: IExtensionService, @@ -101,17 +101,19 @@ export class DebugService implements IDebugService { this.configurationManager = this.instantiationService.createInstance(ConfigurationManager); this.toDispose.push(this.configurationManager); - this.debugType = CONTEXT_DEBUG_TYPE.bindTo(contextKeyService); - this.debugState = CONTEXT_DEBUG_STATE.bindTo(contextKeyService); - this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); - this.debugUx = CONTEXT_DEBUG_UX.bindTo(contextKeyService); - this.debugUx.set(!!this.configurationManager.selectedConfiguration.name ? 'default' : 'simple'); + contextKeyService.bufferChangeEvents(() => { + this.debugType = CONTEXT_DEBUG_TYPE.bindTo(contextKeyService); + this.debugState = CONTEXT_DEBUG_STATE.bindTo(contextKeyService); + this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); + this.debugUx = CONTEXT_DEBUG_UX.bindTo(contextKeyService); + this.debugUx.set(!!this.configurationManager.selectedConfiguration.name ? 'default' : 'simple'); + this.breakpointsExist = CONTEXT_BREAKPOINTS_EXIST.bindTo(contextKeyService); + }); this.debugStorage = this.instantiationService.createInstance(DebugStorage); this.model = this.instantiationService.createInstance(DebugModel, this.debugStorage); this.telemetry = this.instantiationService.createInstance(DebugTelemetry, this.model); const setBreakpointsExistContext = () => this.breakpointsExist.set(!!(this.model.getBreakpoints().length || this.model.getDataBreakpoints().length || this.model.getFunctionBreakpoints().length)); - this.breakpointsExist = CONTEXT_BREAKPOINTS_EXIST.bindTo(contextKeyService); setBreakpointsExistContext(); this.viewModel = new ViewModel(contextKeyService); @@ -229,10 +231,12 @@ export class DebugService implements IDebugService { private onStateChange(): void { const state = this.state; if (this.previousState !== state) { - this.debugState.set(getStateLabel(state)); - this.inDebugMode.set(state !== State.Inactive); - // Only show the simple ux if debug is not yet started and if no launch.json exists - this.debugUx.set(((state !== State.Inactive && state !== State.Initializing) || this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple'); + this.contextKeyService.bufferChangeEvents(() => { + this.debugState.set(getStateLabel(state)); + this.inDebugMode.set(state !== State.Inactive); + // Only show the simple ux if debug is not yet started and if no launch.json exists + this.debugUx.set(((state !== State.Inactive && state !== State.Initializing) || this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple'); + }); this.previousState = state; this._onDidChangeState.fire(state); } diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index 2394c299e3..a43ce3d3cb 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -78,7 +78,9 @@ } /* Make icons and text the same color as the list foreground on focus selection */ +.debug-pane .monaco-list:focus .monaco-list-row.selected .state > .label, .debug-pane .monaco-list:focus .monaco-list-row.selected.focused .state > .label, +.debug-pane .monaco-list:focus .monaco-list-row.selected .codicon, .debug-pane .monaco-list:focus .monaco-list-row.selected.focused .codicon { color: inherit !important; } diff --git a/src/vs/workbench/contrib/debug/browser/replViewer.ts b/src/vs/workbench/contrib/debug/browser/replViewer.ts index 07c6fa43ca..923c3367fd 100644 --- a/src/vs/workbench/contrib/debug/browser/replViewer.ts +++ b/src/vs/workbench/contrib/debug/browser/replViewer.ts @@ -362,10 +362,10 @@ export class ReplAccessibilityProvider implements IListAccessibilityProvider(); private readonly _onDidSelectExpression = new Emitter(); private multiSessionView: boolean; - private expressionSelectedContextKey: IContextKey; - private breakpointSelectedContextKey: IContextKey; - private loadedScriptsSupportedContextKey: IContextKey; - private stepBackSupportedContextKey: IContextKey; - private focusedSessionIsAttach: IContextKey; - private restartFrameSupportedContextKey: IContextKey; - private stepIntoTargetsSupported: IContextKey; - private jumpToCursorSupported: IContextKey; + private expressionSelectedContextKey!: IContextKey; + private breakpointSelectedContextKey!: IContextKey; + private loadedScriptsSupportedContextKey!: IContextKey; + private stepBackSupportedContextKey!: IContextKey; + private focusedSessionIsAttach!: IContextKey; + private restartFrameSupportedContextKey!: IContextKey; + private stepIntoTargetsSupported!: IContextKey; + private jumpToCursorSupported!: IContextKey; - constructor(contextKeyService: IContextKeyService) { + constructor(private contextKeyService: IContextKeyService) { this.multiSessionView = false; - this.expressionSelectedContextKey = CONTEXT_EXPRESSION_SELECTED.bindTo(contextKeyService); - this.breakpointSelectedContextKey = CONTEXT_BREAKPOINT_SELECTED.bindTo(contextKeyService); - this.loadedScriptsSupportedContextKey = CONTEXT_LOADED_SCRIPTS_SUPPORTED.bindTo(contextKeyService); - this.stepBackSupportedContextKey = CONTEXT_STEP_BACK_SUPPORTED.bindTo(contextKeyService); - this.focusedSessionIsAttach = CONTEXT_FOCUSED_SESSION_IS_ATTACH.bindTo(contextKeyService); - this.restartFrameSupportedContextKey = CONTEXT_RESTART_FRAME_SUPPORTED.bindTo(contextKeyService); - this.stepIntoTargetsSupported = CONTEXT_STEP_INTO_TARGETS_SUPPORTED.bindTo(contextKeyService); - this.jumpToCursorSupported = CONTEXT_JUMP_TO_CURSOR_SUPPORTED.bindTo(contextKeyService); + contextKeyService.bufferChangeEvents(() => { + this.expressionSelectedContextKey = CONTEXT_EXPRESSION_SELECTED.bindTo(contextKeyService); + this.breakpointSelectedContextKey = CONTEXT_BREAKPOINT_SELECTED.bindTo(contextKeyService); + this.loadedScriptsSupportedContextKey = CONTEXT_LOADED_SCRIPTS_SUPPORTED.bindTo(contextKeyService); + this.stepBackSupportedContextKey = CONTEXT_STEP_BACK_SUPPORTED.bindTo(contextKeyService); + this.focusedSessionIsAttach = CONTEXT_FOCUSED_SESSION_IS_ATTACH.bindTo(contextKeyService); + this.restartFrameSupportedContextKey = CONTEXT_RESTART_FRAME_SUPPORTED.bindTo(contextKeyService); + this.stepIntoTargetsSupported = CONTEXT_STEP_INTO_TARGETS_SUPPORTED.bindTo(contextKeyService); + this.jumpToCursorSupported = CONTEXT_JUMP_TO_CURSOR_SUPPORTED.bindTo(contextKeyService); + }); } getId(): string { @@ -66,13 +68,15 @@ export class ViewModel implements IViewModel { this._focusedThread = thread; this._focusedSession = session; - this.loadedScriptsSupportedContextKey.set(session ? !!session.capabilities.supportsLoadedSourcesRequest : false); - this.stepBackSupportedContextKey.set(session ? !!session.capabilities.supportsStepBack : false); - this.restartFrameSupportedContextKey.set(session ? !!session.capabilities.supportsRestartFrame : false); - this.stepIntoTargetsSupported.set(session ? !!session.capabilities.supportsStepInTargetsRequest : false); - this.jumpToCursorSupported.set(session ? !!session.capabilities.supportsGotoTargetsRequest : false); - const attach = !!session && isSessionAttach(session); - this.focusedSessionIsAttach.set(attach); + this.contextKeyService.bufferChangeEvents(() => { + this.loadedScriptsSupportedContextKey.set(session ? !!session.capabilities.supportsLoadedSourcesRequest : false); + this.stepBackSupportedContextKey.set(session ? !!session.capabilities.supportsStepBack : false); + this.restartFrameSupportedContextKey.set(session ? !!session.capabilities.supportsRestartFrame : false); + this.stepIntoTargetsSupported.set(session ? !!session.capabilities.supportsStepInTargetsRequest : false); + this.jumpToCursorSupported.set(session ? !!session.capabilities.supportsGotoTargetsRequest : false); + const attach = !!session && isSessionAttach(session); + this.focusedSessionIsAttach.set(attach); + }); if (shouldEmitForSession) { this._onDidFocusSession.fire(session); diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index 09bee56e9c..72247969d5 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts @@ -175,7 +175,7 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa } else if (canHandlePermissionOrReadonlyErrors && isPermissionDenied) { message = isWindows ? nls.localize('permissionDeniedSaveError', "Failed to save '{0}': Insufficient permissions. Select 'Retry as Admin' to retry as administrator.", basename(resource)) : nls.localize('permissionDeniedSaveErrorSudo', "Failed to save '{0}': Insufficient permissions. Select 'Retry as Sudo' to retry as superuser.", basename(resource)); } else { - message = nls.localize('genericSaveError', "Failed to save '{0}': {1}", basename(resource), toErrorMessage(error, false)); + message = nls.localize({ key: 'genericSaveError', comment: ['{0} is the resource that failed to save and {1} the error message'] }, "Failed to save '{0}': {1}", basename(resource), toErrorMessage(error, false)); } } diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 858c611aea..9eb57377dc 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -410,7 +410,7 @@ async function doSaveEditors(accessor: ServicesAccessor, editors: IEditorIdentif try { await editorService.save(editors, options); } catch (error) { - notificationService.error(nls.localize('genericSaveError', "Failed to save '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false))); + notificationService.error(nls.localize({ key: 'genericSaveError', comment: ['{0} is the resource that failed to save and {1} the error message'] }, "Failed to save '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false))); } } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index b72c4653f8..225cdb55cf 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -486,7 +486,7 @@ export class ExplorerView extends ViewPane { this.rootContext.set(!stat || (stat && stat.isRoot)); if (resource) { - const overrides = resource ? this.editorService.getEditorOverrides(resource, undefined, undefined) : []; + const overrides = resource ? this.editorService.getEditorOverrides(resource, undefined, undefined, undefined) : []; this.availableEditorIdsContext.set(overrides.map(([, entry]) => entry.id).join(',')); } else { this.availableEditorIdsContext.reset(); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 0058703221..05f4c4ffe9 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -518,7 +518,8 @@ registerAction2(class extends InsertCellCommand { MenuRegistry.appendMenuItem(MenuId.NotebookCellBetween, { command: { id: INSERT_CODE_CELL_BELOW_COMMAND_ID, - title: localize('notebookActions.menu.insertCode', "$(add) Code") + title: localize('notebookActions.menu.insertCode', "$(add) Code"), + tooltip: localize('notebookActions.menu.insertCode.tooltip', "Add Code Cell") }, order: 0, group: 'inline' @@ -551,7 +552,8 @@ registerAction2(class extends InsertCellCommand { MenuRegistry.appendMenuItem(MenuId.NotebookCellBetween, { command: { id: INSERT_MARKDOWN_CELL_BELOW_COMMAND_ID, - title: localize('notebookActions.menu.insertMarkdown', "$(add) Markdown") + title: localize('notebookActions.menu.insertMarkdown', "$(add) Markdown"), + tooltip: localize('notebookActions.menu.insertMarkdown.tooltip', "Add Markdown Cell") }, order: 1, group: 'inline' diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index b51d27ac56..ab3d07a16f 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -17,6 +17,11 @@ white-space: initial; } +.monaco-workbench .cell.markdown p.emptyMarkdownPlaceholder { + font-style: italic; + opacity: 0.6; +} + .monaco-workbench .notebookOverlay .simple-fr-find-part-wrapper.visible { z-index: 100; } @@ -308,8 +313,8 @@ } .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar .codicon { - margin: 0 0 0 2px; /* accounts for 2px indicator on left */ - padding: 4px; + margin: 0; + padding: 0 4px; } .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar .actions-container { @@ -324,14 +329,14 @@ .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .execution-count-label { position: absolute; - top: 2px; + top: -2px; font-size: 10px; font-family: var(--monaco-monospace-font); visibility: visible; white-space: pre; width: 100%; text-align: center; - padding-right: 2px; + padding-right: 8px; box-sizing: border-box; opacity: .6; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index e813dbae5e..9928571b5c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -57,6 +57,7 @@ import 'vs/workbench/contrib/notebook/browser/contrib/status/editorStatus'; import 'vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform'; import 'vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform'; import 'vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; /*--------------------------------------------------------------------------------------------- */ @@ -247,7 +248,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri cellOptions = { resource: originalInput.resource, options }; } - if (id === undefined) { + if (id === undefined && originalInput instanceof ResourceEditorInput) { const exitingNotebookEditor = group.editors.find(editor => editor instanceof NotebookEditorInput && isEqual(editor.resource, notebookUri)); id = exitingNotebookEditor?.viewType; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts index 978a91e1b0..3389193b58 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts @@ -84,7 +84,13 @@ export class NotebookEditorInput extends EditorInput { async save(group: GroupIdentifier, options?: ISaveOptions): Promise { if (this._textModel) { - await this._textModel.object.save(); + + if (this.isUntitled()) { + return this.saveAs(group, options); + } else { + await this._textModel.object.save(); + } + return this; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index c57e360937..29e4b49ba6 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -921,7 +921,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor const insertIndex = cell ? (direction === 'above' ? index : nextIndex) : index; - const newCell = this._notebookViewModel!.createCell(insertIndex, initialText.split(/\r?\n/g), language, type, cell?.metadata, true); + const newCell = this._notebookViewModel!.createCell(insertIndex, initialText.split(/\r?\n/g), language, type, undefined, true); return newCell as CellViewModel; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 71ea4ca113..4e7b083fa6 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -666,9 +666,7 @@ export class CellDragAndDropController extends Disposable { } private async moveCell(draggedCell: ICellViewModel, ontoCell: ICellViewModel, direction: 'above' | 'below') { - const editState = draggedCell.editState; await this.notebookEditor.moveCell(draggedCell, ontoCell, direction); - this.notebookEditor.focusNotebookCell(draggedCell, editState === CellEditState.Editing ? 'editor' : 'container'); } private copyCell(draggedCell: ICellViewModel, ontoCell: ICellViewModel, direction: 'above' | 'below') { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 5f740c62eb..28e978c3c1 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -7,7 +7,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import * as UUID from 'vs/base/common/uuid'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_RUN_GUTTER, CELL_STATUSBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants'; import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, ICellViewModel, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -69,8 +68,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod readonly viewType: string, readonly model: NotebookCellTextModel, initialNotebookLayoutInfo: NotebookLayoutInfo | null, - readonly eventDispatcher: NotebookEventDispatcher, - @ITextModelService private readonly _modelService: ITextModelService, + readonly eventDispatcher: NotebookEventDispatcher ) { super(viewType, model, UUID.generateUuid()); this._register(this.model.onDidChangeOutputs((splices) => { @@ -174,7 +172,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod */ async resolveTextModel(): Promise { if (!this.textModel) { - const ref = await this._modelService.createModelReference(this.model.uri); + const ref = await this.model.resolveTextModelRef(); this.textModel = ref.object.textEditorModel; this._register(ref); this._register(this.textModel.onDidChangeContent(() => { @@ -257,4 +255,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod matches }; } + + dispose() { + super.dispose(); + } } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts index a138c5b512..16639888ba 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts @@ -3,12 +3,12 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { Emitter, Event } from 'vs/base/common/event'; import * as UUID from 'vs/base/common/uuid'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_RUN_GUTTER, CELL_STATUSBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_RUN_GUTTER, CELL_STATUSBAR_HEIGHT, EDITOR_TOP_MARGIN, CELL_BOTTOM_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants'; import { CellFindMatch, ICellViewModel, MarkdownCellLayoutChangeEvent, MarkdownCellLayoutInfo, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer'; import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; @@ -45,7 +45,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie set editorHeight(newHeight: number) { this._editorHeight = newHeight; - this.totalHeight = this._editorHeight + BOTTOM_CELL_TOOLBAR_HEIGHT + CELL_STATUSBAR_HEIGHT; + this.totalHeight = this._editorHeight + EDITOR_TOP_MARGIN + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_HEIGHT + CELL_STATUSBAR_HEIGHT; } get editorHeight() { @@ -65,8 +65,8 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie initialNotebookLayoutInfo: NotebookLayoutInfo | null, readonly foldingDelegate: EditorFoldingStateDelegate, readonly eventDispatcher: NotebookEventDispatcher, - private readonly _mdRenderer: MarkdownRenderer, - @ITextModelService private readonly _modelService: ITextModelService) { + private readonly _mdRenderer: MarkdownRenderer + ) { super(viewType, model, UUID.generateUuid()); this._layoutInfo = { @@ -140,7 +140,17 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie return this._html; } let renderer = this.getMarkdownRenderer(); - this._html = renderer.render({ value: this.getText(), isTrusted: true }).element; + const text = this.getText(); + + if (text.length === 0) { + const el = document.createElement('p'); + el.className = 'emptyMarkdownPlaceholder'; + el.innerText = nls.localize('notebook.emptyMarkdownPlaceholder', "Empty markdown cell, double click or press enter to edit."); + this._html = el; + } else { + this._html = renderer.render({ value: this.getText(), isTrusted: true }).element; + } + return this._html; } return null; @@ -148,7 +158,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie async resolveTextModel(): Promise { if (!this.textModel) { - const ref = await this._modelService.createModelReference(this.model.uri); + const ref = await this.model.resolveTextModelRef(); this.textModel = ref.object.textEditorModel; this._register(ref); this._register(this.textModel.onDidChangeContent(() => { diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index 4a9f342505..035ed45257 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -10,6 +10,7 @@ import { URI } from 'vs/base/common/uri'; import * as model from 'vs/editor/common/model'; import { Range } from 'vs/editor/common/core/range'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; export class NotebookCellTextModel extends Disposable implements ICell { private _onDidChangeOutputs = new Emitter(); @@ -86,7 +87,8 @@ export class NotebookCellTextModel extends Disposable implements ICell { private _language: string, public cellKind: CellKind, outputs: IProcessedOutput[], - metadata: NotebookCellMetadata | undefined + metadata: NotebookCellMetadata | undefined, + private readonly _modelService: ITextModelService ) { super(); this._outputs = outputs; @@ -139,4 +141,17 @@ export class NotebookCellTextModel extends Disposable implements ICell { } }; } + + async resolveTextModelRef() { + const ref = await this._modelService.createModelReference(this.uri); + + ref.object.textEditorModel.onWillDispose(() => { + this.textModel = undefined; + }); + return ref; + } + + dispose() { + super.dispose(); + } } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 7bedf791f1..4052080dad 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -11,6 +11,7 @@ import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSpl import { ITextSnapshot } from 'vs/editor/common/model'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { InsertCellEdit, DeleteCellEdit, MoveCellEdit, SpliceCellsEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; function compareRangesUsingEnds(a: [number, number], b: [number, number]): number { if (a[1] === b[1]) { @@ -115,7 +116,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel public viewType: string, public supportBackup: boolean, public uri: URI, - private _undoService: IUndoRedoService + private _undoService: IUndoRedoService, + private _modelService: ITextModelService ) { super(); this.cells = []; @@ -141,7 +143,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel ) { const cellHandle = this._cellhandlePool++; const cellUri = CellUri.generate(this.uri, cellHandle); - return new NotebookCellTextModel(cellUri, cellHandle, source, language, cellKind, outputs || [], metadata); + return new NotebookCellTextModel(cellUri, cellHandle, source, language, cellKind, outputs || [], metadata, this._modelService); } initialize(cells: ICellDto2[]) { @@ -151,7 +153,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const mainCells = cells.map(cell => { const cellHandle = this._cellhandlePool++; const cellUri = CellUri.generate(this.uri, cellHandle); - return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata); + return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata, this._modelService); }); this._isUntitled = false; @@ -215,7 +217,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const mainCells = insertEdit.cells.map(cell => { const cellHandle = this._cellhandlePool++; const cellUri = CellUri.generate(this.uri, cellHandle); - return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata); + return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata, this._modelService); }); this.insertNewCell(insertEdit.index, mainCells, false); break; @@ -583,6 +585,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel dispose() { this._onWillDispose.fire(); this._cellListeners.forEach(val => val.dispose()); + this.cells.forEach(cell => cell.dispose()); super.dispose(); } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 2f6281cbc0..793b532fa6 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -148,6 +148,7 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN const notebook = await this._notebookService.createNotebookFromBackup(this.viewType!, this.resource, data.metadata, data.languages, data.cells, editorId); this._notebook = notebook!; + this._register(this._notebook); this._name = basename(this._notebook!.uri); @@ -167,6 +168,7 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN private async loadFromProvider(forceReloadFromDisk: boolean, editorId: string | undefined, backupId: string | undefined) { const notebook = await this._notebookService.resolveNotebook(this.viewType!, this.resource, forceReloadFromDisk, editorId, backupId); this._notebook = notebook!; + this._register(this._notebook); this._name = basename(this._notebook!.uri); @@ -210,4 +212,8 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN this._notebook.setDirty(false); return true; } + + dispose() { + super.dispose(); + } } diff --git a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts index fda697b9b2..8e004b9bc2 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts @@ -5,10 +5,14 @@ import * as assert from 'assert'; import { NOTEBOOK_DISPLAY_ORDER, sortMimeTypes, CellKind, diff, CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { TestCell } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; +import { TestCell, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; import { URI } from 'vs/base/common/uri'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; suite('NotebookCommon', () => { + const instantiationService = setupInstantiationService(); + const textModelService = instantiationService.get(ITextModelService); + test('sortMimeTypes default orders', function () { const defaultDisplayOrder = NOTEBOOK_DISPLAY_ORDER; @@ -265,7 +269,7 @@ suite('NotebookCommon', () => { for (let i = 0; i < 5; i++) { cells.push( - new TestCell('notebook', i, [`var a = ${i};`], 'javascript', CellKind.Code, []) + new TestCell('notebook', i, [`var a = ${i};`], 'javascript', CellKind.Code, [], textModelService) ); } @@ -291,8 +295,8 @@ suite('NotebookCommon', () => { ] ); - const cellA = new TestCell('notebook', 6, ['var a = 6;'], 'javascript', CellKind.Code, []); - const cellB = new TestCell('notebook', 7, ['var a = 7;'], 'javascript', CellKind.Code, []); + const cellA = new TestCell('notebook', 6, ['var a = 6;'], 'javascript', CellKind.Code, [], textModelService); + const cellB = new TestCell('notebook', 7, ['var a = 7;'], 'javascript', CellKind.Code, [], textModelService); const modifiedCells = [ cells[0], diff --git a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts index ff01699346..fb1c353091 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts @@ -4,14 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { CellKind, CellEditType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { withTestNotebook, TestCell } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; +import { withTestNotebook, TestCell, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; suite('NotebookTextModel', () => { - const instantiationService = new TestInstantiationService(); + const instantiationService = setupInstantiationService(); + const textModelService = instantiationService.get(ITextModelService); const blukEditService = instantiationService.get(IBulkEditService); const undoRedoService = instantiationService.stub(IUndoRedoService, () => { }); instantiationService.spy(IUndoRedoService, 'pushElement'); @@ -29,8 +30,8 @@ suite('NotebookTextModel', () => { ], (editor, viewModel, textModel) => { textModel.$applyEdit(textModel.versionId, [ - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [])] }, - { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 6, ['var f = 6;'], 'javascript', CellKind.Code, [])] }, + { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 6, ['var f = 6;'], 'javascript', CellKind.Code, [], textModelService)] }, ], true, true); assert.equal(textModel.cells.length, 6); @@ -54,8 +55,8 @@ suite('NotebookTextModel', () => { ], (editor, viewModel, textModel) => { textModel.$applyEdit(textModel.versionId, [ - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [])] }, - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 6, ['var f = 6;'], 'javascript', CellKind.Code, [])] }, + { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 6, ['var f = 6;'], 'javascript', CellKind.Code, [], textModelService)] }, ], true, true); assert.equal(textModel.cells.length, 6); @@ -103,7 +104,7 @@ suite('NotebookTextModel', () => { (editor, viewModel, textModel) => { textModel.$applyEdit(textModel.versionId, [ { editType: CellEditType.Delete, index: 1, count: 1 }, - { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [])] }, + { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [], textModelService)] }, ], true, true); assert.equal(textModel.cells.length, 4); @@ -128,7 +129,7 @@ suite('NotebookTextModel', () => { (editor, viewModel, textModel) => { textModel.$applyEdit(textModel.versionId, [ { editType: CellEditType.Delete, index: 1, count: 1 }, - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [])] }, + { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [], textModelService)] }, ], true, true); assert.equal(textModel.cells.length, 4); diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index c4bfdb2bb7..ee853f1b07 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -5,25 +5,25 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellKind, NotebookCellMetadata, diff } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { withTestNotebook, TestCell, NotebookEditorTestModel } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; +import { withTestNotebook, TestCell, NotebookEditorTestModel, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { TrackedRangeStickiness } from 'vs/editor/common/model'; import { reduceCellRanges, ICellRange } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; suite('NotebookViewModel', () => { - const instantiationService = new TestInstantiationService(); + const instantiationService = setupInstantiationService(); + const textModelService = instantiationService.get(ITextModelService); const blukEditService = instantiationService.get(IBulkEditService); - const undoRedoService = instantiationService.stub(IUndoRedoService, () => { }); - instantiationService.spy(IUndoRedoService, 'pushElement'); + const undoRedoService = instantiationService.get(IUndoRedoService); test('ctor', function () { - const notebook = new NotebookTextModel(0, 'notebook', false, URI.parse('test'), undoRedoService); + const notebook = new NotebookTextModel(0, 'notebook', false, URI.parse('test'), undoRedoService, textModelService); const model = new NotebookEditorTestModel(notebook); const eventDispatcher = new NotebookEventDispatcher(); const viewModel = new NotebookViewModel('notebook', model.notebook, eventDispatcher, null, instantiationService, blukEditService, undoRedoService); @@ -43,7 +43,7 @@ suite('NotebookViewModel', () => { assert.equal(viewModel.viewCells[0].metadata?.editable, true); assert.equal(viewModel.viewCells[1].metadata?.editable, false); - const cell = viewModel.insertCell(1, new TestCell(viewModel.viewType, 0, ['var c = 3;'], 'javascript', CellKind.Code, []), true); + const cell = viewModel.insertCell(1, new TestCell(viewModel.viewType, 0, ['var c = 3;'], 'javascript', CellKind.Code, [], textModelService), true); assert.equal(viewModel.viewCells.length, 3); assert.equal(viewModel.notebookDocument.cells.length, 3); assert.equal(viewModel.getCellIndex(cell), 1); @@ -126,13 +126,13 @@ suite('NotebookViewModel', () => { const lastViewCell = viewModel.viewCells[viewModel.viewCells.length - 1]; const insertIndex = viewModel.getCellIndex(firstViewCell) + 1; - const cell = viewModel.insertCell(insertIndex, new TestCell(viewModel.viewType, 3, ['var c = 3;'], 'javascript', CellKind.Code, []), true); + const cell = viewModel.insertCell(insertIndex, new TestCell(viewModel.viewType, 3, ['var c = 3;'], 'javascript', CellKind.Code, [], textModelService), true); const addedCellIndex = viewModel.getCellIndex(cell); viewModel.deleteCell(addedCellIndex, true); const secondInsertIndex = viewModel.getCellIndex(lastViewCell) + 1; - const cell2 = viewModel.insertCell(secondInsertIndex, new TestCell(viewModel.viewType, 4, ['var d = 4;'], 'javascript', CellKind.Code, []), true); + const cell2 = viewModel.insertCell(secondInsertIndex, new TestCell(viewModel.viewType, 4, ['var d = 4;'], 'javascript', CellKind.Code, [], textModelService), true); assert.equal(viewModel.viewCells.length, 3); assert.equal(viewModel.notebookDocument.cells.length, 3); @@ -258,10 +258,10 @@ function getVisibleCells(cells: T[], hiddenRanges: ICellRange[]) { } suite('NotebookViewModel Decorations', () => { - const instantiationService = new TestInstantiationService(); + const instantiationService = setupInstantiationService(); + const textModelService = instantiationService.get(ITextModelService); const blukEditService = instantiationService.get(IBulkEditService); - const undoRedoService = instantiationService.stub(IUndoRedoService, () => { }); - instantiationService.spy(IUndoRedoService, 'pushElement'); + const undoRedoService = instantiationService.get(IUndoRedoService); test('tracking range', function () { withTestNotebook( @@ -283,7 +283,7 @@ suite('NotebookViewModel Decorations', () => { end: 2, }); - viewModel.insertCell(0, new TestCell(viewModel.viewType, 5, ['var d = 6;'], 'javascript', CellKind.Code, []), true); + viewModel.insertCell(0, new TestCell(viewModel.viewType, 5, ['var d = 6;'], 'javascript', CellKind.Code, [], textModelService), true); assert.deepEqual(viewModel.getTrackedRange(trackedId!), { start: 2, @@ -297,7 +297,7 @@ suite('NotebookViewModel Decorations', () => { end: 2 }); - viewModel.insertCell(3, new TestCell(viewModel.viewType, 6, ['var d = 7;'], 'javascript', CellKind.Code, []), true); + viewModel.insertCell(3, new TestCell(viewModel.viewType, 6, ['var d = 7;'], 'javascript', CellKind.Code, [], textModelService), true); assert.deepEqual(viewModel.getTrackedRange(trackedId!), { start: 1, @@ -343,14 +343,14 @@ suite('NotebookViewModel Decorations', () => { end: 3 }); - viewModel.insertCell(5, new TestCell(viewModel.viewType, 8, ['var d = 9;'], 'javascript', CellKind.Code, []), true); + viewModel.insertCell(5, new TestCell(viewModel.viewType, 8, ['var d = 9;'], 'javascript', CellKind.Code, [], textModelService), true); assert.deepEqual(viewModel.getTrackedRange(trackedId!), { start: 1, end: 3 }); - viewModel.insertCell(4, new TestCell(viewModel.viewType, 9, ['var d = 10;'], 'javascript', CellKind.Code, []), true); + viewModel.insertCell(4, new TestCell(viewModel.viewType, 9, ['var d = 10;'], 'javascript', CellKind.Code, [], textModelService), true); assert.deepEqual(viewModel.getTrackedRange(trackedId!), { start: 1, diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index f9c99b4d4f..6f96518eac 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -10,7 +10,6 @@ import { URI } from 'vs/base/common/uri'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { Range } from 'vs/editor/common/core/range'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { EditorModel } from 'vs/workbench/common/editor'; import { ICellRange, ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -24,6 +23,16 @@ import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { ICompositeCodeEditor, IEditor } from 'vs/editor/common/editorCommon'; import { NotImplementedError } from 'vs/base/common/errors'; import { Schemas } from 'vs/base/common/network'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; export class TestCell extends NotebookCellTextModel { constructor( @@ -32,9 +41,10 @@ export class TestCell extends NotebookCellTextModel { public source: string[], language: string, cellKind: CellKind, - outputs: IProcessedOutput[] + outputs: IProcessedOutput[], + modelService: ITextModelService ) { - super(CellUri.generate(URI.parse('test:///fake/notebook'), handle), handle, source, language, cellKind, outputs, undefined); + super(CellUri.generate(URI.parse('test:///fake/notebook'), handle), handle, source, language, cellKind, outputs, undefined, modelService); } } @@ -317,12 +327,26 @@ export class NotebookEditorTestModel extends EditorModel implements INotebookEdi } } -export function withTestNotebook(instantiationService: IInstantiationService, blukEditService: IBulkEditService, undoRedoService: IUndoRedoService, cells: [string[], string, CellKind, IProcessedOutput[], NotebookCellMetadata][], callback: (editor: TestNotebookEditor, viewModel: NotebookViewModel, textModel: NotebookTextModel) => void) { +export function setupInstantiationService() { + const instantiationService = new TestInstantiationService(); + + instantiationService.stub(IUndoRedoService, instantiationService.createInstance(UndoRedoService)); + instantiationService.stub(IConfigurationService, new TestConfigurationService()); + instantiationService.stub(IThemeService, new TestThemeService()); + instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); + instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); + + return instantiationService; +} + +export function withTestNotebook(instantiationService: TestInstantiationService, blukEditService: IBulkEditService, undoRedoService: IUndoRedoService, cells: [string[], string, CellKind, IProcessedOutput[], NotebookCellMetadata][], callback: (editor: TestNotebookEditor, viewModel: NotebookViewModel, textModel: NotebookTextModel) => void) { + const textModelService = instantiationService.get(ITextModelService); + const viewType = 'notebook'; const editor = new TestNotebookEditor(); - const notebook = new NotebookTextModel(0, viewType, false, URI.parse('test'), undoRedoService); + const notebook = new NotebookTextModel(0, viewType, false, URI.parse('test'), undoRedoService, textModelService); notebook.cells = cells.map((cell, index) => { - return new NotebookCellTextModel(notebook.uri, index, cell[0], cell[1], cell[2], cell[3], cell[4]); + return new NotebookCellTextModel(notebook.uri, index, cell[0], cell[1], cell[2], cell[3], cell[4], textModelService); }); const model = new NotebookEditorTestModel(notebook); const eventDispatcher = new NotebookEventDispatcher(); diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index d4031b2ca5..067b4dd83f 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -46,6 +46,7 @@ import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { preferencesEditIcon } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; import { Color, RGBA } from 'vs/base/common/color'; import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; +import { ThemableCheckboxActionViewItem } from 'vs/platform/browser/checkbox'; const $ = DOM.$; @@ -376,13 +377,17 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditorP const actionBar = this._register(new ActionBar(this.actionsContainer, { animated: false, actionViewItemProvider: (action: IAction) => { + let checkboxViewItem: CheckboxActionViewItem | undefined; if (action.id === this.sortByPrecedenceAction.id) { - return new CheckboxActionViewItem(null, action); + checkboxViewItem = new ThemableCheckboxActionViewItem(null, action, undefined, this.themeService); } - if (action.id === this.recordKeysAction.id) { - return new CheckboxActionViewItem(null, action); + else if (action.id === this.recordKeysAction.id) { + checkboxViewItem = new ThemableCheckboxActionViewItem(null, action, undefined, this.themeService); } - return undefined; + if (checkboxViewItem) { + + } + return checkboxViewItem; } })); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 85fb09e0cf..867478aca2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -415,12 +415,12 @@ export class ClearTerminalAction extends Action { } } -export class TerminalLaunchTroubleshootAction extends Action { +export class TerminalLaunchHelpAction extends Action { constructor( @IOpenerService private readonly _openerService: IOpenerService ) { - super('workbench.action.terminal.launchHelp', localize('terminalLaunchTroubleshoot', "Troubleshoot")); + super('workbench.action.terminal.launchHelp', localize('terminalLaunchHelp', "Open Help")); } async run(): Promise { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index b0f1eb2677..906d744634 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -42,7 +42,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IViewsService, IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { EnvironmentVariableInfoWidget } from 'vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; -import { TerminalLaunchTroubleshootAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; +import { TerminalLaunchHelpAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer @@ -997,21 +997,21 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (this._processManager.processState === ProcessState.KILLED_DURING_LAUNCH) { if (commandLine) { - exitCodeMessage = nls.localize('launchFailed.exitCodeAndCommandLine', "The terminal process \"{0}\" failed to launch (exit code: {1})", commandLine, this._exitCode); + exitCodeMessage = nls.localize('launchFailed.exitCodeAndCommandLine', "The terminal process \"{0}\" failed to launch (exit code: {1}).", commandLine, this._exitCode); break; } - exitCodeMessage = nls.localize('launchFailed.exitCodeOnly', "The terminal process failed to launch (exit code: {0})", this._exitCode); + exitCodeMessage = nls.localize('launchFailed.exitCodeOnly', "The terminal process failed to launch (exit code: {0}).", this._exitCode); break; } if (commandLine) { - exitCodeMessage = nls.localize('terminated.exitCodeAndCommandLine', "The terminal process \"{0}\" terminated with exit code: {1}", commandLine, this._exitCode); + exitCodeMessage = nls.localize('terminated.exitCodeAndCommandLine', "The terminal process \"{0}\" terminated with exit code: {1}.", commandLine, this._exitCode); break; } - exitCodeMessage = nls.localize('terminated.exitCodeOnly', "The terminal process terminated with exit code: {0}", this._exitCode); + exitCodeMessage = nls.localize('terminated.exitCodeOnly', "The terminal process terminated with exit code: {0}.", this._exitCode); break; case 'object': this._exitCode = exitCodeOrError.code; - exitCodeMessage = nls.localize('launchFailed.errorMessage', "The terminal process failed to launch: {0}", exitCodeOrError.message); + exitCodeMessage = nls.localize('launchFailed.errorMessage', "The terminal process failed to launch: {0}.", exitCodeOrError.message); break; } @@ -1045,7 +1045,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._notificationService.notify({ message: exitCodeMessage, severity: Severity.Error, - actions: { primary: [this._instantiationService.createInstance(TerminalLaunchTroubleshootAction)] } + actions: { primary: [this._instantiationService.createInstance(TerminalLaunchHelpAction)] } }); } else { // Log to help surface the error in case users report issues with showExitAlert diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index d996a36b50..a92b187351 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -490,10 +490,10 @@ export class EditorService extends Disposable implements EditorServiceImpl { return toDisposable(() => remove()); } - getEditorOverrides(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined): [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry][] { + getEditorOverrides(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined, id: string | undefined): [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry][] { const overrides = []; for (const handler of this.openEditorHandlers) { - const handlers = handler.getEditorOverrides ? handler.getEditorOverrides(resource, options, group).map(val => [handler, val] as [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry]) : []; + const handlers = handler.getEditorOverrides ? handler.getEditorOverrides(resource, options, group, id).map(val => [handler, val] as [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry]) : []; overrides.push(...handlers); } @@ -1347,7 +1347,7 @@ export class DelegatingEditorService implements IEditorService { isOpen(editor: IEditorInput | IResourceEditorInput): boolean { return this.editorService.isOpen(editor as IResourceEditorInput /* TS fail */); } overrideOpenEditor(handler: IOpenEditorOverrideHandler): IDisposable { return this.editorService.overrideOpenEditor(handler); } - getEditorOverrides(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined) { return this.editorService.getEditorOverrides(resource, options, group); } + getEditorOverrides(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined, id: string | undefined) { return this.editorService.getEditorOverrides(resource, options, group, id); } invokeWithinEditorContext(fn: (accessor: ServicesAccessor) => T): T { return this.editorService.invokeWithinEditorContext(fn); } diff --git a/src/vs/workbench/services/editor/common/editorOpenWith.ts b/src/vs/workbench/services/editor/common/editorOpenWith.ts index 639ff39f2a..8ef0a255ef 100644 --- a/src/vs/workbench/services/editor/common/editorOpenWith.ts +++ b/src/vs/workbench/services/editor/common/editorOpenWith.ts @@ -42,7 +42,7 @@ export async function openEditorWith( return undefined; // {{SQL CARBON EDIT}} strict-null-checks } - const allEditorOverrides = getAllAvailableEditors(resource, options, group, editorService); + const allEditorOverrides = getAllAvailableEditors(resource, id, options, group, editorService); if (!allEditorOverrides.length) { return undefined; // {{SQL CARBON EDIT}} strict-null-checks } @@ -128,12 +128,13 @@ export const defaultEditorOverrideEntry = Object.freeze({ */ export function getAllAvailableEditors( resource: URI, + id: string | undefined, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup, editorService: IEditorService ): Array<[IOpenEditorOverrideHandler, IOpenEditorOverrideEntry]> { const fileEditorInputFactory = Registry.as(EditorExtensions.EditorInputFactories).getFileEditorInputFactory(); - const overrides = editorService.getEditorOverrides(resource, options, group); + const overrides = editorService.getEditorOverrides(resource, options, group, id); if (!overrides.some(([_, entry]) => entry.id === DEFAULT_EDITOR_ID)) { overrides.unshift([ { diff --git a/src/vs/workbench/services/editor/common/editorService.ts b/src/vs/workbench/services/editor/common/editorService.ts index 10f2716a8f..93ce4d0ca9 100644 --- a/src/vs/workbench/services/editor/common/editorService.ts +++ b/src/vs/workbench/services/editor/common/editorService.ts @@ -36,7 +36,7 @@ export interface IOpenEditorOverrideEntry { export interface IOpenEditorOverrideHandler { open(editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup, context: OpenEditorContext): IOpenEditorOverride | undefined; - getEditorOverrides?(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined): IOpenEditorOverrideEntry[]; + getEditorOverrides?(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined, id: string | undefined): IOpenEditorOverrideEntry[]; } export interface IOpenEditorOverride { @@ -236,7 +236,7 @@ export interface IEditorService { /** * Get all available editor overrides for the editor input. */ - getEditorOverrides(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined): [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry][]; + getEditorOverrides(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined, id: string | undefined): [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry][]; /** * Allows to override the opening of editors by installing a handler that will diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 5de6296cb3..983c227c07 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -63,7 +63,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE return { onSaveError(error: Error, model: ITextFileEditorModel): void { - notificationService.error(localize('genericSaveError', "Failed to save '{0}': {1}", model.name, toErrorMessage(error, false))); + notificationService.error(localize({ key: 'genericSaveError', comment: ['{0} is the resource that failed to save and {1} the error message'] }, "Failed to save '{0}': {1}", model.name, toErrorMessage(error, false))); } }; })();