VS Code 標準Markdown Extensionsで、改行を有効にする
Qiitaにアップするために、Visual Studio Code(VS Code)を使用しているが、Markdownの改行に関する事項が微妙に仕様と違う。
デフォルトで入っている、Markdownの拡張機能は、空白2つか、「\」で、改行となっている。
githubや、Qiitaでは、改行そのままで、次の行に表示されるようになっている。
違う部分は、他にもあるが、いったん、それを修正できれば、アップのほうは、やりやすくなりそうなので、
標準の拡張機能を修正してみた。
環境
- Windows 10 Pro
- Visual Studio Code 1.4
- インストール先:C:\Program Files (x86)\Microsoft VS Code
- Visual Studio Code 1.9 2017/2/6 追記
VS Code Markdown拡張機能
このドキュメントは、修正方法を先に記載して、その後、簡単に説明をしていきます。
Markdownの拡張機能は、「markdown-it」をライブラリとして使用して、HTMLを構築している。
https://markdown-it.github.io/markdown-it/#MarkdownIt.new
上記マニュアルを確認すると、option指定の説明で、breaksを入れればいいようです。
breaks - false. Set true to convert \n in paragraphs into <br>.
結論を言うと、指定する箇所に、改行で、HTMLでのbreakができるようにする設定の「breaks: true,」を入れます。
拡張機能やバージョンアップの更新あると、変更が必要だけど。
修正方法
VS CodeのMarkdownの拡張機能は、以下にインストールされています。
「VS Codeのインストール場所」\resources\app\extensions\markdown
その中にある、(1.4の場合は、)out/extension.jsが修正対象です。
2017/2/6 時点の最新1.9は、最後に追記しました。
C:\Program Files (x86)\Microsoft VS Code\resources\app\extensions\markdown\out/extension.js
では、out/extension.jsの修正箇所が以下になります。
コメントが書いているところが修正箇所です。
修正後に、再起動すれば、改行コードが、<br>に代わります。
私の場合は、C:\Program Files (x86)の配下にインストールしているので、修正には、管理者権限が必要です。
VS Codeを一時的に、管理者権限で起動して、メニューから「extension.js」を開いて、修正してください。
MDDocumentContentProvider.prototype.createRenderer = function () {
var hljs = require('highlight.js');
var mdnh = require('markdown-it-named-headers');
var md = require('markdown-it')({
html: true,
breaks: 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;
};
説明
すでに、インストールされている、Markdownの拡張機能は、JavaScriptに変換されているので、コードを見るために、githubから確認していきます。
https://github.com/Microsoft/vscode
確認対象は、extension\markdown\src\extension.tsです。
activate関数の必要な個所をピックアップしてみます。
export function activate(context: ExtensionContext) {
・・・
let provider = new MDDocumentContentProvider(context);
let registration = vscode.workspace.registerTextDocumentContentProvider('markdown', provider);
・・・
vscode.workspace.onDidChangeTextDocument(event => {
if (isMarkdownFile(event.document)) {
const uri = getMarkdownUri(event.document.uri);
provider.update(uri);
}
});
・・・
}
「onDidChangeTextDocument」を見てわかるように、「provider.update(uri)」で、htmlへの変換を行っています。
markdownの本体は、「MDDocumentContentProvider」クラスとなります。
クラスは、extension.tsの下のほうに、定義されています。
コードが長いので、コンストラクタ部分とhtml表示の関連個所に分けて、ピックアップします。
コンストラクタ部分
では、はじめにコンストラクタ部分です。
class MDDocumentContentProvider implements TextDocumentContentProvider {
private _context: ExtensionContext;
private _onDidChange = new EventEmitter<Uri>();
private _waiting : boolean;
private _renderer : IRenderer;
constructor(context: 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;
}
}
「constructor」で、大事なところは、createRenderer関数を呼び出して、「markdown-it」が使用できるように読み込んでいるところになります。
前述した、JavaScriptの修正箇所になっています。
markdown-itのオプション設定や、プラグインの追加などを行っています。
実際のHTML作成は、別のところでやっています。
HTML表示部分
では、HTML表示部分をピックアップして、説明します。
class MDDocumentContentProvider implements TextDocumentContentProvider {
private _context: ExtensionContext;
private _onDidChange = new EventEmitter<Uri>();
private _waiting : boolean;
private _renderer : IRenderer;
public provideTextDocumentContent(uri: Uri): Thenable<string> {
return vscode.workspace.openTextDocument(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(document.getText());
const tail = [
'</body>',
'</html>'
].join('\n');
eturn head + body + tail;
});
}
get onDidChange(): Event<Uri> {
return this._onDidChange.event;
}
public update(uri: Uri) {
if (!this._waiting) {
this._waiting = true;
setTimeout(() => {
this._waiting = false;
this._onDidChange.fire(uri);
}, 300);
}
}
}
まず、はじめに見るところは、MDDocumentContentProviderクラスが、インターフェースのTextDocumentContentProviderをimplementsしているところです。
TextDocumentContentProviderのvscode.d.tsの定義ファイルは、以下に記載します。
/**
* A text document content provider allows to add readonly documents
* to the editor, such as source from a dll or generated html from md.
*
* Content providers are [registered](#workbench.registerTextDocumentContentProvider)
* for a [uri-scheme](#Uri.scheme). When a uri with that scheme is to
* be [loaded](#workbench.openTextDocument) the content provider is
* asked.
*/
export interface TextDocumentContentProvider {
/**
* An event to signal a resource has changed.
*/
onDidChange?: Event<Uri>;
/**
* Provide textual content for a given uri.
*
* The editor will use the returned string-content to create a readonly
* [document](TextDocument). Resources allocated should be released when
* the corresponding document has been [closed](#workbench.onDidCloseTextDocument).
*
* @param uri An uri which scheme matches the scheme this provider was [registered](#workspace.registerTextDocumentContentProvider) for.
* @param token A cancellation token.
* @return A string or a thenable that resolves to such.
*/
provideTextDocumentContent(uri: Uri, token: CancellationToken): string | Thenable<string>;
}
TextDocumentContentProviderは、エディタ領域に読み取り専用のドキュメントとして、追加をするAPIとなっています。
provideTextDocumentContent関数を使用して、必要な情報を作成して、戻すことにより、markdownの場合は、HTMLが出力されます。
もう少し解析したいのですが、まだ、わかっていないところが多いので、今回は、こちらで、終わりにします。
- EventEmitter
- vscode.workspace.openTextDocument
- TelemetryReporter
1.9の場合の修正箇所 2017/2/6 追記
out/markdownEngine.jsが修正対象です。
Object.defineProperty(MarkdownEngine.prototype, "engine", {
get: function () {
var _this = this;
if (!this.md) {
var hljs_1 = require('highlight.js');
var mdnh = require('markdown-it-named-headers');
this.md = require('markdown-it')({
html: true,
breaks: true,
highlight: function (str, lang) {
if (lang && hljs_1.getLanguage(lang)) {
try {
return "<pre class=\"hljs\"><code><div>" + hljs_1.highlight(lang, str, true).value + "</div></code></pre>";
}
catch (error) { }
}
return "<pre class=\"hljs\"><code><div>" + _this.engine.utils.escapeHtml(str) + "</div></code></pre>";
}
}).use(mdnh, {});
for (var _i = 0, _a = ['paragraph_open', 'heading_open', 'image', 'code_block', 'blockquote_open', 'list_item_open']; _i < _a.length; _i++) {
var renderName = _a[_i];
this.addLineNumberRenderer(this.md, renderName);
}
this.addLinkNormalizer(this.md);
this.addLinkValidator(this.md);
}
return this.md;
},
enumerable: true,
configurable: true
});
備考
TextDocumentContentProviderのサンプルが、github上のMicrosoftアカウントに存在しています。
https://github.com/Microsoft/vscode-extension-samples