feature image

2023年5月5日 | ブログ記事

部内SNSのVimクライアントもどきを作ってVim†完全に理解した†話

「部内SNSのVimクライアントもどきを作ってVim†完全に理解した†話」とかのたまっておりますが、現在のんびりと開発してる最中で完成してないんですねぇこれが...

もろタイトル詐欺ぎみで申し訳ない...:awoo:

正しいタイトルをつけるなら「部内SNSのVimクライアントもどきを作る過程でVimとかそれに伴うプラグイン等に詳しくなれたので、開発中のプラグインを使用するというていで紹介しますね」とかになりそう。なんかラノベとかのめちゃ長いタイトルみたいで、これはこれで良いのかもしれん...

タグにアドベントカレンダー2022とありますが、これは2022/12/3に担当だったのにあれよあれよといういうまに日付が過ぎてしまったのが原因ですね。当時考えてた記事はおじゃんになってしまったし、どうしたものかと思っていたので、この記事をアドカレという事にしてお茶を濁します。

Vimに関する記事&最近†Vim駅伝†たる企画が走っているみたいなので、そちらにも乗っかります。どちらかというとこっちの方が大きいかも。一つのブログで二度おいしいってやつですね(あまりよくなさそう)。ちなみにうまくいけば駅伝の5/5担当になっているはずです。

Vim 駅伝

※注意

この記事はノリと勢いで書かれており、ろくに推敲されておりません。よってつぎはぎのような文章になってる箇所も多数あります。それでも良いという方は、ゆっくりしていってね!

前書き

どうも19Bの亀茶です。†の中ではこれといってめちゃ目立つ事はしておらず、気が向くときにぷをしたり絵を描いたりしています。最近はふと気づくと†を凍結されていて、解凍してもらったのが記憶に新しいですね。

新しい記憶を思い出した所で、古い記憶(過去のブログ記事)をふと見返してみたのですが、色々な意味でエネルギッシュでまいっちゃいますね、黒歴史ってのはこうしてできるんだなぁと実感しております。この記事も未来の自分にとっちゃ黒歴史になるやもしれませんが、まぁそれはそれで一興ということでここはひとつ...

この記事ですね

この頃からVim使ってたんやなぁと思うと感慨深いものがありますね。

当時はプラグイン?何それ?状態だったので、今となってはプラグイン作れるくらいにはなったんやで~というのを過去の自分に教えてあげたいですね。

さて、蛇足であろう話が終わった所で、現在作成中のプラグインの大まかな説明と使用方法を紹介していきたいと思います。

なんで完成してもいないのに紹介するの?:hatena:という疑問もあるかと思いますが、モチベ低下等で撃沈する前に今知ってる事を記録がてら公開しちゃえ~(誰かの役にも立つかもしれんし...)というお気持ちですね。

部内SNSって何ぞや

さて、†(traP)の民になると部内サービスを色々使えるようになりますが、おそらく多くの人が使うであろうサービスがtraQです。自分も使っているのですが、いかんせんヘビーユーザーではないので詳しい説明はできないです。ただ、簡単に特徴を述べるならSlackのチャンネルがツリー構造で管理できるようになった部員しか使えないコミュニケーションツール、かなぁと思います。

ツリー構造っていうのが個人的に好きな所で、話題ごとにチャンネルをまとめたりする事ができて便利だなぁとか思っています。逆にチャンネルの整理が大変だ~とかいう話も聞くので、まぁ利点ばかりやないんやなぁと...

ちなみに今回プラグインを作ろうと思ったきっかけの一つがツリー構造っていうのもあります。

実際の見た目はこんな感じになっております

チャンネルがツリー形式で保存されていて、チャンネルを表示するとタイムラインが表示されているのが分かりますね。Slackっぽいので分かりやすいと思います。スタンプも押せたりして良い感じですね。蛇足ですが個人的に:akane:、:yannne:、:awoo:、:nico:あたりのスタンプがめちゃお気に入りです。あとこの空気感...良いですねぇ~

ちなみに掲載許可はとっております。協力してくれた方々には感謝感激雨あられですね。

†Vim†って何?

はい、いわずとしれたテキストエディターですね。最近は色んなエディターがあって悩ましいですが、VSCodeユーザーが多かったりするのかなぁと思います。かくいう自分もVSCodeに片足突っ込んでおります。Vimと対比されるEmacsというのも気になっているのですが、いかんせんヨイショが足りず手を出せてないのが悲しい所...

Vimは独特な操作方法であり、ある程度習得すると、はえ~こりゃ便利やね~となるエディタとなっております。また拡張性もそれなりにあって、今回のようにテキストエディタにも関わらずSNSクライアントもどきになったりできます。

最近はVimのプラグインを開発する環境も多種多用になってきており、neovim(Vimのフォークで思想がちょっと違うらしい、自分はこれを使ってる)専用のプラグインのためluaで書いたり、vimscriptで書いたり、denopsと呼ばれるエコシステムを使用しTypeScriptで書いたり...といった現状があるようです。

現在作成しているプラグインはdenopsの上に乗っかりTypeScriptを使用して書いています。denopsのおかげでdenoの上で動かす事ができ、denoがnpmのライブラリを使えるためtraQのクライアントのライブラリ も使えるため色々便利に開発できています。後述するUIフレームワークプラグインもこのdenopsに乗っかっており、このプラグインとの連携も簡単になっています。いや~便利ですね~

ちなみにdenopsの詳しい記事はこちらとなっております

Deno で Vim/Neovim のプラグインを書く (denops.vim)

作成中のプラグイン

ここで作成中のプラグインの詳細な説明をするのが本当は良いんだろうなぁと思いつつも、実装とかの詳細を説明しだすと長くなってしまうの(1個1個は短いんですが...)と、小分けに別ブログとかに切り出した方が検索性がよくなるだろうとの思惑でやめておきます。けっしてさぼりじゃないです、、ほんとだよ~

ただ↓こちらのプラグインの実装を参考にしている箇所もあるので、そこは忘れないうちに記しておきます。実装したかった機能と似たような機能が先に作られてあるので、とても参考になりました:yannne:

VimでTwitterをするプラグインを作った

ちなみに開発中のレポジトリはこちらとなります。リポジトリ名にPracticeとあるように練習感あるのが良い味だしてますね。その名の通り練習で開発してるため、おじゃんになったりしなかったりします。練習だからね、しょうがいないね。

見た目

さて開発中の見た目ですが、以下のようになっています(画像はイメージです←これ言ってみたかった)

本家traQで主にやる事はチャンネルを探してきて、表示する事だと思っていたので、それができるようにしてみました。あくまでvimのバッファとして表示してるだけだから、普段コードを書いてる時でも表示できるよ~ってのをお伝えしたくて無駄にウィンドウ分割とかしてます。なんかこう†それっぽい感†を演出したかったのですが、思ったより難しいですね...

使い方

簡単な使い方は書きかけのヘルプに書いてあるので省きますね。

ひとまず依存しているプラグイン等を導入します。詳しくはリポジトリのREADMEとかを見てもらえると手っ取り早いと思います。

初めにOAuth関連の認証をしてトークンをゲットします。そうすると残りのコマンドが全部使えるようになります。ちなみにまだ認証部分を非同期実行してないので、OAuth認証のためにブラウザを開いている間Vimが固まります。と~ってもヤバい仕様なのですが、一回トークン取得しちゃうと後の開発ができちゃうため、後回しになっております。う~んよくない...

これを書いてる最中に試してみて気づいたのですが、認証するために :TraqSetup をした後にvimを閉じてからもう一度開く~といった事をしないとトークンが認識されないみたいです...う~ん不便...。これもissueにしておかないとなぁ。こうやってちょっとづつ改善されていくんやねぇ

また単純なコマンドとしてホームチャンネルや指定のチャンネルを現在のバッファに表示するというのもあります。こちらは独自のシンタックスとハイライトを定義しているため、タイムラインがそれっぽく色づけされると思います。

使い方は開発中のものです...なのでロク機能がないんですね...しょうがいなね

ddc・dduを使用した使い方

さて、本プラグインでおそらくかなり便利な部類であろうddc・dduとの連携について説明いたします。これこそ、本日のメインイベント~(デデデ風)といっても過言じゃないかもですね。ちなみに本家記事があるので詳しい内容はそちらを参考にしていただければ幸いですね。

新世代の自動補完プラグイン ddc.vim
新世代の UI 作成プラグイン ddu.vim

