LaTeXで書いた学位論文もtextlintでチェックしよう（&工学系論文向けのtextlint presetを作っている話）

この記事は[U-Tokyo mech (東京大学機械系) Advent Calendar 2018](https://qiita.com/advent-calendar/2018/utmech)の24日目の記事です。

## はじめに
学位論文執筆中の方はお疲れ様です。そうでない方もお疲れ様です。

皆さん、当然卒論は$\rm{\LaTeX}$で書いてますよね？
え？　Word？　[暴れ回るティラノサウルスを論文に含める必要がある](http://nlab.itmedia.co.jp/nl/articles/1812/09/news021.html)方は大変ですね。

自分は幸運にも暴れ回るティラノサウルスを論文に掲載する必要がないため、早速$\rm{\LaTeX}$で書こうと準備をしています。
その一環として、**記事の文章校正でいつもお世話になっている[textlint](https://textlint.github.io/)**を使おうと調べていたところ、textlintの創造神であるazuさんのこんなツイートが。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">textlintのLaTeXプラグインはないので使う人が書いて欲しい。<br>自分はTeX環境の苦手意識が強いので作れないと思う</p>&mdash; azu (@azu_re) <a href="https://twitter.com/azu_re/status/734257175795662848?ref_src=twsrc%5Etfw">2016年5月22日</a></blockquote>

ぼく「2016年のツイートか」
ぼく「$\rm{\LaTeX}$は使う人多いし流石にここ2年の間に何かできてるやろ（ｸﾞｸﾞﾘｰ」

この後悪戦苦闘することになったので、書き残しておきます。
~~先に書いておくと、2018年現在$\rm{\LaTeX}$をtextlintでチェックする完全な手法はありません。今後状況が改善するといいですね。~~

### (2019/1/23 追記)
@xtaniguchimasaya さんが$\rm{\LaTeX}$に完全対応したプラグイン**[textlint-plugin-latex2e](https://github.com/ta2gch/textlint-plugin-latex2e)**を制作してくださいました。
こちらでほとんどの$\rm{\LaTeX}$ファイルに対応可能ですので、ぜひお試しください。

<a href="https://github.com/ta2gch/textlint-plugin-latex2e"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/ta2gch/textlint-plugin-latex2e.png" width="460px"></a>

## 環境
- [MacTeX 2018](http://www.tug.org/mactex/)
- Node.js `v10.10.0`
- [Pandoc](https://pandoc.org/) `2.2.3.2`

## Markdownで書かないの？
本題へ入る前に、そもそも$\rm{\LaTeX}$で卒論を書くモチベーションについて触れておきます。
あとで紹介する**Pandoc**を使えば、Markdownで書いた原稿を$\rm{\LaTeX}$に自動変換でき、textlintでのチェックももちろん可能です。

このQiitaを「markdown 論文」などで検索するとそういった記事は沢山出ています。

- [Markdownで卒業論文を書きたい](https://qiita.com/mecaota/items/fa3de81ca4cb04ac852e)

この手法を取らない理由は以下の通りです。

- **結局$\rm{\LaTeX}$に触れる必要がある**
    - 講義のレポートなどと違って学位論文は体裁が重要です
    - 図の位置の調整など細かい部分で、結局$\rm{\LaTeX}$側を加工せざるを得ないでしょう
- **Visual Studio Codeの$\rm{\LaTeX}$拡張機能が快適**
    - [LaTeX Workshop](https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop)を導入すれば、保存時に毎回ビルドが走り・横にPDFがプレビューされ・コマンドは自動補完されるという至れり尽くせりな感じになります
    - $\rm{\LaTeX}$の面倒さはかなり軽減されます
- **$\rm{\LaTeX}$も経験しておきたい**
    - 使えた方がかっこよくないですか？

## textlintでLaTeXファイルを扱う方法

textlintが標準で読んでくれるのはplain textとMarkdown形式だけですが、pluginにより様々な記法に対応しています。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">なんで誰もtextlintのlatex parser作ってくれないの</p>&mdash; がすかる★しゅんてぃー (@Gascar_ShunT) <a href="https://twitter.com/Gascar_ShunT/status/821246373135675392?ref_src=twsrc%5Etfw">2017年1月17日</a></blockquote>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">LaTeXのtextlint plugin書こうと思ったけどきっと誰かが作ってくれると信じて作らないことにした</p>&mdash; 抹茶抜き抹茶ミルク (@ayatk_ch) <a href="https://twitter.com/ayatk_ch/status/948575684565200896?ref_src=twsrc%5Etfw">2018年1月3日</a></blockquote>

<details>
<summary><b>以下古い情報です。</b>前述の通り[textlint-plugin-latex2e](https://github.com/ta2gch/textlint-plugin-latex2e)が登場したため、折り畳み内のような手法は不要になりました :tada:</summary><div>

とても恐ろしい集団心理である&mdash;&mdash;
~~誰も…textlint-plugin-latexを作っていないのである！~~

### [textlint-plugin-latex](https://github.com/elzup/textlint-plugin-latex)
嘘です。ありました。

#### 使ってみる
READMEを読んでみると、このプラグインは`.tex`ファイルを以下のように変換します。

- コメント（`%hode`）やコマンド（`\hoge`）を削る
- コマンドの引数（`{fuga}`）を「fuga」に置換する
- plain textとして[抽象構文木（AST）](https://ja.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E6%A7%8B%E6%96%87%E6%9C%A8)に変換する

では使ってみましょう。[LaTeX動作確認テスト・サンプルファイル](http://www.damp.tottori-u.ac.jp/~hoshi/info/doc-info-2009/sample.tex)を対象とします。
ルールは日本語用有名プリセット4種類を中心に導入しました。

<details>
<summary>textlintの設定（`.textlintrc`）</summary><div>

```json:.textlintrc
{
  "plugins": ["latex"],
  "rules": {
    "ja-hiragana-fukushi": true,
    "ja-hiragana-hojodoushi": true,
    "no-mixed-zenkaku-and-hankaku-alphabet": true,
    "preset-ja-spacing": true,
    "preset-ja-technical-writing": true,
    "preset-japanese": {
      "no-doubled-joshi": {
        "min_interval" : 1
      },
      "sentence-length" : {
        max: 100
      }
    },
    "preset-jtf-style": true
  }
}
```
</div></details>


```bash:出力（先頭のみ）
$ npx textlint textlint.tex

/.../textlint.tex
   2:29  error    文末が"。"で終わっていません。                          preset-ja-technical-writing/ja-no-mixed-period
   3:8   error    {の対となる}が見つかりません。{}                          preset-jtf-style/4.3.6.中かっこ{ }
   3:9   error    文末が"。"で終わっていません。                          preset-ja-technical-writing/ja-no-mixed-period
   4:4   error    文末が"。"で終わっていません。                          preset-ja-technical-writing/ja-no-mixed-period
   9:9   error    {の対となる}が見つかりません。{}                          preset-jtf-style/4.3.6.中かっこ{ }
```

#### 課題
大量のエラーが指摘されましたが、何かが変です。

1番目のエラーが出ている2行目は見出しの行で、`「  動作確認テスト・サンプルファイル」`と変換されているはずです。
エラーを出した[textlint-rule-ja-no-mixed-period](https://github.com/textlint-ja/textlint-rule-ja-no-mixed-period)の説明には、`「」`で囲まれた文はセリフ扱いでエラーにしないと書いてあります。なぜ$\rm{\LaTeX}$だとうまく動かないのでしょう。

この他にも**複数のルールがtextlint-plugin-latexを通すと正常に動きません**。
あちこちソースコードを見回って判明した原因は、これらのルールが[`context.getSource()`というAPIで変換前の原文を参照している](https://github.com/textlint/textlint/blob/master/docs/rule.md#rulecontext-api)ことでした。
`context.getSource()`で生の`.tex`が渡された結果、誤検出が多発してしまったわけですね。

また、各ルールはtextlint-plugin-latexによる変換後のデータを元に`context.getSource()`での取得範囲を指定してしまいます。
従ってずれた範囲が原文として得られてしまい、**検出漏れや誤検出、エラー箇所のずれ**が起きてしまいます。

こういった問題を解決するには、**$\rm{\LaTeX}$を直接ASTに変換する**しかありません。それを行うプラグインは、望まれながらも未だ世に出ていないというわけです……。

### PandocでMarkdownに変換する

ないものは仕方がないので、別の策を考えます。[Pandoc](https://pandoc.org/)は、Markdown・HTML・LaTeXをはじめ多数の文書形式を自動変換してくれるツールです。これでMarkdownに変換してからtextlintにかけている方は多いようです。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">latex文書の自動文書校正はpandocでMarkdownに変換してからtextlintするのが良いのではないかと思えてきた</p>&mdash; 酒田　シンジ (@ysakasin) <a href="https://twitter.com/ysakasin/status/880068821901553664?ref_src=twsrc%5Etfw">2017年6月28日</a></blockquote>
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">TeXファイルをtextlintにかけたいのにTeXには対応してないっぽいのでどうしようかと思ってたけど、そうか、PandocでMarkdownファイルに変換してそっちをlintすればいいのか。検査結果と元のTeXを突き合わせて直すのが少々手間な気がするけどやらないよりは断然まし。</p>&mdash; нёло (@nyoro_712) <a href="https://twitter.com/nyoro_712/status/1060230578962743296?ref_src=twsrc%5Etfw">2018年11月7日</a></blockquote>

#### 使ってみる
そのまま`pandoc textlint.tex -o textlint.md`してもよいのですが、Pandoc標準のMarkdownは`\ref`による参照などを扱えるよう拡張されています。
これはtextlintで日本語を校正する分には邪魔なため、`-t markdown_strict`を指定して拡張なしのMarkdownに変換しましょう。

```bash
$ pandoc textlint.tex -t markdown_strict -o md/textlint.md
$ npx textlint md/textlint.md

/.../textlint.md
   1:14  error    Disallow to use "！"                          preset-ja-technical-writing/no-exclamation-question-mark
   4:9   error    Disallow to use "！"                          preset-ja-technical-writing/no-exclamation-question-mark
   7:14  error    Disallow to use "！"                          preset-ja-technical-writing/no-exclamation-question-mark
  15:1   error    Line 15 sentence length(223) exceeds the maximum sentence length of 100.
Over 123 characters                                                                                    preset-ja-technical-writing/sentence-length
  18:22  error    Disallow to use "？"                          preset-ja-technical-writing/no-exclamation-question-mark
  19:20  ✓ error  "することができます"は冗長な表現です。"することが"を省き簡潔な表現にすると文章が明瞭になります。参考: http://qiita.com/takahi-i/items/a93dc2ff42af6b93f6e0  preset-ja-technical-writing/ja-no-redundant-expression

✖ 6 problems (6 errors, 0 warnings)
✓ 1 fixable problem.
Try to run: $ textlint --fix [file]
```

良さそうです。「Line 15が長すぎる」というのは、元の`.tex`ファイルにあった数式を1つの文章と認識しているためで、これだけは誤検出です。

#### package.jsonにスクリプトを定義する
毎回手で操作をするのは大変なので、**npm-scriptsとして定義**しておきましょう。
npm-scriptsは、textlintが動いているNode.jsのパッケージ管理システムであるnpm経由でコマンドを実行できる仕組みです。

```json:package.json（一部）
  "scripts": {
    "test": "npm run pandoc && npm run textlint-md",
    "pandoc": "rm -r md/* && find . -type f -name 'main.tex' -prune -o -type f -name '*.tex' -print | xargs -I% pandoc % -t markdown_strict -o md/%.md",
    "textlint-md": " textlint -f pretty-error md/",
    "textlint": "find . -type f -name '*.tex' -print  | xargs textlint -f pretty-error"
  },
```

全体のサンプルは[Gistに公開しています](https://gist.github.com/kn1cht/9c8b2e960a4943c640245afd55693424)。

`npm test`を実行すると、**Pandocによる自動変換とtextlintによるチェックを連続実行**します。

Pandocにかける際`main.tex`を除いたのは、これが論文の他のファイル全てをincludeしているためです。Pandocはinclude先まで辿って変換し、`main.tex.md`に論文の全内容が入ってしまいます。
各章の`.tex`ファイルがチェックされれば十分なので、`main.tex`以外を変換して`md/`以下に出力します（gitを使っている場合は`md/`を除外しておきましょう）。

あとは`md/`全体をtextlintして完了です。

#### 課題
この方法を使うには、まずは**Pandocを導入している**必要があります。まあPandocは何かと便利なので、導入しておいて損ではありません。
[Pandocの比較的簡単なインストール方法](https://qiita.com/sky_y/items/3c5c46ebd319490907e8)などを参考にインストールしてください。

第2に、Pandocが間に入る分**時間がかかります**。書き進めて分量が増えるにつれ待ち時間が増える可能性があります。

また、別の形式に変換しているので、**元の`.tex`ファイルと行番号が合いません**。`textlint --fix`による自動修正も上手くいきません。
エラーと見比べながら修正箇所を探しましょう。

</div></details>

## 工学系論文用のプリセットを作成する
上のいずれかの方法でtextlintによる校正はできるようになりました。
ただ、これで論文を書くにはそれに適したルールが必要です。

textlintのルールの多くは**句読点として「`、`」「`。`」を使う**ことを求めています。しかし、[日本機械学会指定テンプレート](https://www.jsme.or.jp/publish/transact/Japanese-Template-mihon.pdf)をはじめ工学系の多くの論文では**「`，`」（カンマ）と「`．`」（ピリオド）**が指定されます。

textlintの句読点を扱うルールは`textlint-rule-preset-JTF-style`に複数あるものの、これらは「`、`」「`。`」しか認めてくれません。
また、[`textlint-rule-ja-no-mixed-period`](https://github.com/textlint-ja/textlint-rule-ja-no-mixed-period)ではオプションで句点を変えられますが、読点はチェックしません。

### textlint-rule-preset-ja-engineering-paper
以上の事情から、工学系論文専用のルールプリセットを作成し、npmに公開しました。

- [kn1cht/textlint-rule-preset-ja-engineering-paper](https://github.com/kn1cht/textlint-rule-preset-ja-engineering-paper)

今後他のルールも盛り込みたいため、ルールでなくプリセットにしました（今は句読点に関するルール1つだけですが……）。

### unify-kuten-and-touten
**句読点を「`．`」（全角ピリオド）と「`，`」（全角カンマ）に統一します**。また、オプションで指定する句読点を変えられるようになっています。

仕組みは単純で、句読点としてありうる記号のうち指定以外のものを検出してエラーを出すだけです。「句読点としてありうる記号」もオプションで追加できます。

### 今後追加したいルール
理系の論文では「数値+単位」の表記も指定されます。

- 数値と単位の間には半角スペースを開ける（例：`1.2 mm`）
- SI単位で表記する（正：`2 m/s`・誤：`2 m/sec`）

などです。単位の表記をチェックするルールもtextlintにはないため、今後追加するつもりです。

## おわりに
軽い気持ちで学位論文にもtextlintを使おうとしたら、かなり大変でした。
`package.json`・`.textlintrc`のサンプルは[Gistにある](https://gist.github.com/kn1cht/9c8b2e960a4943c640245afd55693424)ので、textlintに慣れている方は試してみてください。