提交 09ec3557 编写于 作者: M Matt Bierner 提交者: GitHub

Prototype Allowing Extensions to Extend the Builtin Markdown Extension (#22421)

* Prototype Allowing Extensions to Extend the Builtin Markdown Extension

**Problem**
There have been requests for adding new functionality to the markdown extension preview, such as supporting rendering of math or other syntax in the preview. The only current solution to this is create an extension that provides its own markdown preview. This results in inconsitent behavior with our markdown preview and is not a very scalable approach. We would like to find a way to allow users to add these extensions to our markdown preview without bundling the extensions in the preview itself.

**Fix**
Prototypes a new contribution point that extensions can use to extend the vscode markdown extension. Three types of extensions are possible: adding stypes to the preview, adding scripts to the preview, and extending the markdown it renderer.

My current approach defines the contributed markdown extensions in the package.json using a structure like this:

```
  "contributesTo": {
    "vscode.markdown": {
      "plugins": [
        "./out/math"
      ],
      "scripts": [],
      "styles": [
        "./media/math.css"
      ]
    }
  }
```

We could change the structure here. This design uses a pull model where markdown extensions are looked up by the vscode.markdown extension itself.

The other approach for extension registration would be to use a push model. This would have the vscode.markdown extension export an api that each markdown extension would invoke to register new scripts/styles/plugins. I may switch over to this model but was interested in seeing what a more declarative approach would look like. Let me know if you have any thoughts one way or the other.

The downside of allowing extensions like this is that they can completely change how the markdown preview looks and works. There is no well defined API for restricting what extensions can do like we have with VScode.

* Use extensionDependencies

* Remove example extension

* Added gating and activation event
上级 7ec4cd18
......@@ -231,8 +231,18 @@ export function activate(context: vscode.ExtensionContext) {
});
}
}));
}
if (vscode.workspace.getConfiguration('markdown').get('enableExperimentalExtensionApi', false)) {
vscode.commands.executeCommand('_markdown.onActivateExtensions')
.then(() => void 0, () => void 0);
return {
addPlugin(factory: (md: any) => any) {
engine.addPlugin(factory);
}
};
}
}
function showPreview(uri?: vscode.Uri, sideBySide: boolean = false) {
......
......@@ -31,6 +31,26 @@ export class MarkdownEngine {
private currentDocument: vscode.Uri;
private plugins: Array<(md: any) => any> = [];
constructor() { }
public addPlugin(factory: (md: any) => any): void {
if (this.md) {
this.usePlugin(factory);
} else {
this.plugins.push(factory);
}
}
private usePlugin(factory: (md: any) => any): void {
try {
this.md = factory(this.md);
} catch (e) {
// noop
}
}
private get engine(): MarkdownIt {
if (!this.md) {
const hljs = require('highlight.js');
......@@ -49,6 +69,11 @@ export class MarkdownEngine {
slugify: (header: string) => TableOfContentsProvider.slugify(header)
});
for (const plugin of this.plugins) {
this.usePlugin(plugin);
}
this.plugins = [];
for (const renderName of ['paragraph_open', 'heading_open', 'image', 'code_block', 'blockquote_open', 'list_item_open']) {
this.addLineNumberRenderer(this.md, renderName);
}
......
......@@ -68,7 +68,7 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href)).toString();
}
private computeCustomStyleSheetIncludes(uri: vscode.Uri, _nonce: string): string {
private computeCustomStyleSheetIncludes(uri: vscode.Uri): string {
const styles = vscode.workspace.getConfiguration('markdown')['styles'];
if (styles && Array.isArray(styles) && styles.length > 0) {
return styles.map((style) => {
......@@ -93,6 +93,24 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
</style>`;
}
private getStyles(uri: vscode.Uri, nonce: string): string {
const baseStyles = [
this.getMediaPath('markdown.css'),
this.getMediaPath('tomorrow.css')
];
return `${baseStyles.map(href => `<link rel="stylesheet" type="text/css" href="${href}">`).join('\n')}
${this.getSettingsOverrideStyles(nonce)}
${this.computeCustomStyleSheetIncludes(uri)}`;
}
private getScripts(nonce: string): string {
const scripts = [this.getMediaPath('main.js')];
return scripts
.map(source => `<script src="${source}" nonce="${nonce}"></script>`)
.join('\n');
}
public provideTextDocumentContent(uri: vscode.Uri): Thenable<string> {
const sourceUri = vscode.Uri.parse(uri.query);
return vscode.workspace.openTextDocument(sourceUri).then(document => {
......@@ -138,16 +156,13 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
${csp}
<meta id="vscode-markdown-preview-data" data-settings="${JSON.stringify(initialData).replace(/"/g, '&quot;')}" data-strings="${JSON.stringify(previewStrings).replace(/"/g, '&quot;')}">
<script src="${this.getMediaPath('csp.js')}" nonce="${nonce}"></script>
<link rel="stylesheet" type="text/css" href="${this.getMediaPath('markdown.css')}">
<link rel="stylesheet" type="text/css" href="${this.getMediaPath('tomorrow.css')}">
${this.getSettingsOverrideStyles(nonce)}
${this.computeCustomStyleSheetIncludes(uri, nonce)}
${this.getStyles(uri, nonce)}
<base href="${document.uri.toString(true)}">
</head>
<body class="${scrollBeyondLastLine ? 'scrollBeyondLastLine' : ''} ${wordWrap ? 'wordWrap' : ''} ${!!markdownConfig.get('preview.markEditorSelection') ? 'showEditorSelection' : ''}">
${body}
<div class="code-line" data-line="${document.lineCount}"></div>
<script src="${this.getMediaPath('main.js')}" nonce="${nonce}"></script>
${this.getScripts(nonce)}
</body>
</html>`;
});
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册