さて、自分なりにこれらのプラグインの説明も交えながら使い方もとい設定方法を解説していこうかなぁなんて事を思っています。ただVim固有の設定方法(プラグインマネージャの詳しい導入方法とかね)までやるのは面倒なので省きます。本プラグインはこれらに乗っかっているだけなので、ここでの設定方法は他のプラグインでの設定にも役に立つのではないのかなぁと思っています。ただ、このプラグインを設定する時にどんな感じで動いているかの背景知識をそれとな~く知っておかないと正しく設定できないので、本家の説明よろしくひとまず背景知識の自分なりの解釈を記しておきたいなと思います。

どちらのプラグインもやっている事は大体同じだと思っていて

  1. 何かしらの対象をひとまず集めてくる(source)
  2. 集めてきた対象からどれかをピックアップする(matcher)
  3. ピックアップした対象をそれとな~く並べ替える(sorter)
  4. 並べ替えた対象を表示するのに適するように編集する(converter)
  5. 2~4の処理をされた後の対象から適切に対象を選択し、実行する(kind)

と表現できるんじゃないのかなぁと思います。これらの動きをする枠組みをddc・dduともに整備しているというイメージでしょうか。そして、これらの処理は全て非同期で動くみたいです。なので、裏でじゃんじゃん対象を集めまくっていると、2-4の処理の後の候補もじゃんじゃん増えるといった事も可能だと思います。ちなみに2-4の処理をまとめてfilterと呼ぶらしいです。

1 2 3 4 5
ddu-1 ddu-2 ddu-3 ddu-4 ddu-5

↑の図めちゃ小さく表示されて見づらくて申し訳ねぇ...ごめんやで

そしてこの1~5はそれぞれ分離されているので、ユーザーは好きなものをそれぞれ選べちゃいます。ただ選択肢が増えるとその分選ぶ労力がかかっちゃうのですが、そこはまぁトレードオフというやつなのかも...

ddc・dduともにこの1~5の動きを提供するのですが、ddcは入力中の補完に的を絞っている分5番目のアクションとしてはカーソル位置に選択した単語を挿入するという動きになります(スニペットを展開する~とかもできるみたい)。dduの場合はこの5にあたる処理をユーザーが指定できます。例えば選択したアイテムに対応するファイルをタブで開くか、それとも水平分割で開くか...といった感じです。

さてさてddc・dduの動きをそれとな~く分かったつもりになった所で具体的な設定をしていきたいなと思います。

ddc

まずはddcのスタンプ補完から。本プラグインは†で使用されるスタンプを全部持ってくるだけという処理をします。先ほどの説明でいう、

  1. 何かしらの対象を集めてくる

の箇所だけという事ですね。そのため絞り込み用の他のプラグインを導入しないと大量のスタンプの中から目的のスタンプ文字列を己の眼力のみで探す~とかいう苦行をするハメになります。いわゆる目grepとかいうやつですかね、知らんけど。ちなみに、スタンプ名の前方一致のみでの補完は本家traQで実装されています。せっかくなので、今回はあいまいマッチで絞り込みを行うプラグインを使用しようと思います。あいまいマッチを導入すると :kanzennirikaishita: みたいな長いスタンプ名を :karshit...: みたいな入力で補完に表示できるようになります。単語の初めの方や中間あたり、最後の方をなんとなく覚えているだけで大丈夫になるので結構便利です。(ちなみにこのスタンプを登録したのはワイ君らしいですね)

これらを考慮した際のinit.vim等を少しづつ見ていきたいなと思います。最終的な設定だけ欲しいぜ!って方はもうちょいスクロールすると設定の全体像が分かるかなと思います。

set rtp+=~/.cache/dein/nvim/repos/github.com/vim-denops/denops.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/kamecha/traqVimPractice

" ↑ここまではプラグインをvimに認識させる処理です。
" 大抵はプラグインマネージャがよしなにやってくれる処理です。
"
" ↓ここからプラグインの設定を行います。

まずddcの設定をする前の状態がこんな感じです。説明のため set rtp+= のようなものを使っていますが、この処理は大抵プラグインマネージャ等がよしなにやってくれるので、お好きなプラグインマネージャを使いましょう。自分はdein.vimってのを使っています。設定ファイルをtoml形式で書けて色々ハッピーになれます。最近はlazy.vim?みたいなのも出てきているらしく色々凄そうです。

そうそうちなみになのですが、この set rtp+= を使ってvimにプラグインを読み込ませる方法はプラグインマネージャを介していないので、プラグインのバグ報告をする際によく用いられる方法らしいですよ。俗にいう†最小構成†を作るってやつですかね。この形式のファイルを.vim拡張子で保存しておいて nvim -u hoge.vim のように -u オプションを加えることで、デフォルトのinit.vimとかではなく、指定のファイルを読み込ませて起動する事ができるのだとか...

蛇足が過ぎましたかね...以降はこれにちょっとづつ設定を足していく感じで行きます。ちなみにこの状態でも :TraqSetup をすればちゃんと :TraqHome でホームチャンネルが表示されるはずです。

set rtp+=~/.cache/dein/nvim/repos/github.com/vim-denops/denops.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/kamecha/traqVimPractice
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc.vim "←ここを追加したよ
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc-ui-native "←ここを追加したよ

" ↑ここまではプラグインをvimに認識させる処理です。
" 大抵はプラグインマネージャがよしなにやってくれる処理です。
"
" ↓ここからプラグインの設定を行います。

まずはddcとddc-ui-nativeってのを読み込ませます。ddcは先ほど説明通り、集めて・並び替えたりして・選択するっていう枠組みを提供してくれるやつですね。ddc-ui-nativeってのはその名の通りddcのUIを表示するプラグインです。ddcはあくまで枠組みなのでアイテムの表示は担当してないです。なのでUIも指定してあげないといけないんですね。このUIを指定してあげないとアイテムを集めて・並び替えて〜って処理を裏でやっているのに、UIで表示されないからユーザーは何もできないみたいな事になっちゃいます。nativeっていうのはvimにもともと搭載されている補完用のUIを使うよ〜って事だと思います。ddcと本家vimのUIをうまいこと橋渡ししてくれているのでしょう、多分。

set rtp+=~/.cache/dein/nvim/repos/github.com/vim-denops/denops.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/kamecha/traqVimPractice
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc-ui-native

" ↑ここまではプラグインをvimに認識させる処理です。
" 大抵はプラグインマネージャがよしなにやってくれる処理です。
"
" ↓ここからプラグインの設定を行います。

let s:setting =<< trim MARK
{
	"ui": "native"
}
MARK
let s:setting = s:setting->join('')->json_decode()
call ddc#custom#patch_global(s:setting)
call ddc#enable()

プラグインを読み込ませたので設定をしていきます。 =<< trim MARK MARK ってのは MARK で囲まれた箇所をList形式で保存してくれる便利なやつです( :help =<<を参照)。MARKはなんでも良くて UNKO とかにしてる方も見かけます。この方法を使っているのは改行後に \ を挿入しなくていいからですね。設定に使用するvimscriptって言語は改行した後に \ を挿入しなくちゃいけない事があって、いちいち挿入するのが面倒だからですね。まぁneovimを使っている方でluaで書けばこの手間はなかったりするのかな?

のちの let s:setting = ... decode() ってのはjsonとしていい感じに解釈してくれるよというやつです。ここのjsonとしていい感じに解釈してくれるってのもあって、ちゃんとjsonにしなくちゃダメです。なので、 , の扱いが面倒です。ここもまぁトレードオフ...

この次の call ddc#custom#patch_gloabal() ってのが重要でddcの枠組みの中で設定をグローバルに反映させてくれます。この段階だと、アイテムを表示するためのUIとして native を使用するよ〜とグローバルに設定してます。アイテムを表示する〜なんてことは絶対にやって欲しいのでグローバルに設定しておきます。

次は call ddc#enable() を呼びます。これを呼ばないとddcが起動しないです。これから色々頑張って設定するのに、肝心のddc本体が起動しないと悲しいので絶対に呼んでやります。

さて、UIを設定してddcを有効にしたわけですが、この段階だと何も表示されないです。えっ!UIを設定したのに〜とか思うかもしれませんが、残念...何も表示されないんですねぇ...。それもそのはずで何を集めてくるかっていうのを設定していないからですね。先ほどの説明でいう1番のやつですね。

次は集めてくるやつ、ここでいうスタンプ名を集めるよってのを設定します。

set rtp+=~/.cache/dein/nvim/repos/github.com/vim-denops/denops.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/kamecha/traqVimPractice
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc-ui-native

" ↑ここまではプラグインをvimに認識させる処理です。
" 大抵はプラグインマネージャがよしなにやってくれる処理です。
"
" ↓ここからプラグインの設定を行います。

let s:setting =<< trim MARK
{
	"ui": "native",
	"sources": ["stamp"]
}
MARK
let s:setting = s:setting->join('')->json_decode()
call ddc#custom#patch_global(s:setting)
call ddc#enable()

