Revert "Revert "Merge branch 'main' of https://github.com/microsoft/vscode into main""

This reverts commit 7c01395da1.
This commit is contained in:
rebornix 2021-05-25 17:49:34 -07:00
parent 8822790908
commit 473cfe28bf
45 changed files with 585 additions and 264 deletions

View File

@ -226,7 +226,11 @@
},
"output.selfClosingStyle": {
"type": "string",
"enum": ["html", "xhtml", "xml"],
"enum": [
"html",
"xhtml",
"xml"
],
"default": "html",
"markdownDescription": "%emmetPreferencesOutputSelfClosingStyle%"
},

View File

@ -54,9 +54,9 @@
integrity sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI=
"@types/node@14.x":
version "14.14.43"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.43.tgz#26bcbb0595b305400e8ceaf9a127a7f905ae49c8"
integrity sha512-3pwDJjp1PWacPTpH0LcfhgjvurQvrZFBrC6xxjaUEZ7ifUtT32jtjPxEMMblpqd2Mvx+k8haqQJLQxolyGN/cQ==
version "14.17.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.1.tgz#5e07e0cb2ff793aa7a1b41deae76221e6166049f"
integrity sha512-/tpUyFD7meeooTRwl3sYlihx2BrJE7q9XF71EguPFIySj9B7qgnRtHsHTho+0AUm4m1SvWGm6uSncrR94q6Vtw==
emmet@^2.3.0:
version "2.3.4"
@ -77,9 +77,9 @@ jsonc-parser@^2.3.0:
integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==
vscode-emmet-helper@^2.3.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.6.2.tgz#777b471a7851ba0ca8e4151533be7f92511f39b0"
integrity sha512-SkL1WjZZsA+bfTo52QH4PgqXCQAJSqzOmJtAY3rOl17MKbY6iJhVv2T26PshjmUnHoXnXMNa7PcLMCS75RsQDQ==
version "2.6.4"
resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.6.4.tgz#bea47f17649bba26b412f3d1fac18aaee43eba25"
integrity sha512-fP0nunW1RUWEKGf4gqiYLOVNFFGXSRHjCl0pikxtwCFlty8WwimM+RBJ5o0aIiwerrYD30HqeaVyvDW027Sseg==
dependencies:
emmet "^2.3.0"
jsonc-parser "^2.3.0"

View File

@ -40,7 +40,7 @@
}
},
"contributes": {
"notebookOutputRenderer": [
"notebookRenderer": [
{
"id": "markdownItRenderer",
"displayName": "Markdown it renderer",

View File

@ -21,7 +21,7 @@
}
},
"contributes": {
"notebookOutputRenderer": [
"notebookRenderer": [
{
"id": "markdownItRenderer-katex",
"displayName": "Markdown it katex renderer",

View File

@ -7,6 +7,7 @@ import 'mocha';
import * as assert from 'assert';
import * as vscode from 'vscode';
import { createRandomFile, asPromise, disposeAll, closeAllEditors, revertAllDirty, saveAllEditors, assertNoRpc } from '../utils';
import { TextDecoder } from 'util';
async function createRandomNotebookFile() {
return createRandomFile('', undefined, '.vsctestnb');
@ -60,13 +61,13 @@ class Kernel {
task.executionOrder = 1;
if (cell.notebook.uri.path.endsWith('customRenderer.vsctestnb')) {
await task.replaceOutput([new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/custom', ['test'], undefined)
vscode.NotebookCellOutputItem.text('test', 'text/custom', undefined)
])]);
return;
}
await task.replaceOutput([new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined)
vscode.NotebookCellOutputItem.text('my output', 'text/plain', undefined)
])]);
task.end({ success: true });
}
@ -129,7 +130,7 @@ suite('Notebook API tests', function () {
kind: vscode.NotebookCellKind.Code,
outputs: [
new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', 'Hello World', { testOutputItemMetadata: true })
vscode.NotebookCellOutputItem.text('Hello World', 'text/plain', { testOutputItemMetadata: true })
],
{ testOutputMetadata: true })
],
@ -182,7 +183,7 @@ suite('Notebook API tests', function () {
const task = this.controller.createNotebookCellExecutionTask(cell);
task.start();
await task.replaceOutput([new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined)
vscode.NotebookCellOutputItem.text('my second output', 'text/plain', undefined)
])]);
task.end({ success: true });
}
@ -476,7 +477,7 @@ suite('Notebook API tests', function () {
assert.deepStrictEqual(secondCell!.outputs[0].metadata, { testOutputMetadata: true });
assert.strictEqual(secondCell!.outputs[0].outputs.length, 1);
assert.strictEqual(secondCell!.outputs[0].outputs[0].mime, 'text/plain');
assert.strictEqual(secondCell!.outputs[0].outputs[0].value, 'Hello World');
assert.strictEqual(new TextDecoder().decode(secondCell!.outputs[0].outputs[0].data), 'Hello World');
assert.deepStrictEqual(secondCell!.outputs[0].outputs[0].metadata, { testOutputItemMetadata: true });
assert.strictEqual(secondCell!.executionSummary?.executionOrder, 5);
assert.strictEqual(secondCell!.executionSummary?.success, true);
@ -747,9 +748,7 @@ suite('Notebook API tests', function () {
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
assert.strictEqual(cell.outputs[0].outputs.length, 1);
assert.strictEqual(cell.outputs[0].outputs[0].mime, 'text/plain');
assert.deepStrictEqual(cell.outputs[0].outputs[0].value, [
'my output'
]);
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].outputs[0].data), 'my output');
});
await withEvent<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs, async (event) => {
@ -759,9 +758,7 @@ suite('Notebook API tests', function () {
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
assert.strictEqual(cell.outputs[0].outputs.length, 1);
assert.strictEqual(cell.outputs[0].outputs[0].mime, 'text/plain');
assert.deepStrictEqual(cell.outputs[0].outputs[0].value, [
'my second output'
]);
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].outputs[0].data), 'my second output');
});
});
@ -779,7 +776,7 @@ suite('Notebook API tests', function () {
task.start();
task.token.onCancellationRequested(async () => {
await task.replaceOutput([new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['Canceled'], undefined)
vscode.NotebookCellOutputItem.text('Canceled', 'text/plain', undefined)
])]);
task.end({});
});
@ -801,9 +798,7 @@ suite('Notebook API tests', function () {
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
assert.strictEqual(cell.outputs[0].outputs.length, 1);
assert.strictEqual(cell.outputs[0].outputs[0].mime, 'text/plain');
assert.deepStrictEqual(cell.outputs[0].outputs[0].value, [
'Canceled'
]);
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].outputs[0].data), 'Canceled');
});
cancelableKernel.controller.dispose();
@ -826,7 +821,7 @@ suite('Notebook API tests', function () {
async interrupt() {
await this._task!.replaceOutput([new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['Interrupted'], undefined)
vscode.NotebookCellOutputItem.text('Interrupted', 'text/plain', undefined)
])]);
this._task!.end({});
}
@ -845,9 +840,7 @@ suite('Notebook API tests', function () {
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
assert.strictEqual(cell.outputs[0].outputs.length, 1);
assert.strictEqual(cell.outputs[0].outputs[0].mime, 'text/plain');
assert.deepStrictEqual(cell.outputs[0].outputs[0].value, [
'Interrupted'
]);
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].outputs[0].data), 'Interrupted');
});
interruptableKernel.controller.dispose();
@ -1188,10 +1181,10 @@ suite('Notebook API tests', function () {
const task = this.controller.createNotebookCellExecutionTask(cell);
task.start();
await task.replaceOutput([new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['Some output'], undefined)
vscode.NotebookCellOutputItem.text('Some output', 'text/plain', undefined)
])]);
assert.strictEqual(cell.notebook.cellAt(0).outputs.length, 1);
assert.deepStrictEqual(cell.notebook.cellAt(0).outputs[0].outputs[0].value, ['Some output']);
assert.deepStrictEqual(new TextDecoder().decode(cell.notebook.cellAt(0).outputs[0].outputs[0].data), 'Some output');
task.end({});
called = true;
}

View File

