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

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

Qiitaにアップするために、Visual Studio Code(VS Code)を使用しているが、Markdownの改行に関する事項が微妙に仕様と違う。
デフォルトで入っている、Markdownの拡張機能は、空白２つか、「\\」で、改行となっている。
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](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」を開いて、修正してください。

```ts
    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](https://github.com/Microsoft/vscode)

確認対象は、extension\markdown\src\extension.tsです。
activate関数の必要な個所をピックアップしてみます。

```ts
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表示の関連個所に分けて、ピックアップします。

#### コンストラクタ部分

では、はじめにコンストラクタ部分です。

```ts
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表示部分をピックアップして、説明します。

```ts
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の定義ファイルは、以下に記載します。

```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<Uri>
- 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](https://github.com/Microsoft/vscode-extension-samples)