Print function annotations
authorGreg Hurrell <greg@hurrell.net>
Tue, 21 Jun 2016 15:09:33 +0000 (08:09 -0700)
committerGreg Hurrell <greg@hurrell.net>
Tue, 21 Jun 2016 15:09:33 +0000 (08:09 -0700)
This is pretty rough, but I need to start documenting these in Loupe. May
change. For example, I might want to parse and use argument lists.

Includes Loupe fixtures update.

lib/Text/Docvim/Printer/Markdown.hs
lib/Text/Docvim/Printer/Vim.hs
tests/fixtures/integration/loupe/golden/ast.golden
tests/fixtures/integration/loupe/golden/markdown.golden
tests/fixtures/integration/loupe/golden/plaintext.golden
tests/fixtures/integration/loupe/input/autoload/loupe.vim [new file with mode: 0644]
tests/fixtures/integration/loupe/input/autoload/loupe/private.vim
tests/fixtures/integration/loupe/input/plugin/loupe.vim

index 4a0ded634ee15abd0661e6dd7e15fbc5196d9af1..832d99fa517edb9b66c24cefcd7fe31450b6561d 100644 (file)
@@ -38,6 +38,7 @@ node n = case n of
   CommandsAnnotation      -> h2 "Commands"
   DocBlock d              -> nodes d
   Fenced f                -> return $ fenced f ++ "\n\n"
+  FunctionAnnotation {}   -> function n
   FunctionDeclaration {}  -> nodes $ functionBody n
   FunctionsAnnotation     -> h2 "Functions"
   HeadingAnnotation h     -> h2 h
@@ -161,5 +162,12 @@ command (CommandAnnotation name params) = do
         annotation = rstrip $ name ++ " " ++ fromMaybe "" params
 command _ = invalidNode
 
+function :: Node -> Env
+function (FunctionAnnotation name) = do
+  content <- h3 $ "`" ++ name ++ "()`"
+  return $ target ++ content
+  where target = linkTargets [name ++ "()"]
+function _ = invalidNode
+
 mapping :: String -> Env
 mapping name = h3 $ "`" ++ name ++ "`"
index 6f4b0223fed5230f5baefbe9db803a3ebe3541ff..9eaac54935b123b83d25a45aba9cc4e0a6c7fd47 100644 (file)
@@ -127,8 +127,9 @@ node n = case n of
   CommandsAnnotation         -> heading "commands"
   DocBlock d                 -> nodes d
   Fenced f                   -> fenced f
-  FunctionsAnnotation        -> heading "functions"
+  FunctionAnnotation {}      -> function n
   FunctionDeclaration {}     -> nodes $ functionBody n
+  FunctionsAnnotation        -> heading "functions"
   HeadingAnnotation h        -> heading h
   Link l                     -> append $ link l
   LinkTargets l              -> linkTargets l True
@@ -213,6 +214,16 @@ command (CommandAnnotation name params) = do
 -- once that is done, drop the extra newline above
 command _ = invalidNode
 