はい sourcesstamp ってのを設定していますね。ここが配列になっているのは一度に複数のsourceから集められるよ〜って事です。例えば "sources" : ["stamp", "emoji"] とかすると、スタンプ名だけじゃなくてemoji名も集めてきてくれます。ちなみになのですが、 stamp っていうのは本プラグインが設定している名前なので仕様変更で変わるかもです。練習だからね、しょうがないね。もちろん emoji っていうのは emoji に該当するソースを別途GitHubから落としてきてやる必要があります。本プラグインが提供しているのは stamp だけだからですね。

emoji に該当するのは↓ですかね。これも含めると設定を説明するのが面倒になるので今回は省きます。ちゃんと該当するソースを落としてきて、そのソースに該当する名前を設定してあげなくちゃダメだよということがそれとな〜くわかればOK!

GitHub - gamoutatsumi/ddc-emoji: Emoji source of ddc.vim
Emoji source of ddc.vim. Contribute to gamoutatsumi/ddc-emoji development by creating an account on GitHub.

さてUIを設定しddcを有効にして、集めてくるものも設定しました。なのでこの段階でもちゃんと補完自体はされるはずです。

: を入力して何かしらのアルファベットを入力するとこんな感じになると思います。ただご覧の通り入力したアルファベットは全然関係ない補完候補になっていますね。それもそのはずで現時点では集めきたスタンプをそのまま全部表示しちゃってるからですね。先ほどの説明でいう2~4を設定していないので、そのまま表示しちゃっている状態です。これだと眼力苦行なので、ちゃんと絞りこむ方式をddcに伝えてあげます。

ちなみに上記の画像が質素なのは、説明中のファイルをそのまま nvim -u hoge.vim で読み込んでいるからですね。他のプラグインが読み込まれず、カラースキーマ等の設定もしていないので、3DCGのエラーみたいな紫色で表示されています。

ちなみに、vimは起動する度に設定を読み込むので以降は設定を追加する度にvimを終了させて再度同様のコマンドで起動し直しています。俗にいう†再起動...やはり再起動は全てを解決する...†ってやつです。(ホンマか?!)

set rtp+=~/.cache/dein/nvim/repos/github.com/vim-denops/denops.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/kamecha/traqVimPractice
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc-ui-native
set rtp+=~/.cache/dein/nvim/repos/github.com/tani/ddc-fuzzy "←追加したよ

" ↑ここまではプラグインをvimに認識させる処理です。
" 大抵はプラグインマネージャがよしなにやってくれる処理です。
"
" ↓ここからプラグインの設定を行います。

let s:setting =<< trim MARK
{
	"ui": "native",
	"sources": ["stamp"],
	"sourceOptions": {
		"stamp": {
			"matchers": ["matcher_fuzzy"]
		}
	}
}
MARK
let s:setting = s:setting->join('')->json_decode()
call ddc#custom#patch_global(s:setting)
call ddc#enable()

絞りこみには曖昧マッチを使います。ここもお好きなものを使うことができます。対応する絞りこみプラグインを落としてきて、該当する名前をddcに教えてあげます。絞りこみの設定自体は sourceOptions を通して行います。 sourceOptions はstampに限らずソースごとの個別の設定を行う箇所です。ここでは stamp ソースの絞りこみに matcher_fuzzy を設定するよ、となっています。配列になっているのは複数の絞りこみを使えるということですね。配列の左から順に絞り込んだアイテムを更に絞り込んで〜といった感じに動くのだと思います。( :h ddc-source-option-matchers

同じ文字列を入力しているのに表示されている補完候補が減っているのが分かると思います。スタンプ名を全部表示するのではなく、集めてきたスタンプ名から曖昧マッチに基づいて絞りこみができているからですね。先ほどの説明でいう

  1. 何かしらの対象をひとまず集めてくる(source)←ここ
  2. 集めてきた対象からどれかをピックアップする(matcher)←ここ
  3. ピックアップした対象をそれとな~く並べ替える(sorter)
  4. 並べ替えた対象を表示するのに適するように編集する(converter)
  5. 2~4の処理をされた後の対象から適切に対象を選択し、実行する(kind)

の二つが設定できましたね。これだけでも便利なのですが、せっかくなので(sorter)にあたる箇所も設定してみましょう。

set rtp+=~/.cache/dein/nvim/repos/github.com/vim-denops/denops.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/kamecha/traqVimPractice
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc-ui-native
set rtp+=~/.cache/dein/nvim/repos/github.com/tani/ddc-fuzzy
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc-sorter_rank "←追加したお

" ↑ここまではプラグインをvimに認識させる処理です。
" 大抵はプラグインマネージャがよしなにやってくれる処理です。
"
" ↓ここからプラグインの設定を行います。

let s:setting =<< trim MARK
{
	"ui": "native",
	"sources": ["stamp"],
	"sourceOptions": {
		"stamp": {
			"matchers": ["matcher_fuzzy"],
			"sorters": ["sorter_rank"]
		}
	}
}
MARK
let s:setting = s:setting->join('')->json_decode()
call ddc#custom#patch_global(s:setting)
call ddc#enable()

2-4の処理がまとめてfilterとして呼称されているように、matcherとsorterの設定は同じように書けます。同じように設定してあげると↓みたくなります。なんかこういい感じに並べ替えられてるのが分かりますね。( :kanzennirikaishita: って打とうとしてるのに :wakarase: が最初の候補として出てきて草になってる)

さてこれでddcの設定は終わりましたーーーとなりたいところですが。残念ながらそういうわけにもいかないんですね、これが...。ご覧の通り今までの設定は全てddcのグローバルに設定しています。上記画像のようになんでもない普通のバッファでもスタンプの補完がされちゃってます。これだと普段コーディングする時でもスタンプの補完が表示されてしまい、めちゃ煩わしいです。なので、本プラグインで†にメッセージを送信する時だけスタンプの補完が表示されるようにします。

set rtp+=~/.cache/dein/nvim/repos/github.com/vim-denops/denops.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/kamecha/traqVimPractice
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc-ui-native
set rtp+=~/.cache/dein/nvim/repos/github.com/tani/ddc-fuzzy
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc-sorter_rank

" ↑ここまではプラグインをvimに認識させる処理です。
" 大抵はプラグインマネージャがよしなにやってくれる処理です。
"
" ↓ここからプラグインの設定を行います。

let s:setting =<< trim MARK
{
	"ui": "native",
	"sourceOptions": {
		"stamp": {
			"matchers": ["matcher_fuzzy"],
			"sorters": ["sorter_rank"]
		}
	}
}
MARK
let s:setting = s:setting->join('')->json_decode()
call ddc#custom#patch_global(s:setting)
call ddc#custom#patch_filetype('traqvim-message', {
			\ "sources": ["stamp"],
			\ "specialBufferCompletion": v:true
			\ })
call ddc#enable()

グローバルに設定していた sources が消えていると思います。この時点だとどのバッファで入力を開始しても、実際に補完をするためのsourcesを設定していないため何も集めてくれません。まぁそれはそう、ですね。ただstampソースの時は曖昧マッチで絞り込んで、いい感じに並べ替えるっていう設定はグローバルにされている状態です。

stampソース自体の設定はグローバルでされているので、後は†用のメッセージを書き込む時だけsourcesを別に設定すればOKです。この†用のメッセージと普通のバッファを分別する時にfiletypeってのを使います。.htmlとか.rsとか.tomlとかがそれにあたります。本プラグインはメッセージ用のバッファに traqvim-message というファイルタイプを割り当てているので、そいつを使ってあげます。ちなみにですが、この traqvim-message っていう名前も練習開発中なので今後破滅的変更で変わっちゃうかもなのです。あしからず...

ただこのファイルタイプ君は本プラグイン独自のものなので、そのままだとddcがちゃんと動いてくれません。このバッファはちょいと特殊だからそこんとこよろしくね〜というのをddc君に伝えてあげないといけないんですねぇ。その役割をしてくれているのが specialBuffereCompletion ってやつですね。

これらをいい感じに設定してあげると、†用のメッセージバッファでだけスタンプ補完が効くようになります。下の画像見たいな感じですね。ちなみにメッセージを書きたい時はチャンネルのタイムラインを表示しているバッファで :TraqMessageOpen ってしてやるとそれ用のバッファが開きます。(多分タイムラインのバッファ上で o をうつことでもできるようになっているはず...)あくまで開発中...rec

とここまでで本プラグインとddcを連携させる最低限の設定が終わりました。いや〜疲れましたね。思ったより書くのって疲れるなぁと思う今日このごろ...勢いに任せて書いてるのもあってなんとか耐えてる感じがしますね。

