hotoolong's blog

プログラムのことやエンジニアリングに関することを記事にしています。

asyncomplete-tabnine.vim を neovim に対応してみた

概要

tabnineはAIによる入力補完をサポートしてくれるツールです。
vimでも利用できるのですが、デフォルトで使うと同期処理されてレスポンスが遅く使い勝手が悪いです。
非同期用としてYouCompleteMeやCoC、Deopleteが対象となっていますが、私は普段asyncomplete.vimで補完をしているため対象になってませんでした。
asyncomplete-tabnine.vim とうプラグインを作ってくださった方がおり、インストールしてみたのですが、NeoVimではjob_start関数がないとエラーになっていました。
そこで自分用にneovimで動作させてみました。

リポジトリ

github.com

インストールする場合はREADMEを確認してください。

変更点

https://github.com/kitagry/asyncomplete-tabnine.vim/compare/master...hotoolong:master

上記の差分が修正点になります。興味のある方は確認してみてください。

vimのjob_startはneovimではjobstart関数(ハイフンがない)で似たように処理をしています。
引数で渡すコールバック関数の引数の数が違っていたりするので、
vimとneovim両者に対応する場合はどちらのコールバック関数も作成する必要がありそうです。冗長な状態ですね。

jobでIOを確立した後にchannelを制御するのも vimではch_sendrawを使っているのに対してneovimではchansendを使っています。
このあたりもまだ理解が追いついてないのですがhelpを見ながらなんとか動かしてみたという状態です。

まとめ

以前からneovimで動作しないvim pluginを見かけては使いたかったけど仕様が無い気持ちでいました。
これで少しは便利に使えそうです。

ただ、以下のように

と両者の違いが吸収されていくのか少し期待していたのですが、
Shougoさんから

とあったので
ますます乖離していきそうです。
もともとvimからneovimに完全に乗り換えていたのはvimの動作がもっさりしていたのがきっかけですが、
neovimは独自のエディタとして進んでいきそうです。

最近のVimのバージョンでは新しい機能も増えて速度も随分と改善されているように感じます。
neovimの開発がvimを踏襲したものになってはいないのでこのままneovimを使い続けるべきかとても心動かされる内容となりました。
しばらくは併用して考えるかもしれないです。

参考にした記事

scrapbox.io

Nvim documentation: job_control

Nvim documentation: channel

nanasi.jp

thinca.hatenablog.com

vim-plug で プラグインの遅延ロードを行う

イントロ

最近neovimの起動が もっさり してきました。
プラグインマネージャーにはvim-plugを使っていまます。
こちらの環境で起動時間を改善してみました。

計測

nvim --startuptime ./startup.log

こちらのコマンドで起動時の処理時間が計測できます。

times in msec
 clock   self+sourced   self:  sourced script
 clock   elapsed:              other lines

000.093  000.093: --- NVIM STARTING ---
002.755  002.662: locale set
:
: (途中省略)
:
952.261  000.003: --- NVIM STARTED ---

左端が累計の時間、2番目が実行されている処理の時間です。

:%!sort -r -n -k 2

処理時間別に並び替えて重い処理を抽出できます。

プラグインの遅延ロード

vim-plugでは遅延ロードの種類が限定されています。
onとforをつかって遅延ロードさせるのが常套なのですが、
数秒後に読み込みのような設定はまだないようです。

タイマーで呼び出す

qiita.com

こちらの記事で取り上げられていて試してみたらとても良かったです。

以下のサンプルとしてlspの読み込みを遅延させています。

call plug#begin('~/.vim/plugged')

" .. 省略

Plug 'prabirshrestha/vim-lsp', { 'on': [] }
Plug 'mattn/vim-lsp-settings', { 'on': [] }

" .. 省略

call plug#end()

function! s:load_plug(timer)
    call plug#load(
                \ 'vim-lsp',
                \ 'vim-lsp-settings',
                \ )
endfunction
call timer_start(500, function("s:load_plug"))

Plugにonでから配列を設定することで読み込まなくなります。
timer_startで500ms後にs:load_plug関数を呼び出すようにしています。

plug#loadで遅延させる対象のprefix部分の文字列を省いて登録しておくと呼び出せます。
はじめ間違ってPlugに設定している文字列をそのまま渡してしまいエラーになってしまいました。
気をつけて!

計測時に遅くなっているものとあとからロードしても問題なさそうなプラグインを設定して再度計測してみると