+function :: Node -> Env
+function (FunctionAnnotation name) = do
+  lhs <- append $ name ++ "()"
+  ws <- append " "
+  target' <- linkTargets [name ++ "()"] False
+  trailing <- append "\n"
+  return $ concat [lhs, ws, target', trailing]
+-- TODO indent what follows
+function _ = invalidNode
+
 mapping :: String -> Env
 mapping name = linkTargets [name] True
 
index 490dcb0e2d300f84d4319509c1cd042131071f2c..04c8d343ce4bdf90ee139f5bdea7dbeed725b90c 100644 (file)
@@ -6,6 +6,7 @@ Project
           , "Installation"
           , "Mappings"
           , "Options"
+          , "Functions"
           , "Overrides"
           , "Related"
           , "Website"
@@ -666,17 +667,49 @@ Project
       [ Unit
           [ FunctionDeclaration
               { functionBang = True
-              , functionName = "loupe#private#very_magic_slash"
+              , functionName = "loupe#hlmatch"
               , functionArguments = ArgumentList []
               , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ Empty
+                  , LetStatement
+                      { letLexpr = "l:highlight"
+                      , letValue = "get(g:, 'LoupeHighlightGroup', 'IncSearch')"
+                      }
+                  , GenericStatement "if empty(l:highlight)"
+                  , GenericStatement "return"
+                  , GenericStatement "endif"
+                  , GenericStatement "if has('autocmd')"
+                  , GenericStatement "augroup LoupeHightlightMatch"
+                  , GenericStatement "autocmd!"
+                  , GenericStatement "augroup END"
+                  , GenericStatement "endif"
+                  , GenericStatement "call loupe#private#clear_highlight()"
+                  , LetStatement
+                      { letLexpr = "l:pattern" , letValue = "'\\c\\%#' . @/" }
+                  , GenericStatement "if exists('*matchadd')"
+                  , LetStatement
+                      { letLexpr = "w:loupe_hlmatch"
+                      , letValue = "matchadd(l:highlight, l:pattern)"
+                      }
+                  , GenericStatement "endif"
+                  ]
+              }
+          ]
+      , Unit
+          [ FunctionDeclaration
+              { functionBang = True
+              , functionName = "loupe#private#very_magic_slash"
+              , functionArguments = ArgumentList [ Argument "slash" ]
+              , functionAttributes = [ "abort" ]
               , functionBody =
                   [ GenericStatement "if getcmdtype() != ':'"
-                  , GenericStatement "return '/'"
+                  , GenericStatement "return a:slash"
                   , GenericStatement "endif"
                   , LetStatement { letLexpr = "l:pos" , letValue = "getcmdpos()" }
                   , LetStatement { letLexpr = "l:cmd" , letValue = "getcmdline()" }
                   , GenericStatement "if len(l:cmd) + 1 != l:pos"
-                  , GenericStatement "return '/'"
+                  , GenericStatement "return a:slash"
                   , GenericStatement "endif"
                   , GenericStatement "while 1"
                   , LetStatement
@@ -688,9 +721,10 @@ Project
                   , GenericStatement "endif"
                   , GenericStatement "endwhile"
                   , GenericStatement "if index(['g', 's', 'v'], l:cmd) != -1"
-                  , GenericStatement "return loupe#private#prepare_highlight('/\\v')"
+                  , GenericStatement
+                      "return loupe#private#prepare_highlight(a:slash . '\\v')"
                   , GenericStatement "endif"
-                  , GenericStatement "return '/'"
+                  , GenericStatement "return a:slash"
                   ]
               }
           , FunctionDeclaration
@@ -777,8 +811,7 @@ Project
                   [ GenericStatement "if has('autocmd')"
                   , GenericStatement "augroup LoupeHightlightMatch"
                   , GenericStatement "autocmd!"
-                  , GenericStatement
-                      "autocmd CursorMoved * :call loupe#private#hlmatch()"
+                  , GenericStatement "autocmd CursorMoved * :call loupe#hlmatch()"
                   , GenericStatement "augroup END"
                   , GenericStatement "endif"
                   , GenericStatement "return a:result"
@@ -807,41 +840,11 @@ Project
               , functionArguments = ArgumentList []
               , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ GenericStatement "if !v:hlsearch"
+                  [ GenericStatement "if !exists('v:hlsearch') || !v:hlsearch"
                   , GenericStatement "call loupe#private#clear_highlight()"
                   , GenericStatement "endif"
                   ]
               }
-          , FunctionDeclaration
-              { functionBang = True
-              , functionName = "loupe#private#hlmatch"
-              , functionArguments = ArgumentList []
-              , functionAttributes = [ "abort" ]
-              , functionBody =
-                  [ Empty
-                  , LetStatement
-                      { letLexpr = "l:highlight"
-                      , letValue = "get(g:, 'LoupeHighlightGroup', 'IncSearch')"
-                      }
-                  , GenericStatement "if empty(l:highlight)"
-                  , GenericStatement "return"
-                  , GenericStatement "endif"
-                  , GenericStatement "if has('autocmd')"
-                  , GenericStatement "augroup LoupeHightlightMatch"
-                  , GenericStatement "autocmd!"
-                  , GenericStatement "augroup END"
-                  , GenericStatement "endif"
-                  , GenericStatement "call loupe#private#clear_highlight()"
-                  , LetStatement
-                      { letLexpr = "l:pattern" , letValue = "'\\c\\%#' . @/" }
-                  , GenericStatement "if exists('*matchadd')"
-                  , LetStatement
-                      { letLexpr = "w:loupe_hlmatch"
-                      , letValue = "matchadd(l:highlight, l:pattern)"
-                      }
-                  , GenericStatement "endif"
-                  ]
-              }
           ]
       , Unit
           [ GenericStatement
@@ -867,10 +870,10 @@ Project
           , GenericStatement
               "set smartcase \" Case-sensitive search if search string includes a capital letter."
           , LetStatement
-              { letLexpr = "s:map"
+              { letLexpr = "s:clear"
               , letValue = "get(g:, 'LoupeClearHighlightMap', 1)"
               }
-          , GenericStatement "if s:map"
+          , GenericStatement "if s:clear"
           , GenericStatement
               "if !hasmapto('<Plug>(LoupeClearHighlight)') && maparg('<leader>n', 'n') ==# ''"
           , GenericStatement
@@ -918,28 +921,94 @@ Project
               "xnoremap <expr> ? loupe#private#prepare_highlight('?' . <SID>MagicString())"
           , GenericStatement "if !empty(s:MagicString())"
           , GenericStatement
-              "cnoremap <expr> / loupe#private#very_magic_slash()"
-          , GenericStatement "endif"
-          , LetStatement
-              { letLexpr = "s:center"
-              , letValue = "get(g:, 'LoupeCenterResults', 1)"
-              }
-          , LetStatement
-              { letLexpr = "s:center_string"
-              , letValue = "s:center ? 'zz' : ''"
-              }
+              "cnoremap <expr> ! loupe#private#very_magic_slash('!')"
+          , GenericStatement
+              "cnoremap <expr> # loupe#private#very_magic_slash('#')"
+          , GenericStatement
+              "cnoremap <expr> $ loupe#private#very_magic_slash('$')"
+          , GenericStatement
+              "cnoremap <expr> % loupe#private#very_magic_slash('%')"
+          , GenericStatement
+              "cnoremap <expr> & loupe#private#very_magic_slash('&')"
+          , GenericStatement
+              "cnoremap <expr> ' loupe#private#very_magic_slash(\"'\")"
+          , GenericStatement
+              "cnoremap <expr> ( loupe#private#very_magic_slash('(')"
+          , GenericStatement
+              "cnoremap <expr> ) loupe#private#very_magic_slash(')')"
+          , GenericStatement
+              "cnoremap <expr> * loupe#private#very_magic_slash('*')"
+          , GenericStatement
+              "cnoremap <expr> + loupe#private#very_magic_slash('+')"
+          , GenericStatement
+              "cnoremap <expr> , loupe#private#very_magic_slash(',')"
           , GenericStatement
-              "execute 'nnoremap <silent> # #' . s:center_string . ':call loupe#private#hlmatch()<CR>'"
+              "cnoremap <expr> - loupe#private#very_magic_slash('-')"
           , GenericStatement
-              "execute 'nnoremap <silent> * *' . s:center_string . ':call loupe#private#hlmatch()<CR>'"
+              "cnoremap <expr> . loupe#private#very_magic_slash('.')"
           , GenericStatement
-              "execute 'nnoremap <silent> N N' . s:center_string . ':call loupe#private#hlmatch()<CR>'"
+              "cnoremap <expr> / loupe#private#very_magic_slash('/')"
           , GenericStatement
-              "execute 'nnoremap <silent> g# g#' . s:center_string . ':call loupe#private#hlmatch()<CR>'"
+              "cnoremap <expr> : loupe#private#very_magic_slash(':')"
           , GenericStatement
-              "execute 'nnoremap <silent> g* g*' . s:center_string . ':call loupe#private#hlmatch()<CR>'"
+              "cnoremap <expr> ; loupe#private#very_magic_slash(';')"
           , GenericStatement
-              "execute 'nnoremap <silent> n n' . s:center_string . ':call loupe#private#hlmatch()<CR>'"
+              "cnoremap <expr> < loupe#private#very_magic_slash('<')"
+          , GenericStatement
+              "cnoremap <expr> = loupe#private#very_magic_slash('=')"
+          , GenericStatement
+              "cnoremap <expr> > loupe#private#very_magic_slash('>')"
+          , GenericStatement
+              "cnoremap <expr> ? loupe#private#very_magic_slash('?')"
+          , GenericStatement
+              "cnoremap <expr> @ loupe#private#very_magic_slash('@')"
+          , GenericStatement
+              "cnoremap <expr> [ loupe#private#very_magic_slash('[')"
+          , GenericStatement
+              "cnoremap <expr> ] loupe#private#very_magic_slash(']')"
+          , GenericStatement
+              "cnoremap <expr> ^ loupe#private#very_magic_slash('^')"
+          , GenericStatement
+              "cnoremap <expr> _ loupe#private#very_magic_slash('_')"
+          , GenericStatement
+              "cnoremap <expr> ` loupe#private#very_magic_slash('`')"
+          , GenericStatement
+              "cnoremap <expr> { loupe#private#very_magic_slash('{')"
+          , GenericStatement
+              "cnoremap <expr> } loupe#private#very_magic_slash('}')"
+          , GenericStatement
+              "cnoremap <expr> ~ loupe#private#very_magic_slash('~')"
+          , GenericStatement "endif"
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "s:map"
+              , functionArguments =
+                  ArgumentList [ Argument "keys" , Argument "name" ]
+              , functionAttributes = []
+              , functionBody =
+                  [ Empty
+                  , LetStatement
+                      { letLexpr = "s:center"
+                      , letValue = "get(g:, 'LoupeCenterResults', 1)"
+                      }
+                  , LetStatement
+                      { letLexpr = "s:center_string"
+                      , letValue = "s:center ? 'zz' : ''"
+                      }
+                  , GenericStatement "if !hasmapto('<Plug>(Loupe' . a:name . ')')"
+                  , GenericStatement
+                      "execute 'nmap <silent> ' . a:keys . ' <Plug>(Loupe' . a:name . ')'"
+                  , GenericStatement "endif"
+                  , GenericStatement
+                      "execute 'nnoremap <silent> <Plug>(Loupe' . a:name . ')' . ' ' . a:keys . s:center_string . ':call loupe#hlmatch()<CR>'"
+                  ]
+              }
+          , GenericStatement "call s:map('#', 'Octothorpe')"
+          , GenericStatement "call s:map('*', 'Star')"
+          , GenericStatement "call s:map('N', 'N')"
+          , GenericStatement "call s:map('g#', 'GOctothorpe')"
+          , GenericStatement "call s:map('g*', 'GStar')"
+          , GenericStatement "call s:map('n', 'n')"
           , GenericStatement "if has('autocmd') && has('extra_search')"
           , GenericStatement "augroup LoupeCleanUp"
           , GenericStatement "autocmd!"
@@ -1129,6 +1198,384 @@ Project
           , Plaintext "."
           ]
       ]