@ -46,7 +46,7 @@
]
}
],
"notebookOutputRenderer": [
"notebookRenderer": [
{
"id": "notebookCoreTestRenderer",
"displayName": "Notebook Core Test Renderer",

View File

@ -69,7 +69,7 @@ export function activate(context: vscode.ExtensionContext): any {
const task = controller.createNotebookCellExecutionTask(cell);
task.start();
task.replaceOutput([new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/html', ['test output'], undefined)
vscode.NotebookCellOutputItem.text('test output', 'text/html', undefined)
])]);
task.end({ success: true });
}

View File

@ -32,7 +32,8 @@
"capabilities": {
"untrustedWorkspaces": {
"supported": true
}
},
"virtualWorkspaces": true
},
"contributes": {
"resourceLabelFormatters": [
@ -88,7 +89,7 @@
"statusBar/remoteIndicator": [
{
"command": "vscode-testresolver.newWindow",
"when": "!remoteName",
"when": "!remoteName && !virtualWorkspace",
"group": "remote_90_test_1_local@2"
},
{

View File

@ -4,7 +4,7 @@
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data: blob: vscode-remote-resource:; media-src 'none'; frame-src 'self' vscode-webview:; object-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' https: ws:; font-src 'self' https: vscode-remote-resource:;">
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'; trusted-types default TrustedFunctionWorkaround ExtensionScripts amdLoader cellRendererEditorText defaultWorkerFactory diffEditorWidget editorGhostText domLineBreaksComputer editorViewLayer diffReview extensionHostWorker insane notebookOutputRenderer safeInnerHtml standaloneColorizer tokenizeToString webNestedWorkerExtensionHost webWorkerExtensionHost;">
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'; trusted-types default TrustedFunctionWorkaround ExtensionScripts amdLoader cellRendererEditorText defaultWorkerFactory diffEditorWidget editorGhostText domLineBreaksComputer editorViewLayer diffReview extensionHostWorker insane notebookRenderer safeInnerHtml standaloneColorizer tokenizeToString webNestedWorkerExtensionHost webWorkerExtensionHost;">
</head>
<body aria-label="">
</body>

View File

@ -4,7 +4,7 @@
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data: blob: vscode-remote-resource:; media-src 'none'; frame-src 'self' vscode-webview:; object-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' https: ws:; font-src 'self' https: vscode-remote-resource:;">
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'; trusted-types default TrustedFunctionWorkaround ExtensionScripts amdLoader cellRendererEditorText defaultWorkerFactory diffEditorWidget editorGhostText domLineBreaksComputer editorViewLayer diffReview extensionHostWorker insane notebookOutputRenderer safeInnerHtml standaloneColorizer tokenizeToString webNestedWorkerExtensionHost webWorkerExtensionHost;">
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'; trusted-types default TrustedFunctionWorkaround ExtensionScripts amdLoader cellRendererEditorText defaultWorkerFactory diffEditorWidget editorGhostText domLineBreaksComputer editorViewLayer diffReview extensionHostWorker insane notebookRenderer safeInnerHtml standaloneColorizer tokenizeToString webNestedWorkerExtensionHost webWorkerExtensionHost;">
</head>
<body aria-label="">
</body>

View File

@ -10,6 +10,7 @@ import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
import { Registry } from 'vs/platform/registry/common/platform';
import { Codicon, iconRegistry } from 'vs/base/common/codicons';
import { OperatingSystem } from 'vs/base/common/platform';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
const terminalProfileBaseProperties: IJSONSchemaMap = {
args: {
@ -372,7 +373,7 @@ export function registerTerminalDefaultProfileConfiguration(detectedProfiles?: {
}
function createProfileDescription(profile: ITerminalProfile): string {
let description = `$(${profile.icon || Codicon.terminal.id}) ${profile.profileName}\n- path: ${profile.path}`;
let description = `$(${ThemeIcon.isThemeIcon(profile.icon) ? profile.icon.id : profile.icon ? profile.icon : Codicon.terminal.id}) ${profile.profileName}\n- path: ${profile.path}`;
if (profile.args) {
if (typeof profile.args === 'string') {
description += `\n- args: "${profile.args}"`;

View File

@ -37,6 +37,7 @@ export function detectAvailableProfiles(
safeConfigProvider<boolean>(TerminalSettingId.UseWslProfiles) !== false,
safeConfigProvider(TerminalSettingId.ProfilesWindows),
safeConfigProvider(TerminalSettingId.DefaultProfileWindows),
testPaths,
variableResolver
);
}
@ -58,6 +59,7 @@ async function detectAvailableWindowsProfiles(
useWslProfiles?: boolean,
configProfiles?: { [key: string]: ITerminalProfileObject },
defaultProfileName?: string,
testPaths?: string[],
variableResolver?: (text: string[]) => Promise<string[]>
): Promise<ITerminalProfile[]> {
// Determine the correct System32 path. We want to point to Sysnative
@ -73,7 +75,7 @@ async function detectAvailableWindowsProfiles(
useWSLexe = true;
}
await initializeWindowsProfiles();
await initializeWindowsProfiles(testPaths);
const detectedProfiles: Map<string, ITerminalProfileObject> = new Map();
@ -175,7 +177,7 @@ async function transformToTerminalProfiles(
return resultProfiles;
}
async function initializeWindowsProfiles(): Promise<void> {
async function initializeWindowsProfiles(testPaths?: string[]): Promise<void> {
if (profileSources) {
return;
}
@ -197,7 +199,7 @@ async function initializeWindowsProfiles(): Promise<void> {
});
profileSources.set('PowerShell', {
profileName: 'PowerShell',
paths: await getPowershellPaths(),
paths: testPaths || await getPowershellPaths(),
icon: ThemeIcon.asThemeIcon(Codicon.terminalPowershell)
});
}
@ -245,23 +247,25 @@ async function getWslProfiles(wslPath: string, defaultProfileName: string | unde
profileName,
path: wslPath,
args: [`-d`, `${distroName}`],
isDefault: profileName === defaultProfileName
isDefault: profileName === defaultProfileName,
icon: getWslIcon(distroName)
};
if (distroName.includes('Ubuntu')) {
profile.icon = ThemeIcon.asThemeIcon(Codicon.terminalUbuntu);
}
else if (distroName.includes('Debian')) {
profile.icon = ThemeIcon.asThemeIcon(Codicon.terminalDebian);
} else {
profile.icon = ThemeIcon.asThemeIcon(Codicon.terminalLinux);
}
// Add the profile
profiles.push(profile);
}
return profiles;
}
function getWslIcon(distroName: string): ThemeIcon {
if (distroName.includes('Ubuntu')) {
return ThemeIcon.asThemeIcon(Codicon.terminalUbuntu);
} else if (distroName.includes('Debian')) {
return ThemeIcon.asThemeIcon(Codicon.terminalDebian);
} else {
return ThemeIcon.asThemeIcon(Codicon.terminalLinux);
}
}
async function detectAvailableUnixProfiles(
fsProvider: IFsProvider,
logService?: ILogService,

View File

@ -1247,11 +1247,12 @@ declare module 'vscode' {
*
* *Note* that an UTF-8 encoder is used to create bytes for the string.
*
* @param value A string/
* @param value A string.
* @param mime Optional MIME type, defaults to `text/plain`.
* @param metadata Optional metadata.
* @returns A new output item object.
*/
static text(value: string, mime?: string): NotebookCellOutputItem;
static text(value: string, mime?: string, metadata?: { [key: string]: any }): NotebookCellOutputItem;
/**
* Factory function to create a `NotebookCellOutputItem` from
@ -1263,46 +1264,40 @@ declare module 'vscode' {
*
* @param value A JSON-stringifyable value.
* @param mime Optional MIME type, defaults to `application/json`
* @param metadata Optional metadata.
* @returns A new output item object.
*/
static json(value: any, mime?: string): NotebookCellOutputItem;
/**
* Factory function to create a `NotebookCellOutputItem` from bytes.
*
* @param value An array of unsigned 8-bit integers.
* @param mime Optional MIME type, defaults to `application/octet-stream`.
* @returns A new output item object.
*/
//todo@API better names: bytes, raw, buffer?
static bytes(value: Uint8Array, mime?: string): NotebookCellOutputItem;
static json(value: any, mime?: string, metadata?: { [key: string]: any }): NotebookCellOutputItem;
/**
* Factory function to create a `NotebookCellOutputItem` that uses
* uses the `application/vnd.code.notebook.stdout` mime type.
*
* @param value A string.
* @param metadata Optional metadata.
* @returns A new output item object.
*/
static stdout(value: string): NotebookCellOutputItem;
static stdout(value: string, metadata?: { [key: string]: any }): NotebookCellOutputItem;
/**
* Factory function to create a `NotebookCellOutputItem` that uses
* uses the `application/vnd.code.notebook.stderr` mime type.
*
* @param value A string.
* @param metadata Optional metadata.
* @returns A new output item object.
*/
static stderr(value: string): NotebookCellOutputItem;
static stderr(value: string, metadata?: { [key: string]: any }): NotebookCellOutputItem;
/**
* Factory function to create a `NotebookCellOutputItem` that uses
* uses the `application/vnd.code.notebook.error` mime type.
*
* @param value An error object.
* @param metadata Optional metadata.
* @returns A new output item object.
*/
static error(value: Error): NotebookCellOutputItem;
static error(value: Error, metadata?: { [key: string]: any }): NotebookCellOutputItem;
/**
* The mime type which determines how the {@link NotebookCellOutputItem.value `value`}-property
@ -1314,21 +1309,26 @@ declare module 'vscode' {
mime: string;
/**
* The value of this output item. Must always be an array of unsigned 8-bit integers.
* The data of this output item. Must always be an array of unsigned 8-bit integers.
*/
//todo@API only Unit8Array
value: Uint8Array | unknown;
data: Uint8Array;
/**
* @deprecated
*/
value: unknown;
//todo@API
metadata?: { [key: string]: any };
/**
* Create a new notbook cell output item.
*
* @param data The value of the output item.
* @param mime The mime type of the output item.
* @param value The value of the output item.
* @param metadata Optional metadata for this output item.
*/
constructor(mime: string, value: Uint8Array | unknown, metadata?: { [key: string]: any });
constructor(data: Uint8Array, mime: string, metadata?: { [key: string]: any });
}
// @jrieken transient
@ -1563,6 +1563,9 @@ declare module 'vscode' {
/**
* The identifier of this notebook controller.
*
* _Note_ that controllers are remembered by their identifier and that extensions should use
* stable identifiers across sessions.
*/
readonly id: string;
@ -1654,6 +1657,7 @@ declare module 'vscode' {
* @param cell The notebook cell for which to create the execution.
* @returns A notebook cell execution.
*/
// todo@API rename to NotebookCellExecution
createNotebookCellExecutionTask(cell: NotebookCell): NotebookCellExecutionTask;
// todo@API find a better name than "preloads"

View File

@ -350,7 +350,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
public getDefaultShellArgs(useAutomationShell: boolean): string[] | string {
const profile = useAutomationShell ? this._defaultAutomationProfile : this._defaultProfile;
return profile?.args || [''];
return profile?.args || [];
}
public createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal {

View File

@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
import { VSBuffer } from 'vs/base/common/buffer';
import * as htmlContent from 'vs/base/common/htmlContent';
import { DisposableStore } from 'vs/base/common/lifecycle';
import * as marked from 'vs/base/common/marked/marked';
@ -1536,9 +1535,9 @@ export namespace NotebookCellOutputItem {
export function from(item: types.NotebookCellOutputItem): notebooks.IOutputItemDto {
let value: unknown;
let valueBytes: number[] | undefined;
if (item.value instanceof Uint8Array) {
if (item.data instanceof Uint8Array) {
//todo@jrieken this HACKY and SLOW... hoist VSBuffer instead
valueBytes = Array.from(item.value);
valueBytes = Array.from(item.data);
} else {
value = item.value;
}
@ -1551,15 +1550,13 @@ export namespace NotebookCellOutputItem {
}
export function to(item: notebooks.IOutputItemDto): types.NotebookCellOutputItem {
let value: Uint8Array | unknown;
if (item.value instanceof VSBuffer) {
value = item.value.buffer;
let value: Uint8Array | any;
if (Array.isArray(item.valueBytes)) {
value = new Uint8Array(item.valueBytes);
} else {
value = item.value;
}
return new types.NotebookCellOutputItem(item.mime, value, item.metadata);
return new types.NotebookCellOutputItem(value, item.mime, item.metadata);
}
}

View File

@ -3112,44 +3112,50 @@ export class NotebookCellOutputItem {
return typeof (<vscode.NotebookCellOutputItem>obj).mime === 'string';
}
static error(err: Error | { name: string, message?: string, stack?: string }): NotebookCellOutputItem {
static error(err: Error | { name: string, message?: string, stack?: string }, metadata?: { [key: string]: any }): NotebookCellOutputItem {
const obj = {
name: err.name,
message: err.message,
stack: err.stack
};
return NotebookCellOutputItem.json(obj, 'application/vnd.code.notebook.error');
return NotebookCellOutputItem.json(obj, 'application/vnd.code.notebook.error', metadata);
}
static stdout(value: string): NotebookCellOutputItem {
return NotebookCellOutputItem.text(value, 'application/vnd.code.notebook.stdout');
static stdout(value: string, metadata?: { [key: string]: any }): NotebookCellOutputItem {
return NotebookCellOutputItem.text(value, 'application/vnd.code.notebook.stdout', metadata);
}
static stderr(value: string): NotebookCellOutputItem {
return NotebookCellOutputItem.text(value, 'application/vnd.code.notebook.stderr');
static stderr(value: string, metadata?: { [key: string]: any }): NotebookCellOutputItem {
return NotebookCellOutputItem.text(value, 'application/vnd.code.notebook.stderr', metadata);
}
static bytes(value: Uint8Array, mime: string = 'application/octet-stream'): NotebookCellOutputItem {
return new NotebookCellOutputItem(mime, value);
static bytes(value: Uint8Array, mime: string = 'application/octet-stream', metadata?: { [key: string]: any }): NotebookCellOutputItem {
return new NotebookCellOutputItem(value, mime, metadata);
}
static #encoder = new TextEncoder();
static text(value: string, mime: string = 'text/plain'): NotebookCellOutputItem {
static text(value: string, mime: string = 'text/plain', metadata?: { [key: string]: any }): NotebookCellOutputItem {
const bytes = NotebookCellOutputItem.#encoder.encode(String(value));
return new NotebookCellOutputItem(mime, bytes);
return new NotebookCellOutputItem(bytes, mime, metadata);
}
static json(value: any, mime: string = 'application/json'): NotebookCellOutputItem {
static json(value: any, mime: string = 'application/json', metadata?: { [key: string]: any }): NotebookCellOutputItem {
const rawStr = JSON.stringify(value, undefined, '\t');
return NotebookCellOutputItem.text(rawStr, mime);
return NotebookCellOutputItem.text(rawStr, mime, metadata);
}
/** @deprecated */
public value: Uint8Array | unknown; // JSON'able
constructor(
public data: Uint8Array,
public mime: string,
public value: Uint8Array | unknown, // JSON'able
public metadata?: Record<string, any>
public metadata?: { [key: string]: any }
) {
if (!(data instanceof Uint8Array)) {
this.value = data;
}
if (isFalsyOrWhitespace(this.mime)) {
throw new Error('INVALID mime type, must not be empty or falsy');
}

View File

@ -192,6 +192,6 @@ export const notebooksExtensionPoint = ExtensionsRegistry.registerExtensionPoint
export const notebookRendererExtensionPoint = ExtensionsRegistry.registerExtensionPoint<INotebookRendererContribution[]>(
{
extensionPoint: 'notebookOutputRenderer',
extensionPoint: 'notebookRenderer',
jsonSchema: notebookRendererContribution
});

View File

@ -425,7 +425,7 @@
overflow: hidden;
}
.monaco-workbench .notebookOverlay.cell-statusbar-hidden .cell-statusbar-container {
.monaco-workbench .notebookOverlay .cell-statusbar-hidden .cell-statusbar-container {
display: none;
}

View File

@ -30,7 +30,7 @@ import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEd
import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { NotebookService } from 'vs/workbench/contrib/notebook/browser/notebookServiceImpl';
import { CellKind, CellToolbarLocKey, CellToolbarVisibility, CellUri, DisplayOrderKey, UndoRedoPerCell, ExperimentalUseMarkdownRenderer, getCellUndoRedoComparisonKey, IResolvedNotebookEditorModel, NotebookDocumentBackupData, NotebookTextDiffEditorPreview, NotebookWorkingCopyTypeIdentifier, ShowCellStatusBarKey, CompactView, FocusIndicator, InsertToolbarPosition, GlobalToolbar, ConsolidatedOutputButton, ShowFoldingControls, DragAndDropEnabled } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellKind, CellToolbarLocKey, CellToolbarVisibility, CellUri, DisplayOrderKey, UndoRedoPerCell, ExperimentalUseMarkdownRenderer, getCellUndoRedoComparisonKey, IResolvedNotebookEditorModel, NotebookDocumentBackupData, NotebookTextDiffEditorPreview, NotebookWorkingCopyTypeIdentifier, ShowCellStatusBarKey, CompactView, FocusIndicator, InsertToolbarPosition, GlobalToolbar, ConsolidatedOutputButton, ShowFoldingControls, DragAndDropEnabled, ShowCellStatusBarAfterExecuteKey } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
@ -582,6 +582,12 @@ configurationRegistry.registerConfiguration({
default: true,
tags: ['notebookLayout']
},
[ShowCellStatusBarAfterExecuteKey]: {
description: nls.localize('notebook.showCellStatusbarAfterExecute.description', "Whether the cell status bar should be shown after the cell has been executed."),
type: 'boolean',
default: false,
tags: ['notebookLayout']
},
[NotebookTextDiffEditorPreview]: {
description: nls.localize('notebook.diff.enablePreview.description', "Whether to use the enhanced text diff editor for notebook."),
type: 'boolean',

View File

@ -235,6 +235,7 @@ export interface MarkdownCellLayoutInfo {
readonly fontInfo: FontInfo | null;
readonly editorWidth: number;
readonly editorHeight: number;
readonly previewHeight: number;
readonly bottomToolbarOffset: number;
readonly totalHeight: number;
}
@ -242,6 +243,8 @@ export interface MarkdownCellLayoutInfo {
export interface MarkdownCellLayoutChangeEvent {
font?: FontInfo;
outerWidth?: number;
editorHeight?: number;
previewHeight?: number;
totalHeight?: number;
}

View File

@ -511,9 +511,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
const cellToolbarLocation = this._notebookOptions.computeCellToolbarLocation(this.viewModel?.viewType);
this._overlayContainer.classList.add(`cell-title-toolbar-${cellToolbarLocation}`);
const showCellStatusBar = this._notebookOptions.getLayoutConfiguration().showCellStatusBar;
this._overlayContainer.classList.toggle('cell-statusbar-hidden', !showCellStatusBar);
const cellToolbarInteraction = this._notebookOptions.getLayoutConfiguration().cellToolbarInteraction;
let cellToolbarInteractionState = 'hover';
this._overlayContainer.classList.remove('cell-toolbar-hover');
@ -555,6 +552,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
cellRunGutter,
cellBottomMargin,
codeCellLeftMargin,
markdownCellGutter,
markdownCellLeftMargin,
markdownCellBottomMargin,
markdownCellTopMargin,
@ -565,7 +563,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
focusIndicator,
insertToolbarPosition,
insertToolbarAlignment,
fontSize
fontSize,
focusIndicatorLeftMargin
} = this._notebookOptions.getLayoutConfiguration();
const styleSheets: string[] = [];
@ -643,12 +642,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
height: 100%;
z-index: 10;
}
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left:before {
border-left: 1px solid transparent;
border-right: 1px solid transparent;
border-radius: 2px;
}
`);
// left and right border margins
@ -659,6 +652,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
.monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-right:before {
top: 0px; height: 100%;
}`);
styleSheets.push(`
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-left:before,
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.selected .cell-focus-indicator-left:before {
border-left: 3px solid transparent;
border-radius: 2px;
margin-left: ${focusIndicatorLeftMargin}px;
}`);
}
// between cell insert toolbar
@ -727,7 +728,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .execution-count-label { left: ${codeCellLeftMargin}px; width: ${cellRunGutter}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(`.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator { left: ${(markdownCellLeftMargin - 20) / 2}px; }`);
styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator { left: ${(markdownCellGutter - 20) / 2 + markdownCellLeftMargin}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,
@ -2484,10 +2485,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
const cell = this.getCellById(cellId);
const layoutConfiguration = this._notebookOptions.getLayoutConfiguration();
if (cell && cell instanceof MarkdownCellViewModel) {
if (height + layoutConfiguration.bottomToolbarGap !== cell.layoutInfo.totalHeight) {
this._debug('updateMarkdownCellHeight', cell.handle, height + layoutConfiguration.bottomToolbarGap, isInit);
cell.renderedMarkdownHeight = height;
}
this._debug('updateMarkdownCellHeight', cell.handle, height + layoutConfiguration.bottomToolbarGap, isInit);
cell.renderedMarkdownHeight = height;
}
}

View File

@ -349,13 +349,13 @@ export class NotebookService extends Disposable implements INotebookService {
for (const extension of renderers) {
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`);
extension.collector.error(`Notebook renderer does not specify entry point`);
continue;
}
const id = notebookContribution.id ?? notebookContribution.viewType;
if (!id) {
console.error(`Notebook renderer from ${extension.description.identifier.value} is missing an 'id'`);
extension.collector.error(`Notebook renderer does not specify id-property`);
continue;
}

View File

@ -521,6 +521,8 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
templateData.focusIndicatorBottom.style.top = `${indicatorPostion.bottomIndicatorTop}px`;
templateData.focusIndicatorLeft.style.height = `${indicatorPostion.verticalIndicatorHeight}px`;
templateData.focusIndicatorRight.style.height = `${indicatorPostion.verticalIndicatorHeight}px`;
templateData.container.classList.toggle('cell-statusbar-hidden', element.getEditorStatusbarHeight() === 0);
}
private updateForHover(element: MarkdownCellViewModel, templateData: MarkdownCellRenderTemplate): void {
@ -852,7 +854,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
}
}
private updateForInternalMetadata(element: CodeCellViewModel, templateData: CodeCellRenderTemplate, editorOptions: CellEditorOptions): void {
private updateForInternalMetadata(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void {
if (!this.notebookEditor.hasModel()) {
return;
}
@ -894,6 +896,8 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
templateData.outputContainer.style.top = `${element.layoutInfo.outputContainerOffset}px`;
templateData.outputShowMoreContainer.style.top = `${element.layoutInfo.outputShowMoreContainerOffset}px`;
templateData.dragHandle.style.height = `${element.layoutInfo.totalHeight - layoutInfo.bottomToolbarGap}px`;
templateData.container.classList.toggle('cell-statusbar-hidden', element.getEditorStatusbarHeight() === 0);
}
renderElement(element: CodeCellViewModel, index: number, templateData: CodeCellRenderTemplate, height: number | undefined): void {
@ -959,13 +963,14 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
this.updateForLayout(element, templateData);
}));
this.updateForInternalMetadata(element, templateData, cellEditorOptions);
this.updateForInternalMetadata(element, templateData);
this.updateForHover(element, templateData);
this.updateForFocus(element, templateData);
cellEditorOptions.setLineNumbers(element.lineNumbers);
elementDisposables.add(element.onDidChangeState((e) => {
if (e.internalMetadataChanged) {
this.updateForInternalMetadata(element, templateData, cellEditorOptions);
this.updateForInternalMetadata(element, templateData);
this.updateForLayout(element, templateData);
}
if (e.outputIsHoveredChanged) {

View File

@ -250,6 +250,7 @@ export class StatefulMarkdownCell extends Disposable {
private updateFoldingIconShowClass() {
const showFoldingIcon = this.notebookEditor.notebookOptions.getLayoutConfiguration().showFoldingControls;
this.templateData.foldingIndicator.classList.remove('mouseover', 'always');
this.templateData.foldingIndicator.classList.add(showFoldingIcon);
}

View File

@ -468,8 +468,10 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
text(): string;
json(): any;
bytes(): Uint8Array
data(): Uint8Array;
blob(): Blob;
/** @deprecated */
bytes(): Uint8Array;
}
interface IDestroyCellInfo {
@ -492,7 +494,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
postKernelMessage: (data: unknown) => postNotebookMessage('customKernelMessage', { message: data }),
};
const ttPolicy = window.trustedTypes?.createPolicy('notebookOutputRenderer', {
const ttPolicy = window.trustedTypes?.createPolicy('notebookRenderer', {
createHTML: value => value,
createScript: value => value,
});
@ -643,9 +645,10 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
mime: content.mimeType,
value: content.value,
metadata: content.metadata,
bytes() {
data() {
return content.valueBytes;
},
bytes() { return this.data(); },
text() {
return new TextDecoder().decode(content.valueBytes)
|| String(content.value); //todo@jrieken remove this once `value` is gone!
@ -1038,8 +1041,9 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
outputId: undefined,
text() { return content; },
json() { return undefined; },
bytes() { return new Uint8Array(); },
blob() { return new Blob(); },
bytes() { return this.data(); },
data() { return new TextEncoder().encode(content); },
blob() { return new Blob([this.data()], { type: this.mime }); },
});
}
}();

View File

@ -144,10 +144,8 @@ export abstract class BaseCellViewModel extends Disposable {
this._register(model.onDidChangeInternalMetadata(e => {
this._onDidChangeState.fire({ internalMetadataChanged: true, runStateChanged: e.runStateChanged });
}));
this._register(this._viewContext.notebookOptions.onDidChangeOptions(e => {
if (e.cellStatusBarVisibility || e.insertToolbarPosition) {
if (e.runStateChanged || e.lastRunSuccessChanged) {
// Statusbar visibility may change
this.layoutChange({});
}
}));
@ -160,7 +158,17 @@ export abstract class BaseCellViewModel extends Disposable {
}
getEditorStatusbarHeight() {
return this._viewContext.notebookOptions.computeStatusBarHeight();
return this.statusBarIsVisible() ? this._viewContext.notebookOptions.computeStatusBarHeight() : 0;
}
private statusBarIsVisible(): boolean {
if (this._viewContext.notebookOptions.getLayoutConfiguration().showCellStatusBar) {
return true;
} else if (this._viewContext.notebookOptions.getLayoutConfiguration().showCellStatusBarAfterExecute) {
return typeof this.internalMetadata.lastRunSuccess === 'boolean' || this.internalMetadata.runState !== undefined;
} else {
return false;
}
}
abstract hasDynamicHeight(): boolean;

View File

@ -126,6 +126,12 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
}
}));
this._register(this.viewContext.notebookOptions.onDidChangeOptions(e => {
if (e.cellStatusBarVisibility || e.cellStatusBarAfterExecuteVisibility || e.insertToolbarPosition) {
this.layoutChange({});
}
}));
this._outputCollection = new Array(this.model.outputs.length);
this._layoutInfo = {
@ -152,6 +158,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
const outputShowMoreContainerHeight = state.outputShowMoreContainerHeight ? state.outputShowMoreContainerHeight : this._layoutInfo.outputShowMoreContainerHeight;
let outputTotalHeight = Math.max(this._outputMinHeight, this.metadata.outputCollapsed ? notebookLayoutConfiguration.collapsedIndicatorHeight : this._outputsTop!.getTotalValue());
const originalLayout = this.layoutInfo;
if (!this.metadata.inputCollapsed) {
let newState: CodeCellLayoutState;
let editorHeight: number;
@ -235,10 +242,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
};
}
if (state.editorHeight || state.outputHeight) {
state.totalHeight = true;
}
state.totalHeight = this.layoutInfo.totalHeight !== originalLayout.totalHeight;
state.source = source;
this._fireOnDidChangeLayout(state);

View File

@ -27,33 +27,25 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
return this._layoutInfo;
}
private _previewHeight = 0;
set renderedMarkdownHeight(newHeight: number) {
if (this.getEditState() === CellEditState.Preview) {
const newTotalHeight = newHeight + this.viewContext.notebookOptions.getLayoutConfiguration().bottomToolbarGap; // BOTTOM_CELL_TOOLBAR_GAP;
this.totalHeight = newTotalHeight;
this._previewHeight = newHeight;
this._updateTotalHeight(this._previewHeight + this.viewContext.notebookOptions.getLayoutConfiguration().bottomToolbarGap);
}
}
private set totalHeight(newHeight: number) {
if (newHeight !== this.layoutInfo.totalHeight) {
this.layoutChange({ totalHeight: newHeight });
}
}
private get totalHeight() {
throw new Error('MarkdownCellViewModel.totalHeight is write only');
}
private _editorHeight = 0;
set editorHeight(newHeight: number) {
this._editorHeight = newHeight;
const layoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration();
this.totalHeight = this._editorHeight
this._updateTotalHeight(this._editorHeight
+ layoutConfiguration.markdownCellTopMargin // MARKDOWN_CELL_TOP_MARGIN
+ layoutConfiguration.markdownCellBottomMargin // MARKDOWN_CELL_BOTTOM_MARGIN
+ layoutConfiguration.bottomToolbarGap // BOTTOM_CELL_TOOLBAR_GAP
+ this.viewContext.notebookOptions.computeStatusBarHeight();
+ this.viewContext.notebookOptions.computeStatusBarHeight());
}
get editorHeight() {
@ -116,6 +108,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
this._layoutInfo = {
editorHeight: 0,
previewHeight: 0,
fontInfo: initialNotebookLayoutInfo?.fontInfo || null,
editorWidth: initialNotebookLayoutInfo?.width
? this.viewContext.notebookOptions.computeMarkdownCellEditorWidth(initialNotebookLayoutInfo.width)
@ -133,6 +126,21 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
this._onDidHideInput.fire();
}
}));
this._register(this.viewContext.notebookOptions.onDidChangeOptions(e => {
if (e.cellStatusBarVisibility || e.cellStatusBarAfterExecuteVisibility || e.insertToolbarPosition) {
const layoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration();
if (this.getEditState() === CellEditState.Editing) {
this._updateTotalHeight(this._editorHeight
+ layoutConfiguration.markdownCellTopMargin
+ layoutConfiguration.markdownCellBottomMargin
+ layoutConfiguration.bottomToolbarGap
+ this.viewContext.notebookOptions.computeStatusBarHeight());
} else {
this._updateTotalHeight(this._previewHeight + this.viewContext.notebookOptions.getLayoutConfiguration().bottomToolbarGap);
}
}
}));
}
/**
@ -151,6 +159,12 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
this._onDidChangeState.fire({ foldingStateChanged: true });
}
private _updateTotalHeight(newHeight: number) {
if (newHeight !== this.layoutInfo.totalHeight) {
this.layoutChange({ totalHeight: newHeight });
}
}
layoutChange(state: MarkdownCellLayoutChangeEvent) {
// recompute
if (!this.metadata.inputCollapsed) {
@ -158,10 +172,12 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
? this.viewContext.notebookOptions.computeMarkdownCellEditorWidth(state.outerWidth)
: this._layoutInfo.editorWidth;
const totalHeight = state.totalHeight === undefined ? this._layoutInfo.totalHeight : state.totalHeight;
const previewHeight = this._previewHeight;
this._layoutInfo = {
fontInfo: state.font || this._layoutInfo.fontInfo,
editorWidth,
previewHeight,
editorHeight: this._editorHeight,
bottomToolbarOffset: this.viewContext.notebookOptions.computeBottomToolbarOffset(totalHeight),
totalHeight
@ -178,6 +194,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
fontInfo: state.font || this._layoutInfo.fontInfo,
editorWidth,
editorHeight: this._editorHeight,
previewHeight: this._previewHeight,
bottomToolbarOffset: this.viewContext.notebookOptions.computeBottomToolbarOffset(totalHeight),
totalHeight
};
@ -193,6 +210,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
this._layoutInfo = {
fontInfo: this._layoutInfo.fontInfo,
editorWidth: this._layoutInfo.editorWidth,
previewHeight: this._layoutInfo.previewHeight,
bottomToolbarOffset: this._layoutInfo.bottomToolbarOffset,
totalHeight: totalHeight,
editorHeight: this._editorHeight

View File

@ -59,13 +59,14 @@ export class NotebookCellTextModel extends Disposable implements ICell {
set internalMetadata(newInternalMetadata: NotebookCellInternalMetadata) {
const runStateChanged = this._internalMetadata.runState !== newInternalMetadata.runState;
const lastRunSuccessChanged = this._internalMetadata.lastRunSuccess !== newInternalMetadata.lastRunSuccess;
newInternalMetadata = {
...newInternalMetadata,
...{ runStartTimeAdjustment: computeRunStartTimeAdjustment(this._internalMetadata, newInternalMetadata) }
};
this._internalMetadata = newInternalMetadata;
this._hash = null;
this._onDidChangeInternalMetadata.fire({ runStateChanged });
this._onDidChangeInternalMetadata.fire({ runStateChanged, lastRunSuccessChanged });
}
get language() {

View File

@ -189,6 +189,7 @@ export interface ICellOutput {
export interface CellInternalMetadataChangedEvent {
readonly runStateChanged?: boolean;
readonly lastRunSuccessChanged?: boolean;
}
export interface ICell {
@ -903,6 +904,7 @@ export const DisplayOrderKey = 'notebook.displayOrder';
export const CellToolbarLocKey = 'notebook.cellToolbarLocation';
export const CellToolbarVisibility = 'notebook.cellToolbarVisibility';
export const ShowCellStatusBarKey = 'notebook.showCellStatusBar';
export const ShowCellStatusBarAfterExecuteKey = 'notebook.showCellStatusBarAfterExecute';
export const NotebookTextDiffEditorPreview = 'notebook.diff.enablePreview';
export const ExperimentalUseMarkdownRenderer = 'notebook.experimental.useMarkdownRenderer';
export const ExperimentalInsertToolbarAlignment = 'notebook.experimental.insertToolbarAlignment';

View File

@ -6,7 +6,7 @@
import { Emitter } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CellToolbarLocKey, CellToolbarVisibility, CompactView, ConsolidatedOutputButton, DragAndDropEnabled, FocusIndicator, GlobalToolbar, ExperimentalInsertToolbarAlignment, InsertToolbarPosition, ShowFoldingControls, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellToolbarLocKey, CellToolbarVisibility, CompactView, ConsolidatedOutputButton, DragAndDropEnabled, ExperimentalInsertToolbarAlignment, FocusIndicator, GlobalToolbar, InsertToolbarPosition, ShowCellStatusBarAfterExecuteKey, ShowCellStatusBarKey, ShowFoldingControls } from 'vs/workbench/contrib/notebook/common/notebookCommon';
const SCROLLABLE_ELEMENT_PADDING_TOP = 18;
@ -32,6 +32,7 @@ export interface NotebookLayoutConfiguration {
cellOutputPadding: number;
codeCellLeftMargin: number;
markdownCellLeftMargin: number;
markdownCellGutter: number;
markdownCellTopMargin: number;
markdownCellBottomMargin: number;
markdownPreviewPadding: number;
@ -43,6 +44,7 @@ export interface NotebookLayoutConfiguration {
editorBottomPaddingWithoutStatusBar: number;
collapsedIndicatorHeight: number;
showCellStatusBar: boolean;
showCellStatusBarAfterExecute: boolean;
cellStatusBarHeight: number;
cellToolbarLocation: string | { [key: string]: string };
cellToolbarInteraction: string;
@ -55,10 +57,12 @@ export interface NotebookLayoutConfiguration {
showFoldingControls: 'always' | 'mouseover';
dragAndDropEnabled: boolean;
fontSize: number;
focusIndicatorLeftMargin: number;
}
interface NotebookOptionsChangeEvent {
cellStatusBarVisibility?: boolean;
cellStatusBarAfterExecuteVisibility?: boolean;
cellToolbarLocation?: boolean;
cellToolbarInteraction?: boolean;
editorTopPadding?: boolean;
@ -78,15 +82,19 @@ const defaultConfigConstants = {
cellRunGutter: 32,
markdownCellTopMargin: 8,
markdownCellBottomMargin: 8,
markdownCellLeftMargin: 32,
markdownCellLeftMargin: 0,
markdownCellGutter: 32,
focusIndicatorLeftMargin: 4
};
const compactConfigConstants = {
codeCellLeftMargin: 0,
codeCellLeftMargin: 8,
cellRunGutter: 32,
markdownCellTopMargin: 6,
markdownCellBottomMargin: 6,
markdownCellLeftMargin: 32,
markdownCellLeftMargin: 8,
markdownCellGutter: 32,
focusIndicatorLeftMargin: 4
};
export class NotebookOptions {
@ -97,6 +105,7 @@ export class NotebookOptions {
constructor(private readonly configurationService: IConfigurationService) {
const showCellStatusBar = this.configurationService.getValue<boolean>(ShowCellStatusBarKey);
const showCellStatusBarAfterExecute = this.configurationService.getValue<boolean>(ShowCellStatusBarAfterExecuteKey);
const globalToolbar = this.configurationService.getValue<boolean | undefined>(GlobalToolbar) ?? false;
const consolidatedOutputButton = this.configurationService.getValue<boolean | undefined>(ConsolidatedOutputButton) ?? true;
const dragAndDropEnabled = this.configurationService.getValue<boolean | undefined>(DragAndDropEnabled) ?? true;
@ -127,6 +136,7 @@ export class NotebookOptions {
editorBottomPaddingWithoutStatusBar: 12,
collapsedIndicatorHeight: 24,
showCellStatusBar,
showCellStatusBarAfterExecute,
globalToolbar,
consolidatedOutputButton,
dragAndDropEnabled,
@ -154,6 +164,7 @@ export class NotebookOptions {
private _updateConfiguration(e: IConfigurationChangeEvent) {
const cellStatusBarVisibility = e.affectsConfiguration(ShowCellStatusBarKey);
const cellStatusBarAfterExecuteVisibility = e.affectsConfiguration(ShowCellStatusBarAfterExecuteKey);
const cellToolbarLocation = e.affectsConfiguration(CellToolbarLocKey);
const cellToolbarInteraction = e.affectsConfiguration(CellToolbarVisibility);
const compactView = e.affectsConfiguration(CompactView);
@ -168,6 +179,7 @@ export class NotebookOptions {
if (
!cellStatusBarVisibility
&& !cellStatusBarAfterExecuteVisibility
&& !cellToolbarLocation
&& !cellToolbarInteraction
&& !compactView
@ -188,6 +200,10 @@ export class NotebookOptions {
configuration.showCellStatusBar = this.configurationService.getValue<boolean>(ShowCellStatusBarKey);
}
if (cellStatusBarAfterExecuteVisibility) {
configuration.showCellStatusBarAfterExecute = this.configurationService.getValue<boolean>(ShowCellStatusBarAfterExecuteKey);
}
if (cellToolbarLocation) {
configuration.cellToolbarLocation = this.configurationService.getValue<string | { [key: string]: string }>(CellToolbarLocKey);
}
@ -244,6 +260,7 @@ export class NotebookOptions {
// trigger event
this._onDidChangeOptions.fire({
cellStatusBarVisibility,
cellStatusBarAfterExecuteVisibility,
cellToolbarLocation,
cellToolbarInteraction,
compactView,
@ -329,11 +346,7 @@ export class NotebookOptions {
}
computeStatusBarHeight(): number {
if (this._layoutConfiguration.showCellStatusBar) {
return this._layoutConfiguration.cellStatusBarHeight;
} else {
return 0;
}
return this._layoutConfiguration.cellStatusBarHeight;
}
computeCellToolbarLocation(viewType?: string): 'right' | 'left' | 'hidden' {
@ -384,7 +397,7 @@ export class NotebookOptions {
outputNodePadding: this._layoutConfiguration.cellOutputPadding,
outputNodeLeftPadding: this._layoutConfiguration.cellOutputPadding,
previewNodePadding: this._layoutConfiguration.markdownPreviewPadding,
markdownLeftMargin: this._layoutConfiguration.markdownCellLeftMargin,
markdownLeftMargin: this._layoutConfiguration.markdownCellGutter + this._layoutConfiguration.markdownCellLeftMargin,
leftMargin: this._layoutConfiguration.codeCellLeftMargin,
rightMargin: this._layoutConfiguration.cellRightMargin,
runGutter: this._layoutConfiguration.cellRunGutter,

View File

@ -92,7 +92,25 @@ export class SettingsEditor2 extends EditorPane {
private static CONFIG_SCHEMA_UPDATE_DELAYER = 500;
private static readonly SUGGESTIONS: string[] = [
`@${MODIFIED_SETTING_TAG}`, '@tag:usesOnlineServices', '@tag:sync', `@tag:${REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG}`, `@${ID_SETTING_TAG}`, `@${EXTENSION_SETTING_TAG}`, `@${FEATURE_SETTING_TAG}scm`, `@${FEATURE_SETTING_TAG}explorer`, `@${FEATURE_SETTING_TAG}search`, `@${FEATURE_SETTING_TAG}debug`, `@${FEATURE_SETTING_TAG}extensions`, `@${FEATURE_SETTING_TAG}terminal`, `@${FEATURE_SETTING_TAG}task`, `@${FEATURE_SETTING_TAG}problems`, `@${FEATURE_SETTING_TAG}output`, `@${FEATURE_SETTING_TAG}comments`, `@${FEATURE_SETTING_TAG}remote`, `@${FEATURE_SETTING_TAG}timeline`
`@${MODIFIED_SETTING_TAG}`,
'@tag:usesOnlineServices',
'@tag:sync',
`@tag:${REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG}`,
`@${ID_SETTING_TAG}`,
`@${EXTENSION_SETTING_TAG}`,
`@${FEATURE_SETTING_TAG}scm`,
`@${FEATURE_SETTING_TAG}explorer`,
`@${FEATURE_SETTING_TAG}search`,
`@${FEATURE_SETTING_TAG}debug`,
`@${FEATURE_SETTING_TAG}extensions`,
`@${FEATURE_SETTING_TAG}terminal`,
`@${FEATURE_SETTING_TAG}task`,
`@${FEATURE_SETTING_TAG}problems`,
`@${FEATURE_SETTING_TAG}output`,
`@${FEATURE_SETTING_TAG}comments`,
`@${FEATURE_SETTING_TAG}remote`,
`@${FEATURE_SETTING_TAG}timeline`,
`@${FEATURE_SETTING_TAG}notebook`,
];
private static shouldSettingUpdateFast(type: SettingValueType | SettingValueType[]): boolean {

View File

@ -701,7 +701,7 @@ export class SearchResultModel extends SettingsTreeModel {
const isRemote = !!this.environmentService.remoteAuthority;
this.root.children = this.root.children
.filter(child => child instanceof SettingsTreeSettingElement && child.matchesAllTags(this._viewState.tagFilters) && child.matchesScope(this._viewState.settingsTarget, isRemote) && child.matchesAnyExtension(this._viewState.extensionFilters) && child.matchesAnyId(this._viewState.idFilters) && (this.containsValidFeature() ? child.matchesAnyFeature(this._viewState.featureFilters) : true));
.filter(child => child instanceof SettingsTreeSettingElement && child.matchesAllTags(this._viewState.tagFilters) && child.matchesScope(this._viewState.settingsTarget, isRemote) && child.matchesAnyExtension(this._viewState.extensionFilters) && child.matchesAnyId(this._viewState.idFilters) && child.matchesAnyFeature(this._viewState.featureFilters));
if (this.newExtensionSearchResults && this.newExtensionSearchResults.filterMatches.length) {
const resultExtensionIds = this.newExtensionSearchResults.filterMatches
@ -715,22 +715,6 @@ export class SearchResultModel extends SettingsTreeModel {
}
}
private containsValidFeature(): boolean {
if (!this._viewState.featureFilters || !this._viewState.featureFilters.size || !tocData.children) {
return false;
}
const features = tocData.children.find(child => child.id === 'features');
if (features && features.children) {
return Array.from(this._viewState.featureFilters).some(filter => {
return features.children?.find(feature => 'features/' + filter === feature.id);
});
} else {
return false;
}
}
private getFlatSettings(): ISetting[] {
const flatSettings: ISetting[] = [];
arrays.coalesce(this.getUniqueResults())

View File

@ -25,7 +25,7 @@ import { isWeb } from 'vs/base/common/platform';
import { once } from 'vs/base/common/functional';
import { truncate } from 'vs/base/common/strings';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { getVirtualWorkspaceLocation } from 'vs/platform/remote/common/remoteHosts';
import { getRemoteName, getVirtualWorkspaceLocation, getVirtualWorkspaceScheme } from 'vs/platform/remote/common/remoteHosts';
import { getCodiconAriaLabel } from 'vs/base/common/codicons';
import { ILogService } from 'vs/platform/log/common/log';
import { ReloadWindowAction } from 'vs/workbench/browser/actions/windowActions';
@ -53,6 +53,9 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr
private remoteMenuActionsGroups: ActionGroup[] | undefined;
private readonly remoteAuthority = this.environmentService.remoteAuthority;
private virtualWorkspaceScheme: string | undefined = undefined;
private connectionState: 'initializing' | 'connected' | 'reconnecting' | 'disconnected' | undefined = undefined;
private readonly connectionStateContextKey = new RawContextKey<'' | 'initializing' | 'disconnected' | 'connected'>('remoteConnectionState', '').bindTo(this.contextKeyService);
@ -80,6 +83,8 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr
if (this.remoteAuthority) {
this.connectionState = 'initializing';
this.connectionStateContextKey.set(this.connectionState);
} else {
this.updateVirtualWorkspaceScheme();
}
this.registerActions();
@ -107,7 +112,7 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr
});
// Close Remote Connection
if (RemoteStatusIndicator.SHOW_CLOSE_REMOTE_COMMAND_ID && this.remoteAuthority) {
if (RemoteStatusIndicator.SHOW_CLOSE_REMOTE_COMMAND_ID) {
registerAction2(class extends Action2 {
constructor() {
super({
@ -117,17 +122,18 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr
f1: true
});
}
run = () => that.remoteAuthority && that.hostService.openWindow({ forceReuseWindow: true, remoteAuthority: null });
});
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '6_close',
command: {
id: RemoteStatusIndicator.CLOSE_REMOTE_COMMAND_ID,
title: nls.localize({ key: 'miCloseRemote', comment: ['&& denotes a mnemonic'] }, "Close Re&&mote Connection")
},
order: 3.5
run = () => that.hostService.openWindow({ forceReuseWindow: true, remoteAuthority: null });
});
if (this.remoteAuthority) {
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '6_close',
command: {
id: RemoteStatusIndicator.CLOSE_REMOTE_COMMAND_ID,
title: nls.localize({ key: 'miCloseRemote', comment: ['&& denotes a mnemonic'] }, "Close Re&&mote Connection")
},
order: 3.5
});
}
}
if (this.extensionGalleryService.isEnabled()) {
@ -196,10 +202,17 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr
}));
}
} else {
this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => this.updateRemoteStatusIndicator()));
this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => {
this.updateVirtualWorkspaceScheme();
this.updateRemoteStatusIndicator();
}));
}
}
private updateVirtualWorkspaceScheme() {
this.virtualWorkspaceScheme = getVirtualWorkspaceScheme(this.workspaceContextService.getWorkspace());
}
private async updateWhenInstalledExtensionsRegistered(): Promise<void> {
await this.extensionService.whenInstalledExtensionsRegistered();
@ -280,13 +293,13 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr
this.renderRemoteStatusIndicator(`$(remote) ${truncate(hostLabel, RemoteStatusIndicator.REMOTE_STATUS_LABEL_MAX_LENGTH)}`, nls.localize('host.tooltip', "Editing on {0}", hostLabel));
}
return;
}
// Workspace with label: indicate editing source
const workspaceLabel = this.getWorkspaceLabel();
if (workspaceLabel) {
this.renderRemoteStatusIndicator(`$(remote) ${truncate(workspaceLabel, RemoteStatusIndicator.REMOTE_STATUS_LABEL_MAX_LENGTH)}`, nls.localize('workspace.tooltip', "Editing on {0}", workspaceLabel));
return;
} else if (this.virtualWorkspaceScheme) {
// Workspace with label: indicate editing source
const workspaceLabel = this.getWorkspaceLabel();
if (workspaceLabel) {
this.renderRemoteStatusIndicator(`$(remote) ${truncate(workspaceLabel, RemoteStatusIndicator.REMOTE_STATUS_LABEL_MAX_LENGTH)}`, nls.localize('workspace.tooltip', "Editing on {0}", workspaceLabel));
return;
}
}
// Remote actions: offer menu
@ -342,11 +355,37 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr
return undefined;
};
const matchCurrentRemote = () => {
if (this.remoteAuthority) {
return new RegExp(`^remote_\\d\\d_${getRemoteName(this.remoteAuthority)}_`);
} else if (this.virtualWorkspaceScheme) {
if (this.virtualWorkspaceScheme === 'vscode-vfs') {
return new RegExp(`^remote_\\d\\d_vfs_`);
} else {
return new RegExp(`^virtualfs_\\d\\d_${this.virtualWorkspaceScheme}_`);
}
}
return undefined;
};
const computeItems = () => {
const actionGroups = this.getRemoteMenuActions(true);
let actionGroups = this.getRemoteMenuActions(true);
const items: (IQuickPickItem | IQuickPickSeparator)[] = [];
const currentRemoteMatcher = matchCurrentRemote();
if (currentRemoteMatcher) {
// commands for the current remote go first
actionGroups = actionGroups.sort((g1, g2) => {
const isCurrentRemote1 = currentRemoteMatcher.test(g1[0]);
const isCurrentRemote2 = currentRemoteMatcher.test(g2[0]);
if (isCurrentRemote1 !== isCurrentRemote2) {
return isCurrentRemote1 ? -1 : 1;
}
return g1[0].localeCompare(g2[0]);
});
}
let lastCategoryName: string | undefined = undefined;
for (let actionGroup of actionGroups) {
@ -371,25 +410,38 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr
}
}
if (RemoteStatusIndicator.SHOW_CLOSE_REMOTE_COMMAND_ID && this.remoteAuthority) {
if (items.length) {
items.push({ type: 'separator' });
}
if (RemoteStatusIndicator.SHOW_CLOSE_REMOTE_COMMAND_ID) {
if (this.remoteAuthority) {
if (items.length) {
items.push({ type: 'separator' });
}
items.push({
type: 'item',
id: RemoteStatusIndicator.CLOSE_REMOTE_COMMAND_ID,
label: nls.localize('closeRemote.title', 'Close Remote Connection')
});
if (this.connectionState === 'disconnected') {
items.push({
type: 'item',
id: ReloadWindowAction.ID,
label: nls.localize('reloadWindow', 'Reload Window')
id: RemoteStatusIndicator.CLOSE_REMOTE_COMMAND_ID,
label: nls.localize('closeRemoteConnection.title', 'Close Remote Connection')
});
if (this.connectionState === 'disconnected') {
items.push({
type: 'item',
id: ReloadWindowAction.ID,
label: nls.localize('reloadWindow', 'Reload Window')
});
}
} else if (this.virtualWorkspaceScheme) {
if (items.length) {
items.push({ type: 'separator' });
}
items.push({
type: 'item',
id: RemoteStatusIndicator.CLOSE_REMOTE_COMMAND_ID,
label: nls.localize('closeRemoteWindow.title', 'Close Remote')
});
}
}
if (!this.remoteAuthority && this.extensionGalleryService.isEnabled()) {
items.push({
type: 'separator'

View File

@ -17,6 +17,7 @@ import type { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl';
import { ITerminalStatusList } from 'vs/workbench/contrib/terminal/browser/terminalStatusList';
import { ICompleteTerminalConfiguration } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel';
import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
import { IEditableData } from 'vs/workbench/common/views';
export const ITerminalService = createDecorator<ITerminalService>('terminalService');
export const ITerminalInstanceService = createDecorator<ITerminalInstanceService>('terminalInstanceService');
@ -206,6 +207,8 @@ export interface ITerminalService {
requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): Promise<ITerminalLaunchError | undefined>;
isAttachedToTerminal(remoteTerm: IRemoteTerminalAttachTarget): boolean;
getEditableData(stat: ITerminalInstance): IEditableData | undefined;
setEditable(stat: ITerminalInstance, data: IEditableData | null): Promise<void>;
}
export interface IRemoteTerminalService extends IOffProcessTerminalService {
@ -600,12 +603,13 @@ export interface ITerminalInstance {
registerLinkProvider(provider: ITerminalExternalLinkProvider): IDisposable;
/**
* Triggers a quick pick to rename this terminal.
* Sets the terminal name to the provided title or triggers a quick pick
* to take user input.
*/
rename(): Promise<void>;
rename(title?: string): Promise<void>;
/**
* Triggers a quick pick to rename this terminal.
* Triggers a quick pick to change the icon of this terminal.
*/
changeIcon(): Promise<void>;

View File

@ -23,7 +23,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ILabelService } from 'vs/platform/label/common/label';
import { IListService } from 'vs/platform/list/browser/listService';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IPickOptions, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { ILocalTerminalService, ITerminalProfile, TerminalSettingId, TitleEventSource } from 'vs/platform/terminal/common/terminal';
@ -811,9 +811,30 @@ export function registerTerminalActions() {
});
}
async run(accessor: ServicesAccessor) {
return getSelectedInstances(accessor)?.[0].rename();
const terminalService = accessor.get(ITerminalService);
const notificationService = accessor.get(INotificationService);
const instance = getSelectedInstances(accessor)?.[0];
if (!instance) {
return;
}
await terminalService.setEditable(instance, {
validationMessage: value => validateTerminalName(value),
onFinish: async (value, success) => {
if (success) {
try {
await instance.rename(value);
} catch (e) {
notificationService.error(e);
}
}
await terminalService.setEditable(instance, null);
}
});
}
});
registerAction2(class extends Action2 {
constructor() {
super({
@ -1830,3 +1851,14 @@ function focusNext(accessor: ServicesAccessor): void {
const listService = accessor.get(IListService);
listService.lastFocusedList?.focusNext();
}
export function validateTerminalName(name: string): { content: string, severity: Severity } | null {
if (!name || name.trim().length === 0) {
return {
content: localize('emptyTerminalNameError', "A name must be provided."),
severity: Severity.Error
};
}
return null;
}

View File

@ -775,7 +775,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
private _initDragAndDrop(container: HTMLElement) {
this._dndObserver?.dispose();
const dndController = new TerminalInstanceDropAndDropController(container);
const dndController = new TerminalInstanceDragAndDropController(container);
dndController.onDropTerminal(e => this._onRequestAddInstanceToGroup.fire(e));
dndController.onDropFile(async path => {
const preparedPath = await this._terminalInstanceService.preparePathForTerminalAsync(path, this.shellLaunchConfig.executable, this.title, this.shellType, this.isRemote);
@ -1789,13 +1789,15 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
return this._linkManager.registerExternalLinkProvider(this, provider);
}
async rename() {
const name = await this._quickInputService.input({
value: this.title,
prompt: nls.localize('workbench.action.terminal.rename.prompt', "Enter terminal name"),
});
if (name) {
this.setTitle(name, TitleEventSource.Api);
async rename(title?: string) {
if (!title) {
title = await this._quickInputService.input({
value: this.title,
prompt: nls.localize('workbench.action.terminal.rename.prompt', "Enter terminal name"),
});
}
if (title) {
this.setTitle(title, TitleEventSource.Api);
}
}
@ -1843,7 +1845,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}
}
items.push({ type: 'separator' });
const showAllColorsItem = { label: 'Show all colors' };
const showAllColorsItem = { label: 'Reset to default' };
items.push(showAllColorsItem);
styleElement.textContent = css;
document.body.appendChild(styleElement);
@ -1870,7 +1872,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}
}
class TerminalInstanceDropAndDropController extends Disposable implements IDragAndDropObserverCallbacks {
class TerminalInstanceDragAndDropController extends Disposable implements IDragAndDropObserverCallbacks {
private _dropOverlay?: HTMLElement;
private readonly _onDropFile = new Emitter<string>();

View File

@ -19,7 +19,7 @@ import { IKeyMods, IPickOptions, IQuickInputButton, IQuickInputService, IQuickPi
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ILocalTerminalService, IOffProcessTerminalService, IShellLaunchConfig, ITerminalLaunchError, ITerminalProfile, ITerminalProfileObject, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
import { IEditableData, IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
import { IRemoteTerminalService, ITerminalExternalLinkProvider, ITerminalInstance, ITerminalService, ITerminalGroup, TerminalConnectionState } from 'vs/workbench/contrib/terminal/browser/terminal';
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance';
@ -69,6 +69,8 @@ export class TerminalService implements ITerminalService {
private _localTerminalsInitPromise: Promise<void> | undefined;
private _connectionState: TerminalConnectionState;
private _editable: { instance: ITerminalInstance, data: IEditableData } | undefined;
public get activeGroupIndex(): number { return this._activeGroupIndex; }
public get terminalGroups(): ITerminalGroup[] { return this._terminalGroups; }
public get isProcessSupportRegistered(): boolean { return !!this._processSupportContextKey.get(); }
@ -316,6 +318,25 @@ export class TerminalService implements ITerminalService {
return activeInstance ? activeInstance : this.createTerminal(undefined);
}
async setEditable(instance: ITerminalInstance, data?: IEditableData | null): Promise<void> {
if (!data) {
this._editable = undefined;
} else {
this._editable = { instance: instance, data };
}
const pane = this._viewsService.getActiveViewWithId<TerminalViewPane>(TERMINAL_VIEW_ID);
const isEditing = this._isEditable(instance);
pane?.terminalTabbedView?.setEditable(isEditing);
}
private _isEditable(instance: ITerminalInstance | undefined): boolean {
return !!this._editable && (this._editable.instance === instance || !instance);
}
getEditableData(instance: ITerminalInstance): IEditableData | undefined {
return this._editable && this._editable.instance === instance ? this._editable.data : undefined;
}
requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): Promise<ITerminalLaunchError | undefined> {
// The initial request came from the extension host, no need to wait for it
return new Promise<ITerminalLaunchError | undefined>(callback => {

View File

@ -288,7 +288,7 @@ export class TerminalTabbedView extends Disposable {
const hasText = this._tabListElement.clientWidth > TerminalTabsListSizes.MidpointViewWidth;
this._tabContainer.classList.toggle('has-text', hasText);
this._terminalIsTabsNarrowContextKey.set(!hasText);
this._tabList.render();
this._tabList.refresh();
}
private _addSashListener() {
@ -469,6 +469,13 @@ export class TerminalTabbedView extends Disposable {
];
}
setEditable(isEditing: boolean): void {
if (!isEditing) {
this._tabList.domFocus();
}
return this._tabList.refresh();
}
focusTabs(): void {
if (!this._shouldShowTabs()) {
return;

View File

@ -26,7 +26,7 @@ import { DEFAULT_LABELS_CONTAINER, IResourceLabel, ResourceLabels } from 'vs/wor
import { IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations';
import { IHoverAction, IHoverService } from 'vs/workbench/services/hover/browser/hover';
import Severity from 'vs/base/common/severity';
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IListDragAndDrop, IListDragOverReaction, IListRenderer, ListDragOverEffect } from 'vs/base/browser/ui/list/list';
import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd';
import { disposableTimeout } from 'vs/base/common/async';
@ -34,6 +34,13 @@ import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView';
import { URI } from 'vs/base/common/uri';
import { getColorClass, getIconId, getUriClasses } from 'vs/workbench/contrib/terminal/browser/terminalIcon';
import { Schemas } from 'vs/base/common/network';
import { IEditableData } from 'vs/workbench/common/views';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { once } from 'vs/base/common/functional';
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
const $ = DOM.$;
@ -89,13 +96,13 @@ export class TerminalTabList extends WorkbenchList<ITerminalInstance> {
configurationService,
keybindingService,
);
this._terminalService.onInstancesChanged(() => this.render());
this._terminalService.onGroupsChanged(() => this.render());
this._terminalService.onInstanceTitleChanged(() => this.render());
this._terminalService.onInstanceIconChanged(() => this.render());
this._terminalService.onInstancePrimaryStatusChanged(() => this.render());
this._terminalService.onDidChangeConnectionState(() => this.render());
this._themeService.onDidColorThemeChange(() => this.render());
this._terminalService.onInstancesChanged(() => this.refresh());
this._terminalService.onGroupsChanged(() => this.refresh());
this._terminalService.onInstanceTitleChanged(() => this.refresh());
this._terminalService.onInstanceIconChanged(() => this.refresh());
this._terminalService.onInstancePrimaryStatusChanged(() => this.refresh());
this._terminalService.onDidChangeConnectionState(() => this.refresh());
this._themeService.onDidColorThemeChange(() => this.refresh());
this._terminalService.onActiveInstanceChanged(e => {
if (e) {
const i = this._terminalService.terminalInstances.indexOf(e);
@ -157,10 +164,10 @@ export class TerminalTabList extends WorkbenchList<ITerminalInstance> {
this._decorationsProvider = instantiationService.createInstance(TerminalDecorationsProvider);
_decorationsService.registerDecorationsProvider(this._decorationsProvider);
}
this.render();
this.refresh();
}
render(): void {
refresh(): void {
this.splice(0, this.length, this._terminalService.terminalInstances);
}
@ -182,7 +189,8 @@ class TerminalTabsRenderer implements IListRenderer<ITerminalInstance, ITerminal
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IListService private readonly _listService: IListService,
@IThemeService private readonly _themeService: IThemeService
@IThemeService private readonly _themeService: IThemeService,
@IContextViewService private readonly _contextViewService: IContextViewService
) {
}
@ -231,6 +239,12 @@ class TerminalTabsRenderer implements IListRenderer<ITerminalInstance, ITerminal
}
renderElement(instance: ITerminalInstance, index: number, template: ITerminalTabEntryTemplate): void {
const editableData = this._terminalService.getEditableData(instance);
if (editableData) {
template.label.element.style.display = 'none';
this._renderInputBox(template.label.element.parentElement!, instance, editableData);
return;
}
const group = this._terminalService.getGroupForInstance(instance);
if (!group) {
throw new Error(`Could not find group for instance "${instance.instanceId}"`);
@ -239,6 +253,10 @@ class TerminalTabsRenderer implements IListRenderer<ITerminalInstance, ITerminal
const hasText = !this.shouldHideText();
template.element.classList.toggle('has-text', hasText);
if (template.label.element.style.display = 'none') {
template.label.element.style.display = '';
}
let prefix: string = '';
if (group.terminalInstances.length > 1) {
const terminalIndex = group.terminalInstances.indexOf(instance);
@ -323,6 +341,84 @@ class TerminalTabsRenderer implements IListRenderer<ITerminalInstance, ITerminal
});
}
private _renderInputBox(container: HTMLElement, instance: ITerminalInstance, editableData: IEditableData): IDisposable {
const label = this._labels.create(container);
const value = instance.title || '';
const inputBox = new InputBox(label.element, this._contextViewService, {
validationOptions: {
validation: (value) => {
const message = editableData.validationMessage(value);
if (!message || message.severity !== Severity.Error) {
return null;
}
return {
content: message.content,
formatContent: true,
type: MessageType.ERROR
};
}
},
ariaLabel: localize('terminalInputAriaLabel', "Type terminal name. Press Enter to confirm or Escape to cancel.")
});
const styler = attachInputBoxStyler(inputBox, this._themeService);
inputBox.value = value;
inputBox.focus();
inputBox.select({ start: 0, end: value.length });
const done = once((success: boolean, finishEditing: boolean) => {
inputBox.element.style.display = 'none';
const value = inputBox.value;
dispose(toDispose);
inputBox.element.remove();
if (finishEditing) {
editableData.onFinish(value, success);
}
});
const showInputBoxNotification = () => {
if (inputBox.isInputValid()) {
const message = editableData.validationMessage(inputBox.value);
if (message) {
inputBox.showMessage({
content: message.content,
formatContent: true,
type: message.severity === Severity.Info ? MessageType.INFO : message.severity === Severity.Warning ? MessageType.WARNING : MessageType.ERROR
});
} else {
inputBox.hideMessage();
}
}
};
showInputBoxNotification();
const toDispose = [
inputBox,
DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.KEY_DOWN, (e: IKeyboardEvent) => {
if (e.equals(KeyCode.Enter)) {
done(inputBox.isInputValid(), true);
} else if (e.equals(KeyCode.Escape)) {
done(false, true);
}
}),
DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.KEY_UP, (e: IKeyboardEvent) => {
showInputBoxNotification();
}),
DOM.addDisposableListener(inputBox.inputElement, DOM.EventType.BLUR, () => {
done(inputBox.isInputValid(), true);
}),
label,
styler
];
return toDisposable(() => {
done(false, false);
});
}
disposeElement(instance: ITerminalInstance, index: number, templateData: ITerminalTabEntryTemplate): void {
templateData.elementDispoables?.dispose();
templateData.elementDispoables = undefined;

View File

@ -115,36 +115,36 @@ suite('Workbench - TerminalProfiles', () => {
} as ITestTerminalConfig) as ITerminalConfiguration;
test('should prefer pwsh 7 to Windows PowerShell', async () => {
const fsProvider = createFsProvider([
const expectedPaths = [
'C:\\Program Files\\PowerShell\\7\\pwsh.exe',
'C:\\Sysnative\\WindowsPowerShell\\v1.0\\powershell.exe',
'C:\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'
]);
const profiles = await detectAvailableProfiles(false, buildTestSafeConfigProvider(pwshSourceConfig), fsProvider, undefined, undefined, undefined);
];
const profiles = await detectAvailableProfiles(false, buildTestSafeConfigProvider(pwshSourceConfig), undefined, undefined, undefined, expectedPaths);
const expected = [
{ profileName: 'PowerShell', path: 'C:\\Program Files\\PowerShell\\7\\pwsh.exe', isDefault: true }
];
profilesEqual(profiles, expected);
});
test('should prefer pwsh 7 to pwsh 6', async () => {
const fsProvider = createFsProvider([
const expectedPaths = [
'C:\\Program Files\\PowerShell\\7\\pwsh.exe',
'C:\\Program Files\\PowerShell\\6\\pwsh.exe',
'C:\\Sysnative\\WindowsPowerShell\\v1.0\\powershell.exe',
'C:\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'
]);
const profiles = await detectAvailableProfiles(false, buildTestSafeConfigProvider(pwshSourceConfig), fsProvider, undefined, undefined, undefined);
];
const profiles = await detectAvailableProfiles(false, buildTestSafeConfigProvider(pwshSourceConfig), undefined, undefined, undefined, expectedPaths);
const expected = [
{ profileName: 'PowerShell', path: 'C:\\Program Files\\PowerShell\\7\\pwsh.exe', isDefault: true }
];
profilesEqual(profiles, expected);
});
test.skip('should fallback to Windows PowerShell', async () => {
const fsProvider = createFsProvider([
test('should fallback to Windows PowerShell', async () => {
const expectedPaths = [
'C:\\Windows\\Sysnative\\WindowsPowerShell\\v1.0\\powershell.exe',
'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'
]);
const profiles = await detectAvailableProfiles(false, buildTestSafeConfigProvider(pwshSourceConfig), fsProvider, undefined, undefined, undefined);
];
const profiles = await detectAvailableProfiles(false, buildTestSafeConfigProvider(pwshSourceConfig), undefined, undefined, undefined, expectedPaths);
strictEqual(profiles.length, 1);
strictEqual(profiles[0].profileName, 'PowerShell');
});
@ -228,7 +228,7 @@ suite('Workbench - TerminalProfiles', () => {
},
async readFile(path: string): Promise<Buffer> {
if (path !== '/etc/shells') {
fail('Unexected path');
fail('Unexepected path');
}
return Buffer.from(etcShellsContent);
}

View File

@ -84,7 +84,7 @@
}
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlideCategories {
padding: 12px;
padding: 24px;
}
.monaco-workbench .part.editor > .content .gettingStartedContainer.animationReady .gettingStartedSlide {
@ -557,14 +557,11 @@
}
.monaco-workbench .part.editor > .content .gettingStartedContainer .button-link .codicon-arrow-small-right {
position: relative;
top: 3px;
left: 2px;
padding-left: 8px;
}
.monaco-workbench .part.editor > .content .gettingStartedContainer .button-link .codicon-check-all {
position: relative;
top: 3px;
padding-right: 8px;
}
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide .skip {
@ -606,6 +603,11 @@
display: none;
}
.monaco-workbench .part.editor > .content .gettingStartedContainer .done-next-container {
display: flex;
padding: 8px 16px 16px;
}
.monaco-workbench .part.editor > .content .gettingStartedContainer .button-link {
padding: 0;
background: transparent;
@ -616,9 +618,13 @@
max-width: 100%;
}
.monaco-workbench .part.editor > .content .gettingStartedContainer .done-next-container .button-link {
display: flex;
align-items: center;
}
.monaco-workbench .part.editor > .content .gettingStartedContainer .button-link.next {
position: absolute;
right: 0;
margin-left: auto;
}
.monaco-workbench .part.editor > .content .gettingStartedContainer .button-link:hover {
@ -645,7 +651,7 @@
}
.monaco-workbench .part.editor > .content .gettingStartedContainer .getting-started-category .codicon {
top: 0;
top: 3px;
}
.monaco-workbench .part.editor > .content .getting-started-category .codicon::before{

View File

@ -202,6 +202,11 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
}
getUriTrustInfo(uri: URI): IWorkspaceTrustUriInfo {
// Return trusted when workspace trust is disabled
if (!isWorkspaceTrustEnabled(this.configurationService)) {
return { trusted: true, uri };
}
let resultState = false;
let maxLength = -1;

View File

@ -5,7 +5,8 @@
import * as assert from 'assert';
import { MarkdownString } from 'vs/workbench/api/common/extHostTypeConverters';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { MarkdownString, NotebookCellOutputItem } from 'vs/workbench/api/common/extHostTypeConverters';
import { isEmptyObject } from 'vs/base/common/types';
import { forEach } from 'vs/base/common/collections';
import { LogLevel as _MainLogLevel } from 'vs/platform/log/common/log';
@ -81,4 +82,23 @@ suite('ExtHostTypeConverter', function () {
}
});
});
test('NotebookCellOutputItem', function () {
const item = extHostTypes.NotebookCellOutputItem.text('Hello', 'foo/bar');
const dto = NotebookCellOutputItem.from(item);
assert.strictEqual(dto.mime, 'foo/bar');
assert.strictEqual(dto.metadata, undefined);
assert.strictEqual(dto.value, undefined);
assert.deepStrictEqual(dto.valueBytes, Array.from(new TextEncoder().encode('Hello')));
const item2 = NotebookCellOutputItem.to(dto);
assert.strictEqual(item2.mime, item.mime);
assert.strictEqual(item2.metadata, item.metadata);
assert.strictEqual(item2.value, item.value);
assert.deepStrictEqual(item2.data, item.data);
});
});

View File

@ -696,28 +696,28 @@ suite('ExtHostTypes', function () {
item = types.NotebookCellOutputItem.json(1);
assert.strictEqual(item.mime, 'application/json');
assert.deepStrictEqual(item.value, new TextEncoder().encode(JSON.stringify(1)));
assert.deepStrictEqual(item.data, new TextEncoder().encode(JSON.stringify(1)));
item = types.NotebookCellOutputItem.json(1, 'foo');
assert.strictEqual(item.mime, 'foo');
assert.deepStrictEqual(item.value, new TextEncoder().encode(JSON.stringify(1)));
assert.deepStrictEqual(item.data, new TextEncoder().encode(JSON.stringify(1)));
item = types.NotebookCellOutputItem.json(true);
assert.strictEqual(item.mime, 'application/json');
assert.deepStrictEqual(item.value, new TextEncoder().encode(JSON.stringify(true)));
assert.deepStrictEqual(item.data, new TextEncoder().encode(JSON.stringify(true)));
item = types.NotebookCellOutputItem.json([true, 1, 'ddd']);
assert.strictEqual(item.mime, 'application/json');
assert.deepStrictEqual(item.value, new TextEncoder().encode(JSON.stringify([true, 1, 'ddd'], undefined, '\t')));
assert.deepStrictEqual(item.data, new TextEncoder().encode(JSON.stringify([true, 1, 'ddd'], undefined, '\t')));
// --- text
item = types.NotebookCellOutputItem.text('Hęłlö');
assert.strictEqual(item.mime, 'text/plain');
assert.deepStrictEqual(item.value, new TextEncoder().encode('Hęłlö'));
assert.deepStrictEqual(item.data, new TextEncoder().encode('Hęłlö'));
item = types.NotebookCellOutputItem.text('Hęłlö', 'foo/bar');
assert.strictEqual(item.mime, 'foo/bar');
assert.deepStrictEqual(item.value, new TextEncoder().encode('Hęłlö'));
assert.deepStrictEqual(item.data, new TextEncoder().encode('Hęłlö'));
});
});