macOS で textlint を右クリックのメニューから呼べるようにする

[textlint](https://github.com/textlint/textlint) 、皆さんつかってますか？
設定に応じて文章の問題点を指摘してくれて、校正的な作業が手軽にできてめっちゃ便利ですね。

<img width="739" alt="textlint の使用例" src="https://qiita-image-store.s3.amazonaws.com/0/25886/27d70bca-a46f-60b2-af34-622e3b4bfdfb.png">

これを JavaScript やシェル経由だけでなく、あちこちのアプリケーションで手軽に呼んでチェックできたら、さらに便利だと思いませんか？

この記事では、 macOS の Automator を使って、右クリックで出るメニュー (コンテクストメニュー) から textlint を呼べるようにする方法を解説します。

なお、私は macOS を英語ロケールで使用していますので、日本語ロケールの場合は若干操作を読み替えていただく必要があります。

# インストール方法

## textlint 自体の導入

基本的にはすでに npm や textlint を使っている人向けの記事と考えていますので、深くは解説しません。コマンドだけ載せておきます。

```console
$ npm install -g textlint
$ textlint --init
```

サンプルのために「だ・である」調と「です・ます」調の統一のルールをインストールしています。ここは各々の普段使っている設定で良いでしょう。

```console
$ npm install -g textlint-rule-no-mix-dearu-desumasu
$ cat <<EOF > ~/.textlintrc
{
  "rules": {
    "no-mix-dearu-desumasu": true
  }
}
EOF
```

## Automator でサービスを作る

コンテクストメニューに項目を追加するには、 Automator を使うと手っ取り早いのです。

## 1. Automator アプリケーションの起動

Automator というアプリケーションを `/Applications` から探して起動してください。
あるいは Spotlight から `au` などと入力すれば候補が表示されると思います。

![Choose Application Automator](https://qiita-image-store.s3.amazonaws.com/0/25886/4681b31f-73be-c39a-651e-e953c0d70a88.png)


## 2. New → Service を選択

<img width="628" alt="Choose File ... New" src="https://qiita-image-store.s3.amazonaws.com/0/25886/27d13cec-fd90-19ca-9785-7eb4722622dd.png">

<img width="1112" alt="Choose Service" src="https://qiita-image-store.s3.amazonaws.com/0/25886/1cdf738b-cd87-606b-7d13-e53f2290528c.png">

## 3. Run Shell Script を追加

`Run Shell Script` をライブラリから加えます。
検索ボックスから `run shell script` などと検索すると見つけやすいです。

<img width="384" alt="Choose Run Shell Script" src="https://qiita-image-store.s3.amazonaws.com/0/25886/37fb8763-10fd-087d-74f1-0b0621f0be20.png">

## 4. スクリプトの内容を記述

![スクリプトを貼り付けたときの図](https://qiita-image-store.s3.amazonaws.com/0/25886/b8e79711-42fb-4f3b-bbc2-fea9b78184f2.png)

下記のスクリプトを貼り付ければ OK です。

```bash
export PATH='/usr/local/bin:/usr/bin:/bin'
/usr/bin/osascript -l JavaScript -e '
this.run = (argv) => {
  const app = Application.currentApplication();
  app.includeStandardAdditions = true;
  if (!argv[0]) {
    app.displayAlert("OK");
    return;
  }
  app.displayAlert(argv[0]);
};' "$(cat - | /usr/local/bin/textlint --stdin 2>&1)"
```

##　5. 保存

メニューから File -> Save を選択して、名前をつけて保存します。
私は `Textlint` という名前をつけました。

<img width="561" alt="Save" src="https://qiita-image-store.s3.amazonaws.com/0/25886/74e63127-6949-2015-5ed8-83e2baab2015.png">

これで完了です。

# 使用例

せっかくなので、この Qiita 記事の入力画面での実行した例をスクリーンショットで載せてみます。

こんな感じの、わかりやすい「である」と「です」が混合した文を用意してみました。

```text:サンプル文章
これはオレンジである。リンゴではないのです。
```

## 1. テキストを選択

<img width="646" alt="Sample Text" src="https://qiita-image-store.s3.amazonaws.com/0/25886/2c49e517-279d-c464-41bc-a27feb153820.png">

## 2. 右クリックで `Textlint` を選択してクリック

<img width="524" alt="Sample Right Click Menu" src="https://qiita-image-store.s3.amazonaws.com/0/25886/a043a21c-c319-c3d2-eafd-7a880abad6f6.png">

## 3. すると、ちゃんとエラー内容がアラートがでます

<img width="532" alt="Error is shown" src="https://qiita-image-store.s3.amazonaws.com/0/25886/465ac044-51d1-1bab-5981-93b748c17409.png">

特に問題ない文字列の場合には「OK」と出力します。
このように、どこでもテキストを検証できるので、とてもしあわせになれそうです。
(なお、コンテクストメニューやテキスト選択に対応していないアプリケーションでは無力です。)

# おまけ: 解説

貼り付けたシェルスクリプトの解説をします。

## 1. パスを通す

コンテクストメニューから呼ばれる場合 `/usr/local/bin` あたりのパスが通っていないので明示的に追加してやる必要があります。

```bash
export PATH='/usr/local/bin:/usr/bin:/bin'
```

## 2. textlint 部分

`--stdin` オプションをつけても、うまく標準入力経由から読めなかったため、 `cat -` でパイプしています。
おそらく textlint が TTY 経由とそうでない場合ですこし挙動を変えているためと推測していますが、まだ追っていません。

```bash
cat - | /usr/local/bin/textlint --stdin 2>&1
```

## 3. JXA 部分

この部分で Apple Script (JXA) と textlint をつなげています。

```bash
/usr/bin/osascript -l JavaScript -e '(JavaScript コード)' "$( textlint 部分 )"
```

JXA とは JavaScript for Automation のことで、 AppleScript を JavaScript で書けるようにしたもの、と考えればよいです。

```javascript:(JavaScriptコード)
this.run = (argv) => {
  const app = Application.currentApplication();
  app.includeStandardAdditions = true;
  if (!argv[0]) {
    app.displayAlert("OK");
    return;
  }
  app.displayAlert(argv[0]);
};
```

やっていることはとても単純で、第一引数をエラーメッセージとして、
メッセージが空文字列なら `OK` そうでないならそのままメッセージを出力する、というものです。

グローバルスコープの `run` は C 言語の `main` 関数のようなものです。
(グローバルスコープでない `const run = (argv) => { ... };` のように書いても実行されません。)

表示には displayAlert を使っています。
アラートではなく通知にしたい場合には [`app.displayNotification`](https://github.com/dtinth/JXA-Cookbook/wiki/User-Interactions#displaying-notifications) を使うなど、自分の好みにあわせていろいろと工夫すれば、さらに使いやすくすることができるでしょう。

また、このやり方さえ覚えてしまえば、 textlint 以外の校正ツール (e.g. [redpen](http://redpen.cc/) ) 等でもかんたんに右クリックメニューの項目を作ることができると思います。どうぞお試しください。