Atom で GitHub 向け markdown の表を作る時のちょっとした工夫

#動機
GitHubのIssueに時々表を書くことがあったので、[Atom](https://atom.io) で [markdown-table-editor](https://atom.io/packages/markdown-table-editor) を使っている。
確かにいいのだけど、セル内の文字列が不揃いであったり、空のセルも多かったりすると、連続した空白が結構たくさんあって、それをそのまま幅の狭い Issue のコメント入力欄にコピペすると、とても見づらい状態になった。
なんとかならないものかと考えた次第。

つまり、
<img width="1098" alt="スクリーンショット 2018-03-17 16.52.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/31769/7a571ab9-a32b-9aa6-71c9-039851bd4d71.png">

このような出来上がりを見せた table を空白なしで次のようにして貼り付けたいのです。(Qiita だと勝手にやってくれるのですが GitHub では空白がそのまま残るもので)
<img width="1103" alt="スクリーンショット 2018-03-17 16.53.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/31769/42f0406b-f4d4-992b-80ba-59ef49059fc4.png">
(見出しと各行のセパレータ部分も最低限の長さで良いのでそのうち・・・)

#自分のニーズ
連続する空白は一つに縮めてしまう。
Issue に書く表で「連続ブランクが重要なケースは無い」と勝手に断定。
なので各行に単純に `s/  */ /g` な変換をかけてしまえれば良い。

#方法
キーボードショートカットでクリップボードへのコピーと同時にニーズを満たしたいので、キーバインディングの登録とそこで実行するスクリプトを定義することにした。

次のようにすれば良いだろうと考えた。
ネットをいろいろ探していると Atom には初期ロードするモジュールがいくつかあるらしい。まずはその中のひとつ init.coffee に必要な処理を書くことにする。次にショートカットキーを定義するモジュール keymap.cson ファイルへキーバインディグ設定を追加する。

#init.coffee、keymap.cson への追加

　1. init.coffee を [Atom]->[Init Script...] で開く
　2. 開いたファイルに次のように処理を書く(言語は [CoffeeScript](http://coffeescript.org))

```Coffee
atom.commands.add 'atom-text-editor', 'custom:shrink-multi-blank-to-single', ->
  editor = atom.workspace.getActiveTextEditor()
  text = editor.getSelectedText()
  if text.length > 0
      atom.clipboard.write(text.replace(/  +/g, ' '))
```

　3. 書いたら保存して閉じる
　4. keymap.cson を [Atom]->[Keymap...]　で開く
　5. 開いたファイルに次のように定義を書く (形式はCSON。参考:[JSONのかわりにCSONやJSON5を使うと便利そう](https://blog.kazu69.net/2014/03/09/so-convenient-instead-of-json-using-the-cson-json5/)

```cson
'atom-workspace':
  'ctrl-cmd-c': 'custom:shrink-multi-blank-to-single'
```

　6. 書いたら保存
　7. Atom を再起動

これでうまく希望は叶い、ニーズを満たすことができた。

#少し解説（備忘録）

## 情報源
Atom のドキュメントは [Help]->[[Documentation](https://atom.io/docs] で各種ドキュメントへアクセスできる。今回はその中の [API Reference](https://atom.io/docs/api/v1.25.0/AtomEnvironment]を参照した。

もちろんインターネット上で検索もした(本当言うとこっちが先でした)。
参考にしたのは[1つのキーバインドで、同時に複数のコマンドを実行する](https://qiita.com/74th/items/160b011bbfcac77d4e3c)と、
この背景にも書いた [markdown-table-editorのGitHub](https://github.com/susisu/atom-markdown-table-editor)のヘルプ文書やソースなどを眺めさせてもらった。

****
##コマンド追加の書き方
 `atom.commands.add 'atom-text-editor', 'custom:shrink-multi-blank-to-single`, 
`atom` はグローバル定義された変数で Atom の実行環境へアクセスするためにいつでも参照できる。

`commands`は[`CommandRegistry`](https://atom.io/docs/api/v1.25.0/CommandRegistry#instance)を表す。

[`add`](https://atom.io/docs/api/v1.25.0/CommandRegistry#instance-add) は [`CommandRegistry`](https://atom.io/docs/api/v1.25.0/CommandRegistry#instance) のメソッド。
`atom-text-editor` はターゲットという引数として渡している。実のところこのターゲットにどれだけの種類があるかわかっていない。なんと設定すれば良いかわからなかったが、[1つのキーバインドで、同時に複数のコマンドを実行する](https://qiita.com/74th/items/160b011bbfcac77d4e3c) では、`vim-mode`の時に使うコマンドを定義するために、`atom-text-editor.vim-mode` と指定していた。Atom のドキュメントで[`CommandRegistry`](https://atom.io/docs/api/v1.25.0/CommandRegistry#instance)の説明には、"Associates listener functions with commands in a context-sensitive way using CSS selectors. " と書かれていたので、"CSS selectors" のような方法だと言うなら、後半の ".vim-mode" 削除すればAtom 全体でいつでも使えるコマンドになるだろうと予測。その通りになった。その他一度試したのは、`markdown-table-editor` 使用時にのみ限定するように、'atom-text-editor.markdown-table-editor-active' をターゲットにしてうまくいった。これは [markdown-table-editor](https://atom.io/packages/markdown-table-editor)のヘルプ文書([Atom]->[Preferences...]、[Pakages]で markdown-table-editor のところに表示される)で、デフォルトのキーバインド設定以外の設定追加の方法が説明されていて、その定義例の次の部分を参考にしたものだった。

```Coffee
'atom-text-editor:not(.mini):not(.autocomplete-active).markdown-table-editor-active':
```

名前の `custom:` は名前空間を表していて、多分なんでも良いと思われるが、今回はカスタマイズを意識したものにした。どこかで見かけた記憶もあり自分設定としては衝突の心配などの問題ないだろうと思う。

##リスナー処理の中身
最後の `->` 以降の部分はリスナーに当たる。イベント通知を受けて処理が起動するイメージ。
処理は単純。

```Coffee
 1:    editor = atom.workspace.getActiveTextEditor()
 2:    text = editor.getSelectedText()
 3:    if text.length > 0
 4:       atom.clipboard.write(text.replace(/  +/g, ' '))
```

1. アクティブの[`TextEditor`](https://atom.io/docs/api/v1.25.0/TextEditor)オブジェクトを取得
2. 現在選択中の文字列を取得 ( [`getSelectText`](https://atom.io/docs/api/v1.25.0/TextEditor#instance-getSelectedText))
3. 選択されたテキストがないようならそのまま終了
4. 正規表現で置換し、置換後の値をクリップボードへ登録

CoffeeScript は初めてだったがここまできた。文字列の置換をどうするか一瞬迷ったが、JavaScript っぽく書いてみたら巧くいったのでちょっと照れ笑い。:stuck_out_tongue_winking_eye:

### キーバインディング
Atom のドキュメントとしては [Keymaps-In-Depth](https://flight-manual.atom.io/behind-atom/sections/keymaps-in-depth/) に説明がある。（実は上の init.coffee に書いたようなことは ["Composed" commands](https://flight-manual.atom.io/behind-atom/sections/keymaps-in-depth/#composed-commands)として、このリンク先で説明されている方法のようです。後で気づいた。:sweat:)

```cson
  1:   'atom-text-editor':
  2:      'ctrl-cmd-c': 'custom:shrink-multi-blank-to-single'
```

1. 何に対して有効なのかを決定するターゲットの指定 (この場合Atom全体を指す)
2. キーの組み合わせ(':'の左)と実行するコマンド(':'の右)のバインディング (この場合 controlキー commandキー 文字の c の同時押し)

コマンドは先の `init.coffee` でコマンド追加した時の名前を指定している。

いろいろサンプルなどがあるのでその範囲ではターゲットの指定ができるけど、どれだけターゲットが存在するのかってどこに文書化されているのか見つけられない。見つけたらどこかに控えておこうかな。


自分の作ったこういうマクロ的な資産や、`markdown-table-editor` のようなパッケージって、 Microsoft の Visual Studio Code では利用できるのかな？どうなんだろう。