+  , MappingAnnotation "<Plug>(LoupeOctothorpe)"
+  , Paragraph
+      [ Plaintext "Loupe"
+      , Whitespace
+      , Plaintext "maps"
+      , Whitespace
+      , Link "#"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Link "<Plug>(LoupeOctothorpe)"
+      , Whitespace
+      , Plaintext "in"
+      , Whitespace
+      , Plaintext "order"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Plaintext "implement"
+      , Whitespace
+      , Plaintext "custom"
+      , Whitespace
+      , Plaintext "highlighting"
+      , Whitespace
+      , Plaintext "and"
+      , Whitespace
+      , Plaintext "line-centering"
+      , Whitespace
+      , Plaintext "for"
+      , Whitespace
+      , Plaintext "the"
+      , Whitespace
+      , Plaintext "current"
+      , Whitespace
+      , Plaintext "match."
+      ]
+  , Paragraph
+      [ Plaintext "To"
+      , Whitespace
+      , Plaintext "prevent"
+      , Whitespace
+      , Plaintext "this"
+      , Whitespace
+      , Plaintext "from"
+      , Whitespace
+      , Plaintext "happening,"
+      , Whitespace
+      , Plaintext "create"
+      , Whitespace
+      , Plaintext "an"
+      , Whitespace
+      , Plaintext "alternate"
+      , Whitespace
+      , Plaintext "mapping"
+      , Whitespace
+      , Plaintext "in"
+      , Whitespace
+      , Plaintext "your"
+      , Whitespace
+      , Link ".vimrc"
+      , Plaintext ":"
+      ]
+  , Fenced [ "nmap <Nop> <Plug>(LoupeOctothorpe)" ]
+  , MappingAnnotation "<Plug>(LoupeStar)"
+  , Paragraph
+      [ Plaintext "Loupe"
+      , Whitespace
+      , Plaintext "maps"
+      , Whitespace
+      , Link "star"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Link "<Plug>(LoupeStar)"
+      , Whitespace
+      , Plaintext "in"
+      , Whitespace
+      , Plaintext "order"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Plaintext "implement"
+      , Whitespace
+      , Plaintext "custom"
+      , Whitespace
+      , Plaintext "highlighting"
+      , Whitespace
+      , Plaintext "and"
+      , Whitespace
+      , Plaintext "line-centering"
+      , Whitespace
+      , Plaintext "for"
+      , Whitespace
+      , Plaintext "the"
+      , Whitespace
+      , Plaintext "current"
+      , Whitespace
+      , Plaintext "match."
+      ]
+  , Paragraph
+      [ Plaintext "To"
+      , Whitespace
+      , Plaintext "prevent"
+      , Whitespace
+      , Plaintext "this"
+      , Whitespace
+      , Plaintext "from"
+      , Whitespace
+      , Plaintext "happening,"
+      , Whitespace
+      , Plaintext "create"
+      , Whitespace
+      , Plaintext "an"
+      , Whitespace
+      , Plaintext "alternate"
+      , Whitespace
+      , Plaintext "mapping"
+      , Whitespace
+      , Plaintext "in"
+      , Whitespace
+      , Plaintext "your"
+      , Whitespace
+      , Link ".vimrc"
+      , Plaintext ":"
+      ]
+  , Fenced [ "nmap <Nop> <Plug>(LoupeStar)" ]
+  , MappingAnnotation "<Plug>(LoupeN)"
+  , Paragraph
+      [ Plaintext "Loupe"
+      , Whitespace
+      , Plaintext "maps"
+      , Whitespace
+      , Link "N"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Link "<Plug>(LoupeN)"
+      , Whitespace
+      , Plaintext "in"
+      , Whitespace
+      , Plaintext "order"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Plaintext "implement"
+      , Whitespace
+      , Plaintext "custom"
+      , Whitespace
+      , Plaintext "highlighting"
+      , Whitespace
+      , Plaintext "and"
+      , Whitespace
+      , Plaintext "line-centering"
+      , Whitespace
+      , Plaintext "for"
+      , Whitespace
+      , Plaintext "the"
+      , Whitespace
+      , Plaintext "current"
+      , Whitespace
+      , Plaintext "match."
+      ]
+  , Paragraph
+      [ Plaintext "To"
+      , Whitespace
+      , Plaintext "prevent"
+      , Whitespace
+      , Plaintext "this"
+      , Whitespace
+      , Plaintext "from"
+      , Whitespace
+      , Plaintext "happening,"
+      , Whitespace
+      , Plaintext "create"
+      , Whitespace
+      , Plaintext "an"
+      , Whitespace
+      , Plaintext "alternate"
+      , Whitespace
+      , Plaintext "mapping"
+      , Whitespace
+      , Plaintext "in"
+      , Whitespace
+      , Plaintext "your"
+      , Whitespace
+      , Link ".vimrc"
+      , Plaintext ":"
+      ]
+  , Fenced [ "nmap <Nop> <Plug>(LoupeN)" ]
+  , MappingAnnotation "<Plug>(LoupeGOctothorpe)"
+  , Paragraph
+      [ Plaintext "Loupe"
+      , Whitespace
+      , Plaintext "maps"
+      , Whitespace
+      , Link "g#"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Link "<Plug>(LoupeGOctothorpe)"
+      , Whitespace
+      , Plaintext "in"
+      , Whitespace
+      , Plaintext "order"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Plaintext "implement"
+      , Whitespace
+      , Plaintext "custom"
+      , Whitespace
+      , Plaintext "highlighting"
+      , Whitespace
+      , Plaintext "and"
+      , Whitespace
+      , Plaintext "line-centering"
+      , Whitespace
+      , Plaintext "for"
+      , Whitespace
+      , Plaintext "the"
+      , Whitespace
+      , Plaintext "current"
+      , Whitespace
+      , Plaintext "match."
+      ]
+  , Paragraph
+      [ Plaintext "To"
+      , Whitespace
+      , Plaintext "prevent"
+      , Whitespace
+      , Plaintext "this"
+      , Whitespace
+      , Plaintext "from"
+      , Whitespace
+      , Plaintext "happening,"
+      , Whitespace
+      , Plaintext "create"
+      , Whitespace
+      , Plaintext "an"
+      , Whitespace
+      , Plaintext "alternate"
+      , Whitespace
+      , Plaintext "mapping"
+      , Whitespace
+      , Plaintext "in"
+      , Whitespace
+      , Plaintext "your"
+      , Whitespace
+      , Link ".vimrc"
+      , Plaintext ":"
+      ]
+  , Fenced [ "nmap <Nop> <Plug>(LoupeGOctothorpe)" ]
+  , MappingAnnotation "<Plug>(LoupeGStar)"
+  , Paragraph
+      [ Plaintext "Loupe"
+      , Whitespace
+      , Plaintext "maps"
+      , Whitespace
+      , Link "gstar"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Link "<Plug>(LoupeGStar)"
+      , Whitespace
+      , Plaintext "in"
+      , Whitespace
+      , Plaintext "order"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Plaintext "implement"
+      , Whitespace
+      , Plaintext "custom"
+      , Whitespace
+      , Plaintext "highlighting"
+      , Whitespace
+      , Plaintext "and"
+      , Whitespace
+      , Plaintext "line-centering"
+      , Whitespace
+      , Plaintext "for"
+      , Whitespace
+      , Plaintext "the"
+      , Whitespace
+      , Plaintext "current"
+      , Whitespace
+      , Plaintext "match."
+      ]
+  , Paragraph
+      [ Plaintext "To"
+      , Whitespace
+      , Plaintext "prevent"
+      , Whitespace
+      , Plaintext "this"
+      , Whitespace
+      , Plaintext "from"
+      , Whitespace
+      , Plaintext "happening,"
+      , Whitespace
+      , Plaintext "create"
+      , Whitespace
+      , Plaintext "an"
+      , Whitespace
+      , Plaintext "alternate"
+      , Whitespace
+      , Plaintext "mapping"
+      , Whitespace
+      , Plaintext "in"
+      , Whitespace
+      , Plaintext "your"
+      , Whitespace
+      , Link ".vimrc"
+      , Plaintext ":"
+      ]
+  , Fenced [ "nmap <Nop> <Plug>(LoupeGStar)" ]
+  , MappingAnnotation "<Plug>(Loupen)"
+  , Paragraph
+      [ Plaintext "Loupe"
+      , Whitespace
+      , Plaintext "maps"
+      , Whitespace
+      , Link "n"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Link "<Plug>(Loupen)"
+      , Whitespace
+      , Plaintext "in"
+      , Whitespace
+      , Plaintext "order"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Plaintext "implement"
+      , Whitespace
+      , Plaintext "custom"
+      , Whitespace
+      , Plaintext "highlighting"
+      , Whitespace
+      , Plaintext "and"
+      , Whitespace
+      , Plaintext "line-centering"
+      , Whitespace
+      , Plaintext "for"
+      , Whitespace
+      , Plaintext "the"
+      , Whitespace
+      , Plaintext "current"
+      , Whitespace
+      , Plaintext "match."
+      ]
+  , Paragraph
+      [ Plaintext "To"
+      , Whitespace
+      , Plaintext "prevent"
+      , Whitespace
+      , Plaintext "this"
+      , Whitespace
+      , Plaintext "from"
+      , Whitespace
+      , Plaintext "happening,"
+      , Whitespace
+      , Plaintext "create"
+      , Whitespace
+      , Plaintext "an"
+      , Whitespace
+      , Plaintext "alternate"
+      , Whitespace
+      , Plaintext "mapping"
+      , Whitespace
+      , Plaintext "in"
+      , Whitespace
+      , Plaintext "your"
+      , Whitespace
+      , Link ".vimrc"
+      , Plaintext ":"
+      ]
+  , Fenced [ "nmap <Nop> <Plug>(Loupen)" ]
   , OptionsAnnotation
   , OptionAnnotation
       "g:LoupeHighlightGroup" "string" (Just "IncSearch")
