Add loupe files to integration tests
authorGreg Hurrell <greg@hurrell.net>
Sun, 12 Jun 2016 22:33:30 +0000 (15:33 -0700)
committerGreg Hurrell <greg@hurrell.net>
Sun, 12 Jun 2016 22:33:30 +0000 (15:33 -0700)
bin/get
bin/put
tests/fixtures/integration/loupe/golden/ast.golden [new file with mode: 0644]
tests/fixtures/integration/loupe/golden/markdown.golden [new file with mode: 0644]
tests/fixtures/integration/loupe/golden/plaintext.golden [new file with mode: 0644]
tests/fixtures/integration/loupe/input/.gitignore [new file with mode: 0644]
tests/fixtures/integration/loupe/input/LICENSE.txt [new file with mode: 0644]
tests/fixtures/integration/loupe/input/autoload/loupe/private.vim [new file with mode: 0644]
tests/fixtures/integration/loupe/input/plugin/loupe.vim [new file with mode: 0644]

diff --git a/bin/get b/bin/get
index bb5be75bfff9783ebf73cb5ea4c572fff05b87f7..ba9e6fc78143ec038ffaacfe735599e3775946af 100755 (executable)
--- a/bin/get
+++ b/bin/get
@@ -13,8 +13,9 @@ usage() {
   echo
   echo "  bin/get command-t   #"
   echo "  bin/get ferret      #"
-  echo "  bin/put pinnacle    #"
-  echo "  bin/put scalpel     # freshen a specific downstream"
+  echo "  bin/put loupe       #"
+  echo "  bin/put pinnacle    # freshen a specific downstream"
+  echo "  bin/put scalpel     #"
   echo "  bin/put terminus    #"
   echo "  bin/get vim-clipper #"
   echo "  bin/get vim-docvim  #"
@@ -42,13 +43,14 @@ else
       all)
         update command-t
         update ferret
+        update loupe
         update pinnacle
         update scalpel
         update terminus
         update vim-clipper
         update vim-docvim
         ;;
-      command-t|ferret|pinnacle|scalpel|terminus|vim-clipper|vim-docvim)
+      command-t|ferret|loupe|pinnacle|scalpel|terminus|vim-clipper|vim-docvim)
         update "$PROJECT"
         ;;
       *)
diff --git a/bin/put b/bin/put
index 399f009c44157d5fcd99850649b7d32cf0715888..ceec74c2da3c65c61cd0d343bb7c233f90431134 100755 (executable)
--- a/bin/put
+++ b/bin/put
@@ -19,8 +19,9 @@ usage() {
   echo
   echo "  bin/put command-t   #"
   echo "  bin/put ferret      #"
-  echo "  bin/put pinnacle    #"
-  echo "  bin/put scalpel     # freshen a specific downstream"
+  echo "  bin/put loupe       #"
+  echo "  bin/put pinnacle    # freshen a specific downstream"
+  echo "  bin/put scalpel     #"
   echo "  bin/put terminus    #"
   echo "  bin/get vim-clipper #"
   echo "  bin/put vim-docvim  #"
@@ -50,13 +51,14 @@ else
       all)
         update command-t
         update ferret
+        update loupe
         update pinnacle
         update scalpel
         update terminus
         update vim-clipper
         update vim-docvim
         ;;
-      command-t|ferret|pinnacle|scalpel|terminus|vim-clipper|vim-docvim)
+      command-t|ferret|loupe|pinnacle|scalpel|terminus|vim-clipper|vim-docvim)
         update "$PROJECT"
         ;;
       *)