952.261ms → 455.829ms になりました。

これでしばらくは快適に過ごせそうです。

参考URL

vim-plug/plug.txt at master · junegunn/vim-plug · GitHub

【vim-plug】InsertEnterイベントで遅延読み込み - Qiita

vim/neovimの起動時間・プラグイン読込時間を調べる - Qiita

neovimでpython3のエラーに遭遇

概要

結果としてはpython3のバージョンを上げてしまったのでエラーがでてたので対応したというものです。

起動時のエラー内容

ERROR: Failed to run healthcheck for "denite" plugin. Exception:
  function health#check[21]..health#denite#check[3]..<SNR>211_check_required_python[7]..denite#init#_python_version_check, line 8
  Vim(python3):E319: No "python3" provider found. Run ":checkhealth provider"

上記のような内容のエラーが出ていました。

詳細確認

:checkhealthしてみたところ

## Python 3 provider (optional)
  - WARNING: No Python executable found that can `import neovim`. Using the first available executable for diagnostics.
  - ERROR: Python provider error:
    - ADVICE:
      - provider/pythonx: Could not load Python 3:
          /usr/local/bin/python3 does not have the "neovim" module. :help |provider-python|
          python3.9 not found in search path or not executable.
          /usr/local/bin/python3.8 does not have the "neovim" module. :help |provider-python|
          python3.7 not found in search path or not executable.
          python3.6 not found in search path or not executable.
          python3.5 not found in search path or not executable.
          python3.4 not found in search path or not executable.
          python3.3 not found in search path or not executable.
          /usr/bin/python is Python 2.7 and cannot provide Python 3.
  - INFO: Executable: Not found

というメッセージが出ていたので help provider-pythonして確認すると

To use Python plugins, you need the "pynvim" module. Run |:checkhealth| to see
if you already have it (some package managers install the module with Nvim
itself).

For Python 3 plugins:
1. Make sure Python 3.4+ is available in your $PATH.
2. Install the module (try "python" if "python3" is missing): >
   python3 -m pip install --user --upgrade pynvim

解決方法

python3 -m pip install --user --upgrade pynvim

を実行して解決しました。 めでたし。

ghq list の 代替案

イントロ

普段 ghqとfzfを使ってリポジトリの切り替えをしています。
とても便利なのですが、最近、かなりもっさりしてきて遅いので改善を考えてみました。

改善方法

ボトルネックとしてはghq listがとても遅くなっていました。

簡易改善

ghq root 配下のディレクトリがそこまで多くない場合 git だけを対象とした改善方法をよく見かけました。

ghq list --vcs=git

こちらで改善する場合はこれだけで良さそうです。

今回実施した改善

findに置き換えて改善している記事を見たいのですが、私の場合は動作がもっさりしたままでした。
そこでRUST製のfdを使いことにしました。
バージョン管理がすべてgitで管理してるのでgitの特性から.gitファイルを探すことにしました。

ghq list

fd --type d --min-depth 2 --max-depth 4 --hidden --search-path $i '.git$' | sed -e "s/\/.git\$//"

に変更しています。

このコマンドでは .gitディレクトリを探して .git を削り一覧化しています。
--min-depth 2 --max-depth 4 と しているのは
ghq root を複数設定していてローカルに作成してるリポジトリディレクトリ構造を考慮しています。
利用される方はお好みに合わせて変えるのが良いかと思います。

今回の変更のdiffは以下の様になっています.

gd configs/fish/config.fish
diff --git configs/fish/config.fish configs/fish/config.fish
index 9f2755c..5659e0c 100644
--- configs/fish/config.fish
+++ configs/fish/config.fish
@@ -210,15 +210,20 @@ function fzf_select_ghq_repository
     set fzf_query --query "$query"
   end

-  set -l out (ghq list --vcs=git | \
+  set -l out (
+    for i in (ghq root -all)
+      fd --type d --min-depth 2 --max-depth 4 --hidden --search-path $i '.git$' | \
+      sed -e "s/\/.git\$//"
+    end | \
     fzf $fzf_query \
       --prompt='Select Repository >' \
-      --preview="echo {} | cut -d'/' -f 2- | xargs -I{} gh repo view {} ")
+      --preview="echo {} | awk -F'/' '{ print \$(NF-1)\"/\"\$NF}' | \
+    xargs -I{} gh repo view {}"
+  )
   [ $status != 0 ] && commandline -f repaint && return

   if test -n $out
