Revert "Better notebook markup renderer api (#121882)"

This reverts commit 4cb27d2ec7.
This commit is contained in:
Matt Bierner 2021-04-26 19:43:16 -07:00
parent 4a0a1f2f6f
commit fe7c5aba01
No known key found for this signature in database
GPG key ID: 099C331567E11888
21 changed files with 199 additions and 296 deletions

View file

@ -8,7 +8,6 @@ require('events').EventEmitter.defaultMaxListeners = 100;
const gulp = require('gulp');
const path = require('path');
const child_process = require('child_process');
const nodeUtil = require('util');
const es = require('event-stream');
const filter = require('gulp-filter');
@ -204,38 +203,30 @@ gulp.task(compileExtensionsBuildLegacyTask);
// Additional projects to webpack. These typically build code for webviews
const webpackMediaConfigFiles = [
'markdown-language-features/webpack.config.js',
'markdown-language-features/webpack.notebook.js',
'notebook-markdown-extensions/webpack.notebook.js',
'simple-browser/webpack.config.js',
];
// Additional projects to run esbuild on. These typically build code for webviews
const esbuildMediaScripts = [
'markdown-language-features/esbuild.js',
'notebook-markdown-extensions/esbuild.js',
];
const compileExtensionMediaTask = task.define('compile-extension-media', () => buildExtensionMedia(false));
const compileExtensionMediaTask = task.define('compile-extension-media', () => webpackExtensionMedia(false));
gulp.task(compileExtensionMediaTask);
exports.compileExtensionMediaTask = compileExtensionMediaTask;
const watchExtensionMedia = task.define('watch-extension-media', () => buildExtensionMedia(true));
const watchExtensionMedia = task.define('watch-extension-media', () => webpackExtensionMedia(true));
gulp.task(watchExtensionMedia);
exports.watchExtensionMedia = watchExtensionMedia;
const compileExtensionMediaBuildTask = task.define('compile-extension-media-build', () => buildExtensionMedia(false, '.build/extensions'));
gulp.task(compileExtensionMediaBuildTask);
async function buildExtensionMedia(isWatch, outputRoot) {
function webpackExtensionMedia(isWatch, outputRoot) {
const webpackConfigLocations = webpackMediaConfigFiles.map(p => {
return {
configPath: path.join(extensionsPath, p),
outputRoot: outputRoot ? path.join(root, outputRoot, path.dirname(p)) : undefined
};
});
return Promise.all([
webpackExtensions('webpacking extension media', isWatch, webpackConfigLocations),
esbuildExtensions('esbuilding extension media', isWatch, outputRoot, esbuildMediaScripts.map(p => path.join(extensionsPath, p))),
]);
return webpackExtensions('packaging extension media', isWatch, webpackConfigLocations);
}
const compileExtensionMediaBuildTask = task.define('compile-extension-media-build', () => webpackExtensionMedia(false, '.build/extensions'));
gulp.task(compileExtensionMediaBuildTask);
//#endregion
@ -345,47 +336,4 @@ async function webpackExtensions(taskName, isWatch, webpackConfigLocations) {
});
}
/**
* @param {string} taskName
* @param {boolean} isWatch
* @param {string | undefined} outputRoot
* @param {string} esbuildScripts
*/
async function esbuildExtensions(taskName, isWatch, outputRoot, esbuildScripts) {
function reporter(/** @type {string} */ stdError, /** @type {string} */script) {
const matches = (stdError || '').match(/\> (.+): error: (.+)?/g);
fancyLog(`Finished ${ansiColors.green(taskName)} ${script} with ${matches ? matches.length : 0} errors.`);
for (const match of matches || []) {
fancyLog.error(match);
}
}
const scripts = esbuildScripts.map(script => {
return new Promise((resolve, reject) => {
const args = [script];
if (isWatch) {
args.push('--watch');
}
if (outputRoot) {
args.push('--outputRoot', outputRoot);
}
const proc = child_process.execFile(process.argv[0], args, {}, (error, _stdout, stderr) => {
if (error) {
return reject(error);
}
reporter(stderr, script);
if (stderr) {
return reject();
}
return resolve();
});
proc.stdout.on('data', (data) => {
fancyLog(`${ansiColors.green(taskName)}: ${data.toString('utf8')}`);
});
});
});
return Promise.all(scripts);
}