(゚д゚)ハッ!! (ここは自分が疲れた事を書くんじゃなくて、これを見て設定を行うであろう数少ないありがたい方々に対して「設定お疲れ様でした!」というべきなのでは...)

ddcとdduの設定は似通っているところがあるので、ddcの設定がそれとなくわかればdduの設定もそれとなくできるはず...なのでdduの設定についてはそれとなく端折っても大丈夫...な...はず...(ここで疲れて撃沈)

ddu

次はdduについての説明をば...

dduはddcを更に汎用的にしたものだと個人的に思っています。自由度がぐぐ〜んと上がる分その自由度を反映させるための設定量がぐぐ〜んと多くなっちゃいます。いや〜これがね、結構面倒臭い...なんかこうエイヤッでシュッてするだけで全部うまいこと動いて欲しいなぁ〜なんてことを思ったりするのですが、そうは問屋が卸さないんですねぇ、これが暗黒の力というやつでしょうか。まぁその分プラグインの動きを†完全に理解した†状態になれるのは利点なのかな。

本プラグインとdduを設定すると、本家traQみたくチャンネルをツリー状で管理したり、全部のチャンネルから特定の文字列にマッチするものを探したり〜なんて事ができちゃいます。ついでに言うとチャンネルを探している時に、そのチャンネルのタイムラインがどんな感じなのかな〜ってのをプレビュー表示できたりします。こりゃすげぇですな、えぇっ!こんな凄い事がdduに乗っかるだけでできちゃうんですか!?あたしゃ感動してものも言えないよトホホ...

まぁそれはさておき設定をやってきます。

本プラグイン(開発中)が提供するのは†でのチャンネル一覧を全てとってくるというsourceと、選択したチャンネルをバッファに開く( split vsplit tab ...)を提供しています。上記の説明でいう所の1,5にあたる箇所ですね(←1,5を良い感じに表記する方法分からんかったのだ...)。

ddu-ui-ff

まずは適当なチャンネル一覧を取ってきて〜それから絞りこみをするぞ〜ってやつから設定していきます。集めて・絞り込んで・色々するって流れなので先ほどのddcでの設定への理解がここで活きますね。

" 本プラグイン関連
set rtp+=~/.cache/dein/nvim/repos/github.com/vim-denops/denops.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/kamecha/traqVimPractice

" ddc関連
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc-ui-native
set rtp+=~/.cache/dein/nvim/repos/github.com/tani/ddc-fuzzy
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc-sorter_rank

" ddu関連
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu-ui-ff
set rtp+=~/.cache/dein/nvim/repos/github.com/yuki-yano/ddu-filter-fzf

" ↑ここまではプラグインをvimに認識させる処理です。
" 大抵はプラグインマネージャがよしなにやってくれる処理です。
"
" ↓ここからプラグインの設定を行います。

" ddc用の設定
let s:setting =<< trim MARK
{
	"ui": "native",
	"sourceOptions": {
		"stamp": {
			"matchers": ["matcher_fuzzy"],
			"sorters": ["sorter_rank"]
		}
	}
}
MARK
let s:setting = s:setting->join('')->json_decode()
call ddc#custom#patch_global(s:setting)
call ddc#custom#patch_filetype('traqvim-message', {
			\ "sources": ["stamp"],
			\ "specialBufferCompletion": v:true
			\ })
call ddc#enable()

" dduの設定
let s:ddu_setting =<< trim MARK
	{
		"ui": "ff",
		"sources": [
			{ "name": "channel_rec" }
		],
		"sourceOptions": {
			"_": {
				"matchers": ["matcher_fzf"]
			}
		}
	}
MARK
let s:ddu_setting = s:ddu_setting->join('')->json_decode()
call ddu#custom#patch_global(s:ddu_setting)

まずはこんな感じに設定します。先ほどのddcの設定とかにコメントをつけたり整理して、dduの設定をしてみました。このまま追記していくと単に長くて見づらいだけなので、以降はddc関連の設定は省略しますね。

dduのUIにff(fuzzy-finder用のUI)を設定し、集めてくるソースとしてchannel_recを設定、そして絞りこみプラグインとしてmatcher_fzfを指定しています。channel_recではなく _ に設定しているように見えますが、これはデフォルト設定を行っている箇所で、全てのソースでこの設定を行うよって意味です。もちろんこの設定をした上で個別に設定をすると、個別設定が優先されます。channel_recってのは本プラグインが提供するfuzz-finder用のソースでチャンネルのツリー構造を表現した文字列を返します( "#gps/times/kamecha" みたいな文字列そのものです)。

さて、ddcは補完に特化している分、どのように表示されるかはあらかた予想できると思いますが、dduは更に汎用的になっているので、設定だけだとどのような動き・表示をするのか予想しづらいです。百聞は一見に如かずと言われるほどだし、まずは動かしてみます。

vimを上記の設定を読み込むように起動します( -u オプションをつけたり、 .vimrc に設定を記述したり〜という事です)。そして :call ddu#start({}) と入力してみます。(Tab補完が少しききます)

するとこのように表示されます。チャンネル名の一覧が全部表示されているのが分かりますね。あれ?さっき絞りこみ用のプラグインを設定しているのに全部表示されてる?と思った方、良い気づきですねぇ〜(ikgmボイス)。もちろん絞りこみ用のプラグインは設定されています。ddcでの場合は画面に入力をすると勝手に補完が表示されたかと思いますが、今回の場合はそういうわけではありません。絞りこみ用の文字列を別途入力してやる必要があるんですね。

この状態(カーソルがdduにある状態)で、 :call ddu#ui#ff#do_action('openFilterWindow')としてみてください(これもある程度Tab補完が聞きます)。そうすると、絞りこみに使う文字列を入力するバッファが別途表示されると思います。これはdduの枠組みの中で使用しているuiのff(fuzy-finder)で絞りこみ用のfilterWindowを開くよと言う意味です。

この入力欄に文字列を入力すると、入力した文字列を利用した絞りこみが行われます。ちゃんと曖昧マッチが行われているのが分かりますね。

ただこれだと全部のチャンネル名から曖昧マッチで絞り込んだだけですね。そうやりたいことは集めてきて、絞り込んだチャンネルを開くことでした。

  1. 何かしらの対象をひとまず集めてくる(source)
  2. 集めてきた対象からどれかをピックアップする(matcher)
  3. ピックアップした対象をそれとな~く並べ替える(sorter)
  4. 並べ替えた対象を表示するのに適するように編集する(converter)
  5. 2~4の処理をされた後の対象から適切に対象を選択し、実行する(kind)←ここ!

「←ここ!」の箇所ですね。これをひとまず手動で再現します。今はFilterWindowにカーソルがあるので、<ESC>とか<C-w><C-w>とかを駆使してfuzzy-finder用のウィンドウにカーソルを合わせます。そして目当てのチャンネル名にカーソルを合わせて :call ddu#ui#ff#do_action('itemAction', { 'name' : 'open'}) と入力してエンターします。すると対象のチャンネルのタイムラインがバッファに開かれると思います。これは選択している(カーソル下)のアイテムに対して、 open とアクションを実行するよと言う意味です。この open アクションは本プラグインが提供しているアクションです。ソースに対応するアクションの名前をちゃんと設定しないと実行できないのでご注意を。

ふぅ〜お疲れ様です。これでdduでのファジーファインダーの大まかな流れ、1~5をひとまず手動で体験することができました。さて、今度はこの流れのショートカットを設定していきます。さすがに全部手打ちで :call ddu#ui... なんて毎回入力するのは煩わしいの極みなのでね...

" ....↑ddcとかの設定があるけど省略してます

" dduの設定
let s:ddu_setting =<< trim MARK
	{
		"ui": "ff",
		"sources": [
			{ "name": "channel_rec" }
		],
		"sourceOptions": {
			"_": {
				"matchers": ["matcher_fzf"]
			}
		}
	}
MARK
let s:ddu_setting = s:ddu_setting->join('')->json_decode()
call ddu#custom#patch_global(s:ddu_setting)

autocmd FileType ddu-ff call s:ddu_my_settings()

function! s:ddu_my_settings() abort
	nnoremap <buffer><silent> <CR>
				\ <Cmd>call ddu#ui#ff#do_action('itemAction', { 'name' : 'open' })<CR>
	nnoremap <buffer><silent> i
				\ <Cmd>call ddu#ui#ff#do_action('openFilterWindow')<CR>
endfunction

ひとまずこんな設定にしてみました。先ほどまで愛のある手打ちをしていた call ddu#ui#... みたいな処理がショートカットに登録されているのがなんとなく分かると思います。 autocmd とかいう見慣れないものがありますが、詳しい説明は省きます。FileTypeがddu-ff(filterWindowじゃない方のdduのウィンドウ)の時にマッピングを設定する関数を毎回呼ぶよ〜みたいなイメージで大丈夫だと思います。自分もそれくらいのイメージしか持っていないですしおすし...

