Use sorting to produce stable ASTs
authorGreg Hurrell <greg@hurrell.net>
Thu, 16 Jun 2016 15:44:34 +0000 (08:44 -0700)
committerGreg Hurrell <greg@hurrell.net>
Thu, 16 Jun 2016 15:44:34 +0000 (08:44 -0700)
I think we're falling prey to inode iteration order issues, meaning that our AST
is not always the same, depending on which system we run on.

Example failure:

https://travis-ci.org/wincent/docvim/jobs/138109029

Should be fixed by always visiting the files in the same order.

lib/Text/Docvim/CLI.hs
tests/Tasty.hs
tests/fixtures/integration/ferret/golden/ast.golden

index 851cfc551c7cefbaa247e12f3fd4a2464797ff07..205bc12b5e16c625bad3535911316b9ba5292f4b 100644 (file)
@@ -4,6 +4,7 @@
 module Text.Docvim.CLI (run) where
 
 import Control.Monad
+import Data.List
 import Data.Maybe
 import System.FilePath hiding (hasExtension)
 import System.IO
@@ -31,7 +32,7 @@ run :: IO ()
 run = do
   opts <- options
   paths <- readDir (directory opts)
-  let filtered = filter isVimScript paths
+  let filtered = filter isVimScript $ sort paths
   parsed <- mapM (\path -> do
       when (verbose opts) (hPutStrLn stderr ("Parsing " ++ path))
       parse path
index da0635cb3f33fc404cbfe1da6a7f0e5282790c06..d7b3d980f5f3a39c75c6cfbe58fb7b1030bea336 100644 (file)
@@ -125,7 +125,7 @@ integrationTests sources = testGroup "Integration tests" $
       let
         output = do
           inputs <- getFixtures $ source </> "input"
-          contents <- mapM readFile inputs
+          contents <- mapM readFile (sort inputs)
           return $ pack $ normalize $ process contents
         name = takeBaseName source
         golden = "tests/fixtures/integration" </> (takeBaseName source) </> "golden/" ++ kind ++ ".golden"
@@ -189,8 +189,8 @@ main = do
   integrationSources <- getIntegrationFixtures "tests/fixtures/integration"
   defaultMain $ testGroup "Test suite"
     [ unitTests
-    , goldenTests "parser" parserSources p
-    , goldenTests "Markdown printer" markdownSources pm
-    , goldenTests "Vim help printer" vimHelpSources pv
+    , goldenTests "parser" (sort parserSources) p
+    , goldenTests "Markdown printer" (sort markdownSources) pm
+    , goldenTests "Vim help printer" (sort vimHelpSources) pv
     , integrationTests integrationSources
     ]
index 42f5f6427236f594f99fd2ab2465e7975c0aeb9a..2c8c4cbc10d548b7c61ee05cdb942a5641bd2faa 100644 (file)
@@ -829,771 +829,771 @@ Project
   , Fenced [ ":call pathogen#helptags()" ]
   , Project
       [ Unit
-          [ LetStatement { letLexpr = "s:jobs" , letValue = "{}" }
-          , FunctionDeclaration
+          [ FunctionDeclaration
               { functionBang = True
-              , functionName = "s:channel_id"
-              , functionArguments = ArgumentList [ Argument "channel" ]
+              , functionName = "s:delete"
+              , functionArguments =
+                  ArgumentList [ Argument "first" , Argument "last" ]
               , functionAttributes = []
               , functionBody =
-                  [ GenericStatement "return matchstr(a:channel, '\\d\\+')" ]
+                  [ LetStatement { letLexpr = "l:list" , letValue = "getqflist()" }
+                  , LetStatement { letLexpr = "l:line" , letValue = "a:first" }
+                  , GenericStatement "while l:line >= a:first && l:line <= a:last"
+                  , LetStatement { letLexpr = "l:list[l:line - 1]" , letValue = "0" }
+                  , LetStatement { letLexpr = "l:line" , letValue = "l:line + 1" }
+                  , GenericStatement "endwhile"
+                  , GenericStatement "call setqflist(l:list, 'r')"
+                  , GenericStatement "execute 'cc ' . a:first"
+                  , GenericStatement "execute \"normal \\<C-W>\\<C-P>\""
+                  ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "s:info_from_channel"
-              , functionArguments = ArgumentList [ Argument "channel" ]
+              , functionName = "ferret#private#dispatch"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ Empty
+                  , LetStatement
+                      { letLexpr = "l:dispatch"
+                      , letValue = "get(g:, 'FerretDispatch', 1)"
+                      }
+                  , GenericStatement "return l:dispatch && exists(':Make') == 2"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "ferret#private#async"
+              , functionArguments = ArgumentList []
               , functionAttributes = []
               , functionBody =
                   [ LetStatement
-                      { letLexpr = "l:channel_id"
-                      , letValue = "s:channel_id(a:channel)"
-                      }
-                  , GenericStatement "if has_key(s:jobs, l:channel_id)"
-                  , GenericStatement "return s:jobs[l:channel_id]"
-                  , GenericStatement "endif"
+                      { letLexpr = "l:async" , letValue = "get(g:, 'FerretJob', 1)" }
+                  , GenericStatement "return l:async && has('patch-7-4-1829')"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#async#search"
-              , functionArguments =
-                  ArgumentList [ Argument "command" , Argument "ack" ]
+              , functionName = "ferret#private#error"
+              , functionArguments = ArgumentList [ Argument "message" ]
               , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ GenericStatement "call ferret#private#async#cancel()"
-                  , GenericStatement
-                      "call ferret#private#autocmd('FerretAsyncStart')"
-                  , LetStatement
-                      { letLexpr = "l:command_and_args "
-                      , letValue = "extend(split(&grepprg), a:command)"
-                      }
-                  , LetStatement
-                      { letLexpr = "l:job"
-                      , letValue = "job_start(l:command_and_args, {"
-                      }
-                  , GenericStatement
-                      "\\ 'err_cb': 'ferret#private#async#err_cb', 'out_cb': 'ferret#private#async#out_cb', 'close_cb': 'ferret#private#async#close_cb', 'err_mode': 'raw', 'out_mode': 'raw' })"
-                  , LetStatement
-                      { letLexpr = "l:channel" , letValue = "job_getchannel(l:job)" }
-                  , LetStatement
-                      { letLexpr = "l:channel_id"
-                      , letValue = "s:channel_id(l:channel)"
-                      }
-                  , LetStatement
-                      { letLexpr = "s:jobs[l:channel_id]" , letValue = "{" }
+                  [ GenericStatement "call inputsave()"
+                  , GenericStatement "echohl ErrorMsg"
                   , GenericStatement
-                      "\\ 'channel_id': l:channel_id, 'job': l:job, 'errors': [], 'output': [], 'pending_error': '', 'pending_output': '', 'pending_error_length': 0, 'pending_output_length': 0, 'ack': a:ack, 'window': win_getid() }"
+                      "unsilent call input(a:message . ': press ENTER to continue')"
+                  , GenericStatement "echohl NONE"
+                  , GenericStatement "call inputrestore()"
+                  , GenericStatement "unsilent echo"
+                  , GenericStatement "redraw!"
                   ]
               }
-          , LetStatement
-              { letLexpr = "s:max_line_length" , letValue = "32768" }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#async#err_cb"
-              , functionArguments =
-                  ArgumentList [ Argument "channel" , Argument "msg" ]
-              , functionAttributes = []
+              , functionName = "s:parse"
+              , functionArguments = ArgumentList [ Argument "args" ]
+              , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ LetStatement
-                      { letLexpr = "l:info"
-                      , letValue = "s:info_from_channel(a:channel)"
-                      }
-                  , GenericStatement "if type(l:info) == 4"
-                  , LetStatement { letLexpr = "l:start" , letValue = "0" }
-                  , GenericStatement "while 1"
-                  , LetStatement
-                      { letLexpr = "l:idx" , letValue = "match(a:msg, '\\n', l:start)" }
-                  , GenericStatement "if l:idx==-1"
-                  , GenericStatement
-                      "if l:info.pending_error_length < s:max_line_length"
-                  , LetStatement
-                      { letLexpr = "l:rest" , letValue = "strpart(a:msg, l:start)" }
-                  , LetStatement
-                      { letLexpr = "l:length" , letValue = "strlen(l:rest)" }
-                  , LetStatement
-                      { letLexpr = "l:info.pending_error." , letValue = "l:rest" }
+                  [ GenericStatement "if exists('g:ferret_lastsearch')"
+                  , UnletStatement
+                      { unletBang = False , unletBody = "g:ferret_lastsearch" }
+                  , GenericStatement "endif"
+                  , LetStatement { letLexpr = "l:expanded_args" , letValue = "[]" }
+                  , GenericStatement "for l:arg in a:args"
+                  , GenericStatement "if ferret#private#option(l:arg)"
+                  , GenericStatement "call add(l:expanded_args, l:arg)"
+                  , GenericStatement "elseif exists('g:ferret_lastsearch')"
                   , LetStatement
-                      { letLexpr = "l:info.pending_error_length+"
-                      , letValue = "l:length"
+                      { letLexpr = "l:file_args"
+                      , letValue =
+                          "glob(l:arg, 1, 1) \" Ignore 'wildignore', return a list."
                       }
+                  , GenericStatement "if len(l:file_args)"
+                  , GenericStatement "call extend(l:expanded_args, l:file_args)"
+                  , GenericStatement "else"
+                  , GenericStatement "call add(l:expanded_args, l:arg)"
                   , GenericStatement "endif"
-                  , GenericStatement "break"
                   , GenericStatement "else"
-                  , GenericStatement
-                      "if l:info.pending_error_length < s:max_line_length"
                   , LetStatement
-                      { letLexpr = "l:info.pending_error."
-                      , letValue = "strpart(a:msg, l:start, l:idx - l:start)"
-                      }
+                      { letLexpr = "g:ferret_lastsearch" , letValue = "l:arg" }
+                  , GenericStatement "call add(l:expanded_args, l:arg)"
+                  , GenericStatement "endif"
+                  , GenericStatement "endfor"
+                  , GenericStatement "if ferret#private#async()"
+                  , GenericStatement "return l:expanded_args"
                   , GenericStatement "endif"
-                  , GenericStatement "call add(l:info.errors, l:info.pending_error)"
                   , LetStatement
-                      { letLexpr = "l:info.pending_error" , letValue = "''" }
+                      { letLexpr = "l:each_word_shell_escaped"
+                      , letValue = "map(l:expanded_args, 'shellescape(v:val)')"
+                      }
                   , LetStatement
-                      { letLexpr = "l:info.pending_error_length" , letValue = "0" }
-                  , GenericStatement "endif"
-                  , LetStatement { letLexpr = "l:start" , letValue = "l:idx + 1" }
-                  , GenericStatement "endwhile"
+                      { letLexpr = "l:joined"
+                      , letValue = "join(l:each_word_shell_escaped)"
+                      }
+                  , GenericStatement "return escape(l:joined, '<>#')"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "ferret#private#clearautocmd"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "if has('autocmd')"
+                  , GenericStatement "augroup FerretPostQF"
+                  , GenericStatement "autocmd!"
+                  , GenericStatement "augroup END"
                   , GenericStatement "endif"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#async#out_cb"
-              , functionArguments =
-                  ArgumentList [ Argument "channel" , Argument "msg" ]
-              , functionAttributes = []
+              , functionName = "ferret#private#post"
+              , functionArguments = ArgumentList [ Argument "type" ]
+              , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ LetStatement
-                      { letLexpr = "l:info"
-                      , letValue = "s:info_from_channel(a:channel)"
-                      }
-                  , GenericStatement "if type(l:info) == 4"
-                  , LetStatement { letLexpr = "l:start" , letValue = "0" }
-                  , GenericStatement "while 1"
-                  , LetStatement
-                      { letLexpr = "l:idx" , letValue = "match(a:msg, '\\n', l:start)" }
-                  , GenericStatement "if l:idx==-1"
-                  , GenericStatement
-                      "if l:info.pending_output_length < s:max_line_length"
+                  [ GenericStatement "call ferret#private#clearautocmd()"
                   , LetStatement
-                      { letLexpr = "l:rest" , letValue = "strpart(a:msg, l:start)" }
+                      { letLexpr = "l:lastsearch "
+                      , letValue = "get(g:, 'ferret_lastsearch', '')"
+                      }
                   , LetStatement
-                      { letLexpr = "l:length" , letValue = "strlen(l:rest)" }
+                      { letLexpr = "l:qflist "
+                      , letValue = "a:type == 'qf' ? getqflist() : getloclist(0)"
+                      }
                   , LetStatement
-                      { letLexpr = "l:info.pending_output." , letValue = "l:rest" }
+                      { letLexpr = "l:tip "
+                      , letValue = "' [see `:help ferret-quotes`]'"
+                      }
+                  , GenericStatement "if len(l:qflist) == 0"
                   , LetStatement
-                      { letLexpr = "l:info.pending_output_length+"
-                      , letValue = "l:length"
+                      { letLexpr = "l:base "
+                      , letValue =
+                          "'No results for search pattern `' . l:lastsearch . '`'"
                       }
+                  , GenericStatement
+                      "if l:lastsearch =~ '\\v^([' . \"'\" . '\"])[^ \\1]+\\1$'"
+                  , GenericStatement "call ferret#private#error(l:base . l:tip)"
+                  , GenericStatement "else"
+                  , GenericStatement "call ferret#private#error(l:base)"
                   , GenericStatement "endif"
-                  , GenericStatement "break"
                   , GenericStatement "else"
-                  , GenericStatement
-                      "if l:info.pending_output_length < s:max_line_length"
                   , LetStatement
-                      { letLexpr = "l:info.pending_output."
-                      , letValue = "strpart(a:msg, l:start, l:idx - l:start)"
+                      { letLexpr = "l:invalid "
+                      , letValue = "filter(copy(l:qflist), 'v:val.valid == 0')"
                       }
-                  , GenericStatement "endif"
-                  , GenericStatement "call add(l:info.output, l:info.pending_output)"
+                  , GenericStatement "if len(l:invalid) == len(l:qflist)"
+                  , GenericStatement "redraw!"
+                  , GenericStatement "echohl ErrorMsg"
+                  , GenericStatement "for l:item in l:invalid"
+                  , GenericStatement "unsilent echomsg l:item.text"
+                  , GenericStatement "endfor"
+                  , GenericStatement "echohl NONE"
                   , LetStatement
-                      { letLexpr = "l:info.pending_output" , letValue = "''" }
+                      { letLexpr = "l:base "
+                      , letValue = "'Search for `' . l:lastsearch . '` failed'"
+                      }
                   , LetStatement
-                      { letLexpr = "l:info.pending_output_length" , letValue = "0" }
+                      { letLexpr = "l:suffix "
+                      , letValue = "a:type == 'qf' && ferret#private#dispatch() ?"
+                      }
+                  , GenericStatement "\\ ' (run `:messages` to see details)' : ''"
+                  , GenericStatement
+                      "if l:lastsearch =~ '\\v^[' . \"'\" . '\"].+[^' . \"'\" . '\"]$'"
+                  , GenericStatement
+                      "call ferret#private#error(l:base . l:tip . l:suffix)"
+                  , GenericStatement "else"
+                  , GenericStatement "call ferret#private#error(l:base . l:suffix)"
+                  , GenericStatement "endif"
                   , GenericStatement "endif"
-                  , LetStatement { letLexpr = "l:start" , letValue = "l:idx + 1" }
-                  , GenericStatement "endwhile"
                   , GenericStatement "endif"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#async#close_cb"
-              , functionArguments = ArgumentList [ Argument "channel" ]
+              , functionName = "ferret#private#ack"
+              , functionArguments = ArgumentList [ Argument "..." ]
               , functionAttributes = [ "abort" ]
               , functionBody =
                   [ LetStatement
-                      { letLexpr = "l:info"
-                      , letValue = "s:info_from_channel(a:channel)"
-                      }
-                  , GenericStatement "if type(l:info) == 4"
-                  , GenericStatement "call remove(s:jobs, l:info.channel_id)"
-                  , GenericStatement
-                      "call ferret#private#autocmd('FerretAsyncFinish')"
-                  , GenericStatement "if !l:info.ack"
-                  , GenericStatement "call win_gotoid(l:info.window)"
+                      { letLexpr = "l:command" , letValue = "s:parse(a:000)" }
+                  , GenericStatement "call ferret#private#hlsearch()"
+                  , GenericStatement "if empty(&grepprg)"
+                  , GenericStatement "return"
                   , GenericStatement "endif"
-                  , GenericStatement
-                      "call s:finalize_search(l:info.output, l:info.ack)"
-                  , GenericStatement "for l:error in l:info.errors"
-                  , GenericStatement "unsilent echomsg l:error"
-                  , GenericStatement "endfor"
+                  , GenericStatement "if ferret#private#async()"
+                  , GenericStatement "call ferret#private#async#search(l:command, 1)"
+                  , GenericStatement "elseif ferret#private#dispatch()"
+                  , GenericStatement "call ferret#private#dispatch#search(l:command)"
+                  , GenericStatement "else"
+                  , GenericStatement
+                      "call ferret#private#vanilla#search(l:command, 1)"
                   , GenericStatement "endif"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#async#pull"
-              , functionArguments = ArgumentList []
+              , functionName = "ferret#private#lack"
+              , functionArguments = ArgumentList [ Argument "..." ]
               , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ GenericStatement "for l:channel_id in keys(s:jobs)"
-                  , LetStatement
-                      { letLexpr = "l:info" , letValue = "s:jobs[l:channel_id]" }
+                  [ LetStatement
+                      { letLexpr = "l:command" , letValue = "s:parse(a:000)" }
+                  , GenericStatement "call ferret#private#hlsearch()"
+                  , GenericStatement "if empty(&grepprg)"
+                  , GenericStatement "return"
+                  , GenericStatement "endif"
+                  , GenericStatement "if ferret#private#async()"
+                  , GenericStatement "call ferret#private#async#search(l:command, 0)"
+                  , GenericStatement "else"
                   , GenericStatement
-                      "call s:finalize_search(l:info.output, l:info.ack)"
-                  , GenericStatement "endfor"
+                      "call ferret#private#vanilla#search(l:command, 0)"
+                  , GenericStatement "endif"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#async#cancel"
+              , functionName = "ferret#private#hlsearch"
               , functionArguments = ArgumentList []
               , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ LetStatement { letLexpr = "l:canceled" , letValue = "0" }
-                  , GenericStatement "for l:channel_id in keys(s:jobs)"
+                  [ GenericStatement "if has('extra_search')"
+                  , Empty
                   , LetStatement
-                      { letLexpr = "l:info" , letValue = "s:jobs[l:channel_id]" }
-                  , GenericStatement "call job_stop(l:info.job)"
-                  , GenericStatement "call remove(s:jobs, l:channel_id)"
-                  , LetStatement { letLexpr = "l:canceled" , letValue = "1" }
-                  , GenericStatement "endfor"
-                  , GenericStatement "if l:canceled"
+                      { letLexpr = "l:hlsearch"
+                      , letValue = "get(g:, 'FerretHlsearch', &hlsearch)"
+                      }
+                  , GenericStatement "if l:hlsearch"
+                  , LetStatement
+                      { letLexpr = "@/" , letValue = "g:ferret_lastsearch" }
                   , GenericStatement
-                      "call ferret#private#autocmd('FerretAsyncFinish')"
+                      "call feedkeys(\":let &hlsearch=1 | echo \\<CR>\", 'n')"
                   , GenericStatement "endif"
-                  ]
-              }
-          , FunctionDeclaration
-              { functionBang = True
-              , functionName = "ferret#private#async#debug"
-              , functionArguments = ArgumentList []
-              , functionAttributes = [ "abort" ]
-              , functionBody = [ GenericStatement "return s:jobs" ]
-              }
-          , FunctionDeclaration
-              { functionBang = True
-              , functionName = "s:finalize_search"
-              , functionArguments =
-                  ArgumentList [ Argument "output" , Argument "ack" ]
-              , functionAttributes = []
-              , functionBody =
-                  [ GenericStatement "if a:ack"
-                  , GenericStatement "cexpr a:output"
-                  , GenericStatement
-                      "execute get(g:, 'FerretQFHandler', 'botright cwindow')"
-                  , GenericStatement "call ferret#private#post('qf')"
-                  , GenericStatement "else"
-                  , LexprStatement { lexprBang = False , lexprExpr = "a:output" }
-                  , GenericStatement "execute get(g:, 'FerretLLHandler', 'lwindow')"
-                  , GenericStatement "call ferret#private#post('location')"
                   , GenericStatement "endif"
                   ]
               }
-          ]
-      , Unit
-          [ FunctionDeclaration
+          , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#dispatch#search"
+              , functionName = "ferret#private#acks"
               , functionArguments = ArgumentList [ Argument "command" ]
               , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ GenericStatement "if has('autocmd')"
-                  , GenericStatement "augroup FerretPostQF"
-                  , GenericStatement "autocmd!"
+                  [ LetStatement
+                      { letLexpr = "l:matches "
+                      , letValue =
+                          "matchlist(a:command, '\\v\\C^(([^|\"\\\\a-zA-Z0-9]).+\\2.*\\2)([cgeiI]*)$')"
+                      }
+                  , GenericStatement "if !len(l:matches)"
                   , GenericStatement
-                      "autocmd QuickfixCmdPost cgetfile call ferret#private#post('qf')"
-                  , GenericStatement "augroup END"
+                      "call ferret#private#error( 'Ferret: Expected a substitution expression (/foo/bar/); got: ' . a:command )"
+                  , GenericStatement "return"
                   , GenericStatement "endif"
                   , LetStatement
-                      { letLexpr = "l:original_makeprg" , letValue = "&l:makeprg" }
-                  , LetStatement
-                      { letLexpr = "l:original_errorformat"
-                      , letValue = "&l:errorformat"
-                      }
-                  , GenericStatement "try"
-                  , LetStatement
-                      { letLexpr = "&l:makeprg"
-                      , letValue = "&grepprg . ' ' . a:command"
-                      }
-                  , LetStatement
-                      { letLexpr = "&l:errorformat" , letValue = "&grepformat" }
-                  , GenericStatement "echomsg &l:makeprg"
-                  , GenericStatement "Make"
-                  , GenericStatement "catch"
-                  , GenericStatement "call ferret#private#clearautocmd()"
-                  , GenericStatement "finally"
+                      { letLexpr = "l:pattern " , letValue = "l:matches[1]" }
                   , LetStatement
-                      { letLexpr = "&l:makeprg" , letValue = "l:original_makeprg" }
+                      { letLexpr = "l:options " , letValue = "l:matches[3]" }
+                  , GenericStatement "if l:options !~# 'e'"
+                  , LetStatement { letLexpr = "l:options ." , letValue = "'e'" }
+                  , GenericStatement "endif"
+                  , GenericStatement "if l:options !~# 'g'"
+                  , LetStatement { letLexpr = "l:options ." , letValue = "'g'" }
+                  , GenericStatement "endif"
                   , LetStatement
-                      { letLexpr = "&l:errorformat"
-                      , letValue = "l:original_errorformat"
-                      }
-                  , GenericStatement "endtry"
-                  ]
-              }
-          ]
-      , Unit
-          [ FunctionDeclaration
-              { functionBang = True
-              , functionName = "s:finalize_search"
-              , functionArguments =
-                  ArgumentList [ Argument "output" , Argument "ack" ]
-              , functionAttributes = []
-              , functionBody =
-                  [ GenericStatement "if a:ack"
-                  , GenericStatement "cexpr a:output"
+                      { letLexpr = "l:filenames" , letValue = "ferret#private#qargs()" }
+                  , GenericStatement "if l:filenames ==# ''"
                   , GenericStatement
-                      "execute get(g:, 'FerretQFHandler', 'botright cwindow')"
-                  , GenericStatement "call ferret#private#post('qf')"
-                  , GenericStatement "else"
-                  , LexprStatement { lexprBang = False , lexprExpr = "a:output" }
-                  , GenericStatement "execute get(g:, 'FerretLLHandler', 'lwindow')"
-                  , GenericStatement "call ferret#private#post('location')"
+                      "call ferret#private#error( 'Ferret: Quickfix filenames must be present, but there are none ' . '(must use :Ack to find files before :Acks can be used)' )"
+                  , GenericStatement "return"
                   , GenericStatement "endif"
+                  , GenericStatement "execute 'args' l:filenames"
+                  , GenericStatement "call ferret#private#autocmd('FerretWillWrite')"
+                  , GenericStatement
+                      "execute 'argdo' '%s' . l:pattern . l:options . ' | update'"
+                  , GenericStatement "call ferret#private#autocmd('FerretDidWrite')"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#vanilla#search"
-              , functionArguments =
-                  ArgumentList [ Argument "command" , Argument "ack" ]
+              , functionName = "ferret#private#autocmd"
+              , functionArguments = ArgumentList [ Argument "cmd" ]
               , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ LetStatement
-                      { letLexpr = "l:output"
-                      , letValue = "system(&grepprg . ' ' . a:command)"
-                      }
-                  , GenericStatement "call s:finalize_search(l:output, a:ack)"
-                  ]
-              }
-          ]
-      , Unit
-          [ FunctionDeclaration
-              { functionBang = True
-              , functionName = "s:delete"
-              , functionArguments =
-                  ArgumentList [ Argument "first" , Argument "last" ]
-              , functionAttributes = []
-              , functionBody =
-                  [ LetStatement { letLexpr = "l:list" , letValue = "getqflist()" }
-                  , LetStatement { letLexpr = "l:line" , letValue = "a:first" }
-                  , GenericStatement "while l:line >= a:first && l:line <= a:last"
-                  , LetStatement { letLexpr = "l:list[l:line - 1]" , letValue = "0" }
-                  , LetStatement { letLexpr = "l:line" , letValue = "l:line + 1" }
-                  , GenericStatement "endwhile"
-                  , GenericStatement "call setqflist(l:list, 'r')"
-                  , GenericStatement "execute 'cc ' . a:first"
-                  , GenericStatement "execute \"normal \\<C-W>\\<C-P>\""
+                  [ GenericStatement
+                      "if v:version > 703 || v:version == 703 && has('patch438')"
+                  , GenericStatement
+                      "execute 'silent doautocmd <nomodeline> User ' . a:cmd"
+                  , GenericStatement "else"
+                  , GenericStatement "execute 'silent doautocmd User ' . a:cmd"
+                  , GenericStatement "endif"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#dispatch"
-              , functionArguments = ArgumentList []
+              , functionName = "s:split"
+              , functionArguments = ArgumentList [ Argument "str" ]
               , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ Empty
-                  , LetStatement
-                      { letLexpr = "l:dispatch"
-                      , letValue = "get(g:, 'FerretDispatch', 1)"
-                      }
-                  , GenericStatement "return l:dispatch && exists(':Make') == 2"
+                  [ GenericStatement
+                      "return split(a:str, '\\%(\\%(\\%(^\\|[^\\\\]\\)\\\\\\)\\@<!\\s\\)\\+\\zs')"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#async"
-              , functionArguments = ArgumentList []
-              , functionAttributes = []
+              , functionName = "ferret#private#ackcomplete"
+              , functionArguments =
+                  ArgumentList
+                    [ Argument "arglead" , Argument "cmdline" , Argument "cursorpos" ]
+              , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ LetStatement
-                      { letLexpr = "l:async" , letValue = "get(g:, 'FerretJob', 1)" }
-                  , GenericStatement "return l:async && has('patch-7-4-1829')"
+                  [ GenericStatement
+                      "return ferret#private#complete('Ack', a:arglead, a:cmdline, a:cursorpos)"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#error"
-              , functionArguments = ArgumentList [ Argument "message" ]
+              , functionName = "ferret#private#lackcomplete"
+              , functionArguments =
+                  ArgumentList
+                    [ Argument "arglead" , Argument "cmdline" , Argument "cursorpos" ]
               , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ GenericStatement "call inputsave()"
-                  , GenericStatement "echohl ErrorMsg"
-                  , GenericStatement
-                      "unsilent call input(a:message . ': press ENTER to continue')"
-                  , GenericStatement "echohl NONE"
-                  , GenericStatement "call inputrestore()"
-                  , GenericStatement "unsilent echo"
-                  , GenericStatement "redraw!"
+                  [ GenericStatement
+                      "return ferret#private#complete('Lack', a:arglead, a:cmdline, a:cursorpos)"
                   ]
               }
+          , GenericStatement "if executable('ag')"
+          , LetStatement { letLexpr = "s:executable" , letValue = "'ag'" }
+          , GenericStatement "elseif executable('ack')"
+          , LetStatement { letLexpr = "s:executable" , letValue = "'ack'" }
+          , GenericStatement "elseif executable('grep')"
+          , LetStatement { letLexpr = "s:executable" , letValue = "'grep'" }
+          , GenericStatement "else"
+          , LetStatement { letLexpr = "s:executable" , letValue = "''" }
+          , GenericStatement "endif"
+          , LetStatement { letLexpr = "s:options " , letValue = "{" }
+          , GenericStatement
+              "\\ 'ack': [ '--ignore-ack-defaults', '--ignore-case', '--ignore-dir', '--ignore-directory', '--invert-match', '--known-types', '--literal', '--no-recurse', '--recurse', '--sort-files', '--type', '--word-regexp', '-1', '-Q', '-R', '-i', '-k', '-r', '-v', '-w', ], 'ag': [ '--all-types', '--all-text', '--case-sensitive', '--depth', '--follow', '--ignore', '--ignore-case', '--ignore-dir', '--invert-match', '--literal', '--max-count', '--skip-vcs-ignores', '--unrestricted', '--word-regexp', '-Q', '-U', '-a', '-i', '-m', '-s', '-t', '-u', '-v', '-w' ] }"
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "s:parse"
-              , functionArguments = ArgumentList [ Argument "args" ]
+              , functionName = "ferret#private#complete"
+              , functionArguments =
+                  ArgumentList
+                    [ Argument "cmd"
+                    , Argument "arglead"
+                    , Argument "cmdline"
+                    , Argument "cursorpos"
+                    ]
               , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ GenericStatement "if exists('g:ferret_lastsearch')"
-                  , UnletStatement
-                      { unletBang = False , unletBody = "g:ferret_lastsearch" }
-                  , GenericStatement "endif"
-                  , LetStatement { letLexpr = "l:expanded_args" , letValue = "[]" }
-                  , GenericStatement "for l:arg in a:args"
-                  , GenericStatement "if ferret#private#option(l:arg)"
-                  , GenericStatement "call add(l:expanded_args, l:arg)"
-                  , GenericStatement "elseif exists('g:ferret_lastsearch')"
-                  , LetStatement
-                      { letLexpr = "l:file_args"
-                      , letValue =
-                          "glob(l:arg, 1, 1) \" Ignore 'wildignore', return a list."
+                  [ LetStatement
+                      { letLexpr = "l:args"
+                      , letValue = "s:split(a:cmdline[:a:cursorpos])"
                       }
-                  , GenericStatement "if len(l:file_args)"
-                  , GenericStatement "call extend(l:expanded_args, l:file_args)"
-                  , GenericStatement "else"
-                  , GenericStatement "call add(l:expanded_args, l:arg)"
-                  , GenericStatement "endif"
-                  , GenericStatement "else"
+                  , LetStatement { letLexpr = "l:command_seen" , letValue = "0" }
+                  , LetStatement { letLexpr = "l:pattern_seen" , letValue = "0" }
+                  , LetStatement { letLexpr = "l:position" , letValue = "0" }
+                  , GenericStatement "for l:arg in l:args"
                   , LetStatement
-                      { letLexpr = "g:ferret_lastsearch" , letValue = "l:arg" }
-                  , GenericStatement "call add(l:expanded_args, l:arg)"
-                  , GenericStatement "endif"
-                  , GenericStatement "endfor"
-                  , GenericStatement "if ferret#private#async()"
-                  , GenericStatement "return l:expanded_args"
-                  , GenericStatement "endif"
+                      { letLexpr = "l:position" , letValue = "l:position + len(l:arg)" }
                   , LetStatement
-                      { letLexpr = "l:each_word_shell_escaped"
-                      , letValue = "map(l:expanded_args, 'shellescape(v:val)')"
+                      { letLexpr = "l:stripped"
+                      , letValue = "substitute(l:arg, '\\s\\+$', '', '')"
                       }
+                  , GenericStatement "if ferret#private#option(l:stripped)"
+                  , GenericStatement "if a:cursorpos <= l:position"
                   , LetStatement
-                      { letLexpr = "l:joined"
-                      , letValue = "join(l:each_word_shell_escaped)"
+                      { letLexpr = "l:options"
+                      , letValue = "get(s:options, s:executable, [])"
                       }
-                  , GenericStatement "return escape(l:joined, '<>#')"
+                  , GenericStatement
+                      "return filter(l:options, 'match(v:val, l:stripped) == 0')"
+                  , GenericStatement "endif"
+                  , GenericStatement "elseif l:pattern_seen"
+                  , GenericStatement "if a:cursorpos <= l:position"
+                  , GenericStatement "return glob(a:arglead . '*', 1, 1)"
+                  , GenericStatement "end"
+                  , GenericStatement "elseif l:command_seen"
+                  , LetStatement { letLexpr = "l:pattern_seen" , letValue = "1" }
+                  , GenericStatement "elseif l:stripped ==# a:cmd"
+                  , LetStatement { letLexpr = "l:command_seen" , letValue = "1" }
+                  , GenericStatement "else"
+                  , GenericStatement "end"
+                  , GenericStatement "endfor"
+                  , GenericStatement "return []"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#clearautocmd"
+              , functionName = "ferret#private#option"
+              , functionArguments = ArgumentList [ Argument "str" ]
+              , functionAttributes = [ "abort" ]
+              , functionBody = [ GenericStatement "return a:str =~# '^-'" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "ferret#private#qargs"
               , functionArguments = ArgumentList []
               , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ GenericStatement "if has('autocmd')"
-                  , GenericStatement "augroup FerretPostQF"
-                  , GenericStatement "autocmd!"
-                  , GenericStatement "augroup END"
-                  , GenericStatement "endif"
+                  [ LetStatement { letLexpr = "l:buffer_numbers" , letValue = "{}" }
+                  , GenericStatement "for l:item in getqflist()"
+                  , LetStatement
+                      { letLexpr = "l:buffer_numbers[l:item['bufnr']]"
+                      , letValue = "bufname(l:item['bufnr'])"
+                      }
+                  , GenericStatement "endfor"
+                  , GenericStatement
+                      "return join(map(values(l:buffer_numbers), 'fnameescape(v:val)'))"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#post"
-              , functionArguments = ArgumentList [ Argument "type" ]
-              , functionAttributes = [ "abort" ]
+              , functionName = "ferret#private#qf_delete"
+              , functionArguments = ArgumentList []
+              , functionAttributes = [ "range" ]
               , functionBody =
-                  [ GenericStatement "call ferret#private#clearautocmd()"
+                  [ GenericStatement "call s:delete(a:firstline, a:lastline)" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "ferret#private#qf_delete_motion"
+              , functionArguments =
+                  ArgumentList [ Argument "type" , Argument "..." ]
+              , functionAttributes = []
+              , functionBody =
+                  [ LetStatement
+                      { letLexpr = "l:selection" , letValue = "&selection" }
                   , LetStatement
-                      { letLexpr = "l:lastsearch "
-                      , letValue = "get(g:, 'ferret_lastsearch', '')"
-                      }
+                      { letLexpr = "&selection" , letValue = "'inclusive'" }
                   , LetStatement
-                      { letLexpr = "l:qflist "
-                      , letValue = "a:type == 'qf' ? getqflist() : getloclist(0)"
-                      }
+                      { letLexpr = "l:firstline" , letValue = "line(\"'[\")" }
                   , LetStatement
-                      { letLexpr = "l:tip "
-                      , letValue = "' [see `:help ferret-quotes`]'"
-                      }
-                  , GenericStatement "if len(l:qflist) == 0"
+                      { letLexpr = "l:lastline" , letValue = "line(\"']\")" }
+                  , GenericStatement "call s:delete(l:firstline, l:lastline)"
                   , LetStatement
-                      { letLexpr = "l:base "
-                      , letValue =
-                          "'No results for search pattern `' . l:lastsearch . '`'"
+                      { letLexpr = "&selection" , letValue = "l:selection" }
+                  ]
+              }
+          ]
+      , Unit
+          [ LetStatement { letLexpr = "s:jobs" , letValue = "{}" }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "s:channel_id"
+              , functionArguments = ArgumentList [ Argument "channel" ]
+              , functionAttributes = []
+              , functionBody =
+                  [ GenericStatement "return matchstr(a:channel, '\\d\\+')" ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "s:info_from_channel"
+              , functionArguments = ArgumentList [ Argument "channel" ]
+              , functionAttributes = []
+              , functionBody =
+                  [ LetStatement
+                      { letLexpr = "l:channel_id"
+                      , letValue = "s:channel_id(a:channel)"
                       }
-                  , GenericStatement
-                      "if l:lastsearch =~ '\\v^([' . \"'\" . '\"])[^ \\1]+\\1$'"
-                  , GenericStatement "call ferret#private#error(l:base . l:tip)"
-                  , GenericStatement "else"
-                  , GenericStatement "call ferret#private#error(l:base)"
+                  , GenericStatement "if has_key(s:jobs, l:channel_id)"
+                  , GenericStatement "return s:jobs[l:channel_id]"
                   , GenericStatement "endif"
-                  , GenericStatement "else"
+                  ]
+              }
+          , FunctionDeclaration
+              { functionBang = True
+              , functionName = "ferret#private#async#search"
+              , functionArguments =
+                  ArgumentList [ Argument "command" , Argument "ack" ]
+              , functionAttributes = [ "abort" ]
+              , functionBody =
+                  [ GenericStatement "call ferret#private#async#cancel()"
+                  , GenericStatement
+                      "call ferret#private#autocmd('FerretAsyncStart')"
                   , LetStatement
-                      { letLexpr = "l:invalid "
-                      , letValue = "filter(copy(l:qflist), 'v:val.valid == 0')"
+                      { letLexpr = "l:command_and_args "
+                      , letValue = "extend(split(&grepprg), a:command)"
                       }
-                  , GenericStatement "if len(l:invalid) == len(l:qflist)"
-                  , GenericStatement "redraw!"
-                  , GenericStatement "echohl ErrorMsg"
-                  , GenericStatement "for l:item in l:invalid"
-                  , GenericStatement "unsilent echomsg l:item.text"
-                  , GenericStatement "endfor"
-                  , GenericStatement "echohl NONE"
                   , LetStatement
-                      { letLexpr = "l:base "
-                      , letValue = "'Search for `' . l:lastsearch . '` failed'"
+                      { letLexpr = "l:job"
+                      , letValue = "job_start(l:command_and_args, {"
                       }
+                  , GenericStatement
+                      "\\ 'err_cb': 'ferret#private#async#err_cb', 'out_cb': 'ferret#private#async#out_cb', 'close_cb': 'ferret#private#async#close_cb', 'err_mode': 'raw', 'out_mode': 'raw' })"
                   , LetStatement
-                      { letLexpr = "l:suffix "
-                      , letValue = "a:type == 'qf' && ferret#private#dispatch() ?"
+                      { letLexpr = "l:channel" , letValue = "job_getchannel(l:job)" }
+                  , LetStatement
+                      { letLexpr = "l:channel_id"
+                      , letValue = "s:channel_id(l:channel)"
                       }
-                  , GenericStatement "\\ ' (run `:messages` to see details)' : ''"
-                  , GenericStatement
-                      "if l:lastsearch =~ '\\v^[' . \"'\" . '\"].+[^' . \"'\" . '\"]$'"
+                  , LetStatement
+                      { letLexpr = "s:jobs[l:channel_id]" , letValue = "{" }
                   , GenericStatement
-                      "call ferret#private#error(l:base . l:tip . l:suffix)"
-                  , GenericStatement "else"
-                  , GenericStatement "call ferret#private#error(l:base . l:suffix)"
-                  , GenericStatement "endif"
-                  , GenericStatement "endif"
-                  , GenericStatement "endif"
+                      "\\ 'channel_id': l:channel_id, 'job': l:job, 'errors': [], 'output': [], 'pending_error': '', 'pending_output': '', 'pending_error_length': 0, 'pending_output_length': 0, 'ack': a:ack, 'window': win_getid() }"
                   ]
               }
+          , LetStatement
+              { letLexpr = "s:max_line_length" , letValue = "32768" }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#ack"
-              , functionArguments = ArgumentList [ Argument "..." ]
-              , functionAttributes = [ "abort" ]
+              , functionName = "ferret#private#async#err_cb"
+              , functionArguments =
+                  ArgumentList [ Argument "channel" , Argument "msg" ]
+              , functionAttributes = []
               , functionBody =
                   [ LetStatement
-                      { letLexpr = "l:command" , letValue = "s:parse(a:000)" }
-                  , GenericStatement "call ferret#private#hlsearch()"
-                  , GenericStatement "if empty(&grepprg)"
-                  , GenericStatement "return"
+                      { letLexpr = "l:info"
+                      , letValue = "s:info_from_channel(a:channel)"
+                      }
+                  , GenericStatement "if type(l:info) == 4"
+                  , LetStatement { letLexpr = "l:start" , letValue = "0" }
+                  , GenericStatement "while 1"
+                  , LetStatement
+                      { letLexpr = "l:idx" , letValue = "match(a:msg, '\\n', l:start)" }
+                  , GenericStatement "if l:idx==-1"
+                  , GenericStatement
+                      "if l:info.pending_error_length < s:max_line_length"
+                  , LetStatement
+                      { letLexpr = "l:rest" , letValue = "strpart(a:msg, l:start)" }
+                  , LetStatement
+                      { letLexpr = "l:length" , letValue = "strlen(l:rest)" }
+                  , LetStatement
+                      { letLexpr = "l:info.pending_error." , letValue = "l:rest" }
+                  , LetStatement
+                      { letLexpr = "l:info.pending_error_length+"
+                      , letValue = "l:length"
+                      }
                   , GenericStatement "endif"
-                  , GenericStatement "if ferret#private#async()"
-                  , GenericStatement "call ferret#private#async#search(l:command, 1)"
-                  , GenericStatement "elseif ferret#private#dispatch()"
-                  , GenericStatement "call ferret#private#dispatch#search(l:command)"
+                  , GenericStatement "break"
                   , GenericStatement "else"
                   , GenericStatement
-                      "call ferret#private#vanilla#search(l:command, 1)"
+                      "if l:info.pending_error_length < s:max_line_length"
+                  , LetStatement
+                      { letLexpr = "l:info.pending_error."
+                      , letValue = "strpart(a:msg, l:start, l:idx - l:start)"
+                      }
+                  , GenericStatement "endif"
+                  , GenericStatement "call add(l:info.errors, l:info.pending_error)"
+                  , LetStatement
+                      { letLexpr = "l:info.pending_error" , letValue = "''" }
+                  , LetStatement
+                      { letLexpr = "l:info.pending_error_length" , letValue = "0" }
+                  , GenericStatement "endif"
+                  , LetStatement { letLexpr = "l:start" , letValue = "l:idx + 1" }
+                  , GenericStatement "endwhile"
                   , GenericStatement "endif"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#lack"
-              , functionArguments = ArgumentList [ Argument "..." ]
-              , functionAttributes = [ "abort" ]
+              , functionName = "ferret#private#async#out_cb"
+              , functionArguments =
+                  ArgumentList [ Argument "channel" , Argument "msg" ]
+              , functionAttributes = []
               , functionBody =
                   [ LetStatement
-                      { letLexpr = "l:command" , letValue = "s:parse(a:000)" }
-                  , GenericStatement "call ferret#private#hlsearch()"
-                  , GenericStatement "if empty(&grepprg)"
-                  , GenericStatement "return"
+                      { letLexpr = "l:info"
+                      , letValue = "s:info_from_channel(a:channel)"
+                      }
+                  , GenericStatement "if type(l:info) == 4"
+                  , LetStatement { letLexpr = "l:start" , letValue = "0" }
+                  , GenericStatement "while 1"
+                  , LetStatement
+                      { letLexpr = "l:idx" , letValue = "match(a:msg, '\\n', l:start)" }
+                  , GenericStatement "if l:idx==-1"
+                  , GenericStatement
+                      "if l:info.pending_output_length < s:max_line_length"
+                  , LetStatement
+                      { letLexpr = "l:rest" , letValue = "strpart(a:msg, l:start)" }
+                  , LetStatement
+                      { letLexpr = "l:length" , letValue = "strlen(l:rest)" }
+                  , LetStatement
+                      { letLexpr = "l:info.pending_output." , letValue = "l:rest" }
+                  , LetStatement
+                      { letLexpr = "l:info.pending_output_length+"
+                      , letValue = "l:length"
+                      }
                   , GenericStatement "endif"
-                  , GenericStatement "if ferret#private#async()"
-                  , GenericStatement "call ferret#private#async#search(l:command, 0)"
+                  , GenericStatement "break"
                   , GenericStatement "else"
                   , GenericStatement
-                      "call ferret#private#vanilla#search(l:command, 0)"
-                  , GenericStatement "endif"
-                  ]
-              }
-          , FunctionDeclaration
-              { functionBang = True
-              , functionName = "ferret#private#hlsearch"
-              , functionArguments = ArgumentList []
-              , functionAttributes = [ "abort" ]
-              , functionBody =
-                  [ GenericStatement "if has('extra_search')"
-                  , Empty
+                      "if l:info.pending_output_length < s:max_line_length"
                   , LetStatement
-                      { letLexpr = "l:hlsearch"
-                      , letValue = "get(g:, 'FerretHlsearch', &hlsearch)"
+                      { letLexpr = "l:info.pending_output."
+                      , letValue = "strpart(a:msg, l:start, l:idx - l:start)"
                       }
-                  , GenericStatement "if l:hlsearch"
+                  , GenericStatement "endif"
+                  , GenericStatement "call add(l:info.output, l:info.pending_output)"
+                  , LetStatement
+                      { letLexpr = "l:info.pending_output" , letValue = "''" }
                   , LetStatement
-                      { letLexpr = "@/" , letValue = "g:ferret_lastsearch" }
-                  , GenericStatement
-                      "call feedkeys(\":let &hlsearch=1 | echo \\<CR>\", 'n')"
+                      { letLexpr = "l:info.pending_output_length" , letValue = "0" }
                   , GenericStatement "endif"
+                  , LetStatement { letLexpr = "l:start" , letValue = "l:idx + 1" }
+                  , GenericStatement "endwhile"
                   , GenericStatement "endif"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#acks"
-              , functionArguments = ArgumentList [ Argument "command" ]
+              , functionName = "ferret#private#async#close_cb"
+              , functionArguments = ArgumentList [ Argument "channel" ]
               , functionAttributes = [ "abort" ]
               , functionBody =
                   [ LetStatement
-                      { letLexpr = "l:matches "
-                      , letValue =
-                          "matchlist(a:command, '\\v\\C^(([^|\"\\\\a-zA-Z0-9]).+\\2.*\\2)([cgeiI]*)$')"
+                      { letLexpr = "l:info"
+                      , letValue = "s:info_from_channel(a:channel)"
                       }
-                  , GenericStatement "if !len(l:matches)"
+                  , GenericStatement "if type(l:info) == 4"
+                  , GenericStatement "call remove(s:jobs, l:info.channel_id)"
                   , GenericStatement
-                      "call ferret#private#error( 'Ferret: Expected a substitution expression (/foo/bar/); got: ' . a:command )"
-                  , GenericStatement "return"
-                  , GenericStatement "endif"
-                  , LetStatement
-                      { letLexpr = "l:pattern " , letValue = "l:matches[1]" }
-                  , LetStatement
-                      { letLexpr = "l:options " , letValue = "l:matches[3]" }
-                  , GenericStatement "if l:options !~# 'e'"
-                  , LetStatement { letLexpr = "l:options ." , letValue = "'e'" }
-                  , GenericStatement "endif"
-                  , GenericStatement "if l:options !~# 'g'"
-                  , LetStatement { letLexpr = "l:options ." , letValue = "'g'" }
+                      "call ferret#private#autocmd('FerretAsyncFinish')"
+                  , GenericStatement "if !l:info.ack"
+                  , GenericStatement "call win_gotoid(l:info.window)"
                   , GenericStatement "endif"
-                  , LetStatement
-                      { letLexpr = "l:filenames" , letValue = "ferret#private#qargs()" }
-                  , GenericStatement "if l:filenames ==# ''"
                   , GenericStatement
-                      "call ferret#private#error( 'Ferret: Quickfix filenames must be present, but there are none ' . '(must use :Ack to find files before :Acks can be used)' )"
-                  , GenericStatement "return"
+                      "call s:finalize_search(l:info.output, l:info.ack)"
+                  , GenericStatement "for l:error in l:info.errors"
+                  , GenericStatement "unsilent echomsg l:error"
+                  , GenericStatement "endfor"
                   , GenericStatement "endif"
-                  , GenericStatement "execute 'args' l:filenames"
-                  , GenericStatement "call ferret#private#autocmd('FerretWillWrite')"
-                  , GenericStatement
-                      "execute 'argdo' '%s' . l:pattern . l:options . ' | update'"
-                  , GenericStatement "call ferret#private#autocmd('FerretDidWrite')"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#autocmd"
-              , functionArguments = ArgumentList [ Argument "cmd" ]
+              , functionName = "ferret#private#async#pull"
+              , functionArguments = ArgumentList []
               , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ GenericStatement
-                      "if v:version > 703 || v:version == 703 && has('patch438')"
+                  [ GenericStatement "for l:channel_id in keys(s:jobs)"
+                  , LetStatement
+                      { letLexpr = "l:info" , letValue = "s:jobs[l:channel_id]" }
                   , GenericStatement
-                      "execute 'silent doautocmd <nomodeline> User ' . a:cmd"
-                  , GenericStatement "else"
-                  , GenericStatement "execute 'silent doautocmd User ' . a:cmd"
-                  , GenericStatement "endif"
+                      "call s:finalize_search(l:info.output, l:info.ack)"
+                  , GenericStatement "endfor"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "s:split"
-              , functionArguments = ArgumentList [ Argument "str" ]
+              , functionName = "ferret#private#async#cancel"
+              , functionArguments = ArgumentList []
               , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ GenericStatement
-                      "return split(a:str, '\\%(\\%(\\%(^\\|[^\\\\]\\)\\\\\\)\\@<!\\s\\)\\+\\zs')"
+                  [ LetStatement { letLexpr = "l:canceled" , letValue = "0" }
+                  , GenericStatement "for l:channel_id in keys(s:jobs)"
+                  , LetStatement
+                      { letLexpr = "l:info" , letValue = "s:jobs[l:channel_id]" }
+                  , GenericStatement "call job_stop(l:info.job)"
+                  , GenericStatement "call remove(s:jobs, l:channel_id)"
+                  , LetStatement { letLexpr = "l:canceled" , letValue = "1" }
+                  , GenericStatement "endfor"
+                  , GenericStatement "if l:canceled"
+                  , GenericStatement
+                      "call ferret#private#autocmd('FerretAsyncFinish')"
+                  , GenericStatement "endif"
                   ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#ackcomplete"
-              , functionArguments =
-                  ArgumentList
-                    [ Argument "arglead" , Argument "cmdline" , Argument "cursorpos" ]
+              , functionName = "ferret#private#async#debug"
+              , functionArguments = ArgumentList []
               , functionAttributes = [ "abort" ]
-              , functionBody =
-                  [ GenericStatement
-                      "return ferret#private#complete('Ack', a:arglead, a:cmdline, a:cursorpos)"
-                  ]
+              , functionBody = [ GenericStatement "return s:jobs" ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#lackcomplete"
+              , functionName = "s:finalize_search"
               , functionArguments =
-                  ArgumentList
-                    [ Argument "arglead" , Argument "cmdline" , Argument "cursorpos" ]
-              , functionAttributes = [ "abort" ]
+                  ArgumentList [ Argument "output" , Argument "ack" ]
+              , functionAttributes = []
               , functionBody =
-                  [ GenericStatement
-                      "return ferret#private#complete('Lack', a:arglead, a:cmdline, a:cursorpos)"
+                  [ GenericStatement "if a:ack"
+                  , GenericStatement "cexpr a:output"
+                  , GenericStatement
+                      "execute get(g:, 'FerretQFHandler', 'botright cwindow')"
+                  , GenericStatement "call ferret#private#post('qf')"
+                  , GenericStatement "else"
+                  , LexprStatement { lexprBang = False , lexprExpr = "a:output" }
+                  , GenericStatement "execute get(g:, 'FerretLLHandler', 'lwindow')"
+                  , GenericStatement "call ferret#private#post('location')"
+                  , GenericStatement "endif"
                   ]
               }
-          , GenericStatement "if executable('ag')"
-          , LetStatement { letLexpr = "s:executable" , letValue = "'ag'" }
-          , GenericStatement "elseif executable('ack')"
-          , LetStatement { letLexpr = "s:executable" , letValue = "'ack'" }
-          , GenericStatement "elseif executable('grep')"
-          , LetStatement { letLexpr = "s:executable" , letValue = "'grep'" }
-          , GenericStatement "else"
-          , LetStatement { letLexpr = "s:executable" , letValue = "''" }
-          , GenericStatement "endif"
-          , LetStatement { letLexpr = "s:options " , letValue = "{" }
-          , GenericStatement
-              "\\ 'ack': [ '--ignore-ack-defaults', '--ignore-case', '--ignore-dir', '--ignore-directory', '--invert-match', '--known-types', '--literal', '--no-recurse', '--recurse', '--sort-files', '--type', '--word-regexp', '-1', '-Q', '-R', '-i', '-k', '-r', '-v', '-w', ], 'ag': [ '--all-types', '--all-text', '--case-sensitive', '--depth', '--follow', '--ignore', '--ignore-case', '--ignore-dir', '--invert-match', '--literal', '--max-count', '--skip-vcs-ignores', '--unrestricted', '--word-regexp', '-Q', '-U', '-a', '-i', '-m', '-s', '-t', '-u', '-v', '-w' ] }"
-          , FunctionDeclaration
+          ]
+      , Unit
+          [ FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#complete"
-              , functionArguments =
-                  ArgumentList
-                    [ Argument "cmd"
-                    , Argument "arglead"
-                    , Argument "cmdline"
-                    , Argument "cursorpos"
-                    ]
+              , functionName = "ferret#private#dispatch#search"
+              , functionArguments = ArgumentList [ Argument "command" ]
               , functionAttributes = [ "abort" ]
               , functionBody =
-                  [ LetStatement
-                      { letLexpr = "l:args"
-                      , letValue = "s:split(a:cmdline[:a:cursorpos])"
-                      }
-                  , LetStatement { letLexpr = "l:command_seen" , letValue = "0" }
-                  , LetStatement { letLexpr = "l:pattern_seen" , letValue = "0" }
-                  , LetStatement { letLexpr = "l:position" , letValue = "0" }
-                  , GenericStatement "for l:arg in l:args"
+                  [ GenericStatement "if has('autocmd')"
+                  , GenericStatement "augroup FerretPostQF"
+                  , GenericStatement "autocmd!"
+                  , GenericStatement
+                      "autocmd QuickfixCmdPost cgetfile call ferret#private#post('qf')"
+                  , GenericStatement "augroup END"
+                  , GenericStatement "endif"
                   , LetStatement
-                      { letLexpr = "l:position" , letValue = "l:position + len(l:arg)" }
+                      { letLexpr = "l:original_makeprg" , letValue = "&l:makeprg" }
                   , LetStatement
-                      { letLexpr = "l:stripped"
-                      , letValue = "substitute(l:arg, '\\s\\+$', '', '')"
+                      { letLexpr = "l:original_errorformat"
+                      , letValue = "&l:errorformat"
                       }
-                  , GenericStatement "if ferret#private#option(l:stripped)"
-                  , GenericStatement "if a:cursorpos <= l:position"
+                  , GenericStatement "try"
                   , LetStatement
-                      { letLexpr = "l:options"
-                      , letValue = "get(s:options, s:executable, [])"
+                      { letLexpr = "&l:makeprg"
+                      , letValue = "&grepprg . ' ' . a:command"
                       }
-                  , GenericStatement
-                      "return filter(l:options, 'match(v:val, l:stripped) == 0')"
-                  , GenericStatement "endif"
-                  , GenericStatement "elseif l:pattern_seen"
-                  , GenericStatement "if a:cursorpos <= l:position"
-                  , GenericStatement "return glob(a:arglead . '*', 1, 1)"
-                  , GenericStatement "end"
-                  , GenericStatement "elseif l:command_seen"
-                  , LetStatement { letLexpr = "l:pattern_seen" , letValue = "1" }
-                  , GenericStatement "elseif l:stripped ==# a:cmd"
-                  , LetStatement { letLexpr = "l:command_seen" , letValue = "1" }
-                  , GenericStatement "else"
-                  , GenericStatement "end"
-                  , GenericStatement "endfor"
-                  , GenericStatement "return []"
-                  ]
-              }
-          , FunctionDeclaration
-              { functionBang = True
-              , functionName = "ferret#private#option"
-              , functionArguments = ArgumentList [ Argument "str" ]
-              , functionAttributes = [ "abort" ]
-              , functionBody = [ GenericStatement "return a:str =~# '^-'" ]
-              }
-          , FunctionDeclaration
-              { functionBang = True
-              , functionName = "ferret#private#qargs"
-              , functionArguments = ArgumentList []
-              , functionAttributes = [ "abort" ]
-              , functionBody =
-                  [ LetStatement { letLexpr = "l:buffer_numbers" , letValue = "{}" }
-                  , GenericStatement "for l:item in getqflist()"
                   , LetStatement
-                      { letLexpr = "l:buffer_numbers[l:item['bufnr']]"
-                      , letValue = "bufname(l:item['bufnr'])"
+                      { letLexpr = "&l:errorformat" , letValue = "&grepformat" }
+                  , GenericStatement "echomsg &l:makeprg"
+                  , GenericStatement "Make"
+                  , GenericStatement "catch"
+                  , GenericStatement "call ferret#private#clearautocmd()"
+                  , GenericStatement "finally"
+                  , LetStatement
+                      { letLexpr = "&l:makeprg" , letValue = "l:original_makeprg" }
+                  , LetStatement
+                      { letLexpr = "&l:errorformat"
+                      , letValue = "l:original_errorformat"
                       }
-                  , GenericStatement "endfor"
-                  , GenericStatement
-                      "return join(map(values(l:buffer_numbers), 'fnameescape(v:val)'))"
+                  , GenericStatement "endtry"
                   ]
               }
-          , FunctionDeclaration
+          ]
+      , Unit
+          [ FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#qf_delete"
-              , functionArguments = ArgumentList []
-              , functionAttributes = [ "range" ]
+              , functionName = "s:finalize_search"
+              , functionArguments =
+                  ArgumentList [ Argument "output" , Argument "ack" ]
+              , functionAttributes = []
               , functionBody =
-                  [ GenericStatement "call s:delete(a:firstline, a:lastline)" ]
+                  [ GenericStatement "if a:ack"
+                  , GenericStatement "cexpr a:output"
+                  , GenericStatement
+                      "execute get(g:, 'FerretQFHandler', 'botright cwindow')"
+                  , GenericStatement "call ferret#private#post('qf')"
+                  , GenericStatement "else"
+                  , LexprStatement { lexprBang = False , lexprExpr = "a:output" }
+                  , GenericStatement "execute get(g:, 'FerretLLHandler', 'lwindow')"
+                  , GenericStatement "call ferret#private#post('location')"
+                  , GenericStatement "endif"
+                  ]
               }
           , FunctionDeclaration
               { functionBang = True
-              , functionName = "ferret#private#qf_delete_motion"
+              , functionName = "ferret#private#vanilla#search"
               , functionArguments =
-                  ArgumentList [ Argument "type" , Argument "..." ]
-              , functionAttributes = []
+                  ArgumentList [ Argument "command" , Argument "ack" ]
+              , functionAttributes = [ "abort" ]
               , functionBody =
                   [ LetStatement
-                      { letLexpr = "l:selection" , letValue = "&selection" }
-                  , LetStatement
-                      { letLexpr = "&selection" , letValue = "'inclusive'" }
-                  , LetStatement
-                      { letLexpr = "l:firstline" , letValue = "line(\"'[\")" }
-                  , LetStatement
-                      { letLexpr = "l:lastline" , letValue = "line(\"']\")" }
-                  , GenericStatement "call s:delete(l:firstline, l:lastline)"
-                  , LetStatement
-                      { letLexpr = "&selection" , letValue = "l:selection" }
+                      { letLexpr = "l:output"
+                      , letValue = "system(&grepprg . ' ' . a:command)"
+                      }
+                  , GenericStatement "call s:finalize_search(l:output, a:ack)"
                   ]
               }
           ]