-    set -l dir_path (ghq list --vcs=git --full-path --exact $out)
-    commandline "cd $dir_path"
+    commandline "cd $out"
     commandline -f execute
   end
 end

これでかなり速度が改善されていて快適になりました。
もっといい方法がありそうなので、何かあればコメントないしはDMなどでご連絡いただけると嬉しいです。

参考URL

scrapbox.io

autopp-tech.hatenablog.com

rake release で エラーがでた

イントロ

以前の記事でembed_callbacksを作成したと取り上げたのですが、
その中でRubyGemsでユーザ登録に失敗してたのですが、
サポートの人に対応していただきメールアドレスを変更していただきました。

これでやっと登録できるなと思って登録してみたのですが少しトラブルにあったのでこちらに記載しておきます。
ちなみに登録したgemのアドレスは以下になります。
これでgem install とインストール可能になるのでとてもいい気分です。

rubygems.org

遭遇したエラー

rake releaseしたときに既存のブランチとupstream branchが違うよと指摘された内容になります。

$ rake release
embed_callbacks 0.1.0 built to pkg/embed_callbacks-0.1.0.gem.
Tagged v0.1.0.
Untagging v0.1.0 due to error.
rake aborted!
Couldn't git push. `git push ' failed with the following output:

fatal: The upstream branch of your current branch does not match
the name of your current branch.  To push to the upstream branch
on the remote, use

    git push origin HEAD:trunk

To push to the branch of the same name on the remote, use

    git push origin HEAD

To choose either option permanently, see push.default in 'git help config'.

/Users/hotoolong/.rbenv/versions/2.7.0/bin/bundle:23:in `load'
/Users/hotoolong/.rbenv/versions/2.7.0/bin/bundle:23:in `<main>'
Tasks: TOP => release => release:source_control_push
(See full trace by running task with --trace)

git init してはじめに変更したブランチがtrunkでした。
BLM運動がきっかけてmasterはどうなのかという話がありdefault branch名をどうするか決めかねていたときに以下のコマンドでtrunkにしました。

git branch --set-upstream-to=origin/trunk

その後 github上でmainにしようという流れになっていた(以下のリンク)のでmainに変更していました。

GitHub - github/renaming: Guidance for changing the default branch name for GitHub repositories

git branch -m trunk main
git push -u origin main

ただ、upstreamの設定がそのままになっていたようです。

解決方法

git config -e

してtrunkをmainに置き換えてしまいました。 これで再度rake release するとうまくいきました。 よかったです。

embed_callbacksに機能追加した

概要

前回の記事embed_callbacksというgemを紹介しました。
こちらの機能を追加したので紹介していきます。

追加した機能

  • メソッドがエラーになったときに処理を呼び出せる
  • メソッドエラーになっても呼び出せる

rescue

これはその名の通りでrescueを設定するとエラー時に呼びされます。
コードを見てもらったほうがわかりやすいかと思うので下記にサンプルを記載しておきます。

require 'embed_callbacks'

class Sample
  include EmbedCallbacks
  set_callback :target, :rescue, :rescue_callback

  def target
    raise 'target'
  end

  def rescue_callback
    puts 'rescue_callback'
  end

end
sample = Sample.new
sample.target

#=> rescue_callback
#=> RuntimeError (target)

encure

こちらも同様にencureになります。
こちらはafterに近いかもしれないですが、
エラーが発生しても必ず呼び出されます。

require 'embed_callbacks'

class Sample
  include EmbedCallbacks
  set_callback :target, :ensure, :ensure_callback

  def target
    puts 'target'
  end

  def ensure_callback
    puts 'ensure_callback'
  end

end
sample = Sample.new
sample.target
#=> target
#=> ensure_callback

その他

RubyGemsへの登録

前回からの進捗はない状態ですが、
slackの#rubygemsで質問してみたところ、同様の問題質問があることを教えてもらいました。
Wrong emails when signup / Problems / Discussion Area - RubyGems.org Support
こちらを見ると SUPPORT STAFF で対応してもらい解決したようです。
一旦サポートにメールを送ってみました。
返信までは時間がかかるようなのでしばらく待ってみようと思います。

まとめ

callbackという感じではないですが、 rescue、ensureを後付で追加したいケースがあり作成してみました。
よかったらつかってみてください。