これで :call ddu#start({}) と呼んだ後に、 i でfilterWindowを開き、絞りこみ用文字列の入力、<ESC>とか<C-w><C-w>で移動した後にエンターキー(<CR>)で対象チャンネルを開くといった感じになります。手打ちするよか大分楽になりましたね

ここまででdduの枠組みの中でfuzzy-finderに適したUI(ddu-ui-ff)の簡素な設定が終わりました。このままでも便利なのですが、マイブームな設定の一部も織り交ぜて紹介しておこうかなぁと思います。簡素な設定を終えているので、†オレオレマイブーム設定†もシュッて分かると思います。

" ↑さっきまで同じ
let s:ddu_setting =<< trim MARK
	{
		"ui": "ff",
		"uiParams": {
			"ff": {
				"startFilter": true     //←ここ追加!(このコメントはjsonとしておかしいので参考にする時は消してね
			}
		},
		"sources": [
			{ "name": "channel_rec" }
		],
		"sourceOptions": {
			"_": {
				"matchers": ["matcher_fzf"]
			}
		}
	}
MARK
let s:ddu_setting = s:ddu_setting->join('')->json_decode()
call ddu#custom#patch_global(s:ddu_setting)

autocmd FileType ddu-ff call s:ddu_my_settings()

function! s:ddu_my_settings() abort
	setlocal cursorline " ←ここ追加
	nnoremap <buffer><silent> <CR>
				\ <Cmd>call ddu#ui#ff#do_action('itemAction', { 'name' : 'open' })<CR>
	nnoremap <buffer><silent> i
				\ <Cmd>call ddu#ui#ff#do_action('openFilterWindow')<CR>
endfunction

" ↓ここから追記
autocmd FileType ddu-ff-filter call s:ddu_filter_my_settings()

function! s:ddu_filter_my_settings() abort
	inoremap <buffer> <CR>
				\ <Cmd>call ddu#ui#ff#do_action('itemAction', { 'name' : 'open' })<CR>
	inoremap <buffer> <C-o>
				\ <Cmd>call ddu#ui#ff#do_action('itemAction', { 'name' : 'open', 'params' : { 'command' : 'split'} })<CR>
	inoremap <buffer> <C-v>
				\ <Cmd>call ddu#ui#ff#do_action('itemAction', { 'name' : 'open', 'params' : { 'command' : 'vsplit'} })<CR>
	inoremap <buffer> <C-t>
				\ <Cmd>call ddu#ui#ff#do_action('itemAction', { 'name' : 'open', 'params' : { 'command' : 'tabnew'} })<CR>
	inoremap <buffer> <C-s>
				\ <Cmd>call ddu#ui#ff#do_action('toggleSelectItem')<CR>
	inoremap <buffer> <C-n>
				\ <Cmd>call ddu#ui#ff#execute("call cursor(line('.')+1,0)<Bar>redraw")<CR>
	inoremap <buffer> <C-p>
				\ <Cmd>call ddu#ui#ff#execute("call cursor(line('.')-1,0)<Bar>redraw")<CR>
	inoremap <buffer> <C-q>
				\ <Cmd>call ddu#ui#ff#do_action('quit')<CR>
endfunction

こんな感じに設定すると :call ddu#start({}) した時に自動的にfilterWindowを開いてくれます。そして、filterWindowにカーソルがあってインサートモード中に、コントロールキーを押しながら、nやp、sを押すことでそれぞれの動作を行えます。いやぁ便利!ただ、普段開発してるデスクトップではなくて、この記事を書いてる検証用のmacだと入力してる最中だと<C-p><C-n>がうまく効いてなくて、一文字消去すると効くようになっててなんだが不思議〜となっております。まぁ練習兼開発中だからね、しょうがないね。

ちなみに setlocal cursolrline をしているのはfilterWindowにカーソルがあると、filterWindowじゃない方でどこにカーソルがあるのか判別する事が苦行だからですね。(元々はデフォルトでカーソルラインがついてたけど、アプデで省略されちゃったみたい。こういうこと結構あるのでヘルプのアプデ欄はたまに眺めてると嬉しさがあります。)

そして<C-s>でアイテムを選択することができます。画像で色が変わっている箇所が選択してる部分ですね。この状態で<C-o>とか<C-v>とかすると選択しているチャンネルがその動作をします。画像の例だと2つのチャンネルを同時に開くみたいな感じですね。

ふぅ〜これでチャンネル名をファジーファインダーで絞り込んで開く〜みたいな一連の流れの設定ができました。プレビューとかfloat windowの見た目で絞りこみを行う設定はおまけの欄に記載しておきますね。まぁここまで設定できればあとはddu-ui-ffのヘルプを見ながら設定できなくもないけど、他の人の設定を眺めるのも楽しいかなぁなんてことを思っているので一応載せます。(気力が残っていれば...)

ddu-ui-filer

日付変わりまして明日の名無しがお送りします。いやぁ次の日のkamechaです。設定の紹介みたいなもんだから一日でガッて書き切ってそのままエイヤッで投稿しちゃえ〜なんて事を考えていたのですが、思ったより手間がかかりますね。昨日の自分にお疲れ様、今日の自分にこんにちは、と言うことで設定の続きを紹介していきたいと思います。

昨日はdduの中のファジーファインダーの設定をしました。今日はdduに備わっているファイラー関連の設定をやっていきます。本家traQにも左側にチャンネルがツリー状で表示されていますよね。あれをvim上で再現していきたいと思います。これはtraQのチャンネルのツリー構造を普通のディレクトリ構造(ファイルとフォルダみたいな関係)に見立てることで再現しています。チャンネルがツリー状に管理されてある事の利点ですね。本プラグインで再現したかった事の一つなので、実装できてにっこりしています:yannne:。

ひとまず本家での紹介記事をぶら下げておきますね。そちらの方が簡潔にまとまっていて、見やすいです。(ワイのこの記事の必要性...無いやん:awoo:)

新世代のファイラー UI ddu-ui-filer

ファイラーはファジーファインダーと違って絞りこむ〜といった処理は行いません。(多分そのはず)集めてきて、そのまま表示するだけですね。ひとまず、先ほどまでの設定で使っていたチャンネル名をそのまま設定してみます。

といったものの、まずはどんなものか試してみます。先ほどまでの設定でdduのグローバルに設定しているものを流用する形でいきましょう。

" ...
" ddu関連
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu-ui-ff
set rtp+=~/.cache/dein/nvim/repos/github.com/yuki-yano/ddu-filter-fzf
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu-ui-filer "←ここを追加:yannne:

" ...ddc関連...
let s:ddu_setting =<< trim MARK
	{
		"ui": "ff",
		"uiParams": {
			"ff": {
				"startFilter": true
			}
		},
		"sources": [
			{ "name": "channel_rec" }
		],
		"sourceOptions": {
			"_": {
				"matchers": ["matcher_fzf"]
			}
		}
	}
MARK
let s:ddu_setting = s:ddu_setting->join('')->json_decode()
call ddu#custom#patch_global(s:ddu_setting)
" ... もうちょい込み入ったdduの設定...

まずはファイラー用のUIとしてddu-ui-filerをvimに読み込ませます。dduのグローバルの設定としてはjson形式で設定している箇所ですね。この状態でvimを起動し、 :call ddu#start({ 'ui' : 'filer' }) とやります。

するとこんな感じで全てのチャンネルがファイラーUIで表示されます。 ~/workspace/Plugin ってのは気にしないでください。この設定を記述しているディレクトリなのであんまし関係無いです。左上に表示されている channel_rec を見るとわかる通り、ここはグローバルの設定が適用されています。json形式でグローバルで設定していたものの、ui、uiParams、sources、...の中でuiをfilerとしてdduを起動したのでグローバルで設定していたui(ff)は使用されないといった感じです。sourcesは特に設定せずに起動したのでグローバルのままになっています。

channel_rec てのはファジーファインダー用のソースなのでチャンネル名がそのまま表示されています。ファイラー形式で全部のチャンネルがこの表記をされてもあまり嬉しくありません。そこで本プラグインが提供するファイラー用のソース channel を指定します。ここでもう一度手打ちで入力していくのもいいのですが、この channel ソースはちょっと面倒なので先にショートカットを設定してやります。

" ...先ほどまでの設定
nmap <Space>c <Cmd>call ddu#start({
			\ 'ui': 'filer',
			\ 'sources': [
			\	{'name': 'channel'}
			\ ],
			\ 'sourceOptions': {
			\	'channel': {
			\		'path': 'VtraQ'
			\	}
			\ },
			\ })<CR>

