LoginSignup
23
22

More than 5 years have passed since last update.

VS Code 標準Markdown Extensionsで、改行を有効にする

Last updated at Posted at 2016-08-19

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

23
22
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
22