Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
RubyでNeoVim Pluginを作る技術
Search
fujitani sora
February 19, 2025
110
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
RubyでNeoVim Pluginを作る技術
fujitani sora
February 19, 2025
More Decks by fujitani sora
See All by fujitani sora
TypeScript * xx 一年目の振り返り
fujitanisroa0414
0
220
Terminal IDE の世界
fujitanisroa0414
1
300
toridori DevRel 立ち上げ約半年の成果と今年の目標
fujitanisroa0414
0
320
NestJS GraphQL開発を支える 自作CodeGenerator
fujitanisroa0414
0
61
なぜtoridori開発部はDevRelに取り組むのか
fujitanisroa0414
1
120
AWS SDK for PHPによる AmazonECS構成管理
fujitanisroa0414
2
520
ARR成長の為に開発者個人で出来る事
fujitanisroa0414
0
270
育成ゲームとしてのvim(桃太郎)
fujitanisroa0414
0
78
書籍紹介 「採算に乗る事業」の仕組みづくり
fujitanisroa0414
0
100
Featured
See All Featured
Designing for humans not robots
tammielis
254
26k
KATA
mclloyd
PRO
35
15k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
2k
Building the Perfect Custom Keyboard
takai
2
790
What Being in a Rock Band Can Teach Us About Real World SEO
427marketing
0
250
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
230
The SEO Collaboration Effect
kristinabergwall1
1
480
Into the Great Unknown - MozCon
thekraken
41
2.6k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
122
22k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
730
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
570
VelocityConf: Rendering Performance Case Studies
addyosmani
333
25k
Transcript
RubyでNeoVim Pluginを作る技術 Shibuya.rb[:2025021] fujitani sora
名前 • 藤谷 想楽(fujitani srra) • @_fs0414 • 2001 (23)
所属 • toridori.inc • プロダクト領域開発統括部 tips 今年に入ってからふりかけにはまっています 自己紹介
2022 12月19日 東証グロース市場 に 上場 2016 2017 2019 10月 インフルエンサー広告
プラットフォームシステム アップロント を展開 株式会社 アップロント 設立 2020 8月 インフルエンサー マネジメント事務所 OTOZURE 設立 株式会社 コラボテクノ ロジー に社名変更 インフルエンサー広告 プラットフォームシステム コラボマーケ ティング & コラボベース をリリース 渋谷 オフィス に移転 株式会社 トリドリ に社名変更 各サービス toridori シリーズ へ名称変更 2021 7月 株式会社 GIVIN と事業統合 インフルエンサーブランド 立ち上げ支援 toridori made 始動 10月 10月 9月 6月 沿革 2023 12月 株式会社 OverFlow と事業統合
6つの事業を通して インフルエンサーマーケティング を展開しています ※グループ会社
@toridori_tech(X) @toridori tech blog(Zenn) 会社のNote(Note)
2/5にshibuya.ts第一回の主催をしました。 同じ地域の技術コミュニティ同士、仲良くしていただけると嬉しいです!!
Abstract • NeoVimは一つの起動につき「インスタンス」という単位で管理される(以降 NeoVimインスタンス) • NeoVimインスタンスから外部プロセスのコードを実行して結果を受け取る、RemotePluginという仕組みが用意されている ◦ RemotePluginとして定義したコードをProcedureとしてRPC Requestを実行、結果をNeoVimインスタンスで受け取れる •
RemotePluginは複数言語での記述に対応しており、Ruby言語もサポートされている • neovimというGemからNeoVim classをloadでき、簡単にRemote Procedureとしての関数を実装できる • Rubygemsからneovimというgemが公開されており、これを使ってRuby側の実装を行う • gemの内部実装を見ていくと、DSLクラスの実装によって「Neovimインスタンスからのみ必要なメソッドを実行できる。 それ以外の手段ではraiseする。」という仕組みがある • そんな仕組みを使って実装したrb-rescue-sender.rbはこんな感じ
Out Of Scope • RemotePluginの詳細 ◦ Rubyのイベントなのでそっちをメインで
demo
RemotePlugin 特定のパスに配置したコードをProcedureとして、NeoVimインスタンスからRPC Requestを介して実行する機能。 Vimの設定に使用されることの多いLuaやVimScript以外の言語を使用してNeoVimのPluginを記述できる。 サポート言語の例 Go, TypeScript, Rust, Python, Ruby,
Swift … Rubyも使える。今日はこれの話
RemotePluginのコードが実行されるまで ~ RPC Requestの生成 使用する言語名をhostとし、/rplugin/{host}/ 配下に配置されたコードを実行する。 今回の言語はrubyなので/rplugin/ruby/ になる 配置するだけではNeoVimインスタンスからの実行はできず、RPC Request用のcallを
生成する:UpdateRemotePluginsコマンドを実行する必要がある /rplugin/ruby/ 配下に配置された.rbがRemotePluginとして作成される。 UpdateRemotePluginsコマンドの実行で、manifest = RPCのcallがplugin.vimファイルに生成される
RemotePluginのコードが実行されるまで ~ 実行環境 RemotePluginもコードの実行環境が必要であり、Providerという機能でそれを提供している。 Rubyの場合はgem install neovimで導入でき、neovim-ruby-hostファイルで管理される。 中身はただの実行ファイル。 実行環境が正しく設定されているかの確認は、NeoVimでの checkhealth
ruby で確認できる 実行ファイル名はGemの設定ファイルで 指定されている
RemotePluginのコードが実行されるまで ~ 実行環境 ここまでの手順を踏むことで、RemotePluginのコードをNeoVimインスタンス内から実行可能になる 1. /rplugin/{host}/ にProcedureとして動かしたい関数を配置する 2. NeoVimからUpdateRemotePluginsコマンドを実行して、RPCのcallを生成する 3.
Providerを設定するためのライブラリをインストール(checkhealthで確認)
Neovim Gem Provider設定や、class Neovimのロードに使用する。 内部実装は基本的にRubyであり、NeoVimの情報を扱う為の関数が実装されている。
rb-rescue-sender demoで見せたPluginの実装。 行番号と記述されたコードを取得し、定義したテンプレートに代入することでコード生成を再現しています。 ここからのコードの話は当スライドの実装を引用しているので適宜ここを読みに戻ってきてください。
Neovim.plugin pluginはNeovim moduleのクラスメソッドとして実行されているように見えるが、内部実装は文字列をraiseしているだけ。 だが、RemotePluginの実装ではブロックとして機能している。 🤔🤔 https://github.com/neovim/neovim-ruby/blob/main/lib/neovim.rb
Neovim.plugin # Placeholder method for exposing the remote plugin DSL.
This gets # temporarily overwritten in +Host::Loader#load+. Neovim.pluginは公開用のPlaceholder methodであり、RemotePluginのロード時にHost::Loader#loadに上書きされている。 どこでロードされているのか、内部実装を読み進めていく。
Neovim::Host::CLI /exe/neovim-ruby-host に配置される、 前に紹介したneovim-ruby-hostのProviderから実行されるエントリポイント。 CLI classの内部実装。 ConnectionやEventLoopの初期化を行うと同時に、Hostに関する処理を行っている。 https://github.com/neovim/neovim-ruby/blob/main/exe/neovim-ruby-host#L5 https://github.com/neovim/neovim-ruby/blob/main/lib/neovim/host/cli.rb#L10
Neovim::Host.run クラスメソッドとして定義されるrunで、Loader classを初期化。 インスタンスに対してloadメソッドを実行している。 > # temporarily overwritten in +Host::Loader#load+.
👀👀
Neovim::Host::Loader Neovim.pluginのコメントアウトで言及されていたコードの実態。 Plugin.from_config_blockを実行しており、結果をKernel.loadで読み込んでいる。 Plugin.from_config_block内にはDSLクラスの初期化コードが存在し、 override_plugin_methodとして実行することでNeovim::Pluginのメソッド実行を Neovim::Plugin::DSLに上書きする。
Neovim::Plugin.from_config_block require “neovim/plugin/dsl” でDSL classをロードし、Neovim::Host::Loader#loadで実行されたfrom_config_blockで初期化している。 クラスメソッド内でのtapとインスタンス生成により、form_config_blockの返却値にはPluginクラスのインスタンスが変える、DSLクラスの初期化に もループの分だけPluginクラスのインスタンスが取れる仕組み。 https://github.com/neovim/neovim-ruby/blob/main/lib/neovim/plugin.rb#L9
Neovim::Plugin::DSL DSLとして実行されるメソッドの実態。 ブロック内でcommandメソッドを実装することで、 RemotePluginから実行されるコマンド名を設定できる。 今回の場合はRescueSend。
nvim.get_current_buf.lines[(range_start - 1)..(range_end - 1)] • get_current_buf ◦ 現在アクティブなバッファ(fileのこと)を取得 •
range_start, range_endでバッファの選択範囲の行データを取得
nvim.get_current_buf.lines[range_start - 1] • get_current_buf.lines[range_start - 1] ◦ 選択範囲の一つ上の行データを取得
first_selected_line.match(/^\s*/)[0] • first_selected_lineで取得した行のインデントを取得 ◦ matchと正規表現で文頭の空白文字列を抽出 • base_indent + " "
◦ 式を書き込む行の深いインデントに使用する
rescue_block • 取得したインデント幅を使用して定型分を用意し、バッファから読み込んだコードを書き込む ◦ 最終的にバッファに書き込まれるデータを配列として変数に代入する
buf.set_lines(range_start - 1, range_end, true, []) • バッファを再度取得し、選択範囲に[]で空白を書き込む ◦ やっていることは選択行の削除
buf.set_lines(range_start - 1, range_start - 1, true, rescue_block) • 一つ前で消した行に、rescue_blockの配列を書き込む
◦ 行とインデントが揃ってバッファにコードが生成される
まとめ • 記法がシンプルで今後も使えそう • Gem自体のメンテが心配
引用 • https://neovim.io/doc/user/remote_plugin.html • https://github.com/neovim/neovim-ruby • https://www.rubydoc.info/github/neovim/neovim-ruby/main/Neovim/Plugin/DSL • https://zenn.dev/soramarjr/scraps/55214fb33aff7a
phperkaigi2025で話すよ 明後日からのRails Girls Tokyo、コーチ枠で参加するよ 弊社オフィスでゆめみさんとTSのイベントするよ Compassコミュニティ作ったよ。二回目の企画を考え中