diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2014-2021 itchyny <https://github.com/itchyny>
+Copyright (c) 2014-2025 itchyny
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,253 @@
+# miv
+[![CI Status](https://github.com/itchyny/miv/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/itchyny/miv/actions?query=branch:main)
+[![Hackage](https://img.shields.io/hackage/v/miv.svg)](https://hackage.haskell.org/package/miv)
+[![Release](https://img.shields.io/github/release/itchyny/miv/all.svg)](https://github.com/itchyny/miv/releases)
+[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/itchyny/miv/blob/main/LICENSE)
+
+### Vim plugin manager written in Haskell
+The `miv` is a command line tool for managing Vim plugins with a single YAML
+configuration file. The motivation of this tool is
+
+- to generate a Vim plugin loader files in Vim script
+  - A plugin manager written in Vim script build script code and evaluates it
+    on editor startup. But the executed commands do not change unless the
+    user's configurations do not change. Instead of building the commands on
+    editor startup, `miv` generates static plugin loader scripts after plugin
+    installation.
+- to provide a declarative way to manage Vim plugins
+  - Various loading triggers, script configurations and loading dependency can
+    be defined.
+- to provide a command line tool which is friendly to interact with other tools
+  - You can easily update the Vim plugins in cron schedule or from shell script.
+
+## Installation
+### Homebrew
+```sh
+brew install itchyny/tap/miv
+```
+
+### Build with stack
+```sh
+stack install miv
+```
+
+## User guide
+1. Add the miv plugin path to `runtimepath` in your `.vimrc`.
+2. Create miv configuration file at `~/.vimrc.yaml`. (or `~/.vim/.vimrc.yaml`, `$XDG_CONFIG_HOME/miv/config.yaml`)
+3. Execute `miv install`.
+
+Example vimrc:
+```vim
+filetype off
+if has('vim_starting')
+  set rtp^=~/.vim/miv/miv
+  " or when you set $XDG_DATA_HOME,
+  " set rtp^=$XDG_DATA_HOME/miv/miv
+endif
+filetype plugin indent on
+```
+
+Example miv configuration file (refer to [.vimrc.yaml](https://github.com/itchyny/dotfiles/blob/main/.vimrc.yaml) for how the author configures):
+```yaml
+plugin:
+
+  Align:
+    command: Align
+
+  itchyny/lightline.vim:
+    before: |
+      let g:lightline = {
+            \   'colorscheme': 'wombat',
+            \ }
+
+  itchyny/calendar.vim:
+    mapleader: ","
+    command: Calendar
+    mapping: <Plug>(calendar)
+    script: |
+      nmap <Leader>z <Plug>(calendar)
+    before: |
+      let g:calendar_views = [ 'year', 'month', 'day_3', 'clock' ]
+
+  prabirshrestha/vim-lsp:
+    before: |
+      let g:lsp_async_completion = 1
+      let g:lsp_text_edit_enabled = 0
+      let g:lsp_signs_enabled = 0
+      augroup lsp_install
+        autocmd!
+        autocmd User lsp_buffer_enabled setlocal omnifunc=lsp#complete
+      augroup END
+
+  prabirshrestha/asyncomplete.vim: {}
+
+  prabirshrestha/asyncomplete-lsp.vim:
+    dependon: asyncomplete
+
+  prabirshrestha/asyncomplete-buffer.vim:
+    dependon: asyncomplete
+    after: |
+      call asyncomplete#register_source(asyncomplete#sources#buffer#get_source_options({
+          \ 'name': 'buffer',
+          \ 'whitelist': ['*'],
+          \ 'completor': function('asyncomplete#sources#buffer#completor'),
+          \ 'config': {
+          \    'max_buffer_size': 100000,
+          \  },
+          \ }))
+
+  prabirshrestha/asyncomplete-file.vim:
+    dependon: asyncomplete
+    after: |
+      call asyncomplete#register_source(asyncomplete#sources#file#get_source_options({
+          \ 'name': 'file',
+          \ 'whitelist': ['*'],
+          \ 'completor': function('asyncomplete#sources#file#completor'),
+          \ }))
+
+  mattn/vim-lsp-settings:
+    dependon: lsp
+
+  mattn/emmet-vim:
+    filetype:
+      - html
+      - css
+    before: |
+      let g:user_emmet_settings = { 'indentation' : '  ' }
+    after: |
+      autocmd FileType html,css imap <buffer> <tab> <plug>(emmet-expand-abbr)
+
+  elzr/vim-json:
+    filetype: json
+
+  cespare/vim-toml:
+    filetype: toml
+
+  groenewege/vim-less:
+    filetype: less
+
+  tpope/vim-haml:
+    filetype: haml
+
+  jade.vim:
+    filetype: jade
+
+  kana/vim-textobj-user: {}
+
+  kana/vim-textobj-entire:
+    dependon: textobj-user
+    mapmode:
+      - o
+      - v
+    mapping:
+      - <Plug>(textobj-entire-a)
+      - <Plug>(textobj-entire-i)
+      - ie
+      - ae
+
+  kana/vim-textobj-line:
+    dependon: textobj-user
+    mapmode:
+      - o
+      - v
+    mapping:
+      - <Plug>(textobj-line-a)
+      - <Plug>(textobj-line-i)
+      - il
+      - al
+
+before: |
+  let g:is_bash = 1
+  let g:loaded_2html_plugin = 1
+  let g:loaded_rrhelper = 1
+
+after: |
+  let g:mapleader = ','
+
+filetype:
+  vim: |
+    setlocal foldmethod=marker
+  css: |
+    setlocal iskeyword=37,45,48-57,95,a-z,A-Z,192-255
+  make: |
+    setlocal noexpandtab
+  sh: |
+    setlocal iskeyword=36,45,48-57,64,95,a-z,A-Z,192-255
+```
+
+## `miv` subcommands
+The `miv` command has the following subcommands.
+
+|command|description|
+|:--|:--|
+|`install`|Installs all the plugins.|
+|`update`|Updates the plugins (outdated plugins are skipped).|
+|`update!`|Updates all the plugins.|
+|`update [plugins]`|Updates the specified plugins.|
+|`clean`|Removes unused directories and files.|
+|`generate`|Generates the miv plugin files. (`miv install` and `miv update` automatically do this task)|
+|`ftdetect`|Gather ftdetect scripts. (`miv install` and `miv update` automatically do this task)|
+|`helptags`|Generates the helptags file. (`miv install` and `miv update` automatically do this task)|
+|`list`|Lists all the plugins.|
+|`edit`|Edits the miv config file.|
+|`command`|Lists the subcommands of `miv`.|
+|`path [plugins]`|Prints the paths of the plugins.|
+|`each [commands]`|Executes the commands each directory of the plugins. For example, you can execute `miv each pwd` or `miv each git gc`.|
+|`help`|Shows the help of `miv`.|
+|`version`|Shows the version of `miv`.|
+
+Commands to execute when you want to
+
+|do what|command|
+|:--|:--|
+|install a new plugin|`miv edit`, update the configuration file, save, exit the editor and `miv install`|
+|update the installed plugins but skip outdated plugins|`miv update`|
+|update all the installed plugins|`miv update!`|
+|update specific plugins|`miv update [plugin1] [plugin2]..`|
+|uninstall a plugin|`miv edit`, remove the related configurations, `miv generate` (and `miv clean` if you want)|
+|list all the plugins|`miv list`|
+|count the number of plugins|<code>miv list &vert; wc -l</code>|
+|change the current working directory to a plugin directory|`cd "$(miv path [plugin])"`|
+|want a help|`miv help`|
+
+## Plugin configuration
+### Loading triggers for the plugin
+|key|type|description|
+|:--|:--|:--|
+|`filetype`|<code>string &vert; string[]</code>|load the plugin on setting the filetype|
+|`command`|<code>string &vert; string[]</code>|load the plugin on invoking the command|
+|`function`|<code>string &vert; string[]</code>|load the plugin on calling a function matching the value in regex|
+|`mapping`|<code>string &vert; string[]</code>|load the plugin on the mapping|
+|`mapmode`|<code>'n' &vert; 'v' &vert; 'x' &vert; 's' &vert; 'i' &vert; 'c' &vert; 'l' &vert; 'o' &vert; 't'</code>|specify the `map-modes` for the `mapping` configuration|
+|`cmdline`|<code>':' &vert; '/' &vert; '?' &vert; '@'</code>|the command-line character to load the plugin|
+|`insert`|`boolean`|load the plugin on entering the insert mode for the first time|
+
+### Configurations for the plugin
+|key|type|description|
+|:--|:--|:--|
+|`script`|`string`|script run on startup, specify some configurations or mappings to load the plugin|
+|`after`|`string`|script run after the plugin is loaded|
+|`before`|`string`|script run just before the plugin is loaded|
+|`mapleader`|`string`|the `mapleader` (`<Leader>`) for the `script`|
+
+### Dependency configurations
+|key|type|description|
+|:--|:--|:--|
+|`dependon`|<code>string &vert; string[]</code>|plugins on which the plugin depends; they are loaded just before the plugin is loaded|
+|`dependedby`|<code>string &vert; string[]</code>|(deprecated in favor of `loadafter`) plugins loaded just after the plugin is loaded|
+|`loadbefore`|<code>string &vert; string[]</code>|indicates lazy loading, the plugin is loaded just before any of the configured plugins|
+|`loadafter`|<code>string &vert; string[]</code>|indicates lazy loading, the plugin is loaded just after any of the configured plugins|
+
+### Other miscellaneous configurations
+|key|type|description|
+|:--|:--|:--|
+|`enable`|`string`|enable the plugin when the expression (in Vim script) is truthy|
+|`submodule`|`boolean`|pull the submodules of the repository|
+|`build`|`string`|build shell script to execute after installing and updating|
+|`sync`|`boolean`|skip pulling the repository if the value is `false`|
+
+## Author
+itchyny (<https://github.com/itchyny>)
+
+## License
+This software is released under the MIT License, see LICENSE.
diff --git a/_miv b/_miv
new file mode 100644
--- /dev/null
+++ b/_miv
@@ -0,0 +1,26 @@
+#compdef miv
+
+_miv()
+{
+  local -i ret=1
+
+  _arguments ': :->cmds' '*:: :->args' && ret=0
+
+  case $state in
+    (cmds)
+      local -a commands=("${(*)${(f)$(miv command)}[@]/ ##/:}")
+      _describe -t commands 'command' commands && ret=0
+      ;;
+    (args)
+      case $words[1] in
+        (install|update|path)
+          local -a args=("${words[@]:1}")
+          local -a plugins=("${${${(f)$(miv list)}[@]%% *}[@]:|args}")
+          _describe -t plugins 'plugin' plugins && ret=0
+          ;;
+      esac
+      ;;
+  esac
+
+  return ret
+}
diff --git a/miv.cabal b/miv.cabal
--- a/miv.cabal
+++ b/miv.cabal
@@ -1,67 +1,72 @@
+cabal-version:          3.0
 name:                   miv
-version:                0.4.8
-author:                 itchyny <https://github.com/itchyny>
-maintainer:             itchyny <https://github.com/itchyny>
-license:                MIT
-license-file:           LICENSE
+version:                0.4.9
 category:               Compiler
-build-type:             Simple
-cabal-version:          >=1.10
 synopsis:               Vim plugin manager written in Haskell
 description:            The miv command is a cli tool to manage Vim plugins.
+author:                 itchyny <itchyny@cybozu.co.jp>
+maintainer:             itchyny <itchyny@cybozu.co.jp>
+homepage:               https://github.com/itchyny/miv
+bug-reports:            https://github.com/itchyny/miv/issues
+license:                MIT
+license-file:           LICENSE
+extra-source-files:     README.md _miv
 
+common base
+  default-language:     GHC2021
+  default-extensions:   BlockArguments
+                        LambdaCase
+                        NoFieldSelectors
+                        OverloadedRecordDot
+  build-depends:        base >= 4.18 && < 5
+  ghc-options:          -Wdefault -Wall -Wunused-packages
+
 executable miv
+  import:               base
+  default-extensions:   OverloadedStrings
+                        RecordWildCards
+  ghc-options:          -threaded
   hs-source-dirs:       src
   main-is:              Main.hs
-  ghc-options:          -threaded -Wall
-  default-language:     Haskell2010
   other-modules:        Plugin
-                      , Setting
-                      , Mode
-                      , Command
-                      , Cmdline
-                      , Mapping
-                      , ShowText
-                      , VimScript
-                      , Git
-                      , Paths_miv
-  build-depends:        base >= 4.9 && < 5
-                      , ghc-prim
-                      , process
-                      , async
-                      , concurrent-output
-                      , SafeSemaphore
-                      , time
-                      , directory
-                      , containers
-                      , HsYAML
-                      , bytestring
-                      , text
-                      , unordered-containers
-                      , monad-parallel
-                      , filepath
-                      , filepattern
-                      , unix-compat
-                      , xdg-basedir
+                        Setting
+                        Mode
+                        Command
+                        Cmdline
+                        Mapping
+                        VimScript
+                        Git
+                        Paths_miv
+  autogen-modules:      Paths_miv
+  build-depends:        HsYAML >= 0.2 && < 0.3
+                      , MissingH >= 1.6 && < 1.7
+                      , SafeSemaphore >= 0.10 && < 0.11
+                      , async >= 2.2 && < 2.3
+                      , bytestring >= 0.11 && < 0.13
+                      , concurrent-output >= 1.10 && < 1.11
+                      , containers >= 0.6 && < 0.8
+                      , data-default >= 0.7 && < 0.9
+                      , directory >= 1.3 && < 1.4
+                      , extra >= 1.7 && < 1.9
+                      , filepath >= 1.4 && < 1.6
+                      , filepattern >= 0.1 && < 0.2
+                      , monad-parallel >= 0.8 && < 0.9
+                      , process >= 1.6 && < 1.7
+                      , text >= 2.0 && < 2.2
+                      , text-builder-linear >= 0.1 && < 0.2
+                      , text-display >= 1.0 && < 1.1
+                      , time >= 1.12 && < 1.15
+                      , unix-compat >= 0.7 && < 0.8
+                      , xdg-basedir >= 0.2 && < 0.3
 
 test-suite spec
+  import:               base
+  type:                 exitcode-stdio-1.0
   hs-source-dirs:       test
   main-is:              Spec.hs
-  type:                 exitcode-stdio-1.0
-  default-language:     Haskell2010
-  build-depends:        base >= 4.9 && < 5
-                      , ghc-prim
-                      , process
-                      , time
-                      , directory
-                      , containers
-                      , hspec
-                      , HsYAML
-                      , bytestring
-                      , text
-                      , unordered-containers
-                      , monad-parallel
+  build-depends:        hspec
+  ghc-options:          -Wno-unused-packages
 
 source-repository head
   type:     git
-  location: git@github.com:itchyny/miv.git
+  location: https://github.com/itchyny/miv.git
diff --git a/src/Cmdline.hs b/src/Cmdline.hs
--- a/src/Cmdline.hs
+++ b/src/Cmdline.hs
@@ -1,23 +1,22 @@
-{-# LANGUAGE BlockArguments, LambdaCase, OverloadedStrings #-}
 module Cmdline where
 
 import Data.Text (Text, unpack)
+import Data.Text.Builder.Linear qualified as Builder
+import Data.Text.Display (Display(..))
 import Data.YAML
-import Prelude hiding (show)
 
-import ShowText
-
 data Cmdline = CmdlineExCommand
              | CmdlineForwardSearch
              | CmdlineBackwardSearch
              | CmdlineInput
              deriving (Eq, Ord)
 
-instance ShowText Cmdline where
-  show CmdlineExCommand      = ":"
-  show CmdlineForwardSearch  = "/"
-  show CmdlineBackwardSearch = "?"
-  show CmdlineInput          = "@"
+instance Display Cmdline where
+  displayBuilder = Builder.fromText . \case
+    CmdlineExCommand      -> ":"
+    CmdlineForwardSearch  -> "/"
+    CmdlineBackwardSearch -> "?"
+    CmdlineInput          -> "@"
 
 instance FromYAML Cmdline where
   parseYAML = withStr "!!str" \case
diff --git a/src/Command.hs b/src/Command.hs
--- a/src/Command.hs
+++ b/src/Command.hs
@@ -1,56 +1,63 @@
-{-# LANGUAGE OverloadedStrings #-}
 module Command where
 
-import qualified Data.Text as T
-import Data.Text (Text, unwords)
-import Prelude hiding (show, unwords)
-import ShowText
-
-data CmdBang = CmdBang
-             | CmdNoBang
-             deriving Eq
-
-instance ShowText CmdBang where
-  show CmdBang = "-bang"
-  show CmdNoBang = ""
-
-data CmdBar = CmdBar
-            | CmdNoBar
-            deriving Eq
-
-instance ShowText CmdBar where
-  show CmdBar = "-bar"
-  show CmdNoBar = ""
-
-data CmdRegister = CmdRegister
-                 | CmdNoRegister
-                 deriving Eq
+import Data.Default (Default(..))
+import Data.Text (Text, null, unwords)
+import Data.Text.Builder.Linear qualified as Builder
+import Data.Text.Display (Display(..), display)
+import Prelude hiding (null, unwords)
 
-instance ShowText CmdRegister where
-  show CmdRegister = "-register"
-  show CmdNoRegister = ""
+data Command =
+  Command {
+    name     :: Text,
+    repl     :: Text,
+    bang     :: Bool,
+    bar      :: Bool,
+    register :: Bool,
+    buffer   :: Bool,
+    range    :: Maybe CmdRange,
+    arg      :: CmdArg,
+    complete :: Maybe CmdComplete
+  } deriving Eq
 
-data CmdBuffer = CmdBuffer
-               | CmdNoBuffer
-               deriving Eq
+instance Display Command where
+  displayBuilder cmd =
+    Builder.fromText $ unwords $ filter (not . null)
+        [ "command!",
+          if cmd.bang then "-bang" else "",
+          if cmd.bar then "-bar" else "",
+          if cmd.register then "-register" else "",
+          if cmd.buffer then "-buffer" else "",
+          maybe "" display cmd.range,
+          display cmd.arg,
+          maybe "" display cmd.complete,
+          cmd.name,
+          cmd.repl ]
 
-instance ShowText CmdBuffer where
-  show CmdBuffer = "-buffer"
-  show CmdNoBuffer = ""
+instance Default Command where
+  def = Command {
+    name     = "",
+    repl     = "",
+    bang     = True,
+    bar      = False,
+    register = False,
+    buffer   = False,
+    range    = Just CmdRange,
+    arg      = CmdNonNegArg,
+    complete = Nothing
+  }
 
 data CmdRange = CmdRange
               | CmdRangeWhole
               | CmdRangeN Int
               | CmdRangeCount Int
-              | CmdNoRange
               deriving Eq
 
-instance ShowText CmdRange where
-  show CmdRange = "-range"
-  show CmdRangeWhole = "-range=%"
-  show (CmdRangeN n) = "-range=" <> show n
-  show (CmdRangeCount n) = "-count=" <> show n
-  show CmdNoRange = ""
+instance Display CmdRange where
+  displayBuilder = Builder.fromText . \case
+    CmdRange        -> "-range"
+    CmdRangeWhole   -> "-range=%"
+    CmdRangeN n     -> "-range=" <> display n
+    CmdRangeCount n -> "-count=" <> display n
 
 data CmdArg = CmdNonNegArg
             | CmdZeroOneArg
@@ -59,56 +66,17 @@
             | CmdNoArg
             deriving Eq
 
-instance ShowText CmdArg where
-  show CmdNonNegArg = "-nargs=*"
-  show CmdZeroOneArg = "-nargs=?"
-  show CmdPositiveArg = "-nargs=+"
-  show CmdOneArg = "-nargs=1"
-  show CmdNoArg = "-nargs=0"
-
-data CmdComplete = CmdComplete Text
-                 deriving Eq
-
-instance ShowText CmdComplete where
-  show (CmdComplete "") = ""
-  show (CmdComplete complete) = "-complete=" <> complete
-
-data Command =
-     Command { cmdName     :: Text
-             , cmdRepText  :: Text
-             , cmdBang     :: CmdBang
-             , cmdBar      :: CmdBar
-             , cmdRegister :: CmdRegister
-             , cmdBuffer   :: CmdBuffer
-             , cmdRange    :: CmdRange
-             , cmdArg      :: CmdArg
-             , cmdComplete :: CmdComplete
-     } deriving Eq
-
-instance ShowText Command where
-  show cmd = unwords (filter (not . T.null)
-           [ "command!"
-           , show (cmdBang cmd)
-           , show (cmdBar cmd)
-           , show (cmdRegister cmd)
-           , show (cmdBuffer cmd)
-           , show (cmdRange cmd)
-           , show (cmdArg cmd)
-           , show (cmdComplete cmd)
-           , cmdName cmd
-           , cmdRepText cmd
-           ])
+instance Display CmdArg where
+  displayBuilder = Builder.fromText . \case
+    CmdNonNegArg   -> "-nargs=*"
+    CmdZeroOneArg  -> "-nargs=?"
+    CmdPositiveArg -> "-nargs=+"
+    CmdOneArg      -> "-nargs=1"
+    CmdNoArg       -> "-nargs=0"
 
-defaultCommand :: Command
-defaultCommand
- = Command { cmdName     = ""
-           , cmdRepText  = ""
-           , cmdBang     = CmdBang
-           , cmdBar      = CmdNoBar
-           , cmdRegister = CmdNoRegister
-           , cmdBuffer   = CmdNoBuffer
-           , cmdRange    = CmdRange
-           , cmdArg      = CmdNonNegArg
-           , cmdComplete = CmdComplete ""
- }
+newtype CmdComplete = CmdComplete Text
+                    deriving Eq
 
+instance Display CmdComplete where
+  displayBuilder (CmdComplete complete) =
+    Builder.fromText $ "-complete=" <> complete
diff --git a/src/Git.hs b/src/Git.hs
--- a/src/Git.hs
+++ b/src/Git.hs
@@ -11,7 +11,7 @@
 
 import Data.List (isPrefixOf)
 import System.Exit (ExitCode(..))
-import System.Process (system, readProcess)
+import System.Process (readProcess, system)
 
 clone :: String -> FilePath -> String
 clone repo path = unwords ["git", "clone", gitUrl repo, singleQuote path]
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -1,26 +1,25 @@
-{-# LANGUAGE BlockArguments, OverloadedStrings, ScopedTypeVariables #-}
 module Main where
 
-import Control.Applicative
-import Control.Concurrent (threadDelay, newEmptyMVar, forkIO, putMVar, takeMVar)
+import Control.Concurrent (forkIO, newEmptyMVar, putMVar, takeMVar, threadDelay)
 import Control.Concurrent.Async
-import qualified Control.Concurrent.MSem as MSem
-import Control.Exception
-import Control.Monad (filterM, forM_, unless, void, when, guard)
-import qualified Control.Monad.Parallel as P
-import qualified Data.ByteString.Lazy as BS
+import Control.Concurrent.MSem qualified as MSem
+import Control.Exception (tryJust)
+import Control.Monad (filterM, forM_, guard, unless, void, when)
+import Control.Monad.Parallel qualified as P
+import Data.ByteString.Lazy qualified as BS
 import Data.Functor ((<&>))
 import Data.List (foldl', isPrefixOf, nub, sort, (\\))
-import Data.Maybe (listToMaybe, fromMaybe, isNothing)
-import Data.Text (Text, unlines, pack, unpack)
-import qualified Data.Text as T
-import Data.Text.IO (putStrLn, putStr, writeFile, hGetContents)
+import Data.Maybe (fromMaybe, isNothing, listToMaybe)
+import Data.Text (Text, pack, unlines, unpack)
+import Data.Text qualified as T
+import Data.Text.Builder.Linear qualified as Builder
+import Data.Text.Display (Display(..), display)
+import Data.Text.IO (hGetContents, putStr, putStrLn, writeFile)
 import Data.Time (getZonedTime)
+import Data.Tuple.Utils (fst3, snd3)
 import Data.Version (showVersion)
-import qualified Data.YAML as YAML
+import Data.YAML qualified as YAML
 import GHC.Conc (getNumProcessors, setNumCapabilities)
-import Prelude hiding (readFile, writeFile, unlines, putStrLn, putStr, show)
-import System.Console.Concurrent ()
 import System.Console.Regions
 import System.Directory
 import System.Environment (getArgs)
@@ -28,16 +27,17 @@
 import System.Exit (ExitCode(..))
 import System.FilePath ((</>))
 import System.FilePattern.Directory (getDirectoryFiles)
-import System.IO (openFile, IOMode(..), hClose, hFlush, stdout, stderr, hGetLine, hPutStrLn)
-import System.IO.Error (isDoesNotExistError, tryIOError, isEOFError)
+import System.IO (IOMode(..), hClose, hFlush, hGetLine, hPutStrLn, openFile,
+                  stderr, stdout)
+import System.IO.Error (isDoesNotExistError, isEOFError, tryIOError)
 import System.PosixCompat.Files (setFileTimes)
 import System.Process
+import Prelude hiding (putStr, putStrLn, readFile, unlines, writeFile)
 
 import Git
 import Paths_miv (version)
 import Plugin
 import Setting
-import ShowText
 import VimScript hiding (singleQuote)
 
 nameversion :: Text
@@ -45,55 +45,53 @@
 
 expandHomeDirectory :: FilePath -> IO FilePath
 expandHomeDirectory ('~':'/':path) = getHomeDirectory <&> (</> path)
-expandHomeDirectory path = return path
+expandHomeDirectory path           = return path
 
 getSettingFileCandidates :: IO [FilePath]
 getSettingFileCandidates = do
   xdgConfig <- getUserConfigFile "miv" "config.yaml"
   mapM expandHomeDirectory
-    ([ xdgConfig
-     , "~/.vimrc.yaml"
-     , "~/.vim/.vimrc.yaml"
-     , "~/vimrc.yaml"
-     , "~/.vim/vimrc.yaml"
-     , "~/_vimrc.yaml"
-     , "~/.vim/_vimrc.yaml"
-     , "~/_vim/_vimrc.yaml"
-     , "~/vimfiles/.vimrc.yaml"
-     , "~/vimfiles/vimrc.yaml"
-     , "~/vimfiles/_vimrc.yaml"
-     ])
+    [ xdgConfig
+    , "~/.vimrc.yaml"
+    , "~/.vim/.vimrc.yaml"
+    , "~/vimrc.yaml"
+    , "~/.vim/vimrc.yaml"
+    , "~/_vimrc.yaml"
+    , "~/.vim/_vimrc.yaml"
+    , "~/_vim/_vimrc.yaml"
+    , "~/vimfiles/.vimrc.yaml"
+    , "~/vimfiles/vimrc.yaml"
+    , "~/vimfiles/_vimrc.yaml"
+    ]
 
-getFirstExistingFile :: IO [FilePath] -> IO (Maybe FilePath)
-getFirstExistingFile fs = fmap listToMaybe $ filterM doesFileExist =<< fs
+getFirstExistingFile :: [FilePath] -> IO (Maybe FilePath)
+getFirstExistingFile = fmap listToMaybe . filterM doesFileExist
 
 getSettingFile :: IO (Maybe FilePath)
-getSettingFile = getFirstExistingFile $ getSettingFileCandidates
+getSettingFile = getFirstExistingFile =<< getSettingFileCandidates
 
 getSetting :: IO Setting
 getSetting = do
   candidates <- getSettingFileCandidates
-  maybeFile <- getFirstExistingFile $ return candidates
-  case maybeFile of
-       Just file -> do
-         cnt <- BS.readFile file
-         case YAML.decode1 cnt of
-              Right setting -> return setting
-              Left (pos, err) -> error $ YAML.prettyPosWithSource pos cnt err
-       Nothing -> error $ unpack $ unlines $
-         "No setting file! Tried following locations:" :
-         map (\x -> "  - " <> pack x) candidates
+  getFirstExistingFile candidates >>= \case
+    Just file -> do
+      cnt <- BS.readFile file
+      case YAML.decode1 cnt of
+           Right setting   -> return setting
+           Left (pos, err) -> error $ YAML.prettyPosWithSource pos cnt err
+    Nothing -> error $ unpack $ unlines $
+      "No setting file! Tried following locations:" :
+      map (\x -> "  - " <> pack x) candidates
 
 pluginDirectory :: IO FilePath
 pluginDirectory = do
-  defaultDir <- expandHomeDirectory "~/.vim/miv"
-  xdgDir <- getUserDataDir "miv"
-  x <- return xdgDir <||> doesDirectoryExist
-  y <- return defaultDir <||> doesDirectoryExist
-  z <- expandHomeDirectory "~/vimfiles/miv" <||> doesDirectoryExist
-  return $ fromMaybe defaultDir $ x <|> y <|> z
-    where (<||>) :: IO a -> (a -> IO Bool) -> IO (Maybe a)
-          (<||>) f g = f >>= \x -> g x >>= \b -> return $ if b then Just x else Nothing
+  candidates <- sequence [
+    getUserDataDir "miv",
+    expandHomeDirectory "~/.vim/miv",
+    expandHomeDirectory "~/vimfiles/miv" ]
+  filterM doesDirectoryExist candidates >>= \case
+    (path:_) -> return path
+    [] -> expandHomeDirectory "~/.vim/miv"
 
 createPluginDirectory :: IO ()
 createPluginDirectory =
@@ -103,13 +101,15 @@
 printUsage = mapM_ putStrLn usage
 
 commandHelp :: IO ()
-commandHelp = mapM_ (putStrLn . show) arguments
+commandHelp = mapM_ (putStrLn . display) arguments
 
-data Argument = Argument (Text, Text)
-              deriving (Eq, Ord)
-instance ShowText Argument where
-  show (Argument (x, y)) = x <> T.replicate (10 - T.length x) " " <> y
+newtype Argument = Argument (Text, Text)
+                 deriving (Eq, Ord)
 
+instance Display Argument where
+  displayBuilder (Argument (x, y)) = Builder.fromText $
+    x <> T.replicate (10 - T.length x) " " <> y
+
 arguments :: [Argument]
 arguments = map Argument
           [ ("install" , "Installs the uninstalled plugins.")
@@ -134,7 +134,7 @@
         , ""
         , "Commands:"
         ]
-     <> map (T.append "  " . show) arguments
+     <> map (T.append "  " . display) arguments
      <> [ ""
         , "You can install the plugins by the following command:"
         , "  miv install"
@@ -165,7 +165,7 @@
 levenshtein :: Eq a => [a] -> [a] -> Int
 levenshtein a b = last $ foldl' f [0..length a] b
   where
-    f [] _ = []
+    f [] _         = []
     f xs@(x:xs') c = scanl (g c) (x + 1) (zip3 a xs xs')
     g c z (d, x, y) = minimum [y + 1, z + 1, x + fromEnum (c /= d)]
 
@@ -178,7 +178,7 @@
       containedcommands = [y | y@(Argument (x, _)) <- arguments, length arg > 1 && arg `isContainedIn` unpack x]
   putStrLn $ "Unknown command: " <> pack arg
   putStrLn "Probably:"
-  mapM_ (putStrLn . ("  "<>) . show) (if null prefixcommands then nub (containedcommands <> mincommands) else prefixcommands)
+  mapM_ (putStrLn . ("  "<>) . display) (if null prefixcommands then nub (containedcommands <> mincommands) else prefixcommands)
 
 isContainedIn :: Eq a => [a] -> [a] -> Bool
 isContainedIn xxs@(x:xs) (y:ys) = x == y && xs `isContainedIn` ys || xxs `isContainedIn` ys
@@ -192,25 +192,27 @@
       minplugins = [y | (x, y) <- distplugins, x == mindist]
   putStrLn $ "Unknown plugin: " <> pack arg
   putStrLn "Probably:"
-  mapM_ (putStrLn . ("  "<>) . show) minplugins
+  mapM_ (putStrLn . ("  "<>) . display) minplugins
 
 data Update = Install | Update deriving Eq
-instance ShowText Update where
-  show Install = "installing"
-  show Update = "updating"
 
-data UpdateStatus
-   = UpdateStatus {
-       installed :: [Plugin],
-       updated :: [Plugin],
-       nosync :: [Plugin],
-       outdated :: [Plugin],
-       failed :: [Plugin]
-   }
+instance Display Update where
+  displayBuilder = Builder.fromText . \case
+    Install -> "install"
+    Update  -> "update"
 
+data UpdateStatus =
+  UpdateStatus {
+    installed :: [Plugin],
+    updated   :: [Plugin],
+    nosync    :: [Plugin],
+    outdated  :: [Plugin],
+    failed    :: [Plugin]
+  }
+
 instance Semigroup UpdateStatus where
-  UpdateStatus i u n o f <> UpdateStatus i' u' n' o' f'
-    = UpdateStatus (i <> i') (u <> u') (n <> n') (o <> o') (f <> f')
+  UpdateStatus i u n o f <> UpdateStatus i' u' n' o' f' =
+    UpdateStatus (i <> i') (u <> u') (n <> n') (o <> o') (f <> f')
 
 instance Monoid UpdateStatus where
   mempty = UpdateStatus [] [] [] [] []
@@ -218,28 +220,28 @@
 updatePlugin :: Update -> Maybe [String] -> Setting -> IO ()
 updatePlugin update maybePlugins setting = do
   setNumCapabilities =<< getNumProcessors
-  let unknownPlugins = filter (`notElem` map rtpName (plugins setting)) (fromMaybe [] maybePlugins)
+  let unknownPlugins = filter (`notElem` map rtpName setting.plugins) (fromMaybe [] maybePlugins)
   unless (null unknownPlugins) $
-    mapM_ (suggestPlugin (plugins setting)) unknownPlugins
+    mapM_ (suggestPlugin setting.plugins) unknownPlugins
   createPluginDirectory
   dir <- pluginDirectory
   let specified p = rtpName p `elem` fromMaybe [] maybePlugins || maybePlugins == Just []
   let filterplugin p = isNothing maybePlugins || specified p
-  let ps = filter filterplugin (plugins setting)
-  let count xs = if length xs > 1 then "s (" <> show (length xs) <> ")" else ""
+  let ps = filter filterplugin setting.plugins
+  let count xs = if length xs > 1 then "s (" <> display (length xs) <> ")" else ""
   time <- maximum <$> mapM' (lastUpdatePlugin dir) ps
   status <- fmap mconcat <$> displayConsoleRegions $
     mapM' (\p -> updateOnePlugin time dir update (specified p) p) ps
-  putStrLn $ (if null (failed status) then "Success" else "Error occured") <> " in " <> show update <> "."
-  unless (null (installed status)) do
-    putStrLn $ "Installed plugin" <> count (installed status) <> ": "
-    mapM_ (putStrLn . ("  "<>) . name) (sort (installed status))
-  unless (null (updated status)) do
-    putStrLn $ "Updated plugin" <> count (updated status) <> ": "
-    mapM_ (putStrLn . ("  "<>) . name) (sort (updated status))
-  unless (null (failed status)) do
-    putStrLn $ "Failed plugin" <> count (failed status) <> ": "
-    mapM_ (putStrLn . ("  "<>) . name) (sort (failed status))
+  putStrLn $ (if null status.failed then "Success" else "Error occurred") <> " in " <> display update <> "."
+  unless (null status.installed) do
+    putStrLn $ "Installed plugin" <> count status.installed <> ": "
+    mapM_ (\p -> putStrLn ("  " <> p.name)) (sort status.installed)
+  unless (null status.updated) do
+    putStrLn $ "Updated plugin" <> count status.updated <> ": "
+    mapM_ (\p -> putStrLn ("  " <> p.name)) (sort status.updated)
+  unless (null status.failed) do
+    putStrLn $ "Failed plugin" <> count status.failed <> ": "
+    mapM_ (\p -> putStrLn ("  " <> p.name)) (sort status.failed)
   generatePluginCode setting
   gatherFtdetectScript setting
   generateHelpTags setting
@@ -262,7 +264,7 @@
   dir <- pluginDirectory
   let docdir = dir </> "miv" </> "doc"
   cleanAndCreateDirectory docdir
-  P.forM_ (map (\p -> dir </> rtpName p </> "doc") (plugins setting)) \path -> do
+  P.forM_ (map (\p -> dir </> rtpName p </> "doc") setting.plugins) \path -> do
     exists <- doesDirectoryExist path
     when exists $
       void do (_, _, _, ph) <- createProcess (proc "sh" ["-c", "cp -R * " <> singleQuote docdir]) {
@@ -281,18 +283,18 @@
 updateOnePlugin :: Integer -> FilePath -> Update -> Bool -> Plugin -> IO UpdateStatus
 updateOnePlugin time dir update specified plugin = do
   let path = dir </> rtpName plugin
-      repo = vimScriptRepo (name plugin)
-      cloneCommand = if submodule plugin then cloneSubmodule else clone
-      pullCommand = if submodule plugin then pullSubmodule else pull
-      putStrLn' = \region -> setConsoleRegion region . ((name plugin <> ": ") <>)
-      finish' = \region -> finishConsoleRegion region . ((name plugin <> ": ") <>)
+      repo = vimScriptRepo plugin.name
+      cloneCommand = if plugin.submodule then cloneSubmodule else clone
+      pullCommand = if plugin.submodule then pullSubmodule else pull
+      putStrLn' region = setConsoleRegion region . (plugin.name<>) . (": "<>)
+      finish' region = finishConsoleRegion region . (plugin.name<>) . (": "<>)
   exists <- doesDirectoryExist path
   gitstatus <- gitStatus path
-  if not exists || (gitstatus /= ExitSuccess && not (sync plugin))
+  if not exists || (gitstatus /= ExitSuccess && not plugin.sync)
      then withConsoleRegion Linear \region -> do
        putStrLn' region "Installing"
        when exists $ removeDirectoryRecursive path
-       cloneStatus <- execCommand (unpack $ name plugin) region dir $ cloneCommand repo path
+       cloneStatus <- execCommand (unpack plugin.name) region dir $ cloneCommand repo path
        created <- doesDirectoryExist path
        when created do
          buildPlugin path region
@@ -300,7 +302,7 @@
        if cloneStatus /= ExitSuccess || not created
           then return mempty { failed = [plugin] }
           else return mempty { installed = [plugin] }
-     else if update == Install || not (sync plugin)
+     else if update == Install || not plugin.sync
              then return mempty { nosync = [plugin] }
              else withConsoleRegion Linear \region -> do
                lastUpdateTime <- lastUpdate path
@@ -310,10 +312,10 @@
                     return mempty { outdated = [plugin] }
                   else do
                     putStrLn' region "Pulling"
-                    pullStatus <- execCommand (unpack $ name plugin) region dir $ pullCommand path
+                    pullStatus <- execCommand (unpack plugin.name) region dir $ pullCommand path
                     newUpdateTime <- lastUpdate path
                     if pullStatus /= ExitSuccess
-                       then return $ mempty { failed = [plugin] }
+                       then return mempty { failed = [plugin] }
                        else if newUpdateTime <= lastUpdateTime
                                then return mempty
                                else do
@@ -321,12 +323,11 @@
                                  changeModifiedTime path newUpdateTime
                                  return mempty { updated = [plugin] }
     where changeModifiedTime path mtime =
-            when (mtime > 0) $ let ctime = fromInteger mtime
-                                   in setFileTimes path ctime ctime
-          buildPlugin path region = do
-            let command = build plugin
-            when (not (T.null command)) $
-              void $ execCommand (unpack $ name plugin) region path (unpack command)
+            when (mtime > 0) let ctime = fromInteger mtime
+                                 in setFileTimes path ctime ctime
+          buildPlugin path region =
+            unless (T.null plugin.build) $
+              void $ execCommand (unpack plugin.name) region path (unpack plugin.build)
 
 singleQuote :: String -> String
 singleQuote str = "'" <> str <> "'"
@@ -347,26 +348,25 @@
   code <- waitForProcess ph
   finishConsoleRegion region (pluginName ++ ": " ++ if null outline then errline else outline)
   return code
-  where go h mvar lastLine = do
-          e <- tryIOError $ hGetLine h
-          case e of
-               Left err -> if isEOFError err then putMVar mvar lastLine else return ()
-               Right line -> do
-                 setConsoleRegion region (pluginName ++ ": " ++ line)
-                 threadDelay 100000
-                 go h mvar line
+  where go h mvar lastLine =
+          tryIOError (hGetLine h) >>= \case
+            Left err -> when (isEOFError err) $ putMVar mvar lastLine
+            Right line -> do
+              setConsoleRegion region (pluginName ++ ": " ++ line)
+              threadDelay 100000
+              go h mvar line
 
 vimScriptRepo :: Text -> FilePath
 vimScriptRepo pluginname | T.any (=='/') pluginname = unpack pluginname
                          | otherwise = unpack $ "vim-scripts/" <> pluginname
 
 listPlugin :: Setting -> IO ()
-listPlugin setting = mapM_ putStrLn $ space $ map format $ plugins setting
-  where format p = [show p, name p, pack $ gitUrl (vimScriptRepo (name p))]
+listPlugin setting = mapM_ putStrLn $ space $ map format $ setting.plugins
+  where format (p :: Plugin) = (display p, p.name, pack $ gitUrl (vimScriptRepo p.name))
         space xs =
-          let max0 = maximum (map (T.length . (!!0)) xs) + 1
-              max1 = maximum (map (T.length . (!!1)) xs) + 1
-              in map (\(as:bs:cs:_) -> as <> T.replicate (max0 - T.length as) " " <> bs <> T.replicate (max1 - T.length bs) " " <> cs) xs
+          let max0 = maximum (map (T.length . fst3) xs) + 1
+              max1 = maximum (map (T.length . snd3) xs) + 1
+              in map (\(a, b, c) -> a <> T.replicate (max0 - T.length a) " " <> b <> T.replicate (max1 - T.length b) " " <> c) xs
 
 cleanDirectory :: Setting -> IO ()
 cleanDirectory setting = do
@@ -374,12 +374,12 @@
   dir <- pluginDirectory
   createDirectoryIfMissing True dir
   cnt <- listDirectory dir
-  let paths = "miv" : map (unpack . show) (plugins setting)
+  let paths = "miv" : map (unpack . display) setting.plugins
       delpath' = [ dir </> d | d <- cnt, d `notElem` paths ]
   deldirs <- filterM doesDirectoryExist delpath'
-  files <- getDirectoryFiles (dir </> "miv") $ (unpack . show . ($ "*")) <$> [ Autoload, Ftplugin, Syntax ]
+  files <- getDirectoryFiles (dir </> "miv") $ unpack . display . ($ "*") <$> [ Autoload, Ftplugin, Syntax ]
   let delfiles = (delpath' \\ deldirs)
-        <> (((dir </> "miv") </>) <$> (files \\ [ unpack (show place) | (place, _) <- vimScriptToList (gatherScript setting) ]))
+        <> (((dir </> "miv") </>) <$> (files \\ [ unpack (display place) | (place, _) <- vimScriptToList (gatherScript setting) ]))
   let delpaths = deldirs <> delfiles
   if not (null delpaths)
      then do putStrLn "Remove:"
@@ -389,12 +389,12 @@
              c <- getChar
              when (c == 'y' || c == 'Y') do
                mapM_ removeDirectoryRecursive deldirs
-               mapM_ removeFile $ delfiles
+               mapM_ removeFile delfiles
      else putStrLn "Clean."
 
 saveScript :: (FilePath, Place, [Text]) -> IO ()
 saveScript (dir, place, code) = do
-  let relname = show place
+  let relname = display place
       path = dir </> unpack relname
       isAllAscii = all (T.all (<='~')) code
       body = "" : (if isAllAscii then [] else [ "scriptencoding utf-8", "" ])
@@ -410,18 +410,17 @@
   when (contents /= Just body) $
     writeFile path $ unlines $
               [ "\" Filename: " <> relname
-              , "\" Last Change: " <> show time
+              , "\" Last Change: " <> pack (show time)
               , "\" Generated by " <> nameversion ] ++ body
 
 fileContents :: String -> IO (Maybe [Text])
-fileContents path = do
-  eitherFile <- tryJust (guard . isDoesNotExistError) (openFile path ReadMode)
-  case eitherFile of
-       Right file -> do
-         contentLines <- T.lines <$> hGetContents file
-         hClose file
-         return $ Just $ dropWhile ("\" " `T.isPrefixOf`) contentLines
-       Left _ -> return Nothing
+fileContents path =
+  tryJust (guard . isDoesNotExistError) (openFile path ReadMode) >>= \case
+    Right file -> do
+      contentLines <- T.lines <$> hGetContents file
+      hClose file
+      return $ Just $ dropWhile ("\" " `T.isPrefixOf`) contentLines
+    Left _ -> return Nothing
 
 generatePluginCode :: Setting -> IO ()
 generatePluginCode setting = do
@@ -431,7 +430,7 @@
   createDirectoryIfMissing True (dir </> "autoload" </> "miv")
   createDirectoryIfMissing True (dir </> "ftplugin")
   createDirectoryIfMissing True (dir </> "syntax")
-  when (any (not . null . dependedby) (plugins setting)) $
+  unless (all (null . (.dependedby)) setting.plugins) $
     hPutStrLn stderr "`dependedby` is deprecated in favor of `loadafter`"
   P.mapM_ (saveScript . (\(t, s) -> (dir, t, s)))
           (vimScriptToList (gatherScript setting))
@@ -441,7 +440,7 @@
 gatherFtdetectScript setting = do
   dir <- pluginDirectory
   cleanAndCreateDirectory (dir </> "miv" </> "ftdetect")
-  forM_ (plugins setting) \plugin -> do
+  forM_ setting.plugins \plugin -> do
     let path = rtpName plugin
     exists <- doesDirectoryExist (dir </> path </> "ftdetect")
     when exists do
@@ -450,7 +449,7 @@
         copyFile (dir </> path </> "ftdetect" </> file) (dir </> "miv" </> "ftdetect" </> file)
   putStrLn "Success in gathering ftdetect scripts."
 
-data EachStatus = EachStatus { failed' :: [Plugin] }
+newtype EachStatus = EachStatus { failed' :: [Plugin] }
 
 instance Semigroup EachStatus where
   EachStatus f <> EachStatus f' = EachStatus (f <> f')
@@ -462,10 +461,10 @@
 eachPlugin command setting = do
   createPluginDirectory
   dir <- pluginDirectory
-  status <- mconcat <$> mapM (eachOnePlugin command dir) (plugins setting)
-  unless (null (failed' status)) do
+  status <- mconcat <$> mapM (eachOnePlugin command dir) setting.plugins
+  unless (null status.failed') do
     putStrLn "Error:"
-    mapM_ (putStrLn . ("  "<>) . name) (failed' status)
+    mapM_ (\p -> putStrLn ("  " <> p.name)) status.failed'
 
 eachOnePlugin :: String -> FilePath -> Plugin -> IO EachStatus
 eachOnePlugin command dir plugin = do
@@ -476,19 +475,18 @@
      else do (_, _, _, ph) <- createProcess (proc "sh" ["-c", command]) {
                cwd = Just path
              }
-             exitCode <- waitForProcess ph
-             case exitCode of
-                  ExitSuccess -> return mempty
-                  _ -> return $ mempty { failed' = [plugin] }
+             waitForProcess ph >>= \case
+               ExitSuccess -> return mempty
+               _ -> return mempty { failed' = [plugin] }
 
 eachHelp :: IO ()
 eachHelp = mapM_ putStrLn [ "Specify command:", "  miv each [command]" ]
 
 pathPlugin :: [String] -> Setting -> IO ()
 pathPlugin plugins' setting = do
-  let ps = filter (\p -> rtpName p `elem` plugins' || null plugins') (plugins setting)
+  let ps = filter (\p -> rtpName p `elem` plugins' || null plugins') setting.plugins
   dir <- pluginDirectory
-  forM_ ps (\plugin -> putStrLn $ pack (dir </> unpack (show plugin)))
+  forM_ ps (\plugin -> putStrLn $ pack (dir </> unpack (display plugin)))
 
 mainProgram :: [String] -> IO ()
 mainProgram [] = printUsage
diff --git a/src/Mapping.hs b/src/Mapping.hs
--- a/src/Mapping.hs
+++ b/src/Mapping.hs
@@ -1,49 +1,37 @@
-{-# LANGUAGE OverloadedStrings #-}
 module Mapping where
 
-import Data.Text (Text, unwords, null)
-import Prelude hiding (show, unwords, null)
+import Data.Default (Default(..))
+import Data.Text (Text, null, unwords)
+import Data.Text.Builder.Linear qualified as Builder
+import Data.Text.Display (Display(..), display)
+import Prelude hiding (null, unwords)
 
 import Mode
-import ShowText
 
-data MapUnique = MapUnique | MapNoUnique
-               deriving Eq
-
-instance ShowText MapUnique where
-  show MapUnique = "<unique>"
-  show MapNoUnique = ""
-
-data MapSilent = MapSilent | MapNoSilent
-               deriving Eq
-
-instance ShowText MapSilent where
-  show MapSilent = "<silent>"
-  show MapNoSilent = ""
-
 data Mapping =
-     Mapping { mapName    :: Text
-             , mapRepText :: Text
-             , mapUnique  :: MapUnique
-             , mapSilent  :: MapSilent
-             , mapMode    :: Mode
-     } deriving Eq
+  Mapping {
+    name   :: Text,
+    repl   :: Text,
+    unique :: Bool,
+    silent :: Bool,
+    mode   :: Mode
+  } deriving Eq
 
-instance ShowText Mapping where
-  show m = unwords (filter (not . null)
-          [ show (mapMode m) <> "noremap"
-          , show (mapUnique m)
-         <> show (mapSilent m)
-          , mapName m
-          , mapRepText m
-          ])
+instance Display Mapping where
+  displayBuilder m =
+    Builder.fromText $ unwords $ filter (not . null)
+        [ display m.mode <> "noremap",
+          (if m.unique then "<unique>" else "")
+       <> (if m.silent then "<silent>" else ""),
+          m.name,
+          m.repl
+        ]
 
-defaultMapping :: Mapping
-defaultMapping
-  = Mapping { mapName    = ""
-            , mapRepText = ""
-            , mapUnique  = MapUnique
-            , mapSilent  = MapSilent
-            , mapMode    = NormalMode
+instance Default Mapping where
+  def = Mapping {
+    name    = "",
+    repl    = "",
+    unique  = True,
+    silent  = True,
+    mode    = NormalMode
   }
-
diff --git a/src/Mode.hs b/src/Mode.hs
--- a/src/Mode.hs
+++ b/src/Mode.hs
@@ -1,11 +1,10 @@
-{-# LANGUAGE BlockArguments, LambdaCase, OverloadedStrings #-}
 module Mode where
 
 import Data.Text (unpack)
+import Data.Text.Builder.Linear qualified as Builder
+import Data.Text.Display (Display(..))
 import Data.YAML
 
-import ShowText
-
 data Mode = NormalMode
           | VisualSelectMode
           | VisualMode
@@ -17,16 +16,17 @@
           | TerminalMode
           deriving (Eq, Ord)
 
-instance ShowText Mode where
-  show NormalMode = "n"
-  show VisualSelectMode = "v"
-  show VisualMode = "x"
-  show SelectMode = "s"
-  show InsertMode = "i"
-  show CmdlineMode = "c"
-  show LangArgMode = "l"
-  show OperatorPendingMode = "o"
-  show TerminalMode = "t"
+instance Display Mode where
+  displayBuilder = Builder.fromText . \case
+    NormalMode          -> "n"
+    VisualSelectMode    -> "v"
+    VisualMode          -> "x"
+    SelectMode          -> "s"
+    InsertMode          -> "i"
+    CmdlineMode         -> "c"
+    LangArgMode         -> "l"
+    OperatorPendingMode -> "o"
+    TerminalMode        -> "t"
 
 instance FromYAML Mode where
   parseYAML = withStr "!!str" \case
diff --git a/src/Plugin.hs b/src/Plugin.hs
--- a/src/Plugin.hs
+++ b/src/Plugin.hs
@@ -1,40 +1,41 @@
-{-# LANGUAGE BlockArguments, OverloadedStrings, RecordWildCards #-}
 module Plugin where
 
 import Control.Applicative ((<|>))
-import qualified Data.Text as T
 import Data.Text (Text, unpack)
+import Data.Text qualified as T
+import Data.Text.Builder.Linear qualified as Builder
+import Data.Text.Display (Display(..), display)
 import Data.YAML
 
 import Cmdline
 import Mode
-import ShowText
 
 data Plugin =
-     Plugin { name       :: Text
-            , filetypes  :: [Text]
-            , commands   :: [Text]
-            , functions  :: [Text]
-            , mappings   :: [Text]
-            , mapmodes   :: [Mode]
-            , cmdlines   :: [Cmdline]
-            , insert     :: Bool
-            , enable     :: Text
-            , sync       :: Bool
-            , mapleader  :: Text
-            , script     :: [Text]
-            , after      :: [Text]
-            , before     :: [Text]
-            , dependon   :: [Text]
-            , dependedby :: [Text]
-            , loadafter  :: [Text]
-            , loadbefore :: [Text]
-            , build      :: Text
-            , submodule  :: Bool
-     } deriving (Eq, Ord)
+  Plugin {
+    name       :: Text,
+    filetypes  :: [Text],
+    commands   :: [Text],
+    functions  :: [Text],
+    mappings   :: [Text],
+    mapmodes   :: [Mode],
+    cmdlines   :: [Cmdline],
+    insert     :: Bool,
+    enable     :: Text,
+    sync       :: Bool,
+    mapleader  :: Text,
+    script     :: [Text],
+    after      :: [Text],
+    before     :: [Text],
+    dependon   :: [Text],
+    dependedby :: [Text],
+    loadafter  :: [Text],
+    loadbefore :: [Text],
+    build      :: Text,
+    submodule  :: Bool
+  } deriving (Eq, Ord)
 
-instance ShowText Plugin where
-  show plg = subst (name plg)
+instance Display Plugin where
+  displayBuilder plugin = Builder.fromText $ subst plugin.name
     where subst s | T.any (=='/') s = subst (T.tail $ T.dropWhile (/='/') s)
                   | ".git" `T.isSuffixOf` s = subst $ T.take (T.length s - 4) s
                   | ".vim" `T.isSuffixOf` s = T.take (T.length s - 4) s
@@ -43,7 +44,7 @@
                   | otherwise = T.filter (`notElem`("!?;:/<>()[]{}|~'\"" :: String)) s
 
 rtpName :: Plugin -> String
-rtpName = unpack . ShowText.show
+rtpName = unpack . display
 
 instance FromYAML Plugin where
   parseYAML = withMap "!!map" \o -> do
diff --git a/src/Setting.hs b/src/Setting.hs
--- a/src/Setting.hs
+++ b/src/Setting.hs
@@ -1,9 +1,7 @@
-{-# LANGUAGE BlockArguments, OverloadedStrings, RecordWildCards #-}
 module Setting where
 
-import Data.Function (on)
-import Data.List (sortBy)
-import qualified Data.Map.Strict as M
+import Data.List (sortOn)
+import Data.Map.Strict qualified as M
 import Data.Text (Text, lines)
 import Data.YAML
 import Prelude hiding (lines)
@@ -11,12 +9,13 @@
 import Plugin
 
 data Setting =
-     Setting { plugins  :: [Plugin]
-             , filetype :: M.Map Text [Text]
-             , syntax   :: M.Map Text [Text]
-             , before   :: [Text]
-             , after    :: [Text]
-     } deriving Eq
+  Setting {
+    plugins  :: [Plugin],
+    filetype :: M.Map Text [Text],
+    syntax   :: M.Map Text [Text],
+    before   :: [Text],
+    after    :: [Text]
+  } deriving Eq
 
 instance FromYAML Setting where
   parseYAML = withMap "!!map" \o -> do
@@ -26,7 +25,5 @@
     before <- lines <$> o .:? "before" .!= ""
     after <- lines <$> o .:? "after" .!= ""
     return Setting {..}
-      where pluginMapToList = sortWith name . M.foldlWithKey' (\a k v -> v { name = k } : a) []
-
-sortWith :: Ord b => (a -> b) -> [a] -> [a]
-sortWith = sortBy . on compare
+      where pluginMapToList = sortOn (.name) .
+              M.foldlWithKey' (\a k v -> v { name = k } : a) []
diff --git a/src/ShowText.hs b/src/ShowText.hs
deleted file mode 100644
--- a/src/ShowText.hs
+++ /dev/null
@@ -1,10 +0,0 @@
-{-# LANGUAGE FlexibleInstances, IncoherentInstances, UndecidableInstances #-}
-module ShowText where
-
-import Data.Text
-
-class ShowText a where
-  show :: a -> Text
-
-instance Show a => ShowText a where
-  show = pack . Prelude.show
diff --git a/src/VimScript.hs b/src/VimScript.hs
--- a/src/VimScript.hs
+++ b/src/VimScript.hs
@@ -1,37 +1,39 @@
-{-# LANGUAGE DeriveGeneric, OverloadedStrings #-}
 module VimScript where
 
-import Data.Char (isAlpha, isAlphaNum, ord, toLower)
-import Data.Function (on)
-import Data.List (foldl', groupBy, sort, sortBy, nub)
-import qualified Data.Map.Strict as M
+import Data.Char (isAlpha, isAlphaNum, isSpace, ord, toLower)
+import Data.Default (def)
+import Data.List (foldl', nub, sort)
+import Data.List.Extra (groupSort)
+import Data.Map.Strict qualified as M
 import Data.Text (Text, singleton, unpack, unwords)
-import qualified Data.Text as T
-import Prelude hiding (show, unwords)
+import Data.Text qualified as T
+import Data.Text.Builder.Linear qualified as Builder
+import Data.Text.Display (Display(..), display)
+import Prelude hiding (unwords)
 
 import Cmdline
-import qualified Command as C
-import qualified Mapping as M
+import Command
+import Mapping
 import Mode
-import qualified Plugin as P
-import qualified Setting as S
-import ShowText
+import Plugin
+import Setting
 
-data VimScript = VimScript (M.Map Place [Text])
-               deriving (Eq)
+newtype VimScript = VimScript (M.Map Place [Text])
+                  deriving Eq
 
-data Place = Plugin
+data Place = Plugin'
            | Autoload Text
            | Ftplugin Text
            | Syntax   Text
            deriving (Eq, Ord)
 
-instance ShowText Place where
-  show Plugin        = "plugin/miv.vim"
-  show (Autoload "") = "autoload/miv.vim"
-  show (Autoload s)  = "autoload/miv/" <> s <> ".vim"
-  show (Ftplugin s)  = "ftplugin/" <> s <> ".vim"
-  show (Syntax s)    = "syntax/" <> s <> ".vim"
+instance Display Place where
+  displayBuilder = Builder.fromText . \case
+    Plugin'     -> "plugin/miv.vim"
+    Autoload "" -> "autoload/miv.vim"
+    Autoload s  -> "autoload/miv/" <> s <> ".vim"
+    Ftplugin s  -> "ftplugin/" <> s <> ".vim"
+    Syntax s    -> "syntax/" <> s <> ".vim"
 
 vimScriptToList :: VimScript -> [(Place, [Text])]
 vimScriptToList (VimScript x) = M.toList x
@@ -44,23 +46,23 @@
     where
       concat' a [] = a
       concat' [] b = b
-      concat' a b = a <> [""] <> b
+      concat' a b  = a <> [""] <> b
 
 instance Monoid VimScript where
   mempty = VimScript M.empty
 
-gatherScript :: S.Setting -> VimScript
+gatherScript :: Setting -> VimScript
 gatherScript setting = beforeScript setting
-                    <> gatherBeforeAfterScript plugins
-                    <> gather' "dependon" P.dependon P.loadbefore plugins
-                    <> gather' "dependedby" P.dependedby P.loadafter plugins
-                    <> gather "mappings" P.mappings plugins
-                    <> gather "mapmodes" (map show . P.mapmodes) plugins
-                    <> gather "functions" P.functions plugins
+                    <> gatherBeforeAfterScript setting.plugins
+                    <> gather' "dependon" (.dependon) (.loadbefore) setting.plugins
+                    <> gather' "dependedby" (.dependedby) (.loadafter) setting.plugins
+                    <> gather "mappings" (.mappings) setting.plugins
+                    <> gather "mapmodes" (map display . (.mapmodes)) setting.plugins
+                    <> gather "functions" (.functions) setting.plugins
                     <> pluginLoader
                     <> mappingLoader
                     <> commandLoader
-                    <> foldl' (<>) mempty (map pluginConfig plugins)
+                    <> foldl' (<>) mempty (map pluginConfig setting.plugins)
                     <> filetypeLoader setting
                     <> funcUndefinedLoader setting
                     <> cmdlineEnterLoader setting
@@ -68,9 +70,8 @@
                     <> filetypeScript setting
                     <> syntaxScript setting
                     <> afterScript setting
-  where plugins = S.plugins setting
 
-gatherBeforeAfterScript :: [P.Plugin] -> VimScript
+gatherBeforeAfterScript :: [Plugin] -> VimScript
 gatherBeforeAfterScript x = insertAuNameMap $ gatherScripts x (mempty, M.empty)
   where
     insertAuNameMap :: (VimScript, M.Map Text Text) -> VimScript
@@ -78,107 +79,99 @@
           [ "let s:autoload = {" ]
        <> [ "      \\ " <> singleQuote k <> ": " <> singleQuote a <> "," | (k, a) <- M.toList m ]
        <> [ "      \\ }" ]) <> vs
-    gatherScripts :: [P.Plugin] -> (VimScript, M.Map Text Text) -> (VimScript, M.Map Text Text)
+    gatherScripts :: [Plugin] -> (VimScript, M.Map Text Text) -> (VimScript, M.Map Text Text)
     gatherScripts (p:ps) (vs, m)
-            | null (P.before p) && null (P.after p) = gatherScripts ps (vs, m)
+            | null p.before && null p.after = gatherScripts ps (vs, m)
             | otherwise = gatherScripts ps (vs <> vs', M.insert name hchar m)
       where
-        name = T.filter isAlphaNum (T.toLower (show p))
-        hchar | null (loadScript p) = maybe "_" singleton $ getHeadChar $ show p
+        name = T.filter isAlphaNum (T.toLower (display p))
+        hchar | null (loadScript p) = maybe "_" singleton $ getHeadChar $ display p
               | otherwise = "_"
         funcname str = "miv#" <> hchar <> "#" <> str <> "_" <> name
         vs' = VimScript $ M.singleton (Autoload hchar) $
-          wrapFunction (funcname "before") (P.before p) <>
-          wrapFunction (funcname "after") (P.after p)
+          wrapFunction (funcname "before") p.before <>
+          wrapFunction (funcname "after") p.after
     gatherScripts [] (vs, m) = (vs, m)
 
-gather :: Text -> (P.Plugin -> [Text]) -> [P.Plugin] -> VimScript
-gather name f plg
+gather :: Text -> (Plugin -> [Text]) -> [Plugin] -> VimScript
+gather name f plugin
   = VimScript (M.singleton (Autoload "") $
       [ "let s:" <> name <> " = {" ]
-   <> [ "      \\ " <> singleQuote (show p)
+   <> [ "      \\ " <> singleQuote (display p)
                     <> ": [ " <> T.intercalate ", " (map singleQuote (f p))  <> " ],"
-        | p <- plg, enabled p, not (null (f p)) ]
+        | p <- plugin, p.enable /= "0", not (null (f p)) ]
    <> [ "      \\ }" ])
-  where enabled p = P.enable p /= "0"
 
-gather' :: Text -> (P.Plugin -> [Text]) -> (P.Plugin -> [Text]) -> [P.Plugin] -> VimScript
-gather' name f g plg
+gather' :: Text -> (Plugin -> [Text]) -> (Plugin -> [Text]) -> [Plugin] -> VimScript
+gather' name f g plugin
   = VimScript (M.singleton (Autoload "") $
       [ "let s:" <> name <> " = {" ]
    <> [ "      \\ " <> singleQuote p
                     <> ": [ " <> T.intercalate ", " (map singleQuote $ sort $ nub q)  <> " ],"
-        | (p, q) <- collectSndByFst $ [ (show p, q) | p <- plg, enabled p, q <- f p ]
-                                   <> [ (q, show p) | p <- plg, enabled p, q <- g p ] ]
+        | (p, q) <- groupSort $ [ (display p, q) | p <- plugin, p.enable /= "0", q <- f p ]
+                             <> [ (q, display p) | p <- plugin, p.enable /= "0", q <- g p ] ]
    <> [ "      \\ }" ])
-  where enabled p = P.enable p /= "0"
 
-collectSndByFst :: Ord a => [(a,b)] -> [(a,[b])]
-collectSndByFst xs = [ (fst (ys !! 0), map snd ys)
-                       | ys <- groupBy ((==) `on` fst) $ sortBy (compare `on'` fst) xs ]
-  where on' f g x y = g x `f` g y
-
-pluginConfig :: P.Plugin -> VimScript
-pluginConfig plg
-    = VimScript (M.singleton Plugin $ wrapInfo $
-        wrapEnable (P.enable plg) $ mapleader <> gatherCommand plg <> gatherMapping plg <> P.script plg <> loadScript plg)
+pluginConfig :: Plugin -> VimScript
+pluginConfig plugin
+  = VimScript (M.singleton Plugin' $ wrapInfo $
+      wrapEnable plugin.enable $ mapleader <> gatherCommand plugin <> gatherMapping plugin <> plugin.script <> loadScript plugin)
   where
-    wrapInfo [] = []
-    wrapInfo str = ("\" " <> P.name plg) : str
-    mapleader = (\s -> if T.null s then [] else ["let g:mapleader = " <> singleQuote s]) (P.mapleader plg)
+    wrapInfo []  = []
+    wrapInfo str = ("\" " <> plugin.name) : str
+    mapleader = let s = plugin.mapleader in ["let g:mapleader = " <> singleQuote s | not (T.null s)]
 
-loadScript :: P.Plugin -> [Text]
-loadScript plg
-  | all null [ P.commands plg, P.mappings plg, P.functions plg, P.filetypes plg
-             , P.loadafter plg, P.loadbefore plg ] && null (P.cmdlines plg) && not (P.insert plg)
-  = ["call miv#load(" <> singleQuote (show plg) <> ")"]
+loadScript :: Plugin -> [Text]
+loadScript plugin
+  | all null [ plugin.commands, plugin.mappings, plugin.functions, plugin.filetypes
+             , plugin.loadafter, plugin.loadbefore ] && null plugin.cmdlines && not plugin.insert
+  = ["call miv#load(" <> singleQuote (display plugin) <> ")"]
   | otherwise = []
 
-gatherCommand :: P.Plugin -> [Text]
-gatherCommand plg
-  | not (null (P.commands plg))
-    = [show C.defaultCommand
-          { C.cmdName = c,
-            C.cmdRepText = unwords [
-              "call miv#command(" <> singleQuote (show plg) <> ",",
+gatherCommand :: Plugin -> [Text]
+gatherCommand plugin
+  | not (null plugin.commands)
+    = [display def
+          { Command.name = c,
+            Command.repl = unwords [
+              "call miv#command(" <> singleQuote (display plugin) <> ",",
               singleQuote c <> ",",
               singleQuote "<bang>" <> ",",
               "<q-args>,",
               "expand('<line1>'),",
               "expand('<line2>'))"
-          ] } | c <- P.commands plg]
+          ] } | c <- plugin.commands]
   | otherwise = []
 
-gatherMapping :: P.Plugin -> [Text]
-gatherMapping plg
-  | not (null (P.mappings plg))
-    = [show M.defaultMapping
-          { M.mapName = mapping,
-            M.mapRepText = escape mode <> ":<C-u>call miv#mapping("
-                <> singleQuote (show plg) <> ", "
+gatherMapping :: Plugin -> [Text]
+gatherMapping plugin
+  | not (null plugin.mappings)
+    = [display def
+          { Mapping.name = mapping,
+            Mapping.repl = escape mode <> ":<C-u>call miv#mapping("
+                <> singleQuote (display plugin) <> ", "
                 <> singleQuote mapping <> ", "
-                <> singleQuote (show mode) <> ")<CR>",
-            M.mapMode = mode
-          } | mapping <- P.mappings plg, mode <- modes]
+                <> singleQuote (display mode) <> ")<CR>",
+            Mapping.mode = mode
+          } | mapping <- plugin.mappings, mode <- modes]
   | otherwise = []
-    where modes = if null (P.mapmodes plg) then [NormalMode] else P.mapmodes plg
+    where modes = if null plugin.mapmodes then [NormalMode] else plugin.mapmodes
           escape mode = if mode `elem` [InsertMode, OperatorPendingMode] then "<ESC>" else ""
 
-beforeScript :: S.Setting -> VimScript
-beforeScript setting = VimScript (M.singleton Plugin (S.before setting))
+beforeScript :: Setting -> VimScript
+beforeScript setting = VimScript (M.singleton Plugin' setting.before)
 
-afterScript :: S.Setting -> VimScript
-afterScript setting = VimScript (M.singleton Plugin (S.after setting))
+afterScript :: Setting -> VimScript
+afterScript setting = VimScript (M.singleton Plugin' setting.after)
 
-filetypeLoader :: S.Setting -> VimScript
-filetypeLoader setting
-  = mconcat $ map (uncurry f) $
-      collectSndByFst [(ft, p) | p <- S.plugins setting, ft <- P.filetypes p]
+filetypeLoader :: Setting -> VimScript
+filetypeLoader setting =
+  mconcat $ map (uncurry f) $ groupSort [(ft, p) | p <- setting.plugins, ft <- p.filetypes]
   where
-    f :: Text -> [P.Plugin] -> VimScript
+    f :: Text -> [Plugin] -> VimScript
     f ft plugins = flip foldMap (getHeadChar ft) $
       \c -> let funcname = "miv#" <> singleton c <> "#load_" <> T.filter isAlphaNum (T.toLower ft)
-                in VimScript (M.singleton Plugin
+                in VimScript (M.singleton Plugin'
                      [ "augroup miv-file-type-" <> ft
                      , "  autocmd!"
                      , "  autocmd FileType " <> ft <> " call " <> funcname <> "()"
@@ -194,29 +187,29 @@
                      , "endfunction" ])
 
 wrapEnable :: Text -> [Text] -> [Text]
-wrapEnable enable str
-  | null str = []
-  | T.null enable = str
-  | enable == "0" = []
-  | otherwise = (indent <> "if " <> enable)
-                           : map ("  "<>) str
-             <> [indent <> "endif"]
-  where indent = T.takeWhile (==' ') (head str)
+wrapEnable _ [] = []
+wrapEnable "" strs = strs
+wrapEnable "0" _ = []
+wrapEnable enable strs@(s:_) =
+  [indent <> "if " <> enable] <>
+        map ("  "<>) strs <>
+  [indent <> "endif"]
+  where indent = T.takeWhile isSpace s
 
-loadPlugins :: [P.Plugin] -> [Text]
+loadPlugins :: [Plugin] -> [Text]
 loadPlugins plugins = concat
   [wrapEnable enable
-    ["  call miv#load(" <> singleQuote (show p) <> ")" | p <- plugins']
-      | (enable, plugins') <- collectSndByFst [(P.enable p, p) | p <- plugins]]
+    ["  call miv#load(" <> singleQuote (display p) <> ")" | p <- plugins']
+      | (enable, plugins') <- groupSort [(p.enable, p) | p <- plugins]]
 
 singleQuote :: Text -> Text
 singleQuote str = "'" <> str <> "'"
 
-filetypeScript :: S.Setting -> VimScript
-filetypeScript = foldMap (\(ft, src) -> VimScript (M.singleton (Ftplugin ft) src)) . M.toList . S.filetype
+filetypeScript :: Setting -> VimScript
+filetypeScript setting = foldMap (\(ft, src) -> VimScript (M.singleton (Ftplugin ft) src)) $ M.toList setting.filetype
 
-syntaxScript :: S.Setting -> VimScript
-syntaxScript = foldMap (\(ft, src) -> VimScript (M.singleton (Syntax ft) src)) . M.toList . S.syntax
+syntaxScript :: Setting -> VimScript
+syntaxScript setting = foldMap (\(ft, src) -> VimScript (M.singleton (Syntax ft) src)) $ M.toList setting.syntax
 
 wrapFunction :: Text -> [Text] -> [Text]
 wrapFunction funcname script =
@@ -256,9 +249,9 @@
   , "  endtry"
   , "endfunction" ])
 
-funcUndefinedLoader :: S.Setting -> VimScript
+funcUndefinedLoader :: Setting -> VimScript
 funcUndefinedLoader setting = if null functions then mempty else
-  VimScript (M.singleton Plugin
+  VimScript (M.singleton Plugin'
   [ "\" FuncUndefined"
   , "augroup miv-func-undefined"
   , "  autocmd!"
@@ -283,20 +276,19 @@
   , "    endfor"
   , "  endfor"
   , "endfunction" ])
-  where functions = [ f | p <- S.plugins setting, f <- P.functions p ]
+  where functions = [f | p <- setting.plugins, f <- p.functions]
 
-cmdlineEnterLoader :: S.Setting -> VimScript
-cmdlineEnterLoader setting
-  = mconcat $ map (uncurry f) $
-      collectSndByFst [(cmdline, p) | p <- S.plugins setting, cmdline <- P.cmdlines p]
+cmdlineEnterLoader :: Setting -> VimScript
+cmdlineEnterLoader setting =
+  mconcat $ map (uncurry f) $ groupSort [(cmdline, p) | p <- setting.plugins, cmdline <- p.cmdlines]
   where
-    f :: Cmdline -> [P.Plugin] -> VimScript
-    f cmdline plugins = VimScript (M.singleton Plugin
-      [ "\" CmdlineEnter " <> (show cmdline)
+    f :: Cmdline -> [Plugin] -> VimScript
+    f cmdline plugins = VimScript (M.singleton Plugin'
+      [ "\" CmdlineEnter " <> display cmdline
       , "if exists('#CmdlineEnter')"
       , "  augroup " <> group
       , "    autocmd!"
-      , "    autocmd CmdlineEnter " <> (cmdlinePattern cmdline) <> " call miv#cmdline_enter_" <> c <> "()"
+      , "    autocmd CmdlineEnter " <> cmdlinePattern cmdline <> " call miv#cmdline_enter_" <> c <> "()"
       , "  augroup END"
       , "else"
       , "  call miv#cmdline_enter_" <> c <> "()"
@@ -309,12 +301,12 @@
       , "    augroup! " <> group
       , "  endif"
       , "endfunction" ])
-      where c = T.concat $ map (show . ord) (unpack (show cmdline))
+      where c = T.concat $ map (display . ord) (unpack (display cmdline))
             group = "miv-cmdline-enter-" <> c
 
-insertEnterLoader :: S.Setting -> VimScript
+insertEnterLoader :: Setting -> VimScript
 insertEnterLoader setting = if null plugins then mempty else
-  VimScript (M.singleton Plugin
+  VimScript (M.singleton Plugin'
   [ "\" InsertEnter"
   , "augroup miv-insert-enter"
   , "  autocmd!"
@@ -327,7 +319,7 @@
   , "  augroup! miv-insert-enter"
   , "  silent! doautocmd InsertEnter"
   , "endfunction" ])
-  where plugins = filter P.insert (S.plugins setting)
+  where plugins = filter (.insert) setting.plugins
 
 pluginLoader :: VimScript
 pluginLoader = VimScript (M.singleton (Autoload "")