はい、こんな感じに設定してやります。これはノーマルモードで、スペースキーを押した後に c を押す(channelのcのつもり)みたいなイメージです。sourceとしてchannelを指定し、そのchannelソースの設定として pathVtraQ としていますね。この設定をしないとちゃんとチャンネルを集めてきてくれません。これは本家traQのチャンネルの管理方法からなる仕様です。本家†のチャンネルはツリー形式で管理されているのですが、全てのチャンネルの親チャンネルといったものが存在しないんですね。

...

↑こんな感じに管理されていて

...

みたくなってないんですね。そのため、一番親のチャンネルとしてひとまず VtraQ として設定してやります。ちなみにこの名前も後々...rec

とまぁ†の仕様っぽい事についてそれとなくわかったので、実際にノーマルモードで<Space>cとします。

はいこんな感じに表示されると思います。ツリー形式の一番親のチャンネルだけが表示されていますね。これぞ†ファイラー形式†って感じがしてテンション上がりますねぇ、僕は上がりました。

この状態になれば、もうこっちのもんです。ファジーファインダーでの設定よろしく同じような事をやっていきます。ただ今回はツリー表示されているので、それ独特のアクションを紹介します。それはツリー形式のチャンネルを展開するアクションですね(ディレクトリを開くみたいなイメージ)。

まず開きたいチャンネルにカーソルを合わせて :call ddu#ui#filer#do_action('expandItem') としてやりましょう、するとカーソル下のチャンネルが開きます。

こんな感じで#gps チャンネルの一個下のチャンネルが表示されました。う〜ん便利。ただこれだとどのチャンネルがどのチャンネルに属しているのかとても見づらいです。なので、†良い感じ†に表示してくれるようにそれ用のプラグインを導入します。こいつ何かする度にプラグイン導入してるな...とか思ったそこのあなた!!そうなんです、vimで何かしらを導入するにはプラグインをじゃんじゃん入れていく必要があるんですねぇ...。今回は説明のためプラグインの導入箇所の詳細説明をはしょっていますが、プラグインマネージャを使うと幸せになれませう。

さて御託は置いといて更なるプラグインを導入していきます。

" ...プラグインの導入
" ddu関連
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu-ui-ff
set rtp+=~/.cache/dein/nvim/repos/github.com/yuki-yano/ddu-filter-fzf
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu-ui-filer 
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu-column-filename "←ここを追加

" ↑ここまではプラグインをvimに認識させる処理です。
" 大抵はプラグインマネージャがよしなにやってくれる処理です。
"
" ↓ここからプラグインの設定を行います。

" ...ddcとかの設定

" dduの設定
let s:ddu_setting =<< trim MARK
	{
		"ui": "ff",
		"uiParams": {
			"ff": {
				"startFilter": true
			}
		},
		"sources": [
			{ "name": "channel_rec" }
		],
		"sourceOptions": {
			"channel": {
				"columns": ["filename"]  // ←ここを追加!!
			},
			"_": {
				"matchers": ["matcher_fzf"]
			}
		}
	}
MARK
let s:ddu_setting = s:ddu_setting->join('')->json_decode()
call ddu#custom#patch_global(s:ddu_setting)

" ...ショートカットとかの設定

はい、ddu-dolumn-filenameてプラグインを導入しました。本来ディレクトリとかファイル名をいい感じに表示するものなのですが、ツリー形式をシンプルな表示( + とか - とか)で表現するので、今回のようなファイルと全く関係ないチャンネルのツリー表記にもそれっぽく見えるので重宝しています。ちなみにですが、ファイル形式に特化した表現が欲しい!と言う方はddu-column-icon_filenameと言うのを導入すれば、適切なアイコンが表示されるので(NerdFontとか入れる必要あるかも)いい感じになります。

先ほどの expandItem をしてみるとこんな感じに表示されます。どこが誰の親なのか、子供はいるのかいないのか等が見やすく表示されておりいい感じですね。ちなみにツリー表示されていますが、チャンネルを開く時はファジーファインダーの時と同様に itemAction でopenってnameを設定してあげればOKです。

さて、これらの動作のショートカットを先ほどまでと同様に設定してあげます。ツリー形式で表示されているバッファの時だけ〜みたいな感じなので一緒ですね。慣れてきたと思うので、少し多めに設定してあげます。

" ...これまでの設定
" ↓ここから追記
autocmd FileType ddu-filer call s:ddu_filer_my_settings()

function! s:ddu_filer_my_settings() abort
	nnoremap <buffer><expr> <CR>
				\ ddu#ui#get_item()->get('isTree', v:false) ?
				\ "<Cmd>call ddu#ui#filer#do_action('expandItem', {'mode': 'toggle'})<CR>" :
				\ "<Cmd>call ddu#ui#filer#do_action('itemAction', {'name': 'open'})<CR>"
	nnoremap <buffer> <C-o>
				\ <Cmd>call ddu#ui#filer#do_action('itemAction', { 'name' : 'open', 'params' : { 'command' : 'split'} })<CR>
	nnoremap <buffer> <C-v>
				\ <Cmd>call ddu#ui#filer#do_action('itemAction', { 'name' : 'open', 'params' : { 'command' : 'vsplit'} })<CR>
	nnoremap <buffer> <C-t>
				\ <Cmd>call ddu#ui#filer#do_action('itemAction', { 'name' : 'open', 'params' : { 'command' : 'tabnew'} })<CR>
	nnoremap <buffer> <C-s>
				\ <Cmd>call ddu#ui#filer#do_action('toggleSelectItem')<CR>
	nnoremap <buffer> <C-q>
				\ <Cmd>call ddu#ui#filer#do_action('quit')<CR>
endfunction

はいこんな感じに設定してみました。filerとfilterがややこしいので注意してくださいね。今はfiler(ファイラー)の方を設定しています。だいたいはファジーファインダーと一緒なのですが、ツリー状のアイテムをエンターキーで開いたり閉じたり(toggle)できるようにしています。子供がいるかいないかを isTree で判定hしている感じですね。

あえ〜疲れました。日をまたいいで体力回復してるはずな事に加えて書く文章量も昨日より少ないってのにこれだからなぁ。困っちゃうわね。かくいうこれを見てちまちま少しづつ設定している方もお疲れ様です。これでだいたいの設定は終わりました。これでddc・dduを†完全に理解した†になったと思います!これ以降はおまけ的な設定を紹介しておきますね。せっかくメッセージを書きたいのにvimで日本語書くの面倒臭いぞ!どないしてくれんねん!てな方や、ワイはチャンネル探す時にそのチャンネルでどんな事が話されてるか知りたいんや!iOS版LINE限定のトークを長押しで既読つけずにプレビュー見れるやつ(自分Android勢なのでこの機能なくて泣いてる)やりたいんや!みたいな方には参考になるやもしれません。

あっそうそうせっかくなのでここまでの設定を全部終えたverを晒しておきますね。

" 本プラグイン関連
set rtp+=~/.cache/dein/nvim/repos/github.com/vim-denops/denops.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/kamecha/traqVimPractice

" ddc関連
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc-ui-native
set rtp+=~/.cache/dein/nvim/repos/github.com/tani/ddc-fuzzy
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddc-sorter_rank

" ddu関連
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu-ui-ff
set rtp+=~/.cache/dein/nvim/repos/github.com/yuki-yano/ddu-filter-fzf
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu-ui-filer 
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu-column-filename

" ↑ここまではプラグインをvimに認識させる処理です。
" 大抵はプラグインマネージャがよしなにやってくれる処理です。
"
" ↓ここからプラグインの設定を行います。

" ddc用の設定
let s:setting =<< trim MARK
{
	"ui": "native",
	"sourceOptions": {
		"stamp": {
			"matchers": ["matcher_fuzzy"],
			"sorters": ["sorter_rank"]
		}
	}
}
MARK
let s:setting = s:setting->join('')->json_decode()
call ddc#custom#patch_global(s:setting)
call ddc#custom#patch_filetype('traqvim-message', {
			\ "sources": ["stamp"],
			\ "specialBufferCompletion": v:true
			\ })
call ddc#enable()
" ...ddc等の設定

" dduの設定
let s:ddu_setting =<< trim MARK
	{
		"ui": "ff",
		"uiParams": {
			"ff": {
				"startFilter": true
			}
		},
		"sources": [
			{ "name": "channel_rec" }
		],
		"sourceOptions": {
			"channel": {
				"columns": ["filename"]
			},
			"_": {
				"matchers": ["matcher_fzf"]
			}
		}
	}
