LoginSignup
2
1

More than 5 years have passed since last update.

YiでEditorM aの状態を変更したい時はMonadEditor使えば

Last updated at Posted at 2016-10-29

YiでEditorM aの状態を変更したい時はMonadEditor使えば

 最近はYiコントリビューターになるために、.vimrcのキーマッピングをyi.hsに再実装している。
stackageのlts-7.4にあるYiはまだドキュメントが充実していないことがわかってきた。

(ただしHaddockにあるHaskellの型シグネチャと実装読みによってだいたいは補える)

Yi布教を兼ねてメモしていく。

YiでEditorM aの状態を変更したい時は、最悪MonadEditor m => m a使えばいい

YiのVimインターフェース
(まだこれに対する正確な用語を僕は持たないので、
Yi.Keymap.Vim以下の関数やdefaultVimConfigなどによって提供される
VimライクなYi環境をVimインターフェースと呼称している)

で設定する際に困惑したのが、

  • ウィンドウを閉じる
  • バッファを破棄する
  • バッファの指すファイルを保存する

といった操作を扱う型にYiM a, EditorM aという2つの型があったこと。

これは多分…YiMがYi全体を覆う、状態と操作の、全ての包含であることに対して

EditorMはVimインターフェースでいう、現在がNormalモードであるとか、バッファをいくつ持っているかどれを持っているか…だとかいう
まさにエディタとしての状態をStateで持っているのかなと思う(そして操作もStateによって提供されているのかな)。

  • YiM
    • ReaderT Yi IO aのnewtype
  • EditorM
    • ReaderT Config (State Editor) aのnewtype

MonadEditorについて

YiMEditorMは両方MonadEditorインスタンスとして定義されているので、このimplBindingのようなアドホック多相な関数を作れる。

-- The keymapping implementor for both of V.VimBindingY and V.VimBindingE
implBinding :: MonadEditor m => V.EventString -> m () -> V.EventString -> V.VimState -> V.MatchResult (m V.RepeatToken)
implBinding key context = \key' state ->
  case V.vsMode state of
    V.Insert _ -> (const $ context >> return V.Continue) <$> key' `V.matchesString` key
    _          -> V.NoMatch

これを使って、Vimインターフェースのキーマッピングを表す型であるVimBindingを得る。

inoremapEinoremapYは、EventStringと、YiM ()またはEditorM ()を使ってVimBindingを作る関数。

2つの関数に分岐してしまっているのは、VimBinding型の値構築子の定義の関係。

-- Like inoremap of Vim from V.EventString for EditorM
-- for ∀a. V.Insert a
inoremapE :: V.EventString -> EditorM () -> V.VimBinding
inoremapE key x = V.VimBindingE $ implBinding key x

-- Like inoremap of Vim from V.EventString for YiM
-- for ∀a. V.Insert a
inoremapY :: V.EventString -> YiM () -> V.VimBinding
inoremapY key x = V.VimBindingY $ implBinding key x

本題: MonadEditorを使って、問題を解決する

 僕は、.vimrcにも定義してある、日常的に使っている
「Insertモードで、カレントバッファの指すファイルを保存した後にNormalモードに戻る」というキーマッピングを定義しようとした。

僕はyiWrite, switchModeE Normalという関数を見つけた。

  • viWrite :: YiM ()
    • カレントバッファの指すファイルを保存する
  • switchModeE :: VimMode -> EditorM ()
    • 現在のVimインターフェースのモード(VimState型のvsMode)を変更する
  • data VimMode = Normal | Insert Char | ...

でもこれらの関数は型が違うので、同時に使用できない。

以下の要点と関数に注目して、switchModeY :: VimMode -> YiM ()を定義することで解決した。

  • MonadEditorインスタンスは必ずMonadStateである
  • getEditorDyn :: (MonadEditor m, YiVariable a) => m a (いくつかの定義を省略している)
  • putEditorDyn :: (MonadEditor m, YiVariable a) => a -> m () (いくつかの定義を省略している)
  • instance YiVariable VimState

  • => YiMMonadStateとして使えばVimStateを変更できる

これを書いた。

escVimInsWithSave = inoremapY' "<C-k><CR>" (viWrite >> switchModeY V.Normal)  -- Yi interpret <C-j> as <CR>
  where
    switchModeY :: V.VimMode -> YiM ()
    switchModeY mode = getEditorDyn >>= \s -> putEditorDyn s { V.vsMode = mode }

あとはこれをキーマッピングに追加すればいいだけだっ!

解決。
ヨッシャ。

結論

VimStateMonadEditorで使えば、Vimインターフェースの状態についてはなんとかなりそう。

余談: EventとEventStringについて

僕がYiの型を巡回して最初に迷ったのは、キーマッピングをされる対象のキーを表す型が、EventEventStringの2つあったことで、

これはEventStringYi.Keymap.EventUtils以下に定義されていることから、
EventStringはVimインターフェースのために用いられるものなのかな」と考えている。

以下の関数によって相互に変換ができる


この記事はYiによって書かれた。

lts-7.4のYiにはまだMarkdownのためのModeがない。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1