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

Add yaml front matter support for Markdown (#15218)

* Add language support for yaml front matter to markdown grammar

* Add option to strip yaml front matter from preview

* Use enum for setting instead of boolean

* Better names

* Fix merge error
上级 c9a70707
......@@ -120,6 +120,11 @@
"type": ["array"],
"default": [],
"description": "A list of URLs or local paths to CSS style sheets to use from the markdown preview. Relative paths are interpreted relative to the folder open in the explorer. If there is no open folder, they are interpreted relative to the location of the markdown file. All '\\' need to be written as '\\\\'."
},
"markdown.previewFrontMatter": {
"type": ["string"],
"default": "hide",
"description": "Sets how YAML front matter should be rendered in the markdown preview. 'hide' removes the front matter. Otherwise, the front matter is treated as markdown content"
}
}
}
......
......@@ -224,7 +224,6 @@ class MDDocumentContentProvider implements vscode.TextDocumentContentProvider {
}
public provideTextDocumentContent(uri: vscode.Uri): Thenable<string> {
return vscode.workspace.openTextDocument(vscode.Uri.parse(uri.query)).then(document => {
const head = [].concat(
'<!DOCTYPE html>',
......@@ -238,8 +237,7 @@ class MDDocumentContentProvider implements vscode.TextDocumentContentProvider {
'</head>',
'<body>'
).join('\n');
const body = this._renderer.render(document.getText());
const body = this._renderer.render(this.getDocumentContentForPreview(document));
const tail = [
'</body>',
......@@ -263,4 +261,13 @@ class MDDocumentContentProvider implements vscode.TextDocumentContentProvider {
}, 300);
}
}
private getDocumentContentForPreview(document: vscode.TextDocument): string {
const content = document.getText();
const previewFrontMatter = vscode.workspace.getConfiguration('markdown')['previewFrontMatter'];
if (previewFrontMatter === 'hide') {
return content.replace(/^-{3}[ \t]*(\r\n|\n)(.|\r\n|\n)*?(\r\n|\n)-{3}[ \t]*(\r\n|\n)/, '');
}
return content;
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import * as path from 'path';
import TelemetryReporter from 'vscode-extension-telemetry';
interface IPackageInfo {
name: string;
version: string;
aiKey: string;
}
var telemetryReporter: TelemetryReporter;
export function activate(context: vscode.ExtensionContext) {
let packageInfo = getPackageInfo(context);
telemetryReporter = packageInfo && new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
let provider = new MDDocumentContentProvider(context);
let registration = vscode.workspace.registerTextDocumentContentProvider('markdown', provider);
let d1 = vscode.commands.registerCommand('markdown.showPreview', showPreview);
let d2 = vscode.commands.registerCommand('markdown.showPreviewToSide', uri => showPreview(uri, true));
let d3 = vscode.commands.registerCommand('markdown.showSource', showSource);
context.subscriptions.push(d1, d2, d3, registration);
vscode.workspace.onDidSaveTextDocument(document => {
if (isMarkdownFile(document)) {
const uri = getMarkdownUri(document.uri);
provider.update(uri);
}
});
vscode.workspace.onDidChangeTextDocument(event => {
if (isMarkdownFile(event.document)) {
const uri = getMarkdownUri(event.document.uri);
provider.update(uri);
}
});
vscode.workspace.onDidChangeConfiguration(() => {
vscode.workspace.textDocuments.forEach(document => {
if (document.uri.scheme === 'markdown') {
// update all generated md documents
provider.update(document.uri);
}
});
});
}
function isMarkdownFile(document: vscode.TextDocument) {
return document.languageId === 'markdown'
&& document.uri.scheme !== 'markdown'; // prevent processing of own documents
}
function getMarkdownUri(uri: vscode.Uri) {
return uri.with({ scheme: 'markdown', path: uri.path + '.rendered', query: uri.toString() });
}
function showPreview(uri?: vscode.Uri, sideBySide: boolean = false) {
let resource = uri;
if (!(resource instanceof vscode.Uri)) {
if (vscode.window.activeTextEditor) {
// we are relaxed and don't check for markdown files
resource = vscode.window.activeTextEditor.document.uri;
}
}
if (!(resource instanceof vscode.Uri)) {
if (!vscode.window.activeTextEditor) {
// this is most likely toggling the preview
return vscode.commands.executeCommand('markdown.showSource');
}
// nothing found that could be shown or toggled
return;
}
let thenable = vscode.commands.executeCommand('vscode.previewHtml',
getMarkdownUri(resource),
getViewColumn(sideBySide),
`Preview '${path.basename(resource.fsPath)}'`);
telemetryReporter.sendTelemetryEvent('openPreview', {
where: sideBySide ? 'sideBySide' : 'inPlace',
how: (uri instanceof vscode.Uri) ? 'action' : 'pallete'
});
return thenable;
}
function getViewColumn(sideBySide): vscode.ViewColumn {
const active = vscode.window.activeTextEditor;
if (!active) {
return vscode.ViewColumn.One;
}
if (!sideBySide) {
return active.viewColumn;
}
switch (active.viewColumn) {
case vscode.ViewColumn.One:
return vscode.ViewColumn.Two;
case vscode.ViewColumn.Two:
return vscode.ViewColumn.Three;
}
return active.viewColumn;
}
function showSource(mdUri: vscode.Uri) {
if (!mdUri) {
return vscode.commands.executeCommand('workbench.action.navigateBack');
}
const docUri = vscode.Uri.parse(mdUri.query);
for (let editor of vscode.window.visibleTextEditors) {
if (editor.document.uri.toString() === docUri.toString()) {
return vscode.window.showTextDocument(editor.document, editor.viewColumn);
}
}
return vscode.workspace.openTextDocument(docUri).then(doc => {
return vscode.window.showTextDocument(doc);
});
}
function getPackageInfo(context: vscode.ExtensionContext): IPackageInfo {
let extensionPackage = require(context.asAbsolutePath('./package.json'));
if (extensionPackage) {
return {
name: extensionPackage.name,
version: extensionPackage.version,
aiKey: extensionPackage.aiKey
};
}
return null;
}
interface IRenderer {
render(text: string): string;
}
class MDDocumentContentProvider implements vscode.TextDocumentContentProvider {
private _context: vscode.ExtensionContext;
private _onDidChange = new vscode.EventEmitter<vscode.Uri>();
private _waiting: boolean;
private _renderer: IRenderer;
constructor(context: vscode.ExtensionContext) {
this._context = context;
this._waiting = false;
this._renderer = this.createRenderer();
}
private createRenderer(): IRenderer {
const hljs = require('highlight.js');
const mdnh = require('markdown-it-named-headers');
const md = require('markdown-it')({
html: true,
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return `<pre class="hljs"><code><div>${hljs.highlight(lang, str, true).value}</div></code></pre>`;
} catch (error) { }
}
return `<pre class="hljs"><code><div>${md.utils.escapeHtml(str)}</div></code></pre>`;
}
}).use(mdnh, {});
return md;
}
private getMediaPath(mediaFile): string {
return this._context.asAbsolutePath(path.join('media', mediaFile));
}
private isAbsolute(p): boolean {
return path.normalize(p + '/') === path.normalize(path.resolve(p) + '/');
}
private fixHref(resource: vscode.Uri, href: string): string {
if (href) {
// Use href if it is already an URL
if (vscode.Uri.parse(href).scheme) {
return href;
}
// Use href as file URI if it is absolute
if (this.isAbsolute(href)) {
return vscode.Uri.file(href).toString();
}
// use a workspace relative path if there is a workspace
let rootPath = vscode.workspace.rootPath;
if (rootPath) {
return vscode.Uri.file(path.join(rootPath, href)).toString();
}
// otherwise look relative to the markdown file
return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href)).toString();
}
return href;
}
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) => {
return `<link rel="stylesheet" href="${this.fixHref(uri, style)}" type="text/css" media="screen">`;
});
}
return [];
}
public provideTextDocumentContent(uri: Uri): Thenable<string> {
return vscode.workspace.openTextDocument(vscode.Uri.parse(uri.query)).then(document => {
const head = [].concat(
'<!DOCTYPE html>',
'<html>',
'<head>',
'<meta http-equiv="Content-type" content="text/html;charset=UTF-8">',
`<link rel="stylesheet" type="text/css" href="${this.getMediaPath('markdown.css')}" >`,
`<link rel="stylesheet" type="text/css" href="${this.getMediaPath('tomorrow.css')}" >`,
this.computeCustomStyleSheetIncludes(uri),
`<base href="${document.uri.toString(true)}">`,
'</head>',
'<body>'
).join('\n');
const body = this._renderer.render(this.getDocumentContentForPreview(document));
const tail = [
'</body>',
'</html>'
].join('\n');
return head + body + tail;
});
}
get onDidChange(): vscode.Event<vscode.Uri> {
return this._onDidChange.event;
}
public update(uri: vscode.Uri) {
if (!this._waiting) {
this._waiting = true;
setTimeout(() => {
this._waiting = false;
this._onDidChange.fire(uri);
}, 300);
}
}
private getDocumentContentForPreview(document: vscode.TextDocument): string {
const content = document.getText();
const previewFrontMatter = vscode.workspace.getConfiguration('markdown')['previewFrontMatter'];
if (previewFrontMatter === 'hide') {
return content.replace(/^-{3}[ \t]*(\r\n|\n)(.|\r\n|\n)*?(\r\n|\n)-{3}[ \t]*(\r\n|\n)/, '');
}
return content;
}
}
\ No newline at end of file
......@@ -15,6 +15,10 @@
<string>Markdown</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#frontMatter</string>
</dict>
<dict>
<key>include</key>
<string>#block</string>
......@@ -1880,6 +1884,20 @@
</dict>
</dict>
</dict>
<key>frontMatter</key>
<dict>
<key>begin</key>
<string>\A-{3}\s*$</string>
<key>while</key>
<string>^(?!-{3}\s*$)</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>source.yaml</string>
</dict>
</array>
</dict>
</dict>
<key>scopeName</key>
<string>text.html.markdown</string>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册