MARK
let s:ddu_setting = s:ddu_setting->join('')->json_decode()
call ddu#custom#patch_global(s:ddu_setting)

autocmd FileType ddu-ff call s:ddu_my_settings()

function! s:ddu_my_settings() abort
	setlocal cursorline
	nnoremap <buffer><silent> <CR>
				\ <Cmd>call ddu#ui#ff#do_action('itemAction', { 'name' : 'open' })<CR>
	nnoremap <buffer><silent> i
				\ <Cmd>call ddu#ui#ff#do_action('openFilterWindow')<CR>
endfunction

autocmd FileType ddu-ff-filter call s:ddu_filter_my_settings()

function! s:ddu_filter_my_settings() abort
	inoremap <buffer> <CR>
				\ <Cmd>call ddu#ui#ff#do_action('itemAction', { 'name' : 'open' })<CR>
	inoremap <buffer> <C-o>
				\ <Cmd>call ddu#ui#ff#do_action('itemAction', { 'name' : 'open', 'params' : { 'command' : 'split'} })<CR>
	inoremap <buffer> <C-v>
				\ <Cmd>call ddu#ui#ff#do_action('itemAction', { 'name' : 'open', 'params' : { 'command' : 'vsplit'} })<CR>
	inoremap <buffer> <C-t>
				\ <Cmd>call ddu#ui#ff#do_action('itemAction', { 'name' : 'open', 'params' : { 'command' : 'tabnew'} })<CR>
	inoremap <buffer> <C-s>
				\ <Cmd>call ddu#ui#ff#do_action('toggleSelectItem')<CR>
	inoremap <buffer> <C-n>
				\ <Cmd>call ddu#ui#ff#execute("call cursor(line('.')+1,0)<Bar>redraw")<CR>
	inoremap <buffer> <C-p>
				\ <Cmd>call ddu#ui#ff#execute("call cursor(line('.')-1,0)<Bar>redraw")<CR>
	inoremap <buffer> <C-q>
				\ <Cmd>call ddu#ui#ff#do_action('quit')<CR>
endfunction

nmap <Space>c <Cmd>call ddu#start({
			\ 'ui': 'filer',
			\ 'sources': [
			\	{'name': 'channel'}
			\ ],
			\ 'sourceOptions': {
			\	'channel': {
			\		'path': 'VtraQ'
			\	}
			\ },
			\ })<CR>

autocmd FileType ddu-filer call s:ddu_filer_my_settings()

function! s:ddu_filer_my_settings() abort
	nnoremap <buffer><expr> <CR>
				\ ddu#ui#get_item()->get('isTree', v:false) ?
				\ "<Cmd>call ddu#ui#filer#do_action('expandItem', {'mode': 'toggle'})<CR>" :
				\ "<Cmd>call ddu#ui#filer#do_action('itemAction', {'name': 'open'})<CR>"
	nnoremap <buffer> <C-o>
				\ <Cmd>call ddu#ui#filer#do_action('itemAction', { 'name' : 'open', 'params' : { 'command' : 'split'} })<CR>
	nnoremap <buffer> <C-v>
				\ <Cmd>call ddu#ui#filer#do_action('itemAction', { 'name' : 'open', 'params' : { 'command' : 'vsplit'} })<CR>
	nnoremap <buffer> <C-t>
				\ <Cmd>call ddu#ui#filer#do_action('itemAction', { 'name' : 'open', 'params' : { 'command' : 'tabnew'} })<CR>
	nnoremap <buffer> <C-s>
				\ <Cmd>call ddu#ui#filer#do_action('toggleSelectItem')<CR>
	nnoremap <buffer> <C-q>
				\ <Cmd>call ddu#ui#filer#do_action('quit')<CR>
endfunction

これ折りたたみ表記できたらいいなぁとか思ってたんですが、無理っぽいですね...う〜ん無念...

おまけ

さて「おまけ」です。小学生くらいの頃コロコロを買うとよくついてたやつですね。あのおまけでついてきたカレーパンの匂いがするデュエマのカードがインパクト強かったですね。(なお当方カードゲーム全然わからなくて、匂い嗅いですげ〜ってなってました。)それのこの記事verです。

ファジーファインダーでのプレビューの設定とそれっぽく表示

はいやっていきます。ここで注意しておきたいのは、プレビュー機能はvim・neovim両対応ですが、floatWindowでそれっぽく表示するのはneovim限定機能らしいと言う事ですね。どちらを使うかは各々の思想に委ねるとして今回は自分が使っているneovimに基づいて設定していきます。

あとあくまで†おまけ†なので一個づつ挙動を確認して〜と言うよりも、これが完成した設定です!(ババーンッ)みたくやっていきます。これで「おまけ」と言うていで諸々の面倒くささから解放されるんですね、いやぁ良い試みですな。


" ↑dduとかのプラグイン読み込み
" color scheme
set rtp+=~/.cache/dein/nvim/repos/github.com/sainnhe/edge
set termguicolors
let g:edge_style = 'aura'
colorscheme edge

" ↑↓プラグインの読み込み〜とか云々カンヌンの説明がき

" dduの設定
let s:ddu_setting =<< trim MARK
	{
		"ui": "ff",
		"uiParams": {
			"ff": {
				"startFilter": true,
				"prompt": "> ",
				"autoAction": { "name": "preview" },
				"split": "floating",
				"filterSplitDirection": "floating",
				"previewFloating": true,
				"floatingBorder": "rounded",
				"previewFloatingBorder": "rounded"
			}
		},
		"sources": [
			{ "name": "channel_rec" }
		],
		"sourceOptions": {
			"channel": {
				"columns": ["filename"]
			},
			"_": {
				"matchers": ["matcher_fzf"]
			}
		}
	}
MARK
let s:ddu_setting = s:ddu_setting->join('')->json_decode()
call ddu#custom#patch_global(s:ddu_setting)

function! s:set_size() abort
	let winCol = &columns / 8
	let winWidth = &columns - (&columns / 4)
	let winRow = &lines / 8
	let winHeight = &lines - (&lines / 4)
	" デフォルトのウィンドウは左上が始点
	let s:ddu_setting['uiParams']['ff']['winCol'] = winCol
	let s:ddu_setting['uiParams']['ff']['winRow'] = winRow
	let s:ddu_setting['uiParams']['ff']['winWidth'] = winWidth / 2
	let s:ddu_setting['uiParams']['ff']['winHeight'] = winHeight
	" プレビューウィンドウはかな~り本家の実装依存になってます
	" 今回の例だと左下が始点になってる
	" さらに枠線をroundedで設定しているのでその分を足し引きしてる(あんまし格好よくないな...)
	let s:ddu_setting['uiParams']['ff']['previewCol'] = winCol + winWidth / 2 + 2
	let s:ddu_setting['uiParams']['ff']['previewRow'] = winRow + winHeight + 5
	let s:ddu_setting['uiParams']['ff']['previewWidth'] = winWidth / 2
	let s:ddu_setting['uiParams']['ff']['previewHeight'] = winHeight + 3
	call ddu#custom#patch_global(s:ddu_setting)
endfunction

call s:set_size()

" autocmd File ...みたいなのが以下に続いてます。

さて、この設定はファジーファインダーのUIをneovimのfloatWindowで表示するようにしています。あとさらに、floatWindowの大きさ調整ですね。先ほどまでの設定の上から書いてるので、filterWindow内で<C-n><C-p>とかでカーソルを動かすとそれに伴いプレビューが表示されるようになっています。

それとカラースキーマの設定も行っています。デフォルトのfloatingWindowの色は見づらいのでね...

これらを設定するとこんな感じになります。

なんかそれっぽく表示されて格好いいですねぇ〜(今思うとカーソルの表示がちょっと分かりづらいな...)

ただこのプレビュー表示残念ながらまだ安定してないです...:awoo::gomen:。乗っかっている本家のプラグインと自作プラグインのどちらが影響してるのかとっても分かりづらいからですね。しかもバグ報告をしようにも、このプラグインをまともに使えるのは†erの民に限られてしまうので、本家作者様が再現できないという...参ったねこりゃ

日本語入力しようぜ!

ふぅ「おまけ」っぽく設定を書いてババーンって貼ってスクショも添えるだけなので、先ほどまでの説明がなくて書く量が減っていいですね。文章量が少なくて設定を添えるだけなので、vimの設定に慣れてる方はそっちの方が良かったりするのかな?

さてはてそんな事はさておきvimでの日本語入力ですね。メッセージを送信する時にスタンプの補完はされるのですが、あくまでこれはvimでの機能なので、IME(Google 日本語入力みたいなやつです)の日本語入力モードだと当然補完してくれません。さらにvimを使っている際にIMEをオンにしていると、ノーマルモードでの移動の際に jjjj 見たく表示されてとても鬱陶しいです。これを解決する方法が...あるんです!ちょっと奥さん聞いてってくださいな〜こりゃ必見だよぉ〜、です!