diff --git a/tests/fixtures/integration/loupe/golden/ast.golden b/tests/fixtures/integration/loupe/golden/ast.golden
new file mode 100644 (file)
index 0000000..b50fb6c
--- /dev/null
@@ -0,0 +1,291 @@
+Project
+  [ Project
+      [ Unit
+          [ FunctionDeclaration
+              { functionBang = True
+              , functionName = "loupe#private#very_magic_slash"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if getcmdtype() != ':'"
+                  , GenericStatement "return '/'"
+                  , 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 "endif"
+                  , GenericStatement "while 1"
+                  , LetStatement
+                      { letLexpr = "l:stripped" , letValue = "s:strip_ranges(l:cmd)" }
+                  , GenericStatement "if l:stripped ==# l:cmd"
+                  , GenericStatement "break"
+                  , GenericStatement "else"
+                  , LetStatement { letLexpr = "l:cmd" , letValue = "l:stripped" }
+                  , GenericStatement "endif"
+                  , GenericStatement "endwhile"
+                  , GenericStatement "if index(['g', 's', 'v'], l:cmd) != -1"
+                  , GenericStatement "return loupe#private#prepare_highlight('/\\v')"
+                  , GenericStatement "endif"
+                  , GenericStatement "return '/'"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "s:strip_ranges"
+              , functionArguments = ArgumentList [ Argument "cmdline" ]
+              , functionAttributes = []
+              , functionBody =
+                  [ LetStatement { letLexpr = "l:cmdline" , letValue = "a:cmdline" }
+                  , LetStatement
+                      { letLexpr = "l:modifier" , letValue = "'\\([+-]\\d*\\)*'" }
+                  , LetStatement
+                      { letLexpr = "l:cmdline"
+                      , letValue =
+                          "substitute(l:cmdline, '^\\d\\+' . l:modifier, '', '') \" line number"
+                      }
+                  , LetStatement
+                      { letLexpr = "l:cmdline"
+                      , letValue =
+                          "substitute(l:cmdline, '^\\.' . l:modifier, '', '') \" current line"
+                      }
+                  , LetStatement
+                      { letLexpr = "l:cmdline"
+                      , letValue =
+                          "substitute(l:cmdline, '^$' . l:modifier, '', '') \" last line in file"
+                      }
+                  , LetStatement
+                      { letLexpr = "l:cmdline"
+                      , letValue =
+                          "substitute(l:cmdline, '^%' . l:modifier, '', '') \" entire file"
+                      }
+                  , LetStatement
+                      { letLexpr = "l:cmdline"
+                      , letValue =
+                          "substitute(l:cmdline, \"^'[a-z]\\\\c\" . l:modifier, '', '') \" mark t (or T)"
+                      }
+                  , LetStatement
+                      { letLexpr = "l:cmdline"
+                      , letValue =
+                          "substitute(l:cmdline, \"^'[<>]\" . l:modifier, '', '') \" visual selection marks"
+                      }
+                  , LetStatement
+                      { letLexpr = "l:cmdline"
+                      , letValue =
+                          "substitute(l:cmdline, '^/[^/]\\+/' . l:modifier, '', '') \" /{pattern}/"
+                      }
+                  , LetStatement
+                      { letLexpr = "l:cmdline"
+                      , letValue =
+                          "substitute(l:cmdline, '^?[^?]\\+?' . l:modifier, '', '') \" ?{pattern}?"
+                      }
+                  , LetStatement
+                      { letLexpr = "l:cmdline"
+                      , letValue =
+                          "substitute(l:cmdline, '^\\\\/' . l:modifier, '', '') \" \\/ (next match of previous pattern)"
+                      }
+                  , LetStatement
+                      { letLexpr = "l:cmdline"
+                      , letValue =
+                          "substitute(l:cmdline, '^\\\\?' . l:modifier, '', '') \" \\? (last match of previous pattern)"
+                      }
+                  , LetStatement
+                      { letLexpr = "l:cmdline"
+                      , letValue =
+                          "substitute(l:cmdline, '^\\\\&' . l:modifier, '', '') \" \\& (last match of previous substitution)"
+                      }
+                  , LetStatement
+                      { letLexpr = "l:cmdline"
+                      , letValue = "substitute(l:cmdline, '^,', '', '') \" , (separator)"
+                      }
+                  , LetStatement
+                      { letLexpr = "l:cmdline"
+                      , letValue = "substitute(l:cmdline, '^;', '', '') \" ; (separator)"
+                      }
+                  , GenericStatement "return l:cmdline"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "loupe#private#prepare_highlight"
+              , functionArguments = ArgumentList [ Argument "result" ]
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('autocmd')"
+                  , GenericStatement "augroup LoupeHightlightMatch"
+                  , GenericStatement "autocmd!"
+                  , GenericStatement
+                      "autocmd CursorMoved * :call loupe#private#hlmatch()"
+                  , GenericStatement "augroup END"
+                  , GenericStatement "endif"
+                  , GenericStatement "return a:result"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "loupe#private#clear_highlight"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if exists('w:loupe_hlmatch')"
+                  , GenericStatement "try"
+                  , GenericStatement "call matchdelete(w:loupe_hlmatch)"
+                  , GenericStatement "catch /\\v<(E802|E803)>/"
+                  , GenericStatement "finally"
+                  , UnletStatement
+                      { unletBang = False , unletBody = "w:loupe_hlmatch" }
+                  , GenericStatement "endtry"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "loupe#private#cleanup"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if !v:hlsearch"
+                  , GenericStatement "call loupe#private#clear_highlight()"
+                  , GenericStatement "endif"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "loupe#private#hlmatch"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ 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
+              "if exists('g:LoupeLoaded') || &compatible || v:version < 700"
+          , GenericStatement "finish"
+          , GenericStatement "endif"
+          , LetStatement { letLexpr = "g:LoupeLoaded" , letValue = "1" }
+          , LetStatement
+              { letLexpr = "s:cpoptions" , letValue = "&cpoptions" }
+          , GenericStatement "set cpoptions&vim"
+          , GenericStatement "if &history < 1000"
+          , GenericStatement
+              "set history=1000 \" Longer search and command history (default is 50)."
+          , GenericStatement "endif"
+          , GenericStatement "if has('extra_search')"
+          , GenericStatement "set hlsearch \" Highlight search strings."
+          , GenericStatement
+              "set incsearch \" Incremental search (\"find as you type\")."
+          , GenericStatement "endif"
+          , GenericStatement "set ignorecase \" Ignore case when searching."
+          , GenericStatement
+              "set shortmess+=s \" Don't echo search wrap messages."
+          , GenericStatement
+              "set smartcase \" Case-sensitive search if search string includes a capital letter."
+          , LetStatement
+              { letLexpr = "s:map"
+              , letValue = "get(g:, 'LoupeClearHighlightMap', 1)"
+              }
+          , GenericStatement "if s:map"
+          , GenericStatement
+              "if !hasmapto('<Plug>(LoupeClearHighlight)') && maparg('<leader>n', 'n') ==# ''"
+          , GenericStatement
+              "nmap <silent> <unique> <leader>n <Plug>(LoupeClearHighlight)"
+          , GenericStatement "endif"
+          , GenericStatement "endif"
+          , GenericStatement
+              "nnoremap <silent> <Plug>(LoupeClearHighlight) :nohlsearch<CR> :call loupe#private#clear_highlight()<CR>"
+          , GenericStatement
+              "cabbrev <silent> <expr> noh (getcmdtype() == ':' && getcmdpos() == 4 ? 'noh <bar> call loupe#private#clear_highlight()<CR>' : 'noh')"
+          , GenericStatement
+              "cabbrev <silent> <expr> nohl (getcmdtype() == ':' && getcmdpos() == 5 ? 'nohl <bar> call loupe#private#clear_highlight()<CR>' : 'nohl')"
+          , GenericStatement
+              "cabbrev <silent> <expr> nohls (getcmdtype() == ':' && getcmdpos() == 6 ? 'nohls <bar> call loupe#private#clear_highlight()<CR>' : 'nohls')"
+          , GenericStatement
+              "cabbrev <silent> <expr> nohlse (getcmdtype() == ':' && getcmdpos() == 7 ? 'nohlse <bar> call loupe#private#clear_highlight()<CR>' : 'nohlse')"
+          , GenericStatement
+              "cabbrev <silent> <expr> nohlsea (getcmdtype() == ':' && getcmdpos() == 8 ? 'nohlsea <bar> call loupe#private#clear_highlight()<CR>' : 'nohlsea')"
+          , GenericStatement
+              "cabbrev <silent> <expr> nohlsear (getcmdtype() == ':' && getcmdpos() == 9 ? 'nohlsear <bar> call loupe#private#clear_highlight()<CR>' : 'nohlsear')"
+          , GenericStatement
+              "cabbrev <silent> <expr> nohlsearc (getcmdtype() == ':' && getcmdpos() == 10 ? 'nohlsearc <bar> call loupe#private#clear_highlight()<CR>' : 'nohlsearc')"
+          , GenericStatement
+              "cabbrev <silent> <expr> nohlsearch (getcmdtype() == ':' && getcmdpos() == 11 ? 'nohlsearch <bar> call loupe#private#clear_highlight()<CR>' : 'nohlsearch')"
+          , FunctionDeclaration
+              { functionBang = False
+              , functionName = "s:MagicString"
+              , functionArguments = ArgumentList []
+              , functionAttributes = []
+              , functionBody =
+                  [ LetStatement
+                      { letLexpr = "s:magic"
+                      , letValue = "get(g:, 'LoupeVeryMagic', 1)"
+                      }
+                  , GenericStatement "return s:magic ? '\\v' : ''"
+                  ]
+              }
+          , GenericStatement
+              "nnoremap <expr> / loupe#private#prepare_highlight('/' . <SID>MagicString())"
+          , GenericStatement
+              "nnoremap <expr> ? loupe#private#prepare_highlight('?' . <SID>MagicString())"
+          , GenericStatement
+              "xnoremap <expr> / loupe#private#prepare_highlight('/' . <SID>MagicString())"
+          , GenericStatement
+              "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' : ''"
+              }
+          , GenericStatement
+              "execute 'nnoremap <silent> # #' . s:center_string . ':call loupe#private#hlmatch()<CR>'"
+          , GenericStatement
+              "execute 'nnoremap <silent> * *' . s:center_string . ':call loupe#private#hlmatch()<CR>'"
+          , GenericStatement
+              "execute 'nnoremap <silent> N N' . s:center_string . ':call loupe#private#hlmatch()<CR>'"
+          , GenericStatement
+              "execute 'nnoremap <silent> g# g#' . s:center_string . ':call loupe#private#hlmatch()<CR>'"
+          , GenericStatement
+              "execute 'nnoremap <silent> g* g*' . s:center_string . ':call loupe#private#hlmatch()<CR>'"
+          , GenericStatement
+              "execute 'nnoremap <silent> n n' . s:center_string . ':call loupe#private#hlmatch()<CR>'"
+          , GenericStatement "if has('autocmd') && has('extra_search')"
+          , GenericStatement "augroup LoupeCleanUp"
+          , GenericStatement "autocmd!"
+          , GenericStatement
+              "autocmd WinEnter * :call loupe#private#cleanup()"
+          , GenericStatement "augroup END"
+          , GenericStatement "endif"
+          , LetStatement
+              { letLexpr = "&cpoptions" , letValue = "s:cpoptions" }
+          , UnletStatement { unletBang = False , unletBody = "s:cpoptions" }
+          ]
+      ]
+  ]
diff --git a/tests/fixtures/integration/loupe/golden/markdown.golden b/tests/fixtures/integration/loupe/golden/markdown.golden
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/tests/fixtures/integration/loupe/golden/plaintext.golden b/tests/fixtures/integration/loupe/golden/plaintext.golden
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/tests/fixtures/integration/loupe/input/.gitignore b/tests/fixtures/integration/loupe/input/.gitignore
new file mode 100644 (file)
index 0000000..20cb71e
--- /dev/null
@@ -0,0 +1 @@
+/*.zip
diff --git a/tests/fixtures/integration/loupe/input/LICENSE.txt b/tests/fixtures/integration/loupe/input/LICENSE.txt
new file mode 100644 (file)
index 0000000..3906829
--- /dev/null
@@ -0,0 +1,22 @@
+Copyright 2015-present Greg Hurrell. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/tests/fixtures/integration/loupe/input/autoload/loupe/private.vim b/tests/fixtures/integration/loupe/input/autoload/loupe/private.vim
new file mode 100644 (file)
index 0000000..bae7bb5
--- /dev/null
@@ -0,0 +1,126 @@
+" Copyright 2015-present Greg Hurrell. All rights reserved.
+" Licensed under the terms of the BSD 2-clause license.
+
+" 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.
+"
+" 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
+  if getcmdtype() != ':'
+    return '/'
+  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 '/'
+  endif
+
+  " Skip over ranges
+  while 1
+    let l:stripped=s:strip_ranges(l:cmd)
+    if l:stripped ==# l:cmd
+      break
+    else
+      let l:cmd=l:stripped
+    endif
+  endwhile
+
+  if index(['g', 's', 'v'], l:cmd) != -1
+    return loupe#private#prepare_highlight('/\v')
+  endif
+
+  return '/'
+endfunction
+
+function! s:strip_ranges(cmdline)
+  let l:cmdline=a:cmdline
+
+  " All the range tokens may be followed (several times) by '+' or '-' and an
+  " optional number.
+  let l:modifier='\([+-]\d*\)*'
+
+  " Range tokens as specified in `:h cmdline-ranges`.
+  let l:cmdline=substitute(l:cmdline, '^\d\+' . l:modifier, '', '') " line number
+  let l:cmdline=substitute(l:cmdline, '^\.' . l:modifier, '', '') " current line
+  let l:cmdline=substitute(l:cmdline, '^$' . l:modifier, '', '') " last line in file
+  let l:cmdline=substitute(l:cmdline, '^%' . l:modifier, '', '') " entire file
+  let l:cmdline=substitute(l:cmdline, "^'[a-z]\\c" . l:modifier, '', '') " mark t (or T)
+  let l:cmdline=substitute(l:cmdline, "^'[<>]" . l:modifier, '', '') " visual selection marks
+  let l:cmdline=substitute(l:cmdline, '^/[^/]\+/' . l:modifier, '', '') " /{pattern}/
+  let l:cmdline=substitute(l:cmdline, '^?[^?]\+?' . l:modifier, '', '') " ?{pattern}?
+  let l:cmdline=substitute(l:cmdline, '^\\/' . l:modifier, '', '') " \/ (next match of previous pattern)
+  let l:cmdline=substitute(l:cmdline, '^\\?' . l:modifier, '', '') " \? (last match of previous pattern)
+  let l:cmdline=substitute(l:cmdline, '^\\&' . l:modifier, '', '') " \& (last match of previous substitution)
+
+  " Separators (see: `:h :,` and `:h :;`).
+  let l:cmdline=substitute(l:cmdline, '^,', '', '') " , (separator)
+  let l:cmdline=substitute(l:cmdline, '^;', '', '') " ; (separator)
+
+  return l:cmdline
+endfunction
+
+" Prepare to highlight the match as soon as the cursor moves to it.
+function! loupe#private#prepare_highlight(result) abort
+  if has('autocmd')
+    augroup LoupeHightlightMatch
+      autocmd!
+      autocmd CursorMoved * :call loupe#private#hlmatch()
+    augroup END
+  endif
+  return a:result
+endfunction
+
+" Clear previously applied match highlighting.
+function! loupe#private#clear_highlight() abort
+  if exists('w:loupe_hlmatch')
+    try
+      call matchdelete(w:loupe_hlmatch)
+    catch /\v<(E802|E803)>/
+      " https://github.com/wincent/loupe/issues/1
+    finally
+      unlet w:loupe_hlmatch
+    endtry
+  endif
+endfunction
+
+" Called from WinEnter autocmd to clean up stray `matchadd()` vestiges.
+" If we switch into a window and there is no 'hlsearch' in effect but we do have
+" a `w:loupe_hlmatch` variable, it means that `:nohighight` was probably run
+" from another window and we should clean up the straggling match and the
+" window-local variable.
+function! loupe#private#cleanup() abort
+  if !v:hlsearch
+    call loupe#private#clear_highlight()
+  endif
+endfunction
+
+" Apply highlighting to the current search match.
+function! loupe#private#hlmatch() abort
+  " When g:loupeHighlight is set (and it is set to "IncSearch" by default), use
+  " that highlight group to make the current search result stand out.
+  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
diff --git a/tests/fixtures/integration/loupe/input/plugin/loupe.vim b/tests/fixtures/integration/loupe/input/plugin/loupe.vim
new file mode 100644 (file)
index 0000000..f399121
--- /dev/null
@@ -0,0 +1,85 @@
+" Copyright 2015-present Greg Hurrell. All rights reserved.
+" Licensed under the terms of the BSD 2-clause license.
+
+" Provide users with means to prevent loading, as recommended in `:h
+" write-plugin`.
+if exists('g:LoupeLoaded') || &compatible || v:version < 700
+  finish
+endif
+let g:LoupeLoaded=1
+
+" Temporarily set 'cpoptions' to Vim default as per `:h use-cpo-save`.
+let s:cpoptions=&cpoptions
+set cpoptions&vim
+
+" Reasonable defaults for search-related settings.
+if &history < 1000
+  set history=1000 " Longer search and command history (default is 50).
+endif
+if has('extra_search')
+  set hlsearch   " Highlight search strings.
+  set incsearch  " Incremental search ("find as you type").
+endif
+set ignorecase   " Ignore case when searching.
+set shortmess+=s " Don't echo search wrap messages.
+set smartcase    " Case-sensitive search if search string includes a capital letter.
+
+" Map <leader>n to clear search highlighting.
+let s:map=get(g:, 'LoupeClearHighlightMap', 1)
+if s:map
+  if !hasmapto('<Plug>(LoupeClearHighlight)') && maparg('<leader>n', 'n') ==# ''
+    nmap <silent> <unique> <leader>n <Plug>(LoupeClearHighlight)
+  endif
+endif
+nnoremap <silent> <Plug>(LoupeClearHighlight)
+      \ :nohlsearch<CR>
+      \ :call loupe#private#clear_highlight()<CR>
+
+" Make `:nohlsearch` behave like <Plug>(LoupeClearHighlight).
+cabbrev <silent> <expr> noh (getcmdtype() == ':' && getcmdpos() == 4 ? 'noh <bar> call loupe#private#clear_highlight()<CR>' : 'noh')
+cabbrev <silent> <expr> nohl (getcmdtype() == ':' && getcmdpos() == 5 ? 'nohl <bar> call loupe#private#clear_highlight()<CR>' : 'nohl')
+cabbrev <silent> <expr> nohls (getcmdtype() == ':' && getcmdpos() == 6 ? 'nohls <bar> call loupe#private#clear_highlight()<CR>' : 'nohls')
+cabbrev <silent> <expr> nohlse (getcmdtype() == ':' && getcmdpos() == 7 ? 'nohlse <bar> call loupe#private#clear_highlight()<CR>' : 'nohlse')
+cabbrev <silent> <expr> nohlsea (getcmdtype() == ':' && getcmdpos() == 8 ? 'nohlsea <bar> call loupe#private#clear_highlight()<CR>' : 'nohlsea')
+cabbrev <silent> <expr> nohlsear (getcmdtype() == ':' && getcmdpos() == 9 ? 'nohlsear <bar> call loupe#private#clear_highlight()<CR>' : 'nohlsear')
+cabbrev <silent> <expr> nohlsearc (getcmdtype() == ':' && getcmdpos() == 10 ? 'nohlsearc <bar> call loupe#private#clear_highlight()<CR>' : 'nohlsearc')
+cabbrev <silent> <expr> nohlsearch (getcmdtype() == ':' && getcmdpos() == 11 ? 'nohlsearch <bar> call loupe#private#clear_highlight()<CR>' : 'nohlsearch')
+
+" When g:LoupeVeryMagic is true (and it is by default), make Vim's regexen more
+" Perl-like.
+function s:MagicString()
+  let s:magic=get(g:, 'LoupeVeryMagic', 1)
+  return s:magic ? '\v' : ''
+endfunction
+
+nnoremap <expr> / loupe#private#prepare_highlight('/' . <SID>MagicString())
+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()
+endif
+
+" When g:LoupeCenterResults is true (and it is by default), remain vertically
+" centered when moving to next/previous search.
+let s:center=get(g:, 'LoupeCenterResults', 1)
+let s:center_string=s:center ? 'zz' : ''
+
+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>'
+
+" Clean-up stray `matchadd()` vestiges.
+if has('autocmd') && has('extra_search')
+  augroup LoupeCleanUp
+    autocmd!
+    autocmd WinEnter * :call loupe#private#cleanup()
+  augroup END
+endif
+
+" Restore 'cpoptions' to its former value.
+let &cpoptions=s:cpoptions
+unlet s:cpoptions