メソッドにcallbackを追加するgemをつくってみた

概要

以前からgemを作ってみたいと思いつつ作れてなかったので作成してみました。
methodにcallbackを追加するembed_callbacksというgemを作成しました。
探すと似たようなgemが出てくるので車輪の再発明的なものになりますが作成してみました.

gemの機能

まだそこまで機能はないのですが以下のようになります。

  • メソッドの前に処理を追加
  • メソッドの後に処理を追加
  • メソッドの前後に処理を追加

上記を簡単に呼び出せるようにしています。
今後更に機能は追加する予定です。詳細はGitHubを確認していただければと思います。

gemの作り方

参考記事

https://qiita.com/coe401_/items/8e90373a76f590ef0abe
しおいさんがQiitaで取り上げてくれた記事を参考にしました。
丁寧に説明してくれていてとてもいい記事ですね。

gemの命名

https://guides.rubygems.org/name-your-gem/
こちらの記事でどのような命名するとよいか記載されている内容を参考にしました。

gem searchコマンドもしくは https://rubygems.org/ から作成するgem名を検索しておき
同じ名前にならないようにしておくのがいいかと思います。
はじめに考えていた名前はすでに登録されていたので別名を考えていた今回の命名になりました。
命名は先行優位ですね。

困ったこと

RubyGemsへの登録

RubyGemsにユーザ登録時のメールアドレスを間違ってしまい、
確認メールをチェックできなくなってしまいました。
ログインはできるのですが確認メールをチェックしてください。というような文言で確認できない状況です。
メールアドレスは一度目の登録からは変更できないようなので、
とりあえずはRubyGemsには登録しないでしばらくGitHubだけで公開しておきます。
同様の問題で困っている人は少なそうですが、解決方法がわかる方いらっしゃいましたら情報いただければと思います。
どうしようもなくなったらhotoolong2みたいなのにして登録しようと思います。

gemの作成

参考記事にはbundle gem hogehoge などすると対話式にオプションを設定できるようなものもあったのですが、私の環境ではそうならなかったので引数などを設定しました。

$ bundle gem embed_callbacks --test=rspec --coc --mit
Creating gem 'embed_callbacks'...
MIT License enabled in config
Code of conduct enabled in config
      create  embed_callbacks/Gemfile
      create  embed_callbacks/lib/embed_callbacks.rb
      create  embed_callbacks/lib/embed_callbacks/version.rb
      create  embed_callbacks/embed_callbacks.gemspec
      create  embed_callbacks/Rakefile
      create  embed_callbacks/README.md
      create  embed_callbacks/bin/console
      create  embed_callbacks/bin/setup
      create  embed_callbacks/.gitignore
      create  embed_callbacks/.travis.yml
      create  embed_callbacks/.rspec
      create  embed_callbacks/spec/spec_helper.rb
      create  embed_callbacks/spec/embed_callbacks_spec.rb
      create  embed_callbacks/LICENSE.txt
      create  embed_callbacks/CODE_OF_CONDUCT.md
Initializing git repo in /Users/hotoolong/repositories/embed_callbacks
Gem 'embed_callbacks' was successfully created. For more information on making a RubyGem visit https://bundler.io/guides/creating_gem.html

まとめ

比較的簡単に作成まではいけそうなので、他にもgemにしていなかったものや放置してるものを徐々にupしていければいいかなと思っています。 なにか助言やPRなど私の困ったことへの解決の糸口になるものなどアドバイスいただければとてもありがたいです。

参考にした記事

はじめての自作gem 「Hello, Tama!」を出力してみる - Qiita
RubyGemはめっちゃ簡単に作れる! | 酒と涙とRubyとRailsと
https://qiita.com/yu-croco/items/a93693d02ca39ac38f78
RubyGemsの作り方 - Qiita
Bundler: How to create a Ruby gem with Bundler
Name your gem - RubyGems Guides
GitHubリポジトリのREADMEの良さそうな書き方・テンプレート - karaage. [からあげ]
個人gemのCIをほぼ全部Travis CIからGitHub Actionsに移行した - くりにっき
GitHub Actions がローンチされたのでマイグレートしてる - HsbtDiary(2019-08-11)
GitHub - actions/setup-ruby: Set up your GitHub Actions workflow with a specific version of Ruby
今の時代だからこそオススメする車輪の再発明|erukiti