neovimのリモートプラグイン開発入門してみた


最近は仕事でcmakeを扱うことが多いので、neovimプラグインの開発を勉強しながら、neovimからcmakeを扱いやすくするプラグインを作ってみたいと思います。

今回作ったもののリポジトリはこちら

https://github.com/honeytrap15/vim-cmake-proj

## 環境

* mac
* nvim 0.2.0
* python3

## 準備

プラグインの管理はShougoさんのdein.vimを使用します。

インストール方法はググると大量に出てくるのでそちらを参考に。

目標は

* tomlに記述してdeinでpluginをインストールできるようにする
* neovim上からcmakeを実行してMakefileを生成する

* neovim上からcmake .. && make を実行してバイナリを生成する

という感じにしたいと思います。

どうやって開発してんの的なのはあまり書いてる人がいないので、効率の良い方法があればコメントください。


## gitリポジトリを作る

プラグインのgitリポジトリを作ります。
プラグインにするための構成は決まっているので以下のような構成にします。

```zsh
% tree nvim-cmake-proj
nvim-cmake-proj
├── README.md
└── rplugin
    └── python3
        └── nvim-cmake-proj
            └── __init__.py
```

\_\_init__.pyにプラグインの実装をしていきます。

一旦は、読み込んでコマンドが実行できるか試したいので以下のようなファイルを作成しました。


```__init__.py
# -*- coding: utf-8 -*-
#
# File: __init__
# Date: 2017-12-13
# 

import neovim

@neovim.plugin
class NvimCMakeProj(object):

    def __init__(self, nvim):
        self.nvim = nvim

    def echo(self, msg):
        self.nvim.command("echo '" + msg + "'")

    @neovim.command('CMakeReload')
    def cmake_reload(self):
        self.echo("test")
```

## 動作を確認する

runtimepathにプラグインのディレクトリを追加するか、runtimepathに記述してあるディレクトリに作成したプラグインを配置します。

追加後にnvimを開いて

:UpdateRemotePlugins

と実行します。

すると、

remote/host: python3 host registered plugins ['deoplete', 'nvim-cmake-proj'] 

と表示されるので、一度nvimを終了して、再度nvimを開くと:CMakeReloadが実行できるようになっており、testと表示されるようになっています。

## CMakeReload, CMakeBuildを実装する

コマンドの中はpython3のコードで実装できるので、python3の実装で書くことができます。

カレントディレクトリのCMakeLists.txtからMakefileを生成するコマンドとmakeを実行するコマンドを実装します。

```__init__.py
# -*- coding: utf-8 -*-

import neovim
import os

from subprocess import  Popen, PIPE

@neovim.plugin
class NvimCMakeProj(object):

    BUILD_DIR = 'vim-cmake-build'
    CMAKE_CMD = 'cmake ..'
    MAKE_CMD = 'make'

    def __init__(self, nvim):
        self.nvim = nvim

    def echo(self, msg):
        self.nvim.command("echo '" + msg + "'")

    def _is_cmake_project(self):
        cur_dir = os.path.abspath(".")
        return os.path.exists(os.path.join(cur_dir, 'CMakeLists.txt'))

    def _has_build_dir(self):
        cur_dir = os.path.abspath(".")
        return os.path.exists(os.path.join(cur_dir, self.BUILD_DIR))

    def _run_cmake(self):
        os.chdir('vim-cmake-build')
        with Popen(self.CMAKE_CMD, stdout=PIPE, shell=True) as proc:
            ret = proc.stdout.read()
            self.echo(ret.decode('utf-8'))
        os.chdir('..')

    @neovim.command('CMakeReload')
    def cmake_reload(self):
        if self._is_cmake_project():
            if not self._has_build_dir():
                os.mkdir(self.BUILD_DIR)
            self._run_cmake()
            return True
        else:
            self.echo('CMakeLists.txt is not exists.')
            return False


    @neovim.command('CMakeBuild')
    def cmake_build(self):
        if self.cmake_reload():
            os.chdir('vim-cmake-build')
            with Popen(self.MAKE_CMD, stdout=PIPE, shell=True) as proc:
                ret = proc.stdout.read()
                self.echo(ret.decode('utf-8'))
            os.chdir('..')

```

ファイルの存在チェックなどもしてますが、単純にvim-cmake-buildに移動してcmake ..するのとmakeを実行するだけです。

実装は出力先にneovimモジュールを使うところとneovimのデコレータがついているところ以外は普通のpython3のスクリプトになっているかと思います。

cmakeもmakeもオプションが色々あるので、その辺の扱いをどうするかが課題。。

とりあえずは、初めてなので単純にvimからshellコマンドを実行する的な簡単なものを作ってみましたが、慣れてきたらもっと複雑なのを作ってみたいなー。