@@ -1380,6 +1827,23 @@ Project
       , Plaintext "0:"
       ]
   , Fenced [ "let g:LoupeCenterResults=0" ]
+  , FunctionsAnnotation
+  , FunctionAnnotation "loupe#hlmatch"
+  , Paragraph
+      [ Plaintext "Apply"
+      , Whitespace
+      , Plaintext "highlighting"
+      , Whitespace
+      , Plaintext "to"
+      , Whitespace
+      , Plaintext "the"
+      , Whitespace
+      , Plaintext "current"
+      , Whitespace
+      , Plaintext "search"
+      , Whitespace
+      , Plaintext "match."
+      ]
   , FooterAnnotation
   , HeadingAnnotation "Overrides"
   , Paragraph
@@ -1425,7 +1889,32 @@ Project
       , Whitespace
       , Plaintext "box\""
       , Whitespace
-      , Plaintext "experience:"
+      , Plaintext "experience."
+      , Whitespace
+      , Plaintext "The"
+      , Whitespace
+      , Plaintext "following"
+      , Whitespace
+      , Plaintext "overrides"
+      , Whitespace
+      , Plaintext "will"
+      , Whitespace
+      , Plaintext "be"
+      , Whitespace
+      , Plaintext "set"
+      , Whitespace
+      , Plaintext "unless"
+      , Whitespace
+      , Plaintext "suppressed"
+      , Whitespace
+      , Plaintext "or"
+      , Whitespace
+      , Plaintext "overridden"
+      , Whitespace
+      , Plaintext "(see"
+      , Whitespace
+      , Link "loupe-suppress-overrides"
+      , Plaintext "):"
       ]
   , IndentAnnotation
   , LinkTargets [ "loupe-history-override" ]
@@ -1651,6 +2140,9 @@ Project
       , Plaintext "characters)."
       ]
   , DedentAnnotation
+  , LinkTargets [ "loupe-suppress-overrides" ]
+  , SubheadingAnnotation
+      "Preventing Loupe overrides from taking effect"
   , Paragraph
       [ Plaintext "To"
       , Whitespace
@@ -2400,6 +2892,60 @@ Project
           [ Plaintext "https://www.youtube.com/watch?v=aHm36-na4-4" ]
       ]
   , HeadingAnnotation "History"