View file

@ -1,31 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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');
const esbuild = require('esbuild');
const args = process.argv.slice(2);
const isWatch = args.indexOf('--watch') >= 0;
let outputRoot = __dirname;
const outputRootIndex = args.indexOf('--outputRoot');
if (outputRootIndex >= 0) {
outputRoot = args[outputRootIndex + 1];
}
const outDir = path.join(outputRoot, 'notebook-out');
esbuild.build({
entryPoints: [
path.join(__dirname, 'notebook', 'index.ts'),
],
bundle: true,
minify: true,
sourcemap: false,
format: 'esm',
outdir: outDir,
platform: 'browser',
target: ['es2020'],
incremental: isWatch,
}).catch(() => process.exit(1));

View file

@ -3,37 +3,31 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const MarkdownIt = require('markdown-it');
import * as MarkdownIt from 'markdown-it';
export async function activate(ctx: {
dependencies: ReadonlyArray<{ entrypoint: string }>
}) {
let markdownIt = new MarkdownIt({
declare const acquireNotebookRendererApi: any;
type extendMarkdownItFnType = (
(f: (md: MarkdownIt.MarkdownIt) => void) => void
);
(function () {
const markdownIt = new MarkdownIt({
html: true
});
// Should we load the deps before this point?
// Also could we await inside `renderMarkup`?
await Promise.all(ctx.dependencies.map(async (dep) => {
try {
const api = await import(dep.entrypoint);
if (api?.extendMarkdownIt) {
markdownIt = api.extendMarkdownIt(markdownIt);
}
} catch (e) {
console.error('Could not load markdown entryPoint', e);
}
}));
(globalThis as any).extendMarkdownIt = ((f: (md: MarkdownIt.MarkdownIt) => void) => {
f(markdownIt);
}) as extendMarkdownItFnType;
return {
renderMarkup: (context: { element: HTMLElement, content: string }) => {
const rendered = markdownIt.render(context.content);
context.element.innerHTML = rendered;
const notebook = acquireNotebookRendererApi('notebookCoreTestRenderer');
// Insert styles into markdown preview shadow dom so that they are applied
for (const markdownStyleNode of document.getElementsByClassName('markdown-style')) {
context.element.appendChild(markdownStyleNode.cloneNode(true));
}
notebook.onDidCreateMarkdown(({ element, content }: any) => {
const rendered = markdownIt.render(content);
element.innerHTML = rendered;
// Insert styles into markdown preview shadow dom so that they are applied
for (const markdownStyleNode of document.getElementsByClassName('markdown-style')) {
element.appendChild(markdownStyleNode.cloneNode(true));
}
};
}
});
}());

View file

@ -3,7 +3,6 @@
"compilerOptions": {
"outDir": "./dist/",
"jsx": "react",
"module": "es2020",
"lib": [
"es2018",
"DOM",

View file

@ -40,14 +40,11 @@
}
},
"contributes": {
"notebookMarkupRenderers": [
"notebookMarkdownRenderer": [
{
"id": "markdownItRenderer",
"displayName": "Markdown it renderer",
"entrypoint": "./notebook-out/index.js",
"mimeTypes": [
"text/markdown"
]
"entrypoint": "./notebook-out/index.js"
}
],
"commands": [
@ -346,7 +343,7 @@
"vscode:prepublish": "npm run build-ext && npm run build-preview",
"build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown-language-features ./tsconfig.json",
"build-preview": "npx webpack-cli --mode production",
"build-notebook": "node ./esbuild",
"build-notebook": "npx webpack-cli --config webpack.notebook --mode production",
"compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none",
"watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose"
},

View file

@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* 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 = {
mode: 'production',
entry: {
index: path.join(__dirname, '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')
}
};

View file

@ -1,41 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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');
const fse = require('fs-extra');
const esbuild = require('esbuild');
const args = process.argv.slice(2);
const isWatch = args.indexOf('--watch') >= 0;
let outputRoot = __dirname;
const outputRootIndex = args.indexOf('--outputRoot');
if (outputRootIndex >= 0) {
outputRoot = args[outputRootIndex + 1];
}
const outDir = path.join(outputRoot, 'notebook-out');
esbuild.build({
entryPoints: [
path.join(__dirname, 'notebook', 'katex.ts'),
path.join(__dirname, 'notebook', 'emoji.ts')
],
bundle: true,
minify: true,
sourcemap: false,
format: 'esm',
outdir: outDir,
platform: 'browser',
target: ['es2020'],
incremental: isWatch,
}).catch(() => process.exit(1));
fse.copySync(
path.join(__dirname, 'node_modules/katex/dist/katex.min.css'),
path.join(outDir, 'katex.min.css'));
fse.copySync(
path.join(__dirname, 'node_modules/katex/dist/fonts'),
path.join(outDir, 'fonts/'));

View file

@ -4,8 +4,17 @@
*--------------------------------------------------------------------------------------------*/
import type * as markdownIt from 'markdown-it';
const emoji = require('markdown-it-emoji');
declare const extendMarkdownIt: undefined | (
(f: (md: markdownIt.MarkdownIt) => void) => void
);
(function () {
if (typeof extendMarkdownIt !== 'undefined') {
const emoji = require('markdown-it-emoji');
extendMarkdownIt((md: markdownIt.MarkdownIt) => {
md.use(emoji);
});
}
}());
export function extendMarkdownIt(md: markdownIt.MarkdownIt) {
return md.use(emoji);
}

View file

@ -4,7 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import type * as markdownIt from 'markdown-it';
const styleHref = import.meta.url.replace(/katex.js$/, 'katex.min.css');
declare const extendMarkdownIt: undefined | (
(f: (md: markdownIt.MarkdownIt) => void) => void
);
const styleHref = (document.currentScript as any).src.replace(/katex.js$/, 'katex.min.css');
const link = document.createElement('link');
link.rel = 'stylesheet';
@ -13,8 +17,12 @@ link.href = styleHref;
document.head.append(link);
const katex = require('@iktakahiro/markdown-it-katex');
(function () {
const katex = require('@iktakahiro/markdown-it-katex');
if (typeof extendMarkdownIt !== 'undefined') {
export function extendMarkdownIt(md: markdownIt.MarkdownIt) {
return md.use(katex);
}
extendMarkdownIt((md: markdownIt.MarkdownIt) => {
md.use(katex);
});
}
}());

View file

@ -3,7 +3,6 @@
"compilerOptions": {
"outDir": "./dist/",
"jsx": "react",
"module": "es2020",
"lib": [
"es2018",
"DOM",

View file

@ -18,25 +18,23 @@
"virtualWorkspaces": false
},
"contributes": {
"notebookMarkupRenderers": [
"notebookMarkdownRenderer": [
{
"id": "markdownItRenderer-katex",
"displayName": "Markdown it katex renderer",
"entrypoint": "./notebook-out/katex.js",
"dependsOn": "markdownItRenderer"
"entrypoint": "./notebook-out/katex.js"
},
{
"id": "markdownItRenderer-emoji",
"displayName": "Markdown it emoji renderer",
"entrypoint": "./notebook-out/emoji.js",
"dependsOn": "markdownItRenderer"
"entrypoint": "./notebook-out/emoji.js"
}
]
},
"scripts": {
"compile": "npm run build-notebook",
"watch": "npm run build-notebook",
"build-notebook": "node ./esbuild"
"build-notebook": "npx webpack-cli --config webpack.notebook.js --mode production"
},
"devDependencies": {
"@iktakahiro/markdown-it-katex": "https://github.com/mjbvz/markdown-it-katex.git",

View file

@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* 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');
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
context: path.resolve(__dirname),
mode: 'production',
performance: {
hints: false,
maxEntrypointSize: 512000,
maxAssetSize: 512000
},
entry: {
katex: './notebook/katex.ts',
emoji: './notebook/emoji.ts',
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'notebook-out')
},
plugins: [
// @ts-ignore
new CopyPlugin({
patterns: [
{
from: './node_modules/katex/dist/katex.min.css',
to: 'katex.min.css'
},
{
from: './node_modules/katex/dist/fonts',
to: 'fonts/'
},
],
}),
]
};

View file

@ -10,7 +10,6 @@
"postinstall": "node ./postinstall"
},
"devDependencies": {
"esbuild": "^0.11.12",
"vscode-grammar-updater": "^1.0.3"
}
}

View file

@ -14,11 +14,6 @@ cson-parser@^1.3.3:
dependencies:
coffee-script "^1.10.0"
esbuild@^0.11.12:
version "0.11.12"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.11.12.tgz#8cbe15bcb44212624c3e77c896a835f74dc71c3c"
integrity sha512-c8cso/1RwVj+fbDvLtUgSG4ZJQ0y9Zdrl6Ot/GAjyy4pdMCHaFnDMts5gqFnWRPLajWtEnI+3hlET4R9fVoZng==
fast-plist@0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/fast-plist/-/fast-plist-0.1.2.tgz#a45aff345196006d406ca6cdcd05f69051ef35b8"

View file

@ -42,20 +42,16 @@ export interface INotebookRendererContribution {
readonly [NotebookRendererContribution.optionalDependencies]: readonly string[];
}
enum NotebookMarkupRendererContribution {
enum NotebookMarkdownRendererContribution {
id = 'id',
displayName = 'displayName',
entrypoint = 'entrypoint',
dependsOn = 'dependsOn',
mimeTypes = 'mimeTypes',
}
export interface INotebookMarkupRendererContribution {
readonly [NotebookMarkupRendererContribution.id]?: string;
readonly [NotebookMarkupRendererContribution.displayName]: string;
readonly [NotebookMarkupRendererContribution.entrypoint]: string;
readonly [NotebookMarkupRendererContribution.dependsOn]: string | undefined;
readonly [NotebookMarkupRendererContribution.mimeTypes]: string[] | undefined;
export interface INotebookMarkdownRendererContribution {
readonly [NotebookMarkdownRendererContribution.id]?: string;
readonly [NotebookMarkdownRendererContribution.displayName]: string;
readonly [NotebookMarkdownRendererContribution.entrypoint]: string;
}
const notebookProviderContribution: IJSONSchema = {
@ -164,39 +160,30 @@ const notebookRendererContribution: IJSONSchema = {
}
}
};
const notebookMarkupRendererContribution: IJSONSchema = {
const notebookMarkdownRendererContribution: IJSONSchema = {
description: nls.localize('contributes.notebook.markdownRenderer', 'Contributes a renderer for markdown cells in notebooks.'),
type: 'array',
defaultSnippets: [{ body: [{ id: '', displayName: '', entrypoint: '' }] }],
items: {
type: 'object',
required: [
NotebookMarkupRendererContribution.id,
NotebookMarkupRendererContribution.displayName,
NotebookMarkupRendererContribution.entrypoint,
NotebookMarkdownRendererContribution.id,
NotebookMarkdownRendererContribution.displayName,
NotebookMarkdownRendererContribution.entrypoint,
],
properties: {
[NotebookMarkupRendererContribution.id]: {
[NotebookMarkdownRendererContribution.id]: {
type: 'string',
description: nls.localize('contributes.notebook.markdownRenderer.id', 'Unique identifier of the notebook markdown renderer.'),
},
[NotebookMarkupRendererContribution.displayName]: {
[NotebookMarkdownRendererContribution.displayName]: {
type: 'string',
description: nls.localize('contributes.notebook.markdownRenderer.displayName', 'Human readable name of the notebook markdown renderer.'),
},
[NotebookMarkupRendererContribution.entrypoint]: {
[NotebookMarkdownRendererContribution.entrypoint]: {
type: 'string',
description: nls.localize('contributes.notebook.markdownRenderer.entrypoint', 'File to load in the webview to render the extension.'),
},
[NotebookMarkupRendererContribution.mimeTypes]: {
type: 'array',
items: { type: 'string' },
description: nls.localize('contributes.notebook.markdownRenderer.mimeTypes', 'The mime type that the renderer handles.'),
},
[NotebookMarkupRendererContribution.dependsOn]: {
type: 'string',
description: nls.localize('contributes.notebook.markdownRenderer.dependsOn', 'If specified, this renderer augments another renderer instead of providing full rendering.'),
},
}
}
};
@ -213,8 +200,8 @@ export const notebookRendererExtensionPoint = ExtensionsRegistry.registerExtensi
jsonSchema: notebookRendererContribution
});
export const notebookMarkupRendererExtensionPoint = ExtensionsRegistry.registerExtensionPoint<INotebookMarkupRendererContribution[]>(
export const notebookMarkdownRendererExtensionPoint = ExtensionsRegistry.registerExtensionPoint<INotebookMarkdownRendererContribution[]>(
{
extensionPoint: 'notebookMarkupRenderers',
jsonSchema: notebookMarkupRendererContribution
extensionPoint: 'notebookMarkdownRenderer',
jsonSchema: notebookMarkdownRendererContribution
});

View file

@ -20,12 +20,12 @@ 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, notebookMarkupRendererExtensionPoint, notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
import { INotebookEditorContribution, notebookMarkdownRendererExtensionPoint, notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
import { NotebookEditorOptions, updateEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellUri, DisplayOrderKey, INotebookExclusiveDocumentFilter, INotebookMarkupRendererInfo, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, NotebookDataDto, NotebookEditorPriority, NotebookRendererMatch, NotebookTextDiffEditorPreview, RENDERER_NOT_AVAILABLE, sortMimeTypes, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookMarkupRendererInfo as NotebookMarkupRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookMarkdownRenderer';
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellUri, DisplayOrderKey, INotebookExclusiveDocumentFilter, INotebookMarkdownRendererInfo, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, NotebookDataDto, NotebookEditorPriority, NotebookRendererMatch, NotebookTextDiffEditorPreview, RENDERER_NOT_AVAILABLE, sortMimeTypes, TransientOptions } 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 { ComplexNotebookProviderInfo, INotebookContentProvider, INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService';
@ -272,7 +272,7 @@ export class NotebookService extends Disposable implements INotebookService, IEd
private readonly _notebookProviders = new Map<string, ComplexNotebookProviderInfo | SimpleNotebookProviderInfo>();
private readonly _notebookProviderInfoStore: NotebookProviderInfoStore;
private readonly _notebookRenderersInfoStore = this._instantiationService.createInstance(NotebookOutputRendererInfoStore);
private readonly _markdownRenderersInfos = new Set<INotebookMarkupRendererInfo>();
private readonly _markdownRenderersInfos = new Set<INotebookMarkdownRendererInfo>();
private readonly _models = new ResourceMap<ModelData>();
private readonly _onDidCreateNotebookDocument = this._register(new Emitter<NotebookTextModel>());
@ -333,7 +333,8 @@ export class NotebookService extends Disposable implements INotebookService, IEd
}
}
});
notebookMarkupRendererExtensionPoint.setHandler((renderers) => {
notebookMarkdownRendererExtensionPoint.setHandler((renderers) => {
this._markdownRenderersInfos.clear();
for (const extension of renderers) {
@ -354,13 +355,11 @@ export class NotebookService extends Disposable implements INotebookService, IEd
continue;
}
this._markdownRenderersInfos.add(new NotebookMarkupRendererInfo({
this._markdownRenderersInfos.add(new NotebookMarkdownRendererInfo({
id,
extension: extension.description,
entrypoint: notebookContribution.entrypoint,
displayName: notebookContribution.displayName,
mimeTypes: notebookContribution.mimeTypes,
dependsOn: notebookContribution.dependsOn,
}));
}
}
@ -519,7 +518,7 @@ export class NotebookService extends Disposable implements INotebookService, IEd
this._notebookRenderersInfoStore.setPreferred(mimeType, rendererId);
}
getMarkupRendererInfo(): INotebookMarkupRendererInfo[] {
getMarkdownRendererInfo(): INotebookMarkdownRendererInfo[] {
return Array.from(this._markdownRenderersInfos);
}

View file

@ -449,7 +449,7 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
this.element.style.position = 'absolute';
}
private generateContent(coreDependencies: string, baseUrl: string) {
const markupRenderer = this.getMarkdownRenderer();
const markdownRenderersSrc = this.getMarkdownRendererScripts();
const outputWidth = `calc(100% - ${this.options.leftMargin + this.options.rightMargin + this.options.runGutter}px)`;
const outputMarginLeft = `${this.options.leftMargin + this.options.runGutter}px`;
return html`
@ -718,42 +718,31 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
</script>
${coreDependencies}
<div id='container' class="widgetarea" style="position: absolute;width:100%;top: 0px"></div>
<script type="module">${preloadsScriptStr({
<script>${preloadsScriptStr({
outputNodePadding: this.options.outputNodePadding,
outputNodeLeftPadding: this.options.outputNodeLeftPadding,
}, {
entrypoint: markupRenderer[0].entrypoint,
dependencies: markupRenderer[0].dependencies,
})}</script>
${markdownRenderersSrc}
</body>
</html>`;
}
private getMarkdownRenderer(): Array<{ entrypoint: string, dependencies: Array<{ entrypoint: string }> }> {
const allRenderers = this.notebookService.getMarkupRendererInfo();
private getMarkdownRendererScripts() {
const markdownRenderers = this.notebookService.getMarkdownRendererInfo();
const topLevelMarkdownRenderers = allRenderers
.filter(renderer => !renderer.dependsOn)
.filter(renderer => renderer.mimeTypes?.includes('text/markdown'));
const subRenderers = new Map<string, Array<{ entrypoint: string }>>();
for (const renderer of allRenderers) {
if (renderer.dependsOn) {
if (!subRenderers.has(renderer.dependsOn)) {
subRenderers.set(renderer.dependsOn, []);
return markdownRenderers
.sort((a, b) => {
// prefer built-in extension
if (a.extensionIsBuiltin) {
return b.extensionIsBuiltin ? 0 : -1;
}
const entryPoint = asWebviewUri(this.environmentService, this.id, renderer.entrypoint);
subRenderers.get(renderer.dependsOn)!.push({ entrypoint: entryPoint.toString(true) });
}
}
return topLevelMarkdownRenderers.map((renderer) => {
const src = asWebviewUri(this.environmentService, this.id, renderer.entrypoint);
return {
entrypoint: src.toString(),
dependencies: subRenderers.get(renderer.id) || [],
};
});
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) {
@ -1155,7 +1144,7 @@ var requirejs = (function() {
this.localResourceRootsCache = [
...this.notebookService.getNotebookProviderResourceRoots(),
...this.notebookService.getMarkupRendererInfo().map(x => dirname(x.entrypoint)),
...this.notebookService.getMarkdownRendererInfo().map(x => dirname(x.entrypoint)),
...workspaceFolders,
rootPath,
];

View file

@ -29,10 +29,6 @@ declare class ResizeObserver {
declare const __outputNodePadding__: number;
declare const __outputNodeLeftPadding__: number;
declare const markdownRenderer: {
renderMarkup: (context: { element: HTMLElement, content: string }) => void,
};
type Listener<T> = { fn: (evt: T) => void; thisArg: unknown; };
interface EmitterLike<T> {
@ -413,12 +409,18 @@ function webviewPreloads() {
metadata: unknown;
}
interface ICreateMarkdownInfo {
readonly content: string;
readonly element: HTMLElement;
}
interface IDestroyCellInfo {
outputId: string;
}
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';
@ -445,6 +447,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),
};
};
@ -802,11 +805,6 @@ function webviewPreloads() {
}
});
// This expression is replaced later on by the markup renderer imports.
// Due to optimization passes, we need to use an expression instead of a comment
// or other placeholder
console.log('__markdown_renderer_block__');
vscode.postMessage({
__vscode_notebook_message: true,
type: 'initialized'
@ -915,10 +913,10 @@ function webviewPreloads() {
previewContainerNode.innerText = '';
} else {
previewContainerNode.classList.remove('emptyMarkdownCell');
markdownRenderer.renderMarkup({
onDidCreateMarkdown.fire([undefined /* data.apiNamespace */, {
element: previewNode,
content: content
});
}]);
if (!hasPostedRenderedMathTelemetry) {
const hasRenderedMath = previewNode.querySelector('.katex');
@ -1017,24 +1015,11 @@ function webviewPreloads() {
}();
}
export function preloadsScriptStr(styleValues: {
export function preloadsScriptStr(values: {
outputNodePadding: number;
outputNodeLeftPadding: number;
}, markdownRenderer: {
entrypoint: string,
dependencies: Array<{ entrypoint: string }>,
}) {
const markdownCtx = {
dependencies: markdownRenderer.dependencies,
};
return `${webviewPreloads}`
.slice(0, -1)
.replace(/^function \w+\(\) \{/gm, '')
.replace(/__outputNodePadding__/g, `${styleValues.outputNodePadding}`)
.replace(/__outputNodeLeftPadding__/g, `${styleValues.outputNodeLeftPadding}`)
.replace(/console.log\('__markdown_renderer_block__'\);/g, `
import * as markdownRendererModule from "${markdownRenderer.entrypoint}";
const markdownRenderer = await markdownRendererModule.activate(JSON.parse(decodeURIComponent("${encodeURIComponent(JSON.stringify(markdownCtx))}")))
`);
return `(${webviewPreloads})()`
.replace(/__outputNodePadding__/g, `${values.outputNodePadding}`)
.replace(/__outputNodeLeftPadding__/g, `${values.outputNodeLeftPadding}`);
}

View file

@ -141,14 +141,11 @@ export interface INotebookRendererInfo {
matches(mimeType: string, kernelProvides: ReadonlyArray<string>): NotebookRendererMatch;
}
export interface INotebookMarkupRendererInfo {
readonly id: string;
export interface INotebookMarkdownRendererInfo {
readonly entrypoint: URI;
readonly extensionLocation: URI;
readonly extensionId: ExtensionIdentifier;
readonly extensionIsBuiltin: boolean;
readonly dependsOn: string | undefined;
readonly mimeTypes: readonly string[] | undefined;
}
export interface NotebookCellOutputMetadata {

View file

@ -6,9 +6,9 @@
import { joinPath } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { INotebookMarkupRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookMarkdownRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
export class NotebookMarkupRendererInfo implements INotebookMarkupRendererInfo {
export class NotebookMarkdownRendererInfo implements INotebookMarkdownRendererInfo {
readonly id: string;
readonly entrypoint: URI;
@ -16,16 +16,12 @@ export class NotebookMarkupRendererInfo implements INotebookMarkupRendererInfo {
readonly extensionLocation: URI;
readonly extensionId: ExtensionIdentifier;
readonly extensionIsBuiltin: boolean;
readonly dependsOn: string | undefined;
readonly mimeTypes: readonly string[] | undefined;
constructor(descriptor: {
readonly id: string;
readonly displayName: string;
readonly entrypoint: string;
readonly extension: IExtensionDescription;
readonly mimeTypes: readonly string[] | undefined,
readonly dependsOn: string | undefined,
}) {
this.id = descriptor.id;
this.extensionId = descriptor.extension.identifier;
@ -33,7 +29,5 @@ export class NotebookMarkupRendererInfo implements INotebookMarkupRendererInfo {
this.entrypoint = joinPath(this.extensionLocation, descriptor.entrypoint);
this.displayName = descriptor.displayName;
this.extensionIsBuiltin = descriptor.extension.isBuiltin;
this.dependsOn = descriptor.dependsOn;
this.mimeTypes = descriptor.mimeTypes;
}
}

View file

@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri';
import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol';
import { Event } from 'vs/base/common/event';
import { INotebookRendererInfo, NotebookDataDto, TransientOptions, INotebookExclusiveDocumentFilter, IOrderedMimeType, IOutputDto, INotebookMarkupRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookRendererInfo, NotebookDataDto, TransientOptions, INotebookExclusiveDocumentFilter, IOrderedMimeType, IOutputDto, INotebookMarkdownRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { CancellationToken } from 'vs/base/common/cancellation';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
@ -71,7 +71,7 @@ export interface INotebookService {
getMimeTypeInfo(textModel: NotebookTextModel, kernelProvides: readonly string[] | undefined, output: IOutputDto): readonly IOrderedMimeType[];
getRendererInfo(id: string): INotebookRendererInfo | undefined;
getMarkupRendererInfo(): INotebookMarkupRendererInfo[];
getMarkdownRendererInfo(): INotebookMarkdownRendererInfo[];
/** Updates the preferred renderer for the given mimetype in the workspace. */
updateMimePreferredRenderer(mimeType: string, rendererId: string): void;