SmartでLightweightなMarkdownエディタを目指してテキストエディタのマクロを書いてみた


## WindowsのMarkdownエディタひどすぎ問題
だいたい、「Windows Markdownエディタ」でぐぐるとAtom.io使えとかいう話になっている。

AtomもVisual Studio Codeも実際ぜんぜん悪くないのだが、とにかく重い。さっと書きたいときには向かない。
Sublime Textは軽いけど、日本語入力がスゲェアホなのでちょっと日本語の文章は書けない。

VimとかEmacsを文章書きで普段遣いしてる人は流石に変態だと思うし。

1つのファイルをさらっと書く用の、Macでいうと[MacDown](https://macdown.uranusjr.com)みたいなアプリというと、とたんに困る。

いや数だけはいっぱいあるのだが、Chromiumで重かったり、デザインが死んでたり、Windows Store専用でWindows7で使えなかったりで数え切れないほど失望を味わった。

色々試した結果、[MarkdownPad 2](http://markdownpad.com/news/2013/introducing-markdownpad-2/)が一番マシだと思う。思うが納得はいってない。行入れ替えとかできないし。あとそこそこ重い。

## テキストエディタは拡張するもの
いわゆるプログラマ向けのNotepad Alternativeなエディタだと、シンタックスハイライトまではだいたいできる。

問題は編集、というかほぼ[オートバレット](https://wbtmiu.herokuapp.com/2014/04/30/S5QYHv6KiB/)（リスト行で改行した時リスト文字`-`とかを補完してくれるやつ）。個人的には、それ以外は普通に手入力したらええやんと思っている。

Markdownは所詮全部文字なので、最悪AutoHotKeyとかで入力補完できんでもない。ただ、通常のパラグラフなら普通に改行して、リスト行なら補完して、とかいう話になると、入力中のテキストの解析が必要になってくる。
Sublime Textだとキーバインドの設定でできるようなやつ。（参考：[SublimeText2のキーバインドを、ちょっと突っ込んで解説 \- Qiita](https://qiita.com/shibainurou/items/dc18f2dfc91e36adb208)）

AutoHotKeyでやろうとすると、いったん`Ctrl+C`送ってクリップボードに格納して……という話になってしまいやってらんないので、エディタ自体がドキュメントの内容を取らせてくれないと困る。

## そうか秀丸エディタか
んで、そゆスクリプタブルなエディタを探してみたら秀丸エディタでできるっぽい。

というわけで、何年ぶりかわからんが秀丸エディタを触ってみることにした。

秀丸エディタでも、調べた限りでは「現在行の内容」とかをダイレクトに取得することはできない。が、マクロの関数で範囲選択すると選択内容は取れるので、それを正規表現マッチしたら判定はできそう。

ということでやってみたのがこちら。

<!-- - [秀まるおのホームページ\(サイトー企画\)－Markdownファイル編集中、改行した時に現在行の開始文字を自動挿入する](https://hide.maruo.co.jp/lib/macro/md_list_linebreak.html) -->

### ソースコード
引用ブロックも補完するよ！

```MD_list_linebreak.MAC
/* Markdownファイル編集中、改行した時に現在行の開始文字を自動挿入する

Markdownファイルであるかはファイル名から判断しています。よって未保存文書等では使えません。
自動挿入する開始文字は以下のとおりです。

順序なしリスト : '-' '*' '+'
順序ありリスト : '1.' '2.' ...
引用ブロック   : '>'

行頭にこれらの文字があった場合、改行後に自動挿入します。
それ以外はすべて通常の改行と同じですので、キー割り当て拡張しEnterに割り当ててのご利用をおすすめします。

[2018-07-26] 初版
[2018-07-28] 順序ありリストに対応
             コードを整理
*/

// 拡張子が.md/.markdown/.txtの場合
if (filetype == ".md" || filetype == ".markdown" || filetype == ".txt") {
	loaddll "HMJRE.DLL";
	#n = dllfunc("SetUnicodeIndexAutoConvert", 1);

	// 文字列選択状態を取得
	if (selecting) {
		#Selected = 1;
		#c_seltopx = seltopx;
		#c_seltopy = seltopy;
		#c_selendx = selendx;
		#c_selendy = selendy;
	}
	else {
		#Selected = 0;
		#c_x = x;
		#c_y = y;
	}

	// 現在行の内容を取得
	call SELECTLINES;
	$CurrentLine = gettext2(seltopcolumn, seltoplineno, selendcolumn, selendlineno, 1);
	escape;
	// 選択状態を復元
	if (#Selected == 1) {
		moveto #c_seltopx, #c_seltopy;
		beginsel;
		moveto #c_selendx, #c_selendy;
		endsel;
	}
	else {
		moveto #c_x, #c_y;
	}

	// Markdown構文を検出
	if (dllfunc( "FindRegular", "^\\s*[-\\*\\+>|\\d+\\.] ?", $CurrentLine, 0 ) != -1) {
		// 現在行の内容からMarkdown構文に一致する部分を取得
		$syntax = dllfuncstr( "ReplaceRegular", "^\\s*([-\\*\\+>] ?|\\d+\\. ?).*\\n?", $CurrentLine, 0, "\\1" );
		// 順序ありリストなら+1する
		$num = dllfuncstr( "ReplaceRegular", "^(\\d+).*", $syntax, 0, "\\1" );
		if ( $num != "" ) {
			$num = str(val($num) + 1);
			$syntax = dllfuncstr( "ReplaceRegular", "^\\d+", $syntax, 0, $num );
		}
	}

	insert "\n" + $syntax;
	endmacro;

	// 指定された行を選択状態にする。
	SELECTLINES:
		moveto2 0, lineno;
		beginlinesel;
			moveto2 0, (lineno + 1);
		endsel;
	return;
}
// 拡張子がそれ以外の場合
else {
	insertreturn;
}
```

<!-- なぜか順序ありリストの`1.`とかがマッチできなくて発狂しかけたので、とりあえずリリース。 -->

Markdown Pad2よりは快適に使えている。

が、デザインとか微妙にまったりした動作がまだちょっと気に食わないので、[Mery](https://www.haijin-boys.com/wiki/%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9A%E3%83%BC%E3%82%B8)あたりでできないか調べてみようと思う。

### そうかMeryもか
というわけで[Mery](https://www.haijin-boys.com/wiki/%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9A%E3%83%BC%E3%82%B8)版追加。

- [Markdown文書で改行時にリスト、引用ブロック開始記号を自動入力する \- MeryWiki](https://www.haijin-boys.com/wiki/Markdown%E6%96%87%E6%9B%B8%E3%81%A7%E6%94%B9%E8%A1%8C%E6%99%82%E3%81%AB%E3%83%AA%E3%82%B9%E3%83%88%E3%80%81%E5%BC%95%E7%94%A8%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E9%96%8B%E5%A7%8B%E8%A8%98%E5%8F%B7%E3%82%92%E8%87%AA%E5%8B%95%E5%85%A5%E5%8A%9B%E3%81%99%E3%82%8B)

JavaScript知らないから最初戸惑ったけど、こっちのほうが遙かに簡単だった。なにしろ現在行の内容が`GetLine`一発で取れる。

あと、編集モードという情報があるので、拡張子がtxtだろうがftだろうが未保存ドキュメントだろうが対応できるのが素晴らしい。

### ソースコード

```js
#title="Markdown文書で改行時にリスト、引用ブロック開始記号を自動入力"

// by catfist
//
// 編集モードが'Markdown'の場合のみ実行
// 現在行が以下の構文を含む場合、改行して行頭に開始記号を追加する
// （順序ありリストの場合は数値を1加算する）
//
// 順序なしリスト: '-','*','+'
// 順序ありリスト: '1.','2.'...
// 引用ブロック  : '>'
//
// 開始記号のみの場合は記号を削除して改行する
//
// Enterに割り当てて使うことを想定
//
// [2018-07-27] 1.0
// [2018-07-28] 1.1
// bug fix
// 開始記号しかない行では開始記号削除＋通常の改行を行うようにした
// 自動インデント機能を利用するため、改行記号の挿入をNewLineメソッドに変更。ただしアンドゥが2回必要。

if (Document.Mode == "Markdown") { // 編集モードがMarkdownの場合
    // 現在行の内容を取得
    var s = Document.GetLine(Document.Selection.GetActivePointY(mePosLogical), 0);
    // リストまたはブロックコメント開始記号に一致する部分を取得
    if (s.match(/^\s*([-\*\+>] ?|\d+\. ?)[^\s]+$/))
    {
        var s_syntax = s.replace(/^\s*([-\*\+>] ?|\d+\. ?).*$/,"$1");
        // 順序ありリストの場合は数値を+1する
        var s_num = s_syntax.replace(/^(\d+).*/,"$1");
        if (s_num !== "") {
            s_num = Number(s_num);
            s_num = s_num + 1;
            s_syntax = s_syntax.replace(/^\d+/,s_num);
        }
    }
    else if (s.match(/^\s*([-\*\+>]|\d+\.)\s*$/)) { // インデントと開始記号のみは場合は削除
        Document.Selection.DeleteLeft(s.length);
        s_syntax = "";
    }
    else { // 指定の構文がない場合は改行のみ
        s_syntax = "";
    }
}
else { // 編集モードがMarkdown以外の場合は改行のみ
    s_syntax = "";
}
// 改行と開始記号を挿入
// Document.Selection.Text = "\n" + s_syntax;
Document.Selection.NewLine()
Document.Selection.Text = s_syntax;
// alert(s_syntax); //[dev]
```

あとで見つけたけど[スマートインデント\(C 構文\) \- MeryWiki](https://www.haijin-boys.com/wiki/%E3%82%B9%E3%83%9E%E3%83%BC%E3%83%88%E3%82%A4%E3%83%B3%E3%83%87%E3%83%B3%E3%83%88(C_%E6%A7%8B%E6%96%87))の再発明っぽい