+  , Paragraph
+      [ Plaintext "1.1"
+      , Whitespace
+      , Plaintext "(15"
+      , Whitespace
+      , Plaintext "June"
+      , Whitespace
+      , Plaintext "2016)"
+      ]
+  , List
+      [ ListItem
+          [ Plaintext "Make"
+          , Whitespace
+          , Plaintext "compatible"
+          , Whitespace
+          , Plaintext "with"
+          , Whitespace
+          , Plaintext "older"
+          , Whitespace
+          , Plaintext "versions"
+          , Whitespace
+          , Plaintext "of"
+          , Whitespace
+          , Plaintext "Vim"
+          , Whitespace
+          , Plaintext "that"
+          , Whitespace
+          , Plaintext "do"
+          , Whitespace
+          , Plaintext "not"
+          , Whitespace
+          , Plaintext "have"
+          , Whitespace
+          , Link "v:hlsearch"
+          , Plaintext "."
+          ]
+      , ListItem
+          [ Plaintext "Add"
+          , Whitespace
+          , Plaintext "support"
+          , Whitespace
+          , Plaintext "for"
+          , Whitespace
+          , Plaintext "special"
+          , Whitespace
+          , Plaintext "delimiters"
+          , Whitespace
+          , Plaintext "with"
+          , Whitespace
+          , Link ":substitute"
+          , Whitespace
+          , Plaintext "command."
+          ]
+      ]
   , SubheadingAnnotation "1.0 (28 December 2015)"
   , List
       [ ListItem
index 585070daae96c98137a6de978b085beab6bfd394..dab99512e6803b1768539d5c10af6be77541468f 100644 (file)
@@ -68,6 +68,66 @@ Note that Loupe will not try to set up its <leader>n mapping if any of the follo
 - An alternative mapping for <strong>[`<Plug>(LoupeClearHighlight)`](#user-content-plugloupeclearhighlight)</strong> has already been set up from a <strong>`.vimrc`</strong>.
 - The mapping has been suppressed by setting <strong>`g:LoupeClearHighlightMap`</strong> to 1 in your <strong>`.vimrc`</strong>.
 
+### `<Plug>(LoupeOctothorpe)`<a name="loupe-plugloupeoctothorpe" href="#user-content-loupe-plugloupeoctothorpe"></a>
+
+Loupe maps <strong>`#`</strong> to <strong>[`<Plug>(LoupeOctothorpe)`](#user-content-plugloupeoctothorpe)</strong> in order to implement custom highlighting and line-centering for the current match.
+
+To prevent this from happening, create an alternate mapping in your <strong>`.vimrc`</strong>:
+
+```
+nmap <Nop> <Plug>(LoupeOctothorpe)
+```
+
+### `<Plug>(LoupeStar)`<a name="loupe-plugloupestar" href="#user-content-loupe-plugloupestar"></a>
+
+Loupe maps <strong>`star`</strong> to <strong>[`<Plug>(LoupeStar)`](#user-content-plugloupestar)</strong> in order to implement custom highlighting and line-centering for the current match.
+
+To prevent this from happening, create an alternate mapping in your <strong>`.vimrc`</strong>:
+
+```
+nmap <Nop> <Plug>(LoupeStar)
+```
+
+### `<Plug>(LoupeN)`<a name="loupe-plugloupen" href="#user-content-loupe-plugloupen"></a>
+
+Loupe maps <strong>`N`</strong> to <strong>[`<Plug>(LoupeN)`](#user-content-plugloupen)</strong> in order to implement custom highlighting and line-centering for the current match.
+
+To prevent this from happening, create an alternate mapping in your <strong>`.vimrc`</strong>:
+
+```
+nmap <Nop> <Plug>(LoupeN)
+```
+
+### `<Plug>(LoupeGOctothorpe)`<a name="loupe-plugloupegoctothorpe" href="#user-content-loupe-plugloupegoctothorpe"></a>
+
+Loupe maps <strong>`g#`</strong> to <strong>[`<Plug>(LoupeGOctothorpe)`](#user-content-plugloupegoctothorpe)</strong> in order to implement custom highlighting and line-centering for the current match.
+
+To prevent this from happening, create an alternate mapping in your <strong>`.vimrc`</strong>:
+
+```
+nmap <Nop> <Plug>(LoupeGOctothorpe)
+```
+
+### `<Plug>(LoupeGStar)`<a name="loupe-plugloupegstar" href="#user-content-loupe-plugloupegstar"></a>
+
+Loupe maps <strong>`gstar`</strong> to <strong>[`<Plug>(LoupeGStar)`](#user-content-plugloupegstar)</strong> in order to implement custom highlighting and line-centering for the current match.
+
+To prevent this from happening, create an alternate mapping in your <strong>`.vimrc`</strong>:
+
+```
+nmap <Nop> <Plug>(LoupeGStar)
+```
+
+### `<Plug>(Loupen)`<a name="loupe-plugloupen" href="#user-content-loupe-plugloupen"></a>
+
+Loupe maps <strong>`n`</strong> to <strong>[`<Plug>(Loupen)`](#user-content-plugloupen)</strong> in order to implement custom highlighting and line-centering for the current match.
+
+To prevent this from happening, create an alternate mapping in your <strong>`.vimrc`</strong>:
+
+```
+nmap <Nop> <Plug>(Loupen)
+```
+
 ## Options<a name="loupe-options" href="#user-content-loupe-options"></a>
 
 <p align="right"><a name="gloupehighlightgroup" href="#user-content-gloupehighlightgroup"><code>g:LoupeHighlightGroup</code></a></p>
@@ -117,9 +177,16 @@ Controls whether the match's line is vertically centered within the window when
 let g:LoupeCenterResults=0
 ```
 
+## Functions<a name="loupe-functions" href="#user-content-loupe-functions"></a>
+
+<p align="right"><a name="loupehlmatch" href="#user-content-loupehlmatch"><code>loupe#hlmatch()</code></a></p>
+### `loupe#hlmatch()`<a name="loupe-loupehlmatch" href="#user-content-loupe-loupehlmatch"></a>
+
+Apply highlighting to the current search match.
+
 ## Overrides<a name="loupe-overrides" href="#user-content-loupe-overrides"></a>
 
-Loupe sets a number of search-related Vim settings to reasonable defaults in order to provide a good "out of the box" experience:
+Loupe sets a number of search-related Vim settings to reasonable defaults in order to provide a good "out of the box" experience. The following overrides will be set unless suppressed or overridden (see <strong>[`loupe-suppress-overrides`](#user-content-loupe-suppress-overrides)</strong>):
 
 <p align="right"><a name="loupe-history-override" href="#user-content-loupe-history-override"><code>loupe-history-override</code></a></p>
 'history'
@@ -151,6 +218,9 @@ Adds "s", which suppresses the display of "search hit BOTTOM, continuing at TOP"
 
 Turned on (overrides 'ignorecase', making the search pattern case-sensitive whenever it containers uppercase characters).
 
+<p align="right"><a name="loupe-suppress-overrides" href="#user-content-loupe-suppress-overrides"><code>loupe-suppress-overrides</code></a></p>
+### Preventing Loupe overrides from taking effect<a name="loupe-preventing-loupe-overrides-from-taking-effect" href="#user-content-loupe-preventing-loupe-overrides-from-taking-effect"></a>
+
 To override any of these choices, you can place overrides in an <strong>`after-directory`</strong> (ie. `~/.vim/after/plugin/loupe.vim`). For example:
 
 ```
@@ -249,6 +319,11 @@ Which he discussed in his "More Instantly Better Vim" presentation at OSCON 2013
 
 ## History<a name="loupe-history" href="#user-content-loupe-history"></a>
 
+1.1 (15 June 2016)
+
+- Make compatible with older versions of Vim that do not have <strong>`v:hlsearch`</strong>.
+- Add support for special delimiters with <strong>`:substitute`</strong> command.
+
 ### 1.0 (28 December 2015)<a name="loupe-10-28-december-2015" href="#user-content-loupe-10-28-december-2015"></a>
 
 - Renamed the <strong>`<Plug>LoupeClearHighlight`</strong> mapping to <strong>[`<Plug>(LoupeClearHighlight)`](#user-content-plugloupeclearhighlight)</strong>.
index 517d3f0b133db26a73620c946f42d87e0ab95625..c3bece515476a1d135323f0af2c886305d44a99c 100644 (file)
@@ -6,13 +6,14 @@ CONTENTS                                                        *loupe-contents*
 2. Installation   |loupe-installation|
 3. Mappings       |loupe-mappings|
 4. Options        |loupe-options|
-5. Overrides      |loupe-overrides|
-6. Related        |loupe-related|
-7. Website        |loupe-website|
-8. License        |loupe-license|
-9. Development    |loupe-development|
-10. Authors       |loupe-authors|
-11. History       |loupe-history|
+5. Functions      |loupe-functions|
+6. Overrides      |loupe-overrides|
+7. Related        |loupe-related|
+8. Website        |loupe-website|
+9. License        |loupe-license|
+10. Development   |loupe-development|
+11. Authors       |loupe-authors|
+12. History       |loupe-history|
 
 INTRO                                                              *loupe-intro*
 
@@ -97,6 +98,60 @@ following are true:
 - The mapping has been suppressed by setting |g:LoupeClearHighlightMap| to 1
   in your |.vimrc|.
 
+
+                                                       *<Plug>(LoupeOctothorpe)*
+Loupe maps |#| to |<Plug>(LoupeOctothorpe)| in order to implement custom
+highlighting and line-centering for the current match.
+
+To prevent this from happening, create an alternate mapping in your |.vimrc|:
+>
+    nmap <Nop> <Plug>(LoupeOctothorpe)
+<
+
+                                                             *<Plug>(LoupeStar)*
+Loupe maps |star| to |<Plug>(LoupeStar)| in order to implement custom
+highlighting and line-centering for the current match.
+
+To prevent this from happening, create an alternate mapping in your |.vimrc|:
+>
+    nmap <Nop> <Plug>(LoupeStar)
+<
+
+                                                                *<Plug>(LoupeN)*
+Loupe maps |N| to |<Plug>(LoupeN)| in order to implement custom highlighting and
+line-centering for the current match.
+
+To prevent this from happening, create an alternate mapping in your |.vimrc|:
+>
+    nmap <Nop> <Plug>(LoupeN)
+<
+
+                                                      *<Plug>(LoupeGOctothorpe)*
+Loupe maps |g#| to |<Plug>(LoupeGOctothorpe)| in order to implement custom
+highlighting and line-centering for the current match.
+
+To prevent this from happening, create an alternate mapping in your |.vimrc|:
+>
+    nmap <Nop> <Plug>(LoupeGOctothorpe)
+<
+
+                                                            *<Plug>(LoupeGStar)*
+Loupe maps |gstar| to |<Plug>(LoupeGStar)| in order to implement custom
+highlighting and line-centering for the current match.
+
+To prevent this from happening, create an alternate mapping in your |.vimrc|:
+>
+    nmap <Nop> <Plug>(LoupeGStar)
+<
+
+                                                                *<Plug>(Loupen)*
+Loupe maps |n| to |<Plug>(Loupen)| in order to implement custom highlighting and
+line-centering for the current match.
+
+To prevent this from happening, create an alternate mapping in your |.vimrc|:
+>
+    nmap <Nop> <Plug>(Loupen)
+<
 OPTIONS                                                          *loupe-options*
 
 
@@ -148,10 +203,17 @@ when jumping (via |n|, |N| etc). To disable, set to 0:
 >
     let g:LoupeCenterResults=0
 <
+FUNCTIONS                                                      *loupe-functions*
+
+loupe#hlmatch()                                                *loupe#hlmatch()*
+
+Apply highlighting to the current search match.
+
 OVERRIDES                                                      *loupe-overrides*
 
 Loupe sets a number of search-related Vim settings to reasonable defaults in
-order to provide a good "out of the box" experience:
+order to provide a good "out of the box" experience. The following overrides
+will be set unless suppressed or overridden (see |loupe-suppress-overrides|):
 
 
                                                         *loupe-history-override*
@@ -195,6 +257,10 @@ TOP" and "search hit TOP, continuing at BOTTOM" messages.
 Turned on (overrides 'ignorecase', making the search pattern case-sensitive
 whenever it containers uppercase characters).
 
+
+                                                      *loupe-suppress-overrides*
+Preventing Loupe overrides from taking effect ~
+
 To override any of these choices, you can place overrides in an
 |after-directory| (ie. `~/.vim/after/plugin/loupe.vim`). For example:
 >
@@ -304,6 +370,11 @@ Which he discussed in his "More Instantly Better Vim" presentation at OSCON
 
 HISTORY                                                          *loupe-history*
 
+1.1 (15 June 2016)
+
+- Make compatible with older versions of Vim that do not have |v:hlsearch|.
+- Add support for special delimiters with |:substitute| command.
+
 1.0 (28 December 2015) ~
 
 - Renamed the |<Plug>LoupeClearHighlight| mapping to
diff --git a/tests/fixtures/integration/loupe/input/autoload/loupe.vim b/tests/fixtures/integration/loupe/input/autoload/loupe.vim
new file mode 100644 (file)
index 0000000..4c0a05a
--- /dev/null
@@ -0,0 +1,43 @@
+" Copyright 2015-present Greg Hurrell. All rights reserved.
+" Licensed under the terms of the BSD 2-clause license.
+
+""
+" @function loupe#hlmatch
+"
+" Apply highlighting to the current search match.
+"
+function! loupe#hlmatch() abort
+  ""
+  " @option g:LoupeHighlightGroup string IncSearch
+  " Specifies the |:highlight| group used to emphasize the match currently under
+  " the cursor for the current search pattern. Defaults to "IncSearch" (ie.
+  " |hl-IncSearch|). For example:
+  "
+  " ```
+  " let g:LoupeHighlightGroup='Error'
+  " ```
+  "
+  " To prevent any special highlighting from being applied, set this option to
+  " "" (ie. the empty string).
+  let l:highlight=get(g:, 'LoupeHighlightGroup', 'IncSearch')
+  if empty(l:highlight)
+    return
+  endif
+
+  if has('autocmd')
+    augroup LoupeHightlightMatch
+      autocmd!
+    augroup END
+  endif
+
+  call loupe#private#clear_highlight()
+
+  " \c case insensitive
+  " \%# current cursor position
+  " @/ current search pattern
+  let l:pattern='\c\%#' . @/
+
+  if exists('*matchadd')
+    let w:loupe_hlmatch=matchadd(l:highlight, l:pattern)
+  endif
+endfunction
index c14a7c9ea78683eafd1eb8f666a60f4d8505f996..245a341761efd0288b4b5bbf28aaada211e58638 100644 (file)
@@ -3,20 +3,22 @@
 
 " Dynamically returns "/" or "/\v" depending on the location of the just-typed
 " "/" within the command-line. Only "/" that looks to be at the start of a
-" command gets replaced.
+" command gets replaced. The "slash" is itself configurable via the `slash`
+" argument, meaning that this function can be used in conjunction with other
+" pattern delimiters like "?" and "@" etc (ie. "?" -> "?\v", "@" -> "@\v").
 "
 " Doesn't handle the full list of possible range types (specified in `:h
 " cmdline-ranges`), but catches the most common ones.
-function! loupe#private#very_magic_slash() abort
+function! loupe#private#very_magic_slash(slash) abort
   if getcmdtype() != ':'
-    return '/'
+    return a:slash
   endif
 
   " For simplicity, only consider "/" typed at the end of the command-line.
   let l:pos=getcmdpos()
   let l:cmd=getcmdline()
   if len(l:cmd) + 1 != l:pos
-    return '/'
+    return a:slash
   endif
 
   " Skip over ranges
@@ -30,10 +32,10 @@ function! loupe#private#very_magic_slash() abort
   endwhile
 
   if index(['g', 's', 'v'], l:cmd) != -1
-    return loupe#private#prepare_highlight('/\v')
+    return loupe#private#prepare_highlight(a:slash . '\v')
   endif
 
-  return '/'
+  return a:slash
 endfunction
 
 function! s:strip_ranges(cmdline)
@@ -68,7 +70,7 @@ function! loupe#private#prepare_highlight(result) abort
   if has('autocmd')
     augroup LoupeHightlightMatch
       autocmd!
-      autocmd CursorMoved * :call loupe#private#hlmatch()
+      autocmd CursorMoved * :call loupe#hlmatch()
     augroup END
   endif
   return a:result
@@ -93,44 +95,7 @@ endfunction
 " from another window and we should clean up the straggling match and the
 " window-local variable.
 function! loupe#private#cleanup() abort
-  if !v:hlsearch
+  if !exists('v:hlsearch') || !v:hlsearch
     call loupe#private#clear_highlight()
   endif
 endfunction
-
-" Apply highlighting to the current search match.
-function! loupe#private#hlmatch() abort
-  ""
-  " @option g:LoupeHighlightGroup string IncSearch
-  " Specifies the |:highlight| group used to emphasize the match currently under
-  " the cursor for the current search pattern. Defaults to "IncSearch" (ie.
-  " |hl-IncSearch|). For example:
-  "
-  " ```
-  " let g:LoupeHighlightGroup='Error'
-  " ```
-  "
-  " To prevent any special highlighting from being applied, set this option to
-  " "" (ie. the empty string).
-  let l:highlight=get(g:, 'LoupeHighlightGroup', 'IncSearch')
-  if empty(l:highlight)
-    return
-  endif
-
-  if has('autocmd')
-    augroup LoupeHightlightMatch
-      autocmd!
-    augroup END
-  endif
-
-  call loupe#private#clear_highlight()
-
-  " \c case insensitive
-  " \%# current cursor position
-  " @/ current search pattern
-  let l:pattern='\c\%#' . @/
-
-  if exists('*matchadd')
-    let w:loupe_hlmatch=matchadd(l:highlight, l:pattern)
-  endif
-endfunction
index 2987662d44f82fa1c40bd44ea472020f895e6773..bd05b025a0fe6cf95a5ce11c82b451f94d3ac73a 100644 (file)
@@ -79,7 +79,8 @@
 " # Overrides
 "
 " Loupe sets a number of search-related Vim settings to reasonable defaults in
-" order to provide a good "out of the box" experience:
+" order to provide a good "out of the box" experience. The following overrides
+" will be set unless suppressed or overridden (see |loupe-suppress-overrides|):
 "
 " @indent
 "                                                        *loupe-history-override*
 "
 " @dedent
 "
+"                                                      *loupe-suppress-overrides*
+" ## Preventing Loupe overrides from taking effect
+"
 " To override any of these choices, you can place overrides in an
 " |after-directory| (ie. `~/.vim/after/plugin/loupe.vim`). For example:
 "
 "
 " # History
 "
+" 1.1 (15 June 2016)
+"
+" - Make compatible with older versions of Vim that do not have |v:hlsearch|.
+" - Add support for special delimiters with |:substitute| command.
+"
 " ## 1.0 (28 December 2015)
 "
 " - Renamed the |<Plug>LoupeClearHighlight| mapping to
@@ -288,8 +297,8 @@ set smartcase    " Case-sensitive search if search string includes a capital let
 " ```
 " let g:LoupeClearHighlightMap=0
 " ```
-let s:map=get(g:, 'LoupeClearHighlightMap', 1)
-if s:map
+let s:clear=get(g:, 'LoupeClearHighlightMap', 1)
+if s:clear
   if !hasmapto('<Plug>(LoupeClearHighlight)') && maparg('<leader>n', 'n') ==# ''
     nmap <silent> <unique> <leader>n <Plug>(LoupeClearHighlight)
   endif
@@ -348,27 +357,139 @@ nnoremap <expr> ? loupe#private#prepare_highlight('?' . <SID>MagicString())
 xnoremap <expr> / loupe#private#prepare_highlight('/' . <SID>MagicString())
 xnoremap <expr> ? loupe#private#prepare_highlight('?' . <SID>MagicString())
 if !empty(s:MagicString())
-  cnoremap <expr> / loupe#private#very_magic_slash()
+  " Any single-byte character may be used as a delimiter except \, ", | and
+  " alphanumerics. See `:h E146`.
+  cnoremap <expr> ! loupe#private#very_magic_slash('!')
+  cnoremap <expr> # loupe#private#very_magic_slash('#')
+  cnoremap <expr> $ loupe#private#very_magic_slash('$')
+  cnoremap <expr> % loupe#private#very_magic_slash('%')
+  cnoremap <expr> & loupe#private#very_magic_slash('&')
+  cnoremap <expr> ' loupe#private#very_magic_slash("'")
+  cnoremap <expr> ( loupe#private#very_magic_slash('(')
+  cnoremap <expr> ) loupe#private#very_magic_slash(')')
+  cnoremap <expr> * loupe#private#very_magic_slash('*')
+  cnoremap <expr> + loupe#private#very_magic_slash('+')
+  cnoremap <expr> , loupe#private#very_magic_slash(',')
+  cnoremap <expr> - loupe#private#very_magic_slash('-')
+  cnoremap <expr> . loupe#private#very_magic_slash('.')
+  cnoremap <expr> / loupe#private#very_magic_slash('/')
+  cnoremap <expr> : loupe#private#very_magic_slash(':')
+  cnoremap <expr> ; loupe#private#very_magic_slash(';')
+  cnoremap <expr> < loupe#private#very_magic_slash('<')
+  cnoremap <expr> = loupe#private#very_magic_slash('=')
+  cnoremap <expr> > loupe#private#very_magic_slash('>')
+  cnoremap <expr> ? loupe#private#very_magic_slash('?')
+  cnoremap <expr> @ loupe#private#very_magic_slash('@')
+  cnoremap <expr> [ loupe#private#very_magic_slash('[')
+  cnoremap <expr> ] loupe#private#very_magic_slash(']')
+  cnoremap <expr> ^ loupe#private#very_magic_slash('^')
+  cnoremap <expr> _ loupe#private#very_magic_slash('_')
+  cnoremap <expr> ` loupe#private#very_magic_slash('`')
+  cnoremap <expr> { loupe#private#very_magic_slash('{')
+  cnoremap <expr> } loupe#private#very_magic_slash('}')
+  cnoremap <expr> ~ loupe#private#very_magic_slash('~')
 endif
 
+function! s:map(keys, name)
+  ""
+  " @option g:LoupeCenterResults boolean 1
+  "
+  " Controls whether the match's line is vertically centered within the window
+  " when jumping (via |n|, |N| etc). To disable, set to 0:
+  "
+  " ```
+  " let g:LoupeCenterResults=0
+  " ```
+  let s:center=get(g:, 'LoupeCenterResults', 1)
+  let s:center_string=s:center ? 'zz' : ''
+
+  if !hasmapto('<Plug>(Loupe' . a:name . ')')
+    execute 'nmap <silent> ' . a:keys . ' <Plug>(Loupe' . a:name . ')'
+  endif
+  execute 'nnoremap <silent> <Plug>(Loupe' . a:name . ')' .
+        \ ' ' .
+        \ a:keys .
+        \ s:center_string .
+        \ ':call loupe#hlmatch()<CR>'
+endfunction
+
+""
+" @mapping <Plug>(LoupeOctothorpe)
+"
+" Loupe maps |#| to |<Plug>(LoupeOctothorpe)| in order to implement custom
+" highlighting and line-centering for the current match.
+"
+" To prevent this from happening, create an alternate mapping in your |.vimrc|:
+"
+" ```
+" nmap <Nop> <Plug>(LoupeOctothorpe)
+" ```
+call s:map('#', 'Octothorpe')
+
+""
+" @mapping <Plug>(LoupeStar)
+"
+" Loupe maps |star| to |<Plug>(LoupeStar)| in order to implement custom
+" highlighting and line-centering for the current match.
+"
+" To prevent this from happening, create an alternate mapping in your |.vimrc|:
+"
+" ```
+" nmap <Nop> <Plug>(LoupeStar)
+" ```
+call s:map('*', 'Star')
+
 ""
-" @option g:LoupeCenterResults boolean 1
+" @mapping <Plug>(LoupeN)
 "
-" Controls whether the match's line is vertically centered within the window
-" when jumping (via |n|, |N| etc). To disable, set to 0:
+" Loupe maps |N| to |<Plug>(LoupeN)| in order to implement custom
+" highlighting and line-centering for the current match.
+"
+" To prevent this from happening, create an alternate mapping in your |.vimrc|:
 "
 " ```
-" let g:LoupeCenterResults=0
+" nmap <Nop> <Plug>(LoupeN)
 " ```
-let s:center=get(g:, 'LoupeCenterResults', 1)
-let s:center_string=s:center ? 'zz' : ''
+call s:map('N', 'N')
 
-execute 'nnoremap <silent> # #' . s:center_string . ':call loupe#private#hlmatch()<CR>'
-execute 'nnoremap <silent> * *' . s:center_string . ':call loupe#private#hlmatch()<CR>'
-execute 'nnoremap <silent> N N' . s:center_string . ':call loupe#private#hlmatch()<CR>'
-execute 'nnoremap <silent> g# g#' . s:center_string . ':call loupe#private#hlmatch()<CR>'
-execute 'nnoremap <silent> g* g*' . s:center_string . ':call loupe#private#hlmatch()<CR>'
-execute 'nnoremap <silent> n n' . s:center_string . ':call loupe#private#hlmatch()<CR>'
+""
+" @mapping <Plug>(LoupeGOctothorpe)
+"
+" Loupe maps |g#| to |<Plug>(LoupeGOctothorpe)| in order to implement custom
+" highlighting and line-centering for the current match.
+"
+" To prevent this from happening, create an alternate mapping in your |.vimrc|:
+"
+" ```
+" nmap <Nop> <Plug>(LoupeGOctothorpe)
+" ```
+call s:map('g#', 'GOctothorpe')
+
+""
+" @mapping <Plug>(LoupeGStar)
+"
+" Loupe maps |gstar| to |<Plug>(LoupeGStar)| in order to implement custom
+" highlighting and line-centering for the current match.
+"
+" To prevent this from happening, create an alternate mapping in your |.vimrc|:
+"
+" ```
+" nmap <Nop> <Plug>(LoupeGStar)
+" ```
+call s:map('g*', 'GStar')
+
+""
+" @mapping <Plug>(Loupen)
+"
+" Loupe maps |n| to |<Plug>(Loupen)| in order to implement custom
+" highlighting and line-centering for the current match.
+"
+" To prevent this from happening, create an alternate mapping in your |.vimrc|:
+"
+" ```
+" nmap <Nop> <Plug>(Loupen)
+" ```
+call s:map('n', 'n')
 
 " Clean-up stray `matchadd()` vestiges.
 if has('autocmd') && has('extra_search')