vimの外側からIMEを通して入力するのがダメなので、vimの中にIMEをプラグインとして導入してやろうと言うのがその思想です。これも詳しいことを解説しようとすると長くなりますし、自分もそこまで理解しているわけではないので厳しいです。とりあえず日本語入力にskkと言う方式を採用し、vimでskkができるプラグインを導入しようってことができればOKだと思います。

vimでskkができるプラグインは複数あるのですが、今回はskkeletonというものを使います。これもdenopsの上に乗っかっているので、今回の用途にいい感じに合いますね。

作者様の記事はこちら!

新しいVim用日本語入力プラグインを作った
" hogehoge
" skk(日本語入力)
set rtp+=~/.cache/dein/nvim/repos/github.com/vim-skk/skkeleton
" 辞書の設定
" 管理のしやすさのため、githubのページを参照している
" https://github.com/skk-dev/dict
call skkeleton#config({'globalJisyo': '~/.cache/dein/nvim/repos/github.com/skk-dev/dict/SKK-JISYO.L'})
" mapping
imap <C-j> <Plug>(skkeleton-enable)
cmap <C-j> <Plug>(skkeleton-enable)

" ↑ここまではプラグインをvimに認識させる処理です。
" 大抵はプラグインマネージャがよしなにやってくれる処理です。
"
" ↓ここからプラグインの設定を行います。


" ddc用の設定
let s:setting =<< trim MARK
{
	"ui": "native",
	"sourceOptions": {
		"skkeleton": {
			"mark": "skk",
			"matchers": ["skkeleton"],
			"sorters": [],
			"isVolatile": true,
			"minAutoCompleteLength": 0
		},
		"stamp": {
			"matchers": ["matcher_fuzzy"],
			"sorters": ["sorter_rank"]
		}
	}
}
MARK
let s:setting = s:setting->join('')->json_decode()
call ddc#custom#patch_global(s:setting)
call ddc#custom#patch_filetype('traqvim-message', {
			\ "sources": ["stamp"],
			\ "specialBufferCompletion": v:true
			\ })
call ddc#enable()

" skkeletonの設定
autocmd User skkeleton-enable-pre call s:skkeleton_pre()

function! s:skkeleton_pre() abort
	let s:prev_buffer_config = ddc#custom#get_buffer()
	call ddc#custom#patch_buffer({'sources': ['skkeleton']})
endfunction

autocmd User skkeleton-disable-pre call s:skkeleton_post()

function! s:skkeleton_post() abort
	" Restore sources
	call ddc#custom#set_buffer(s:prev_buffer_config)
endfunction

" hogehoge

こんな感じに設定してあげると↓見たくなります。skkでの入力方法の詳細は省きます。なんてったって「おまけ」ですから:kanzennirikaishita:。

他のファイラーソースと一体化させてみようぜ!

さて、次はdduのファイラーのいい感じの設定についてですね。先ほど説明したファイラーにいい感じにソースを追加していきます。本家traQでよくある未読チャンネルとか普段使うプロジェクトディレクトリとかソースに使うと良さそうですね。

やってみます。

" hoge
" ddu関連
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu.vim
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu-ui-ff
set rtp+=~/.cache/dein/nvim/repos/github.com/yuki-yano/ddu-filter-fzf
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu-ui-filer 
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu-column-filename
set rtp+=~/.cache/dein/nvim/repos/github.com/ryota2357/ddu-column-icon_filename "ここ追加
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu-source-file "ここ追加
set rtp+=~/.cache/dein/nvim/repos/github.com/Shougo/ddu-kind-file "ここ追加

" hoge
" dduの設定
let s:ddu_setting =<< trim MARK
	{
		"ui": "ff",
		"uiParams": {
			"ff": {
				"startFilter": true,
				"prompt": "> ",
				"autoAction": { "name": "preview" },
				"split": "floating",
				"filterSplitDirection": "floating",
				"previewFloating": true,
				"floatingBorder": "rounded",
				"previewFloatingBorder": "rounded"
			}
		},
		"sources": [
			{ "name": "channel_rec" }
		],
		"sourceOptions": {
			"file": {
				"columns": ["icon_filename"] // ここ!
			},
			"channel": {
				"columns": ["filename"]
			},
			"_": {
				"matchers": ["matcher_fzf"]
			}
		}
	}
MARK
let s:ddu_setting = s:ddu_setting->join('')->json_decode()
call ddu#custom#patch_global(s:ddu_setting)

"hoge
nmap <Space>c <Cmd>call ddu#start({
			\ 'ui': 'filer',
			\ 'uiParams': {
			\	'filer': {
			\		'split': 'vertical',
			\		'splitDirection': 'topleft',
			\	},
			\ },
			\ 'sources': [
			\	{'name': 'file'},
			\	{'name': 'channel_rec', 'params': {'type': 'unread'}},
			\	{'name': 'channel'},
			\ ],
			\ 'sourceOptions': {
			\	'channel_rec': {
			\		'path': 'unread'
			\	},
			\	'channel': {
			\		'path': 'VtraQ'
			\	},
			\ },
			\ })<CR>

"hoge

だいぶん力尽きてきてるのを感じますね。省略文字が hogeになっちゃってます。sourcesに突っ込まれたソースを順に表示しているのが分かりますね。普通のファイルを表示する時はそれ用のcolumnを設定してます。アイコンが表示されていていい感じですね。ここもちゃんとdo_actionを設定してあげれば、このアイコンから開いたりできます。

ちなみになのですが、sourceOptionsのchannel_recのパスにunreadって設定していますが、これはchannel_recのソース取得自体には関係なくて、単にchannel_recの隣にunreadって表示したいから指定しています(実際はファジーファインダー用のソースでパスに関係なく全部取ってくるため)。ツリー表示用ソースのchannelはちゃんとパスにVtraQって指定してあげなきゃだめです。

あとがき

あえ〜疲れました。未完成のプラグインの使い方を説明するなんてヤバイかもしれんな...などと書き終わった後に思い始めております。後はもう少しスクショを撮って掲載すればこのブログも完成しますね。あっそうだサムネも考えないと...

日をまたぎつつも、気が赴くままにつらつらと文章をしたためて、ろくに推敲してないので、色々ヤバそうな気配を感じております。まぁQiitaとかzennみたいな技術ブログではなくて、どんな話題でも何でもござれの†ブログだからこそなせる技...と思い込むことで気を確かに保ちます。

これまたどうでもいいことなんですが、この部内SNSを作ろうとしているぜ!って記事はもう一つ作っていたんです。ただこの記事は†お酒†を飲んだ勢いで書いちゃったということもあって、現在のこの記事よりもさらにヤバイことになっています。下書きで存在している書きかけのあの記事をどうしたものか...なんて事が少し気がかりですねぇ。†のwikiにひとまず投げてみるのもいいのかも。

さて、振り返ってみると自作プラグインこんな事できるよ!の説明のつもりがddc・dduの設定解説記事みたくなってますね...まぁこれをきっかけにddc・ddu、denopsの使用者が増えるといいなぁなんて思っています。

使い方の説明を主にして、開発の話を全然していなかった気がするので、話題にあげます。主な通信関連はdenopsの上に乗っかり、チャンネル選択とかの便利機能はddc・dduの上に乗っかるだけでそれっぽいものができそうなので、これらの開発者さん達すご〜という感想ですね。これからは簡単なものから一通り機能を追加して行って、あらかた追加できたらテスト環境を整えて〜みたいな事をしていきたいなと思っています。

ふと思ったのですが、npmのライブラリがあって、何かしらを集めて・絞り込んで・何かする、といったものは同じように開発できそうな気がしますね。slackやメールクライアントを作ってみるのも面白そう。

kamecha icon
この記事を書いた人
kamecha

全強志望の全弱 強強の人たちに囲まれるから自分も強くなるのでは?と勘違いした人です

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2021年5月19日
CPCTF2021を実現させたスコアサーバー
xxpoxx icon xxpoxx
2023年7月15日
2023 春ハッカソン 06班 stamProlog
H1rono_K icon H1rono_K
2022年9月26日
競プロしかシラン人間が web アプリ QK Judge を作った話
tqk icon tqk
2024年4月14日
Spotifyのクライアントを自作しよう
d_etteiu8383 icon d_etteiu8383
2024年3月15日
個人開発として2週間でWebサービスを作ってみた話 〜「LABEL」の紹介〜
Natsuki icon Natsuki
2023年9月13日
ブログリレーを支えるリマインダー
H1rono_K icon H1rono_K
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記