Modularizing the config
Let’s first show you an extension for Yi numbers with boilerplate in the yi.hs file, then modularize the code and factor out some components
Extending Vim numbers
Just to show you how easy it is to configure Yi, consider the following case.
You want to modify the behavior of <C-a>
in Vim by making the cursor stay at the same location, while still incrementing the number. You went ahead and looked at the source code for <C-a>
which gave you getCountE >>= withCurrentBuffer . incrementNextNumberByB
. Using this, you can simple use function composition, to add savingPointB
which saves the cursor location and makes sure the cursor moves back to that location after the function is executed. In our case, the function incrementNextNumberByB
is what gets executed, and that’s the function responsible for moving the cursor.
{-# LANGUAGE OverloadedStrings #-}
import Yi
import qualified Yi.Keymap.Vim as V2
import qualified Yi.Keymap.Vim.Common as V2
import qualified Yi.Keymap.Vim.Utils as V2
import Yi.Keymap.Vim.StateUtils (getCountE)
main = yi $ myConfig
myConfig = defaultVimConfig { defaultKm = myKeymap }
myKeymap = v2KeymapSet $ myBindings
myBindings :: (V2.EventString -> EditorM ()) -> [V2.VimBinding]
myBindings eval =
[ nmap "\\<C-a>" ((getCountE >>= withCurrentBuffer . savingPointB . incrementNextNumberByB) :: EditorM ())
, nmap "\\<C-x>" ((getCountE >>= withCurrentBuffer . savingPointB . incrementNextNumberByB . negate) :: EditorM ())
]
-- Boilerplate begins here
v2KeymapSet :: ((V2.EventString -> EditorM ()) -> [V2.VimBinding]) -> KeymapSet
v2KeymapSet myBindings = V2.mkKeymapSet $ V2.defVimConfig `override` \super this ->
let eval = V2.pureEval this
in super {
V2.vimBindings = myBindings eval ++ V2.vimBindings super
}
nmap x y = V2.mkStringBindingE V2.Normal V2.Drop (x, y, id)
-- Boilerplate ends here
Removing Boilerplate
The boilerplate code mentioned above looks kinda ugly in the Yi config, and won’t be useful unless you understand the eDSL thoroughly. So let’s try and eliminate it.
Basically, we want to modularize our config by moving the boilerplate to a different file. You can do this by simply adding your file to the .config/yi/lib
directory.
Include the following in .config/yi/lib/Yi/Custom/Helper.hs
.
-- File Helper.hs
{-# LANGUAGE OverloadedStrings #-}
module Yi.Custom.Helper
( v2KeymapSet
, nmap
, imap
, nmap'
, leader
)
where
import Data.Monoid (mappend)
import Yi
import qualified Yi.Keymap.Vim as V2
import qualified Yi.Keymap.Vim.Common as V2
import qualified Yi.Keymap.Vim.Utils as V2
import qualified Data.Text as T
v2KeymapSet :: ((V2.EventString -> EditorM ()) -> [V2.VimBinding]) -> KeymapSet
v2KeymapSet myBindings = V2.mkKeymapSet $ V2.defVimConfig `override` \super this ->
let eval = V2.pureEval this
in super {
V2.vimBindings = myBindings eval ++ V2.vimBindings super
}
nmap x y = V2.mkStringBindingE V2.Normal V2.Drop (x, y, id)
imap x y = V2.VimBindingE (\evs state -> case V2.vsMode state of
V2.Insert _ ->
fmap (const (y >> return V2.Continue))
(evs `V2.matchesString` x)
_ -> V2.NoMatch)
nmap' x y = V2.mkStringBindingY V2.Normal (x, y, id)
leader :: V2.EventString -> V2.EventString
leader = mappend "\\"
Then, you can simply use the following Yi config:
{-# LANGUAGE OverloadedStrings #-}
import Yi
import qualified Yi.Keymap.Vim as V2
import qualified Yi.Keymap.Vim.Common as V2
import qualified Yi.Keymap.Vim.Utils as V2
import Yi.Custom.Helper (v2KeymapSet, nmap, imap, nmap')
import Yi.Keymap.Vim.StateUtils (getCountE)
main = yi $ myConfig
myConfig = defaultVimConfig { defaultKm = myKeymap }
myKeymap = v2KeymapSet $ myBindings
myBindings :: (V2.EventString -> EditorM ()) -> [V2.VimBinding]
myBindings eval =
[ nmap (leader "<C-a>") ((getCountE >>= withCurrentBuffer . savingPointB . incrementNextNumberByB) :: EditorM ())
, nmap (leader "<C-x>") ((getCountE >>= withCurrentBuffer . savingPointB . incrementNextNumberByB . negate) :: EditorM ())
]
And that’s it. It looks much cleaner now.