Merge remote-tracking branch 'origin/main' into tyriar/getprofilesexthost

This commit is contained in:
Daniel Imms
2021-05-13 05:16:55 -07:00
146 changed files with 2020 additions and 1150 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
{
"kind": 2,
"language": "github-issues",
"value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub\n\n$MILESTONE=milestone:\"April 2021\""
"value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub\n\n$MILESTONE=milestone:\"May 2021\""
},
{
"kind": 1,
@@ -40,7 +40,7 @@
}
},
"contributes": {
"notebookMarkupRenderers": [
"notebookOutputRenderer": [
{
"id": "markdownItRenderer",
"displayName": "Markdown it renderer",
@@ -21,18 +21,28 @@
}
},
"contributes": {
"notebookMarkupRenderers": [
"notebookOutputRenderer": [
{
"id": "markdownItRenderer-katex",
"displayName": "Markdown it katex renderer",
"entrypoint": "./notebook-out/katex.js",
"dependsOn": "markdownItRenderer"
"mimeTypes": [
"text/markdown"
],
"dependencies": [
"markdownItRenderer"
]
},
{
"id": "markdownItRenderer-emoji",
"displayName": "Markdown it emoji renderer",
"entrypoint": "./notebook-out/emoji.js",
"dependsOn": "markdownItRenderer"
"mimeTypes": [
"text/markdown"
],
"dependencies": [
"markdownItRenderer"
]
}
]
},
+1 -1
View File
@@ -117,7 +117,7 @@
]
}
],
"notebookProvider": [
"notebooks": [
{
"viewType": "notebookCoreTest",
"displayName": "Notebook Core Test",
@@ -40,9 +40,7 @@ suite('Notebook Editor', function () {
});
test.skip('showNotebookDocment', async function () {
const count1 = vscode.notebook.notebookDocuments.length;
test('showNotebookDocment', async function () {
const p = utils.asPromise(vscode.notebook.onDidOpenNotebookDocument);
const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest');
@@ -53,9 +51,8 @@ suite('Notebook Editor', function () {
const event = await p;
assert.strictEqual(event.uri.toString(), uri.toString());
const count2 = vscode.notebook.notebookDocuments.length;
assert.strictEqual(count1 + 1, count2);
const includes = vscode.notebook.notebookDocuments.includes(editor.document);
assert.strictEqual(true, includes);
});
test('notebook editor has viewColumn', async function () {
@@ -121,7 +121,7 @@ suite('Notebook API tests', function () {
kind: vscode.NotebookCellKind.Code,
outputs: [],
metadata: new vscode.NotebookCellMetadata().with({ custom: { testCellMetadata: 123 } }),
latestExecutionSummary: { startTime: 10, endTime: 20 }
executionSummary: { startTime: 10, endTime: 20 }
},
{
value: 'test2',
@@ -133,7 +133,7 @@ suite('Notebook API tests', function () {
],
{ testOutputMetadata: true })
],
latestExecutionSummary: { executionOrder: 5, success: true },
executionSummary: { executionOrder: 5, success: true },
metadata: new vscode.NotebookCellMetadata().with({ custom: { testCellMetadata: 456 } })
}
]
@@ -478,8 +478,8 @@ suite('Notebook API tests', function () {
assert.strictEqual(secondCell!.outputs[0].outputs[0].mime, 'text/plain');
assert.strictEqual(secondCell!.outputs[0].outputs[0].value, 'Hello World');
assert.deepStrictEqual(secondCell!.outputs[0].outputs[0].metadata, { testOutputItemMetadata: true });
assert.strictEqual(secondCell!.latestExecutionSummary?.executionOrder, 5);
assert.strictEqual(secondCell!.latestExecutionSummary?.success, true);
assert.strictEqual(secondCell!.executionSummary?.executionOrder, 5);
assert.strictEqual(secondCell!.executionSummary?.success, true);
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
assert.strictEqual(getFocusedCell(vscode.window.activeNotebookEditor)?.document.getText(), '');
@@ -1205,30 +1205,30 @@ suite('Notebook API tests', function () {
verifyOutputSyncKernel.controller.dispose();
});
test('latestExecutionSummary', async () => {
test('executionSummary', async () => {
const resource = await createRandomNotebookFile();
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const editor = vscode.window.activeNotebookEditor!;
const cell = editor.document.cellAt(0);
assert.strictEqual(cell.latestExecutionSummary?.success, undefined);
assert.strictEqual(cell.latestExecutionSummary?.executionOrder, undefined);
assert.strictEqual(cell.executionSummary?.success, undefined);
assert.strictEqual(cell.executionSummary?.executionOrder, undefined);
await vscode.commands.executeCommand('notebook.cell.execute');
assert.strictEqual(cell.outputs.length, 1, 'should execute');
assert.ok(cell.latestExecutionSummary);
assert.strictEqual(cell.latestExecutionSummary!.success, true);
assert.strictEqual(typeof cell.latestExecutionSummary!.executionOrder, 'number');
assert.ok(cell.executionSummary);
assert.strictEqual(cell.executionSummary!.success, true);
assert.strictEqual(typeof cell.executionSummary!.executionOrder, 'number');
});
test('initialize latestExecutionSummary', async () => {
test('initialize executionSummary', async () => {
const document = await openRandomNotebookDocument();
const cell = document.cellAt(0);
assert.strictEqual(cell.latestExecutionSummary?.success, undefined);
assert.strictEqual(cell.latestExecutionSummary?.startTime, 10);
assert.strictEqual(cell.latestExecutionSummary?.endTime, 20);
assert.strictEqual(cell.executionSummary?.success, undefined);
assert.strictEqual(cell.executionSummary?.startTime, 10);
assert.strictEqual(cell.executionSummary?.endTime, 20);
});
@@ -34,7 +34,7 @@
"icon": "$(debug)"
}
],
"notebookProvider": [
"notebooks": [
{
"viewType": "notebookSmokeTest",
"displayName": "Notebook Smoke Test",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.57.0",
"distro": "5a7e0f9786abefbe5c166898c7cb71537b3d651e",
"distro": "d4088d7fa3ad4446065d4cec45d90d02d04fe745",
"author": {
"name": "Microsoft Corporation"
},
+40 -14
View File
@@ -1179,28 +1179,44 @@ export function computeScreenAwareSize(cssPx: number): number {
}
/**
* Open safely a new window. This is the best way to do so, but you cannot tell
* if the window was opened or if it was blocked by the brower's popup blocker.
* If you want to tell if the browser blocked the new window, use `windowOpenNoOpenerWithSuccess`.
*
* See https://github.com/microsoft/monaco-editor/issues/601
* To protect against malicious code in the linked site, particularly phishing attempts,
* the window.opener should be set to null to prevent the linked site from having access
* to change the location of the current page.
* See https://mathiasbynens.github.io/rel-noopener/
*/
export function windowOpenNoOpener(url: string): boolean {
if (browser.isElectron || browser.isEdgeLegacyWebView) {
// In VSCode, window.open() always returns null...
// The same is true for a WebView (see https://github.com/microsoft/monaco-editor/issues/628)
// Also call directly window.open in sandboxed Electron (see https://github.com/microsoft/monaco-editor/issues/2220)
window.open(url);
export function windowOpenNoOpener(url: string): void {
// By using 'noopener' in the `windowFeatures` argument, the newly created window will
// not be able to use `window.opener` to reach back to the current page.
// See https://stackoverflow.com/a/46958731
// See https://developer.mozilla.org/en-US/docs/Web/API/Window/open#noopener
// However, this also doesn't allow us to realize if the browser blocked
// the creation of the window.
window.open(url, '_blank', 'noopener');
}
/**
* Open safely a new window. This technique is not appropiate in certain contexts,
* like for example when the JS context is executing inside a sandboxed iframe.
* If it is not necessary to know if the browser blocked the new window, use
* `windowOpenNoOpener`.
*
* See https://github.com/microsoft/monaco-editor/issues/601
* See https://github.com/microsoft/monaco-editor/issues/2474
* See https://mathiasbynens.github.io/rel-noopener/
*/
export function windowOpenNoOpenerWithSuccess(url: string): boolean {
const newTab = window.open();
if (newTab) {
(newTab as any).opener = null;
newTab.location.href = url;
return true;
} else {
let newTab = window.open();
if (newTab) {
(newTab as any).opener = null;
newTab.location.href = url;
return true;
}
return false;
}
return false;
}
export function animate(fn: () => void): IDisposable {
@@ -1596,3 +1612,13 @@ export function getCookieValue(name: string): string | undefined {
return match ? match.pop() : undefined;
}
export function addMatchMediaChangeListener(query: string, callback: () => void): void {
const mediaQueryList = window.matchMedia(query);
if (typeof mediaQueryList.addEventListener === 'function') {
mediaQueryList.addEventListener('change', callback);
} else {
// Safari 13.x
mediaQueryList.addListener(callback);
}
}
+1 -1
View File
@@ -53,7 +53,7 @@ declare const self: unknown;
export const globals: any = (typeof self === 'object' ? self : typeof global === 'object' ? global : {});
let nodeProcess: INodeProcess | undefined = undefined;
if (typeof globals.vscode !== 'undefined') {
if (typeof globals.vscode !== 'undefined' && typeof globals.vscode.process !== 'undefined') {
// Native environment (sandboxed)
nodeProcess = globals.vscode.process;
} else if (typeof process !== 'undefined') {
+1 -1
View File
@@ -9,7 +9,7 @@ let safeProcess: INodeProcess & { nextTick: (callback: (...args: any[]) => void)
declare const process: INodeProcess;
// Native sandbox environment
if (typeof globals.vscode !== 'undefined') {
if (typeof globals.vscode !== 'undefined' && typeof globals.vscode.process !== 'undefined') {
const sandboxProcess: INodeProcess = globals.vscode.process;
safeProcess = {
get platform() { return sandboxProcess.platform; },
+5 -3
View File
@@ -349,14 +349,16 @@ export abstract class EditorAction extends EditorCommand {
public abstract run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | Promise<void>;
}
export type EditorActionImplementation = (accessor: ServicesAccessor, editor: ICodeEditor, args: any) => boolean | Promise<void>;
export class MultiEditorAction extends EditorAction {
private readonly _implementations: [number, CommandImplementation][] = [];
private readonly _implementations: [number, EditorActionImplementation][] = [];
/**
* A higher priority gets to be looked at first
*/
public addImplementation(priority: number, implementation: CommandImplementation): IDisposable {
public addImplementation(priority: number, implementation: EditorActionImplementation): IDisposable {
this._implementations.push([priority, implementation]);
this._implementations.sort((a, b) => b[0] - a[0]);
return {
@@ -373,7 +375,7 @@ export class MultiEditorAction extends EditorAction {
public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | Promise<void> {
for (const impl of this._implementations) {
const result = impl[1](accessor, args);
const result = impl[1](accessor, editor, args);
if (result) {
if (typeof result === 'boolean') {
return;
@@ -226,7 +226,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
protected readonly _instantiationService: IInstantiationService;
protected readonly _contextKeyService: IContextKeyService;
private readonly _notificationService: INotificationService;
private readonly _codeEditorService: ICodeEditorService;
protected readonly _codeEditorService: ICodeEditorService;
private readonly _commandService: ICommandService;
private readonly _themeService: IThemeService;
@@ -1048,6 +1048,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
return;
}
this._triggerCommand(handlerId, payload);
}
protected _triggerCommand(handlerId: string, payload: any): void {
this._commandService.executeCommand(handlerId, payload);
}
+9 -1
View File
@@ -383,7 +383,7 @@ export interface IEditorOptions {
*/
suggest?: ISuggestOptions;
/**
* Smart select opptions;
* Smart select options.
*/
smartSelect?: ISmartSelectOptions;
/**
@@ -638,6 +638,10 @@ export interface IEditorOptions {
* Control the behavior and rendering of the inline hints.
*/
inlayHints?: IEditorInlayHintsOptions;
/**
* Control if the editor should use shadow DOM.
*/
useShadowDOM?: boolean;
}
/**
@@ -3845,6 +3849,7 @@ export const enum EditorOption {
tabCompletion,
tabIndex,
unusualLineTerminators,
useShadowDOM,
useTabStops,
wordSeparators,
wordWrap,
@@ -4460,6 +4465,9 @@ export const EditorOptions = {
description: nls.localize('unusualLineTerminators', "Remove unusual line terminators that might cause problems.")
}
)),
useShadowDOM: register(new EditorBooleanOption(
EditorOption.useShadowDOM, 'useShadowDOM', true
)),
useTabStops: register(new EditorBooleanOption(
EditorOption.useTabStops, 'useTabStops', true,
{ description: nls.localize('useTabStops', "Inserting and deleting whitespace follows tab stops.") }
-2
View File
@@ -1220,7 +1220,6 @@ export interface ITextModel {
/**
* An event emitted when the model has been attached to the first editor or detached from the last editor.
* @event
* @internal
*/
onDidChangeAttached(listener: () => void): IDisposable;
/**
@@ -1247,7 +1246,6 @@ export interface ITextModel {
/**
* Returns if this model is attached to an editor or not.
* @internal
*/
isAttachedToEditor(): boolean;
@@ -278,23 +278,24 @@ export enum EditorOption {
tabCompletion = 108,
tabIndex = 109,
unusualLineTerminators = 110,
useTabStops = 111,
wordSeparators = 112,
wordWrap = 113,
wordWrapBreakAfterCharacters = 114,
wordWrapBreakBeforeCharacters = 115,
wordWrapColumn = 116,
wordWrapOverride1 = 117,
wordWrapOverride2 = 118,
wrappingIndent = 119,
wrappingStrategy = 120,
showDeprecated = 121,
inlayHints = 122,
editorClassName = 123,
pixelRatio = 124,
tabFocusMode = 125,
layoutInfo = 126,
wrappingInfo = 127
useShadowDOM = 111,
useTabStops = 112,
wordSeparators = 113,
wordWrap = 114,
wordWrapBreakAfterCharacters = 115,
wordWrapBreakBeforeCharacters = 116,
wordWrapColumn = 117,
wordWrapOverride1 = 118,
wordWrapOverride2 = 119,
wrappingIndent = 120,
wrappingStrategy = 121,
showDeprecated = 122,
inlayHints = 123,
editorClassName = 124,
pixelRatio = 125,
tabFocusMode = 126,
layoutInfo = 127,
wrappingInfo = 128
}
/**
@@ -11,6 +11,7 @@ import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { Lazy } from 'vs/base/common/lazy';
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { CodeAction, CodeActionProviderRegistry, Command } from 'vs/editor/common/modes';
@@ -92,8 +93,10 @@ export class CodeActionMenu extends Disposable {
const anchor = Position.isIPosition(at) ? this._toCoords(at) : at || { x: 0, y: 0 };
const resolver = this._keybindingResolver.getResolver();
const useShadowDOM = this._editor.getOption(EditorOption.useShadowDOM);
this._contextMenuService.showContextMenu({
domForShadowRoot: this._editor.getDomNode()!,
domForShadowRoot: useShadowDOM ? this._editor.getDomNode()! : undefined,
getAnchor: () => anchor,
getActions: () => menuActions,
onHide: () => {
@@ -209,10 +209,12 @@ export class ContextMenuController implements IEditorContribution {
anchor = { x: posx, y: posy };
}
const useShadowDOM = this._editor.getOption(EditorOption.useShadowDOM);
// Show menu
this._contextMenuIsBeingShownCount++;
this._contextMenuService.showContextMenu({
domForShadowRoot: this._editor.getDomNode(),
domForShadowRoot: useShadowDOM ? this._editor.getDomNode() : undefined,
getAnchor: () => anchor!,
+3 -11
View File
@@ -26,7 +26,6 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
const SEARCH_STRING_MAX_LENGTH = 524288;
@@ -502,12 +501,7 @@ export const StartFindAction = registerMultiEditorAction(new MultiEditorAction({
}
}));
StartFindAction.addImplementation(0, (accessor: ServicesAccessor, args: any): boolean | Promise<void> => {
const codeEditorService = accessor.get(ICodeEditorService);
const editor = codeEditorService.getFocusedCodeEditor() || codeEditorService.getActiveCodeEditor();
if (!editor) {
return false;
}
StartFindAction.addImplementation(0, (accessor: ServicesAccessor, editor: ICodeEditor, args: any): boolean | Promise<void> => {
const controller = CommonFindController.get(editor);
if (!controller) {
return false;
@@ -726,10 +720,8 @@ export const StartFindReplaceAction = registerMultiEditorAction(new MultiEditorA
}
}));
StartFindReplaceAction.addImplementation(0, (accessor: ServicesAccessor, args: any): boolean | Promise<void> => {
const codeEditorService = accessor.get(ICodeEditorService);
const editor = codeEditorService.getFocusedCodeEditor() || codeEditorService.getActiveCodeEditor();
if (!editor || !editor.hasModel() || editor.getOption(EditorOption.readOnly)) {
StartFindReplaceAction.addImplementation(0, (accessor: ServicesAccessor, editor: ICodeEditor, args: any): boolean | Promise<void> => {
if (!editor.hasModel() || editor.getOption(EditorOption.readOnly)) {
return false;
}
const controller = CommonFindController.get(editor);
@@ -1045,7 +1045,41 @@ export class TitleCaseAction extends AbstractCaseAction {
}
}
class BackwardsCompatibleRegExp {
private _actual: RegExp | null;
private _evaluated: boolean;
constructor(
private readonly _pattern: string,
private readonly _flags: string
) {
this._actual = null;
this._evaluated = false;
}
public get(): RegExp | null {
if (!this._evaluated) {
this._evaluated = true;
try {
this._actual = new RegExp(this._pattern, this._flags);
} catch (err) {
// this browser does not support this regular expression
}
}
return this._actual;
}
public isSupported(): boolean {
return (this.get() !== null);
}
}
export class SnakeCaseAction extends AbstractCaseAction {
public static regExp1 = new BackwardsCompatibleRegExp('(\\p{Ll})(\\p{Lu})', 'gmu');
public static regExp2 = new BackwardsCompatibleRegExp('(\\p{Lu}|\\p{N})(\\p{Lu})(\\p{Ll})', 'gmu');
constructor() {
super({
id: 'editor.action.transformToSnakecase',
@@ -1056,9 +1090,15 @@ export class SnakeCaseAction extends AbstractCaseAction {
}
protected _modifyText(text: string, wordSeparators: string): string {
const regExp1 = SnakeCaseAction.regExp1.get();
const regExp2 = SnakeCaseAction.regExp2.get();
if (!regExp1 || !regExp2) {
// cannot support this
return text;
}
return (text
.replace(/(\p{Ll})(\p{Lu})/gmu, '$1_$2')
.replace(/(\p{Lu}|\p{N})(\p{Lu})(\p{Ll})/gmu, '$1_$2$3')
.replace(regExp1, '$1_$2')
.replace(regExp2, '$1_$2$3')
.toLocaleLowerCase()
);
}
@@ -1084,4 +1124,7 @@ registerEditorAction(TransposeAction);
registerEditorAction(UpperCaseAction);
registerEditorAction(LowerCaseAction);
registerEditorAction(TitleCaseAction);
registerEditorAction(SnakeCaseAction);
if (SnakeCaseAction.regExp1.isSupported() && SnakeCaseAction.regExp2.isSupported()) {
registerEditorAction(SnakeCaseAction);
}
@@ -34,6 +34,7 @@ import { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standal
import { IModelService } from 'vs/editor/common/services/modelService';
import { ILanguageSelection, IModeService } from 'vs/editor/common/services/modeService';
import { URI } from 'vs/base/common/uri';
import { StandaloneCodeEditorServiceImpl } from 'vs/editor/standalone/browser/standaloneCodeServiceImpl';
/**
* Description of an action contribution
@@ -363,6 +364,20 @@ export class StandaloneCodeEditor extends CodeEditorWidget implements IStandalon
return toDispose;
}
protected override _triggerCommand(handlerId: string, payload: any): void {
if (this._codeEditorService instanceof StandaloneCodeEditorServiceImpl) {
// Help commands find this editor as the active editor
try {
this._codeEditorService.setActiveCodeEditor(this);
super._triggerCommand(handlerId, payload);
} finally {
this._codeEditorService.setActiveCodeEditor(null);
}
} else {
super._triggerCommand(handlerId, payload);
}
}
}
export class StandaloneEditor extends StandaloneCodeEditor implements IStandaloneCodeEditor {
@@ -18,6 +18,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
export class StandaloneCodeEditorServiceImpl extends CodeEditorServiceImpl {
private readonly _editorIsOpen: IContextKey<boolean>;
private _activeCodeEditor: ICodeEditor | null;
constructor(
styleSheet: GlobalStyleSheet | null,
@@ -28,6 +29,7 @@ export class StandaloneCodeEditorServiceImpl extends CodeEditorServiceImpl {
this.onCodeEditorAdd(() => this._checkContextKey());
this.onCodeEditorRemove(() => this._checkContextKey());
this._editorIsOpen = contextKeyService.createKey('editorIsOpen', false);
this._activeCodeEditor = null;
}
private _checkContextKey(): void {
@@ -41,8 +43,12 @@ export class StandaloneCodeEditorServiceImpl extends CodeEditorServiceImpl {
this._editorIsOpen.set(hasCodeEditor);
}
public setActiveCodeEditor(activeCodeEditor: ICodeEditor | null): void {
this._activeCodeEditor = activeCodeEditor;
}
public getActiveCodeEditor(): ICodeEditor | null {
return null; // not supported in the standalone case
return this._activeCodeEditor;
}
public openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
@@ -233,7 +233,7 @@ export class StandaloneThemeServiceImpl extends Disposable implements IStandalon
this._updateCSS();
});
window.matchMedia('(forced-colors: active)').addEventListener('change', () => {
dom.addMatchMediaChangeListener('(forced-colors: active)', () => {
this._updateActualTheme();
});
}
@@ -86,15 +86,14 @@ function createKeywordMatcher(arr: string[], caseInsensitive: boolean = false):
* @example /@@text/ will not be replaced and will become /@text/.
*/
function compileRegExp(lexer: monarchCommon.ILexerMin, str: string): RegExp {
// @@ must be interpreted as a literal @, so we replace all occurences of @@ with a placeholder character
str = str.replace(/@@/g, `\x01`);
let n = 0;
let hadExpansion: boolean;
do {
hadExpansion = false;
str = str.replace(/(.|^)@(\w+)/g, function (s, charBeforeAtSign, attr?) {
if (charBeforeAtSign === '@') {
// do not expand @@
return s;
}
str = str.replace(/@(\w+)/g, function (s, attr?) {
hadExpansion = true;
let sub = '';
if (typeof (lexer[attr]) === 'string') {
@@ -108,13 +107,13 @@ function compileRegExp(lexer: monarchCommon.ILexerMin, str: string): RegExp {
throw monarchCommon.createError(lexer, 'attribute reference \'' + attr + '\' must be a string, used at: ' + str);
}
}
return charBeforeAtSign + (monarchCommon.empty(sub) ? '' : '(?:' + sub + ')');
return (monarchCommon.empty(sub) ? '' : '(?:' + sub + ')');
});
n++;
} while (hadExpansion && n < 5);
// handle escaped @@
str = str.replace(/@@/g, '@');
str = str.replace(/\x01/g, '@');
let flags = (lexer.ignoreCase ? 'i' : '') + (lexer.unicode ? 'u' : '');
return new RegExp(str, flags);
@@ -264,4 +264,31 @@ suite('Monarch', () => {
]);
});
test('microsoft/monaco-editor#2424: Allow to target @@', () => {
const modeService = new ModeServiceImpl();
const tokenizer = createMonarchTokenizer(modeService, 'test', {
ignoreCase: false,
tokenizer: {
root: [
{
regex: /@@@@/,
action: { token: 'ham' }
},
],
},
});
const lines = [
`@@`
];
const actualTokens = getTokens(tokenizer, lines);
assert.deepStrictEqual(actualTokens, [
[
new Token(0, 'ham.test', 'test'),
]
]);
});
});
+33 -18
View File
@@ -1983,6 +1983,11 @@ declare namespace monaco.editor {
* @event
*/
onDidChangeLanguageConfiguration(listener: (e: IModelLanguageConfigurationChangedEvent) => void): IDisposable;
/**
* An event emitted when the model has been attached to the first editor or detached from the last editor.
* @event
*/
onDidChangeAttached(listener: () => void): IDisposable;
/**
* An event emitted right before disposing the model.
* @event
@@ -1993,6 +1998,10 @@ declare namespace monaco.editor {
* and make all necessary clean-up to release this object to the GC.
*/
dispose(): void;
/**
* Returns if this model is attached to an editor or not.
*/
isAttachedToEditor(): boolean;
}
/**
@@ -2965,7 +2974,7 @@ declare namespace monaco.editor {
*/
suggest?: ISuggestOptions;
/**
* Smart select opptions;
* Smart select options.
*/
smartSelect?: ISmartSelectOptions;
/**
@@ -3220,6 +3229,10 @@ declare namespace monaco.editor {
* Control the behavior and rendering of the inline hints.
*/
inlayHints?: IEditorInlayHintsOptions;
/**
* Control if the editor should use shadow DOM.
*/
useShadowDOM?: boolean;
}
/**
@@ -4094,23 +4107,24 @@ declare namespace monaco.editor {
tabCompletion = 108,
tabIndex = 109,
unusualLineTerminators = 110,
useTabStops = 111,
wordSeparators = 112,
wordWrap = 113,
wordWrapBreakAfterCharacters = 114,
wordWrapBreakBeforeCharacters = 115,
wordWrapColumn = 116,
wordWrapOverride1 = 117,
wordWrapOverride2 = 118,
wrappingIndent = 119,
wrappingStrategy = 120,
showDeprecated = 121,
inlayHints = 122,
editorClassName = 123,
pixelRatio = 124,
tabFocusMode = 125,
layoutInfo = 126,
wrappingInfo = 127
useShadowDOM = 111,
useTabStops = 112,
wordSeparators = 113,
wordWrap = 114,
wordWrapBreakAfterCharacters = 115,
wordWrapBreakBeforeCharacters = 116,
wordWrapColumn = 117,
wordWrapOverride1 = 118,
wordWrapOverride2 = 119,
wrappingIndent = 120,
wrappingStrategy = 121,
showDeprecated = 122,
inlayHints = 123,
editorClassName = 124,
pixelRatio = 125,
tabFocusMode = 126,
layoutInfo = 127,
wrappingInfo = 128
}
export const EditorOptions: {
acceptSuggestionOnCommitCharacter: IEditorOption<EditorOption.acceptSuggestionOnCommitCharacter, boolean>;
@@ -4226,6 +4240,7 @@ declare namespace monaco.editor {
tabCompletion: IEditorOption<EditorOption.tabCompletion, 'on' | 'off' | 'onlySnippets'>;
tabIndex: IEditorOption<EditorOption.tabIndex, number>;
unusualLineTerminators: IEditorOption<EditorOption.unusualLineTerminators, 'auto' | 'off' | 'prompt'>;
useShadowDOM: IEditorOption<EditorOption.useShadowDOM, boolean>;
useTabStops: IEditorOption<EditorOption.useTabStops, boolean>;
wordSeparators: IEditorOption<EditorOption.wordSeparators, string>;
wordWrap: IEditorOption<EditorOption.wordWrap, 'on' | 'off' | 'wordWrapColumn' | 'bounded'>;
@@ -117,9 +117,9 @@ export interface IWalkthroughStep {
readonly id: string;
readonly title: string;
readonly description: string | undefined;
readonly media:
| { path: string | { dark: string, light: string, hc: string }, altText: string }
| { path: string, },
readonly media: { path: string | { dark: string, light: string, hc: string }, altText?: string }
readonly completionEvents?: string[];
/** @deprecated use `completionEvents: 'onCommand:...'` */
readonly doneOn?: { command: string };
readonly when?: string;
}
+1 -1
View File
@@ -234,7 +234,7 @@ export class FileService extends Disposable implements IFileService {
mtime: stat.mtime,
ctime: stat.ctime,
size: stat.size,
readonly: Boolean(stat.permissions ?? 0 & FilePermission.Readonly) ?? Boolean(provider.capabilities & FileSystemProviderCapabilities.Readonly),
readonly: Boolean(stat.permissions ?? 0 & FilePermission.Readonly) || Boolean(provider.capabilities & FileSystemProviderCapabilities.Readonly),
etag: etag({ mtime: stat.mtime, size: stat.size })
};
+10 -1
View File
@@ -18,6 +18,10 @@ export interface ILinkDescriptor {
readonly title?: string;
}
export interface ILinkOptions {
readonly opener?: (href: string) => void;
}
export interface ILinkStyles {
readonly textLinkForeground?: Color;
readonly disabled?: boolean;
@@ -33,6 +37,7 @@ export class Link extends Disposable {
constructor(
link: ILinkDescriptor,
options: ILinkOptions | undefined = undefined,
@IOpenerService openerService: IOpenerService
) {
super();
@@ -53,7 +58,11 @@ export class Link extends Disposable {
this._register(onOpen(e => {
EventHelper.stop(e, true);
if (!this.disabled) {
openerService.open(link.href, { allowCommands: true });
if (options?.opener) {
options.opener(link.href);
} else {
openerService.open(link.href, { allowCommands: true });
}
}
}));
+1 -1
View File
@@ -13,7 +13,7 @@ import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes
let product: IProductConfiguration;
// Native sandbox environment
if (typeof globals.vscode !== 'undefined') {
if (typeof globals.vscode !== 'undefined' && typeof globals.vscode.context !== 'undefined') {
const configuration: ISandboxConfiguration | undefined = globals.vscode.context.configuration();
if (configuration) {
product = configuration.product;
@@ -101,7 +101,7 @@ export async function resolveCommonProperties(
return result;
}
function verifyMicrosoftInternalDomain(domainList: readonly string[]): boolean {
export function verifyMicrosoftInternalDomain(domainList: readonly string[]): boolean {
const userDnsDomain = env['USERDNSDOMAIN'];
if (!userDnsDomain) {
return false;
+9 -5
View File
@@ -174,7 +174,7 @@ export interface IOffProcessTerminalService {
getShellEnvironment(): Promise<IProcessEnvironment | undefined>;
setTerminalLayoutInfo(layoutInfo?: ITerminalsLayoutInfoById): Promise<void>;
updateTitle(id: number, title: string): Promise<void>;
updateIcon(id: number, icon: string): Promise<void>;
updateIcon(id: number, icon: string, color?: string): Promise<void>;
getTerminalLayoutInfo(): Promise<ITerminalsLayoutInfo | undefined>;
reduceConnectionGraceTime(): Promise<void>;
}
@@ -239,9 +239,8 @@ export interface IPtyService {
processBinary(id: number, data: string): Promise<void>;
/** Confirm the process is _not_ an orphan. */
orphanQuestionReply(id: number): Promise<void>;
updateTitle(id: number, title: string): Promise<void>
updateIcon(id: number, icon: string): Promise<void>
updateTitle(id: number, title: string): Promise<void>;
updateIcon(id: number, icon: string, color?: string): Promise<void>;
getDefaultSystemShell(osOverride?: OperatingSystem): Promise<string>;
getProfiles?(includeDetectedProfiles?: boolean): Promise<ITerminalProfile[]>;
getEnvironment(): Promise<IProcessEnvironment>;
@@ -349,7 +348,7 @@ export interface IShellLaunchConfig {
/**
* This is a terminal that attaches to an already running terminal.
*/
attachPersistentProcess?: { id: number; pid: number; title: string; cwd: string; icon?: string; };
attachPersistentProcess?: { id: number; pid: number; title: string; cwd: string; icon?: string; color?: string };
/**
* Whether the terminal process environment should be exactly as provided in
@@ -393,6 +392,11 @@ export interface IShellLaunchConfig {
* icon.
*/
icon?: string;
/**
* The color ID to use for this terminal. If not specified it will use the default fallback
*/
color?: string;
}
export interface IShellLaunchConfigDto {
@@ -56,6 +56,7 @@ export interface IProcessDetails {
workspaceName: string;
isOrphan: boolean;
icon: string | undefined;
color: string | undefined;
}
export type ITerminalTabLayoutInfoDto = IRawTerminalTabLayoutInfo<IProcessDetails>;
@@ -170,8 +170,8 @@ export class PtyHostService extends Disposable implements IPtyService {
updateTitle(id: number, title: string): Promise<void> {
return this._proxy.updateTitle(id, title);
}
updateIcon(id: number, icon: string): Promise<void> {
return this._proxy.updateIcon(id, icon);
updateIcon(id: number, icon: string, color?: string): Promise<void> {
return this._proxy.updateIcon(id, icon, color);
}
attachToProcess(id: number): Promise<void> {
return this._proxy.attachToProcess(id);
+9 -5
View File
@@ -118,8 +118,8 @@ export class PtyService extends Disposable implements IPtyService {
this._throwIfNoPty(id).setTitle(title);
}
async updateIcon(id: number, icon: string): Promise<void> {
this._throwIfNoPty(id).setIcon(icon);
async updateIcon(id: number, icon: string, color?: string): Promise<void> {
this._throwIfNoPty(id).setIcon(icon, color);
}
async detachFromProcess(id: number): Promise<void> {
@@ -256,7 +256,8 @@ export class PtyService extends Disposable implements IPtyService {
workspaceName: persistentProcess.workspaceName,
cwd,
isOrphan,
icon: persistentProcess.icon
icon: persistentProcess.icon,
color: persistentProcess.color
};
}
@@ -309,13 +310,15 @@ export class PersistentTerminalProcess extends Disposable {
get pid(): number { return this._pid; }
get title(): string { return this._title || this._terminalProcess.currentTitle; }
get icon(): string | undefined { return this._icon; }
get color(): string | undefined { return this._color; }
setTitle(title: string): void {
this._title = title;
}
setIcon(icon: string): void {
setIcon(icon: string, color?: string): void {
this._icon = icon;
this._color = color;
}
constructor(
@@ -326,7 +329,8 @@ export class PersistentTerminalProcess extends Disposable {
readonly shouldPersistTerminal: boolean,
cols: number, rows: number,
private readonly _logService: ILogService,
private _icon?: string
private _icon?: string,
private _color?: string
) {
super();
this._recorder = new TerminalRecorder(cols, rows);
+40 -7
View File
@@ -392,6 +392,25 @@ declare module 'vscode' {
Warning = 2,
}
/**
* A message regarding a completed search.
*/
export interface TextSearchCompleteMessage {
/**
* Markdown text of the message.
*/
text: string,
/**
* Whether the source of the message is trusted, command links are disabled for untrusted message sources.
* Messaged are untrusted by default.
*/
trusted?: boolean,
/**
* The message type, this affects how the message will be rendered.
*/
type: TextSearchCompleteMessageType,
}
/**
* Information collected when text search is complete.
*/
@@ -411,8 +430,10 @@ declare module 'vscode' {
* Messages with "Information" tyle support links in markdown syntax:
* - Click to [run a command](command:workbench.action.OpenQuickPick)
* - Click to [open a website](https://aka.ms)
*
* Commands may optionally return { triggerSearch: true } to signal to VS Code that the original search should run be agian.
*/
message?: { text: string, type: TextSearchCompleteMessageType } | { text: string, type: TextSearchCompleteMessageType }[];
message?: TextSearchCompleteMessage | TextSearchCompleteMessage[];
}
/**
@@ -1071,8 +1092,10 @@ declare module 'vscode' {
*/
readonly outputs: ReadonlyArray<NotebookCellOutput>;
// todo@API maybe just executionSummary or lastExecutionSummary?
readonly latestExecutionSummary: NotebookCellExecutionSummary | undefined;
/**
* The most recent {@link NotebookCellExecutionSummary excution summary} for this cell.
*/
readonly executionSummary?: NotebookCellExecutionSummary;
}
/**
@@ -1269,9 +1292,17 @@ declare module 'vscode' {
// static textplain(value:string): NotebookCellOutputItem;
// static errortrace(value:any): NotebookCellOutputItem;
/**
* Creates `application/x.notebook.error`
*
* @param err An error for which an output item is wanted
*/
static error(err: Error): NotebookCellOutputItem;
mime: string;
//todo@API string or Unit8Array?
// value: string | Uint8Array | unknown;
value: unknown;
metadata?: { [key: string]: any };
@@ -1320,8 +1351,10 @@ declare module 'vscode' {
*/
metadata?: NotebookCellMetadata;
// todo@API just executionSummary or lastExecutionSummary
latestExecutionSummary?: NotebookCellExecutionSummary;
/**
* The execution summary of this cell data.
*/
executionSummary?: NotebookCellExecutionSummary;
/**
* Create new cell data. Minimal cell data specifies its kind, its source value, and the
@@ -1332,9 +1365,9 @@ declare module 'vscode' {
* @param languageId The language identifier of the source value.
* @param outputs //TODO@API remove ctor?
* @param metadata //TODO@API remove ctor?
* @param latestExecutionSummary //TODO@API remove ctor?
* @param executionSummary //TODO@API remove ctor?
*/
constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, latestExecutionSummary?: NotebookCellExecutionSummary);
constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, executionSummary?: NotebookCellExecutionSummary);
}
/**
@@ -145,7 +145,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService));
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadBulkEdits)));
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, extHostDocuments, extHostLogService, extensionStoragePaths));
const extHostNotebookKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookKernels, new ExtHostNotebookKernels(rpcProtocol, initData, extHostNotebook));
const extHostNotebookKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookKernels, new ExtHostNotebookKernels(rpcProtocol, initData, extHostNotebook, extHostLogService));
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, { ...initData.environment, remote: initData.remote }));
@@ -87,7 +87,7 @@ export class ExtHostCell {
document: data.document,
get outputs() { return that._outputs.slice(0); },
get metadata() { return that._metadata; },
get latestExecutionSummary() { return that._previousResult; }
get executionSummary() { return that._previousResult; }
});
}
return this._apiCell;
@@ -20,6 +20,7 @@ import { CellEditType, IImmediateCellEditOperation, NullablePartialNotebookCellM
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { NotebookCellExecutionState } from 'vs/workbench/api/common/extHostTypes';
import { asArray } from 'vs/base/common/arrays';
import { ILogService } from 'vs/platform/log/common/log';
interface IKernelData {
extensionId: ExtensionIdentifier,
@@ -40,7 +41,8 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
constructor(
private readonly _mainContext: IMainContext,
private readonly _initData: IExtHostInitDataService,
private readonly _extHostNotebook: ExtHostNotebookController
private readonly _extHostNotebook: ExtHostNotebookController,
@ILogService private readonly _logService: ILogService,
) {
this._proxy = _mainContext.getProxy(MainContext.MainThreadNotebookKernels);
}
@@ -53,9 +55,12 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
}
}
const handle = this._handlePool++;
const that = this;
this._logService.trace(`NotebookController[${handle}], CREATED by ${extension.identifier.value}, ${id}`);
const _defaultExecutHandler = () => console.warn(`NO execute handler from notebook controller '${data.id}' of extension: '${extension.identifier}'`);
let isDisposed = false;
@@ -164,12 +169,14 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
throw new Error('notebook controller is DISPOSED');
}
if (!associatedNotebooks.has(cell.notebook.uri)) {
that._logService.trace(`NotebookController[${handle}] NOT associated to notebook, associated to THESE notebooks:`, Array.from(associatedNotebooks.keys()).map(u => u.toString()));
throw new Error(`notebook controller is NOT associated to notebook: ${cell.notebook.uri.toString()}`);
}
return that._createNotebookCellExecution(cell);
},
dispose: () => {
if (!isDisposed) {
this._logService.trace(`NotebookController[${handle}], DISPOSED`);
isDisposed = true;
this._kernelData.delete(handle);
commandDisposables.dispose();
@@ -212,6 +219,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
} else {
obj.associatedNotebooks.delete(notebook.uri);
}
this._logService.trace(`NotebookController[${handle}] ASSOCIATE notebook`, notebook.uri.toString(), value);
// send event
obj.onDidChangeSelection.fire({
selected: value,
@@ -236,9 +244,11 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
}
try {
this._logService.trace(`NotebookController[${handle}] EXECUTE cells`, document.uri.toString(), cells.length);
await obj.controller.executeHandler.call(obj.controller, cells, document.apiNotebook, obj.controller);
} catch (err) {
//
this._logService.error(`NotebookController[${handle}] execute cells FAILED`, err);
console.error(err);
}
}
@@ -1494,7 +1494,7 @@ export namespace NotebookCellData {
source: data.value,
metadata: {
...data.metadata,
...NotebookCellPreviousExecutionResult.from(data.latestExecutionSummary ?? {})
...NotebookCellPreviousExecutionResult.from(data.executionSummary ?? {})
},
outputs: data.outputs ? data.outputs.map(NotebookCellOutput.from) : []
};
+10 -3
View File
@@ -3049,15 +3049,15 @@ export class NotebookCellData {
languageId: string;
outputs?: NotebookCellOutput[];
metadata?: NotebookCellMetadata;
latestExecutionSummary?: vscode.NotebookCellExecutionSummary;
executionSummary?: vscode.NotebookCellExecutionSummary;
constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, latestExecutionSummary?: vscode.NotebookCellExecutionSummary) {
constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, executionSummary?: vscode.NotebookCellExecutionSummary) {
this.kind = kind;
this.value = value;
this.languageId = languageId;
this.outputs = outputs ?? [];
this.metadata = metadata;
this.latestExecutionSummary = latestExecutionSummary;
this.executionSummary = executionSummary;
NotebookCellData.validate(this);
}
@@ -3087,6 +3087,13 @@ export class NotebookCellOutputItem {
return typeof (<vscode.NotebookCellOutputItem>obj).mime === 'string';
}
static error(err: Error): NotebookCellOutputItem {
return new NotebookCellOutputItem(
'application/x.notebook.error',
JSON.stringify({ name: err.name, message: err.message, stack: err.stack })
);
}
constructor(
public mime: string,
public value: unknown, // JSON'able
@@ -162,7 +162,7 @@ export class BannerPart extends Part implements IBannerService {
const actionContainer = append(this.element, $('div.message-actions-container'));
for (const action of item.actions) {
const actionLink = this._register(this.instantiationService.createInstance(Link, action));
const actionLink = this._register(this.instantiationService.createInstance(Link, action, {}));
this._register(attachLinkStyler(actionLink, this.themeService, { textLinkForeground: BANNER_FOREGROUND }));
actionContainer.appendChild(actionLink.el);
}
@@ -487,7 +487,7 @@ function registerOpenEditorAPICommands(): void {
group = editorGroupsService.getGroup(viewColumnToEditorGroup(editorGroupsService, columnArg)) ?? editorGroupsService.activeGroup;
}
return editorService.openEditor({ resource: URI.revive(resource), options: { ...optionsArg, override: id } }, group);
return editorService.openEditor({ resource: URI.revive(resource), options: { ...optionsArg, pinned: true, override: id } }, group);
});
}
@@ -63,7 +63,7 @@ export class EditorControl extends Disposable {
this._register(this.workspaceTrustService.onDidChangeTrust(() => this.onDidChangeWorkspaceTrust()));
}
private async onDidChangeWorkspaceTrust(): Promise<void> {
private onDidChangeWorkspaceTrust() {
// If the active editor pane requires workspace trust
// we need to re-open it anytime trust changes to
@@ -72,7 +72,7 @@ export class EditorControl extends Disposable {
// to handle errors properly.
const editor = this._activeEditorPane?.input;
const options = this._activeEditorPane?.options;
if (editor && await editor.requiresWorkspaceTrust()) {
if (editor?.requiresWorkspaceTrust()) {
this.groupView.openEditor(editor, options);
}
}
@@ -80,7 +80,7 @@ export class EditorControl extends Disposable {
async openEditor(editor: EditorInput, options: EditorOptions | undefined, context: IEditorOpenContext = Object.create(null)): Promise<IOpenEditorResult> {
// Editor descriptor
const descriptor = await this.resolveEditorDescriptor(editor);
const descriptor = this.getEditorDescriptor(editor);
// Editor pane
const editorPane = this.doShowEditorPane(descriptor);
@@ -90,17 +90,16 @@ export class EditorControl extends Disposable {
return { editorPane, editorChanged };
}
private async resolveEditorDescriptor(editor: EditorInput): Promise<IEditorDescriptor> {
const editorRequiresTrust = await editor.requiresWorkspaceTrust();
const editorBlockedByTrust = editorRequiresTrust && !this.workspaceTrustService.isWorkpaceTrusted();
private getEditorDescriptor(editor: EditorInput): IEditorDescriptor {
if (editor.requiresWorkspaceTrust() && !this.workspaceTrustService.isWorkpaceTrusted()) {
// Workspace trust: if an editor signals it needs workspace trust
// but the current workspace is untrusted, we fallback to a generic
// editor descriptor to indicate this an do NOT load the registered
// editor.
return WorkspaceTrustRequiredEditor.DESCRIPTOR;
}
// Workspace trust: if an editor signals it needs workspace trust
// but the current workspace is untrusted, we fallback to a generic
// editor descriptor to indicate this an do NOT load the registered
// editor.
const descriptor = editorBlockedByTrust ? WorkspaceTrustRequiredEditor.DESCRIPTOR : this.editorsRegistry.getEditor(editor);
return assertIsDefined(descriptor);
return assertIsDefined(this.editorsRegistry.getEditor(editor));
}
private doShowEditorPane(descriptor: IEditorDescriptor): EditorPane {
@@ -579,7 +579,7 @@ export abstract class ViewPane extends Pane implements IView {
if (typeof node === 'string') {
append(p, document.createTextNode(node));
} else {
const link = this.instantiationService.createInstance(Link, node);
const link = this.instantiationService.createInstance(Link, node, {});
append(p, link.el);
disposables.add(link);
disposables.add(attachLinkStyler(link, this.themeService));
+7
View File
@@ -63,6 +63,7 @@ import { ITimerService } from 'vs/workbench/services/timer/browser/timerService'
import { WorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/common/workspaceTrust';
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
import { HTMLFileSystemProvider } from 'vs/platform/files/browser/htmlFileSystemProvider';
import { IOpenerService } from 'vs/platform/opener/common/opener';
class BrowserMain extends Disposable {
@@ -106,16 +107,22 @@ class BrowserMain extends Disposable {
const commandService = accessor.get(ICommandService);
const lifecycleService = accessor.get(ILifecycleService);
const timerService = accessor.get(ITimerService);
const openerService = accessor.get(IOpenerService);
const productService = accessor.get(IProductService);
return {
commands: {
executeCommand: (command, ...args) => commandService.executeCommand(command, ...args)
},
env: {
uriScheme: productService.urlProtocol,
async retrievePerformanceMarks() {
await timerService.whenReady();
return timerService.getPerformanceMarks();
},
async openUri(uri: URI): Promise<boolean> {
return openerService.open(uri, {});
}
},
shutdown: () => lifecycleService.shutdown()
+2 -2
View File
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { setFullscreen } from 'vs/base/browser/browser';
import { addDisposableListener, addDisposableThrottledListener, detectFullscreen, EventHelper, EventType, windowOpenNoOpener } from 'vs/base/browser/dom';
import { addDisposableListener, addDisposableThrottledListener, detectFullscreen, EventHelper, EventType, windowOpenNoOpenerWithSuccess, windowOpenNoOpener } from 'vs/base/browser/dom';
import { domEvent } from 'vs/base/browser/event';
import { timeout } from 'vs/base/common/async';
import { Event } from 'vs/base/common/event';
@@ -144,7 +144,7 @@ export class BrowserWindow extends Disposable {
this.openerService.setDefaultExternalOpener({
openExternal: async (href: string) => {
if (matchesScheme(href, Schemas.http) || matchesScheme(href, Schemas.https)) {
const opened = windowOpenNoOpener(href);
const opened = windowOpenNoOpenerWithSuccess(href);
if (!opened) {
const showResult = await this.dialogService.show(Severity.Warning, localize('unableToOpenExternal', "The browser interrupted the opening of a new tab or window. Press 'Open' to open it anyway."),
[localize('open', "Open"), localize('learnMore', "Learn More"), localize('cancel', "Cancel")], { cancelId: 2, detail: href });
+4 -6
View File
@@ -435,7 +435,7 @@ export interface IEditorInput extends IDisposable {
/**
* Returns if the input requires workspace trust or not.
*/
requiresWorkspaceTrust(): Promise<boolean>;
requiresWorkspaceTrust(): boolean
/**
* Returns if this input is readonly or not.
@@ -577,7 +577,7 @@ export abstract class EditorInput extends Disposable implements IEditorInput {
return { typeId: this.typeId };
}
async requiresWorkspaceTrust(): Promise<boolean> {
requiresWorkspaceTrust(): boolean {
return false;
}
@@ -799,10 +799,8 @@ export class SideBySideEditorInput extends EditorInput {
return this.description;
}
override async requiresWorkspaceTrust(): Promise<boolean> {
const requiresTrust = await Promise.all([this.primary.requiresWorkspaceTrust(), this.secondary.requiresWorkspaceTrust()]);
return requiresTrust.some(value => value === true);
override requiresWorkspaceTrust(): boolean {
return this.primary.requiresWorkspaceTrust() || this.secondary.requiresWorkspaceTrust();
}
override isReadonly(): boolean {
@@ -877,8 +877,8 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi
});
this.registerExtensionAction({
id: 'workbench.extensions.action.listTrustRequiredExtensions',
title: { value: localize('showTrustRequiredExtensions', "Show Extensions Requiring Trust"), original: 'Show Extensions Requiring Trust' },
id: 'workbench.extensions.action.listWorkspaceUnsupportedExtensions',
title: { value: localize('showWorkspaceUnsupportedExtensions', "Show Extensions Unsupported By Workspace"), original: 'Show Extensions Unsupported By Workspace' },
category: ExtensionsLocalizedLabel,
menu: [{
id: MenuId.CommandPalette,
@@ -889,9 +889,9 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi
order: 6,
}],
menuTitles: {
[extensionsFilterSubMenu.id]: localize('trust required filter', "Trust Required")
[extensionsFilterSubMenu.id]: localize('workspace unsupported filter', "Workspace Unsupported")
},
run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@trustRequired'))
run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@workspaceUnsupported'))
});
this.registerExtensionAction({
@@ -22,7 +22,7 @@ import { InstallLocalExtensionsInRemoteAction, InstallRemoteExtensionsInLocalAct
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInFeatureExtensionsView, BuiltInThemesExtensionsView, BuiltInProgrammingLanguageExtensionsView, ServerInstalledExtensionsView, DefaultRecommendedExtensionsView, TrustRequiredOnStartExtensionsView, TrustRequiredOnDemandExtensionsView } from 'vs/workbench/contrib/extensions/browser/extensionsViews';
import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInFeatureExtensionsView, BuiltInThemesExtensionsView, BuiltInProgrammingLanguageExtensionsView, ServerInstalledExtensionsView, DefaultRecommendedExtensionsView, UntrustedWorkspaceUnsupportedExtensionsView, UntrustedWorkspacePartiallySupportedExtensionsView, VirtualWorkspaceUnsupportedExtensionsView } from 'vs/workbench/contrib/extensions/browser/extensionsViews';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import Severity from 'vs/base/common/severity';
@@ -70,8 +70,8 @@ const HasInstalledExtensionsContext = new RawContextKey<boolean>('hasInstalledEx
const HasInstalledWebExtensionsContext = new RawContextKey<boolean>('hasInstalledWebExtensions', false);
const BuiltInExtensionsContext = new RawContextKey<boolean>('builtInExtensions', false);
const SearchBuiltInExtensionsContext = new RawContextKey<boolean>('searchBuiltInExtensions', false);
const TrustRequiredExtensionsContext = new RawContextKey<boolean>('trustRequiredExtensions', false);
const SearchTrustRequiredExtensionsContext = new RawContextKey<boolean>('searchTrustRequiredExtensions', false);
const UnsupportedWorkspaceExtensionsContext = new RawContextKey<boolean>('unsupportedWorkspaceExtensions', false);
const SearchUnsupportedWorkspaceExtensionsContext = new RawContextKey<boolean>('searchUnsupportedWorkspaceExtensions', false);
const RecommendedExtensionsContext = new RawContextKey<boolean>('recommendedExtensions', false);
export class ExtensionsViewletViewsContribution implements IWorkbenchContribution {
@@ -104,7 +104,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
viewDescriptors.push(...this.createBuiltinExtensionsViewDescriptors());
/* Trust Required extensions views */
viewDescriptors.push(...this.createTrustRequiredExtensionsViewDescriptors());
viewDescriptors.push(...this.createUnsupportedWorkspaceExtensionsViewDescriptors());
Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).registerViews(viewDescriptors, this.container);
}
@@ -344,13 +344,13 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
});
/*
* View used for searching trustRequired extensions
* View used for searching workspace unsupported extensions
*/
viewDescriptors.push({
id: 'workbench.views.extensions.searchTrustRequired',
name: localize('trustRequired', "Trust Required"),
id: 'workbench.views.extensions.searchWorkspaceUnsupported',
name: localize('workspaceUnsupported', "Workspace Unsupported"),
ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchTrustRequiredExtensions')),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchWorkspaceUnsupportedExtensions')),
});
return viewDescriptors;
@@ -405,21 +405,28 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
return viewDescriptors;
}
private createTrustRequiredExtensionsViewDescriptors(): IViewDescriptor[] {
private createUnsupportedWorkspaceExtensionsViewDescriptors(): IViewDescriptor[] {
const viewDescriptors: IViewDescriptor[] = [];
viewDescriptors.push({
id: 'workbench.views.extensions.trustRequiredOnStartExtensions',
name: localize('trustRequiredOnStartExtensions', "Trust Required To Enable"),
ctorDescriptor: new SyncDescriptor(TrustRequiredOnStartExtensionsView, [{}]),
when: ContextKeyExpr.has('trustRequiredExtensions'),
id: 'workbench.views.extensions.untrustedUnsupportedExtensions',
name: localize('untrustedUnsupportedExtensions', "Disabled in Restricted Mode"),
ctorDescriptor: new SyncDescriptor(UntrustedWorkspaceUnsupportedExtensionsView, [{}]),
when: ContextKeyExpr.has('unsupportedWorkspaceExtensions'),
});
viewDescriptors.push({
id: 'workbench.views.extensions.trustRequiredOnDemandExtensions',
name: localize('trustRequiredOnDemandExtensions', "Trust Required For Features"),
ctorDescriptor: new SyncDescriptor(TrustRequiredOnDemandExtensionsView, [{}]),
when: ContextKeyExpr.has('trustRequiredExtensions'),
id: 'workbench.views.extensions.untrustedPartiallySupportedExtensions',
name: localize('untrustedPartiallySupportedExtensions', "Limited in Restricted Mode"),
ctorDescriptor: new SyncDescriptor(UntrustedWorkspacePartiallySupportedExtensionsView, [{}]),
when: ContextKeyExpr.has('unsupportedWorkspaceExtensions'),
});
viewDescriptors.push({
id: 'workbench.views.extensions.virtualUnsupportedExtensions',
name: localize('virtualUnsupportedExtensions', "Disabled in Virtual Workspaces"),
ctorDescriptor: new SyncDescriptor(VirtualWorkspaceUnsupportedExtensionsView, [{}]),
when: ContextKeyExpr.and(ContextKeyExpr.has('unsupportedWorkspaceExtensions'), ContextKeyExpr.has('virtualWorkspace')),
});
return viewDescriptors;
@@ -440,8 +447,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
private hasInstalledWebExtensionsContextKey: IContextKey<boolean>;
private builtInExtensionsContextKey: IContextKey<boolean>;
private searchBuiltInExtensionsContextKey: IContextKey<boolean>;
private trustRequiredExtensionsContextKey: IContextKey<boolean>;
private searchTrustRequiredExtensionsContextKey: IContextKey<boolean>;
private workspaceUnsupportedExtensionsContextKey: IContextKey<boolean>;
private searchWorkspaceUnsupportedExtensionsContextKey: IContextKey<boolean>;
private recommendedExtensionsContextKey: IContextKey<boolean>;
private searchDelayer: Delayer<void>;
@@ -478,8 +485,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
this.sortByContextKey = ExtensionsSortByContext.bindTo(contextKeyService);
this.searchMarketplaceExtensionsContextKey = SearchMarketplaceExtensionsContext.bindTo(contextKeyService);
this.searchInstalledExtensionsContextKey = SearchIntalledExtensionsContext.bindTo(contextKeyService);
this.trustRequiredExtensionsContextKey = TrustRequiredExtensionsContext.bindTo(contextKeyService);
this.searchTrustRequiredExtensionsContextKey = SearchTrustRequiredExtensionsContext.bindTo(contextKeyService);
this.workspaceUnsupportedExtensionsContextKey = UnsupportedWorkspaceExtensionsContext.bindTo(contextKeyService);
this.searchWorkspaceUnsupportedExtensionsContextKey = SearchUnsupportedWorkspaceExtensionsContext.bindTo(contextKeyService);
this.searchOutdatedExtensionsContextKey = SearchOutdatedExtensionsContext.bindTo(contextKeyService);
this.searchEnabledExtensionsContextKey = SearchEnabledExtensionsContext.bindTo(contextKeyService);
this.searchDisabledExtensionsContextKey = SearchDisabledExtensionsContext.bindTo(contextKeyService);
@@ -517,7 +524,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
const header = append(this.root, $('.header'));
const placeholder = localize('searchExtensions', "Search Extensions in Marketplace");
const searchValue = this.searchViewletState['query.value'] ? this.searchViewletState['query.value'] : (this.workspaceTrustService.isWorkpaceTrusted() ? '' : '@trustRequired');
const searchValue = this.searchViewletState['query.value'] ? this.searchViewletState['query.value'] : (this.workspaceTrustService.isWorkpaceTrusted() ? '' : '@workspaceUnsupported');
this.searchBox = this._register(this.instantiationService.createInstance(SuggestEnabledInput, `${VIEWLET_ID}.searchbox`, header, {
triggerCharacters: ['@'],
@@ -671,8 +678,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
this.searchEnabledExtensionsContextKey.set(ExtensionsListView.isEnabledExtensionsQuery(value));
this.searchDisabledExtensionsContextKey.set(ExtensionsListView.isDisabledExtensionsQuery(value));
this.searchBuiltInExtensionsContextKey.set(ExtensionsListView.isSearchBuiltInExtensionsQuery(value));
this.trustRequiredExtensionsContextKey.set(ExtensionsListView.isTrustRequiredExtensionsQuery(value));
this.searchTrustRequiredExtensionsContextKey.set(ExtensionsListView.isSearchTrustRequiredExtensionsQuery(value));
this.workspaceUnsupportedExtensionsContextKey.set(ExtensionsListView.isWorkspaceUnsupportedExtensionsQuery(value));
this.searchWorkspaceUnsupportedExtensionsContextKey.set(ExtensionsListView.isSearchWorkspaceUnsupportedExtensionsQuery(value));
this.builtInExtensionsContextKey.set(ExtensionsListView.isBuiltInExtensionsQuery(value));
this.recommendedExtensionsContextKey.set(isRecommendedExtensionsQuery);
this.searchMarketplaceExtensionsContextKey.set(!!value && !ExtensionsListView.isLocalExtensionsQuery(value) && !isRecommendedExtensionsQuery);
@@ -49,6 +49,7 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
import { getVirtualWorkspaceScheme } from 'vs/platform/remote/common/remoteHosts';
// Extensions that are automatically classified as Programming Language extensions, but should be Feature extensions
const FORCE_FEATURE_EXTENSIONS = ['vscode.git', 'vscode.search-result'];
@@ -121,6 +122,7 @@ export class ExtensionsListView extends ViewPane {
@IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService,
@IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService,
@IWorkbenchExtensionManagementService protected readonly extensionManagementService: IWorkbenchExtensionManagementService,
@IWorkspaceContextService protected readonly workspaceService: IWorkspaceContextService,
@IProductService protected readonly productService: IProductService,
@IContextKeyService contextKeyService: IContextKeyService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@@ -394,8 +396,8 @@ export class ExtensionsListView extends ViewPane {
extensions = this.filterEnabledExtensions(local, runningExtensions, query, options);
}
else if (/@trustRequired/i.test(value)) {
extensions = this.filterTrustRequiredExtensions(local, query, options);
else if (/@workspaceUnsupported/i.test(value)) {
extensions = this.filterWorkspaceUnsupportedExtensions(local, query, options);
}
return { extensions, canIncludeInstalledExtensions };
@@ -548,32 +550,46 @@ export class ExtensionsListView extends ViewPane {
return this.sortExtensions(result, options);
}
private filterTrustRequiredExtensions(local: IExtension[], query: Query, options: IQueryOptions): IExtension[] {
private filterWorkspaceUnsupportedExtensions(local: IExtension[], query: Query, options: IQueryOptions): IExtension[] {
let value = query.value;
const onStartOnly = /@trustRequired:onStart/i.test(value);
if (onStartOnly) {
value = value.replace(/@trustRequired:onStart/g, '');
const untrustedPartiallySupportedOnly = /@workspaceUnsupported:untrustedPartial/i.test(value);
if (untrustedPartiallySupportedOnly) {
value = value.replace(/@workspaceUnsupported:untrustedPartial/g, '');
}
const onDemandOnly = /@trustRequired:onDemand/i.test(value);
if (onDemandOnly) {
value = value.replace(/@trustRequired:onDemand/g, '');
const untrustedUnsupportedOnly = /@workspaceUnsupported:untrusted/i.test(value);
if (untrustedUnsupportedOnly) {
value = value.replace(/@workspaceUnsupported:untrusted/g, '');
}
const virtualUnsupportedOnly = /@workspaceUnsupported:virtual/i.test(value);
if (virtualUnsupportedOnly) {
value = value.replace(/@workspaceUnsupported:virtual/g, '');
}
value = value.replace(/@trustRequired/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase();
value = value.replace(/@workspaceUnsupported/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase();
const result = local.filter(extension => extension.local && this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(extension.local.manifest) !== true && (extension.name.toLowerCase().indexOf(value) > -1 || extension.displayName.toLowerCase().indexOf(value) > -1));
const isVirtualWorkspace = getVirtualWorkspaceScheme(this.workspaceService.getWorkspace()) !== undefined;
const virtualUnsupportedExtensions = local.filter(extension => extension.local && !this.extensionManifestPropertiesService.canSupportVirtualWorkspace(extension.local.manifest) && (extension.name.toLowerCase().indexOf(value) > -1 || extension.displayName.toLowerCase().indexOf(value) > -1));
if (onStartOnly) {
const onStartExtensions = result.filter(extension => extension.local && this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(extension.local.manifest) === false);
return this.sortExtensions(onStartExtensions, options);
let trustRequiringExtensions = local.filter(extension => extension.local && this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(extension.local.manifest) !== true && (extension.name.toLowerCase().indexOf(value) > -1 || extension.displayName.toLowerCase().indexOf(value) > -1));
if (isVirtualWorkspace) {
trustRequiringExtensions = trustRequiringExtensions.filter(extension => extension.local && this.extensionManifestPropertiesService.canSupportVirtualWorkspace(extension.local.manifest));
}
if (onDemandOnly) {
const onDemandExtensions = result.filter(extension => extension.local && this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(extension.local.manifest) === 'limited');
return this.sortExtensions(onDemandExtensions, options);
if (untrustedUnsupportedOnly) {
const untrustedUnsupportedExtensions = trustRequiringExtensions.filter(extension => extension.local && this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(extension.local.manifest) === false);
return this.sortExtensions(untrustedUnsupportedExtensions, options);
}
return this.sortExtensions(result, options);
if (untrustedPartiallySupportedOnly) {
const untrustedPartiallySupportedExtensions = trustRequiringExtensions.filter(extension => extension.local && this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(extension.local.manifest) === 'limited');
return this.sortExtensions(untrustedPartiallySupportedExtensions, options);
}
if (virtualUnsupportedOnly) {
return this.sortExtensions(virtualUnsupportedExtensions, options);
}
return this.sortExtensions(trustRequiringExtensions, options);
}
@@ -964,9 +980,9 @@ export class ExtensionsListView extends ViewPane {
|| this.isBuiltInExtensionsQuery(query)
|| this.isSearchBuiltInExtensionsQuery(query)
|| this.isBuiltInGroupExtensionsQuery(query)
|| this.isSearchTrustRequiredExtensionsQuery(query)
|| this.isTrustRequiredExtensionsQuery(query)
|| this.isTrustRequiredGroupExtensionsQuery(query);
|| this.isSearchWorkspaceUnsupportedExtensionsQuery(query)
|| this.isWorkspaceUnsupportedExtensionsQuery(query)
|| this.isWorkspaceUnsupportedGroupExtensionsQuery(query);
}
static isSearchBuiltInExtensionsQuery(query: string): boolean {
@@ -981,16 +997,16 @@ export class ExtensionsListView extends ViewPane {
return /^\s*@builtin:.+$/i.test(query.trim());
}
static isSearchTrustRequiredExtensionsQuery(query: string): boolean {
return /@trustRequired\s.+/i.test(query);
static isSearchWorkspaceUnsupportedExtensionsQuery(query: string): boolean {
return /@workspaceUnsupported\s.+/i.test(query);
}
static isTrustRequiredExtensionsQuery(query: string): boolean {
return /^\s*@trustRequired$/i.test(query.trim());
static isWorkspaceUnsupportedExtensionsQuery(query: string): boolean {
return /^\s*@workspaceUnsupported$/i.test(query.trim());
}
static isTrustRequiredGroupExtensionsQuery(query: string): boolean {
return /^\s*@trustRequired:.+$/i.test(query.trim());
static isWorkspaceUnsupportedGroupExtensionsQuery(query: string): boolean {
return /^\s*@workspaceUnsupported:.+$/i.test(query.trim());
}
static isInstalledExtensionsQuery(query: string): boolean {
@@ -1088,15 +1104,21 @@ export class BuiltInProgrammingLanguageExtensionsView extends ExtensionsListView
}
}
export class TrustRequiredOnStartExtensionsView extends ExtensionsListView {
export class UntrustedWorkspaceUnsupportedExtensionsView extends ExtensionsListView {
override async show(query: string): Promise<IPagedModel<IExtension>> {
return (query && query.trim() !== '@trustRequired') ? this.showEmptyModel() : super.show('@trustRequired:onStart');
return (query && query.trim() !== '@workspaceUnsupported') ? this.showEmptyModel() : super.show('@workspaceUnsupported:untrusted');
}
}
export class TrustRequiredOnDemandExtensionsView extends ExtensionsListView {
export class UntrustedWorkspacePartiallySupportedExtensionsView extends ExtensionsListView {
override async show(query: string): Promise<IPagedModel<IExtension>> {
return (query && query.trim() !== '@trustRequired') ? this.showEmptyModel() : super.show('@trustRequired:onDemand');
return (query && query.trim() !== '@workspaceUnsupported') ? this.showEmptyModel() : super.show('@workspaceUnsupported:untrustedPartial');
}
}
export class VirtualWorkspaceUnsupportedExtensionsView extends ExtensionsListView {
override async show(query: string): Promise<IPagedModel<IExtension>> {
return (query && query.trim() !== '@workspaceUnsupported') ? this.showEmptyModel() : super.show('@workspaceUnsupported:virtual');
}
}
@@ -13,7 +13,7 @@ export class Query {
}
static suggestions(query: string): string[] {
const commands = ['installed', 'outdated', 'enabled', 'disabled', 'builtin', 'recommended', 'trustRequired', 'sort', 'category', 'tag', 'ext', 'id'] as const;
const commands = ['installed', 'outdated', 'enabled', 'disabled', 'builtin', 'recommended', 'workspaceUnsupported', 'sort', 'category', 'tag', 'ext', 'id'] as const;
const subcommands = {
'sort': ['installs', 'rating', 'name'],
'category': EXTENSION_CATEGORIES.map(c => `"${c.toLowerCase()}"`),
@@ -17,6 +17,7 @@ import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/
import { FILE_EDITOR_INPUT_ID } from 'vs/workbench/contrib/files/common/files';
import { Schemas } from 'vs/base/common/network';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { IWorkingCopyEditorService } from 'vs/workbench/services/workingCopy/common/workingCopyEditorService';
export class TextFileEditorTracker extends Disposable implements IWorkbenchContribution {
@@ -26,7 +27,8 @@ export class TextFileEditorTracker extends Disposable implements IWorkbenchContr
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IHostService private readonly hostService: IHostService,
@ICodeEditorService private readonly codeEditorService: ICodeEditorService,
@IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService
@IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService,
@IWorkingCopyEditorService private readonly workingCopyEditorService: IWorkingCopyEditorService
) {
super();
@@ -57,19 +59,24 @@ export class TextFileEditorTracker extends Disposable implements IWorkbenchContr
return false; // resource must be dirty
}
const model = this.textFileService.files.get(resource);
if (model?.hasState(TextFileEditorModelState.PENDING_SAVE)) {
const fileModel = this.textFileService.files.get(resource);
if (fileModel?.hasState(TextFileEditorModelState.PENDING_SAVE)) {
return false; // resource must not be pending to save
}
if (this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY && !model?.hasState(TextFileEditorModelState.ERROR)) {
if (this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY && !fileModel?.hasState(TextFileEditorModelState.ERROR)) {
// leave models auto saved after short delay unless
// the save resulted in an error
return false;
}
if (this.editorService.isOpened({ resource, typeId: resource.scheme === Schemas.untitled ? UntitledTextEditorInput.ID : FILE_EDITOR_INPUT_ID })) {
return false; // model must not be opened already as file
return false; // model must not be opened already as file (fast check via editor type)
}
const model = fileModel ?? this.textFileService.untitled.get(resource);
if (model && this.workingCopyEditorService.findEditor(model)) {
return false; // model must not be opened already as file (slower check via working copy)
}
return true;
@@ -30,7 +30,7 @@ export class ExplorerModel implements IDisposable {
fileService: IFileService
) {
const setRoots = () => this._roots = this.contextService.getWorkspace().folders
.map(folder => new ExplorerItem(folder.uri, fileService, undefined, true, false, folder.name));
.map(folder => new ExplorerItem(folder.uri, fileService, undefined, true, false, false, folder.name));
setRoots();
this._listener = this.contextService.onDidChangeWorkspaceFolders(() => {
@@ -89,6 +89,7 @@ export class ExplorerItem {
private _parent: ExplorerItem | undefined,
private _isDirectory?: boolean,
private _isSymbolicLink?: boolean,
private _readonly?: boolean,
private _name: string = basenameOrAuthority(resource),
private _mtime?: number,
private _unknown = false
@@ -124,7 +125,7 @@ export class ExplorerItem {
}
get isReadonly(): boolean {
return this.fileService.hasCapability(this.resource, FileSystemProviderCapabilities.Readonly);
return this._readonly || this.fileService.hasCapability(this.resource, FileSystemProviderCapabilities.Readonly);
}
get mtime(): number | undefined {
@@ -179,7 +180,7 @@ export class ExplorerItem {
}
static create(fileService: IFileService, raw: IFileStat, parent: ExplorerItem | undefined, resolveTo?: readonly URI[]): ExplorerItem {
const stat = new ExplorerItem(raw.resource, fileService, parent, raw.isDirectory, raw.isSymbolicLink, raw.name, raw.mtime, !raw.isFile && !raw.isDirectory);
const stat = new ExplorerItem(raw.resource, fileService, parent, raw.isDirectory, raw.isSymbolicLink, raw.readonly, raw.name, raw.mtime, !raw.isFile && !raw.isDirectory);
// Recursively add children if present
if (stat.isDirectory) {
@@ -14,7 +14,7 @@ import { TestFileService } from 'vs/workbench/test/browser/workbenchTestServices
const fileService = new TestFileService();
function createStat(this: any, path: string, name: string, isFolder: boolean, hasChildren: boolean, size: number, mtime: number): ExplorerItem {
return new ExplorerItem(toResource.call(this, path), fileService, undefined, isFolder, false, name, mtime);
return new ExplorerItem(toResource.call(this, path), fileService, undefined, isFolder, false, false, name, mtime);
}
suite('Files - View Model', function () {
@@ -245,19 +245,19 @@ suite('Files - View Model', function () {
});
test('Merge Local with Disk', function () {
const merge1 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), fileService, undefined, true, false, 'to', Date.now());
const merge2 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), fileService, undefined, true, false, 'to', Date.now());
const merge1 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), fileService, undefined, true, false, false, 'to', Date.now());
const merge2 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), fileService, undefined, true, false, false, 'to', Date.now());
// Merge Properties
ExplorerItem.mergeLocalWithDisk(merge2, merge1);
assert.strictEqual(merge1.mtime, merge2.mtime);
// Merge Child when isDirectoryResolved=false is a no-op
merge2.addChild(new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), fileService, undefined, true, false, 'foo.html', Date.now()));
merge2.addChild(new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), fileService, undefined, true, false, false, 'foo.html', Date.now()));
ExplorerItem.mergeLocalWithDisk(merge2, merge1);
// Merge Child with isDirectoryResolved=true
const child = new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), fileService, undefined, true, false, 'foo.html', Date.now());
const child = new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), fileService, undefined, true, false, false, 'foo.html', Date.now());
merge2.removeChild(child);
merge2.addChild(child);
(<any>merge2)._isDirectoryResolved = true;
@@ -19,7 +19,7 @@ const $ = dom.$;
const fileService = new TestFileService();
function createStat(this: any, path: string, name: string, isFolder: boolean, hasChildren: boolean, size: number, mtime: number, isSymLink = false, isUnknown = false): ExplorerItem {
return new ExplorerItem(toResource.call(this, path), fileService, undefined, isFolder, isSymLink, name, mtime, isUnknown);
return new ExplorerItem(toResource.call(this, path), fileService, undefined, isFolder, isSymLink, false, name, mtime, isUnknown);
}
suite('Files - ExplorerView', () => {
@@ -6,41 +6,3 @@
// Scrollable Element
export const SCROLLABLE_ELEMENT_PADDING_TOP = 20;
// export const SCROLLABLE_ELEMENT_PADDING_TOP_WITH_TOOLBAR = 8;
// Code cell layout:
// [CODE_CELL_LEFT_MARGIN][CELL_RUN_GUTTER][editorWidth][CELL_RIGHT_MARGIN]
// Markdown cell layout:
// [CELL_MARGIN][content][CELL_RIGHT_MARGIN]
// Markdown editor cell layout:
// [CODE_CELL_LEFT_MARGIN][content][CELL_RIGHT_MARGIN]
// Cell sizing related
export const CELL_RIGHT_MARGIN = 16;
export const CELL_RUN_GUTTER = 28;
export const CODE_CELL_LEFT_MARGIN = 32;
export const EDITOR_TOOLBAR_HEIGHT = 0;
export const BOTTOM_CELL_TOOLBAR_GAP = 18;
export const BOTTOM_CELL_TOOLBAR_HEIGHT = 22;
export const CELL_STATUSBAR_HEIGHT = 22;
// Margin above editor
export const CELL_TOP_MARGIN = 6;
export const CELL_BOTTOM_MARGIN = 6;
export const MARKDOWN_CELL_TOP_MARGIN = 8;
export const MARKDOWN_CELL_BOTTOM_MARGIN = 8;
// 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 = 4;
export const EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR = 12;
export const CELL_OUTPUT_PADDING = 14;
export const COLLAPSED_INDICATOR_HEIGHT = 24;
export const MARKDOWN_PREVIEW_PADDING = 8;
@@ -35,6 +35,9 @@ import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/not
import { Iterable } from 'vs/base/common/iterator';
import { flatten } from 'vs/base/common/arrays';
// Kernel Command
export const SELECT_KERNEL_ID = 'notebook.selectKernel';
// Notebook Commands
const EXECUTE_NOTEBOOK_COMMAND_ID = 'notebook.execute';
const CANCEL_NOTEBOOK_COMMAND_ID = 'notebook.cancelExecution';
@@ -28,6 +28,7 @@ import { StartFindAction, StartFindReplaceAction } from 'vs/editor/contrib/find/
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { NLS_MATCHES_LOCATION, NLS_NO_RESULTS } from 'vs/editor/contrib/find/findWidget';
import { FindModel } from 'vs/workbench/contrib/notebook/browser/contrib/find/findModel';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
const FIND_HIDE_TRANSITION = 'find-hide-transition';
const FIND_SHOW_TRANSITION = 'find-show-transition';
@@ -325,7 +326,7 @@ registerAction2(class extends Action2 {
}
});
StartFindAction.addImplementation(100, (accessor: ServicesAccessor, args: any) => {
StartFindAction.addImplementation(100, (accessor: ServicesAccessor, codeEditor: ICodeEditor, args: any) => {
const editorService = accessor.get(IEditorService);
const editor = getNotebookEditorFromEditorPane(editorService.activeEditorPane);
@@ -338,7 +339,7 @@ StartFindAction.addImplementation(100, (accessor: ServicesAccessor, args: any) =
return true;
});
StartFindReplaceAction.addImplementation(100, (accessor: ServicesAccessor, args: any) => {
StartFindReplaceAction.addImplementation(100, (accessor: ServicesAccessor, codeEditor: ICodeEditor, args: any) => {
const editorService = accessor.get(IEditorService);
const editor = getNotebookEditorFromEditorPane(editorService.activeEditorPane);
@@ -38,7 +38,7 @@ export class FoldingController extends Disposable implements INotebookEditorCont
return;
}
this._localStore.add(this._notebookEditor.viewModel.eventDispatcher.onDidChangeCellState(e => {
this._localStore.add(this._notebookEditor.viewModel.viewContext.eventDispatcher.onDidChangeCellState(e => {
if (e.source.editStateChanged && e.cell.cellKind === CellKind.Markup) {
this._foldingModel?.recompute();
// this._updateEditorFoldingRanges();
@@ -5,11 +5,11 @@
import * as nls from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IQuickInputButton, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
import { getNotebookEditorFromEditorPane, INotebookEditor, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NOTEBOOK_ACTIONS_CATEGORY, SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
import { getNotebookEditorFromEditorPane, INotebookEditor, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
@@ -26,16 +26,23 @@ import { ILogService } from 'vs/platform/log/common/log';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { HoverProviderRegistry } from 'vs/editor/common/modes';
import { Schemas } from 'vs/base/common/network';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
registerAction2(class extends Action2 {
constructor() {
super({
id: 'notebook.selectKernel',
id: SELECT_KERNEL_ID,
category: NOTEBOOK_ACTIONS_CATEGORY,
title: { value: nls.localize('notebookActions.selectKernel', "Select Notebook Kernel"), original: 'Select Notebook Kernel' },
precondition: NOTEBOOK_IS_ACTIVE_EDITOR,
icon: selectKernelIcon,
f1: true,
menu: {
id: MenuId.EditorTitle,
when: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT.notEqualsTo(0), ContextKeyExpr.equals('config.notebook.experimental.showKernelInEditorTitle', true),),
group: 'navigation',
order: -10
},
description: {
description: nls.localize('notebookActions.selectKernel.args', "Notebook Kernel Args"),
args: [
@@ -57,7 +64,6 @@ registerAction2(class extends Action2 {
}
]
},
});
}
@@ -265,7 +271,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution {
text: `$(notebook-kernel-select) ${kernel.label}`,
ariaLabel: kernel.label,
tooltip: isSuggested ? nls.localize('tooltop', "{0} (suggestion)", tooltip) : tooltip,
command: 'notebook.selectKernel',
command: SELECT_KERNEL_ID,
},
'notebook.selectKernel',
nls.localize('notebook.info', "Notebook Kernel Info"),
@@ -282,7 +288,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution {
{
text: nls.localize('kernel.select.label', "Select Kernel"),
ariaLabel: nls.localize('kernel.select.label', "Select Kernel"),
command: 'notebook.selectKernel',
command: SELECT_KERNEL_ID,
backgroundColor: { id: 'statusBarItem.prominentBackground' }
},
'notebook.selectKernel',
@@ -10,7 +10,6 @@ import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/edit
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { DiffElementViewModelBase, getFormatedMetadataJSON, OUTPUT_EDITOR_HEIGHT_MAGIC, PropertyFoldingState, SideBySideDiffElementViewModel, SingleSideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel';
import { CellDiffSideBySideRenderTemplate, CellDiffSingleSideRenderTemplate, DiffSide, DIFF_CELL_MARGIN, INotebookTextDiffEditor, NOTEBOOK_DIFF_CELL_PROPERTY, NOTEBOOK_DIFF_CELL_PROPERTY_EXPANDED } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser';
import { EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget';
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget';
import { IModelService } from 'vs/editor/common/services/modelService';
@@ -26,7 +25,6 @@ import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/men
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { Delayer } from 'vs/base/common/async';
import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView';
import { getEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { collapsedIcon, expandedIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
import { OutputContainer } from 'vs/workbench/contrib/notebook/browser/diff/diffElementOutputs';
import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions';
@@ -980,7 +978,8 @@ export class DeletedElement extends SingleSideDiffElement {
const originalCell = this.cell.original!;
const lineCount = originalCell.textModel.textBuffer.getLineCount();
const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17;
const editorHeight = lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING;
const editorPadding = this.notebookEditor.notebookOptions.computeEditorPadding();
const editorHeight = lineCount * lineHeight + editorPadding.top + editorPadding.bottom;
this._editor = this.templateData.sourceEditor;
this._editor.layout({
@@ -1131,7 +1130,8 @@ export class InsertElement extends SingleSideDiffElement {
const modifiedCell = this.cell.modified!;
const lineCount = modifiedCell.textModel.textBuffer.getLineCount();
const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17;
const editorHeight = lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING;
const editorPadding = this.notebookEditor.notebookOptions.computeEditorPadding();
const editorHeight = lineCount * lineHeight + editorPadding.top + editorPadding.bottom;
this._editor = this.templateData.sourceEditor;
this._editor.layout(
@@ -1469,7 +1469,9 @@ export class ModifiedElement extends AbstractElementRenderer {
const modifiedCell = this.cell.modified!;
const lineCount = modifiedCell.textModel.textBuffer.getLineCount();
const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17;
const editorHeight = this.cell.layoutInfo.editorHeight !== 0 ? this.cell.layoutInfo.editorHeight : lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING;
const editorPadding = this.notebookEditor.notebookOptions.computeEditorPadding();
const editorHeight = this.cell.layoutInfo.editorHeight !== 0 ? this.cell.layoutInfo.editorHeight : lineCount * lineHeight + editorPadding.top + editorPadding.bottom;
this._editorContainer = this.templateData.editorContainer;
this._editor = this.templateData.sourceEditor;
@@ -15,6 +15,7 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
export enum DiffSide {
Original = 0,
@@ -26,6 +27,7 @@ export interface IDiffCellInfo extends ICommonCellInfo {
}
export interface INotebookTextDiffEditor extends ICommonNotebookEditor {
notebookOptions: NotebookOptions;
readonly textModel?: NotebookTextModel;
onMouseUp: Event<{ readonly event: MouseEvent; readonly target: DiffElementViewModelBase; }>;
onDidDynamicOutputRendered: Event<{ cell: IGenericCellViewModel, output: ICellOutputViewModel }>;
@@ -38,9 +38,9 @@ import { generateUuid } from 'vs/base/common/uuid';
import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { DiffNestedCellViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffNestedCellViewModel';
import { BackLayerWebView } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';
import { CELL_OUTPUT_PADDING, MARKDOWN_PREVIEW_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { NotebookDiffEditorEventDispatcher, NotebookDiffLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/diff/eventDispatcher';
import { readFontInfo } from 'vs/editor/browser/config/configuration';
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
const $ = DOM.$;
@@ -75,6 +75,12 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
protected _onDidDynamicOutputRendered = new Emitter<{ cell: IGenericCellViewModel, output: ICellOutputViewModel }>();
onDidDynamicOutputRendered = this._onDidDynamicOutputRendered.event;
private _notebookOptions: NotebookOptions;
get notebookOptions() {
return this._notebookOptions;
}
private readonly _localStore = this._register(new DisposableStore());
private _isDisposed: boolean = false;
@@ -93,10 +99,11 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
@IStorageService storageService: IStorageService,
) {
super(NotebookTextDiffEditor.ID, telemetryService, themeService, storageService);
this._notebookOptions = new NotebookOptions(this.configurationService);
this._register(this._notebookOptions);
const editorOptions = this.configurationService.getValue<IEditorOptions>('editor');
this._fontInfo = readFontInfo(BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel(), getPixelRatio()));
this._revealFirst = true;
this._outputRenderer = new OutputRenderer(this, this.instantiationService);
}
@@ -366,21 +373,12 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
}));
}
private readonly webviewOptions = {
outputNodePadding: CELL_OUTPUT_PADDING,
outputNodeLeftPadding: 32,
previewNodePadding: MARKDOWN_PREVIEW_PADDING,
leftMargin: 0,
rightMargin: 0,
runGutter: 0
};
private async _createModifiedWebview(id: string, resource: URI): Promise<void> {
if (this._modifiedWebview) {
this._modifiedWebview.dispose();
}
this._modifiedWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, this.webviewOptions) as BackLayerWebView<IDiffCellInfo>;
this._modifiedWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, this._notebookOptions.computeDiffWebviewOptions()) as BackLayerWebView<IDiffCellInfo>;
// attach the webview container to the DOM tree first
this._list.rowsContainer.insertAdjacentElement('afterbegin', this._modifiedWebview.element);
await this._modifiedWebview.createWebview();
@@ -393,7 +391,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
this._originalWebview.dispose();
}
this._originalWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, this.webviewOptions) as BackLayerWebView<IDiffCellInfo>;
this._originalWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, this._notebookOptions.computeDiffWebviewOptions()) as BackLayerWebView<IDiffCellInfo>;
// attach the webview container to the DOM tree first
this._list.rowsContainer.insertAdjacentElement('afterbegin', this._originalWebview.element);
await this._originalWebview.createWebview();
@@ -42,22 +42,6 @@ export interface INotebookRendererContribution {
readonly [NotebookRendererContribution.optionalDependencies]: readonly string[];
}
enum NotebookMarkupRendererContribution {
id = 'id',
displayName = 'displayName',
entrypoint = 'entrypoint',
dependsOn = 'dependsOn',
mimeTypes = 'mimeTypes',
}
export interface INotebookMarkupRendererContribution {
readonly [NotebookMarkupRendererContribution.id]?: string;
readonly [NotebookMarkupRendererContribution.displayName]: string;
readonly [NotebookMarkupRendererContribution.entrypoint]: string;
readonly [NotebookMarkupRendererContribution.dependsOn]: string | undefined;
readonly [NotebookMarkupRendererContribution.mimeTypes]: string[] | undefined;
}
const notebookProviderContribution: IJSONSchema = {
description: nls.localize('contributes.notebook.provider', 'Contributes notebook document provider.'),
type: 'array',
@@ -164,48 +148,6 @@ const notebookRendererContribution: IJSONSchema = {
}
}
};
const notebookMarkupRendererContribution: IJSONSchema = {
description: nls.localize('contributes.notebook.markdownRenderer', 'Contributes a renderer for markdown cells in notebooks.'),
type: 'array',
defaultSnippets: [{ body: [{ id: '', displayName: '', entrypoint: '' }] }],
items: {
type: 'object',
required: [
NotebookMarkupRendererContribution.id,
NotebookMarkupRendererContribution.displayName,
NotebookMarkupRendererContribution.entrypoint,
],
properties: {
[NotebookMarkupRendererContribution.id]: {
type: 'string',
description: nls.localize('contributes.notebook.markdownRenderer.id', 'Unique identifier of the notebook markdown renderer.'),
},
[NotebookMarkupRendererContribution.displayName]: {
type: 'string',
description: nls.localize('contributes.notebook.markdownRenderer.displayName', 'Human readable name of the notebook markdown renderer.'),
},
[NotebookMarkupRendererContribution.entrypoint]: {
type: 'string',
description: nls.localize('contributes.notebook.markdownRenderer.entrypoint', 'File to load in the webview to render the extension.'),
},
[NotebookMarkupRendererContribution.mimeTypes]: {
type: 'array',
items: { type: 'string' },
description: nls.localize('contributes.notebook.markdownRenderer.mimeTypes', 'The mime type that the renderer handles.'),
},
[NotebookMarkupRendererContribution.dependsOn]: {
type: 'string',
description: nls.localize('contributes.notebook.markdownRenderer.dependsOn', 'If specified, this renderer augments another renderer instead of providing full rendering.'),
},
}
}
};
export const notebooksExtensionPoint2 = ExtensionsRegistry.registerExtensionPoint<INotebookEditorContribution[]>(
{
extensionPoint: 'notebookProvider',
jsonSchema: { deprecationMessage: 'Use \'notebooks\' instead' }
});
export const notebooksExtensionPoint = ExtensionsRegistry.registerExtensionPoint<INotebookEditorContribution[]>(
{
@@ -218,9 +160,3 @@ export const notebookRendererExtensionPoint = ExtensionsRegistry.registerExtensi
extensionPoint: 'notebookOutputRenderer',
jsonSchema: notebookRendererContribution
});
export const notebookMarkupRendererExtensionPoint = ExtensionsRegistry.registerExtensionPoint<INotebookMarkupRendererContribution[]>(
{
extensionPoint: 'notebookMarkupRenderers',
jsonSchema: notebookMarkupRendererContribution
});
@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench .kernel-action-view-item {
border-radius: 5px;
}
.monaco-workbench .kernel-action-view-item:hover {
background-color: var(--code-toolbarHoverBackground);
}
.monaco-workbench .kernel-action-view-item .action-label {
display: inline-flex;
}
.monaco-workbench .kernel-action-view-item .kernel-label {
font-size: 11px;
padding: 3px 5px 3px 3px;
border-radius: 5px;
height: 16px;
display: inline-flex;
vertical-align: text-bottom;
}
@@ -8,7 +8,7 @@ import { IListContextMenuEvent, IListEvent, IListMouseEvent } from 'vs/base/brow
import { IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWidget';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { Emitter, Event } from 'vs/base/common/event';
import { Event } from 'vs/base/common/event';
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { ScrollEvent } from 'vs/base/common/scrollable';
import { URI } from 'vs/base/common/uri';
@@ -31,6 +31,7 @@ import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
import { IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation';
import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets';
import { INotebookWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
export const NOTEBOOK_EDITOR_ID = 'workbench.editor.notebook';
export const NOTEBOOK_DIFF_EDITOR_ID = 'workbench.editor.notebookTextDiffEditor';
@@ -382,6 +383,7 @@ export interface INotebookEditor extends ICommonNotebookEditor {
readonly onDidScroll: Event<void>;
readonly onDidChangeActiveCell: Event<void>;
readonly notebookOptions: NotebookOptions;
isDisposed: boolean;
dispose(): void;
@@ -908,20 +910,6 @@ export function getNotebookEditorFromEditorPane(editorPane?: IEditorPane): INote
return editorPane?.getId() === NOTEBOOK_EDITOR_ID ? editorPane.getControl() as INotebookEditor | undefined : undefined;
}
let EDITOR_TOP_PADDING = 12;
const editorTopPaddingChangeEmitter = new Emitter<void>();
export const EditorTopPaddingChangeEvent = editorTopPaddingChangeEmitter.event;
export function updateEditorTopPadding(top: number) {
EDITOR_TOP_PADDING = top;
editorTopPaddingChangeEmitter.fire();
}
export function getEditorTopPadding() {
return EDITOR_TOP_PADDING;
}
/**
* ranges: model selections
* this will convert model selections to view indexes first, and then include the hidden ranges in the list view
@@ -29,6 +29,10 @@ import { NotebookEditorOptions, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/
import { IBorrowValue, INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService';
import { clearMarks, getAndClearMarks, mark } from 'vs/workbench/contrib/notebook/common/notebookPerformance';
import { IFileService } from 'vs/platform/files/common/files';
import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { IAction } from 'vs/base/common/actions';
import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
import { NotebooKernelActionViewItem } from 'vs/workbench/contrib/notebook/browser/notebookKernelActionViewItem';
const NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'NotebookEditorViewState';
@@ -103,6 +107,14 @@ export class NotebookEditor extends EditorPane {
return this._rootElement;
}
override getActionViewItem(action: IAction): IActionViewItem | undefined {
if (action.id === SELECT_KERNEL_ID) {
// this is being disposed by the consumer
return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this);
}
return undefined;
}
override getControl(): NotebookEditorWidget | undefined {
return this._widget.value;
}
@@ -10,6 +10,7 @@ import { CellKind, INotebookKernel, INotebookTextModel, NotebookCellExecutionSta
import { ICommandService } from 'vs/platform/commands/common/commands';
import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust';
import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
export class NotebookEditorKernelManager extends Disposable {
@@ -36,7 +37,7 @@ export class NotebookEditorKernelManager extends Disposable {
let kernel = this.getSelectedOrSuggestedKernel(notebook);
if (!kernel) {
await this._commandService.executeCommand('notebook.selectKernel');
await this._commandService.executeCommand(SELECT_KERNEL_ID);
kernel = this.getSelectedOrSuggestedKernel(notebook);
}
@@ -42,7 +42,6 @@ import { IEditorMemento } from 'vs/workbench/common/editor';
import { Memento, MementoObject } from 'vs/workbench/common/memento';
import { PANEL_BORDER } from 'vs/workbench/common/theme';
import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugColors';
import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_OUTPUT_PADDING, CELL_RIGHT_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, MARKDOWN_CELL_BOTTOM_MARGIN, MARKDOWN_CELL_TOP_MARGIN, MARKDOWN_PREVIEW_PADDING, SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFocusMode, IActiveNotebookEditor, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IFocusNotebookCellOptions, IGenericCellViewModel, IInsetRenderOutput, INotebookCellList, INotebookCellOutputLayoutInfo, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorMouseEvent, NotebookEditorOptions, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_ID, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookDecorationCSSRules, NotebookRefCountedStyleSheet } from 'vs/workbench/contrib/notebook/browser/notebookEditorDecorations';
import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
@@ -59,7 +58,7 @@ import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbenc
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { CellKind, CellToolbarLocKey, CellToolbarVisibility, ExperimentalUseMarkdownRenderer, SelectionStateType, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellKind, ExperimentalUseMarkdownRenderer, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator';
import { Webview } from 'vs/workbench/contrib/webview/browser/webview';
@@ -75,6 +74,9 @@ import { mark } from 'vs/workbench/contrib/notebook/common/notebookPerformance';
import { readFontInfo } from 'vs/editor/browser/config/configuration';
import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
import { NotebookEditorContextKeys } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidgetContextKeys';
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
import { SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants';
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
const $ = DOM.$;
@@ -203,6 +205,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
private _overlayContainer!: HTMLElement;
private _notebookTopToolbarContainer!: HTMLElement;
private _body!: HTMLElement;
private _styleElement!: HTMLStyleElement;
private _overflowContainer!: HTMLElement;
private _webview: BackLayerWebView<ICommonCellInfo> | null = null;
private _webviewResolvePromise: Promise<BackLayerWebView<ICommonCellInfo> | null> | null = null;
@@ -213,7 +216,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
private _dndController: CellDragAndDropController | null = null;
private _listTopCellToolbar: ListTopCellToolbar | null = null;
private _renderedEditors: Map<ICellViewModel, ICodeEditor | undefined> = new Map();
private _eventDispatcher: NotebookEventDispatcher | undefined;
private _viewContext: ViewContext | undefined;
private _notebookViewModel: NotebookViewModel | undefined;
private _localStore: DisposableStore = this._register(new DisposableStore());
private _localCellStateListeners: DisposableStore[] = [];
@@ -306,6 +309,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
public readonly scopedContextKeyService: IContextKeyService;
private readonly instantiationService: IInstantiationService;
private readonly _notebookOptions: NotebookOptions;
get notebookOptions() {
return this._notebookOptions;
}
constructor(
readonly creationOptions: INotebookEditorCreationOptions,
@@ -330,6 +338,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this.isEmbedded = creationOptions.isEmbedded || false;
this.useRenderer = !isWeb && !!this.configurationService.getValue<boolean>(ExperimentalUseMarkdownRenderer) && !accessibilityService.isScreenReaderOptimized();
this._notebookOptions = new NotebookOptions(this.configurationService);
this._register(this._notebookOptions);
this._overlayContainer = document.createElement('div');
this.scopedContextKeyService = contextKeyService.createScoped(this._overlayContainer);
@@ -356,8 +366,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this.layout(this._dimension);
}
}
}));
if (e.affectsConfiguration(CellToolbarLocKey) || e.affectsConfiguration(ShowCellStatusBarKey) || e.affectsConfiguration(CellToolbarVisibility)) {
this._register(this._notebookOptions.onDidChangeOptions(e => {
if (e.cellStatusBarVisibility || e.cellToolbarLocation || e.cellToolbarInteraction) {
this._updateForNotebookConfiguration();
}
}));
@@ -488,43 +500,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
return;
}
const cellToolbarLocation = this.configurationService.getValue<string | { [key: string]: string }>(CellToolbarLocKey);
this._overlayContainer.classList.remove('cell-title-toolbar-left');
this._overlayContainer.classList.remove('cell-title-toolbar-right');
this._overlayContainer.classList.remove('cell-title-toolbar-hidden');
const cellToolbarLocation = this._notebookOptions.computeCellToolbarLocation(this.viewModel?.viewType);
this._overlayContainer.classList.add(`cell-title-toolbar-${cellToolbarLocation}`);
if (typeof cellToolbarLocation === 'string') {
if (cellToolbarLocation === 'left' || cellToolbarLocation === 'right' || cellToolbarLocation === 'hidden') {
this._overlayContainer.classList.add(`cell-title-toolbar-${cellToolbarLocation}`);
}
} else {
if (this.viewModel) {
const notebookSpecificSetting = cellToolbarLocation[this.viewModel.viewType] ?? cellToolbarLocation['default'];
let cellToolbarLocationForCurrentView = 'right';
switch (notebookSpecificSetting) {
case 'left':
cellToolbarLocationForCurrentView = 'left';
break;
case 'right':
cellToolbarLocationForCurrentView = 'right';
case 'hidden':
cellToolbarLocationForCurrentView = 'hidden';
default:
cellToolbarLocationForCurrentView = 'right';
break;
}
this._overlayContainer.classList.add(`cell-title-toolbar-${cellToolbarLocationForCurrentView}`);
} else {
this._overlayContainer.classList.add(`cell-title-toolbar-right`);
}
}
const showCellStatusBar = this.configurationService.getValue<boolean>(ShowCellStatusBarKey);
const showCellStatusBar = this._notebookOptions.getLayoutConfiguration().showCellStatusBar;
this._overlayContainer.classList.toggle('cell-statusbar-hidden', !showCellStatusBar);
const cellToolbarInteraction = this.configurationService.getValue<string>(CellToolbarVisibility);
const cellToolbarInteraction = this._notebookOptions.getLayoutConfiguration().cellToolbarInteraction;
let cellToolbarInteractionState = 'hover';
this._overlayContainer.classList.remove('cell-toolbar-hover');
this._overlayContainer.classList.remove('cell-toolbar-click');
@@ -547,15 +532,74 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this._notebookTopToolbarContainer.style.display = 'none';
DOM.append(parent, this._notebookTopToolbarContainer);
this._body = document.createElement('div');
this._body.classList.add('cell-list-container');
this._createCellList();
DOM.append(parent, this._body);
this._body.classList.add('cell-list-container');
this._createLayoutStyles();
this._createCellList();
this._overflowContainer = document.createElement('div');
this._overflowContainer.classList.add('notebook-overflow-widget-container', 'monaco-editor');
DOM.append(parent, this._overflowContainer);
}
private _createLayoutStyles(): void {
this._styleElement = DOM.createStyleSheet(this._body);
const {
cellRightMargin,
cellTopMargin,
cellRunGutter,
cellBottomMargin,
codeCellLeftMargin,
markdownCellBottomMargin,
markdownCellTopMargin,
bottomCellToolbarGap,
bottomCellToolbarHeight,
collapsedIndicatorHeight
} = this._notebookOptions.getLayoutConfiguration();
const styleSheets: string[] = [];
styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row div.cell.code { margin-left: ${codeCellLeftMargin}px; }`);
styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .code-cell-row div.cell.code { margin-left: ${codeCellLeftMargin + cellRunGutter}px; }`);
styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell { margin-right: ${cellRightMargin}px; }`);
styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .cell-inner-container { padding-top: ${cellTopMargin}px; }`);
styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .cell-inner-container { padding-bottom: ${markdownCellBottomMargin}px; padding-top: ${markdownCellTopMargin}px; }`);
styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .cell-inner-container.webview-backed-markdown-cell { padding: 0; }`);
styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .webview-backed-markdown-cell.markdown-cell-edit-mode .cell.code { padding-bottom: ${markdownCellBottomMargin}px; padding-top: ${markdownCellTopMargin}px; }`);
styleSheets.push(`.notebookOverlay .output { margin: 0px ${cellRightMargin}px 0px ${codeCellLeftMargin + cellRunGutter}px; }`);
styleSheets.push(`.notebookOverlay .output { width: calc(100% - ${codeCellLeftMargin + cellRunGutter + cellRightMargin}px); }`);
styleSheets.push(`.notebookOverlay .output-show-more-container { margin: 0px ${cellRightMargin}px 0px ${codeCellLeftMargin + cellRunGutter}px; }`);
styleSheets.push(`.notebookOverlay .output-show-more-container { width: calc(100% - ${codeCellLeftMargin + cellRunGutter + cellRightMargin}px); }`);
styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.markdown { padding-left: ${cellRunGutter}px; }`);
styleSheets.push(`.notebookOverlay .cell .run-button-container { width: 20px; left: ${codeCellLeftMargin + Math.floor(cellRunGutter - 20) / 2}px }`);
styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row :not(.webview-backed-markdown-cell) .cell-focus-indicator-top { height: ${cellTopMargin}px; }`);
styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-side { bottom: ${bottomCellToolbarGap}px; }`);
styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row.code-cell-row .cell-focus-indicator-left,
.notebookOverlay .monaco-list .monaco-list-row.code-cell-row .cell-drag-handle { width: ${codeCellLeftMargin + cellRunGutter}px; }`);
styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .cell-focus-indicator-left { width: ${codeCellLeftMargin}px; }`);
styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator.cell-focus-indicator-right { width: ${cellRightMargin}px; }`);
styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom { height: ${cellBottomMargin}px; }`);
styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-shadow-container-bottom { top: ${cellBottomMargin}px; }`);
styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-collapsed-part { margin-left: ${codeCellLeftMargin + cellRunGutter}px; height: ${collapsedIndicatorHeight}px; }`);
styleSheets.push(`.notebookOverlay .cell-list-top-cell-toolbar-container { top: -${SCROLLABLE_ELEMENT_PADDING_TOP}px }`);
styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { height: ${bottomCellToolbarHeight}px }`);
styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .cell-list-top-cell-toolbar-container { height: ${bottomCellToolbarHeight}px }`);
// left and right border margins
styleSheets.push(`
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-left:before,
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-right:before,
.monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-left:before,
.monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-right:before {
top: -${cellTopMargin}px; height: calc(100% + ${cellTopMargin + cellBottomMargin}px)
}`);
this._styleElement.textContent = styleSheets.join('\n');
}
private _createCellList(): void {
this._body.classList.add('cell-list-container');
@@ -1076,14 +1120,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
private async _createWebview(id: string, resource: URI): Promise<void> {
this._webview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, {
outputNodePadding: CELL_OUTPUT_PADDING,
outputNodeLeftPadding: CELL_OUTPUT_PADDING,
previewNodePadding: MARKDOWN_PREVIEW_PADDING,
leftMargin: CODE_CELL_LEFT_MARGIN,
rightMargin: CELL_RIGHT_MARGIN,
runGutter: CELL_RUN_GUTTER,
});
this._webview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, this._notebookOptions.computeWebviewOptions());
this._webview.element.style.width = '100%';
// attach the webview container to the DOM tree first
@@ -1093,9 +1130,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
private async _attachModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined) {
await this._createWebview(this.getId(), textModel.uri);
this._eventDispatcher = new NotebookEventDispatcher();
this.viewModel = this.instantiationService.createInstance(NotebookViewModel, textModel.viewType, textModel, this._eventDispatcher, this.getLayoutInfo());
this._eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);
this._viewContext = new ViewContext(this._notebookOptions, new NotebookEventDispatcher());
this.viewModel = this.instantiationService.createInstance(NotebookViewModel, textModel.viewType, textModel, this._viewContext, this.getLayoutInfo());
this._viewContext.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);
this._updateForOptions();
this._updateForNotebookConfiguration();
@@ -1449,7 +1486,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this._webviewTransparentCover.style.width = `${dimension.width}px`;
}
this._eventDispatcher?.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);
this._viewContext?.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);
}
//#endregion
@@ -2385,9 +2422,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
updateMarkdownCellHeight(cellId: string, height: number, isInit: boolean) {
const cell = this.getCellById(cellId);
const layoutConfiguration = this._notebookOptions.getLayoutConfiguration();
if (cell && cell instanceof MarkdownCellViewModel) {
if (height + BOTTOM_CELL_TOOLBAR_GAP !== cell.layoutInfo.totalHeight) {
this._debug('updateMarkdownCellHeight', cell.handle, height + BOTTOM_CELL_TOOLBAR_GAP, isInit);
if (height + layoutConfiguration.bottomCellToolbarGap !== cell.layoutInfo.totalHeight) {
this._debug('updateMarkdownCellHeight', cell.handle, height + layoutConfiguration.bottomCellToolbarGap, isInit);
cell.renderedMarkdownHeight = height;
}
}
@@ -2461,7 +2499,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this._webviewTransparentCover = null;
this._dndController = null;
this._listTopCellToolbar = null;
this._eventDispatcher = undefined;
this._viewContext = undefined;
this._notebookViewModel = undefined;
this._cellContextKeyManager = null;
this._renderedEditors.clear();
@@ -2653,17 +2691,20 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.notebookOverlay .output-show-more-container { background-color: ${containerBackground}; }`);
}
const notebookBackground = theme.getColor(editorBackground);
if (notebookBackground) {
collector.addRule(`.notebookOverlay .cell-drag-image .cell-editor-container > div { background: ${notebookBackground} !important; }`);
collector.addRule(`.notebookOverlay .monaco-list-row .cell-title-toolbar { background-color: ${notebookBackground}; }`);
collector.addRule(`.notebookOverlay .monaco-list-row.cell-drag-image { background-color: ${notebookBackground}; }`);
collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item { background-color: ${notebookBackground} }`);
collector.addRule(`.notebookOverlay .cell-list-top-cell-toolbar-container .action-item { background-color: ${notebookBackground} }`);
}
const editorBackgroundColor = theme.getColor(cellEditorBackground) ?? theme.getColor(editorBackground);
if (editorBackgroundColor) {
collector.addRule(`.notebookOverlay .cell .monaco-editor-background,
.notebookOverlay .cell .margin-view-overlays,
.notebookOverlay .cell .cell-statusbar-container { background: ${editorBackgroundColor}; }`);
collector.addRule(`.notebookOverlay .cell-drag-image .cell-editor-container > div { background: ${editorBackgroundColor} !important; }`);
collector.addRule(`.notebookOverlay .monaco-list-row .cell-title-toolbar { background-color: ${editorBackgroundColor}; }`);
collector.addRule(`.notebookOverlay .monaco-list-row.cell-drag-image { background-color: ${editorBackgroundColor}; }`);
collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item { background-color: ${editorBackgroundColor} }`);
collector.addRule(`.notebookOverlay .cell-list-top-cell-toolbar-container .action-item { background-color: ${editorBackgroundColor} }`);
.notebookOverlay .cell .margin-view-overlays,
.notebookOverlay .cell .cell-statusbar-container { background: ${editorBackgroundColor}; }`);
}
const cellToolbarSeperator = theme.getColor(CELL_TOOLBAR_SEPERATOR);
@@ -2822,43 +2863,5 @@ registerThemingParticipant((theme, collector) => {
}`);
}
// Cell Margin
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row div.cell.code { margin-left: ${CODE_CELL_LEFT_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .code-cell-row div.cell.code { margin-left: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell { margin-right: ${CELL_RIGHT_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .cell-inner-container { padding-top: ${CELL_TOP_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .cell-inner-container { padding-bottom: ${MARKDOWN_CELL_BOTTOM_MARGIN}px; padding-top: ${MARKDOWN_CELL_TOP_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .cell-inner-container.webview-backed-markdown-cell { padding: 0; }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .webview-backed-markdown-cell.markdown-cell-edit-mode .cell.code { padding-bottom: ${MARKDOWN_CELL_BOTTOM_MARGIN}px; padding-top: ${MARKDOWN_CELL_TOP_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .output { margin: 0px ${CELL_RIGHT_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`);
collector.addRule(`.notebookOverlay .output { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + CELL_RIGHT_MARGIN}px); }`);
collector.addRule(`.notebookOverlay .output-show-more-container { margin: 0px ${CELL_RIGHT_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`);
collector.addRule(`.notebookOverlay .output-show-more-container { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + CELL_RIGHT_MARGIN}px); }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.markdown { padding-left: ${CELL_RUN_GUTTER}px; }`);
collector.addRule(`.notebookOverlay .cell .run-button-container { width: 20px; left: ${CODE_CELL_LEFT_MARGIN + Math.floor(CELL_RUN_GUTTER - 20) / 2}px }`);
collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row :not(.webview-backed-markdown-cell) .cell-focus-indicator-top { height: ${CELL_TOP_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-side { bottom: ${BOTTOM_CELL_TOOLBAR_GAP}px; }`);
collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row.code-cell-row .cell-focus-indicator-left,
.notebookOverlay .monaco-list .monaco-list-row.code-cell-row .cell-drag-handle { width: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`);
collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .cell-focus-indicator-left { width: ${CODE_CELL_LEFT_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator.cell-focus-indicator-right { width: ${CELL_RIGHT_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom { height: ${CELL_BOTTOM_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-shadow-container-bottom { top: ${CELL_BOTTOM_MARGIN}px; }`);
collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-collapsed-part { margin-left: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; height: ${COLLAPSED_INDICATOR_HEIGHT}px; }`);
collector.addRule(`.notebookOverlay .cell-list-top-cell-toolbar-container { top: -${SCROLLABLE_ELEMENT_PADDING_TOP}px }`);
collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { height: ${BOTTOM_CELL_TOOLBAR_HEIGHT}px }`);
collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .cell-list-top-cell-toolbar-container { height: ${BOTTOM_CELL_TOOLBAR_HEIGHT}px }`);
// left and right border margins
collector.addRule(`
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-left:before,
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-right:before,
.monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-left:before,
.monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-right:before {
top: -${CELL_TOP_MARGIN}px; height: calc(100% + ${CELL_TOP_MARGIN + CELL_BOTTOM_MARGIN}px)
}`);
});
@@ -0,0 +1,100 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/notebookKernelActionViewItem';
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { Action, IAction } from 'vs/base/common/actions';
import { localize } from 'vs/nls';
import { registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor';
import { selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
import { INotebookKernelMatchResult, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
import { toolbarHoverBackground } from 'vs/platform/theme/common/colorRegistry';
registerThemingParticipant((theme, collector) => {
const value = theme.getColor(toolbarHoverBackground);
collector.addRule(`:root {
--code-toolbarHoverBackground: ${value};
}`);
});
export class NotebooKernelActionViewItem extends ActionViewItem {
private _kernelLabel?: HTMLAnchorElement;
constructor(
actualAction: IAction,
private readonly _editor: NotebookEditor,
@INotebookKernelService private readonly _notebookKernelService: INotebookKernelService,
) {
super(
undefined,
new Action('fakeAction', undefined, ThemeIcon.asClassName(selectKernelIcon), true, (event) => actualAction.run(event)),
{ label: false, icon: true }
);
this._register(_editor.onDidChangeModel(this._update, this));
this._register(_notebookKernelService.onDidChangeNotebookAffinity(this._update, this));
this._register(_notebookKernelService.onDidChangeNotebookKernelBinding(this._update, this));
}
override render(container: HTMLElement): void {
this._update();
super.render(container);
container.classList.add('kernel-action-view-item');
this._kernelLabel = document.createElement('a');
container.appendChild(this._kernelLabel);
this.updateLabel();
}
override updateLabel() {
if (this._kernelLabel) {
this._kernelLabel.classList.add('kernel-label');
this._kernelLabel.innerText = this._action.label;
this._kernelLabel.title = this._action.tooltip;
}
}
private _update(): void {
const widget = this._editor.getControl();
if (!widget || !widget.hasModel()) {
this._resetAction();
return;
}
const notebook = widget.viewModel.notebookDocument;
const info = this._notebookKernelService.getMatchingKernel(notebook);
this._updateActionFromKernelInfo(info);
}
private _updateActionFromKernelInfo(info: INotebookKernelMatchResult): void {
if (info.all.length === 0) {
// should not happen - means "bad" context keys
this._resetAction();
return;
}
this._action.enabled = true;
const selectedOrSuggested = info.selected ?? info.suggested;
if (selectedOrSuggested) {
// selected or suggested kernel
this._action.label = selectedOrSuggested.label;
this._action.tooltip = selectedOrSuggested.description ?? selectedOrSuggested.detail ?? '';
if (!info.selected) {
// special UI for selected kernel?
}
} else {
// many kernels
this._action.label = localize('select', "Select Kernel");
this._action.tooltip = '';
}
}
private _resetAction(): void {
this._action.enabled = false;
this._action.label = '';
this._action.class = '';
}
}
@@ -27,14 +27,14 @@ import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.pr
import { IEditorInput } from 'vs/workbench/common/editor';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import { Memento } from 'vs/workbench/common/memento';
import { INotebookEditorContribution, notebookMarkupRendererExtensionPoint, notebooksExtensionPoint, notebookRendererExtensionPoint, notebooksExtensionPoint2 } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
import { NotebookEditorOptions, updateEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { INotebookEditorContribution, notebooksExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
import { NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditorInput';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellUri, DisplayOrderKey, INotebookExclusiveDocumentFilter, INotebookMarkupRendererInfo, INotebookContributionData, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, NotebookDataDto, NotebookEditorPriority, NotebookRendererMatch, NotebookTextDiffEditorPreview, RENDERER_NOT_AVAILABLE, sortMimeTypes, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellUri, DisplayOrderKey, INotebookExclusiveDocumentFilter, INotebookContributionData, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, NotebookDataDto, NotebookEditorPriority, NotebookRendererMatch, NotebookTextDiffEditorPreview, RENDERER_NOT_AVAILABLE, sortMimeTypes, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput';
import { NotebookMarkupRendererInfo as NotebookMarkupRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookMarkdownRenderer';
import { updateEditorTopPadding } from 'vs/workbench/contrib/notebook/common/notebookOptions';
import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer';
import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
import { ComplexNotebookProviderInfo, INotebookContentProvider, INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService';
@@ -81,7 +81,6 @@ export class NotebookProviderInfoStore extends Disposable {
}));
notebooksExtensionPoint.setHandler(extensions => this._setupHandler(extensions));
notebooksExtensionPoint2.setHandler(extensions => this._setupHandler(extensions));
}
override dispose(): void {
@@ -250,6 +249,10 @@ export class NotebookOutputRendererInfoStore {
return this.contributedRenderers.get(rendererId);
}
getAll(): NotebookOutputRendererInfo[] {
return Array.from(this.contributedRenderers.values());
}
add(info: NotebookOutputRendererInfo): void {
if (this.contributedRenderers.has(info.id)) {
return;
@@ -302,7 +305,6 @@ export class NotebookService extends Disposable implements INotebookService {
private readonly _notebookProviders = new Map<string, ComplexNotebookProviderInfo | SimpleNotebookProviderInfo>();
private readonly _notebookProviderInfoStore: NotebookProviderInfoStore;
private readonly _notebookRenderersInfoStore = this._instantiationService.createInstance(NotebookOutputRendererInfoStore);
private readonly _markdownRenderersInfos = new Set<INotebookMarkupRendererInfo>();
private readonly _models = new ResourceMap<ModelData>();
private readonly _onWillAddNotebookDocument = this._register(new Emitter<NotebookTextModel>());
@@ -369,38 +371,6 @@ export class NotebookService extends Disposable implements INotebookService {
}
}
});
notebookMarkupRendererExtensionPoint.setHandler((renderers) => {
this._markdownRenderersInfos.clear();
for (const extension of renderers) {
if (!extension.description.enableProposedApi && !extension.description.isBuiltin) {
// Only allow proposed extensions to use this extension point
return;
}
for (const notebookContribution of extension.value) {
if (!notebookContribution.entrypoint) { // avoid crashing
console.error(`Cannot register renderer for ${extension.description.identifier.value} since it did not have an entrypoint. This is now required: https://github.com/microsoft/vscode/issues/102644`);
continue;
}
const id = notebookContribution.id;
if (!id) {
console.error(`Notebook renderer from ${extension.description.identifier.value} is missing an 'id'`);
continue;
}
this._markdownRenderersInfos.add(new NotebookMarkupRendererInfo({
id,
extension: extension.description,
entrypoint: notebookContribution.entrypoint,
displayName: notebookContribution.displayName,
mimeTypes: notebookContribution.mimeTypes,
dependsOn: notebookContribution.dependsOn,
}));
}
}
});
const updateOrder = () => {
const userOrder = this._configurationService.getValue<string[]>(DisplayOrderKey);
@@ -552,8 +522,8 @@ export class NotebookService extends Disposable implements INotebookService {
this._notebookRenderersInfoStore.setPreferred(mimeType, rendererId);
}
getMarkupRendererInfo(): INotebookMarkupRendererInfo[] {
return Array.from(this._markdownRenderersInfos);
getRenderers(): INotebookRendererInfo[] {
return this._notebookRenderersInfoStore.getAll();
}
// --- notebook documents: create, destory, retrieve, enumerate
@@ -9,36 +9,35 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { ICellOutputViewModel, ICommonNotebookEditor, IOutputTransformContribution, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { URI } from 'vs/base/common/uri';
import { Disposable } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls';
export class OutputRenderer extends Disposable {
protected _contributions: { [key: string]: IOutputTransformContribution; };
protected _renderers: IOutputTransformContribution[];
private _richMimeTypeRenderers = new Map<string, IOutputTransformContribution>();
private readonly _richMimeTypeRenderers = new Map<string, IOutputTransformContribution>();
constructor(
notebookEditor: ICommonNotebookEditor,
private readonly instantiationService: IInstantiationService
) {
super();
this._contributions = {};
this._renderers = [];
const contributions = NotebookRegistry.getOutputTransformContributions();
for (const desc of contributions) {
for (const desc of NotebookRegistry.getOutputTransformContributions()) {
try {
const contribution = this.instantiationService.createInstance(desc.ctor, notebookEditor);
this._contributions[desc.id] = contribution;
contribution.getMimetypes().forEach(mimetype => {
this._richMimeTypeRenderers.set(mimetype, contribution);
});
this._register(contribution);
} catch (err) {
onUnexpectedError(err);
}
}
}
override dispose(): void {
super.dispose();
this._richMimeTypeRenderers.clear();
}
getContribution(preferredMimeType: string | undefined): IOutputTransformContribution | undefined {
if (preferredMimeType) {
return this._richMimeTypeRenderers.get(preferredMimeType);
@@ -47,17 +46,16 @@ export class OutputRenderer extends Disposable {
return undefined;
}
renderNoop(viewModel: ICellOutputViewModel, container: HTMLElement): IRenderOutput {
private _renderNoop(viewModel: ICellOutputViewModel, container: HTMLElement): IRenderOutput {
const contentNode = document.createElement('p');
contentNode.innerText = `No renderer could be found for output.`;
contentNode.innerText = localize('empty', "No renderer could be found for output.");
container.appendChild(contentNode);
return { type: RenderOutputType.Mainframe };
}
render(viewModel: ICellOutputViewModel, container: HTMLElement, preferredMimeType: string | undefined, notebookUri: URI): IRenderOutput {
if (!viewModel.model.outputs.length) {
return this.renderNoop(viewModel, container);
return this._renderNoop(viewModel, container);
}
if (!preferredMimeType || !this._richMimeTypeRenderers.has(preferredMimeType)) {
@@ -67,9 +65,9 @@ export class OutputRenderer extends Disposable {
const mimeTypesMessage = mimeTypes.join(', ');
if (preferredMimeType) {
contentNode.innerText = `No renderer could be found for MIME type: ${preferredMimeType}`;
contentNode.innerText = localize('noRenderer.1', "No renderer could be found for MIME type: {0}", preferredMimeType);
} else {
contentNode.innerText = `No renderer could be found for output. It has the following MIME types: ${mimeTypesMessage}`;
contentNode.innerText = localize('noRenderer.2', "No renderer could be found for output. It has the following MIME types: {0}", mimeTypesMessage);
}
container.appendChild(contentNode);
@@ -82,13 +80,7 @@ export class OutputRenderer extends Disposable {
if (items.length && renderer) {
return renderer.render(viewModel, items, container, notebookUri);
} else {
return this.renderNoop(viewModel, container);
return this._renderNoop(viewModel, container);
}
}
override dispose() {
this._contributions = {};
this._renderers = [];
this._richMimeTypeRenderers.clear();
}
}
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as DOM from 'vs/base/browser/dom';
import { Disposable } from 'vs/base/common/lifecycle';
import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
import { dirname } from 'vs/base/common/resources';
import { isArray } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
@@ -14,6 +14,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { handleANSIOutput } from 'vs/workbench/contrib/debug/browser/debugANSIHandling';
@@ -120,6 +121,8 @@ class CodeRendererContrib extends Disposable implements IOutputRendererContribut
return ['text/x-javascript'];
}
private readonly _cellDisposables = new Map<number, DisposableStore>();
constructor(
public notebookEditor: ICommonNotebookEditor,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@@ -129,7 +132,19 @@ class CodeRendererContrib extends Disposable implements IOutputRendererContribut
super();
}
override dispose(): void {
dispose(this._cellDisposables.values());
this._cellDisposables.clear();
super.dispose();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI): IRenderOutput {
let cellDisposables = this._cellDisposables.get(output.cellViewModel.handle);
cellDisposables?.dispose();
cellDisposables = new DisposableStore();
this._cellDisposables.set(output.cellViewModel.handle, cellDisposables);
const str = items.map(item => getStringValue(item.value)).join('');
const editor = this.instantiationService.createInstance(CodeEditorWidget, container, {
...getOutputSimpleEditorOptions(),
@@ -155,6 +170,9 @@ class CodeRendererContrib extends Disposable implements IOutputRendererContribut
width
});
cellDisposables.add(editor);
cellDisposables.add(textModel);
container.style.height = `${height + 8}px`;
return { type: RenderOutputType.Mainframe };
@@ -260,6 +278,64 @@ class ErrorRendererContrib extends Disposable implements IOutputRendererContribu
}
}
class JSErrorRendererContrib implements IOutputRendererContribution {
constructor(
public notebookEditor: ICommonNotebookEditor,
@IThemeService private readonly _themeService: IThemeService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ILogService private readonly _logService: ILogService,
) { }
dispose(): void {
// nothing
}
getType() {
return RenderOutputType.Mainframe;
}
getMimetypes() {
return ['application/x.notebook.error'];
}
render(_output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, _notebookUri: URI): IRenderOutput {
const linkDetector = this._instantiationService.createInstance(LinkDetector);
for (let item of items) {
if (typeof item.value !== 'string') {
this._logService.warn('INVALID output item (not a string)', item.value);
continue;
}
let err: Error;
try {
err = <Error>JSON.parse(item.value);
} catch (e) {
this._logService.warn('INVALID output item (failed to parse)', e);
continue;
}
const header = document.createElement('div');
const headerMessage = err.name && err.message ? `${err.name}: ${err.message}` : err.name || err.message;
if (headerMessage) {
header.innerText = headerMessage;
container.appendChild(header);
}
const stack = document.createElement('pre');
stack.classList.add('traceback');
if (err.stack) {
stack.appendChild(handleANSIOutput(err.stack, linkDetector, this._themeService, undefined));
}
container.appendChild(stack);
container.classList.add('error');
}
return { type: RenderOutputType.Mainframe };
}
}
class PlainTextRendererContrib extends Disposable implements IOutputRendererContribution {
getType() {
return RenderOutputType.Mainframe;
@@ -442,6 +518,7 @@ NotebookRegistry.registerOutputTransform('jpeg', JPEGRendererContrib);
NotebookRegistry.registerOutputTransform('plain', PlainTextRendererContrib);
NotebookRegistry.registerOutputTransform('code', CodeRendererContrib);
NotebookRegistry.registerOutputTransform('error-trace', ErrorRendererContrib);
NotebookRegistry.registerOutputTransform('jserror', JSErrorRendererContrib);
NotebookRegistry.registerOutputTransform('stream-text', StreamRendererContrib);
NotebookRegistry.registerOutputTransform('stderr', StderrRendererContrib);
@@ -29,7 +29,7 @@ import { CellEditState, ICellOutputViewModel, ICommonCellInfo, ICommonNotebookEd
import { preloadsScriptStr, WebviewPreloadRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads';
import { transformWebviewThemeVars } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewThemeMapping';
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
import { INotebookKernel, INotebookRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookKernel, INotebookRendererInfo, NotebookRendererMatch } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { IWebviewService, WebviewContentPurpose, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@@ -713,41 +713,41 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
}
private getMarkdownRenderer(): WebviewPreloadRenderer[] {
const allRenderers = this.notebookService.getMarkupRendererInfo();
const markdownMimeType = 'text/markdown';
const allRenderers = this.notebookService.getRenderers()
.filter(renderer => renderer.matchesWithoutKernel(markdownMimeType) !== NotebookRendererMatch.Never);
const topLevelMarkdownRenderers = allRenderers
.filter(renderer => !renderer.dependsOn)
.filter(renderer => renderer.mimeTypes?.includes(markdownMimeType));
.filter(renderer => renderer.dependencies.length === 0);
const subRenderers = new Map<string, Array<{ entrypoint: string }>>();
for (const renderer of allRenderers) {
if (renderer.dependsOn) {
if (!subRenderers.has(renderer.dependsOn)) {
subRenderers.set(renderer.dependsOn, []);
for (const dep of renderer.dependencies) {
if (!subRenderers.has(dep)) {
subRenderers.set(dep, []);
}
const entryPoint = this.asWebviewUri(renderer.entrypoint);
subRenderers.get(renderer.dependsOn)!.push({ entrypoint: entryPoint.toString(true) });
const entryPoint = this.asWebviewUri(renderer.entrypoint, renderer.extensionLocation);
subRenderers.get(dep)!.push({ entrypoint: entryPoint.toString(true) });
}
}
return topLevelMarkdownRenderers.map((renderer): WebviewPreloadRenderer => {
const src = this.asWebviewUri(renderer.entrypoint);
const src = this.asWebviewUri(renderer.entrypoint, renderer.extensionLocation);
return {
entrypoint: src.toString(),
mimeTypes: [markdownMimeType],
mimeTypes: renderer.mimeTypes,
dependencies: subRenderers.get(renderer.id) || [],
};
});
}
private asWebviewUri(uri: URI) {
private asWebviewUri(uri: URI, fromExtension: URI | undefined) {
const remoteAuthority = fromExtension?.scheme === Schemas.vscodeRemote ? fromExtension.authority : undefined;
return asWebviewUri({
isExtensionDevelopmentDebug: this.environmentService.isExtensionDevelopment,
webviewCspSource: this.environmentService.webviewCspSource,
webviewResourceRoot: this.environmentService.webviewResourceRoot,
remote: { authority: undefined } // TODO
remote: { authority: remoteAuthority }
}, this.id, uri);
}
@@ -781,11 +781,11 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
resolveFunc = resolve;
});
const baseUrl = this.asWebviewUri(dirname(this.documentUri));
const baseUrl = this.asWebviewUri(dirname(this.documentUri), undefined);
if (!isWeb) {
const loaderUri = FileAccess.asFileUri('vs/loader.js', require);
const loader = this.asWebviewUri(loaderUri);
const loader = this.asWebviewUri(loaderUri, undefined);
coreDependencies = `<script src="${loader}"></script><script>
var requirejs = (function() {
@@ -1131,7 +1131,7 @@ var requirejs = (function() {
this.localResourceRootsCache = [
...this.notebookService.getNotebookProviderResourceRoots(),
...this.notebookService.getMarkupRendererInfo().map(x => dirname(x.entrypoint)),
...this.notebookService.getRenderers().map(x => dirname(x.entrypoint)),
...workspaceFolders,
rootPath,
];
@@ -1145,7 +1145,6 @@ var requirejs = (function() {
allowMultipleAPIAcquire: true,
allowScripts: true,
localResourceRoots: this.localResourceRootsCache,
useRootAuthority: true
}, undefined);
webview.html = content;
@@ -1572,7 +1571,7 @@ var requirejs = (function() {
const resources: IPreloadResource[] = [];
for (const preload of kernel.preloadUris) {
const uri = this.environmentService.isExtensionDevelopment && (preload.scheme === 'http' || preload.scheme === 'https')
? preload : this.asWebviewUri(preload);
? preload : this.asWebviewUri(preload, undefined);
if (!this._preloadsCache.has(uri.toString())) {
resources.push({ uri: uri.toString(), originalUri: preload.toString(), source: 'kernel' });
@@ -1598,7 +1597,7 @@ var requirejs = (function() {
for (const rendererInfo of renderers) {
extensionLocations.push(rendererInfo.extensionLocation);
for (const preload of [rendererInfo.entrypoint, ...rendererInfo.preloads]) {
const uri = this.asWebviewUri(preload);
const uri = this.asWebviewUri(preload, rendererInfo.extensionLocation);
const resource: IPreloadResource = {
uri: uri.toString(),
originalUri: preload.toString(),
@@ -9,7 +9,6 @@ import { domEvent } from 'vs/base/browser/event';
import { Delayer } from 'vs/base/common/async';
import { Disposable } from 'vs/base/common/lifecycle';
import * as platform from 'vs/base/common/platform';
import { BOTTOM_CELL_TOOLBAR_GAP } from 'vs/workbench/contrib/notebook/browser/constants';
import { BaseCellRenderTemplate, expandCellRangesWithHiddenCells, ICellViewModel, INotebookCellList, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { cloneNotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellEditType, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
@@ -157,7 +156,8 @@ export class CellDragAndDropController extends Disposable {
}
private updateInsertIndicator(dropDirection: string, insertionIndicatorAbsolutePos: number) {
const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_GAP / 2;
const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration();
const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + layoutInfo.bottomCellToolbarGap / 2;
if (insertionIndicatorTop >= 0) {
this.listInsertionIndicator.style.top = `${insertionIndicatorTop}px`;
this.setInsertIndicatorVisibility(true);
@@ -199,7 +199,8 @@ export class CellDragAndDropController extends Disposable {
const cellTop = this.list.getAbsoluteTopOfElement(draggedOverCell);
const cellHeight = this.list.elementHeight(draggedOverCell);
const insertionIndicatorAbsolutePos = dropDirection === 'above' ? cellTop : cellTop + cellHeight;
const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_GAP / 2;
const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration();
const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + layoutInfo.bottomCellToolbarGap / 2;
const editorHeight = this.notebookEditor.getDomNode().getBoundingClientRect().height;
if (insertionIndicatorTop < 0 || insertionIndicatorTop > editorHeight) {
// Ignore drop, insertion point is off-screen
@@ -10,15 +10,14 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { IEditorOptions, LineNumbersType } from 'vs/editor/common/config/editorOptions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { EDITOR_BOTTOM_PADDING, EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR } from 'vs/workbench/contrib/notebook/browser/constants';
import { EditorTopPaddingChangeEvent, getEditorTopPadding, getNotebookEditorFromEditorPane, ICellViewModel, NOTEBOOK_CELL_LINE_NUMBERS, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { getNotebookEditorFromEditorPane, ICellViewModel, NOTEBOOK_CELL_LINE_NUMBERS, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { localize } from 'vs/nls';
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
export class CellEditorOptions extends Disposable {
@@ -48,27 +47,24 @@ export class CellEditorOptions extends Disposable {
private readonly _onDidChange = new Emitter<IEditorOptions>();
readonly onDidChange: Event<IEditorOptions> = this._onDidChange.event;
constructor(readonly configurationService: IConfigurationService, language: string) {
constructor(readonly notebookOptions: NotebookOptions, readonly configurationService: IConfigurationService, language: string) {
super();
this._register(configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('editor') || e.affectsConfiguration('notebook') || e.affectsConfiguration(ShowCellStatusBarKey)) {
if (e.affectsConfiguration('editor') || e.affectsConfiguration('notebook')) {
this._value = computeEditorOptions();
this._onDidChange.fire(this.value);
}
}));
this._register(EditorTopPaddingChangeEvent(() => {
this._value = computeEditorOptions();
this._onDidChange.fire(this.value);
this._register(notebookOptions.onDidChangeOptions(e => {
if (e.cellStatusBarVisibility || e.editorTopPadding) {
this._value = computeEditorOptions();
this._onDidChange.fire(this.value);
}
}));
const computeEditorOptions = () => {
const showCellStatusBar = configurationService.getValue<boolean>(ShowCellStatusBarKey);
const editorPadding = {
top: getEditorTopPadding(),
bottom: showCellStatusBar ? EDITOR_BOTTOM_PADDING : EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR
};
const editorPadding = this.notebookOptions.computeEditorPadding();
const renderLiNumbers = configurationService.getValue<'on' | 'off'>('notebook.lineNumbers') === 'on';
const lineNumbers: LineNumbersType = renderLiNumbers ? 'on' : 'off';
const editorOptions = deepClone(configurationService.getValue<IEditorOptions>('editor', { overrideIdentifier: language }));
@@ -13,7 +13,7 @@ import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/commo
import { CodeCellRenderTemplate, ICellOutputViewModel, IInsetRenderOutput, INotebookEditor, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { BUILTIN_RENDERER_ID, CellUri, NotebookCellOutputsSplice, IOrderedMimeType, mimeTypeIsAlwaysSecure, INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { BUILTIN_RENDERER_ID, CellUri, NotebookCellOutputsSplice, IOrderedMimeType, INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
@@ -444,18 +444,7 @@ export class CellOutputContainer extends Disposable {
}
private _calcuateOutputsToRender(): ICellOutputViewModel[] {
const outputs = this.viewCell.outputsViewModels.slice(0, Math.min(OUTPUT_COUNT_LIMIT, this.viewCell.outputsViewModels.length));
if (!this.notebookEditor.viewModel!.metadata.trusted) {
// not trusted
const secureOutput = outputs.filter(output => {
const mimeTypes = output.model.outputs.map(op => op.mime);
return mimeTypes.some(mimeTypeIsAlwaysSecure);
});
return secureOutput;
}
return outputs;
return this.viewCell.outputsViewModels.slice(0, Math.min(OUTPUT_COUNT_LIMIT, this.viewCell.outputsViewModels.length));
}
private _outputHeightTimer: any = null;
@@ -35,7 +35,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { BOTTOM_CELL_TOOLBAR_GAP, CELL_BOTTOM_MARGIN, CELL_TOP_MARGIN, EDITOR_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants';
import { DeleteCellAction, INotebookActionContext, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
import { BaseCellRenderTemplate, CellEditState, CodeCellLayoutInfo, CodeCellRenderTemplate, EXPAND_CELL_INPUT_COMMAND_ID, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys';
@@ -100,7 +99,7 @@ abstract class AbstractCellRenderer {
language: string,
protected dndController: CellDragAndDropController | undefined,
) {
this.editorOptions = new CellEditorOptions(configurationService, language);
this.editorOptions = new CellEditorOptions(notebookEditor.notebookOptions, configurationService, language);
this.cellMenus = this.instantiationService.createInstance(CellMenus);
}
@@ -184,17 +183,18 @@ abstract class AbstractCellRenderer {
this.notebookEditor.focus();
}
const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration();
if (actions.primary.length || actions.secondary.length) {
templateData.container.classList.add('cell-has-toolbar-actions');
if (isCodeCellRenderTemplate(templateData)) {
templateData.focusIndicatorLeft.style.top = `${EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN}px`;
templateData.focusIndicatorRight.style.top = `${EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN}px`;
templateData.focusIndicatorLeft.style.top = `${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px`;
templateData.focusIndicatorRight.style.top = `${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px`;
}
} else {
templateData.container.classList.remove('cell-has-toolbar-actions');
if (isCodeCellRenderTemplate(templateData)) {
templateData.focusIndicatorLeft.style.top = `${CELL_TOP_MARGIN}px`;
templateData.focusIndicatorRight.style.top = `${CELL_TOP_MARGIN}px`;
templateData.focusIndicatorLeft.style.top = `${layoutInfo.cellTopMargin}px`;
templateData.focusIndicatorRight.style.top = `${layoutInfo.cellTopMargin}px`;
}
}
};
@@ -467,7 +467,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
}));
this.updateForHover(element, templateData);
const cellEditorOptions = new CellEditorOptions(this.configurationService, 'markdown');
const cellEditorOptions = new CellEditorOptions(this.notebookEditor.notebookOptions, this.configurationService, 'markdown');
cellEditorOptions.setLineNumbers(element.lineNumbers);
elementDisposables.add(cellEditorOptions);
@@ -508,11 +508,10 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
}
private updateForLayout(element: MarkdownCellViewModel, templateData: MarkdownCellRenderTemplate): void {
templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP - CELL_BOTTOM_MARGIN}px`;
const focusSideHeight = element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP;
templateData.focusIndicatorLeft.style.height = `${focusSideHeight}px`;
templateData.focusIndicatorRight.style.height = `${focusSideHeight}px`;
const indicatorPostion = this.notebookEditor.notebookOptions.computeIndicatorPosition(element.layoutInfo.totalHeight);
templateData.focusIndicatorBottom.style.top = `${indicatorPostion.bottomIndicatorTop}px`;
templateData.focusIndicatorLeft.style.height = `${indicatorPostion.verticalIndicatorHeight}px`;
templateData.focusIndicatorRight.style.height = `${indicatorPostion.verticalIndicatorHeight}px`;
}
private updateForHover(element: MarkdownCellViewModel, templateData: MarkdownCellRenderTemplate): void {
@@ -879,12 +878,13 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
}
private updateForLayout(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void {
const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration();
templateData.focusIndicatorLeft.style.height = `${element.layoutInfo.indicatorHeight}px`;
templateData.focusIndicatorRight.style.height = `${element.layoutInfo.indicatorHeight}px`;
templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP - CELL_BOTTOM_MARGIN}px`;
templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - layoutInfo.bottomCellToolbarGap - layoutInfo.cellBottomMargin}px`;
templateData.outputContainer.style.top = `${element.layoutInfo.outputContainerOffset}px`;
templateData.outputShowMoreContainer.style.top = `${element.layoutInfo.outputShowMoreContainerOffset}px`;
templateData.dragHandle.style.height = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP}px`;
templateData.dragHandle.style.height = `${element.layoutInfo.totalHeight - layoutInfo.bottomCellToolbarGap}px`;
}
renderElement(element: CodeCellViewModel, index: number, templateData: CodeCellRenderTemplate, height: number | undefined): void {
@@ -938,7 +938,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
elementDisposables.add(this.instantiationService.createInstance(CodeCell, this.notebookEditor, element, templateData));
this.renderedEditors.set(element, templateData.editor);
const cellEditorOptions = new CellEditorOptions(this.configurationService, element.language);
const cellEditorOptions = new CellEditorOptions(this.notebookEditor.notebookOptions, this.configurationService, element.language);
elementDisposables.add(cellEditorOptions);
elementDisposables.add(cellEditorOptions.onDidChange(newValue => templateData.editor.updateOptions(newValue)));
templateData.editor.updateOptions(cellEditorOptions.value);
@@ -10,8 +10,7 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IDimension } from 'vs/editor/common/editorCommon';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellFocusMode, CodeCellRenderTemplate, getEditorTopPadding, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellFocusMode, CodeCellRenderTemplate, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellOutputContainer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellOutput';
import { ClickTargetType } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
@@ -40,8 +39,10 @@ export class CodeCell extends Disposable {
const width = this.viewCell.layoutInfo.editorWidth;
const lineNum = this.viewCell.lineCount;
const lineHeight = this.viewCell.layoutInfo.fontInfo?.lineHeight || 17;
const editorPadding = this.notebookEditor.notebookOptions.computeEditorPadding();
const editorHeight = this.viewCell.layoutInfo.editorHeight === 0
? lineNum * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING
? lineNum * lineHeight + editorPadding.top + editorPadding.bottom
: this.viewCell.layoutInfo.editorHeight;
this.layoutEditor(
@@ -10,8 +10,7 @@ import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposab
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFocusMode, MarkdownCellRenderTemplate, ICellViewModel, getEditorTopPadding, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellEditState, CellFocusMode, MarkdownCellRenderTemplate, ICellViewModel, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellFoldingState } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel';
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -295,7 +294,8 @@ export class StatefulMarkdownCell extends Disposable {
const width = this.viewCell.layoutInfo.editorWidth;
const lineNum = this.viewCell.lineCount;
const lineHeight = this.viewCell.layoutInfo.fontInfo?.lineHeight || 17;
editorHeight = Math.max(lineNum, 1) * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING;
const editorPadding = this.notebookEditor.notebookOptions.computeEditorPadding();
editorHeight = Math.max(lineNum, 1) * lineHeight + editorPadding.top + editorPadding.bottom;
this.templateData.editorContainer.innerText = '';
@@ -955,7 +955,6 @@ async function webviewPreloads(style: PreloadStyles, rendererData: readonly Webv
return;
}
// TODO: handle namespace
if (typeof content === 'string') {
if (content.trim().length === 0) {
previewContainerNode.classList.add('emptyMarkdownCell');
@@ -12,12 +12,12 @@ import { IPosition } from 'vs/editor/common/core/position';
import * as editorCommon from 'vs/editor/common/editorCommon';
import * as model from 'vs/editor/common/model';
import { SearchParams } from 'vs/editor/common/model/textModelSearch';
import { CELL_STATUSBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFocusMode, CursorAtBoundary, CellViewModelStateChangeEvent, IEditableCellViewModel, INotebookCellDecorationOptions, getEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellKind, INotebookSearchOptions, ShowCellStatusBarKey, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellEditState, CellFocusMode, CursorAtBoundary, CellViewModelStateChangeEvent, IEditableCellViewModel, INotebookCellDecorationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellKind, INotebookSearchOptions, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService';
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
export abstract class BaseCellViewModel extends Disposable {
@@ -129,6 +129,7 @@ export abstract class BaseCellViewModel extends Disposable {
readonly viewType: string,
readonly model: NotebookCellTextModel,
public id: string,
private readonly _viewContext: ViewContext,
private readonly _configurationService: IConfigurationService,
private readonly _modelService: ITextModelService,
) {
@@ -138,11 +139,13 @@ export abstract class BaseCellViewModel extends Disposable {
this._onDidChangeState.fire({ metadataChanged: true, runStateChanged: e.runStateChanged });
}));
this._register(this._configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(ShowCellStatusBarKey)) {
this._register(this._viewContext.notebookOptions.onDidChangeOptions(e => {
if (e.cellStatusBarVisibility) {
this.layoutChange({});
}
}));
this._register(this._configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('notebook.lineNumbers')) {
this.lineNumbers = 'inherit';
}
@@ -150,8 +153,7 @@ export abstract class BaseCellViewModel extends Disposable {
}
getEditorStatusbarHeight() {
const showCellStatusBar = this._configurationService.getValue<boolean>(ShowCellStatusBarKey);
return showCellStatusBar ? CELL_STATUSBAR_HEIGHT : 0;
return this._viewContext.notebookOptions.computeStatusBarHeight();
}
abstract hasDynamicHeight(): boolean;
@@ -394,7 +396,8 @@ export abstract class BaseCellViewModel extends Disposable {
return 0;
}
return this._textEditor.getTopForLineNumber(line) + getEditorTopPadding();
const editorPadding = this._viewContext.notebookOptions.computeEditorPadding();
return this._textEditor.getTopForLineNumber(line) + editorPadding.top;
}
getPositionScrollTopOffset(line: number, column: number): number {
@@ -402,7 +405,8 @@ export abstract class BaseCellViewModel extends Disposable {
return 0;
}
return this._textEditor.getTopForPosition(line, column) + getEditorTopPadding();
const editorPadding = this._viewContext.notebookOptions.computeEditorPadding();
return this._textEditor.getTopForPosition(line, column) + editorPadding.top;
}
cursorAtBoundary(): CursorAtBoundary {
@@ -9,10 +9,9 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_RIGHT_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, CodeCellLayoutState, getEditorTopPadding, ICellOutputViewModel, ICellViewModel, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, CodeCellLayoutState, ICellOutputViewModel, ICellViewModel, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellOutputViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/cellOutputViewModel';
import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellKind, INotebookSearchOptions, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
@@ -96,12 +95,12 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
viewType: string,
model: NotebookCellTextModel,
initialNotebookLayoutInfo: NotebookLayoutInfo | null,
readonly eventDispatcher: NotebookEventDispatcher,
readonly viewContext: ViewContext,
@IConfigurationService configurationService: IConfigurationService,
@INotebookService private readonly _notebookService: INotebookService,
@ITextModelService modelService: ITextModelService,
) {
super(viewType, model, UUID.generateUuid(), configurationService, modelService);
super(viewType, model, UUID.generateUuid(), viewContext, configurationService, modelService);
this._outputViewModels = this.model.outputs.map(output => new CellOutputViewModel(this, output, this._notebookService));
this._register(this.model.onDidChangeOutputs((splices) => {
@@ -132,7 +131,9 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
this._layoutInfo = {
fontInfo: initialNotebookLayoutInfo?.fontInfo || null,
editorHeight: 0,
editorWidth: initialNotebookLayoutInfo ? this.computeEditorWidth(initialNotebookLayoutInfo.width) : 0,
editorWidth: initialNotebookLayoutInfo
? this.viewContext.notebookOptions.computeCodeCellEditorWidth(initialNotebookLayoutInfo.width)
: 0,
outputContainerOffset: 0,
outputTotalHeight: 0,
outputShowMoreContainerHeight: 0,
@@ -144,15 +145,12 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
};
}
private computeEditorWidth(outerWidth: number): number {
return outerWidth - (CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + CELL_RIGHT_MARGIN);
}
layoutChange(state: CodeCellLayoutChangeEvent, source?: string) {
// recompute
this._ensureOutputsTop();
const notebookLayoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration();
const outputShowMoreContainerHeight = state.outputShowMoreContainerHeight ? state.outputShowMoreContainerHeight : this._layoutInfo.outputShowMoreContainerHeight;
let outputTotalHeight = Math.max(this._outputMinHeight, this.metadata?.outputCollapsed ? COLLAPSED_INDICATOR_HEIGHT : this._outputsTop!.getTotalValue());
let outputTotalHeight = Math.max(this._outputMinHeight, this.metadata?.outputCollapsed ? notebookLayoutConfiguration.collapsedIndicatorHeight : this._outputsTop!.getTotalValue());
if (!this.metadata?.inputCollapsed) {
let newState: CodeCellLayoutState;
@@ -176,10 +174,18 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
const statusbarHeight = this.getEditorStatusbarHeight();
const indicatorHeight = editorHeight + statusbarHeight + outputTotalHeight + outputShowMoreContainerHeight;
const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + statusbarHeight;
const outputShowMoreContainerOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2 - outputShowMoreContainerHeight;
const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2;
const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth;
const outputContainerOffset = notebookLayoutConfiguration.editorToolbarHeight
+ notebookLayoutConfiguration.cellTopMargin // CELL_TOP_MARGIN
+ editorHeight
+ statusbarHeight;
const outputShowMoreContainerOffset = totalHeight
- notebookLayoutConfiguration.bottomCellToolbarGap
- notebookLayoutConfiguration.bottomCellToolbarHeight / 2
- outputShowMoreContainerHeight;
const bottomToolbarOffset = this.viewContext.notebookOptions.computeBottomToolbarOffset(totalHeight);
const editorWidth = state.outerWidth !== undefined
? this.viewContext.notebookOptions.computeCodeCellEditorWidth(state.outerWidth)
: this._layoutInfo?.editorWidth;
this._layoutInfo = {
fontInfo: state.font ?? this._layoutInfo.fontInfo ?? null,
@@ -196,12 +202,23 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
};
} else {
outputTotalHeight = Math.max(this._outputMinHeight, this.metadata?.inputCollapsed && this.metadata.outputCollapsed ? 0 : outputTotalHeight);
const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight + outputShowMoreContainerHeight;
const outputContainerOffset = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT;
const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + outputTotalHeight + outputShowMoreContainerHeight;
const outputShowMoreContainerOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2 - outputShowMoreContainerHeight;
const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2;
const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth;
const indicatorHeight = notebookLayoutConfiguration.collapsedIndicatorHeight + outputTotalHeight + outputShowMoreContainerHeight;
const outputContainerOffset = notebookLayoutConfiguration.cellTopMargin + notebookLayoutConfiguration.collapsedIndicatorHeight;
const totalHeight =
notebookLayoutConfiguration.cellTopMargin
+ notebookLayoutConfiguration.collapsedIndicatorHeight
+ notebookLayoutConfiguration.cellBottomMargin //CELL_BOTTOM_MARGIN
+ notebookLayoutConfiguration.bottomCellToolbarGap //BOTTOM_CELL_TOOLBAR_GAP
+ outputTotalHeight + outputShowMoreContainerHeight;
const outputShowMoreContainerOffset = totalHeight
- notebookLayoutConfiguration.bottomCellToolbarGap
- notebookLayoutConfiguration.bottomCellToolbarHeight / 2
- outputShowMoreContainerHeight;
const bottomToolbarOffset = this.viewContext.notebookOptions.computeBottomToolbarOffset(totalHeight);
const editorWidth = state.outerWidth !== undefined
? this.viewContext.notebookOptions.computeCodeCellEditorWidth(state.outerWidth)
: this._layoutInfo?.editorWidth;
this._layoutInfo = {
fontInfo: state.font ?? this._layoutInfo.fontInfo ?? null,
@@ -282,11 +299,23 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
}
const verticalScrollbarHeight = hasScrolling ? 12 : 0; // take zoom level into account
return this.lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING + verticalScrollbarHeight;
const editorPadding = this.viewContext.notebookOptions.computeEditorPadding();
return this.lineCount * lineHeight
+ editorPadding.top
+ editorPadding.bottom // EDITOR_BOTTOM_PADDING
+ verticalScrollbarHeight;
}
private computeTotalHeight(editorHeight: number, outputsTotalHeight: number, outputShowMoreContainerHeight: number): number {
return EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + this.getEditorStatusbarHeight() + outputsTotalHeight + outputShowMoreContainerHeight + BOTTOM_CELL_TOOLBAR_GAP + CELL_BOTTOM_MARGIN;
const layoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration();
return layoutConfiguration.editorToolbarHeight //EDITOR_TOOLBAR_HEIGHT
+ layoutConfiguration.cellTopMargin // CELL_TOP_MARGIN
+ editorHeight
+ this.getEditorStatusbarHeight()
+ outputsTotalHeight
+ outputShowMoreContainerHeight
+ layoutConfiguration.bottomCellToolbarGap //BOTTOM_CELL_TOOLBAR_GAP
+ layoutConfiguration.cellBottomMargin; // CELL_BOTTOM_MARGIN;
}
protected onDidChangeTextModelContent(): void {
@@ -8,15 +8,15 @@ import * as UUID from 'vs/base/common/uuid';
import * as editorCommon from 'vs/editor/common/editorCommon';
import * as nls from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, MARKDOWN_CELL_BOTTOM_MARGIN, MARKDOWN_CELL_TOP_MARGIN, CELL_RIGHT_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants';
import { EditorFoldingStateDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel';
import { CellEditState, CellFindMatch, ICellOutputViewModel, ICellViewModel, MarkdownCellLayoutChangeEvent, MarkdownCellLayoutInfo, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel';
import { NotebookCellStateChangedEvent, NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
import { NotebookCellStateChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellKind, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
export class MarkdownCellViewModel extends BaseCellViewModel implements ICellViewModel {
readonly cellKind = CellKind.Markup;
@@ -29,7 +29,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
set renderedMarkdownHeight(newHeight: number) {
if (this.getEditState() === CellEditState.Preview) {
const newTotalHeight = newHeight + BOTTOM_CELL_TOOLBAR_GAP;
const newTotalHeight = newHeight + this.viewContext.notebookOptions.getLayoutConfiguration().bottomCellToolbarGap; // BOTTOM_CELL_TOOLBAR_GAP;
this.totalHeight = newTotalHeight;
}
}
@@ -47,8 +47,13 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
private _editorHeight = 0;
set editorHeight(newHeight: number) {
this._editorHeight = newHeight;
const layoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration();
this.totalHeight = this._editorHeight + MARKDOWN_CELL_TOP_MARGIN + MARKDOWN_CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + this.getEditorStatusbarHeight();
this.totalHeight = this._editorHeight
+ layoutConfiguration.markdownCellTopMargin // MARKDOWN_CELL_TOP_MARGIN
+ layoutConfiguration.markdownCellBottomMargin // MARKDOWN_CELL_BOTTOM_MARGIN
+ layoutConfiguration.bottomCellToolbarGap // BOTTOM_CELL_TOOLBAR_GAP
+ this.viewContext.notebookOptions.computeStatusBarHeight();
}
get editorHeight() {
@@ -102,23 +107,25 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
model: NotebookCellTextModel,
initialNotebookLayoutInfo: NotebookLayoutInfo | null,
readonly foldingDelegate: EditorFoldingStateDelegate,
readonly eventDispatcher: NotebookEventDispatcher,
readonly viewContext: ViewContext,
private readonly _mdRenderer: MarkdownRenderer,
@IConfigurationService configurationService: IConfigurationService,
@ITextModelService textModelService: ITextModelService,
) {
super(viewType, model, UUID.generateUuid(), configurationService, textModelService);
super(viewType, model, UUID.generateUuid(), viewContext, configurationService, textModelService);
this._layoutInfo = {
editorHeight: 0,
fontInfo: initialNotebookLayoutInfo?.fontInfo || null,
editorWidth: initialNotebookLayoutInfo?.width ? this.computeEditorWidth(initialNotebookLayoutInfo.width) : 0,
bottomToolbarOffset: BOTTOM_CELL_TOOLBAR_GAP,
editorWidth: initialNotebookLayoutInfo?.width
? this.viewContext.notebookOptions.computeMarkdownCellEditorWidth(initialNotebookLayoutInfo.width)
: 0,
bottomToolbarOffset: this.viewContext.notebookOptions.getLayoutConfiguration().bottomCellToolbarGap, // BOTTOM_CELL_TOOLBAR_GAP,
totalHeight: 0
};
this._register(this.onDidChangeState(e => {
eventDispatcher.emit([new NotebookCellStateChangedEvent(e, this)]);
this.viewContext.eventDispatcher.emit([new NotebookCellStateChangedEvent(e, this)]);
}));
this._register(model.onDidChangeMetadata(e => {
@@ -144,34 +151,34 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
this._onDidChangeState.fire({ foldingStateChanged: true });
}
private computeEditorWidth(outerWidth: number) {
return outerWidth - CODE_CELL_LEFT_MARGIN - CELL_RIGHT_MARGIN;
}
layoutChange(state: MarkdownCellLayoutChangeEvent) {
// recompute
if (!this.metadata?.inputCollapsed) {
const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo.editorWidth;
const editorWidth = state.outerWidth !== undefined
? this.viewContext.notebookOptions.computeMarkdownCellEditorWidth(state.outerWidth)
: this._layoutInfo.editorWidth;
const totalHeight = state.totalHeight === undefined ? this._layoutInfo.totalHeight : state.totalHeight;
this._layoutInfo = {
fontInfo: state.font || this._layoutInfo.fontInfo,
editorWidth,
editorHeight: this._editorHeight,
bottomToolbarOffset: totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2,
bottomToolbarOffset: this.viewContext.notebookOptions.computeBottomToolbarOffset(totalHeight),
totalHeight
};
} else {
const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo.editorWidth;
const totalHeight = MARKDOWN_CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + BOTTOM_CELL_TOOLBAR_GAP + MARKDOWN_CELL_BOTTOM_MARGIN;
const editorWidth = state.outerWidth !== undefined
? this.viewContext.notebookOptions.computeMarkdownCellEditorWidth(state.outerWidth)
: this._layoutInfo.editorWidth;
const totalHeight = this.viewContext.notebookOptions.computeCollapsedMarkdownCellHeight();
state.totalHeight = totalHeight;
this._layoutInfo = {
fontInfo: state.font || this._layoutInfo.fontInfo,
editorWidth,
editorHeight: this._editorHeight,
bottomToolbarOffset: totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2,
bottomToolbarOffset: this.viewContext.notebookOptions.computeBottomToolbarOffset(totalHeight),
totalHeight
};
}
@@ -20,7 +20,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { CellEditState, CellFindMatch, ICellViewModel, NotebookLayoutInfo, INotebookDeltaDecoration, INotebookDeltaCellStatusBarItems, CellFocusMode, CellFindMatchWithIndex } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { NotebookEventDispatcher, NotebookMetadataChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
import { NotebookMetadataChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
import { CellFoldingState, EditorFoldingStateDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel';
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
@@ -36,6 +36,7 @@ import { ResourceNotebookCellEdit } from 'vs/workbench/contrib/bulkEdit/browser/
import { NotebookCellSelectionCollection } from 'vs/workbench/contrib/notebook/browser/viewModel/cellSelectionCollection';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { groupByNumber } from 'vs/base/common/collections';
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
export interface INotebookEditorViewState {
editingCells: { [key: number]: boolean };
@@ -172,10 +173,6 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
return this._notebook.metadata;
}
get trusted() {
return !!this._notebook.metadata?.trusted;
}
private readonly _onDidChangeViewCells = this._register(new Emitter<INotebookViewCellsUpdateEvent>());
get onDidChangeViewCells(): Event<INotebookViewCellsUpdateEvent> { return this._onDidChangeViewCells.event; }
@@ -233,7 +230,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
constructor(
public viewType: string,
private _notebook: NotebookTextModel,
readonly eventDispatcher: NotebookEventDispatcher,
readonly viewContext: ViewContext,
private _layoutInfo: NotebookLayoutInfo | null,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
@@ -326,7 +323,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
this._register(this._notebook.onDidChangeContent(contentChanges => {
contentChanges.rawEvents.forEach(e => {
if (e.kind === NotebookCellsChangeType.ChangeDocumentMetadata) {
this.eventDispatcher.emit([new NotebookMetadataChangedEvent(this._notebook.metadata)]);
this.viewContext.eventDispatcher.emit([new NotebookMetadataChangedEvent(this._notebook.metadata)]);
}
});
@@ -335,7 +332,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
}
}));
this._register(this.eventDispatcher.onDidChangeLayout((e) => {
this._register(this.viewContext.eventDispatcher.onDidChangeLayout((e) => {
this._layoutInfo = e.value;
this._viewCells.forEach(cell => {
@@ -1181,9 +1178,9 @@ export type CellViewModel = CodeCellViewModel | MarkdownCellViewModel;
export function createCellViewModel(instantiationService: IInstantiationService, notebookViewModel: NotebookViewModel, cell: NotebookCellTextModel) {
if (cell.cellKind === CellKind.Code) {
return instantiationService.createInstance(CodeCellViewModel, notebookViewModel.viewType, cell, notebookViewModel.layoutInfo, notebookViewModel.eventDispatcher);
return instantiationService.createInstance(CodeCellViewModel, notebookViewModel.viewType, cell, notebookViewModel.layoutInfo, notebookViewModel.viewContext);
} else {
const mdRenderer = instantiationService.createInstance(MarkdownRenderer, { baseUrl: dirname(notebookViewModel.uri) });
return instantiationService.createInstance(MarkdownCellViewModel, notebookViewModel.viewType, cell, notebookViewModel.layoutInfo, notebookViewModel, notebookViewModel.eventDispatcher, mdRenderer);
return instantiationService.createInstance(MarkdownCellViewModel, notebookViewModel.viewType, cell, notebookViewModel.layoutInfo, notebookViewModel, notebookViewModel.viewContext, mdRenderer);
}
}
@@ -0,0 +1,15 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
export class ViewContext {
constructor(
readonly notebookOptions: NotebookOptions,
readonly eventDispatcher: NotebookEventDispatcher
) {
}
}
@@ -134,19 +134,14 @@ export interface INotebookRendererInfo {
extensionLocation: URI;
extensionId: ExtensionIdentifier;
readonly mimeTypes: readonly string[];
readonly dependencies: readonly string[];
matchesWithoutKernel(mimeType: string): NotebookRendererMatch;
matches(mimeType: string, kernelProvides: ReadonlyArray<string>): NotebookRendererMatch;
}
export interface INotebookMarkupRendererInfo {
readonly id: string;
readonly entrypoint: URI;
readonly extensionLocation: URI;
readonly extensionId: ExtensionIdentifier;
readonly extensionIsBuiltin: boolean;
readonly dependsOn: string | undefined;
readonly mimeTypes: readonly string[] | undefined;
}
export interface NotebookCellOutputMetadata {
/**
@@ -549,8 +544,9 @@ const _mimeTypeInfo = new Map<string, MimeTypeInfo>([
['text/html', { supportedByCore: true }],
['image/svg+xml', { supportedByCore: true }],
['image/jpeg', { supportedByCore: true }],
['text/x-javascript', { supportedByCore: true }],
['text/x-javascript', { alwaysSecure: true, supportedByCore: true }], // secure because rendered as text, not executed
['application/x.notebook.error-traceback', { alwaysSecure: true, supportedByCore: true }],
['application/x.notebook.error', { alwaysSecure: true, supportedByCore: true }],
['application/x.notebook.stream', { alwaysSecure: true, supportedByCore: true, mergeable: true }],
['application/x.notebook.stdout', { alwaysSecure: true, supportedByCore: true, mergeable: true }],
['application/x.notebook.stderr', { alwaysSecure: true, supportedByCore: true, mergeable: true }],
@@ -465,7 +465,7 @@ export class SimpleNotebookEditorModel extends EditorModel implements INotebookE
}
isReadonly(): boolean {
return this._fileService.hasCapability(this.resource, FileSystemProviderCapabilities.Readonly);
return this._workingCopy?.isReadonly() || this._fileService.hasCapability(this.resource, FileSystemProviderCapabilities.Readonly);
}
revert(options?: IRevertOptions): Promise<void> {
@@ -1,39 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { joinPath } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { INotebookMarkupRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
export class NotebookMarkupRendererInfo implements INotebookMarkupRendererInfo {
readonly id: string;
readonly entrypoint: URI;
readonly displayName: string;
readonly extensionLocation: URI;
readonly extensionId: ExtensionIdentifier;
readonly extensionIsBuiltin: boolean;
readonly dependsOn: string | undefined;
readonly mimeTypes: readonly string[] | undefined;
constructor(descriptor: {
readonly id: string;
readonly displayName: string;
readonly entrypoint: string;
readonly extension: IExtensionDescription;
readonly mimeTypes: readonly string[] | undefined,
readonly dependsOn: string | undefined,
}) {
this.id = descriptor.id;
this.extensionId = descriptor.extension.identifier;
this.extensionLocation = descriptor.extension.extensionLocation;
this.entrypoint = joinPath(this.extensionLocation, descriptor.entrypoint);
this.displayName = descriptor.displayName;
this.extensionIsBuiltin = descriptor.extension.isBuiltin;
this.dependsOn = descriptor.dependsOn;
this.mimeTypes = descriptor.mimeTypes;
}
}
@@ -0,0 +1,266 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CellToolbarLocKey, CellToolbarVisibility, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon';
const CELL_STATUSBAR_HEIGHT = 22;
const EDITOR_TOOLBAR_HEIGHT = 0;
const CELL_OUTPUT_PADDING = 14;
const MARKDOWN_PREVIEW_PADDING = 8;
const CELL_RIGHT_MARGIN = 16;
const CELL_RUN_GUTTER = 28;
const CODE_CELL_LEFT_MARGIN = 32;
const BOTTOM_CELL_TOOLBAR_GAP = 18;
const BOTTOM_CELL_TOOLBAR_HEIGHT = 22;
// Margin above editor
const CELL_TOP_MARGIN = 6;
const CELL_BOTTOM_MARGIN = 6;
const MARKDOWN_CELL_TOP_MARGIN = 8;
const MARKDOWN_CELL_BOTTOM_MARGIN = 8;
const COLLAPSED_INDICATOR_HEIGHT = 24;
const EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR = 12;
const EDITOR_BOTTOM_PADDING = 4;
let EDITOR_TOP_PADDING = 12;
const editorTopPaddingChangeEmitter = new Emitter<void>();
export const EditorTopPaddingChangeEvent = editorTopPaddingChangeEmitter.event;
export function updateEditorTopPadding(top: number) {
EDITOR_TOP_PADDING = top;
editorTopPaddingChangeEmitter.fire();
}
export function getEditorTopPadding() {
return EDITOR_TOP_PADDING;
}
export interface NotebookLayoutConfiguration {
cellRightMargin: number;
cellRunGutter: number;
cellTopMargin: number;
cellBottomMargin: number;
cellOutputPadding: number;
codeCellLeftMargin: number;
markdownCellTopMargin: number;
markdownCellBottomMargin: number;
markdownPreviewPadding: number;
bottomCellToolbarGap: number;
bottomCellToolbarHeight: number;
editorToolbarHeight: number;
editorTopPadding: number;
editorBottomPadding: number;
editorBottomPaddingWithoutStatusBar: number;
collapsedIndicatorHeight: number;
showCellStatusBar: boolean;
cellStatusBarHeight: number;
cellToolbarLocation: string | { [key: string]: string };
cellToolbarInteraction: string;
}
interface NotebookOptionsChangeEvent {
cellStatusBarVisibility?: boolean;
cellToolbarLocation?: boolean;
cellToolbarInteraction?: boolean;
editorTopPadding?: boolean;
}
export class NotebookOptions {
private _layoutConfiguration: NotebookLayoutConfiguration;
protected readonly _onDidChangeOptions = new Emitter<NotebookOptionsChangeEvent>();
readonly onDidChangeOptions = this._onDidChangeOptions.event;
private _disposables: IDisposable[];
constructor(readonly configurationService: IConfigurationService) {
const showCellStatusBar = this.configurationService.getValue<boolean>(ShowCellStatusBarKey);
const cellToolbarLocation = this.configurationService.getValue<string | { [key: string]: string }>(CellToolbarLocKey);
const cellToolbarInteraction = this.configurationService.getValue<string>(CellToolbarVisibility);
this._disposables = [];
this._layoutConfiguration = {
cellRightMargin: CELL_RIGHT_MARGIN,
cellRunGutter: CELL_RUN_GUTTER,
cellTopMargin: CELL_TOP_MARGIN,
cellBottomMargin: CELL_BOTTOM_MARGIN,
codeCellLeftMargin: CODE_CELL_LEFT_MARGIN,
markdownCellTopMargin: MARKDOWN_CELL_TOP_MARGIN,
markdownCellBottomMargin: MARKDOWN_CELL_BOTTOM_MARGIN,
bottomCellToolbarGap: BOTTOM_CELL_TOOLBAR_GAP,
bottomCellToolbarHeight: BOTTOM_CELL_TOOLBAR_HEIGHT,
editorTopPadding: EDITOR_TOP_PADDING,
editorBottomPadding: EDITOR_BOTTOM_PADDING,
editorBottomPaddingWithoutStatusBar: EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR,
editorToolbarHeight: EDITOR_TOOLBAR_HEIGHT,
cellOutputPadding: CELL_OUTPUT_PADDING,
collapsedIndicatorHeight: COLLAPSED_INDICATOR_HEIGHT,
markdownPreviewPadding: MARKDOWN_PREVIEW_PADDING,
cellStatusBarHeight: CELL_STATUSBAR_HEIGHT,
showCellStatusBar,
cellToolbarLocation,
cellToolbarInteraction
};
this._disposables.push(this.configurationService.onDidChangeConfiguration(e => {
let cellStatusBarVisibility = e.affectsConfiguration(ShowCellStatusBarKey);
let cellToolbarLocation = e.affectsConfiguration(CellToolbarLocKey);
let cellToolbarInteraction = e.affectsConfiguration(CellToolbarVisibility);
if (!cellStatusBarVisibility && !cellToolbarLocation && !cellToolbarInteraction) {
return;
}
const configuration = Object.assign({}, this._layoutConfiguration);
if (cellStatusBarVisibility) {
configuration.showCellStatusBar = this.configurationService.getValue<boolean>(ShowCellStatusBarKey);
}
if (cellToolbarLocation) {
configuration.cellToolbarLocation = this.configurationService.getValue<string | { [key: string]: string }>(CellToolbarLocKey);
}
if (cellToolbarInteraction) {
configuration.cellToolbarInteraction = this.configurationService.getValue<string>(CellToolbarVisibility);
}
this._layoutConfiguration = configuration;
// trigger event
this._onDidChangeOptions.fire({
cellStatusBarVisibility: cellStatusBarVisibility,
cellToolbarLocation: cellToolbarLocation,
cellToolbarInteraction: cellToolbarInteraction
});
}));
this._disposables.push(EditorTopPaddingChangeEvent(() => {
const configuration = Object.assign({}, this._layoutConfiguration);
configuration.editorTopPadding = getEditorTopPadding();
this._layoutConfiguration = configuration;
this._onDidChangeOptions.fire({ editorTopPadding: true });
}));
}
getLayoutConfiguration(): NotebookLayoutConfiguration {
return this._layoutConfiguration;
}
computeCollapsedMarkdownCellHeight(): number {
return this._layoutConfiguration.markdownCellTopMargin // MARKDOWN_CELL_TOP_MARGIN
+ this._layoutConfiguration.collapsedIndicatorHeight // COLLAPSED_INDICATOR_HEIGHT
+ this._layoutConfiguration.bottomCellToolbarGap // BOTTOM_CELL_TOOLBAR_GAP
+ this._layoutConfiguration.markdownCellBottomMargin; // MARKDOWN_CELL_BOTTOM_MARGIN;
}
computeBottomToolbarOffset(totalHeight: number) {
return totalHeight
- this._layoutConfiguration.bottomCellToolbarGap // BOTTOM_CELL_TOOLBAR_GAP
- this._layoutConfiguration.bottomCellToolbarHeight / 2;
}
computeCodeCellEditorWidth(outerWidth: number): number {
return outerWidth - (
this._layoutConfiguration.codeCellLeftMargin // CODE_CELL_LEFT_MARGIN
+ this._layoutConfiguration.cellRunGutter // CELL_RUN_GUTTER
+ this._layoutConfiguration.cellRightMargin // CELL_RIGHT_MARGIN
);
}
computeMarkdownCellEditorWidth(outerWidth: number): number {
return outerWidth
- this._layoutConfiguration.codeCellLeftMargin // CODE_CELL_LEFT_MARGIN
- this._layoutConfiguration.cellRightMargin; // CELL_RIGHT_MARGIN;
}
computeStatusBarHeight(): number {
if (this._layoutConfiguration.showCellStatusBar) {
return this._layoutConfiguration.cellStatusBarHeight;
} else {
return 0;
}
}
computeCellToolbarLocation(viewType?: string): 'right' | 'left' | 'hidden' {
const cellToolbarLocation = this._layoutConfiguration.cellToolbarLocation;
if (typeof cellToolbarLocation === 'string') {
if (cellToolbarLocation === 'left' || cellToolbarLocation === 'right' || cellToolbarLocation === 'hidden') {
return cellToolbarLocation;
}
} else {
if (viewType) {
const notebookSpecificSetting = cellToolbarLocation[viewType] ?? cellToolbarLocation['default'];
let cellToolbarLocationForCurrentView: 'right' | 'left' | 'hidden' = 'right';
switch (notebookSpecificSetting) {
case 'left':
cellToolbarLocationForCurrentView = 'left';
break;
case 'right':
cellToolbarLocationForCurrentView = 'right';
break;
case 'hidden':
cellToolbarLocationForCurrentView = 'hidden';
break;
default:
cellToolbarLocationForCurrentView = 'right';
break;
}
return cellToolbarLocationForCurrentView;
}
}
return 'right';
}
computeEditorPadding() {
return {
top: getEditorTopPadding(),
bottom: this._layoutConfiguration.showCellStatusBar
? this._layoutConfiguration.editorBottomPadding// EDITOR_BOTTOM_PADDING
: this._layoutConfiguration.editorBottomPaddingWithoutStatusBar // EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR
};
}
computeWebviewOptions() {
return {
outputNodePadding: this._layoutConfiguration.cellOutputPadding, // CELL_OUTPUT_PADDING,
outputNodeLeftPadding: this._layoutConfiguration.cellOutputPadding, // CELL_OUTPUT_PADDING,
previewNodePadding: this._layoutConfiguration.markdownPreviewPadding, // MARKDOWN_PREVIEW_PADDING,
leftMargin: this._layoutConfiguration.codeCellLeftMargin, // CODE_CELL_LEFT_MARGIN,
rightMargin: this._layoutConfiguration.cellRightMargin, // CELL_RIGHT_MARGIN,
runGutter: this._layoutConfiguration.cellRunGutter, // CELL_RUN_GUTTER,
};
}
computeDiffWebviewOptions() {
return {
outputNodePadding: this._layoutConfiguration.cellOutputPadding, // CELL_OUTPUT_PADDING,
outputNodeLeftPadding: 32,
previewNodePadding: this._layoutConfiguration.markdownPreviewPadding, // MARKDOWN_PREVIEW_PADDING,
leftMargin: 0,
rightMargin: 0,
runGutter: 0
};
}
computeIndicatorPosition(totalHeight: number) {
return {
bottomIndicatorTop: totalHeight - this._layoutConfiguration.bottomCellToolbarGap - this._layoutConfiguration.cellBottomMargin,
verticalIndicatorHeight: totalHeight - this._layoutConfiguration.bottomCellToolbarGap
};
}
dispose() {
this._disposables.forEach(d => d.dispose());
this._disposables = [];
}
}
@@ -19,6 +19,10 @@ class DependencyList {
this.defined = this.value.size > 0;
}
public values(): string[] {
return Array.from(this.value);
}
/** Gets whether any of the 'available' dependencies match the ones in this list */
public matches(available: ReadonlyArray<string>) {
// For now this is simple, but this may expand to support globs later
@@ -39,7 +43,7 @@ export class NotebookOutputRendererInfo implements INotebookRendererInfo {
// todo: re-add preloads in pure renderer API
readonly preloads: ReadonlyArray<URI> = [];
private readonly mimeTypes: readonly string[];
readonly mimeTypes: readonly string[];
private readonly mimeTypeGlobs: glob.ParsedPattern[];
constructor(descriptor: {
@@ -62,6 +66,10 @@ export class NotebookOutputRendererInfo implements INotebookRendererInfo {
this.optionalDependencies = new DependencyList(descriptor.optionalDependencies ?? Iterable.empty());
}
get dependencies(): string[] {
return this.hardDependencies.values();
}
matchesWithoutKernel(mimeType: string) {
if (!this.matchesMimeTypeOnly(mimeType)) {
return NotebookRendererMatch.Never;
@@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri';
import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol';
import { Event } from 'vs/base/common/event';
import { INotebookRendererInfo, NotebookDataDto, TransientOptions, IOrderedMimeType, IOutputDto, INotebookMarkupRendererInfo, INotebookContributionData } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookRendererInfo, NotebookDataDto, TransientOptions, IOrderedMimeType, IOutputDto, INotebookContributionData } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { CancellationToken } from 'vs/base/common/cancellation';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
@@ -73,7 +73,7 @@ export interface INotebookService {
getMimeTypeInfo(textModel: NotebookTextModel, kernelProvides: readonly string[] | undefined, output: IOutputDto): readonly IOrderedMimeType[];
getRendererInfo(id: string): INotebookRendererInfo | undefined;
getMarkupRendererInfo(): INotebookMarkupRendererInfo[];
getRenderers(): INotebookRendererInfo[];
/** Updates the preferred renderer for the given mimetype in the workspace. */
updateMimePreferredRenderer(mimeType: string, rendererId: string): void;
@@ -18,8 +18,10 @@ import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { reduceCellRanges } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { CellKind, diff, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
import { NotebookEditorTestModel, setupInstantiationService, withTestNotebook } from 'vs/workbench/contrib/notebook/test/testNotebookEditor';
@@ -37,8 +39,8 @@ suite('NotebookViewModel', () => {
test('ctor', function () {
const notebook = new NotebookTextModel('notebook', URI.parse('test'), [], notebookDocumentMetadataDefaults, { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false }, undoRedoService, modelService, modeService);
const model = new NotebookEditorTestModel(notebook);
const eventDispatcher = new NotebookEventDispatcher();
const viewModel = new NotebookViewModel('notebook', model.notebook, eventDispatcher, null, instantiationService, bulkEditService, undoRedoService, textModelService);
const viewContext = new ViewContext(new NotebookOptions(instantiationService.get(IConfigurationService)), new NotebookEventDispatcher());
const viewModel = new NotebookViewModel('notebook', model.notebook, viewContext, null, instantiationService, bulkEditService, undoRedoService, textModelService);
assert.strictEqual(viewModel.viewType, 'notebook');
});
@@ -42,6 +42,8 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust';
import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService';
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
export class TestCell extends NotebookCellTextModel {
constructor(
@@ -172,8 +174,8 @@ function _createTestNotebookEditor(instantiationService: TestInstantiationServic
}), notebookDocumentMetadataDefaults, { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false });
const model = new NotebookEditorTestModel(notebook);
const eventDispatcher = new NotebookEventDispatcher();
const viewModel: NotebookViewModel = instantiationService.createInstance(NotebookViewModel, viewType, model.notebook, eventDispatcher, null);
const viewContext = new ViewContext(new NotebookOptions(instantiationService.get(IConfigurationService)), new NotebookEventDispatcher());
const viewModel: NotebookViewModel = instantiationService.createInstance(NotebookViewModel, viewType, model.notebook, viewContext, null);
const cellList = createNotebookCellList(instantiationService);
cellList.attachViewModel(viewModel);
@@ -37,7 +37,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { editorBackground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry';
import { editorBackground, editorErrorForeground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry';
import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge';
@@ -60,7 +60,6 @@ import { ILogService } from 'vs/platform/log/common/log';
import { settingsMoreActionIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons';
import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { SettingsTarget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets';
import { untrustedForegroundColor } from 'vs/workbench/contrib/workspace/browser/workspaceTrustColors';
const $ = DOM.$;
@@ -1537,8 +1536,8 @@ export class SettingUntrustedRenderer extends AbstractSettingRenderer implements
const manageWorkspaceTrustLabel = localize('manageWorkspaceTrust', "Manage Workspace Trust");
const trustLabelElement = $('.setting-item-trust-description');
const untrustedWorkspaceIcon = DOM.append(trustLabelElement, $('span.codicon.codicon-workspace-untrusted'));
template.toDispose.add(attachStylerCallback(this._themeService, { untrustedForegroundColor }, colors => {
untrustedWorkspaceIcon.style.setProperty('--workspace-trust-state-untrusted-color', colors.untrustedForegroundColor?.toString() || '');
template.toDispose.add(attachStylerCallback(this._themeService, { editorErrorForeground }, colors => {
untrustedWorkspaceIcon.style.setProperty('--workspace-trust-state-untrusted-color', colors.editorErrorForeground?.toString() || '');
}));
const element = DOM.append(trustLabelElement, $('span'));
element.textContent = localize('trustLabel', "This setting can only be applied in a trusted workspace");
@@ -51,6 +51,7 @@ import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent';
import { IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
import { IHoverService } from 'vs/workbench/services/hover/browser/hover';
import { STATUS_BAR_HOST_NAME_BACKGROUND } from 'vs/workbench/common/theme';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
export const forwardedPortsViewEnabled = new RawContextKey<boolean>('forwardedPortsViewEnabled', false, nls.localize('tunnel.forwardedPortsViewEnabled', "Whether the Ports view is enabled."));
@@ -1318,7 +1319,7 @@ namespace ChangeLocalPortAction {
export function handler(): ICommandHandler {
return async (accessor, arg) => {
const remoteExplorerService = accessor.get(IRemoteExplorerService);
const notificationService = accessor.get(INotificationService);
const dialogService = accessor.get(IDialogService);
const tunnelService = accessor.get(ITunnelService);
const context = (arg !== undefined || arg instanceof TunnelItem) ? arg : accessor.get(IContextKeyService).getContextKeyValue(TunnelViewSelectionKeyName);
if (context instanceof TunnelItem) {
@@ -1330,7 +1331,9 @@ namespace ChangeLocalPortAction {
const numberValue = Number(value);
const newForward = await remoteExplorerService.forward({ host: context.remoteHost, port: context.remotePort }, numberValue, context.name, undefined, true);
if (newForward && newForward.tunnelLocalPort !== numberValue) {
notificationService.warn(nls.localize('remote.tunnel.changeLocalPortNumber', "The local port {0} is not available. Port number {1} has been used instead", value, newForward.tunnelLocalPort ?? newForward.localAddress));
dialogService.show(Severity.Info,
nls.localize('remote.tunnel.changeLocalPortNumber', "The local port {0} is not available.\n\nThis usually happens when there is already another process using port {0}.\n\nPort number {1} has been used instead.", value, newForward.tunnelLocalPort ?? newForward.localAddress),
[nls.localize('remote.tunnel.changeLocalPortNumber.Ok', "Ok")]);
}
}
},
@@ -19,6 +19,7 @@ import { Iterable } from 'vs/base/common/iterator';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
import { parseLinkedText } from 'vs/base/common/linkedText';
import { Schemas } from 'vs/base/common/network';
import * as env from 'vs/base/common/platform';
import * as strings from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
@@ -35,6 +36,7 @@ import * as nls from 'vs/nls';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
@@ -75,6 +77,7 @@ import { createEditorFromSearchResult } from 'vs/workbench/contrib/searchEditor/
import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { IPreferencesService, ISettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences';
import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchConfigurationProperties, ITextQuery, SearchCompletionExitCode, SearchSortOrder, TextSearchCompleteMessageType } from 'vs/workbench/services/search/common/search';
import { TextSearchCompleteMessage } from 'vs/workbench/services/search/common/searchExtTypes';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
const $ = dom.$;
@@ -159,6 +162,7 @@ export class SearchView extends ViewPane {
@IProgressService private readonly progressService: IProgressService,
@INotificationService private readonly notificationService: INotificationService,
@IDialogService private readonly dialogService: IDialogService,
@ICommandService private readonly commandService: ICommandService,
@IContextViewService private readonly contextViewService: IContextViewService,
@IInstantiationService instantiationService: IInstantiationService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@@ -1524,7 +1528,7 @@ export class SearchView extends ViewPane {
if (completed && completed.messages) {
for (const message of completed.messages) {
if (message.type === TextSearchCompleteMessageType.Information) {
this.addMessage(message.text);
this.addMessage(message);
}
else if (message.type === TextSearchCompleteMessageType.Warning) {
warningMessage += (warningMessage ? ' - ' : '') + message.text;
@@ -1641,8 +1645,8 @@ export class SearchView extends ViewPane {
}
}
private addMessage(message: string) {
const linkedText = parseLinkedText(message);
private addMessage(message: TextSearchCompleteMessage) {
const linkedText = parseLinkedText(message.text);
const messageBox = this.messagesElement.firstChild as HTMLDivElement;
if (!messageBox) {
@@ -1659,7 +1663,26 @@ export class SearchView extends ViewPane {
if (typeof node === 'string') {
dom.append(span, document.createTextNode(node));
} else {
const link = this.instantiationService.createInstance(Link, node);
const link = this.instantiationService.createInstance(Link, node, {
opener: async href => {
if (!message.trusted) { return; }
const parsed = URI.parse(href, true);
if (parsed.scheme === Schemas.command && message.trusted) {
const result = await this.commandService.executeCommand(parsed.path);
if ((result as any)?.triggerSearch) {
this.triggerQueryChange();
}
} else if (parsed.scheme === Schemas.https) {
this.openerService.open(parsed);
} else {
if (parsed.scheme === Schemas.command && !message.trusted) {
this.notificationService.error(nls.localize('unable to open trust', "Unable to open command link from untrusted source: {0}", href));
} else {
this.notificationService.error(nls.localize('unable to open', "Unable to open unknown link: {0}", href));
}
}
}
});
dom.append(span, link.el);
this.messageDisposables.add(link);
this.messageDisposables.add(attachLinkStyler(link, this.themeService));
@@ -538,12 +538,8 @@ class SearchEditorWorkingCopyEditorHandler extends Disposable implements IWorkbe
private installHandler(): void {
this._register(this.workingCopyEditorService.registerHandler({
handles: workingCopy => {
return workingCopy.resource.scheme === SearchEditorConstants.SearchEditorScheme;
},
isOpen: (workingCopy, editor) => {
return editor instanceof SearchEditorInput && isEqual(workingCopy.resource, editor.modelUri);
},
handles: workingCopy => workingCopy.resource.scheme === SearchEditorConstants.SearchEditorScheme,
isOpen: (workingCopy, editor) => editor instanceof SearchEditorInput && isEqual(workingCopy.resource, editor.modelUri),
createEditor: workingCopy => {
const input = this.instantiationService.invokeFunction(getOrMakeSearchEditorInput, { from: 'model', modelUri: workingCopy.resource });
input.setDirty(true);
@@ -55,6 +55,10 @@ import { IFileService } from 'vs/platform/files/common/files';
import { parseLinkedText } from 'vs/base/common/linkedText';
import { Link } from 'vs/platform/opener/browser/link';
import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { Schemas } from 'vs/base/common/network';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { TextSearchCompleteMessage } from 'vs/workbench/services/search/common/searchExtTypes';
import { INotificationService } from 'vs/platform/notification/common/notification';
const RESULT_LINE_REGEX = /^(\s+)(\d+)(:| )(\s+)(.*)$/;
const FILE_LINE_REGEX = /^(\S.*):$/;
@@ -97,6 +101,8 @@ export class SearchEditor extends BaseTextEditor {
@IContextViewService private readonly contextViewService: IContextViewService,
@ICommandService private readonly commandService: ICommandService,
@IContextKeyService readonly contextKeyService: IContextKeyService,
@IOpenerService readonly openerService: IOpenerService,
@INotificationService private readonly notificationService: INotificationService,
@IEditorProgressService readonly progressService: IEditorProgressService,
@ITextResourceConfigurationService textResourceService: ITextResourceConfigurationService,
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@@ -557,7 +563,7 @@ export class SearchEditor extends BaseTextEditor {
if (searchOperation && searchOperation.messages) {
for (const message of searchOperation.messages) {
if (message.type === TextSearchCompleteMessageType.Information) {
this.addMessage(message.text);
this.addMessage(message);
}
else if (message.type === TextSearchCompleteMessageType.Warning) {
warningMessage += (warningMessage ? ' - ' : '') + message.text;
@@ -576,8 +582,8 @@ export class SearchEditor extends BaseTextEditor {
input.setMatchRanges(results.matchRanges);
}
private addMessage(message: string) {
const linkedText = parseLinkedText(message);
private addMessage(message: TextSearchCompleteMessage) {
const linkedText = parseLinkedText(message.text);
let messageBox: HTMLElement;
if (this.messageBox.firstChild) {
@@ -594,7 +600,25 @@ export class SearchEditor extends BaseTextEditor {
if (typeof node === 'string') {
DOM.append(messageBox, document.createTextNode(node));
} else {
const link = this.instantiationService.createInstance(Link, node);
const link = this.instantiationService.createInstance(Link, node, {
opener: async href => {
const parsed = URI.parse(href, true);
if (parsed.scheme === Schemas.command && message.trusted) {
const result = await this.commandService.executeCommand(parsed.path);
if ((result as any)?.triggerSearch) {
this.triggerSearch();
}
} else if (parsed.scheme === Schemas.https) {
this.openerService.open(parsed);
} else {
if (parsed.scheme === Schemas.command && !message.trusted) {
this.notificationService.error(localize('unable to open trust', "Unable to open command link from untrusted source: {0}", href));
} else {
this.notificationService.error(localize('unable to open', "Unable to open unknown link: {0}", href));
}
}
}
});
DOM.append(messageBox, link.el);
this.messageDisposables.add(link);
this.messageDisposables.add(attachLinkStyler(link, this.themeService));
@@ -652,7 +676,9 @@ export class SearchEditor extends BaseTextEditor {
this.saveViewState();
await super.setInput(newInput, options, context, token);
if (token.isCancellationRequested) { return; }
if (token.isCancellationRequested) {
return;
}
const { configurationModel, resultsModel } = await newInput.getModels();
if (token.isCancellationRequested) { return; }

Some files were not shown because too many files have changed in this diff Show More