Merge branch 'master' into sandy081/settingsSync/align

This commit is contained in:
Sandeep Somavarapu
2021-02-11 16:54:41 +01:00
170 changed files with 5115 additions and 2207 deletions
+1
View File
@@ -13,5 +13,6 @@
**/extensions/**/out/**
**/extensions/**/build/**
**/extensions/markdown-language-features/media/**
**/extensions/markdown-language-features/notebook-out/**
**/extensions/typescript-basics/test/colorize-fixtures/**
**/extensions/**/dist/**
+2
View File
@@ -86,6 +86,8 @@ module.exports.indentationFilter = [
'!**/*.Dockerfile',
'!**/*.dockerfile',
'!extensions/markdown-language-features/media/*.js',
'!extensions/markdown-language-features/notebook-out/*.js',
'!extensions/markdown-notebook-math/notebook-out/*.js',
'!extensions/simple-browser/media/*.js',
];
+1
View File
@@ -29,6 +29,7 @@ exports.dirs = [
'extensions/json-language-features',
'extensions/json-language-features/server',
'extensions/markdown-language-features',
'extensions/markdown-notebook-math',
'extensions/merge-conflict',
'extensions/microsoft-authentication',
'extensions/npm',
File diff suppressed because one or more lines are too long
@@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as MarkdownIt from 'markdown-it';
declare const acquireNotebookRendererApi: any;
type extendMarkdownItFnType = (
(f: (md: MarkdownIt.MarkdownIt) => void) => void
);
(function () {
const markdownIt = new MarkdownIt();
(globalThis as any).extendMarkdownIt = ((f: (md: MarkdownIt.MarkdownIt) => void) => {
f(markdownIt);
}) as extendMarkdownItFnType;
const notebook = acquireNotebookRendererApi('notebookCoreTestRenderer');
notebook.onDidCreateMarkdown(({ element, content }: any) => {
const rendered = markdownIt.render(content);
element.innerHTML = rendered;
});
}());
@@ -30,6 +30,13 @@
"onCustomEditor:vscode.markdown.preview.editor"
],
"contributes": {
"notebookMarkdownRenderer": [
{
"id": "markdownItRenderer",
"displayName": "Markdown it renderer",
"entrypoint": "./notebook-out/index.js"
}
],
"commands": [
{
"command": "markdown.showPreview",
@@ -0,0 +1,27 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const path = require('path');
module.exports = {
entry: {
index: './notebook/index.ts'
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'notebook-out')
}
};
@@ -0,0 +1,12 @@
test/**
test-workspace/**
src/**
tsconfig.json
out/test/**
out/**
extension.webpack.config.js
extension-browser.webpack.config.js
cgmanifest.json
yarn.lock
preview-src/**
webpack.config.js
@@ -0,0 +1,3 @@
# Markdown Notebook Math support
**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

File diff suppressed because one or more lines are too long
@@ -0,0 +1,22 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type * as markdownIt from 'markdown-it';
import 'katex/dist/katex.min.css';
declare const extendMarkdownIt: undefined | (
(f: (md: markdownIt.MarkdownIt) => void) => void
);
(function () {
const katex = require('@iktakahiro/markdown-it-katex');
if (typeof extendMarkdownIt !== 'undefined') {
extendMarkdownIt((md: markdownIt.MarkdownIt) => {
md.use(katex);
});
}
}());
@@ -0,0 +1,12 @@
{
"extends": "../../shared.tsconfig.json",
"compilerOptions": {
"outDir": "./dist/",
"jsx": "react",
"lib": [
"es2018",
"DOM",
"DOM.Iterable"
]
}
}
@@ -0,0 +1,45 @@
{
"name": "markdown-notebook-math",
"displayName": "%displayName%",
"description": "%description%",
"version": "1.0.0",
"icon": "icon.png",
"publisher": "vscode",
"enableProposedApi": true,
"license": "MIT",
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"engines": {
"vscode": "^1.54.0"
},
"categories": [
"Other"
],
"contributes": {
"notebookMarkdownRenderer": [
{
"id": "markdownItRenderer-katex",
"displayName": "Markdown it renderer",
"entrypoint": "./notebook-out/extension.js"
}
]
},
"scripts": {
"compile": "npm run build-notebook",
"watch": "npm run build-notebook",
"build-notebook": "npx webpack-cli --config webpack.notebook.js --mode production"
},
"dependencies": {
"@iktakahiro/markdown-it-katex": "^4.0.1"
},
"devDependencies": {
"@types/markdown-it": "^0.0.0",
"css-loader": "^5.0.2",
"markdown-it": "^12.0.4",
"style-loader": "^2.0.0",
"url-loader": "^4.1.1"
},
"repository": {
"type": "git",
"url": "https://github.com/microsoft/vscode.git"
}
}
@@ -0,0 +1,4 @@
{
"displayName": "Markdown Notebook math",
"description": "Provides rich language support for Markdown."
}
@@ -0,0 +1,35 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const path = require('path');
module.exports = {
entry: {
extension: './notebook/extension.ts',
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
use: ['url-loader?limit=100000']
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'notebook-out')
}
};
+315
View File
@@ -0,0 +1,315 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@iktakahiro/markdown-it-katex@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@iktakahiro/markdown-it-katex/-/markdown-it-katex-4.0.1.tgz#65ff9d12afd4c0b7684dd247abe7ce42fc1edac3"
integrity sha512-kGFooO7fIOgY34PSG8ZNVsUlKhhNoqhzW2kq94TNGa8COzh73PO4KsEoPOsQVG1mEAe8tg7GqG0FoVao0aMHaw==
dependencies:
katex "^0.12.0"
"@types/json-schema@^7.0.6":
version "7.0.7"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
"@types/markdown-it@^0.0.0":
version "0.0.0"
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.0.tgz#8f6acaa5e3245e275f684e95deb3e518d1c6ab16"
integrity sha1-j2rKpeMkXidfaE6V3rPlGNHGqxY=
ajv-keywords@^3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
ajv@^6.12.5:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
big.js@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
camelcase@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
colorette@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b"
integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==
commander@^2.19.0:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
css-loader@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.0.2.tgz#24f758dae349bad0a440c50d7e2067742e0899cb"
integrity sha512-gbkBigdcHbmNvZ1Cg6aV6qh6k9N6XOr8YWzISLQGrwk2mgOH8LLrizhkxbDhQtaLtktyKHD4970S0xwz5btfTA==
dependencies:
camelcase "^6.2.0"
cssesc "^3.0.0"
icss-utils "^5.1.0"
loader-utils "^2.0.0"
postcss "^8.2.4"
postcss-modules-extract-imports "^3.0.0"
postcss-modules-local-by-default "^4.0.0"
postcss-modules-scope "^3.0.0"
postcss-modules-values "^4.0.0"
postcss-value-parser "^4.1.0"
schema-utils "^3.0.0"
semver "^7.3.4"
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
emojis-list@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
entities@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
fast-deep-equal@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
icss-utils@^5.0.0, icss-utils@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
indexes-of@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json5@^2.1.2:
version "2.2.0"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
dependencies:
minimist "^1.2.5"
katex@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/katex/-/katex-0.12.0.tgz#2fb1c665dbd2b043edcf8a1f5c555f46beaa0cb9"
integrity sha512-y+8btoc/CK70XqcHqjxiGWBOeIL8upbS0peTPXTvgrh21n1RiWWcIpSWM+4uXq+IAgNh9YYQWdc7LVDPDAEEAg==
dependencies:
commander "^2.19.0"
linkify-it@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.2.tgz#f55eeb8bc1d3ae754049e124ab3bb56d97797fb8"
integrity sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==
dependencies:
uc.micro "^1.0.1"
loader-utils@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
dependencies:
big.js "^5.2.2"
emojis-list "^3.0.0"
json5 "^2.1.2"
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
markdown-it@^12.0.4:
version "12.0.4"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.0.4.tgz#eec8247d296327eac3ba9746bdeec9cfcc751e33"
integrity sha512-34RwOXZT8kyuOJy25oJNJoulO8L0bTHYWXcdZBYZqFnjIy3NgjeoM3FmPXIOFQ26/lSHYMr8oc62B6adxXcb3Q==
dependencies:
argparse "^2.0.1"
entities "~2.1.0"
linkify-it "^3.0.1"
mdurl "^1.0.1"
uc.micro "^1.0.5"
mdurl@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
mime-db@1.45.0:
version "1.45.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea"
integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==
mime-types@^2.1.27:
version "2.1.28"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd"
integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==
dependencies:
mime-db "1.45.0"
minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
nanoid@^3.1.20:
version "3.1.20"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==
postcss-modules-extract-imports@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d"
integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==
postcss-modules-local-by-default@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c"
integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==
dependencies:
icss-utils "^5.0.0"
postcss-selector-parser "^6.0.2"
postcss-value-parser "^4.1.0"
postcss-modules-scope@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06"
integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==
dependencies:
postcss-selector-parser "^6.0.4"
postcss-modules-values@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c"
integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==
dependencies:
icss-utils "^5.0.0"
postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4:
version "6.0.4"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3"
integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==
dependencies:
cssesc "^3.0.0"
indexes-of "^1.0.1"
uniq "^1.0.1"
util-deprecate "^1.0.2"
postcss-value-parser@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
postcss@^8.2.4:
version "8.2.6"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.6.tgz#5d69a974543b45f87e464bc4c3e392a97d6be9fe"
integrity sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg==
dependencies:
colorette "^1.2.1"
nanoid "^3.1.20"
source-map "^0.6.1"
punycode@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
schema-utils@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef"
integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==
dependencies:
"@types/json-schema" "^7.0.6"
ajv "^6.12.5"
ajv-keywords "^3.5.2"
semver@^7.3.4:
version "7.3.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
dependencies:
lru-cache "^6.0.0"
source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
style-loader@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c"
integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==
dependencies:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
uniq@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
dependencies:
punycode "^2.1.0"
url-loader@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2"
integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==
dependencies:
loader-utils "^2.0.0"
mime-types "^2.1.27"
schema-utils "^3.0.0"
util-deprecate@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
File diff suppressed because it is too large Load Diff
@@ -70,14 +70,14 @@ export function smokeTestActivate(context: vscode.ExtensionContext): any {
label: 'notebookSmokeTest',
isPreferred: true,
executeAllCells: async (_document: vscode.NotebookDocument) => {
const edit = new vscode.WorkspaceEdit();
for (let i = 0; i < _document.cells.length; i++) {
_document.cells[i].outputs = [{
outputKind: vscode.CellOutputKind.Rich,
data: {
'text/html': ['test output']
}
}];
edit.replaceNotebookCellOutput(_document.uri, i, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/html', ['test output'], undefined)
])]);
}
await vscode.workspace.applyEdit(edit);
},
cancelAllCellsExecution: async () => { },
executeCell: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell | undefined) => {
@@ -85,12 +85,11 @@ export function smokeTestActivate(context: vscode.ExtensionContext): any {
_cell = _document.cells[0];
}
_cell.outputs = [{
outputKind: vscode.CellOutputKind.Rich,
data: {
'text/html': ['test output']
}
}];
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCellOutput(_document.uri, _cell.index, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/html', ['test output'], undefined)
])]);
await vscode.workspace.applyEdit(edit);
return;
},
cancelCellExecution: async () => { }
@@ -61,15 +61,12 @@ export function activate(context: vscode.ExtensionContext): any {
label: 'Notebook Test Kernel',
isPreferred: true,
executeAllCells: async (_document: vscode.NotebookDocument) => {
const cell = _document.cells[0];
const edit = new vscode.WorkspaceEdit();
cell.outputs = [{
outputKind: vscode.CellOutputKind.Rich,
data: {
'text/plain': ['my output']
}
}];
return;
edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined)
])]);
return vscode.workspace.applyEdit(edit);
},
cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { },
executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => {
@@ -78,26 +75,21 @@ export function activate(context: vscode.ExtensionContext): any {
}
if (document.uri.path.endsWith('customRenderer.vsctestnb')) {
cell.outputs = [{
outputKind: vscode.CellOutputKind.Rich,
data: {
'text/custom': 'test'
}
}];
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/custom', ['test'], undefined)
])]);
return;
return vscode.workspace.applyEdit(edit);
}
const edit = new vscode.WorkspaceEdit();
// const previousOutputs = cell.outputs;
const newOutputs: vscode.CellOutput[] = [{
outputKind: vscode.CellOutputKind.Rich,
data: {
'text/plain': ['my output']
}
}];
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined)
])]);
cell.outputs = newOutputs;
return;
return vscode.workspace.applyEdit(edit);
},
cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { }
};
@@ -107,15 +99,12 @@ export function activate(context: vscode.ExtensionContext): any {
label: 'Notebook Secondary Test Kernel',
isPreferred: false,
executeAllCells: async (_document: vscode.NotebookDocument) => {
const cell = _document.cells[0];
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined)
])]);
cell.outputs = [{
outputKind: vscode.CellOutputKind.Rich,
data: {
'text/plain': ['my second output']
}
}];
return;
return vscode.workspace.applyEdit(edit);
},
cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { },
executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => {
@@ -123,26 +112,19 @@ export function activate(context: vscode.ExtensionContext): any {
cell = document.cells[0];
}
if (document.uri.path.endsWith('customRenderer.vsctestnb')) {
cell.outputs = [{
outputKind: vscode.CellOutputKind.Rich,
data: {
'text/custom': 'test 2'
}
}];
const edit = new vscode.WorkspaceEdit();
return;
if (document.uri.path.endsWith('customRenderer.vsctestnb')) {
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/custom', ['test 2'], undefined)
])]);
} else {
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined)
])]);
}
const newOutputs: vscode.CellOutput[] = [{
outputKind: vscode.CellOutputKind.Rich,
data: {
'text/plain': ['my second output']
}
}];
cell.outputs = newOutputs;
return;
return vscode.workspace.applyEdit(edit);
},
cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { }
};
+6 -2
View File
@@ -19,6 +19,7 @@ export interface ICheckboxOpts extends ICheckboxStyles {
readonly icon?: CSSIcon;
readonly title: string;
readonly isChecked: boolean;
readonly notFocusable?: boolean;
}
export interface ICheckboxStyles {
@@ -51,7 +52,8 @@ export class CheckboxActionViewItem extends BaseActionViewItem {
this.checkbox = new Checkbox({
actionClassName: this._action.class,
isChecked: this._action.checked,
title: this._action.label
title: this._action.label,
notFocusable: true
});
this.disposables.add(this.checkbox);
this.disposables.add(this.checkbox.onChange(() => this._action.checked = !!this.checkbox && this.checkbox.checked, this));
@@ -113,7 +115,9 @@ export class Checkbox extends Widget {
this.domNode = document.createElement('div');
this.domNode.title = this._opts.title;
this.domNode.classList.add(...classes);
this.domNode.tabIndex = 0;
if (!this._opts.notFocusable) {
this.domNode.tabIndex = 0;
}
this.domNode.setAttribute('role', 'checkbox');
this.domNode.setAttribute('aria-checked', String(this._checked));
this.domNode.setAttribute('aria-label', this._opts.title);
@@ -54,6 +54,7 @@
/* TODO: actions should be part of the pane, but they aren't yet */
.monaco-pane-view .pane:hover > .pane-header.expanded > .actions,
.monaco-pane-view .pane:focus-within > .pane-header.expanded > .actions,
.monaco-pane-view .pane > .pane-header.actions-always-visible.expanded > .actions,
.monaco-pane-view .pane > .pane-header.focused.expanded > .actions {
display: initial;
+18 -18
View File
@@ -10,8 +10,8 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { LinkedList } from 'vs/base/common/linkedList';
import { URI } from 'vs/base/common/uri';
export function isThenable<T>(obj: any): obj is Promise<T> {
return obj && typeof (<Promise<any>>obj).then === 'function';
export function isThenable<T>(obj: unknown): obj is Promise<T> {
return !!obj && typeof (obj as unknown as Promise<T>).then === 'function';
}
export interface CancelablePromise<T> extends Promise<T> {
@@ -166,10 +166,10 @@ export class Throttler {
this.activePromise = promiseFactory();
return new Promise((resolve, reject) => {
this.activePromise!.then((result: any) => {
this.activePromise!.then((result: T) => {
this.activePromise = null;
resolve(result);
}, (err: any) => {
}, (err: unknown) => {
this.activePromise = null;
reject(err);
});
@@ -179,7 +179,7 @@ export class Throttler {
export class Sequencer {
private current: Promise<any> = Promise.resolve(null);
private current: Promise<unknown> = Promise.resolve(null);
queue<T>(promiseTask: ITask<Promise<T>>): Promise<T> {
return this.current = this.current.then(() => promiseTask(), () => promiseTask());
@@ -188,7 +188,7 @@ export class Sequencer {
export class SequencerByKey<TKey> {
private promiseMap = new Map<TKey, Promise<any>>();
private promiseMap = new Map<TKey, Promise<unknown>>();
queue<T>(key: TKey, promiseTask: ITask<Promise<T>>): Promise<T> {
const runningPromise = this.promiseMap.get(key) ?? Promise.resolve();
@@ -321,7 +321,7 @@ export class ThrottledDelayer<T> {
}
trigger(promiseFactory: ITask<Promise<T>>, delay?: number): Promise<T> {
return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as any as Promise<T>;
return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as unknown as Promise<T>;
}
isTriggered(): boolean {
@@ -488,7 +488,7 @@ export function firstParallel<T>(promiseList: Promise<T>[], shouldStop: (t: T) =
interface ILimitedTaskFactory<T> {
factory: ITask<Promise<T>>;
c: (value: T | Promise<T>) => void;
e: (error?: any) => void;
e: (error?: unknown) => void;
}
/**
@@ -667,7 +667,7 @@ export class IntervalTimer implements IDisposable {
export class RunOnceScheduler {
protected runner: ((...args: any[]) => void) | null;
protected runner: ((...args: unknown[]) => void) | null;
private timeoutToken: any;
private timeout: number;
@@ -827,7 +827,7 @@ export class IdleValue<T> {
private _didRun: boolean = false;
private _value?: T;
private _error: any;
private _error: unknown;
constructor(executor: () => T) {
this._executor = () => {
@@ -1017,7 +1017,7 @@ export class IntervalCounter {
//#region
export type ValueCallback<T = any> = (value: T | Promise<T>) => void;
export type ValueCallback<T = unknown> = (value: T | Promise<T>) => void;
/**
* Creates a promise whose resolution or rejection can be controlled imperatively.
@@ -1025,7 +1025,7 @@ export type ValueCallback<T = any> = (value: T | Promise<T>) => void;
export class DeferredPromise<T> {
private completeCallback!: ValueCallback<T>;
private errorCallback!: (err: any) => void;
private errorCallback!: (err: unknown) => void;
private rejected = false;
private resolved = false;
@@ -1058,7 +1058,7 @@ export class DeferredPromise<T> {
});
}
public error(err: any) {
public error(err: unknown) {
return new Promise<void>(resolve => {
this.errorCallback(err);
this.rejected = true;
@@ -1080,14 +1080,14 @@ export class DeferredPromise<T> {
//#region
export interface IWaitUntil {
waitUntil(thenable: Promise<any>): void;
waitUntil(thenable: Promise<unknown>): void;
}
export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
private _asyncDeliveryQueue?: LinkedList<[Listener<T>, Omit<T, 'waitUntil'>]>;
async fireAsync(data: Omit<T, 'waitUntil'>, token: CancellationToken, promiseJoin?: (p: Promise<any>, listener: Function) => Promise<any>): Promise<void> {
async fireAsync(data: Omit<T, 'waitUntil'>, token: CancellationToken, promiseJoin?: (p: Promise<unknown>, listener: Function) => Promise<unknown>): Promise<void> {
if (!this._listeners) {
return;
}
@@ -1103,11 +1103,11 @@ export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
while (this._asyncDeliveryQueue.size > 0 && !token.isCancellationRequested) {
const [listener, data] = this._asyncDeliveryQueue.shift()!;
const thenables: Promise<any>[] = [];
const thenables: Promise<unknown>[] = [];
const event = <T>{
...data,
waitUntil: (p: Promise<any>): void => {
waitUntil: (p: Promise<unknown>): void => {
if (Object.isFrozen(thenables)) {
throw new Error('waitUntil can NOT be called asynchronous');
}
@@ -1208,7 +1208,7 @@ export namespace Promises {
return undefined; // do not rethrow so that other promises can settle
})));
if (firstError) {
if (typeof firstError !== 'undefined') {
throw firstError;
}
-1
View File
@@ -28,7 +28,6 @@ export function toSlashes(osPath: string) {
* or `getRoot('\\server\shares\path') === \\server\shares\`
*/
export function getRoot(path: string, sep: string = posix.sep): string {
if (!path) {
return '';
}
+18 -18
View File
@@ -15,15 +15,15 @@ export function isArray<T>(array: T | {}): array is T extends readonly any[] ? (
/**
* @returns whether the provided parameter is a JavaScript String or not.
*/
export function isString(str: any): str is string {
export function isString(str: unknown): str is string {
return (typeof str === 'string');
}
/**
* @returns whether the provided parameter is a JavaScript Array and each element in the array is a string.
*/
export function isStringArray(value: any): value is string[] {
return Array.isArray(value) && (<any[]>value).every(elem => isString(elem));
export function isStringArray(value: unknown): value is string[] {
return Array.isArray(value) && (<unknown[]>value).every(elem => isString(elem));
}
/**
@@ -31,7 +31,7 @@ export function isStringArray(value: any): value is string[] {
* @returns whether the provided parameter is of type `object` but **not**
* `null`, an `array`, a `regexp`, nor a `date`.
*/
export function isObject(obj: any): obj is Object {
export function isObject(obj: unknown): obj is Object {
// The method can't do a type cast since there are type (like strings) which
// are subclasses of any put not positvely matched by the function. Hence type
// narrowing results in wrong results.
@@ -46,21 +46,21 @@ export function isObject(obj: any): obj is Object {
* In **contrast** to just checking `typeof` this will return `false` for `NaN`.
* @returns whether the provided parameter is a JavaScript Number or not.
*/
export function isNumber(obj: any): obj is number {
export function isNumber(obj: unknown): obj is number {
return (typeof obj === 'number' && !isNaN(obj));
}
/**
* @returns whether the provided parameter is a JavaScript Boolean or not.
*/
export function isBoolean(obj: any): obj is boolean {
export function isBoolean(obj: unknown): obj is boolean {
return (obj === true || obj === false);
}
/**
* @returns whether the provided parameter is undefined.
*/
export function isUndefined(obj: any): obj is undefined {
export function isUndefined(obj: unknown): obj is undefined {
return (typeof obj === 'undefined');
}
@@ -74,12 +74,12 @@ export function isDefined<T>(arg: T | null | undefined): arg is T {
/**
* @returns whether the provided parameter is undefined or null.
*/
export function isUndefinedOrNull(obj: any): obj is undefined | null {
export function isUndefinedOrNull(obj: unknown): obj is undefined | null {
return (isUndefined(obj) || obj === null);
}
export function assertType(condition: any, type?: string): asserts condition {
export function assertType(condition: unknown, type?: string): asserts condition {
if (!condition) {
throw new Error(type ? `Unexpected type, expected '${type}'` : 'Unexpected type');
}
@@ -123,7 +123,7 @@ const hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* @returns whether the provided parameter is an empty JavaScript Object or not.
*/
export function isEmptyObject(obj: any): obj is any {
export function isEmptyObject(obj: unknown): obj is object {
if (!isObject(obj)) {
return false;
}
@@ -140,27 +140,27 @@ export function isEmptyObject(obj: any): obj is any {
/**
* @returns whether the provided parameter is a JavaScript Function or not.
*/
export function isFunction(obj: any): obj is Function {
export function isFunction(obj: unknown): obj is Function {
return (typeof obj === 'function');
}
/**
* @returns whether the provided parameters is are JavaScript Function or not.
*/
export function areFunctions(...objects: any[]): boolean {
export function areFunctions(...objects: unknown[]): boolean {
return objects.length > 0 && objects.every(isFunction);
}
export type TypeConstraint = string | Function;
export function validateConstraints(args: any[], constraints: Array<TypeConstraint | undefined>): void {
export function validateConstraints(args: unknown[], constraints: Array<TypeConstraint | undefined>): void {
const len = Math.min(args.length, constraints.length);
for (let i = 0; i < len; i++) {
validateConstraint(args[i], constraints[i]);
}
}
export function validateConstraint(arg: any, constraint: TypeConstraint | undefined): void {
export function validateConstraint(arg: unknown, constraint: TypeConstraint | undefined): void {
if (isString(constraint)) {
if (typeof arg !== constraint) {
@@ -174,7 +174,7 @@ export function validateConstraint(arg: any, constraint: TypeConstraint | undefi
} catch {
// ignore
}
if (!isUndefinedOrNull(arg) && arg.constructor === constraint) {
if (!isUndefinedOrNull(arg) && (arg as any).constructor === constraint) {
return;
}
if (constraint.length === 1 && constraint.call(undefined, arg) === true) {
@@ -204,8 +204,8 @@ export function getAllMethodNames(obj: object): string[] {
return methods;
}
export function createProxyObject<T extends object>(methodNames: string[], invoke: (method: string, args: any[]) => any): T {
const createProxyMethod = (method: string): () => any => {
export function createProxyObject<T extends object>(methodNames: string[], invoke: (method: string, args: unknown[]) => unknown): T {
const createProxyMethod = (method: string): () => unknown => {
return function () {
const args = Array.prototype.slice.call(arguments, 0);
return invoke(method, args);
@@ -242,7 +242,7 @@ export type AddFirstParameterToFunctions<Target, TargetFunctionsReturnType, Firs
[K in keyof Target]:
// Function: add param to function
Target[K] extends (...args: any) => TargetFunctionsReturnType ? (firstArg: FirstParameter, ...args: Parameters<Target[K]>) => ReturnType<Target[K]> :
Target[K] extends (...args: any[]) => TargetFunctionsReturnType ? (firstArg: FirstParameter, ...args: Parameters<Target[K]>) => ReturnType<Target[K]> :
// Else: just leave as is
Target[K]
+8 -4
View File
@@ -71,10 +71,8 @@ export interface IIPCOptions {
debugBrk?: number;
/**
* See https://github.com/microsoft/vscode/issues/27665
* Allows to pass in fresh execArgv to the forked process such that it doesn't inherit them from `process.execArgv`.
* e.g. Launching the extension host process with `--inspect-brk=xxx` and then forking a process from the extension host
* results in the forked process inheriting `--inspect-brk=xxx`.
* If set, starts the fork with empty execArgv. If not set, execArgv from the parent proces are inherited,
* except --inspect= and --inspect-brk= which are filtered as they would result in a port conflict.
*/
freshExecArgv?: boolean;
@@ -198,6 +196,12 @@ export class Client implements IChannelClient, IDisposable {
forkOpts.execArgv = ['--nolazy', '--inspect-brk=' + this.options.debugBrk];
}
if (forkOpts.execArgv === undefined) {
// if not set, the forked process inherits the execArgv of the parent process
// --inspect and --inspect-brk can not be inherited as the port would conflict
forkOpts.execArgv = process.execArgv.filter(a => !/^--inspect(-brk)?=/.test(a)); // remove
}
if (isMacintosh && forkOpts.env) {
// Unset `DYLD_LIBRARY_PATH`, as it leads to process crashes
// See https://github.com/microsoft/vscode/issues/105848
@@ -40,7 +40,7 @@ import { NodeCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/co
import { LanguagePackCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner';
import { StorageDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner';
import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { MessagePortMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog';
import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService';
+1 -1
View File
@@ -12,7 +12,7 @@ import { IWindowOpenable } from 'vs/platform/windows/common/windows';
import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { resolveShellEnv } from 'vs/platform/environment/node/shellEnv';
import { IUpdateService } from 'vs/platform/update/common/update';
import { UpdateChannel } from 'vs/platform/update/electron-main/updateIpc';
import { UpdateChannel } from 'vs/platform/update/common/updateIpc';
import { getDelayedChannel, StaticRouter, ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc.electron';
import { Server as NodeIPCServer } from 'vs/base/parts/ipc/node/ipc.net';
@@ -22,11 +22,12 @@ import BaseHtml from 'vs/code/electron-sandbox/issue/issueReporterPage';
import { localize } from 'vs/nls';
import { isRemoteDiagnosticError, SystemInfo } from 'vs/platform/diagnostics/common/diagnostics';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IMainProcessService, ElectronIPCMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { IssueReporterData, IssueReporterExtensionData, IssueReporterFeatures, IssueReporterStyles, IssueType } from 'vs/platform/issue/common/issue';
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
import { Codicon } from 'vs/base/common/codicons';
import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels';
import { ElectronIPCMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
const MAX_URL_LENGTH = 2045;
@@ -15,7 +15,6 @@ import { URI } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { EditorOpenContext } from 'vs/platform/editor/common/editor';
import { ILogService } from 'vs/platform/log/common/log';
import { IExternalOpener, IExternalUriResolver, IOpener, IOpenerService, IResolvedExternalUri, IValidator, matchesScheme, OpenOptions, ResolveExternalUriOptions } from 'vs/platform/opener/common/opener';
class CommandOpener implements IOpener {
@@ -106,8 +105,7 @@ export class OpenerService implements IOpenerService {
constructor(
@ICodeEditorService editorService: ICodeEditorService,
@ICommandService commandService: ICommandService,
@ILogService private logService: ILogService
@ICommandService commandService: ICommandService
) {
// Default external opener is going through window.open()
this._defaultExternalOpener = {
@@ -169,7 +167,6 @@ export class OpenerService implements IOpenerService {
const targetURI = typeof target === 'string' ? URI.parse(target) : target;
// validate against the original URI that this URI resolves to, if one exists
const validationTarget = this._resolvedUriTargets.get(targetURI) ?? targetURI;
this.logService.trace(`OpenerService#open: ${targetURI.authority} validating via ${validationTarget.authority}`);
for (const validator of this._validators) {
if (!(await validator.shouldOpen(validationTarget))) {
return false;
@@ -192,7 +189,6 @@ export class OpenerService implements IOpenerService {
const result = await resolver.resolveExternalUri(resource, options);
if (result) {
if (!this._resolvedUriTargets.has(result.resolved)) {
this.logService.trace(`OpenerService#resolveExternalUri: ${resource.authority} resolved to ${result.resolved.authority}`);
this._resolvedUriTargets.set(result.resolved, resource);
}
return result;
@@ -1140,15 +1140,15 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
const clonedOptions = { ...options };
clonedOptions.inDiffEditor = true;
clonedOptions.automaticLayout = false;
clonedOptions.scrollbar = clonedOptions.scrollbar || {};
// Clone scrollbar options before changing them
clonedOptions.scrollbar = { ...(clonedOptions.scrollbar || {}) };
clonedOptions.scrollbar.vertical = 'visible';
clonedOptions.folding = false;
clonedOptions.codeLens = this._diffCodeLens;
clonedOptions.fixedOverflowWidgets = true;
// clonedOptions.lineDecorationsWidth = '2ch';
if (!clonedOptions.minimap) {
clonedOptions.minimap = {};
}
// Clone minimap options before changing them
clonedOptions.minimap = { ...(clonedOptions.minimap || {}) };
clonedOptions.minimap.enabled = false;
return clonedOptions;
}
@@ -43,7 +43,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
import { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standaloneThemeServiceImpl';
import { splitLines } from 'vs/base/common/strings';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ILogService } from 'vs/platform/log/common/log';
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
@@ -57,7 +56,7 @@ function withAllStandaloneServices<T extends IEditor>(domElement: HTMLElement, o
}
if (!services.has(IOpenerService)) {
services.set(IOpenerService, new OpenerService(services.get(ICodeEditorService), services.get(ICommandService), services.get(ILogService)));
services.set(IOpenerService, new OpenerService(services.get(ICodeEditorService), services.get(ICommandService)));
}
let result = callback(services);
@@ -8,7 +8,6 @@ import { URI } from 'vs/base/common/uri';
import { OpenerService } from 'vs/editor/browser/services/openerService';
import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices';
import { CommandsRegistry, ICommandService, NullCommandService } from 'vs/platform/commands/common/commands';
import { NullLogService } from 'vs/platform/log/common/log';
import { matchesScheme } from 'vs/platform/opener/common/opener';
suite('OpenerService', function () {
@@ -31,13 +30,13 @@ suite('OpenerService', function () {
});
test('delegate to editorService, scheme:///fff', async function () {
const openerService = new OpenerService(editorService, NullCommandService, new NullLogService());
const openerService = new OpenerService(editorService, NullCommandService);
await openerService.open(URI.parse('another:///somepath'));
assert.equal(editorService.lastInput!.options!.selection, undefined);
});
test('delegate to editorService, scheme:///fff#L123', async function () {
const openerService = new OpenerService(editorService, NullCommandService, new NullLogService());
const openerService = new OpenerService(editorService, NullCommandService);
await openerService.open(URI.parse('file:///somepath#L23'));
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
@@ -59,7 +58,7 @@ suite('OpenerService', function () {
});
test('delegate to editorService, scheme:///fff#123,123', async function () {
const openerService = new OpenerService(editorService, NullCommandService, new NullLogService());
const openerService = new OpenerService(editorService, NullCommandService);
await openerService.open(URI.parse('file:///somepath#23'));
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
@@ -77,7 +76,7 @@ suite('OpenerService', function () {
});
test('delegate to commandsService, command:someid', async function () {
const openerService = new OpenerService(editorService, commandService, new NullLogService());
const openerService = new OpenerService(editorService, commandService);
const id = `aCommand${Math.random()}`;
CommandsRegistry.registerCommand(id, function () { });
@@ -99,7 +98,7 @@ suite('OpenerService', function () {
});
test('links are protected by validators', async function () {
const openerService = new OpenerService(editorService, commandService, new NullLogService());
const openerService = new OpenerService(editorService, commandService);
openerService.registerValidator({ shouldOpen: () => Promise.resolve(false) });
@@ -110,7 +109,7 @@ suite('OpenerService', function () {
});
test('links validated by validators go to openers', async function () {
const openerService = new OpenerService(editorService, commandService, new NullLogService());
const openerService = new OpenerService(editorService, commandService);
openerService.registerValidator({ shouldOpen: () => Promise.resolve(true) });
@@ -129,7 +128,7 @@ suite('OpenerService', function () {
});
test('links validated by multiple validators', async function () {
const openerService = new OpenerService(editorService, commandService, new NullLogService());
const openerService = new OpenerService(editorService, commandService);
let v1 = 0;
openerService.registerValidator({
@@ -166,7 +165,7 @@ suite('OpenerService', function () {
});
test('links invalidated by first validator do not continue validating', async function () {
const openerService = new OpenerService(editorService, commandService, new NullLogService());
const openerService = new OpenerService(editorService, commandService);
let v1 = 0;
openerService.registerValidator({
@@ -10,7 +10,7 @@ import { TernarySearchTree } from 'vs/base/common/map';
import { distinct } from 'vs/base/common/objects';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContext, IContextKey, IContextKeyChangeEvent, IContextKeyService, IContextKeyServiceTarget, IReadableSet, SET_CONTEXT_COMMAND_ID, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
import { IContext, IContextKey, IContextKeyChangeEvent, IContextKeyService, IContextKeyServiceTarget, IReadableSet, SET_CONTEXT_COMMAND_ID, ContextKeyExpression, RawContextKey, ContextKeyInfo } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver';
const KEYBINDING_CONTEXT_ATTR = 'data-keybinding-context';
@@ -501,3 +501,16 @@ function findContextAttr(domNode: IContextKeyServiceTarget | null): number {
CommandsRegistry.registerCommand(SET_CONTEXT_COMMAND_ID, function (accessor, contextKey: any, contextValue: any) {
accessor.get(IContextKeyService).createKey(String(contextKey), contextValue);
});
CommandsRegistry.registerCommand('_generateContextKeyInfo', function () {
const result: ContextKeyInfo[] = [];
const seen = new Set<string>();
for (let info of RawContextKey.all()) {
if (!seen.has(info.key)) {
seen.add(info.key);
result.push(info);
}
}
result.sort((a, b) => a.key.localeCompare(b.key));
console.log(JSON.stringify(result, undefined, 2));
});
@@ -1257,13 +1257,28 @@ export class ContextKeyOrExpr implements IContextKeyExpression {
}
}
export interface ContextKeyInfo {
readonly key: string;
readonly type: string;
readonly description?: string;
}
export class RawContextKey<T> extends ContextKeyDefinedExpr {
private static _info: ContextKeyInfo[] = [];
static all(): IterableIterator<ContextKeyInfo> {
return RawContextKey._info.values();
}
private readonly _defaultValue: T | undefined;
constructor(key: string, defaultValue: T | undefined) {
constructor(key: string, defaultValue: T | undefined, description?: string) {
super(key);
this._defaultValue = defaultValue;
// collect all context keys into a central place
RawContextKey._info.push({ key, description, type: typeof defaultValue });
}
public bindTo(target: IContextKeyService): IContextKey<T> {
@@ -6,7 +6,7 @@
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/node/driver';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { timeout } from 'vs/base/common/async';
import { BaseWindowDriver } from 'vs/platform/driver/browser/baseDriver';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
@@ -61,9 +61,7 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
});
// Logging
if (this.verboseLogging) {
this.log(`Start watching: [${rootsToStartWatching.map(r => r.path).join(',')}]\nStop watching: [${rootsToStopWatching.join(',')}]`);
}
this.debug(`Start watching: [${rootsToStartWatching.map(r => r.path).join(',')}]\nStop watching: [${rootsToStopWatching.join(',')}]`);
// Stop watching some roots
rootsToStopWatching.forEach(root => {
@@ -133,9 +131,7 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
}
}
if (this.verboseLogging) {
this.log(`Start watching with nsfw: ${request.path}`);
}
this.debug(`Start watching with nsfw: ${request.path}`);
nsfw(request.path, events => {
for (const e of events) {
@@ -249,4 +245,8 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
private error(message: string) {
this._onDidLogMessage.fire({ type: 'error', message: `[File Watcher (nsfw)] ` + message });
}
private debug(message: string) {
this._onDidLogMessage.fire({ type: 'debug', message: `[File Watcher (chokidar)] ` + message });
}
}
@@ -146,9 +146,7 @@ export class ChokidarWatcherService extends Disposable implements IWatcherServic
this.warn(`Watcher basePath does not match version on disk and was corrected (original: ${basePath}, real: ${realBasePath})`);
}
if (this.verboseLogging) {
this.log(`Start watching with chokidar: ${realBasePath}, excludes: ${excludes.join(',')}, usePolling: ${usePolling ? 'true, interval ' + pollingInterval : 'false'}`);
}
this.debug(`Start watching with chokidar: ${realBasePath}, excludes: ${excludes.join(',')}, usePolling: ${usePolling ? 'true, interval ' + pollingInterval : 'false'}`);
let chokidarWatcher: chokidar.FSWatcher | null = chokidar.watch(realBasePath, watcherOpts);
this._watcherCount++;
@@ -301,6 +299,10 @@ export class ChokidarWatcherService extends Disposable implements IWatcherServic
this._onDidLogMessage.fire({ type: 'trace', message: `[File Watcher (chokidar)] ` + message });
}
private debug(message: string) {
this._onDidLogMessage.fire({ type: 'debug', message: `[File Watcher (chokidar)] ` + message });
}
private warn(message: string) {
this._onDidLogMessage.fire({ type: 'warn', message: `[File Watcher (chokidar)] ` + message });
}
@@ -13,7 +13,7 @@ export interface IDiskFileChange {
}
export interface ILogMessage {
type: 'trace' | 'warn' | 'error';
type: 'trace' | 'warn' | 'error' | 'info' | 'debug';
message: string;
}
@@ -8,8 +8,14 @@ import { ServiceIdentifier, BrandedService } from './instantiation';
const _registry: [ServiceIdentifier<any>, SyncDescriptor<any>][] = [];
export function registerSingleton<T, Services extends BrandedService[]>(id: ServiceIdentifier<T>, ctor: new (...services: Services) => T, supportsDelayedInstantiation?: boolean): void {
_registry.push([id, new SyncDescriptor<T>(ctor as new (...args: any[]) => T, [], supportsDelayedInstantiation)]);
export function registerSingleton<T, Services extends BrandedService[]>(id: ServiceIdentifier<T>, ctor: new (...services: Services) => T, supportsDelayedInstantiation?: boolean): void;
export function registerSingleton<T, Services extends BrandedService[]>(id: ServiceIdentifier<T>, descriptor: SyncDescriptor<any>): void;
export function registerSingleton<T, Services extends BrandedService[]>(id: ServiceIdentifier<T>, ctorOrDescriptor: { new(...services: Services): T } | SyncDescriptor<any>, supportsDelayedInstantiation?: boolean): void {
if (!(ctorOrDescriptor instanceof SyncDescriptor)) {
ctorOrDescriptor = new SyncDescriptor<T>(ctorOrDescriptor as new (...args: any[]) => T, [], supportsDelayedInstantiation);
}
_registry.push([id, ctorOrDescriptor]);
}
export function getSingletonServiceDescriptors(): [ServiceIdentifier<any>, SyncDescriptor<any>][] {
@@ -5,7 +5,7 @@
import { IChannel, IServerChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
import { Server as MessagePortServer } from 'vs/base/parts/ipc/electron-browser/ipc.mp';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
/**
* An implementation of `IMainProcessService` that leverages MessagePorts.
@@ -6,18 +6,7 @@
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { Client as IPCElectronClient } from 'vs/base/parts/ipc/electron-sandbox/ipc.electron';
import { Disposable } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IMainProcessService = createDecorator<IMainProcessService>('mainProcessService');
export interface IMainProcessService {
readonly _serviceBrand: undefined;
getChannel(channelName: string): IChannel;
registerChannel(channelName: string, channel: IServerChannel<string>): void;
}
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
/**
* An implementation of `IMainProcessService` that leverages Electron's IPC.
@@ -0,0 +1,77 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChannel, IServerChannel, ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
type ChannelClientCtor<T> = { new(channel: IChannel): T };
type Remote = { getChannel(channelName: string): IChannel; };
abstract class RemoteServiceStub<T> {
constructor(
channelName: string,
channelClientCtor: ChannelClientCtor<T> | undefined,
remote: Remote
) {
const channel = remote.getChannel(channelName);
if (channelClientCtor) {
return new channelClientCtor(channel);
} else {
return ProxyChannel.toService(channel);
}
}
}
export interface IRemoteServiceOptions<T> {
readonly channelClientCtor?: ChannelClientCtor<T>;
readonly supportsDelayedInstantiation?: boolean;
}
//#region Main Process
export const IMainProcessService = createDecorator<IMainProcessService>('mainProcessService');
export interface IMainProcessService {
readonly _serviceBrand: undefined;
getChannel(channelName: string): IChannel;
registerChannel(channelName: string, channel: IServerChannel<string>): void;
}
class MainProcessRemoteServiceStub<T> extends RemoteServiceStub<T> {
constructor(channelName: string, channelClientCtor: ChannelClientCtor<T> | undefined, @IMainProcessService ipcService: IMainProcessService) {
super(channelName, channelClientCtor, ipcService);
}
}
export function registerMainProcessRemoteService<T>(id: ServiceIdentifier<T>, channelName: string, options: IRemoteServiceOptions<T> = {}): void {
registerSingleton(id, new SyncDescriptor(MainProcessRemoteServiceStub, [channelName, options.channelClientCtor], options.supportsDelayedInstantiation));
}
//#endregion
//#region Shared Process
export const ISharedProcessService = createDecorator<ISharedProcessService>('sharedProcessService');
export interface ISharedProcessService {
readonly _serviceBrand: undefined;
getChannel(channelName: string): IChannel;
registerChannel(channelName: string, channel: IServerChannel<string>): void;
}
class SharedProcessRemoteServiceStub<T> extends RemoteServiceStub<T> {
constructor(channelName: string, channelClientCtor: ChannelClientCtor<T> | undefined, @ISharedProcessService ipcService: ISharedProcessService) {
super(channelName, channelClientCtor, ipcService);
}
}
export function registerSharedProcessRemoteService<T>(id: ServiceIdentifier<T>, channelName: string, options: IRemoteServiceOptions<T> = {}): void {
registerSingleton(id, new SyncDescriptor(SharedProcessRemoteServiceStub, [channelName, options.channelClientCtor], options.supportsDelayedInstantiation));
}
//#endregion
@@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import { ipcMessagePort } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { Client as MessagePortClient } from 'vs/base/parts/ipc/common/ipc.mp';
@@ -12,16 +11,7 @@ import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { generateUuid } from 'vs/base/common/uuid';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';
export const ISharedProcessService = createDecorator<ISharedProcessService>('sharedProcessService');
export interface ISharedProcessService {
readonly _serviceBrand: undefined;
getChannel(channelName: string): IChannel;
registerChannel(channelName: string, channel: IServerChannel<string>): void;
}
import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services';
export class SharedProcessService extends Disposable implements ISharedProcessService {
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
// @ts-ignore: interface is implemented via proxy
@@ -0,0 +1,74 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { Emitter, Event } from 'vs/base/common/event';
import { IUpdateService, State } from 'vs/platform/update/common/update';
export class UpdateChannel implements IServerChannel {
constructor(private service: IUpdateService) { }
listen(_: unknown, event: string): Event<any> {
switch (event) {
case 'onStateChange': return this.service.onStateChange;
}
throw new Error(`Event not found: ${event}`);
}
call(_: unknown, command: string, arg?: any): Promise<any> {
switch (command) {
case 'checkForUpdates': return this.service.checkForUpdates(arg);
case 'downloadUpdate': return this.service.downloadUpdate();
case 'applyUpdate': return this.service.applyUpdate();
case 'quitAndInstall': return this.service.quitAndInstall();
case '_getInitialState': return Promise.resolve(this.service.state);
case 'isLatestVersion': return this.service.isLatestVersion();
}
throw new Error(`Call not found: ${command}`);
}
}
export class UpdateChannelClient implements IUpdateService {
declare readonly _serviceBrand: undefined;
private readonly _onStateChange = new Emitter<State>();
readonly onStateChange: Event<State> = this._onStateChange.event;
private _state: State = State.Uninitialized;
get state(): State { return this._state; }
set state(state: State) {
this._state = state;
this._onStateChange.fire(state);
}
constructor(private readonly channel: IChannel) {
this.channel.listen<State>('onStateChange')(state => this.state = state);
this.channel.call<State>('_getInitialState').then(state => this.state = state);
}
checkForUpdates(context: any): Promise<void> {
return this.channel.call('checkForUpdates', context);
}
downloadUpdate(): Promise<void> {
return this.channel.call('downloadUpdate');
}
applyUpdate(): Promise<void> {
return this.channel.call('applyUpdate');
}
quitAndInstall(): Promise<void> {
return this.channel.call('quitAndInstall');
}
isLatestVersion(): Promise<boolean> {
return this.channel.call('isLatestVersion');
}
}
@@ -1,34 +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 { IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
import { IUpdateService } from 'vs/platform/update/common/update';
export class UpdateChannel implements IServerChannel {
constructor(private service: IUpdateService) { }
listen(_: unknown, event: string): Event<any> {
switch (event) {
case 'onStateChange': return this.service.onStateChange;
}
throw new Error(`Event not found: ${event}`);
}
call(_: unknown, command: string, arg?: any): Promise<any> {
switch (command) {
case 'checkForUpdates': return this.service.checkForUpdates(arg);
case 'downloadUpdate': return this.service.downloadUpdate();
case 'applyUpdate': return this.service.applyUpdate();
case 'quitAndInstall': return this.service.quitAndInstall();
case '_getInitialState': return Promise.resolve(this.service.state);
case 'isLatestVersion': return this.service.isLatestVersion();
}
throw new Error(`Call not found: ${command}`);
}
}
@@ -195,8 +195,29 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
try {
await synchroniser.sync(manifest, syncHeaders);
} catch (e) {
this.handleSynchronizerError(e, synchroniser.resource);
this._syncErrors.push([synchroniser.resource, UserDataSyncError.toUserDataSyncError(e)]);
if (e instanceof UserDataSyncError) {
// Bail out for following errors
switch (e.code) {
case UserDataSyncErrorCode.TooLarge:
throw new UserDataSyncError(e.message, e.code, synchroniser.resource);
case UserDataSyncErrorCode.TooManyRequests:
case UserDataSyncErrorCode.TooManyRequestsAndRetryAfter:
case UserDataSyncErrorCode.LocalTooManyRequests:
case UserDataSyncErrorCode.Gone:
case UserDataSyncErrorCode.UpgradeRequired:
case UserDataSyncErrorCode.IncompatibleRemoteContent:
case UserDataSyncErrorCode.IncompatibleLocalContent:
throw e;
}
}
// Log and report other errors and continue
const userDataSyncError = UserDataSyncError.toUserDataSyncError(e);
this.reportUserDataSyncError(userDataSyncError, executionId);
this.logService.error(e);
this.logService.error(`${synchroniser.resource}: ${toErrorMessage(e)}`);
this._syncErrors.push([synchroniser.resource, userDataSyncError]);
}
}
@@ -371,26 +392,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
}
private handleSynchronizerError(e: Error, source: SyncResource): void {
if (e instanceof UserDataSyncError) {
switch (e.code) {
case UserDataSyncErrorCode.TooLarge:
throw new UserDataSyncError(e.message, e.code, source);
case UserDataSyncErrorCode.TooManyRequests:
case UserDataSyncErrorCode.TooManyRequestsAndRetryAfter:
case UserDataSyncErrorCode.LocalTooManyRequests:
case UserDataSyncErrorCode.Gone:
case UserDataSyncErrorCode.UpgradeRequired:
case UserDataSyncErrorCode.IncompatibleRemoteContent:
case UserDataSyncErrorCode.IncompatibleLocalContent:
throw e;
}
}
this.logService.error(e);
this.logService.error(`${source}: ${toErrorMessage(e)}`);
}
private reportUserDataSyncError(userDataSyncError: UserDataSyncError, executionId: string) {
this.telemetryService.publicLog2<{ code: string, service: string, url?: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error',
{ code: userDataSyncError.code, url: userDataSyncError instanceof UserDataSyncStoreError ? userDataSyncError.url : undefined, resource: userDataSyncError.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() });
+130 -161
View File
@@ -1008,55 +1008,20 @@ declare module 'vscode' {
Idle = 2
}
// TODO@API
// make this a class, allow modified using with-pattern
export interface NotebookCellMetadata {
/**
* Controls whether a cell's editor is editable/readonly.
*/
editable?: boolean;
/**
* Controls if the cell is executable.
* This metadata is ignored for markdown cell.
*/
runnable?: boolean;
/**
* Controls if the cell has a margin to support the breakpoint UI.
* This metadata is ignored for markdown cell.
*/
breakpointMargin?: boolean;
/**
* Whether the [execution order](#NotebookCellMetadata.executionOrder) indicator will be displayed.
* Defaults to true.
*/
hasExecutionOrder?: boolean;
/**
* The order in which this cell was executed.
*/
executionOrder?: number;
/**
* A status message to be shown in the cell's status bar
*/
statusMessage?: string;
/**
* The cell's current run state
*/
runState?: NotebookCellRunState;
/**
* If the cell is running, the time at which the cell started running
*/
runStartTime?: number;
/**
* The total duration of the cell's last run
*/
lastRunDuration?: number;
/**
* Whether a code cell's editor is collapsed
*/
@@ -1081,13 +1046,16 @@ declare module 'vscode' {
readonly cellKind: CellKind;
readonly document: TextDocument;
readonly language: string;
readonly outputs: readonly NotebookCellOutput[];
readonly metadata: NotebookCellMetadata;
/** @deprecated use WorkspaceEdit.replaceCellOutput */
outputs: CellOutput[];
// outputs: CellOutput[];
// readonly outputs2: NotebookCellOutput[];
/** @deprecated use WorkspaceEdit.replaceCellMetadata */
metadata: NotebookCellMetadata;
// metadata: NotebookCellMetadata;
}
export interface NotebookDocumentMetadata {
/**
* Controls if users can add or delete cells
@@ -1095,30 +1063,11 @@ declare module 'vscode' {
*/
editable?: boolean;
/**
* Controls whether the full notebook can be run at once.
* Defaults to true
*/
runnable?: boolean;
/**
* Default value for [cell editable metadata](#NotebookCellMetadata.editable).
* Defaults to true.
*/
cellEditable?: boolean;
/**
* Default value for [cell runnable metadata](#NotebookCellMetadata.runnable).
* Defaults to true.
*/
cellRunnable?: boolean;
/**
* Default value for [cell hasExecutionOrder metadata](#NotebookCellMetadata.hasExecutionOrder).
* Defaults to true.
*/
cellHasExecutionOrder?: boolean;
displayOrder?: GlobPattern[];
/**
@@ -1126,11 +1075,6 @@ declare module 'vscode' {
*/
custom?: { [key: string]: any; };
/**
* The document's current run state
*/
runState?: NotebookRunState;
/**
* Whether the document is trusted, default to true
* When false, insecure outputs like HTML, JavaScript, SVG will not be rendered.
@@ -1166,10 +1110,15 @@ declare module 'vscode' {
readonly isUntitled: boolean;
readonly cells: ReadonlyArray<NotebookCell>;
readonly contentOptions: NotebookDocumentContentOptions;
// todo@API
// - move to kernel -> control runnable state of a cell
// - remove from this type
languages: string[];
metadata: NotebookDocumentMetadata;
readonly metadata: NotebookDocumentMetadata;
}
// todo@API maybe have a NotebookCellPosition sibling
// todo@API should be a class
export interface NotebookCellRange {
readonly start: number;
/**
@@ -1241,18 +1190,6 @@ declare module 'vscode' {
readonly onDidDispose: Event<void>;
}
// todo@API stale?
export interface NotebookOutputSelector {
mimeTypes?: string[];
}
// todo@API stale?
export interface NotebookRenderRequest {
output: CellDisplayOutput;
mimeType: string;
outputId: string;
}
export interface NotebookDocumentMetadataChangeEvent {
readonly document: NotebookDocument;
}
@@ -1273,17 +1210,6 @@ declare module 'vscode' {
readonly changes: ReadonlyArray<NotebookCellsChangeData>;
}
// todo@API stale?
export interface NotebookCellMoveEvent {
/**
* The affected document.
*/
readonly document: NotebookDocument;
readonly index: number;
readonly newIndex: number;
}
export interface NotebookCellOutputsChangeEvent {
/**
@@ -1326,7 +1252,7 @@ declare module 'vscode' {
readonly source: string;
readonly language: string;
// todo@API maybe use a separate data type?
readonly outputs: CellOutput[];
readonly outputs: NotebookCellOutput[];
readonly metadata: NotebookCellMetadata | undefined;
}
@@ -1416,86 +1342,29 @@ declare module 'vscode' {
//#region https://github.com/microsoft/vscode/issues/106744, NotebookCellOutput
export enum CellOutputKind {
Text = 1,
Error = 2,
Rich = 3
}
export interface CellStreamOutput {
outputKind: CellOutputKind.Text;
text: string;
}
export interface CellErrorOutput {
outputKind: CellOutputKind.Error;
/**
* Exception Name
*/
ename: string;
/**
* Exception Value
*/
evalue: string;
/**
* Exception call stack
*/
traceback: string[];
}
export interface NotebookCellOutputMetadata {
/**
* Additional attributes of a cell metadata.
*/
custom?: { [key: string]: any; };
}
export interface CellDisplayOutput {
outputKind: CellOutputKind.Rich;
/**
* { mime_type: value }
*
* Example:
* ```json
* {
* "outputKind": vscode.CellOutputKind.Rich,
* "data": {
* "text/html": [
* "<h1>Hello</h1>"
* ],
* "text/plain": [
* "<IPython.lib.display.IFrame at 0x11dee3e80>"
* ]
* }
* }
*/
data: { [key: string]: any; };
readonly metadata?: NotebookCellOutputMetadata;
}
export type CellOutput = CellStreamOutput | CellErrorOutput | CellDisplayOutput;
// code specific mime types
// application/x.notebook.error-traceback
// application/x.notebook.stream
export class NotebookCellOutputItem {
// todo@API
// add factory functions for common mime types
// static textplain(value:string): NotebookCellOutputItem;
// static errortrace(value:any): NotebookCellOutputItem;
readonly mime: string;
readonly value: unknown;
readonly metadata?: Record<string, string | number | boolean>;
readonly metadata?: Record<string, string | number | boolean | unknown>;
constructor(mime: string, value: unknown, metadata?: Record<string, string | number | boolean>);
constructor(mime: string, value: unknown, metadata?: Record<string, string | number | boolean | unknown>);
}
// @jrieken
//TODO@API add execution count to cell output?
export class NotebookCellOutput {
readonly id: string;
readonly outputs: NotebookCellOutputItem[];
constructor(outputs: NotebookCellOutputItem[]);
//TODO@jrieken HACK to workaround dependency issues...
toJSON(): any;
constructor(outputs: NotebookCellOutputItem[], id?: string);
}
//#endregion
@@ -1504,22 +1373,24 @@ declare module 'vscode' {
export interface WorkspaceEdit {
replaceNotebookMetadata(uri: Uri, value: NotebookDocumentMetadata): void;
// todo@API use NotebookCellRange
replaceNotebookCells(uri: Uri, start: number, end: number, cells: NotebookCellData[], metadata?: WorkspaceEditEntryMetadata): void;
replaceNotebookCellMetadata(uri: Uri, index: number, cellMetadata: NotebookCellMetadata, metadata?: WorkspaceEditEntryMetadata): void;
replaceNotebookCellOutput(uri: Uri, index: number, outputs: (NotebookCellOutput | CellOutput)[], metadata?: WorkspaceEditEntryMetadata): void;
appendNotebookCellOutput(uri: Uri, index: number, outputs: (NotebookCellOutput | CellOutput)[], metadata?: WorkspaceEditEntryMetadata): void;
replaceNotebookCellOutput(uri: Uri, index: number, outputs: NotebookCellOutput[], metadata?: WorkspaceEditEntryMetadata): void;
appendNotebookCellOutput(uri: Uri, index: number, outputs: NotebookCellOutput[], metadata?: WorkspaceEditEntryMetadata): void;
// TODO@api
// https://jupyter-protocol.readthedocs.io/en/latest/messaging.html#update-display-data
// replaceNotebookCellOutput(uri: Uri, index: number, outputId:string, outputs: NotebookCellOutputItem[], metadata?: WorkspaceEditEntryMetadata): void;
// appendNotebookCellOutput(uri: Uri, index: number, outputId:string, outputs: NotebookCellOutputItem[], metadata?: WorkspaceEditEntryMetadata): void;
replaceNotebookCellOutputItems(uri: Uri, index: number, outputId: string, items: NotebookCellOutputItem[], metadata?: WorkspaceEditEntryMetadata): void;
appendNotebookCellOutputItems(uri: Uri, index: number, outputId: string, items: NotebookCellOutputItem[], metadata?: WorkspaceEditEntryMetadata): void;
}
export interface NotebookEditorEdit {
replaceMetadata(value: NotebookDocumentMetadata): void;
replaceCells(start: number, end: number, cells: NotebookCellData[]): void;
replaceCellOutput(index: number, outputs: (NotebookCellOutput | CellOutput)[]): void;
replaceCellOutput(index: number, outputs: NotebookCellOutput[]): void;
replaceCellMetadata(index: number, metadata: NotebookCellMetadata): void;
}
@@ -1612,6 +1483,98 @@ declare module 'vscode' {
//#region https://github.com/microsoft/vscode/issues/106744, NotebookKernel
export interface NotebookDocumentMetadata {
/**
* Controls whether the full notebook can be run at once.
* Defaults to true
*/
// todo@API infer from kernel
// todo@API remove
runnable?: boolean;
/**
* Default value for [cell runnable metadata](#NotebookCellMetadata.runnable).
* Defaults to true.
*/
cellRunnable?: boolean;
/**
* Default value for [cell hasExecutionOrder metadata](#NotebookCellMetadata.hasExecutionOrder).
* Defaults to true.
*/
cellHasExecutionOrder?: boolean;
/**
* The document's current run state
*/
runState?: NotebookRunState;
}
// todo@API use the NotebookCellExecution-object as a container to model and enforce
// the flow of a cell execution
// kernel -> execute_info
// ext -> createNotebookCellExecution(cell)
// kernel -> done
// exec.dispose();
// export interface NotebookCellExecution {
// dispose(): void;
// clearOutput(): void;
// appendOutput(out: NotebookCellOutput): void;
// replaceOutput(out: NotebookCellOutput): void;
// appendOutputItems(output:string, items: NotebookCellOutputItem[]):void;
// replaceOutputItems(output:string, items: NotebookCellOutputItem[]):void;
// }
// export function createNotebookCellExecution(cell: NotebookCell, startTime?: number): NotebookCellExecution;
// export const onDidStartNotebookCellExecution: Event<any>;
// export const onDidStopNotebookCellExecution: Event<any>;
export interface NotebookCellMetadata {
/**
* Controls if the cell is executable.
* This metadata is ignored for markdown cell.
*/
// todo@API infer from kernel
runnable?: boolean;
/**
* Whether the [execution order](#NotebookCellMetadata.executionOrder) indicator will be displayed.
* Defaults to true.
*/
hasExecutionOrder?: boolean;
/**
* The order in which this cell was executed.
*/
executionOrder?: number;
/**
* A status message to be shown in the cell's status bar
*/
// todo@API duplicates status bar API
statusMessage?: string;
/**
* The cell's current run state
*/
runState?: NotebookCellRunState;
/**
* If the cell is running, the time at which the cell started running
*/
runStartTime?: number;
/**
* The total duration of the cell's last run
*/
// todo@API depends on having output
lastRunDuration?: number;
}
export interface NotebookKernel {
readonly id?: string;
label: string;
@@ -1619,6 +1582,12 @@ declare module 'vscode' {
detail?: string;
isPreferred?: boolean;
preloads?: Uri[];
// todo@API
// languages supported by kernel
// first is preferred
// languages: string[];
// @roblourens
// todo@API change to `executeCells(document: NotebookDocument, cells: NotebookCellRange[], context:{isWholeNotebooke: boolean}, token: CancelationToken): void;`
// todo@API interrupt vs cancellation, https://github.com/microsoft/vscode/issues/106741
@@ -23,7 +23,7 @@ import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookB
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService';
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookEditorModel, INotebookExclusiveDocumentFilter, NotebookCellOutputsSplice, NotebookCellsChangeType, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookEditorModel, INotebookExclusiveDocumentFilter, NotebookCellsChangeType, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { IEditorGroup, IEditorGroupsService, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService';
@@ -569,29 +569,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
textModel?.updateLanguages(languages);
}
async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[]): Promise<void> {
this.logService.debug('MainThreadNotebooks#spliceNotebookCellOutputs', resource.path, cellHandle);
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
if (!textModel) {
return;
}
const cell = textModel.cells.find(cell => cell.handle === cellHandle);
if (!cell) {
return;
}
textModel.applyEdits(textModel.versionId, [
{
editType: CellEditType.OutputsSplice,
index: textModel.cells.indexOf(cell),
splices
}
], true, undefined, () => undefined, undefined);
}
async $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise<boolean> {
const editor = this._notebookService.getNotebookEditor(editorId) as INotebookEditor | undefined;
if (editor?.isNotebookEditor) {
@@ -51,6 +51,17 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
}
}
/**
* @inheritdoc
*/
$retireTest(extId: string): void {
for (const result of this.resultService.results) {
if (result instanceof LiveTestResult) {
result.retire(extId);
}
}
}
/**
* @inheritdoc
*/
@@ -1257,10 +1257,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
// checkProposedApiEnabled(extension);
return extHostTypes.CellKind;
},
get CellOutputKind() {
// checkProposedApiEnabled(extension);
return extHostTypes.CellOutputKind;
},
get NotebookCellRunState() {
// checkProposedApiEnabled(extension);
return extHostTypes.NotebookCellRunState;
@@ -51,7 +51,7 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
import { TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions } from 'vs/platform/remote/common/tunnel';
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline';
import { revive } from 'vs/base/common/marshalling';
import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { Dto } from 'vs/base/common/types';
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
@@ -745,7 +745,7 @@ export interface ICellDto {
source: string[];
language: string;
cellKind: CellKind;
outputs: IProcessedOutput[];
outputs: IOutputDto[];
metadata?: NotebookCellMetadata;
}
@@ -758,7 +758,7 @@ export type NotebookCellsSplice = [
export type NotebookCellOutputsSplice = [
number /* start */,
number /* delete count */,
IProcessedOutput[]
IOutputDto[]
];
export enum NotebookEditorRevealType {
@@ -790,7 +790,6 @@ export interface MainThreadNotebookShape extends IDisposable {
$onNotebookKernelChange(handle: number, uri: UriComponents | undefined): void;
$tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise<boolean>;
$updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise<void>;
$spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[]): Promise<void>;
$postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise<boolean>;
$setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise<void>;
$tryOpenDocument(uriComponents: UriComponents, viewType?: string): Promise<URI>;
@@ -1843,6 +1842,7 @@ export interface MainThreadTestingShape {
$publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
$updateTestStateInRun(runId: string, testId: string, state: ITestState): void;
$runTests(req: RunTestsRequest, token: CancellationToken): Promise<string>;
$retireTest(extId: string): void;
}
// --- proxy identifiers
@@ -9,7 +9,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as UUID from 'vs/base/common/uuid';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorPropertiesChangeData, MainContext, MainThreadBulkEditsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorPropertiesChangeData, MainContext, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
import { ILogService } from 'vs/platform/log/common/log';
import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
@@ -17,7 +17,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import { addIdToOutput, CellStatusbarAlignment, CellUri, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookExclusiveDocumentFilter, INotebookKernelInfoDto2, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellStatusbarAlignment, CellUri, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookExclusiveDocumentFilter, INotebookKernelInfoDto2, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import * as vscode from 'vscode';
import { ResourceMap } from 'vs/base/common/map';
import { ExtHostCell, ExtHostNotebookDocument } from './extHostNotebookDocument';
@@ -214,7 +214,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
private static _notebookKernelProviderHandlePool: number = 0;
private readonly _proxy: MainThreadNotebookShape;
private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape;
private readonly _notebookContentProviders = new Map<string, { readonly provider: vscode.NotebookContentProvider, readonly extension: IExtensionDescription; }>();
private readonly _notebookKernelProviders = new Map<number, ExtHostNotebookKernelProviderAdapter>();
private readonly _documents = new ResourceMap<ExtHostNotebookDocument>();
@@ -271,7 +270,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
private readonly _extensionStoragePaths: IExtensionStoragePaths,
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook);
this._mainThreadBulkEdits = mainContext.getProxy(MainContext.MainThreadBulkEdits);
this._commandsConverter = commands.converter;
commands.registerArgumentProcessor({
@@ -456,10 +454,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
...data.metadata
},
languages: data.languages,
cells: data.cells.map(cell => ({
...cell,
outputs: cell.outputs.map(o => addIdToOutput(o))
})),
cells: data.cells.map(typeConverters.NotebookCellData.from),
};
}
@@ -711,7 +706,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
}
const that = this;
const document = new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, this._mainThreadBulkEdits, {
const document = new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, {
emitModelChange(event: vscode.NotebookCellsChangeEvent): void {
that._onDidChangeNotebookCells.fire(event);
},
@@ -5,39 +5,17 @@
import { Emitter, Event } from 'vs/base/common/event';
import { hash } from 'vs/base/common/hash';
import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { joinPath } from 'vs/base/common/resources';
import { ISplice } from 'vs/base/common/sequence';
import { URI } from 'vs/base/common/uri';
import * as UUID from 'vs/base/common/uuid';
import { CellKind, INotebookDocumentPropertiesChangeData, IWorkspaceCellEditDto, MainThreadBulkEditsShape, MainThreadNotebookShape, NotebookCellOutputsSplice, WorkspaceEditType } from 'vs/workbench/api/common/extHost.protocol';
import { CellKind, INotebookDocumentPropertiesChangeData, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { CellEditType, CellOutputKind, diff, IMainCellDto, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { IMainCellDto, IOutputDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import * as vscode from 'vscode';
interface IObservable<T> {
proxy: T;
onDidChange: Event<void>;
}
function getObservable<T extends Object>(obj: T): IObservable<T> {
const onDidChange = new Emitter<void>();
const proxy = new Proxy(obj, {
set(target: T, p: PropertyKey, value: any, _receiver: any): boolean {
target[p as keyof T] = value;
onDidChange.fire();
return true;
}
});
return {
proxy,
onDidChange: onDidChange.event
};
}
class RawContentChangeEvent {
constructor(readonly start: number, readonly deletedCount: number, readonly deletedItems: ExtHostCell[], readonly items: ExtHostCell[]) { }
@@ -69,14 +47,12 @@ export class ExtHostCell extends Disposable {
private _onDidDispose = new Emitter<void>();
readonly onDidDispose: Event<void> = this._onDidDispose.event;
private _onDidChangeOutputs = new Emitter<ISplice<IProcessedOutput>[]>();
readonly onDidChangeOutputs: Event<ISplice<IProcessedOutput>[]> = this._onDidChangeOutputs.event;
private _onDidChangeOutputs = new Emitter<ISplice<IOutputDto>[]>();
readonly onDidChangeOutputs: Event<ISplice<IOutputDto>[]> = this._onDidChangeOutputs.event;
private _outputs: any[];
private _outputMapping = new WeakMap<vscode.CellOutput, string | undefined /* output ID */>();
private _outputs: IOutputDto[];
private _metadata: vscode.NotebookCellMetadata;
private _metadataChangeListener: IDisposable;
readonly handle: number;
readonly uri: URI;
@@ -85,7 +61,6 @@ export class ExtHostCell extends Disposable {
private _cell: vscode.NotebookCell | undefined;
constructor(
private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape,
private readonly _notebook: ExtHostNotebookDocument,
private readonly _extHostDocument: ExtHostDocumentsAndEditors,
private readonly _cellData: IMainCellDto,
@@ -97,16 +72,9 @@ export class ExtHostCell extends Disposable {
this.cellKind = _cellData.cellKind;
this._outputs = _cellData.outputs;
for (const output of this._outputs) {
this._outputMapping.set(output, output.outputId);
delete output.outputId;
}
const observableMetadata = getObservable(_cellData.metadata ?? {});
this._metadata = observableMetadata.proxy;
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
this._updateMetadata();
}));
this._metadata = _cellData.metadata ?? {};
}
get cell(): vscode.NotebookCell {
@@ -123,13 +91,10 @@ export class ExtHostCell extends Disposable {
cellKind: this._cellData.cellKind,
document: data.document,
get language() { return data!.document.languageId; },
get outputs() { return that._outputs; },
set outputs(value) { that._updateOutputs(value); },
get outputs() { return that._outputs.map(extHostTypeConverters.NotebookCellOutput.to); },
set outputs(_value) { throw new Error('Use WorkspaceEdit to update cell outputs.'); },
get metadata() { return that._metadata; },
set metadata(value) {
that.setMetadata(value);
that._updateMetadata();
},
set metadata(_value) { throw new Error('Use WorkspaceEdit to update cell metadata.'); },
});
}
return this._cell;
@@ -140,61 +105,12 @@ export class ExtHostCell extends Disposable {
this._onDidDispose.fire();
}
setOutputs(newOutputs: vscode.CellOutput[]): void {
setOutputs(newOutputs: IOutputDto[]): void {
this._outputs = newOutputs;
}
private _updateOutputs(newOutputs: vscode.CellOutput[]) {
const rawDiffs = diff<vscode.CellOutput>(this._outputs || [], newOutputs || [], (a) => {
return this._outputMapping.has(a);
});
const transformedDiffs: ISplice<IProcessedOutput>[] = rawDiffs.map(diff => {
for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
this._outputMapping.delete(this._outputs[i]);
}
return {
deleteCount: diff.deleteCount,
start: diff.start,
toInsert: diff.toInsert.map((output): IProcessedOutput => {
if (output.outputKind === CellOutputKind.Rich) {
const uuid = UUID.generateUuid();
this._outputMapping.set(output, uuid);
return { ...output, outputId: uuid };
}
this._outputMapping.set(output, undefined);
return output;
})
};
});
this._outputs = newOutputs;
this._onDidChangeOutputs.fire(transformedDiffs);
}
setMetadata(newMetadata: vscode.NotebookCellMetadata): void {
// Don't apply metadata defaults here, 'undefined' means 'inherit from document metadata'
this._metadataChangeListener.dispose();
const observableMetadata = getObservable(newMetadata);
this._metadata = observableMetadata.proxy;
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
this._updateMetadata();
}));
}
private _updateMetadata(): Promise<boolean> {
const index = this._notebook.notebookDocument.cells.indexOf(this.cell);
const edit: IWorkspaceCellEditDto = {
_type: WorkspaceEditType.Cell,
metadata: undefined,
resource: this._notebook.uri,
notebookVersionId: this._notebook.notebookDocument.version,
edit: { editType: CellEditType.Metadata, index, metadata: this._metadata }
};
return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit({ edits: [edit] });
this._metadata = newMetadata;
}
}
@@ -221,8 +137,8 @@ export class ExtHostNotebookDocument extends Disposable {
private _cellDisposableMapping = new Map<number, DisposableStore>();
private _notebook: vscode.NotebookDocument | undefined;
private _metadata: Required<vscode.NotebookDocumentMetadata>;
private _metadataChangeListener: IDisposable;
// private _metadata: Required<vscode.NotebookDocumentMetadata>;
// private _metadataChangeListener: IDisposable;
private _versionId = 0;
private _isDirty: boolean = false;
private _backupCounter = 1;
@@ -233,21 +149,14 @@ export class ExtHostNotebookDocument extends Disposable {
constructor(
private readonly _proxy: MainThreadNotebookShape,
private readonly _documentsAndEditors: ExtHostDocumentsAndEditors,
private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape,
private readonly _emitter: INotebookEventEmitter,
private readonly _viewType: string,
private readonly _contentOptions: vscode.NotebookDocumentContentOptions,
metadata: Required<vscode.NotebookDocumentMetadata>,
private _metadata: Required<vscode.NotebookDocumentMetadata>,
public readonly uri: URI,
private readonly _storagePath: URI | undefined
) {
super();
const observableMetadata = getObservable(metadata);
this._metadata = observableMetadata.proxy;
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
this._tryUpdateMetadata();
}));
}
dispose() {
@@ -256,36 +165,6 @@ export class ExtHostNotebookDocument extends Disposable {
dispose(this._cellDisposableMapping.values());
}
private _updateMetadata(newMetadata: Required<vscode.NotebookDocumentMetadata>) {
this._metadataChangeListener.dispose();
newMetadata = {
...notebookDocumentMetadataDefaults,
...newMetadata
};
if (this._metadataChangeListener) {
this._metadataChangeListener.dispose();
}
const observableMetadata = getObservable(newMetadata);
this._metadata = observableMetadata.proxy;
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
this._tryUpdateMetadata();
}));
this._tryUpdateMetadata();
}
private _tryUpdateMetadata() {
const edit: IWorkspaceCellEditDto = {
_type: WorkspaceEditType.Cell,
metadata: undefined,
edit: { editType: CellEditType.DocumentMetadata, metadata: this._metadata },
resource: this.uri,
notebookVersionId: this.notebookDocument.version,
};
return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit({ edits: [edit] });
}
get notebookDocument(): vscode.NotebookDocument {
if (!this._notebook) {
@@ -301,7 +180,7 @@ export class ExtHostNotebookDocument extends Disposable {
get languages() { return that._languages; },
set languages(value: string[]) { that._trySetLanguages(value); },
get metadata() { return that._metadata; },
set metadata(value: Required<vscode.NotebookDocumentMetadata>) { that._updateMetadata(value); },
set metadata(_value: Required<vscode.NotebookDocumentMetadata>) { throw new Error('Use WorkspaceEdit to update metadata.'); },
get contentOptions() { return that._contentOptions; }
});
}
@@ -336,17 +215,7 @@ export class ExtHostNotebookDocument extends Disposable {
...notebookDocumentMetadataDefaults,
...data.metadata
};
if (this._metadataChangeListener) {
this._metadataChangeListener.dispose();
}
const observableMetadata = getObservable(newMetadata);
this._metadata = observableMetadata.proxy;
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
this._tryUpdateMetadata();
}));
this._metadata = newMetadata;
this._emitter.emitDocumentMetadataChange({ document: this.notebookDocument });
}
@@ -383,7 +252,7 @@ export class ExtHostNotebookDocument extends Disposable {
const cellDtos = splice[2];
const newCells = cellDtos.map(cell => {
const extCell = new ExtHostCell(this._mainThreadBulkEdits, this, this._documentsAndEditors, cell);
const extCell = new ExtHostCell(this, this._documentsAndEditors, cell);
if (!initialization) {
addedCellDocuments.push(ExtHostCell.asModelAddData(this.notebookDocument, cell));
@@ -395,12 +264,6 @@ export class ExtHostNotebookDocument extends Disposable {
this._cellDisposableMapping.set(extCell.handle, store);
}
const store = this._cellDisposableMapping.get(extCell.handle)!;
store.add(extCell.onDidChangeOutputs((diffs) => {
this.eventuallyUpdateCellOutputs(extCell, diffs);
}));
return extCell;
});
@@ -450,7 +313,7 @@ export class ExtHostNotebookDocument extends Disposable {
});
}
private _setCellOutputs(index: number, outputs: IProcessedOutput[]): void {
private _setCellOutputs(index: number, outputs: IOutputDto[]): void {
const cell = this._cells[index];
cell.setOutputs(outputs);
this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] });
@@ -469,23 +332,6 @@ export class ExtHostNotebookDocument extends Disposable {
this._emitter.emitCellMetadataChange(event);
}
async eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice<IProcessedOutput>[]) {
const outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => {
const outputs = diff.toInsert;
return [diff.start, diff.deleteCount, outputs];
});
if (!outputDtos.length) {
return;
}
await this._proxy.$spliceNotebookCellOutputs(this._viewType, this.uri, cell.handle, outputDtos);
this._emitter.emitCellOutputsChange({
document: this.notebookDocument,
cells: [cell.cell]
});
}
getCell(cellHandle: number): ExtHostCell | undefined {
return this._cells.find(cell => cell.handle === cellHandle);
}
@@ -8,10 +8,16 @@ import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { addIdToOutput, CellEditType, ICellEditOperation, ICellReplaceEdit, INotebookEditData, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import * as extHostConverter from 'vs/workbench/api/common/extHostTypeConverters';
import { CellEditType, ICellEditOperation, ICellReplaceEdit, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import * as vscode from 'vscode';
import { ExtHostNotebookDocument } from './extHostNotebookDocument';
interface INotebookEditData {
documentVersionId: number;
cellEdits: ICellEditOperation[];
}
class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
private readonly _documentVersionId: number;
@@ -54,17 +60,13 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
});
}
replaceCellOutput(index: number, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[]): void {
replaceCellOutput(index: number, outputs: vscode.NotebookCellOutput[]): void {
this._throwIfFinalized();
this._collectedEdits.push({
editType: CellEditType.Output,
index,
outputs: outputs.map(output => {
if (extHostTypes.NotebookCellOutput.isNotebookCellOutput(output)) {
return addIdToOutput(output.toJSON());
} else {
return addIdToOutput(output);
}
return extHostConverter.NotebookCellOutput.from(output);
})
});
}
@@ -78,12 +80,7 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
editType: CellEditType.Replace,
index: from,
count: to - from,
cells: cells.map(data => {
return {
...data,
outputs: data.outputs.map(output => addIdToOutput(output)),
};
})
cells: cells.map(extHostConverter.NotebookCellData.from)
});
}
}
@@ -170,6 +170,14 @@ export class ExtHostTesting implements ExtHostTestingShape {
collection.addRoot(hierarchy.root, id);
Promise.resolve(hierarchy.discoveredInitialTests).then(() => collection.pushDiff([TestDiffOpType.DeltaDiscoverComplete, -1]));
hierarchy.onDidChangeTest(e => collection.onItemChange(e, id));
hierarchy.onDidInvalidateTest?.(e => {
const internal = collection.getTestByReference(e);
if (!internal) {
console.warn(`Received a TestProvider.onDidInvalidateTest for a test that does not currently exist.`);
} else {
this.proxy.$retireTest(internal.item.extId);
}
});
} catch (e) {
console.error(e);
}
@@ -31,7 +31,7 @@ import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
import { CellOutputKind, IDisplayOutput, INotebookDecorationRenderOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellEditType, ICellDto2, INotebookDecorationRenderOptions, IOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ITestItem, ITestState } from 'vs/workbench/contrib/testing/common/testCollection';
export interface PositionLike {
@@ -546,6 +546,60 @@ export namespace WorkspaceEdit {
notebookMetadata: entry.notebookMetadata,
notebookVersionId: notebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version
});
} else if (entry._type === types.FileEditType.CellOutput) {
if (entry.newOutputs) {
result.edits.push({
_type: extHostProtocol.WorkspaceEditType.Cell,
metadata: entry.metadata,
resource: entry.uri,
edit: {
editType: CellEditType.Output,
index: entry.index,
append: entry.append,
outputs: entry.newOutputs.map(NotebookCellOutput.from)
}
});
}
// todo@joh merge metadata and output edit?
if (entry.newMetadata) {
result.edits.push({
_type: extHostProtocol.WorkspaceEditType.Cell,
metadata: entry.metadata,
resource: entry.uri,
edit: {
editType: CellEditType.Metadata,
index: entry.index,
metadata: entry.newMetadata
}
});
}
} else if (entry._type === types.FileEditType.CellReplace) {
result.edits.push({
_type: extHostProtocol.WorkspaceEditType.Cell,
metadata: entry.metadata,
resource: entry.uri,
notebookVersionId: notebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version,
edit: {
editType: CellEditType.Replace,
index: entry.index,
count: entry.count,
cells: entry.cells.map(NotebookCellData.from)
}
});
} else if (entry._type === types.FileEditType.CellOutputItem) {
result.edits.push({
_type: extHostProtocol.WorkspaceEditType.Cell,
metadata: entry.metadata,
resource: entry.uri,
edit: {
editType: CellEditType.OutputItems,
index: entry.index,
outputId: entry.outputId,
items: entry.newOutputItems?.map(item => ({ mime: item.mime, value: item.value, metadata: item.metadata })) || [],
append: entry.append
}
});
}
}
}
@@ -1290,22 +1344,54 @@ export namespace LanguageSelector {
}
}
export namespace NotebookCellOutput {
export function from(output: types.NotebookCellOutput): IDisplayOutput {
return output.toJSON();
}
}
export namespace NotebookCellData {
export namespace NotebookCellOutputItem {
export function from(output: types.NotebookCellOutputItem): IDisplayOutput {
export function from(data: vscode.NotebookCellData): ICellDto2 {
return {
outputKind: CellOutputKind.Rich,
data: { [output.mime]: output.value },
metadata: output.metadata && { custom: output.metadata }
cellKind: data.cellKind,
language: data.language,
source: data.source,
metadata: data.metadata,
outputs: data.outputs.map(output => ({
outputId: output.id, outputs: (output.outputs || []).map(op => ({
mime: op.mime,
value: op.value,
metadata: op.metadata
}))
}))
};
}
}
export namespace NotebookCellOutput {
export function from(output: types.NotebookCellOutput): IOutputDto {
const data = Object.create(null);
const custom = Object.create(null);
for (let item of output.outputs) {
data[item.mime] = item.value;
custom[item.mime] = item.metadata;
}
return {
outputId: output.id,
outputs: (output.outputs || []).map(op => ({
mime: op.mime,
value: op.value,
metadata: op.metadata
})) || [],
// metadata: isEmptyObject(custom) ? undefined : { custom }
};
}
export function to(output: IOutputDto): vscode.NotebookCellOutput {
const items: types.NotebookCellOutputItem[] = output.outputs.map(op => new types.NotebookCellOutputItem(op.mime, op.value, op.metadata));
return new types.NotebookCellOutput(items, output.outputId);
}
}
export namespace NotebookExclusiveDocumentPattern {
export function from(pattern: { include: vscode.GlobPattern | undefined, exclude: vscode.GlobPattern | undefined }): { include: string | types.RelativePattern | undefined, exclude: string | types.RelativePattern | undefined };
export function from(pattern: vscode.GlobPattern): string | types.RelativePattern;
+62 -56
View File
@@ -13,7 +13,7 @@ import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files';
import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { addIdToOutput, CellEditType, ICellEditOperation, ICellOutputEdit, ITransformedDisplayOutputDto, notebookDocumentMetadataDefaults, NOTEBOOK_DISPLAY_ORDER } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellEditType, ICellEditOperation, notebookDocumentMetadataDefaults, NOTEBOOK_DISPLAY_ORDER } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import type * as vscode from 'vscode';
function es5ClassCompat(target: Function): any {
@@ -585,7 +585,10 @@ export interface IFileOperationOptions {
export const enum FileEditType {
File = 1,
Text = 2,
Cell = 3
Cell = 3,
CellOutput = 4,
CellReplace = 5,
CellOutputItem = 6
}
export interface IFileOperation {
@@ -611,13 +614,45 @@ export interface IFileCellEdit {
metadata?: vscode.WorkspaceEditEntryMetadata;
}
export interface ICellEdit {
_type: FileEditType.CellReplace;
metadata?: vscode.WorkspaceEditEntryMetadata;
uri: URI;
index: number;
count: number;
cells: vscode.NotebookCellData[];
}
export interface ICellOutputEdit {
_type: FileEditType.CellOutput;
uri: URI;
index: number;
append: boolean;
newOutputs?: NotebookCellOutput[];
newMetadata?: vscode.NotebookCellMetadata;
metadata?: vscode.WorkspaceEditEntryMetadata;
}
export interface ICellOutputItemsEdit {
_type: FileEditType.CellOutputItem;
uri: URI;
index: number;
outputId: string;
append: boolean;
newOutputItems?: NotebookCellOutputItem[];
metadata?: vscode.WorkspaceEditEntryMetadata;
}
type WorkspaceEditEntry = IFileOperation | IFileTextEdit | IFileCellEdit | ICellEdit | ICellOutputEdit | ICellOutputItemsEdit;
@es5ClassCompat
export class WorkspaceEdit implements vscode.WorkspaceEdit {
private readonly _edits = new Array<IFileOperation | IFileTextEdit | IFileCellEdit>();
private readonly _edits: WorkspaceEditEntry[] = [];
_allEntries(): ReadonlyArray<IFileTextEdit | IFileOperation | IFileCellEdit> {
_allEntries(): ReadonlyArray<WorkspaceEditEntry> {
return this._edits;
}
@@ -643,32 +678,33 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
replaceNotebookCells(uri: URI, start: number, end: number, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
if (start !== end || cells.length > 0) {
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Replace, index: start, count: end - start, cells: cells.map(cell => ({ ...cell, outputs: cell.outputs.map(output => addIdToOutput(output)) })) } });
this._edits.push({ _type: FileEditType.CellReplace, uri, index: start, count: end - start, cells, metadata });
}
}
replaceNotebookCellOutput(uri: URI, index: number, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
replaceNotebookCellOutput(uri: URI, index: number, outputs: vscode.NotebookCellOutput[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
this._editNotebookCellOutput(uri, index, false, outputs, metadata);
}
appendNotebookCellOutput(uri: URI, index: number, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
appendNotebookCellOutput(uri: URI, index: number, outputs: vscode.NotebookCellOutput[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
this._editNotebookCellOutput(uri, index, true, outputs, metadata);
}
private _editNotebookCellOutput(uri: URI, index: number, append: boolean, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void {
const edit: ICellOutputEdit = {
editType: CellEditType.Output,
index,
append,
outputs: outputs.map(output => {
if (NotebookCellOutput.isNotebookCellOutput(output)) {
return output.toJSON();
} else {
return addIdToOutput(output);
}
})
};
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit });
replaceNotebookCellOutputItems(uri: URI, index: number, outputId: string, items: NotebookCellOutputItem[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
this._editNotebookCellOutputItems(uri, index, outputId, false, items, metadata);
}
appendNotebookCellOutputItems(uri: URI, index: number, outputId: string, items: NotebookCellOutputItem[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
this._editNotebookCellOutputItems(uri, index, outputId, true, items, metadata);
}
private _editNotebookCellOutputItems(uri: URI, index: number, id: string, append: boolean, items: vscode.NotebookCellOutputItem[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void {
this._edits.push({ _type: FileEditType.CellOutputItem, metadata, uri, index, outputId: id, append, newOutputItems: items });
}
private _editNotebookCellOutput(uri: URI, index: number, append: boolean, outputs: vscode.NotebookCellOutput[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void {
let newOutputs: NotebookCellOutput[] = outputs;
this._edits.push({ _type: FileEditType.CellOutput, metadata, uri, index, append, newOutputs, newMetadata: undefined });
}
replaceNotebookCellMetadata(uri: URI, index: number, cellMetadata: vscode.NotebookCellMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
@@ -2888,39 +2924,15 @@ export class NotebookCellOutputItem {
constructor(
readonly mime: string,
readonly value: unknown, // JSON'able
readonly metadata?: Record<string, string | number | boolean>
readonly metadata?: any
) { }
}
export class NotebookCellOutput {
static isNotebookCellOutput(obj: unknown): obj is vscode.NotebookCellOutput {
return obj instanceof NotebookCellOutput;
}
readonly id: string = generateUuid();
constructor(readonly outputs: NotebookCellOutputItem[]) { }
toJSON(): ITransformedDisplayOutputDto {
let data: { [key: string]: unknown; } = {};
let custom: { [key: string]: unknown; } = {};
let hasMetadata = false;
for (let item of this.outputs) {
data[item.mime] = item.value;
if (item.metadata) {
custom[item.mime] = item.metadata;
hasMetadata = true;
}
}
return {
outputId: this.id,
outputKind: CellOutputKind.Rich,
data,
metadata: hasMetadata ? { custom } : undefined
};
}
constructor(
readonly outputs: NotebookCellOutputItem[],
readonly id: string = generateUuid()
) { }
}
export enum CellKind {
@@ -2928,12 +2940,6 @@ export enum CellKind {
Code = 2
}
export enum CellOutputKind {
Text = 1,
Error = 2,
Rich = 3
}
export enum NotebookCellRunState {
Running = 1,
Idle = 2,
@@ -192,7 +192,8 @@ export abstract class ViewPane extends Pane implements IView {
return this._titleDescription;
}
private readonly menuActions: ViewMenuActions;
readonly menuActions: ViewMenuActions;
private progressBar!: ProgressBar;
private progressIndicator!: IProgressIndicator;
@@ -478,7 +479,7 @@ export abstract class ViewPane extends Pane implements IView {
private setActions(): void {
if (this.toolbar) {
this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()));
this.toolbar.setActions(prepareActions(this.menuActions.getPrimaryActions()), prepareActions(this.menuActions.getSecondaryActions()));
this.toolbar.context = this.getActionsContext();
}
}
@@ -496,18 +497,6 @@ export abstract class ViewPane extends Pane implements IView {
this._onDidChangeTitleArea.fire();
}
getActions(): IAction[] {
return this.menuActions.getPrimaryActions();
}
getSecondaryActions(): IAction[] {
return this.menuActions.getSecondaryActions();
}
getContextMenuActions(): IAction[] {
return this.menuActions.getContextMenuActions();
}
getActionViewItem(action: IAction): IActionViewItem | undefined {
return createActionViewItem(this.instantiationService, action);
}
@@ -583,13 +583,13 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
const result = [];
result.push(...this.menuActions.getPrimaryActions());
if (this.isViewMergedWithContainer()) {
result.push(...this.paneItems[0].pane.getActions());
result.push(...this.paneItems[0].pane.menuActions.getPrimaryActions());
}
return result;
}
getSecondaryActions(): IAction[] {
const viewPaneActions = this.isViewMergedWithContainer() ? this.paneItems[0].pane.getSecondaryActions() : [];
const viewPaneActions = this.isViewMergedWithContainer() ? this.paneItems[0].pane.menuActions.getSecondaryActions() : [];
let menuActions = this.menuActions.getSecondaryActions();
const viewsSubmenuActionIndex = menuActions.findIndex(action => action instanceof SubmenuItemAction && action.item.submenu === ViewsSubMenu);
@@ -768,7 +768,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
event.stopPropagation();
event.preventDefault();
const actions: IAction[] = viewPane.getContextMenuActions();
const actions: IAction[] = viewPane.menuActions.getContextMenuActions();
let anchor: { x: number, y: number } = { x: event.posx, y: event.posy };
this.contextMenuService.showContextMenu({
@@ -7,7 +7,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { Disposable } from 'vs/base/common/lifecycle';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { IDisplayMainService } from 'vs/platform/display/common/displayMainService';
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { clearAllFontInfos } from 'vs/editor/browser/config/configuration';
@@ -465,7 +465,7 @@ class ExceptionBreakpointsRenderer implements IListRenderer<IExceptionBreakpoint
renderElement(exceptionBreakpoint: IExceptionBreakpoint, index: number, data: IExceptionBreakpointTemplateData): void {
data.context = exceptionBreakpoint;
data.name.textContent = exceptionBreakpoint.label || `${exceptionBreakpoint.filter} exceptions`;
data.breakpoint.title = data.name.textContent;
data.breakpoint.title = exceptionBreakpoint.description || data.name.textContent;
data.checkbox.checked = exceptionBreakpoint.enabled;
data.condition.textContent = exceptionBreakpoint.condition || '';
data.condition.title = localize('expressionCondition', "Expression condition: {0}", exceptionBreakpoint.condition);
@@ -763,7 +763,6 @@ class ExceptionBreakpointInputRenderer implements IListRenderer<IExceptionBreakp
this.view.breakpointInputFocused.set(true);
const inputBoxContainer = dom.append(breakpoint, $('.inputBoxContainer'));
const inputBox = new InputBox(inputBoxContainer, this.contextViewService, {
placeholder: localize('exceptionBreakpointPlaceholder', "Break when expression evaluates to true"),
ariaLabel: localize('exceptionBreakpointAriaLabel', "Type exception breakpoint condition")
});
const styler = attachInputBoxStyler(inputBox, this.themeService);
@@ -800,6 +799,8 @@ class ExceptionBreakpointInputRenderer implements IListRenderer<IExceptionBreakp
}
renderElement(exceptionBreakpoint: ExceptionBreakpoint, _index: number, data: IExceptionBreakpointInputTemplateData): void {
const placeHolder = exceptionBreakpoint.conditionDescription || localize('exceptionBreakpointPlaceholder', "Break when expression evaluates to true");
data.inputBox.setPlaceHolder(placeHolder);
data.breakpoint = exceptionBreakpoint;
data.checkbox.checked = exceptionBreakpoint.enabled;
data.checkbox.disabled = true;
@@ -411,6 +411,7 @@ export interface IExceptionBreakpoint extends IEnablement {
readonly filter: string;
readonly label: string;
readonly condition: string | undefined;
readonly description: string | undefined;
}
export interface IDataBreakpoint extends IBaseBreakpoint {
@@ -865,7 +865,15 @@ export class DataBreakpoint extends BaseBreakpoint implements IDataBreakpoint {
export class ExceptionBreakpoint extends Enablement implements IExceptionBreakpoint {
constructor(public filter: string, public label: string, enabled: boolean, public supportsCondition: boolean, public condition: string | undefined) {
constructor(
public filter: string,
public label: string,
enabled: boolean,
public supportsCondition: boolean,
public condition: string | undefined,
public description: string | undefined,
public conditionDescription: string | undefined
) {
super(enabled, generateUuid());
}
@@ -1074,14 +1082,15 @@ export class DebugModel implements IDebugModel {
setExceptionBreakpoints(data: DebugProtocol.ExceptionBreakpointsFilter[]): void {
if (data) {
if (this.exceptionBreakpoints.length === data.length && this.exceptionBreakpoints.every((exbp, i) => exbp.filter === data[i].filter && exbp.label === data[i].label && exbp.supportsCondition === data[i].supportsCondition)) {
if (this.exceptionBreakpoints.length === data.length && this.exceptionBreakpoints.every((exbp, i) =>
exbp.filter === data[i].filter && exbp.label === data[i].label && exbp.supportsCondition === data[i].supportsCondition && exbp.conditionDescription === data[i].conditionDescription && exbp.description === data[i].description)) {
// No change
return;
}
this.exceptionBreakpoints = data.map(d => {
const ebp = this.exceptionBreakpoints.filter(ebp => ebp.filter === d.filter).pop();
return new ExceptionBreakpoint(d.filter, d.label, ebp ? ebp.enabled : !!d.default, !!d.supportsCondition, ebp?.condition);
return new ExceptionBreakpoint(d.filter, d.label, ebp ? ebp.enabled : !!d.default, !!d.supportsCondition, ebp?.condition, d.description, d.conditionDescription);
});
this._onDidChangeBreakpoints.fire(undefined);
}
@@ -58,7 +58,7 @@ export class DebugStorage {
let result: ExceptionBreakpoint[] | undefined;
try {
result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => {
return new ExceptionBreakpoint(exBreakpoint.filter, exBreakpoint.label, exBreakpoint.enabled, exBreakpoint.supportsCondition, exBreakpoint.condition);
return new ExceptionBreakpoint(exBreakpoint.filter, exBreakpoint.label, exBreakpoint.enabled, exBreakpoint.supportsCondition, exBreakpoint.condition, exBreakpoint.description, exBreakpoint.conditionDescription);
});
} catch (e) { }
@@ -5,7 +5,7 @@
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
export class ExtensionHostDebugService extends ExtensionHostDebugChannelClient {
@@ -13,7 +13,7 @@ import { IExtensionManagementServerService } from 'vs/workbench/services/extensi
import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
import { ILabelService } from 'vs/platform/label/common/label';
import { extensionButtonProminentBackground, extensionButtonProminentForeground, ExtensionToolTipAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { IThemeService, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IThemeService, IColorTheme, ThemeIcon, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { EXTENSION_BADGE_REMOTE_BACKGROUND, EXTENSION_BADGE_REMOTE_FOREGROUND } from 'vs/workbench/common/theme';
import { Emitter, Event } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -21,6 +21,7 @@ import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { installCountIcon, ratingIcon, remoteIcon, starEmptyIcon, starFullIcon, starHalfIcon, syncIgnoredIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons';
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
export abstract class ExtensionWidget extends Disposable implements IExtensionContainer {
private _extension: IExtension | null = null;
@@ -368,3 +369,13 @@ export class SyncIgnoredWidget extends ExtensionWidget {
this.element.classList.toggle('hide', !(this.extension && this.extension.state === ExtensionState.Installed && this.userDataAutoSyncEnablementService.isEnabled() && this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension)));
}
}
// Rating icon
export const extensionRatingIconColor = registerColor('extensionIcon.starForeground', { light: '#DF6100', dark: '#FF8E00', hc: '#FF8E00' }, localize('extensionIconStarForeground', "The icon color for extension ratings."), true);
registerThemingParticipant((theme, collector) => {
const extensionRatingIcon = theme.getColor(extensionRatingIconColor);
if (extensionRatingIcon) {
collector.addRule(`.extension-ratings .codicon-extensions-star-full, .extension-ratings .codicon-extensions-star-half { color: ${extensionRatingIcon}; }`);
}
});
@@ -27,12 +27,6 @@
margin-left: 0;
}
/* TODO @misolori make this a color token */
.extension-ratings .codicon-extensions-star-full,
.extension-ratings .codicon-extensions-star-half {
color: #FF8E00 !important;
}
.extension-install-count .codicon,
.extension-action.codicon-extensions-info-message,
.extension-action.codicon-extensions-warning-message,
@@ -24,7 +24,7 @@ import { ExtensionsAutoProfiler } from 'vs/workbench/contrib/extensions/electron
import { OpenExtensionsFolderAction } from 'vs/workbench/contrib/extensions/electron-sandbox/extensionsActions';
import { ExtensionsLabel } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations';
import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/sharedProcessService';
import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { ExtensionRecommendationNotificationServiceChannel } from 'vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc';
import { Codicon } from 'vs/base/common/codicons';
@@ -40,7 +40,7 @@ import { IExperimentService } from 'vs/workbench/contrib/experiments/common/expe
import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/sharedProcessService';
import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { FileService } from 'vs/platform/files/common/fileService';
import { NullLogService, ILogService } from 'vs/platform/log/common/log';
import { IFileService } from 'vs/platform/files/common/files';
@@ -35,7 +35,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-sandbox/remoteAgentServiceImpl';
import { ExtensionIdentifier, IExtensionContributions, ExtensionType, IExtensionDescription, IExtension } from 'vs/platform/extensions/common/extensions';
import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/sharedProcessService';
import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ILabelService, IFormatterChangeEvent } from 'vs/platform/label/common/label';
import { IProductService } from 'vs/platform/product/common/productService';
@@ -37,7 +37,7 @@ import { IExperimentService, ExperimentState, ExperimentActionType, ExperimentSe
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-sandbox/remoteAgentServiceImpl';
import { ExtensionType, IExtension, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/sharedProcessService';
import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
import { IMenuService } from 'vs/platform/actions/common/actions';
@@ -37,7 +37,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtensionType, IExtension, ExtensionKind } from 'vs/platform/extensions/common/extensions';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-sandbox/remoteAgentServiceImpl';
import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/sharedProcessService';
import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices';
import { IProductService } from 'vs/platform/product/common/productService';
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
@@ -14,7 +14,7 @@ import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/wi
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { CellEditType, CellUri, IProcessedOutput, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellEditType, CellUri, IOutputDto, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IMenu, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
@@ -564,8 +564,8 @@ abstract class AbstractElementRenderer extends Disposable {
}
}
private _getFormatedOutputJSON(outputs: IProcessedOutput[]) {
return JSON.stringify(outputs, undefined, '\t');
private _getFormatedOutputJSON(outputs: IOutputDto[]) {
return JSON.stringify(outputs.map(op => ({ outputs: op.outputs })), undefined, '\t');
}
private _buildOutputEditor() {
@@ -9,7 +9,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { DiffElementViewModelBase, SideBySideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel';
import { DiffSide, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser';
import { ICellOutputViewModel, IDisplayOutputViewModel, IRenderOutput, outputHasDynamicHeight, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { ICellOutputViewModel, IRenderOutput, outputHasDynamicHeight, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { BUILTIN_RENDERER_ID, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon';
@@ -49,38 +49,38 @@ export class OutputElement extends Disposable {
const outputItemDiv = document.createElement('div');
let result: IRenderOutput | undefined = undefined;
if (this.output.isDisplayOutput()) {
const [mimeTypes, pick] = this.output.resolveMimeTypes(this._notebookTextModel);
const pickedMimeTypeRenderer = mimeTypes[pick];
if (mimeTypes.length > 1) {
outputItemDiv.style.position = 'relative';
const mimeTypePicker = DOM.$('.multi-mimetype-output');
mimeTypePicker.classList.add(...ThemeIcon.asClassNameArray(mimetypeIcon));
mimeTypePicker.tabIndex = 0;
mimeTypePicker.title = nls.localize('mimeTypePicker', "Choose a different output mimetype, available mimetypes: {0}", mimeTypes.map(mimeType => mimeType.mimeType).join(', '));
outputItemDiv.appendChild(mimeTypePicker);
this.resizeListener.add(DOM.addStandardDisposableListener(mimeTypePicker, 'mousedown', async e => {
if (e.leftButton) {
e.preventDefault();
e.stopPropagation();
await this.pickActiveMimeTypeRenderer(this._notebookTextModel, this.output as IDisplayOutputViewModel);
}
}));
const [mimeTypes, pick] = this.output.resolveMimeTypes(this._notebookTextModel);
const pickedMimeTypeRenderer = mimeTypes[pick];
if (mimeTypes.length > 1) {
outputItemDiv.style.position = 'relative';
const mimeTypePicker = DOM.$('.multi-mimetype-output');
mimeTypePicker.classList.add(...ThemeIcon.asClassNameArray(mimetypeIcon));
mimeTypePicker.tabIndex = 0;
mimeTypePicker.title = nls.localize('mimeTypePicker', "Choose a different output mimetype, available mimetypes: {0}", mimeTypes.map(mimeType => mimeType.mimeType).join(', '));
outputItemDiv.appendChild(mimeTypePicker);
this.resizeListener.add(DOM.addStandardDisposableListener(mimeTypePicker, 'mousedown', async e => {
if (e.leftButton) {
e.preventDefault();
e.stopPropagation();
await this.pickActiveMimeTypeRenderer(this._notebookTextModel, this.output);
}
}));
this.resizeListener.add((DOM.addDisposableListener(mimeTypePicker, DOM.EventType.KEY_DOWN, async e => {
const event = new StandardKeyboardEvent(e);
if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) {
e.preventDefault();
e.stopPropagation();
await this.pickActiveMimeTypeRenderer(this._notebookTextModel, this.output as IDisplayOutputViewModel);
}
})));
}
this.resizeListener.add((DOM.addDisposableListener(mimeTypePicker, DOM.EventType.KEY_DOWN, async e => {
const event = new StandardKeyboardEvent(e);
if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) {
e.preventDefault();
e.stopPropagation();
await this.pickActiveMimeTypeRenderer(this._notebookTextModel, this.output);
}
})));
}
const innerContainer = DOM.$('.output-inner-container');
DOM.append(outputItemDiv, innerContainer);
const innerContainer = DOM.$('.output-inner-container');
DOM.append(outputItemDiv, innerContainer);
if (mimeTypes.length !== 0) {
if (pickedMimeTypeRenderer.rendererId !== BUILTIN_RENDERER_ID) {
const renderer = this._notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);
result = renderer
@@ -91,12 +91,6 @@ export class OutputElement extends Disposable {
}
this.output.pickedMimeType = pick;
} else {
// for text and error, there is no mimetype
const innerContainer = DOM.$('.output-inner-container');
DOM.append(outputItemDiv, innerContainer);
result = this._notebookEditor.getOutputRenderer().render(this.output, innerContainer, undefined, this._notebookTextModel.uri);
}
this.domNode = outputItemDiv;
@@ -113,7 +107,7 @@ export class OutputElement extends Disposable {
this._outputContainer.appendChild(outputItemDiv);
}
if (result.type !== RenderOutputType.None) {
if (result.type !== RenderOutputType.Mainframe) {
// this.viewCell.selfSizeMonitoring = true;
this._notebookEditor.createInset(
this._diffElementViewModel,
@@ -157,7 +151,7 @@ export class OutputElement extends Disposable {
elementSizeObserver.startObserving();
this.resizeListener.add(elementSizeObserver);
this.updateHeight(index, clientHeight);
} else if (result.type === RenderOutputType.None) { // no-op if it's a webview
} else if (result.type === RenderOutputType.Mainframe) { // no-op if it's a webview
const clientHeight = Math.ceil(outputItemDiv.clientHeight);
this.updateHeight(index, clientHeight);
@@ -166,7 +160,7 @@ export class OutputElement extends Disposable {
}
}
private async pickActiveMimeTypeRenderer(notebookTextModel: NotebookTextModel, viewModel: IDisplayOutputViewModel) {
private async pickActiveMimeTypeRenderer(notebookTextModel: NotebookTextModel, viewModel: ICellOutputViewModel) {
const [mimeTypes, currIndex] = viewModel.resolveMimeTypes(notebookTextModel);
const items = mimeTypes.filter(mimeType => mimeType.isTrusted).map((mimeType, index): IMimeTypeRenderer => ({
@@ -294,9 +288,7 @@ export class OutputContainer extends Disposable {
removedKeys.push(key);
// remove element from DOM
this._outputContainer.removeChild(value.domNode);
if (key.isDisplayOutput()) {
this._editor.removeInset(this._diffElementViewModel, this._nestedCellViewModel, key, this._diffSide);
}
this._editor.removeInset(this._diffElementViewModel, this._nestedCellViewModel, key, this._diffSide);
}
});
@@ -334,19 +326,14 @@ export class OutputContainer extends Disposable {
showOutputs() {
for (let index = 0; index < this._nestedCellViewModel.outputsViewModels.length; index++) {
const currOutput = this._nestedCellViewModel.outputsViewModels[index];
if (currOutput.isDisplayOutput()) {
// always add to the end
this._editor.showInset(this._diffElementViewModel, currOutput.cellViewModel, currOutput, this._diffSide);
}
// always add to the end
this._editor.showInset(this._diffElementViewModel, currOutput.cellViewModel, currOutput, this._diffSide);
}
}
hideOutputs() {
this._outputEntries.forEach((outputElement, cellOutputViewModel) => {
if (cellOutputViewModel.isDisplayOutput()) {
this._editor.hideInset(this._diffElementViewModel, this._nestedCellViewModel, cellOutputViewModel);
}
this._editor.hideInset(this._diffElementViewModel, this._nestedCellViewModel, cellOutputViewModel);
});
}
@@ -316,7 +316,7 @@ export class SideBySideDiffElementViewModel extends DiffElementViewModelBase {
}
checkIfOutputsModified() {
return !this.mainDocumentTextModel.transientOptions.transientOutputs && hash(this.original?.outputs ?? []) !== hash(this.modified?.outputs ?? []);
return !this.mainDocumentTextModel.transientOptions.transientOutputs && hash(this.original?.outputs.map(op => op.outputs) ?? []) !== hash(this.modified?.outputs.map(op => op.outputs) ?? []);
}
checkMetadataIfModified(): boolean {
@@ -370,7 +370,7 @@ export class SideBySideDiffElementViewModel extends DiffElementViewModelBase {
}
getNestedCellViewModel(diffSide: DiffSide): DiffNestedCellViewModel {
throw new Error('Method not implemented.');
return diffSide === DiffSide.Original ? this.original : this.modified;
}
getCellByUri(cellUri: URI): IGenericCellViewModel {
@@ -189,7 +189,7 @@
width: 100%;
padding: 4px 8px 4px 32px;
box-sizing: border-box;
overflow-x: hidden;
overflow: hidden;
}
.monaco-workbench .notebook-text-diff-editor .cell-body.full .output-info-container.modified .output-view-container .output-view-container-left {
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ICommonCellInfo, ICommonNotebookEditor, IDisplayOutputViewModel, IGenericCellViewModel, IInsetRenderOutput, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { ICellOutputViewModel, ICommonCellInfo, ICommonNotebookEditor, IGenericCellViewModel, IInsetRenderOutput, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { DiffElementViewModelBase } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel';
import { Event } from 'vs/base/common/event';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
@@ -28,15 +28,15 @@ export interface IDiffCellInfo extends ICommonCellInfo {
export interface INotebookTextDiffEditor extends ICommonNotebookEditor {
readonly textModel?: NotebookTextModel;
onMouseUp: Event<{ readonly event: MouseEvent; readonly target: DiffElementViewModelBase; }>;
onDidDynamicOutputRendered: Event<{ cell: IGenericCellViewModel, output: IDisplayOutputViewModel }>;
onDidDynamicOutputRendered: Event<{ cell: IGenericCellViewModel, output: ICellOutputViewModel }>;
getOverflowContainerDomNode(): HTMLElement;
getLayoutInfo(): NotebookLayoutInfo;
layoutNotebookCell(cell: DiffElementViewModelBase, height: number): void;
getOutputRenderer(): OutputRenderer;
createInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, output: IInsetRenderOutput, getOffset: () => number, diffSide: DiffSide): void;
showInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, displayOutput: IDisplayOutputViewModel, diffSide: DiffSide): void;
removeInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, output: IDisplayOutputViewModel, diffSide: DiffSide): void;
hideInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, output: IDisplayOutputViewModel): void;
showInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, displayOutput: ICellOutputViewModel, diffSide: DiffSide): void;
removeInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, output: ICellOutputViewModel, diffSide: DiffSide): void;
hideInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, output: ICellOutputViewModel): void;
/**
* Trigger the editor to scroll from scroll event programmatically
*/
@@ -44,7 +44,7 @@ export interface INotebookTextDiffEditor extends ICommonNotebookEditor {
getCellByInfo(cellInfo: ICommonCellInfo): IGenericCellViewModel;
focusNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): void;
focusNextNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): void;
updateOutputHeight(cellInfo: ICommonCellInfo, output: IDisplayOutputViewModel, height: number, isInit: boolean): void;
updateOutputHeight(cellInfo: ICommonCellInfo, output: ICellOutputViewModel, height: number, isInit: boolean): void;
deltaCellOutputContainerClassNames(diffSide: DiffSide, cellId: string, added: string[], removed: string[]): void;
}
@@ -23,7 +23,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { getPixelRatio, getZoomLevel } from 'vs/base/browser/browser';
import { IDisplayOutputLayoutUpdateRequest, IDisplayOutputViewModel, IGenericCellViewModel, IInsetRenderOutput, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellEditState, ICellOutputViewModel, IDisplayOutputLayoutUpdateRequest, IGenericCellViewModel, IInsetRenderOutput, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { DiffSide, DIFF_CELL_MARGIN, IDiffCellInfo, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser';
import { Emitter } from 'vs/base/common/event';
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
@@ -74,7 +74,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
private _revealFirst: boolean;
private readonly _insetModifyQueueByOutputId = new SequencerByKey<string>();
protected _onDidDynamicOutputRendered = new Emitter<{ cell: IGenericCellViewModel, output: IDisplayOutputViewModel }>();
protected _onDidDynamicOutputRendered = new Emitter<{ cell: IGenericCellViewModel, output: ICellOutputViewModel }>();
onDidDynamicOutputRendered = this._onDidDynamicOutputRendered.event;
private _localStore: DisposableStore = this._register(new DisposableStore());
@@ -113,7 +113,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
// throw new Error('Method not implemented.');
}
updateOutputHeight(cellInfo: IDiffCellInfo, output: IDisplayOutputViewModel, outputHeight: number, isInit: boolean): void {
updateOutputHeight(cellInfo: IDiffCellInfo, output: ICellOutputViewModel, outputHeight: number, isInit: boolean): void {
const diffElement = cellInfo.diffElement;
const cell = this.getCellByInfo(cellInfo);
const outputIndex = cell.outputsViewModels.indexOf(output);
@@ -134,6 +134,19 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
}
}
setMarkdownCellEditState(cellId: string, editState: CellEditState): void {
// throw new Error('Method not implemented.');
}
markdownCellDragStart(cellId: string, position: { clientY: number }): void {
// throw new Error('Method not implemented.');
}
markdownCellDrag(cellId: string, position: { clientY: number }): void {
// throw new Error('Method not implemented.');
}
markdownCellDragEnd(cellId: string, position: { clientY: number }): void {
// throw new Error('Method not implemented.');
}
protected createEditor(parent: HTMLElement): void {
this._rootElement = DOM.append(parent, DOM.$('.notebook-text-diff-editor'));
this._overflowContainer = document.createElement('div');
@@ -231,7 +244,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
if (activeWebview.insetMapping) {
const updateItems: IDisplayOutputLayoutUpdateRequest[] = [];
const removedItems: IDisplayOutputViewModel[] = [];
const removedItems: ICellOutputViewModel[] = [];
activeWebview.insetMapping.forEach((value, key) => {
const cell = getActiveNestedCell(value.cellInfo.diffElement);
if (!cell) {
@@ -589,11 +602,15 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
});
}
updateMarkdownCellHeight() {
// TODO
}
getCellByInfo(cellInfo: IDiffCellInfo): IGenericCellViewModel {
return cellInfo.diffElement.getCellByUri(cellInfo.cellUri);
}
removeInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: DiffNestedCellViewModel, displayOutput: IDisplayOutputViewModel, diffSide: DiffSide) {
removeInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: DiffNestedCellViewModel, displayOutput: ICellOutputViewModel, diffSide: DiffSide) {
this._insetModifyQueueByOutputId.queue(displayOutput.model.outputId + (diffSide === DiffSide.Modified ? '-right' : 'left'), async () => {
const activeWebview = diffSide === DiffSide.Modified ? this._modifiedWebview : this._originalWebview;
if (!activeWebview) {
@@ -608,7 +625,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
});
}
showInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: DiffNestedCellViewModel, displayOutput: IDisplayOutputViewModel, diffSide: DiffSide) {
showInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: DiffNestedCellViewModel, displayOutput: ICellOutputViewModel, diffSide: DiffSide) {
this._insetModifyQueueByOutputId.queue(displayOutput.model.outputId + (diffSide === DiffSide.Modified ? '-right' : 'left'), async () => {
const activeWebview = diffSide === DiffSide.Modified ? this._modifiedWebview : this._originalWebview;
if (!activeWebview) {
@@ -627,7 +644,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
});
}
hideInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: DiffNestedCellViewModel, output: IDisplayOutputViewModel) {
hideInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: DiffNestedCellViewModel, output: ICellOutputViewModel) {
this._modifiedWebview?.hideInset(output);
this._originalWebview?.hideInset(output);
}
@@ -38,6 +38,12 @@ export interface INotebookRendererContribution {
readonly [NotebookRendererContribution.entrypoint]: string;
}
export interface INotebookMarkdownRendererContribution {
readonly [NotebookRendererContribution.id]?: string;
readonly [NotebookRendererContribution.viewType]?: string;
readonly [NotebookRendererContribution.entrypoint]: string;
}
const notebookProviderContribution: IJSONSchema = {
description: nls.localize('contributes.notebook.provider', 'Contributes notebook document provider.'),
type: 'array',
@@ -144,3 +150,9 @@ export const notebookRendererExtensionPoint = ExtensionsRegistry.registerExtensi
extensionPoint: 'notebookOutputRenderer',
jsonSchema: notebookRendererContribution
});
export const notebookMarkdownRendererExtensionPoint = ExtensionsRegistry.registerExtensionPoint<INotebookMarkdownRendererContribution[]>(
{
extensionPoint: 'notebookMarkdownRenderer',
jsonSchema: notebookRendererContribution //
});
@@ -164,7 +164,7 @@
box-sizing: border-box;
}
.monaco-workbench .notebookOverlay .output > div.foreground > .output-inner-container {
.monaco-workbench .notebookOverlay .output > div.foreground.output-inner-container {
width: 100%;
padding: 4px 8px;
box-sizing: border-box;
@@ -72,9 +72,6 @@ import 'vs/workbench/contrib/notebook/browser/contrib/status/editorStatus';
import 'vs/workbench/contrib/notebook/browser/diff/notebookDiffActions';
// Output renderers registration
import 'vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform';
import 'vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform';
import 'vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform';
/*--------------------------------------------------------------------------------------------- */
@@ -22,7 +22,7 @@ import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/outpu
import { RunStateRenderer, TimerRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer';
import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellKind, IProcessedOutput, NotebookCellMetadata, NotebookDocumentMetadata, IEditor, INotebookKernelInfo2, ICellRange, IOrderedMimeType, ITransformedDisplayOutputDto, INotebookRendererInfo, IErrorOutput, IStreamOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellKind, NotebookCellMetadata, NotebookDocumentMetadata, IEditor, INotebookKernelInfo2, ICellRange, IOrderedMimeType, INotebookRendererInfo, ICellOutput, IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { Webview } from 'vs/workbench/contrib/webview/browser/webview';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { IMenu } from 'vs/platform/actions/common/actions';
@@ -75,14 +75,15 @@ export const EXECUTE_CELL_COMMAND_ID = 'notebook.cell.execute';
//#region Output related types
export const enum RenderOutputType {
None,
Mainframe,
Html,
Extension
}
export interface IRenderNoOutput {
type: RenderOutputType.None;
export interface IRenderMainframeOutput {
type: RenderOutputType.Mainframe;
hasDynamicHeight: boolean;
supportAppend?: boolean;
}
export interface IRenderPlainHtmlOutput {
@@ -100,32 +101,28 @@ export interface IRenderOutputViaExtension {
}
export type IInsetRenderOutput = IRenderPlainHtmlOutput | IRenderOutputViaExtension;
export type IRenderOutput = IRenderNoOutput | IInsetRenderOutput;
export type IRenderOutput = IRenderMainframeOutput | IInsetRenderOutput;
export const outputHasDynamicHeight = (o: IRenderOutput) => o.type !== RenderOutputType.Extension && o.hasDynamicHeight;
export interface ICellOutputViewModel {
cellViewModel: IGenericCellViewModel;
model: IProcessedOutput;
isDisplayOutput(): this is IDisplayOutputViewModel;
isErrorOutput(): this is IErrorOutputViewModel;
isStreamOutput(): this is IStreamOutputViewModel;
/**
* When rendering an output, `model` should always be used as we convert legacy `text/error` output to `display_data` output under the hood.
*/
model: ICellOutput;
resolveMimeTypes(textModel: NotebookTextModel): [readonly IOrderedMimeType[], number];
pickedMimeType: number;
supportAppend(): boolean;
toRawJSON(): any;
}
export interface IDisplayOutputViewModel extends ICellOutputViewModel {
model: ITransformedDisplayOutputDto;
resolveMimeTypes(textModel: NotebookTextModel): [readonly IOrderedMimeType[], number];
pickedMimeType: number;
}
export interface IErrorOutputViewModel extends ICellOutputViewModel {
model: IErrorOutput;
}
export interface IStreamOutputViewModel extends ICellOutputViewModel {
model: IStreamOutput;
}
//#endregion
@@ -167,6 +164,11 @@ export interface ICommonNotebookEditor {
focusNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): void;
focusNextNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): void;
updateOutputHeight(cellInfo: ICommonCellInfo, output: IDisplayOutputViewModel, height: number, isInit: boolean): void;
updateMarkdownCellHeight(cellId: string, height: number, isInit: boolean): void;
setMarkdownCellEditState(cellId: string, editState: CellEditState): void;
markdownCellDragStart(cellId: string, position: { clientY: number }): void;
markdownCellDrag(cellId: string, position: { clientY: number }): void;
markdownCellDragEnd(cellId: string, position: { clientY: number, ctrlKey: boolean, altKey: boolean }): void;
}
//#endregion
@@ -462,6 +464,10 @@ export interface INotebookEditor extends IEditor, ICommonNotebookEditor {
*/
layoutNotebookCell(cell: ICellViewModel, height: number): Promise<void>;
createMarkdownPreview(cell: ICellViewModel): Promise<void>;
hideMarkdownPreview(cell: ICellViewModel): Promise<void>;
removeMarkdownPreview(cell: ICellViewModel): Promise<void>;
/**
* Render the output in webview layer
*/
@@ -684,6 +690,7 @@ export interface BaseCellRenderTemplate {
export interface MarkdownCellRenderTemplate extends BaseCellRenderTemplate {
editorContainer: HTMLElement;
foldingIndicator: HTMLElement;
focusIndicatorBottom: HTMLElement;
currentEditor?: ICodeEditor;
}
@@ -708,6 +715,7 @@ export function isCodeCellRenderTemplate(templateData: BaseCellRenderTemplate):
}
export interface IOutputTransformContribution {
getMimetypes(): string[];
/**
* Dispose this contribution.
*/
@@ -718,7 +726,7 @@ export interface IOutputTransformContribution {
* This call is allowed to have side effects, such as placing output
* directly into the container element.
*/
render(output: ICellOutputViewModel, container: HTMLElement, preferredMimeType: string | undefined, notebookUri: URI | undefined): IRenderOutput;
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI | undefined): IRenderOutput;
}
export interface CellFindMatch {
@@ -41,7 +41,7 @@ import { IEditorMemento } from 'vs/workbench/common/editor';
import { Memento, MementoObject } from 'vs/workbench/common/memento';
import { PANEL_BORDER } from 'vs/workbench/common/theme';
import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_OUTPUT_PADDING, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFocusMode, IActiveNotebookEditor, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IDisplayOutputViewModel, IGenericCellViewModel, IInsetRenderOutput, INotebookCellList, INotebookCellOutputLayoutInfo, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorMouseEvent, NotebookEditorOptions, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellEditState, CellFocusMode, IActiveNotebookEditor, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IGenericCellViewModel, IInsetRenderOutput, INotebookCellList, INotebookCellOutputLayoutInfo, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorMouseEvent, NotebookEditorOptions, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
import { NotebookKernelProviderAssociation, NotebookKernelProviderAssociations, notebookKernelProviderAssociationsSettingId } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation';
import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList';
@@ -64,6 +64,7 @@ import { configureKernelIcon, errorStateIcon, successStateIcon } from 'vs/workbe
import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugColors';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { extname } from 'vs/base/common/resources';
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
const $ = DOM.$;
@@ -1016,6 +1017,19 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this._updateForMetadata();
}));
const useRenderer = this.configurationService.getValue<string>('notebook.experimental.useMarkdownRenderer');
if (useRenderer) {
await this._resolveWebview();
await this._webview!.initializeMarkdown(this.viewModel.viewCells
.filter(cell => cell.cellKind === CellKind.Markdown)
.map(cell => ({ cellId: cell.id, content: cell.getText() }))
// TODO: look at cell position cache instead of just getting first five cells
.slice(0, 5));
}
// restore view states, including contributions
{
@@ -1066,7 +1080,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
if (this._webview?.insetMapping) {
const updateItems: IDisplayOutputLayoutUpdateRequest[] = [];
const removedItems: IDisplayOutputViewModel[] = [];
const removedItems: ICellOutputViewModel[] = [];
this._webview?.insetMapping.forEach((value, key) => {
const cell = this.viewModel?.getCellByHandle(value.cellInfo.cellHandle);
if (!cell || !(cell instanceof CodeCellViewModel)) {
@@ -1105,6 +1119,18 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this._webview?.updateViewScrollTop(-scrollTop, false, updateItems);
}
}
if (this._webview?.markdownPreviewMapping) {
const updateItems: { id: string, top: number }[] = [];
this._webview!.markdownPreviewMapping.forEach(cellId => {
const cell = this.viewModel?.viewCells.find(cell => cell.id === cellId);
if (cell) {
const cellTop = this._list.getAbsoluteTopOfElement(cell);
updateItems.push({ id: cellId, top: cellTop });
}
});
this._webview?.updateMarkdownScrollTop(updateItems);
}
});
}));
@@ -1957,6 +1983,41 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this._list.triggerScrollFromMouseWheelEvent(event);
}
async createMarkdownPreview(cell: MarkdownCellViewModel) {
if (!this._webview) {
return;
}
await this._resolveWebview();
const cellTop = this._list.getAbsoluteTopOfElement(cell);
if (this._webview.markdownPreviewMapping.has(cell.id)) {
await this._webview!.showMarkdownPreview(cell.id, cell.getText(), cellTop);
} else {
await this._webview!.createMarkdownPreview(cell.id, cell.getText(), cellTop);
}
}
async hideMarkdownPreview(cell: MarkdownCellViewModel) {
if (!this._webview) {
return;
}
await this._resolveWebview();
await this._webview!.hideMarkdownPreview(cell.id);
}
async removeMarkdownPreview(cell: MarkdownCellViewModel) {
if (!this._webview) {
return;
}
await this._resolveWebview();
await this._webview!.removeMarkdownPreview(cell.id);
}
async createInset(cell: CodeCellViewModel, output: IInsetRenderOutput, offset: number): Promise<void> {
this._insetModifyQueueByOutputId.queue(output.source.model.outputId, async () => {
if (!this._webview) {
@@ -1980,10 +2041,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
removeInset(output: ICellOutputViewModel) {
if (!output.isDisplayOutput()) {
return;
}
this._insetModifyQueueByOutputId.queue(output.model.outputId, async () => {
if (!this._webview || !this._webviewResolved) {
return;
@@ -1997,10 +2054,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
return;
}
if (!output.isDisplayOutput()) {
return;
}
this._insetModifyQueueByOutputId.queue(output.model.outputId, async () => {
this._webview!.hideInset(output);
});
@@ -2039,7 +2092,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
return this.viewModel?.viewCells.find(vc => vc.handle === cellHandle) as CodeCellViewModel;
}
updateOutputHeight(cellInfo: ICommonCellInfo, output: IDisplayOutputViewModel, outputHeight: number, isInit: boolean): void {
updateOutputHeight(cellInfo: ICommonCellInfo, output: ICellOutputViewModel, outputHeight: number, isInit: boolean): void {
const cell = this.viewModel?.viewCells.find(vc => vc.handle === cellInfo.cellHandle);
if (cell && cell instanceof CodeCellViewModel) {
const outputIndex = cell.outputsViewModels.indexOf(output);
@@ -2048,6 +2101,45 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
}
updateMarkdownCellHeight(cellId: string, height: number, isInit: boolean) {
const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId);
if (cell && cell instanceof MarkdownCellViewModel) {
cell.renderedMarkdownHeight = height;
}
}
setMarkdownCellEditState(cellId: string, editState: CellEditState): void {
const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId);
if (cell && cell instanceof MarkdownCellViewModel) {
cell.editState = editState;
}
}
markdownCellDragStart(cellId: string, ctx: { clientY: number }): void {
const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId);
if (cell && cell instanceof MarkdownCellViewModel) {
this._dndController?.startExplicitDrag(cell, ctx);
}
}
markdownCellDrag(cellId: string, ctx: { clientY: number }): void {
const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId);
if (cell && cell instanceof MarkdownCellViewModel) {
this._dndController?.explicitDrag(cell, ctx);
}
}
markdownCellDragEnd(cellId: string, ctx: { clientY: number, ctrlKey: boolean, altKey: boolean }): void {
const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId);
if (cell && cell instanceof MarkdownCellViewModel) {
this._dndController?.endExplicitDrag(cell, ctx);
}
}
//#endregion
@@ -2303,8 +2395,7 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-focus-indicator-top:before,
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-focus-indicator-bottom:before,
.monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused .cell-inner-container:not(.cell-editor-focus):before,
.monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused .cell-inner-container:not(.cell-editor-focus):after {
.monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused .cell-inner-container:not(.cell-editor-focus):before {
border-color: ${focusedCellBorderColor} !important;
}`);
@@ -2321,8 +2412,7 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-editor-focus .cell-focus-indicator-top:before,
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-editor-focus .cell-focus-indicator-bottom:before,
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container.cell-editor-focus:before,
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container.cell-editor-focus:after {
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container.cell-editor-focus:before {
border-color: ${selectedCellBorderColor} !important;
}`);
@@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CellOutputKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { BrandedService, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation';
import { ICommonNotebookEditor, IOutputTransformContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
@@ -11,7 +10,6 @@ export type IOutputTransformCtor = IConstructorSignature1<ICommonNotebookEditor,
export interface IOutputTransformDescription {
id: string;
kind: CellOutputKind;
ctor: IOutputTransformCtor;
}
@@ -20,8 +18,8 @@ export const NotebookRegistry = new class NotebookRegistryImpl {
readonly outputTransforms: IOutputTransformDescription[] = [];
registerOutputTransform<Services extends BrandedService[]>(id: string, kind: CellOutputKind, ctor: { new(editor: ICommonNotebookEditor, ...services: Services): IOutputTransformContribution }): void {
this.outputTransforms.push({ id: id, kind: kind, ctor: ctor as IOutputTransformCtor });
registerOutputTransform<Services extends BrandedService[]>(id: string, ctor: { new(editor: ICommonNotebookEditor, ...services: Services): IOutputTransformContribution }): void {
this.outputTransforms.push({ id: id, ctor: ctor as IOutputTransformCtor });
}
getOutputTransformContributions(): IOutputTransformDescription[] {
@@ -26,13 +26,14 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol';
import { Memento } from 'vs/workbench/common/memento';
import { INotebookEditorContribution, notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
import { INotebookEditorContribution, notebookMarkdownRendererExtensionPoint, notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
import { CellEditState, getActiveNotebookEditor, ICellViewModel, INotebookEditor, NotebookEditorOptions, updateEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookKernelProviderAssociationRegistry, NotebookViewTypesExtensionRegistry, updateNotebookKernelProvideAssociationSchema } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation';
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
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, CellEditType, CellKind, CellOutputKind, DisplayOrderKey, ICellEditOperation, IDisplayOutput, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, RENDERER_NOT_AVAILABLE, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellKind, DisplayOrderKey, ICellEditOperation, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookMarkdownRendererInfo, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, RENDERER_NOT_AVAILABLE, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookMarkdownRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookMarkdownRenderer';
import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer';
import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
@@ -235,11 +236,13 @@ class ModelData implements IDisposable {
this._modelEventListeners.dispose();
}
}
export class NotebookService extends Disposable implements INotebookService, ICustomEditorViewTypesHandler {
declare readonly _serviceBrand: undefined;
private readonly _notebookProviders = new Map<string, { controller: IMainNotebookController, extensionData: NotebookExtensionDescription; }>();
notebookProviderInfoStore: NotebookProviderInfoStore;
notebookRenderersInfoStore: NotebookOutputRendererInfoStore = new NotebookOutputRendererInfoStore();
private readonly markdownRenderersInfos = new Set<INotebookMarkdownRendererInfo>();
notebookKernelProviderInfoStore: NotebookKernelProviderInfoStore = new NotebookKernelProviderInfoStore();
private readonly _models = new ResourceMap<ModelData>();
private _onDidChangeActiveEditor = new Emitter<string | null>();
@@ -320,6 +323,32 @@ export class NotebookService extends Disposable implements INotebookService, ICu
}
});
notebookMarkdownRendererExtensionPoint.setHandler((renderers) => {
this.markdownRenderersInfos.clear();
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`);
continue;
}
const id = notebookContribution.id ?? notebookContribution.viewType;
if (!id) {
console.error(`Notebook renderer from ${extension.description.identifier.value} is missing an 'id'`);
continue;
}
this.markdownRenderersInfos.add(new NotebookMarkdownRendererInfo({
id,
extension: extension.description,
entrypoint: notebookContribution.entrypoint,
displayName: 'todo',
}));
}
}
});
this._editorService.registerCustomEditorViewTypesHandler('Notebook', this);
const updateOrder = () => {
@@ -496,14 +525,11 @@ export class NotebookService extends Disposable implements INotebookService, ICu
language: cell.language,
cellKind: cell.cellKind,
outputs: cell.outputs.map(output => {
if (output.outputKind === CellOutputKind.Rich) {
return {
...output,
outputId: UUID.generateUuid()
};
}
return output;
return {
...output,
// paste should generate new outputId
outputId: UUID.generateUuid()
};
}),
metadata: {
editable: cell.metadata?.editable,
@@ -535,14 +561,10 @@ export class NotebookService extends Disposable implements INotebookService, ICu
language: cell.language,
cellKind: cell.cellKind,
outputs: cell.outputs.map(output => {
if (output.outputKind === CellOutputKind.Rich) {
return {
...output,
outputId: UUID.generateUuid()
};
}
return output;
return {
...output,
outputId: UUID.generateUuid()
};
}),
metadata: {
editable: cell.metadata?.editable,
@@ -747,6 +769,10 @@ export class NotebookService extends Disposable implements INotebookService, ICu
return this.notebookRenderersInfoStore.get(id);
}
getMarkdownRendererInfo(): INotebookMarkdownRendererInfo[] {
return Array.from(this.markdownRenderersInfos);
}
async resolveNotebook(viewType: string, uri: URI, forceReload: boolean, backupId?: string): Promise<NotebookTextModel> {
if (!await this.canResolve(viewType)) {
@@ -796,13 +822,20 @@ export class NotebookService extends Disposable implements INotebookService, ICu
return Iterable.map(this._models.values(), data => data.model);
}
getMimeTypeInfo(textModel: NotebookTextModel, output: ITransformedDisplayOutputDto): readonly IOrderedMimeType[] {
getMimeTypeInfo(textModel: NotebookTextModel, output: IOutputDto): readonly IOrderedMimeType[] {
// TODO@rebornix no string[] casting
return this._getOrderedMimeTypes(textModel, output, textModel.metadata.displayOrder as string[] ?? []);
}
private _getOrderedMimeTypes(textModel: NotebookTextModel, output: IDisplayOutput, documentDisplayOrder: string[]): IOrderedMimeType[] {
const mimeTypes = Object.keys(output.data);
private _getOrderedMimeTypes(textModel: NotebookTextModel, output: IOutputDto, documentDisplayOrder: string[]): IOrderedMimeType[] {
const mimeTypeSet = new Set<string>();
let mimeTypes: string[] = [];
output.outputs.forEach(op => {
if (!mimeTypeSet.has(op.mime)) {
mimeTypeSet.add(op.mime);
mimeTypes.push(op.mime);
}
});
const coreDisplayOrder = this._displayOrder;
const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], documentDisplayOrder, coreDisplayOrder?.defaultOrder || []);
@@ -11,14 +11,15 @@ import { URI } from 'vs/base/common/uri';
export class OutputRenderer {
protected readonly _contributions: { [key: string]: IOutputTransformContribution; };
protected readonly _mimeTypeMapping: { [key: number]: IOutputTransformContribution; };
protected readonly _renderers: IOutputTransformContribution[];
private _richMimeTypeRenderers = new Map<string, IOutputTransformContribution>();
constructor(
notebookEditor: ICommonNotebookEditor,
private readonly instantiationService: IInstantiationService
) {
this._contributions = {};
this._mimeTypeMapping = {};
this._renderers = [];
const contributions = NotebookRegistry.getOutputTransformContributions();
@@ -26,7 +27,9 @@ export class OutputRenderer {
try {
const contribution = this.instantiationService.createInstance(desc.ctor, notebookEditor);
this._contributions[desc.id] = contribution;
this._mimeTypeMapping[desc.kind] = contribution;
contribution.getMimetypes().forEach(mimetype => {
this._richMimeTypeRenderers.set(mimetype, contribution);
});
} catch (err) {
onUnexpectedError(err);
}
@@ -34,20 +37,39 @@ export class OutputRenderer {
}
renderNoop(viewModel: ICellOutputViewModel, container: HTMLElement): IRenderOutput {
const output = viewModel.model;
const contentNode = document.createElement('p');
contentNode.innerText = `No renderer could be found for output. It has the following output type: ${output.outputKind}`;
contentNode.innerText = `No renderer could be found for output.`;
container.appendChild(contentNode);
return { type: RenderOutputType.None, hasDynamicHeight: false };
return { type: RenderOutputType.Mainframe, hasDynamicHeight: false };
}
render(viewModel: ICellOutputViewModel, container: HTMLElement, preferredMimeType: string | undefined, notebookUri: URI | undefined): IRenderOutput {
const output = viewModel.model;
const transform = this._mimeTypeMapping[output.outputKind];
if (!viewModel.model.outputs.length) {
return this.renderNoop(viewModel, container);
}
if (transform) {
return transform.render(viewModel, container, preferredMimeType, notebookUri);
if (!preferredMimeType || !this._richMimeTypeRenderers.has(preferredMimeType)) {
const contentNode = document.createElement('p');
const mimeTypes = viewModel.model.outputs.map(op => op.mime);
const mimeTypesMessage = mimeTypes.join(', ');
if (preferredMimeType) {
contentNode.innerText = `No renderer could be found for MIME type: ${preferredMimeType}`;
} else {
contentNode.innerText = `No renderer could be found for output. It has the following MIME types: ${mimeTypesMessage}`;
}
container.appendChild(contentNode);
return { type: RenderOutputType.Mainframe, hasDynamicHeight: false };
}
const renderer = this._richMimeTypeRenderers.get(preferredMimeType);
const items = viewModel.model.outputs.filter(op => op.mime === preferredMimeType);
if (items.length && renderer) {
return renderer.render(viewModel, items, container, notebookUri);
} else {
return this.renderNoop(viewModel, container);
}
@@ -3,22 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CellOutputKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry';
import { RGBA, Color } from 'vs/base/common/color';
import { ansiColorIdentifiers } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ICommonNotebookEditor, IErrorOutputViewModel, IOutputTransformContribution, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
export class ErrorTransform implements IOutputTransformContribution {
constructor(
_editor: ICommonNotebookEditor,
@IThemeService private readonly themeService: IThemeService
) {
}
render(viewModel: IErrorOutputViewModel, container: HTMLElement): IRenderOutput {
const output = viewModel.model;
export class ErrorTransform {
static render(output: any, container: HTMLElement, themeService: IThemeService): IRenderOutput {
const header = document.createElement('div');
const headerMessage = output.ename && output.evalue
? `${output.ename}: ${output.evalue}`
@@ -31,20 +22,15 @@ export class ErrorTransform implements IOutputTransformContribution {
traceback.classList.add('traceback');
if (output.traceback) {
for (let j = 0; j < output.traceback.length; j++) {
traceback.appendChild(handleANSIOutput(output.traceback[j], this.themeService));
traceback.appendChild(handleANSIOutput(output.traceback[j], themeService));
}
}
container.appendChild(traceback);
container.classList.add('error');
return { type: RenderOutputType.None, hasDynamicHeight: false };
}
dispose(): void {
return { type: RenderOutputType.Mainframe, hasDynamicHeight: false };
}
}
NotebookRegistry.registerOutputTransform('notebook.output.error', CellOutputKind.Error, ErrorTransform);
/**
* @param text The content to stylize.
* @returns An {@link HTMLSpanElement} that contains the potentially stylized text.
@@ -3,10 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CellOutputKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry';
import * as DOM from 'vs/base/browser/dom';
import { ICommonNotebookEditor, IDisplayOutputViewModel, IOutputTransformContribution, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { ICellOutputViewModel, ICommonNotebookEditor, IOutputTransformContribution as IOutputRendererContribution, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { isArray } from 'vs/base/common/types';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
@@ -21,65 +20,30 @@ import { truncatedArrayOfString } from 'vs/workbench/contrib/notebook/browser/vi
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { ErrorTransform } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform';
import { Disposable } from 'vs/base/common/lifecycle';
import { IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
class RichRenderer implements IOutputTransformContribution {
private _richMimeTypeRenderers = new Map<string, (output: IDisplayOutputViewModel, notebookUri: URI, container: HTMLElement) => IRenderOutput>();
function getStringValue(data: unknown): string {
return isArray(data) ? data.join('') : String(data);
}
class JSONRendererContrib extends Disposable implements IOutputRendererContribution {
getMimetypes() {
return ['application/json'];
}
constructor(
public notebookEditor: ICommonNotebookEditor,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IModelService private readonly modelService: IModelService,
@IModeService private readonly modeService: IModeService,
@IThemeService private readonly themeService: IThemeService,
@IOpenerService readonly openerService: IOpenerService,
@ITextFileService readonly textFileService: ITextFileService,
) {
this._richMimeTypeRenderers.set('application/json', this.renderJSON.bind(this));
this._richMimeTypeRenderers.set('application/javascript', this.renderJavaScript.bind(this));
this._richMimeTypeRenderers.set('text/html', this.renderHTML.bind(this));
this._richMimeTypeRenderers.set('image/svg+xml', this.renderSVG.bind(this));
this._richMimeTypeRenderers.set('text/markdown', this.renderMarkdown.bind(this));
this._richMimeTypeRenderers.set('image/png', this.renderPNG.bind(this));
this._richMimeTypeRenderers.set('image/jpeg', this.renderJPEG.bind(this));
this._richMimeTypeRenderers.set('text/plain', this.renderPlainText.bind(this));
this._richMimeTypeRenderers.set('text/x-javascript', this.renderCode.bind(this));
this._richMimeTypeRenderers.set('application/x.notebook.error-traceback', this._renderErrorTraceback.bind(this));
super();
}
render(output: IDisplayOutputViewModel, container: HTMLElement, preferredMimeType: string | undefined, notebookUri: URI): IRenderOutput {
if (!output.model.data) {
const contentNode = document.createElement('p');
contentNode.innerText = `No data could be found for output.`;
container.appendChild(contentNode);
return { type: RenderOutputType.None, hasDynamicHeight: false };
}
if (!preferredMimeType || !this._richMimeTypeRenderers.has(preferredMimeType)) {
const contentNode = document.createElement('p');
const mimeTypes = [];
for (const property in output.model.data) {
mimeTypes.push(property);
}
const mimeTypesMessage = mimeTypes.join(', ');
if (preferredMimeType) {
contentNode.innerText = `No renderer could be found for MIME type: ${preferredMimeType}`;
} else {
contentNode.innerText = `No renderer could be found for output. It has the following MIME types: ${mimeTypesMessage}`;
}
container.appendChild(contentNode);
return { type: RenderOutputType.None, hasDynamicHeight: false };
}
const renderer = this._richMimeTypeRenderers.get(preferredMimeType);
return renderer!(output, notebookUri, container);
}
renderJSON(output: IDisplayOutputViewModel, notebookUri: URI, container: HTMLElement): IRenderOutput {
const data = output.model.data['application/json'];
const str = JSON.stringify(data, null, '\t');
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI | undefined): IRenderOutput {
const str = items.map(item => JSON.stringify(item.value, null, '\t')).join('');
const editor = this.instantiationService.createInstance(CodeEditorWidget, container, {
...getOutputSimpleEditorOptions(),
@@ -105,15 +69,57 @@ class RichRenderer implements IOutputTransformContribution {
width
});
container.style.height = `${height + 16}px`;
container.style.height = `${height + 8}px`;
return { type: RenderOutputType.None, hasDynamicHeight: true };
return { type: RenderOutputType.Mainframe, hasDynamicHeight: true };
}
}
class JavaScriptRendererContrib extends Disposable implements IOutputRendererContribution {
getMimetypes() {
return ['application/javascript'];
}
renderCode(output: IDisplayOutputViewModel, notebookUri: URI, container: HTMLElement): IRenderOutput {
const data = output.model.data['text/x-javascript'];
const str = (isArray(data) ? data.join('') : data) as string;
constructor(
public notebookEditor: ICommonNotebookEditor,
) {
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI | undefined): IRenderOutput {
let scriptVal = '';
items.forEach(item => {
const data = item.value;
const str = isArray(data) ? data.join('') : data;
scriptVal += `<script type="application/javascript">${str}</script>`;
});
return {
type: RenderOutputType.Html,
source: output,
htmlContent: scriptVal,
hasDynamicHeight: false
};
}
}
class CodeRendererContrib extends Disposable implements IOutputRendererContribution {
getMimetypes() {
return ['text/x-javascript'];
}
constructor(
public notebookEditor: ICommonNotebookEditor,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IModelService private readonly modelService: IModelService,
@IModeService private readonly modeService: IModeService,
) {
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI | undefined): IRenderOutput {
const str = items.map(item => getStringValue(item.value)).join('');
const editor = this.instantiationService.createInstance(CodeEditorWidget, container, {
...getOutputSimpleEditorOptions(),
dimension: {
@@ -138,99 +144,227 @@ class RichRenderer implements IOutputTransformContribution {
width
});
container.style.height = `${height + 16}px`;
container.style.height = `${height + 8}px`;
return { type: RenderOutputType.None, hasDynamicHeight: true };
}
renderJavaScript(output: IDisplayOutputViewModel, notebookUri: URI, container: HTMLElement): IRenderOutput {
const data = output.model.data['application/javascript'];
const str = isArray(data) ? data.join('') : data;
const scriptVal = `<script type="application/javascript">${str}</script>`;
return {
type: RenderOutputType.Html,
source: output,
htmlContent: scriptVal,
hasDynamicHeight: false
};
}
renderHTML(output: IDisplayOutputViewModel, notebookUri: URI, container: HTMLElement): IRenderOutput {
const data = output.model.data['text/html'];
const str = (isArray(data) ? data.join('') : data) as string;
return {
type: RenderOutputType.Html,
source: output,
htmlContent: str,
hasDynamicHeight: false
};
}
renderSVG(output: IDisplayOutputViewModel, notebookUri: URI, container: HTMLElement): IRenderOutput {
const data = output.model.data['image/svg+xml'];
const str = (isArray(data) ? data.join('') : data) as string;
return {
type: RenderOutputType.Html,
source: output,
htmlContent: str,
hasDynamicHeight: false
};
}
renderMarkdown(output: IDisplayOutputViewModel, notebookUri: URI, container: HTMLElement): IRenderOutput {
const data = output.model.data['text/markdown'];
const str = (isArray(data) ? data.join('') : data) as string;
const mdOutput = document.createElement('div');
const mdRenderer = this.instantiationService.createInstance(MarkdownRenderer, { baseUrl: dirname(notebookUri) });
mdOutput.appendChild(mdRenderer.render({ value: str, isTrusted: true, supportThemeIcons: true }, undefined, { gfm: true }).element);
container.appendChild(mdOutput);
return { type: RenderOutputType.None, hasDynamicHeight: true };
}
renderPNG(output: IDisplayOutputViewModel, notebookUri: URI, container: HTMLElement): IRenderOutput {
const image = document.createElement('img');
image.src = `data:image/png;base64,${output.model.data['image/png']}`;
const display = document.createElement('div');
display.classList.add('display');
display.appendChild(image);
container.appendChild(display);
return { type: RenderOutputType.None, hasDynamicHeight: true };
}
renderJPEG(output: IDisplayOutputViewModel, notebookUri: URI, container: HTMLElement): IRenderOutput {
const image = document.createElement('img');
image.src = `data:image/jpeg;base64,${output.model.data['image/jpeg']}`;
const display = document.createElement('div');
display.classList.add('display');
display.appendChild(image);
container.appendChild(display);
return { type: RenderOutputType.None, hasDynamicHeight: true };
}
renderPlainText(output: IDisplayOutputViewModel, notebookUri: URI, container: HTMLElement): IRenderOutput {
const data = output.model.data['text/plain'];
const contentNode = DOM.$('.output-plaintext');
truncatedArrayOfString(contentNode, isArray(data) ? data : [data], this.openerService, this.textFileService, this.themeService);
container.appendChild(contentNode);
return { type: RenderOutputType.None, hasDynamicHeight: false };
}
private _renderErrorTraceback(outputViewModel: IDisplayOutputViewModel, _notebookUri: URI, container: HTMLElement): IRenderOutput {
const output = outputViewModel.model.data['application/x.notebook.error-traceback'] as any;
const transform = new ErrorTransform(this.notebookEditor, this.themeService);
transform.render(output, container);
transform.dispose();
return { type: RenderOutputType.None, hasDynamicHeight: false };
}
dispose(): void {
return { type: RenderOutputType.Mainframe, hasDynamicHeight: true };
}
}
NotebookRegistry.registerOutputTransform('notebook.output.rich', CellOutputKind.Rich, RichRenderer);
class StreamRendererContrib extends Disposable implements IOutputRendererContribution {
getMimetypes() {
return ['application/x.notebook.stream'];
}
constructor(
public notebookEditor: ICommonNotebookEditor,
@IOpenerService private readonly openerService: IOpenerService,
@IThemeService private readonly themeService: IThemeService,
@ITextFileService private readonly textFileService: ITextFileService
) {
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI | undefined): IRenderOutput {
items.forEach(item => {
const text = getStringValue(item.value);
const contentNode = DOM.$('span.output-stream');
truncatedArrayOfString(contentNode, [text], this.openerService, this.textFileService, this.themeService);
container.appendChild(contentNode);
});
return { type: RenderOutputType.Mainframe, hasDynamicHeight: false };
}
}
class ErrorRendererContrib extends Disposable implements IOutputRendererContribution {
getMimetypes() {
return ['application/x.notebook.error-traceback'];
}
constructor(
public notebookEditor: ICommonNotebookEditor,
@IThemeService private readonly themeService: IThemeService
) {
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI | undefined): IRenderOutput {
items.forEach(item => {
const data = item.value;
ErrorTransform.render(data, container, this.themeService);
});
return { type: RenderOutputType.Mainframe, hasDynamicHeight: false };
}
}
class PlainTextRendererContrib extends Disposable implements IOutputRendererContribution {
getMimetypes() {
return ['text/plain'];
}
constructor(
public notebookEditor: ICommonNotebookEditor,
@IOpenerService private readonly openerService: IOpenerService,
@IThemeService private readonly themeService: IThemeService,
@ITextFileService private readonly textFileService: ITextFileService
) {
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI | undefined): IRenderOutput {
const str = items.map(item => getStringValue(item.value));
const contentNode = DOM.$('.output-plaintext');
truncatedArrayOfString(contentNode, str, this.openerService, this.textFileService, this.themeService);
container.appendChild(contentNode);
return { type: RenderOutputType.Mainframe, hasDynamicHeight: false, supportAppend: true };
}
}
class HTMLRendererContrib extends Disposable implements IOutputRendererContribution {
getMimetypes() {
return ['text/html'];
}
constructor(
public notebookEditor: ICommonNotebookEditor,
) {
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI | undefined): IRenderOutput {
const data = items.map(item => getStringValue(item.value)).join('');
const str = (isArray(data) ? data.join('') : data) as string;
return {
type: RenderOutputType.Html,
source: output,
htmlContent: str,
hasDynamicHeight: false
};
}
}
class SVGRendererContrib extends Disposable implements IOutputRendererContribution {
getMimetypes() {
return ['image/svg+xml'];
}
constructor(
public notebookEditor: ICommonNotebookEditor,
) {
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI | undefined): IRenderOutput {
const str = items.map(item => getStringValue(item.value)).join('');
return {
type: RenderOutputType.Html,
source: output,
htmlContent: str,
hasDynamicHeight: false
};
}
}
class MdRendererContrib extends Disposable implements IOutputRendererContribution {
getMimetypes() {
return ['text/markdown'];
}
constructor(
public notebookEditor: ICommonNotebookEditor,
@IInstantiationService private readonly instantiationService: IInstantiationService,
) {
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI): IRenderOutput {
items.forEach(item => {
const data = item.value;
const str = (isArray(data) ? data.join('') : data) as string;
const mdOutput = document.createElement('div');
const mdRenderer = this.instantiationService.createInstance(MarkdownRenderer, { baseUrl: dirname(notebookUri) });
mdOutput.appendChild(mdRenderer.render({ value: str, isTrusted: true, supportThemeIcons: true }, undefined, { gfm: true }).element);
container.appendChild(mdOutput);
});
return { type: RenderOutputType.Mainframe, hasDynamicHeight: true };
}
}
class PNGRendererContrib extends Disposable implements IOutputRendererContribution {
getMimetypes() {
return ['image/png'];
}
constructor(
public notebookEditor: ICommonNotebookEditor,
) {
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI | undefined): IRenderOutput {
items.forEach(item => {
const image = document.createElement('img');
const imagedata = item.value;
image.src = `data:image/png;base64,${imagedata}`;
const display = document.createElement('div');
display.classList.add('display');
display.appendChild(image);
container.appendChild(display);
});
return { type: RenderOutputType.Mainframe, hasDynamicHeight: true };
}
}
class JPEGRendererContrib extends Disposable implements IOutputRendererContribution {
getMimetypes() {
return ['image/jpeg'];
}
constructor(
public notebookEditor: ICommonNotebookEditor,
) {
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI | undefined): IRenderOutput {
items.forEach(item => {
const image = document.createElement('img');
const imagedata = item.value;
image.src = `data:image/jpeg;base64,${imagedata}`;
const display = document.createElement('div');
display.classList.add('display');
display.appendChild(image);
container.appendChild(display);
});
return { type: RenderOutputType.Mainframe, hasDynamicHeight: true };
}
}
NotebookRegistry.registerOutputTransform('json', JSONRendererContrib);
NotebookRegistry.registerOutputTransform('javascript', JavaScriptRendererContrib);
NotebookRegistry.registerOutputTransform('html', HTMLRendererContrib);
NotebookRegistry.registerOutputTransform('svg', SVGRendererContrib);
NotebookRegistry.registerOutputTransform('markdown', MdRendererContrib);
NotebookRegistry.registerOutputTransform('png', PNGRendererContrib);
NotebookRegistry.registerOutputTransform('jpeg', JPEGRendererContrib);
NotebookRegistry.registerOutputTransform('plain', PlainTextRendererContrib);
NotebookRegistry.registerOutputTransform('code', CodeRendererContrib);
NotebookRegistry.registerOutputTransform('error-trace', ErrorRendererContrib);
NotebookRegistry.registerOutputTransform('stream-text', StreamRendererContrib);
export function getOutputSimpleEditorOptions(): IEditorOptions {
return {
@@ -1,36 +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 * as DOM from 'vs/base/browser/dom';
import { CellOutputKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry';
import { ICommonNotebookEditor, IOutputTransformContribution, IRenderOutput, IStreamOutputViewModel, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { truncatedArrayOfString } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IThemeService } from 'vs/platform/theme/common/themeService';
class StreamRenderer implements IOutputTransformContribution {
constructor(
editor: ICommonNotebookEditor,
@IOpenerService readonly openerService: IOpenerService,
@ITextFileService readonly textFileService: ITextFileService,
@IThemeService readonly themeService: IThemeService
) {
}
render(viewModel: IStreamOutputViewModel, container: HTMLElement): IRenderOutput {
const output = viewModel.model;
const contentNode = DOM.$('span.output-stream');
truncatedArrayOfString(contentNode, [output.text], this.openerService, this.textFileService, this.themeService);
container.appendChild(contentNode);
return { type: RenderOutputType.None, hasDynamicHeight: false };
}
dispose(): void {
}
}
NotebookRegistry.registerOutputTransform('notebook.output.stream', CellOutputKind.Text, StreamRenderer);
@@ -17,10 +17,10 @@ import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IFileService } from 'vs/platform/files/common/files';
import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ICommonCellInfo, ICommonNotebookEditor, IDisplayOutputLayoutUpdateRequest, IDisplayOutputViewModel, IGenericCellViewModel, IInsetRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellEditState, ICellOutputViewModel, ICommonCellInfo, ICommonNotebookEditor, IDisplayOutputLayoutUpdateRequest, IDisplayOutputViewModel, IGenericCellViewModel, IInsetRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { preloadsScriptStr } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads';
import { transformWebviewThemeVars } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewThemeMapping';
import { CellOutputKind, IDisplayOutput, INotebookRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookRendererInfo } 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 { asWebviewUri } from 'vs/workbench/contrib/webview/common/webviewUri';
@@ -37,6 +37,7 @@ export interface IDimensionMessage {
id: string;
init: boolean;
data: DOM.Dimension;
isOutput: boolean;
}
export interface IMouseEnterMessage {
@@ -79,15 +80,64 @@ export interface IClickedDataUrlMessage {
downloadName?: string;
}
export interface IToggleMarkdownPreviewMessage {
__vscode_notebook_message: boolean;
type: 'toggleMarkdownPreview';
cellId: string;
}
export interface ICellDragStartMessage {
__vscode_notebook_message: boolean;
type: 'cell-drag-start';
cellId: string;
position: { clientX: number, clientY: number };
}
export interface ICellDragMessage {
__vscode_notebook_message: boolean;
type: 'cell-drag';
cellId: string;
position: { clientX: number, clientY: number };
}
export interface ICellDragEndMessage {
readonly __vscode_notebook_message: boolean;
readonly type: 'cell-drag-end';
readonly cellId: string;
readonly ctrlKey: boolean
readonly altKey: boolean;
readonly position: {
readonly clientX: number;
readonly clientY: number;
};
}
export interface IClearMessage {
type: 'clear';
}
export interface IOutputRequestMetadata {
/**
* Additional attributes of a cell metadata.
*/
custom?: { [key: string]: unknown };
}
export interface IOutputRequestDto {
/**
* { mime_type: value }
*/
data: { [key: string]: unknown; }
metadata?: IOutputRequestMetadata;
outputId: string;
}
export interface ICreationRequestMessage {
type: 'html';
content:
| { type: RenderOutputType.Html; htmlContent: string }
| { type: RenderOutputType.Extension; output: IDisplayOutput; mimeType: string };
| { type: RenderOutputType.Extension; output: IOutputRequestDto; mimeType: string };
cellId: string;
outputId: string;
top: number;
@@ -111,6 +161,11 @@ export interface IViewScrollTopRequestMessage {
version: number;
}
export interface IViewScrollMarkdownRequestMessage {
type: 'view-scroll-markdown';
cells: { id: string; top: number }[];
}
export interface IScrollRequestMessage {
type: 'scroll';
id: string;
@@ -170,6 +225,35 @@ export interface ICustomRendererMessage {
message: unknown;
}
export interface ICreateMarkdownMessage {
type: 'createMarkdownPreview',
id: string;
content: string;
top: number;
}
export interface IRemoveMarkdownMessage {
type: 'removeMarkdownPreview',
id: string;
}
export interface IHideMarkdownMessage {
type: 'hideMarkdownPreview',
id: string;
}
export interface IShowMarkdownMessage {
type: 'showMarkdownPreview',
id: string;
content: string;
top: number;
}
export interface IInitializeMarkdownMessage {
type: 'initializeMarkdownPreview';
cells: Array<{ cellId: string, content: string }>;
}
export type FromWebviewMessage =
| WebviewIntialized
| IDimensionMessage
@@ -179,8 +263,12 @@ export type FromWebviewMessage =
| IScrollAckMessage
| IBlurOutputMessage
| ICustomRendererMessage
| IClickedDataUrlMessage;
| IClickedDataUrlMessage
| IToggleMarkdownPreviewMessage
| ICellDragStartMessage
| ICellDragMessage
| ICellDragEndMessage
;
export type ToWebviewMessage =
| IClearMessage
| IFocusOutputMessage
@@ -192,7 +280,13 @@ export type ToWebviewMessage =
| IShowOutputMessage
| IUpdatePreloadResourceMessage
| IUpdateDecorationsMessage
| ICustomRendererMessage;
| ICustomRendererMessage
| ICreateMarkdownMessage
| IRemoveMarkdownMessage
| IShowMarkdownMessage
| IHideMarkdownMessage
| IInitializeMarkdownMessage
| IViewScrollMarkdownRequestMessage;
export type AnyMessage = FromWebviewMessage | ToWebviewMessage;
@@ -221,6 +315,7 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
element: HTMLElement;
webview: WebviewElement | undefined = undefined;
insetMapping: Map<IDisplayOutputViewModel, ICachedInset<T>> = new Map();
markdownPreviewMapping: Set<string> = new Set();
hiddenInsetMapping: Set<IDisplayOutputViewModel> = new Set();
reversedInsetMapping: Map<string, IDisplayOutputViewModel> = new Map();
localResourceRootsCache: URI[] | undefined = undefined;
@@ -256,20 +351,173 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
this.element.style.height = '1400px';
this.element.style.position = 'absolute';
}
generateContent(coreDependencies: string, baseUrl: string) {
private generateContent(coreDependencies: string, baseUrl: string) {
const markdownRenderersSrc = this.getMarkdownRendererScripts();
return html`
<html lang="en">
<head>
<meta charset="UTF-8">
<base href="${baseUrl}/"/>
<style>
#container > div > div {
#container > div > div.output {
width: 100%;
padding: ${this.options.outputNodePadding}px ${this.options.outputNodePadding}px ${this.options.outputNodePadding}px ${this.options.outputNodeLeftPadding}px;
box-sizing: border-box;
background-color: var(--vscode-notebook-outputContainerBackgroundColor);
}
#container > div > div.preview {
width: 100%;
box-sizing: border-box;
white-space: nowrap;
overflow: hidden;
user-select: text;
-webkit-user-select: text;
-ms-user-select: text;
white-space: initial;
padding-left: 0px !important;
}
/* markdown */
#container > div > div.preview img {
max-width: 100%;
max-height: 100%;
}
#container > div > div.preview a {
text-decoration: none;
}
#container > div > div.preview a:hover {
text-decoration: underline;
}
#container > div > div.preview a:focus,
#container > div > div.preview input:focus,
#container > div > div.preview select:focus,
#container > div > div.preview textarea:focus {
outline: 1px solid -webkit-focus-ring-color;
outline-offset: -1px;
}
#container > div > div.preview hr {
border: 0;
height: 2px;
border-bottom: 2px solid;
}
#container > div > div.preview h1 {
padding-bottom: 0.3em;
line-height: 1.2;
border-bottom-width: 1px;
border-bottom-style: solid;
border-color: rgba(255, 255, 255, 0.18);
}
#container > div > div.preview h1 {
border-color: rgba(0, 0, 0, 0.18);
}
#container > div > div.preview h1,
#container > div > div.preview h2,
#container > div > div.preview h3 {
font-weight: normal;
}
#container > div > div.preview div {
width: 100%;
}
/* Adjust margin of first item in markdown cell */
#container > div > div.preview *:first-child {
margin-top: 0px;
}
/* h1 tags don't need top margin */
#container > div > div.preview h1:first-child {
margin-top: 0;
}
/* Removes bottom margin when only one item exists in markdown cell */
#container > div > div.preview *:only-child,
#container > div > div.preview *:last-child {
margin-bottom: 0;
padding-bottom: 0;
}
/* makes all markdown cells consistent */
#container > div > div.preview div {
min-height: 24px;
}
#container > div > div.preview table {
border-collapse: collapse;
border-spacing: 0;
}
#container > div > div.preview table th,
#container > div > div.preview table td {
border: 1px solid;
}
#container > div > div.preview table > thead > tr > th {
text-align: left;
border-bottom: 1px solid;
}
#container > div > div.preview table > thead > tr > th,
#container > div > div.preview table > thead > tr > td,
#container > div > div.preview table > tbody > tr > th,
#container > div > div.preview table > tbody > tr > td {
padding: 5px 10px;
}
#container > div > div.preview table > tbody > tr + tr > td {
border-top: 1px solid;
}
#container > div > div.preview blockquote {
margin: 0 7px 0 5px;
padding: 0 16px 0 10px;
border-left-width: 5px;
border-left-style: solid;
}
#container > div > div.preview code,
#container > div > div.preview .code {
font-family: var(--monaco-monospace-font);
font-size: 1em;
line-height: 1.357em;
}
#container > div > div.preview .code {
white-space: pre-wrap;
}
#container > div > div.preview .latex-block {
display: block;
}
#container > div > div.preview .latex {
vertical-align: middle;
display: inline-block;
}
#container > div > div.preview .latex img,
#container > div > div.preview .latex-block img {
filter: brightness(0) invert(0)
}
#container > div > div.preview.dragging {
background-color: var(--vscode-editor-background);
}
.monaco-workbench.vs-dark .notebookOverlay .cell.markdown .latex img,
.monaco-workbench.vs-dark .notebookOverlay .cell.markdown .latex-block img {
filter: brightness(0) invert(1)
}
#container > div.nb-symbolHighlight > div {
background-color: var(--vscode-notebook-symbolHighlightBackground);
}
@@ -333,10 +581,29 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
${coreDependencies}
<div id='container' class="widgetarea" style="position: absolute;width:100%;top: 0px"></div>
<script>${preloadsScriptStr(this.options.outputNodePadding, this.options.outputNodeLeftPadding)}</script>
${markdownRenderersSrc}
</body>
</html>`;
}
private getMarkdownRendererScripts() {
const markdownRenderers = this.notebookService.getMarkdownRendererInfo();
return markdownRenderers
.sort((a, b) => {
// prefer built-in extension
if (a.extensionIsBuiltin) {
return b.extensionIsBuiltin ? 0 : -1;
}
return b.extensionIsBuiltin ? 1 : -1;
})
.map(renderer => {
return asWebviewUri(this.environmentService, this.id, renderer.entrypoint);
})
.map(src => `<script src="${src}"></script>`)
.join('\n');
}
postRendererMessage(rendererId: string, message: any) {
this._sendMessageToWebview({
__vscode_notebook_message: true,
@@ -346,7 +613,7 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
});
}
private resolveOutputId(id: string): { cellInfo: T, output: IDisplayOutputViewModel } | undefined {
private resolveOutputId(id: string): { cellInfo: T, output: ICellOutputViewModel } | undefined {
const output = this.reversedInsetMapping.get(id);
if (!output) {
return;
@@ -459,13 +726,18 @@ var requirejs = (function() {
if (data.__vscode_notebook_message) {
if (data.type === 'dimension') {
const height = data.data.height;
const outputHeight = height;
if (data.isOutput) {
const height = data.data.height;
const outputHeight = height;
const resolvedResult = this.resolveOutputId(data.id);
if (resolvedResult) {
const { cellInfo, output } = resolvedResult;
this.notebookEditor.updateOutputHeight(cellInfo, output, outputHeight, !!data.init);
const resolvedResult = this.resolveOutputId(data.id);
if (resolvedResult) {
const { cellInfo, output } = resolvedResult;
this.notebookEditor.updateOutputHeight(cellInfo, output, outputHeight, !!data.init);
}
} else {
const cellId = data.id.substr(0, data.id.length - '_preview'.length);
this.notebookEditor.updateMarkdownCellHeight(cellId, data.data.height, !!data.init);
}
} else if (data.type === 'mouseenter') {
const resolvedResult = this.resolveOutputId(data.id);
@@ -511,6 +783,18 @@ var requirejs = (function() {
this._onDidClickDataLink(data);
} else if (data.type === 'customRendererMessage') {
this._onMessage.fire({ message: data.message, forRenderer: data.rendererId });
} else if (data.type === 'toggleMarkdownPreview') {
this.notebookEditor.setMarkdownCellEditState(data.cellId, CellEditState.Editing);
} else if (data.type === 'cell-drag-start') {
this.notebookEditor.markdownCellDragStart(data.cellId, data.position);
} else if (data.type === 'cell-drag') {
this.notebookEditor.markdownCellDrag(data.cellId, data.position);
} else if (data.type === 'cell-drag-end') {
this.notebookEditor.markdownCellDragEnd(data.cellId, {
clientY: data.position.clientY,
ctrlKey: data.ctrlKey,
altKey: data.altKey,
});
}
return;
}
@@ -559,7 +843,12 @@ var requirejs = (function() {
const workspaceFolders = this.contextService.getWorkspace().folders.map(x => x.uri);
this.localResourceRootsCache = [...this.notebookService.getNotebookProviderResourceRoots(), ...workspaceFolders, rootPath];
this.localResourceRootsCache = [
...this.notebookService.getNotebookProviderResourceRoots(),
...this.notebookService.getMarkdownRendererInfo().map(x => dirname(x.entrypoint)),
...workspaceFolders,
rootPath,
];
const webview = webviewService.createWebviewElement(this.id, {
purpose: WebviewContentPurpose.NotebookRenderer,
@@ -587,7 +876,7 @@ var requirejs = (function() {
return webview;
}
shouldUpdateInset(cell: IGenericCellViewModel, output: IDisplayOutputViewModel, cellTop: number) {
shouldUpdateInset(cell: IGenericCellViewModel, output: ICellOutputViewModel, cellTop: number) {
if (this._disposed) {
return;
}
@@ -611,6 +900,13 @@ var requirejs = (function() {
return true;
}
updateMarkdownScrollTop(items: { id: string, top: number }[]) {
this._sendMessageToWebview({
type: 'view-scroll-markdown',
cells: items
});
}
updateViewScrollTop(top: number, forceDisplay: boolean, items: IDisplayOutputLayoutUpdateRequest[]) {
if (this._disposed) {
return;
@@ -639,6 +935,83 @@ var requirejs = (function() {
});
}
async createMarkdownPreview(cellId: string, content: string, cellTop: number) {
if (this._disposed) {
return;
}
const initialTop = cellTop;
this.markdownPreviewMapping.add(cellId);
this._sendMessageToWebview({
type: 'createMarkdownPreview',
id: cellId,
content: content,
top: initialTop,
});
}
async showMarkdownPreview(cellId: string, content: string, cellTop: number) {
if (this._disposed) {
return;
}
this._sendMessageToWebview({
type: 'showMarkdownPreview',
id: cellId,
content: content,
top: cellTop
});
}
async hideMarkdownPreview(cellId: string,) {
if (this._disposed) {
return;
}
this._sendMessageToWebview({
type: 'hideMarkdownPreview',
id: cellId
});
}
async removeMarkdownPreview(cellId: string,) {
if (this._disposed) {
return;
}
this.markdownPreviewMapping.delete(cellId);
this._sendMessageToWebview({
type: 'removeMarkdownPreview',
id: cellId
});
}
async initializeMarkdown(cells: Array<{ cellId: string, content: string }>) {
await this._loaded;
// TODO: use proper handler
const p = new Promise<void>(resolve => {
this.webview?.onMessage(e => {
if (e.type === 'initializedMarkdownPreview') {
resolve();
}
});
});
for (const cell of cells) {
this.markdownPreviewMapping.add(cell.cellId);
}
this._sendMessageToWebview({
type: 'initializeMarkdownPreview',
cells: cells,
});
await p;
}
async createInset(cellInfo: T, content: IInsetRenderOutput, cellTop: number, offset: number) {
if (this._disposed) {
return;
@@ -674,6 +1047,10 @@ var requirejs = (function() {
if (content.type === RenderOutputType.Extension) {
const output = content.source.model;
renderer = content.renderer;
let data: { [key: string]: unknown } = {};
let metadata: { [key: string]: unknown } = {};
data[content.mimeType] = output.outputs.find(op => op.mime === content.mimeType)?.value || undefined;
metadata[content.mimeType] = output.outputs.find(op => op.mime === content.mimeType)?.metadata || undefined;
message = {
...messageBase,
outputId: output.outputId,
@@ -683,9 +1060,9 @@ var requirejs = (function() {
type: RenderOutputType.Extension,
mimeType: content.mimeType,
output: {
outputKind: CellOutputKind.Rich,
metadata: output.metadata,
data: output.data,
metadata: metadata,
data: data,
outputId: output.outputId
},
},
};
@@ -706,7 +1083,7 @@ var requirejs = (function() {
this.reversedInsetMapping.set(message.outputId, content.source);
}
removeInset(output: IDisplayOutputViewModel) {
removeInset(output: ICellOutputViewModel) {
if (this._disposed) {
return;
}
@@ -729,7 +1106,7 @@ var requirejs = (function() {
this.reversedInsetMapping.delete(id);
}
hideInset(output: IDisplayOutputViewModel) {
hideInset(output: ICellOutputViewModel) {
if (this._disposed) {
return;
}
@@ -149,7 +149,7 @@ export class CellDragAndDropController extends Disposable {
return;
}
const dropDirection = this.getDropInsertDirection(event);
const dropDirection = this.getDropInsertDirection(event.dragPosRatio);
const insertionIndicatorAbsolutePos = dropDirection === 'above' ? event.cellTop : event.cellTop + event.cellHeight;
const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_GAP / 2;
if (insertionIndicatorTop >= 0) {
@@ -160,8 +160,8 @@ export class CellDragAndDropController extends Disposable {
}
}
private getDropInsertDirection(event: CellDragEvent): 'above' | 'below' {
return event.dragPosRatio < 0.5 ? 'above' : 'below';
private getDropInsertDirection(dragPosRatio: number): 'above' | 'below' {
return dragPosRatio < 0.5 ? 'above' : 'below';
}
private onCellDrop(event: CellDragEvent): void {
@@ -189,7 +189,7 @@ export class CellDragAndDropController extends Disposable {
const isCopy = (event.browserEvent.ctrlKey && !platform.isMacintosh) || (event.browserEvent.altKey && platform.isMacintosh);
const dropDirection = this.getDropInsertDirection(event);
const dropDirection = this.getDropInsertDirection(event.dragPosRatio);
const insertionIndicatorAbsolutePos = dropDirection === 'above' ? event.cellTop : event.cellTop + event.cellHeight;
const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_GAP / 2;
const editorHeight = this.notebookEditor.getDomNode().getBoundingClientRect().height;
@@ -275,4 +275,65 @@ export class CellDragAndDropController extends Disposable {
this.notebookEditor.textModel!.pushStackElement('Copy Cells', undefined, undefined);
}
public startExplicitDrag(cell: ICellViewModel, position: { clientY: number }) {
this.currentDraggedCell = cell;
this.setInsertIndicatorVisibility(true);
}
public explicitDrag(cell: ICellViewModel, position: { clientY: number }) {
const target = this.list.elementAt(position.clientY);
if (target && target !== cell) {
const cellTop = this.list.getAbsoluteTopOfElement(target);
const cellHeight = this.list.elementHeight(target);
const dragOffset = this.list.scrollTop + position.clientY - cellTop;
const dragPosInElement = dragOffset - cellTop;
const dragPosRatio = dragPosInElement / cellHeight;
const dropDirection = this.getDropInsertDirection(dragPosRatio);
const insertionIndicatorAbsolutePos = dropDirection === 'above' ? cellTop : cellTop + cellHeight;
const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_GAP / 2;
if (insertionIndicatorTop >= 0) {
this.listInsertionIndicator.style.top = `${insertionIndicatorTop}px`;
}
}
}
public endExplicitDrag(cell: ICellViewModel, ctx: { clientY: number, ctrlKey: boolean, altKey: boolean }) {
this.currentDraggedCell = undefined;
this.setInsertIndicatorVisibility(false);
const target = this.list.elementAt(ctx.clientY);
if (!target || target === cell) {
return;
}
const cellTop = this.list.getAbsoluteTopOfElement(target);
const cellHeight = this.list.elementHeight(target);
const dragOffset = this.list.scrollTop + ctx.clientY - cellTop;
const dragPosInElement = dragOffset - cellTop;
const dragPosRatio = dragPosInElement / cellHeight;
const dropDirection = this.getDropInsertDirection(dragPosRatio);
const isCopy = (ctx.ctrlKey && !platform.isMacintosh) || (ctx.altKey && platform.isMacintosh);
if (isCopy) {
this.copyCells([cell], target, dropDirection);
} else {
const viewModel = this.notebookEditor.viewModel!;
let originalToIdx = viewModel.getCellIndex(target);
if (dropDirection === 'below') {
const relativeToIndex = viewModel.getCellIndex(target);
const newIdx = viewModel.getNextVisibleCellIndex(relativeToIndex);
originalToIdx = newIdx;
}
const draggedCellRange = [this.notebookEditor.viewModel!.getCellIndex(cell), 1];
this.notebookEditor.moveCellsToIdx(draggedCellRange[0], draggedCellRange[1], originalToIdx);
}
}
}
@@ -10,10 +10,10 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { CodeCellRenderTemplate, ICellOutputViewModel, IDisplayOutputViewModel, IInsetRenderOutput, INotebookEditor, IRenderOutput, outputHasDynamicHeight, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CodeCellRenderTemplate, ICellOutputViewModel, IInsetRenderOutput, INotebookEditor, IRenderOutput, outputHasDynamicHeight, 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, CellOutputKind, NotebookCellOutputsSplice, IOrderedMimeType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { BUILTIN_RENDERER_ID, CellUri, NotebookCellOutputsSplice, IOrderedMimeType } 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';
@@ -55,6 +55,10 @@ export class CellOutputElement extends Disposable {
readonly output: ICellOutputViewModel
) {
super();
this._register(this.output.model.onDidChangeData(() => {
this.updateOutputRendering();
}));
}
detach() {
@@ -67,6 +71,22 @@ export class CellOutputElement extends Disposable {
}
}
updateOutputRendering() {
// user chooses another mimetype
const index = this.viewCell.outputsViewModels.indexOf(this.output);
const nextElement = this.domNode.nextElementSibling;
this.resizeListener.clear();
const element = this.domNode;
if (element) {
element.parentElement?.removeChild(element);
this.notebookEditor.removeInset(this.output);
}
// this.output.pickedMimeType = pick;
this.render(index, nextElement as HTMLElement);
this.relayoutCell();
}
render(index: number, beforeElement?: HTMLElement) {
if (this.viewCell.metadata.outputCollapsed || !this.notebookEditor.hasModel()) {
@@ -75,60 +95,36 @@ export class CellOutputElement extends Disposable {
const notebookTextModel = this.notebookEditor.viewModel.notebookDocument;
let outputItemDiv;
let renderResult: IRenderOutput | undefined = undefined;
if (this.output.isDisplayOutput()) {
outputItemDiv = document.createElement('div');
const [mimeTypes, pick] = this.output.resolveMimeTypes(notebookTextModel);
if (mimeTypes.length > 1) {
this.attachMimetypeSwitcher(outputItemDiv, notebookTextModel, mimeTypes);
}
// Reuse output item div
this.useDedicatedDOM = !(!beforeElement && this.output.supportAppend() && this.previousDivSupportAppend());
this.domNode = this.useDedicatedDOM ? DOM.$('.output-inner-container') : this.outputContainer.lastChild as HTMLElement;
if (mimeTypes.length !== 0) {
const pickedMimeTypeRenderer = mimeTypes[pick];
const innerContainer = DOM.$('.output-inner-container');
DOM.append(outputItemDiv, innerContainer);
if (pickedMimeTypeRenderer.rendererId !== BUILTIN_RENDERER_ID) {
const renderer = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);
renderResult = renderer
? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType }
: this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),);
} else {
renderResult = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),);
}
this.output.pickedMimeType = pick;
}
} else if (this.output.isStreamOutput()) {
let innerContainer: HTMLElement;
if (!beforeElement && this.isPreviousDivStreamOutput() && this.output.isStreamOutput()) {
this.useDedicatedDOM = false;
// the previous output and this one are both stream output
outputItemDiv = this.outputContainer.lastChild as HTMLElement;
innerContainer = outputItemDiv.lastChild && (<HTMLElement>outputItemDiv.lastChild).classList.contains('output-inner-container') ? outputItemDiv.lastChild as HTMLElement : document.createElement('div');
} else {
outputItemDiv = document.createElement('div');
innerContainer = DOM.$('.output-inner-container');
}
outputItemDiv.classList.add('stream-output');
DOM.append(outputItemDiv, innerContainer);
renderResult = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, undefined, this.getNotebookUri(),);
} else {
// for text and error, there is no mimetype
outputItemDiv = document.createElement('div');
const innerContainer = DOM.$('.output-inner-container');
DOM.append(outputItemDiv, innerContainer);
renderResult = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, undefined, this.getNotebookUri(),);
if (this.output.supportAppend()) {
this.domNode.classList.add('support-append');
}
const [mimeTypes, pick] = this.output.resolveMimeTypes(notebookTextModel);
if (mimeTypes.length > 1) {
this.attachMimetypeSwitcher(this.domNode, notebookTextModel, mimeTypes);
}
if (mimeTypes.length !== 0) {
const pickedMimeTypeRenderer = mimeTypes[pick];
if (pickedMimeTypeRenderer.rendererId !== BUILTIN_RENDERER_ID) {
const renderer = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);
renderResult = renderer
? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType }
: this.notebookEditor.getOutputRenderer().render(this.output, this.domNode, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),);
} else {
renderResult = this.notebookEditor.getOutputRenderer().render(this.output, this.domNode, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),);
}
this.output.pickedMimeType = pick;
}
this.domNode = outputItemDiv;
this.renderResult = renderResult;
if (!renderResult) {
@@ -137,27 +133,28 @@ export class CellOutputElement extends Disposable {
}
if (beforeElement) {
this.outputContainer.insertBefore(outputItemDiv, beforeElement);
this.outputContainer.insertBefore(this.domNode, beforeElement);
} else if (this.useDedicatedDOM) {
this.outputContainer.appendChild(outputItemDiv);
this.outputContainer.appendChild(this.domNode);
}
if (renderResult.type !== RenderOutputType.None) {
if (renderResult.type !== RenderOutputType.Mainframe) {
this.notebookEditor.createInset(this.viewCell, renderResult, this.viewCell.getOutputOffset(index));
this.domNode.classList.add('background');
} else {
outputItemDiv.classList.add('foreground', 'output-element');
outputItemDiv.style.position = 'absolute';
this.domNode.classList.add('foreground', 'output-element');
this.domNode.style.position = 'absolute';
}
if (outputHasDynamicHeight(renderResult)) {
const clientHeight = outputItemDiv.clientHeight;
const clientHeight = this.domNode.clientHeight;
const dimension = {
width: this.viewCell.layoutInfo.editorWidth,
height: clientHeight
};
const elementSizeObserver = getResizesObserver(outputItemDiv, dimension, () => {
const elementSizeObserver = getResizesObserver(this.domNode, dimension, () => {
if (this.outputContainer && document.body.contains(this.outputContainer)) {
const height = Math.ceil(elementSizeObserver.getHeight());
const height = Math.ceil(elementSizeObserver.getHeight()) + 8;
if (clientHeight === height) {
return;
@@ -175,19 +172,19 @@ export class CellOutputElement extends Disposable {
elementSizeObserver.startObserving();
this.resizeListener.add(elementSizeObserver);
this.viewCell.updateOutputHeight(index, clientHeight);
} else if (renderResult.type === RenderOutputType.None) { // no-op if it's a webview
} else if (renderResult.type === RenderOutputType.Mainframe) { // no-op if it's a webview
if (this.useDedicatedDOM) {
const clientHeight = Math.ceil(outputItemDiv.clientHeight);
const clientHeight = Math.ceil(this.domNode.clientHeight);
this.viewCell.updateOutputHeight(index, clientHeight);
const top = this.viewCell.getOutputOffsetInContainer(index);
outputItemDiv.style.top = `${top}px`;
this.domNode.style.top = `${top}px`;
}
}
}
private isPreviousDivStreamOutput() {
return this.outputContainer.lastChild && (<HTMLElement>this.outputContainer.lastChild).classList.contains('stream-output');
private previousDivSupportAppend() {
return this.outputContainer.lastChild && (<HTMLElement>this.outputContainer.lastChild).classList.contains('support-append');
}
private async attachMimetypeSwitcher(outputItemDiv: HTMLElement, notebookTextModel: NotebookTextModel, mimeTypes: readonly IOrderedMimeType[]) {
@@ -201,7 +198,7 @@ export class CellOutputElement extends Disposable {
if (e.leftButton) {
e.preventDefault();
e.stopPropagation();
await this.pickActiveMimeTypeRenderer(notebookTextModel, this.output as IDisplayOutputViewModel);
await this.pickActiveMimeTypeRenderer(notebookTextModel, this.output);
}
}));
@@ -210,12 +207,12 @@ export class CellOutputElement extends Disposable {
if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) {
e.preventDefault();
e.stopPropagation();
await this.pickActiveMimeTypeRenderer(notebookTextModel, this.output as IDisplayOutputViewModel);
await this.pickActiveMimeTypeRenderer(notebookTextModel, this.output);
}
})));
}
private async pickActiveMimeTypeRenderer(notebookTextModel: NotebookTextModel, viewModel: IDisplayOutputViewModel) {
private async pickActiveMimeTypeRenderer(notebookTextModel: NotebookTextModel, viewModel: ICellOutputViewModel) {
const [mimeTypes, currIndex] = viewModel.resolveMimeTypes(notebookTextModel);
const items = mimeTypes.filter(mimeType => mimeType.isTrusted).map((mimeType, index): IMimeTypeRenderer => ({
@@ -367,7 +364,7 @@ export class CellOutputContainer extends Disposable {
const renderedOutput = this.outputEntries.get(currOutput);
if (renderedOutput && renderedOutput.renderResult) {
if (renderedOutput.renderResult.type !== RenderOutputType.None) {
if (renderedOutput.renderResult.type !== RenderOutputType.Mainframe) {
this.notebookEditor.createInset(this.viewCell, renderedOutput.renderResult as IInsetRenderOutput, this.viewCell.getOutputOffset(index));
} else {
this.viewCell.updateOutputHeight(index, renderedOutput.domClientHeight);
@@ -383,14 +380,14 @@ export class CellOutputContainer extends Disposable {
viewUpdateHideOuputs(): void {
for (const e of this.outputEntries.keys()) {
this.notebookEditor.hideInset(e as IDisplayOutputViewModel);
this.notebookEditor.hideInset(e);
}
}
onCellWidthChange(): void {
this.viewCell.outputsViewModels.forEach((o, i) => {
const renderedOutput = this.outputEntries.get(o);
if (renderedOutput && renderedOutput.renderResult && renderedOutput.renderResult.type === RenderOutputType.None && !renderedOutput.renderResult.hasDynamicHeight) {
if (renderedOutput && renderedOutput.renderResult && renderedOutput.renderResult.type === RenderOutputType.Mainframe && !renderedOutput.renderResult.hasDynamicHeight) {
this.viewCell.updateOutputHeight(i, renderedOutput.domClientHeight);
}
});
@@ -401,30 +398,18 @@ export class CellOutputContainer extends Disposable {
if (!this.notebookEditor.viewModel!.metadata.trusted) {
// not trusted
const secureOutput = outputs.filter(output => {
switch (output.model.outputKind) {
case CellOutputKind.Text:
return true;
case CellOutputKind.Error:
return true;
case CellOutputKind.Rich:
{
const mimeTypes = [];
for (const property in output.model.data) {
mimeTypes.push(property);
}
const mimeTypes = output.model.outputs.map(op => op.mime);
if (mimeTypes.indexOf('text/plain') >= 0
|| mimeTypes.indexOf('text/markdown') >= 0
|| mimeTypes.indexOf('application/json') >= 0
|| mimeTypes.includes('image/png')) {
return true;
}
return false;
}
default:
return false;
if (mimeTypes.indexOf('application/x.notebook.stream') >= 0
|| mimeTypes.indexOf('application/x.notebook.error-traceback') >= 0
|| mimeTypes.indexOf('text/plain') >= 0
|| mimeTypes.indexOf('text/markdown') >= 0
|| mimeTypes.indexOf('application/json') >= 0
|| mimeTypes.includes('image/png')) {
return true;
}
return false;
});
return secureOutput;
@@ -460,9 +445,7 @@ export class CellOutputContainer extends Disposable {
removedKeys.push(key);
// remove element from DOM
value.detach();
if (key.isDisplayOutput()) {
this.notebookEditor.removeInset(key);
}
this.notebookEditor.removeInset(key);
}
});
@@ -525,25 +508,7 @@ export class CellOutputContainer extends Disposable {
callback: (content) => {
if (content === 'command:workbench.action.openLargeOutput') {
const content = JSON.stringify(this.viewCell.outputsViewModels.map(output => {
switch (output.model.outputKind) {
case CellOutputKind.Text:
return {
outputKind: 'text',
text: output.model.text
};
case CellOutputKind.Error:
return {
outputKind: 'error',
ename: output.model.ename,
evalue: output.model.evalue,
traceback: output.model.traceback
};
case CellOutputKind.Rich:
return {
data: output.model.data,
metadata: output.model.metadata
};
}
return output.toRawJSON();
}));
const edits = format(content, undefined, {});
const metadataSource = applyEdits(content, edits);
@@ -414,6 +414,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container'));
const betweenCellToolbar = disposables.add(this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService));
const focusIndicatorBottom = DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom'));
const statusBar = disposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart));
DOM.hide(statusBar.durationContainer);
@@ -432,6 +433,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
editorPart,
editorContainer,
focusIndicatorLeft,
focusIndicatorBottom,
foldingIndicator,
disposables,
elementDisposables: new DisposableStore(),
@@ -524,6 +526,11 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, this.notebookEditor.viewModel.notebookDocument!, element));
this.updateForLayout(element, templateData);
elementDisposables.add(element.onDidChangeLayout(() => {
this.updateForLayout(element, templateData);
}));
// render toolbar first
this.setupCellToolbarActions(templateData, elementDisposables);
@@ -545,6 +552,11 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
templateData.statusBar.update(toolbarContext);
}
private updateForLayout(element: MarkdownCellViewModel, templateData: MarkdownCellRenderTemplate): void {
// templateData.focusIndicatorLeft.style.height = `${element.layoutInfo.indicatorHeight}px`;
templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP - CELL_BOTTOM_MARGIN}px`;
}
disposeTemplate(templateData: MarkdownCellRenderTemplate): void {
templateData.disposables.clear();
}
@@ -23,16 +23,97 @@ import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/com
import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { collapsedIcon, expandedIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
interface IMarkdownRenderStrategy extends IDisposable {
update(): void;
}
class WebviewMarkdownRenderer extends Disposable implements IMarkdownRenderStrategy {
constructor(
readonly notebookEditor: IActiveNotebookEditor,
readonly viewCell: MarkdownCellViewModel
) {
super();
}
update(): void {
this.notebookEditor.createMarkdownPreview(this.viewCell);
}
}
class BuiltinMarkdownRenderer extends Disposable implements IMarkdownRenderStrategy {
private localDisposables = new DisposableStore();
constructor(
readonly notebookEditor: IActiveNotebookEditor,
readonly viewCell: MarkdownCellViewModel,
readonly container: HTMLElement,
readonly markdownContainer: HTMLElement,
readonly dataSource: { codeEditor: CodeEditorWidget | null; }
) {
super();
this._register(getResizesObserver(this.markdownContainer, undefined, () => {
if (viewCell.editState === CellEditState.Preview) {
this.viewCell.renderedMarkdownHeight = container.clientHeight;
}
})).startObserving();
}
update(): void {
const markdownRenderer = this.viewCell.getMarkdownRenderer();
const renderedHTML = this.viewCell.getHTML();
if (renderedHTML) {
this.markdownContainer.appendChild(renderedHTML);
}
if (this.dataSource.codeEditor) {
// switch from editing mode
this.viewCell.renderedMarkdownHeight = this.container.clientHeight;
this.relayoutCell();
} else {
// first time, readonly mode
this.localDisposables.add(markdownRenderer.onDidRenderAsync(() => {
this.viewCell.renderedMarkdownHeight = this.container.clientHeight;
this.relayoutCell();
}));
this.localDisposables.add(this.viewCell.textBuffer.onDidChangeContent(() => {
this.markdownContainer.innerText = '';
this.viewCell.clearHTML();
const renderedHTML = this.viewCell.getHTML();
if (renderedHTML) {
this.markdownContainer.appendChild(renderedHTML);
}
}));
this.viewCell.renderedMarkdownHeight = this.container.clientHeight;
this.relayoutCell();
}
}
relayoutCell() {
this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight);
}
}
export class StatefulMarkdownCell extends Disposable {
private editor: CodeEditorWidget | null = null;
get codeEditor() {
return this.editor;
}
private markdownContainer: HTMLElement;
private editorPart: HTMLElement;
private localDisposables = new DisposableStore();
private foldingState: CellFoldingState;
private _activeCellRunPlaceholder: IDisposable | null = null;
private _useRenderer: boolean = false;
private _renderStrategy: IMarkdownRenderStrategy;
constructor(
private readonly notebookEditor: IActiveNotebookEditor,
@@ -43,11 +124,21 @@ export class StatefulMarkdownCell extends Disposable {
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@INotebookCellStatusBarService readonly notebookCellStatusBarService: INotebookCellStatusBarService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IConfigurationService private readonly configurationSerivce: IConfigurationService
) {
super();
this.markdownContainer = templateData.cellContainer;
this.editorPart = templateData.editorPart;
this._useRenderer = !!(this.configurationSerivce.getValue<string>('notebook.experimental.useMarkdownRenderer'));
if (this._useRenderer) {
this._renderStrategy = new WebviewMarkdownRenderer(this.notebookEditor, this.viewCell);
} else {
this._renderStrategy = new BuiltinMarkdownRenderer(this.notebookEditor, this.viewCell, this.templateData.container, this.markdownContainer, this);
}
this._register(this._renderStrategy);
this._register(this.localDisposables);
this._register(toDisposable(() => renderedEditors.delete(this.viewCell)));
@@ -64,12 +155,6 @@ export class StatefulMarkdownCell extends Disposable {
this.viewUpdate();
}));
this._register(getResizesObserver(this.markdownContainer, undefined, () => {
if (viewCell.editState === CellEditState.Preview) {
this.viewCell.renderedMarkdownHeight = templateData.container.clientHeight;
}
})).startObserving();
const updateForFocusMode = () => {
if (viewCell.focusMode === CellFocusMode.Editor) {
this.focusEditorIfNeeded();
@@ -286,35 +371,8 @@ export class StatefulMarkdownCell extends Disposable {
this.markdownContainer.innerText = '';
this.viewCell.clearHTML();
const markdownRenderer = this.viewCell.getMarkdownRenderer();
const renderedHTML = this.viewCell.getHTML();
if (renderedHTML) {
this.markdownContainer.appendChild(renderedHTML);
}
if (this.editor) {
// switch from editing mode
this.viewCell.renderedMarkdownHeight = this.templateData.container.clientHeight;
this.relayoutCell();
} else {
// first time, readonly mode
this.localDisposables.add(markdownRenderer.onDidRenderAsync(() => {
this.viewCell.renderedMarkdownHeight = this.templateData.container.clientHeight;
this.relayoutCell();
}));
this.localDisposables.add(this.viewCell.textBuffer.onDidChangeContent(() => {
this.markdownContainer.innerText = '';
this.viewCell.clearHTML();
const renderedHTML = this.viewCell.getHTML();
if (renderedHTML) {
this.markdownContainer.appendChild(renderedHTML);
}
}));
this.viewCell.renderedMarkdownHeight = this.templateData.container.clientHeight;
this.relayoutCell();
}
this._renderStrategy.update();
}
private focusEditorIfNeeded() {
@@ -342,7 +400,7 @@ export class StatefulMarkdownCell extends Disposable {
// this.relayoutCell();
}
private relayoutCell(): void {
relayoutCell(): void {
this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight);
}
@@ -420,6 +478,7 @@ export class StatefulMarkdownCell extends Disposable {
}
dispose() {
this.notebookEditor.removeMarkdownPreview(this.viewCell);
this.viewCell.detachTextEditor();
super.dispose();
}
@@ -5,7 +5,7 @@
import type { Event } from 'vs/base/common/event';
import type { IDisposable } from 'vs/base/common/lifecycle';
import { ToWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';
import { ICellDragEndMessage, ICellDragMessage, ICellDragStartMessage, ToWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';
import { RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
// !! IMPORTANT !! everything must be in-line within the webviewPreloads
@@ -136,7 +136,7 @@ function webviewPreloads() {
const outputObservers = new Map<string, ResizeObserver>();
const resizeObserve = (container: Element, id: string) => {
const resizeObserve = (container: Element, id: string, output: boolean) => {
const resizeObserver = new ResizeObserver(entries => {
for (const entry of entries) {
if (!document.body.contains(entry.target)) {
@@ -152,7 +152,8 @@ function webviewPreloads() {
id: id,
data: {
height: entry.contentRect.height + __outputNodePadding__ * 2
}
},
isOutput: output
});
} else {
entry.target.style.padding = `0px`;
@@ -162,7 +163,8 @@ function webviewPreloads() {
id: id,
data: {
height: entry.contentRect.height
}
},
isOutput: output
});
}
}
@@ -320,6 +322,10 @@ function webviewPreloads() {
mimeType?: string;
element: HTMLElement;
}
interface ICreateMarkdownInfo {
readonly content: string;
readonly element: HTMLElement;
}
interface IDestroyCellInfo {
outputId: string;
@@ -327,6 +333,7 @@ function webviewPreloads() {
const onWillDestroyOutput = createEmitter<[string | undefined /* namespace */, IDestroyCellInfo | undefined /* cell uri */]>();
const onDidCreateOutput = createEmitter<[string | undefined /* namespace */, ICreateCellInfo]>();
const onDidCreateMarkdown = createEmitter<[string | undefined /* namespace */, ICreateMarkdownInfo]>();
const onDidReceiveMessage = createEmitter<[string, unknown]>();
const matchesNs = (namespace: string, query: string | undefined) => namespace === '*' || query === namespace || query === 'undefined';
@@ -355,6 +362,7 @@ function webviewPreloads() {
onDidReceiveMessage: mapEmitter(onDidReceiveMessage, ([ns, data]) => ns === namespace ? data : dontEmit),
onWillDestroyOutput: mapEmitter(onWillDestroyOutput, ([ns, data]) => matchesNs(namespace, ns) ? data : dontEmit),
onDidCreateOutput: mapEmitter(onDidCreateOutput, ([ns, data]) => matchesNs(namespace, ns) ? data : dontEmit),
onDidCreateMarkdown: mapEmitter(onDidCreateMarkdown, ([ns, data]) => data),
};
};
@@ -405,6 +413,51 @@ function webviewPreloads() {
const event = rawEvent as ({ data: ToWebviewMessage; });
switch (event.data.type) {
case 'initializeMarkdownPreview':
for (const cell of event.data.cells) {
createMarkdownPreview(cell.cellId, cell.content, -10000);
}
vscode.postMessage({
__vscode_notebook_message: true,
type: 'initializedMarkdownPreview',
});
break;
case 'createMarkdownPreview':
createMarkdownPreview(event.data.id, event.data.content, event.data.top);
break;
case 'showMarkdownPreview':
{
const data = event.data;
let cellContainer = document.getElementById(data.id);
if (cellContainer) {
cellContainer.style.display = 'block';
}
const previewNode = document.getElementById(`${data.id}_container`);
if (previewNode) {
previewNode.style.top = `${data.top}px`;
}
updateMarkdownPreview(data.id, data.content);
}
break;
case 'hideMarkdownPreview':
{
const data = event.data;
let cellContainer = document.getElementById(data.id);
if (cellContainer) {
cellContainer.style.display = 'none';
}
}
break;
case 'removeMarkdownPreview':
{
const data = event.data;
let cellContainer = document.getElementById(data.id);
if (cellContainer) {
cellContainer?.parentElement?.removeChild(cellContainer);
}
}
break;
case 'html':
enqueueOutputAction(event.data, async data => {
const preloadResults = await Promise.all(data.requiredPreloads.map(p => preloadPromises.get(p.uri)));
@@ -431,6 +484,7 @@ function webviewPreloads() {
}
const outputNode = document.createElement('div');
outputNode.classList.add('output');
outputNode.style.position = 'absolute';
outputNode.style.top = data.top + 'px';
outputNode.style.left = data.left + 'px';
@@ -468,7 +522,7 @@ function webviewPreloads() {
cellOutputContainer.appendChild(outputNode);
}
resizeObserve(outputNode, outputId);
resizeObserve(outputNode, outputId, true);
vscode.postMessage({
__vscode_notebook_message: true,
@@ -498,6 +552,21 @@ function webviewPreloads() {
}
}
}
break;
}
case 'view-scroll-markdown':
{
// const date = new Date();
// console.log('----- will scroll ---- ', date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds());
event.data.cells.map(cell => {
const widget = document.getElementById(`${cell.id}_preview`)!;
if (widget) {
widget.style.top = `${cell.top}px`;
}
});
break;
}
case 'clear':
@@ -589,6 +658,127 @@ function webviewPreloads() {
__vscode_notebook_message: true,
type: 'initialized'
});
document.addEventListener('dragover', e => {
// Allow dropping dragged markdown cells
e.preventDefault();
});
const markdownCellDragDataType = 'x-vscode-markdown-cell-drag';
document.addEventListener('drop', e => {
const data = e.dataTransfer?.getData(markdownCellDragDataType);
if (!data) {
return;
}
e.preventDefault();
const { cellId } = JSON.parse(data);
const msg: ICellDragEndMessage = {
__vscode_notebook_message: true,
type: 'cell-drag-end',
cellId: cellId,
ctrlKey: e.ctrlKey,
altKey: e.altKey,
position: { clientX: e.clientX, clientY: e.clientY },
};
vscode.postMessage(msg);
});
function createMarkdownPreview(cellId: string, content: string, top: number) {
let cellContainer = document.getElementById(cellId);
if (!cellContainer) {
const container = document.getElementById('container')!;
const newElement = document.createElement('div');
newElement.id = `${cellId}`;
container.appendChild(newElement);
cellContainer = newElement;
const previewContainerNode = document.createElement('div');
previewContainerNode.style.position = 'absolute';
previewContainerNode.style.top = top + 'px';
previewContainerNode.id = `${cellId}_preview`;
previewContainerNode.classList.add('preview');
previewContainerNode.addEventListener('dblclick', () => {
vscode.postMessage({
__vscode_notebook_message: true,
type: 'toggleMarkdownPreview',
cellId,
});
});
previewContainerNode.setAttribute('draggable', 'true');
previewContainerNode.addEventListener('dragstart', e => {
if (!e.dataTransfer) {
return;
}
e.dataTransfer.setData(markdownCellDragDataType, JSON.stringify({ cellId }));
(e.target as HTMLElement).classList.add('dragging');
const msg: ICellDragStartMessage = {
__vscode_notebook_message: true,
type: 'cell-drag-start',
cellId: cellId,
position: { clientX: e.clientX, clientY: e.clientY },
};
vscode.postMessage(msg);
});
previewContainerNode.addEventListener('drag', e => {
const msg: ICellDragMessage = {
__vscode_notebook_message: true,
type: 'cell-drag',
cellId: cellId,
position: { clientX: e.clientX, clientY: e.clientY },
};
vscode.postMessage(msg);
});
previewContainerNode.addEventListener('dragend', e => {
(e.target as HTMLElement).classList.remove('dragging');
});
cellContainer.appendChild(previewContainerNode);
const previewNode = document.createElement('div');
previewContainerNode.appendChild(previewNode);
// TODO: handle namespace
onDidCreateMarkdown.fire([undefined /* data.apiNamespace */, {
element: previewNode,
content: content
}]);
resizeObserve(previewContainerNode, `${cellId}_preview`, false);
vscode.postMessage({
__vscode_notebook_message: true,
type: 'dimension',
id: `${cellId}_preview`,
init: true,
data: {
height: previewContainerNode.clientHeight
},
isOutput: false
});
} else {
updateMarkdownPreview(cellId, content);
}
}
function updateMarkdownPreview(cellId: string, content: string) {
const previewNode = document.getElementById(`${cellId}_preview`);
if (previewNode) {
// TODO: handle namespace
onDidCreateMarkdown.fire([undefined /* data.apiNamespace */, {
element: previewNode,
content: content
}]);
}
}
}
export const preloadsScriptStr = (outputNodePadding: number, outputNodeLeftPadding: number) => `(${webviewPreloads})()`.replace(/__outputNodePadding__/g, `${outputNodePadding}`).replace(/__outputNodeLeftPadding__/g, `${outputNodeLeftPadding}`);
@@ -5,7 +5,7 @@
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { CellKind, IProcessedOutput, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellKind, IOutputDto, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IResourceUndoRedoElement, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
import { URI } from 'vs/base/common/uri';
import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel';
@@ -16,7 +16,7 @@ import { ITextCellEditingDelegate } from 'vs/workbench/contrib/notebook/common/m
export interface IViewCellEditingDelegate extends ITextCellEditingDelegate {
createCellViewModel?(cell: NotebookCellTextModel): BaseCellViewModel;
createCell?(index: number, source: string, language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, outputs: IProcessedOutput[]): BaseCellViewModel;
createCell?(index: number, source: string, language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, outputs: IOutputDto[]): BaseCellViewModel;
}
export class JoinCellEdit implements IResourceUndoRedoElement {
@@ -4,16 +4,16 @@
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { ICellOutputViewModel, IDisplayOutputViewModel, IErrorOutputViewModel, IGenericCellViewModel, IStreamOutputViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { ICellOutputViewModel, IGenericCellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { CellOutputKind, IOrderedMimeType, IProcessedOutput, ITransformedDisplayOutputDto, RENDERER_NOT_AVAILABLE } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ICellOutput, IOrderedMimeType, RENDERER_NOT_AVAILABLE } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
let handle = 0;
export class CellOutputViewModel extends Disposable implements ICellOutputViewModel {
outputHandle = handle++;
get model() {
return this._outputData;
get model(): ICellOutput {
return this._outputRawData;
}
private _pickedMimeType: number = -1;
@@ -27,26 +27,19 @@ export class CellOutputViewModel extends Disposable implements ICellOutputViewMo
constructor(
readonly cellViewModel: IGenericCellViewModel,
private readonly _outputData: IProcessedOutput,
private readonly _outputRawData: ICellOutput,
private readonly _notebookService: INotebookService
) {
super();
}
isStreamOutput(): this is IStreamOutputViewModel {
return this._outputData.outputKind === CellOutputKind.Text;
}
isErrorOutput(): this is IErrorOutputViewModel {
return this._outputData.outputKind === CellOutputKind.Error;
}
isDisplayOutput(): this is IDisplayOutputViewModel {
return this._outputData.outputKind === CellOutputKind.Rich;
supportAppend() {
// if there is any mime type other than `application/x.notebook.stream`, then it's not mergeable.
return !this._outputRawData.outputs.find(op => op.mime !== 'application/x.notebook.stream');
}
resolveMimeTypes(textModel: NotebookTextModel): [readonly IOrderedMimeType[], number] {
const mimeTypes = this._notebookService.getMimeTypeInfo(textModel, this.model as ITransformedDisplayOutputDto);
const mimeTypes = this._notebookService.getMimeTypeInfo(textModel, this.model);
if (this._pickedMimeType === -1) {
// there is at least one mimetype which is safe and can be rendered by the core
this._pickedMimeType = Math.max(mimeTypes.findIndex(mimeType => mimeType.rendererId !== RENDERER_NOT_AVAILABLE && mimeType.isTrusted), 0);
@@ -54,4 +47,11 @@ export class CellOutputViewModel extends Disposable implements ICellOutputViewMo
return [mimeTypes, this._pickedMimeType];
}
toRawJSON() {
return {
outputs: this._outputRawData.outputs,
// TODO@rebronix, no id, right?
};
}
}
@@ -23,7 +23,7 @@ import { NotebookEventDispatcher, NotebookMetadataChangedEvent } from 'vs/workbe
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';
import { CellKind, NotebookCellMetadata, INotebookSearchOptions, ICellRange, NotebookCellsChangeType, ICell, NotebookCellTextModelSplice, CellEditType, IProcessedOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellKind, NotebookCellMetadata, INotebookSearchOptions, ICellRange, NotebookCellsChangeType, ICell, NotebookCellTextModelSplice, CellEditType, IOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
@@ -645,7 +645,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
return result;
}
createCell(index: number, source: string, language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, outputs: IProcessedOutput[], synchronous: boolean, pushUndoStop: boolean = true, previouslyFocused: ICellViewModel[] = []): CellViewModel {
createCell(index: number, source: string, language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, outputs: IOutputDto[], synchronous: boolean, pushUndoStop: boolean = true, previouslyFocused: ICellViewModel[] = []): CellViewModel {
const beforeSelections = previouslyFocused.map(e => e.handle);
this._notebook.applyEdits(this._notebook.versionId, [
{

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