Skip to content

Instantly share code, notes, and snippets.

@romainl

romainl/grep.md

Last active Oct 5, 2020
Embed
What would you like to do?
Instant grep + quickfix

FOREWORDS

I don't mean the snippet at the bottom of this gist to be a generic plug-n-play solution to your search needs. It is very likely to not work for you or even break things, and it certainly is not as extensively tested and genericised as your regular third-party plugin.

My goal, here and in most of my posts, is to show how Vim's features can be leveraged to build your own high-level, low-maintenance, workflows without systematically jumping on the plugins bandwagon or twisting Vim's arm.


Instant grep + quickfix

:help :grep is a fantastic tool, especially when combined with the quickfix, but it is certainly a bit rough around the edges. Let's see what it takes to smooth it out.

Tell Vim what external command to use for grepping

By default, Vim is set to use grep for :grep, which seems logical given the name of the command and all. But it also lets us customize the actual command via :help 'grepprg'. We can use it to add default parameters, for example, or to point Vim to a completely different program.

Over the years, people have been busy working on faster and/or more developer-friendly grep alternatives. The first such effort that I remember of was ack, then ag changed the rules of the game and other players entered the field, the latest being rg. Depending on your use cases, switching :grep to one of those alternatives may be a wise choice.

In the example below I use ag but feel free to substitute it with whatever you want.

set grepprg=ag\ --vimgrep

Now, this change is not strictly necessary for what we are trying to do but, even if we stopped here, using ag or rg over grep can be a cheap and non-negligible upgrade to the built-in :grep and :lgrep.

Perform the search in a sub-shell

One of those rough edges I alluded to earlier is the fact that Vim executes the command in Vim's parent shell. This has a few consequences that contribute to a less than ideal experience:

  • Vim is suspended for how long it takes the command to finish.
  • The results are printed in the shell and clog our terminal's scroll buffer.
  • We must press the dreaded <CR> to come back to Vim.

Enters a sister command of :grep, :help :cgetexpr, that takes an "expression" as input. In concrete terms, :cgetexpr takes the raw output of grep or ag or whatever, parses it according to :help 'errorformat' and creates a quickfix list. :cgetexpr operates at a lower level than :grep and doesn't involve any I/O so it is simpler and faster.

:cgetexpr <expression>

Now, how do we get the output of ag if we can't execute it in the shell? Via :help system() of course:

:cgetexpr system('ag foo')

But that's a lot to type, right? Well, how about wrapping it in a custom function?

function! Grep(args)
    return system('ag ' . a:args)
endfunction

Now we have the following, which seems to solve all of the issues outlined above:

:cgetexpr Grep('foo')

But we already have ag set as our 'grepprg' so we might as well use that, making our function future-proof:

function! Grep(args)
    return system(&grepprg . ' ' . a:args)
endfunction

From there, we can modify the function to handle multiple arguments because :grep 'foo bar' filename is a very reasonable thing to do:

function! Grep(...)
    return system(&grepprg . ' ' . join(a:000, ' '))
endfunction

There are many ways to write the expression above, and choosing one or the other is mostly a matter of personal preference… and performance:

Expression Performance Stylish
&grepprg . ' ' . join(a:000, ' ') 0.000072s No
join(extend([&grepprg], a:000), ' ') 0.000065s Yes
join([&grepprg] + a:000, ' ') 0.000050s Yes

The third one it is, then:

function! Grep(...)
    return system(join([&grepprg] + a:000), ' '))
endfunction

What this does is:

  • concatenate a list with &grepprg as sole value with a list containing all the arguments passed to the function,
  • join the resulting list into a single string,
  • pass it to system().

Nice… Our little function works very well but calling it manually kind of defeats the point we are trying to make.

Create a command to populate the quickfix list

We will start small and simple with a simple :Grep command that takes one or more arguments:

command! -nargs=+ Grep cgetexpr grep(<f-args>)

which seems to do the job:

:Grep foo

but we might want to restrict our search to a given directory or file so it might be a good idea to use command-line completion:

command! -nargs=+ -complete=file_in_path Grep cgetexpr Grep(<f-args>)

and we might want to execute another command after :Grep foo, so we should add another flag:

command! -nargs=+ -complete=file_in_path -bar Grep  cgetexpr Grep(<f-args>)

and let's not forget :help :lgrep:

command! -nargs=+ -complete=file_in_path -bar Grep  cgetexpr Grep(<f-args>)
command! -nargs=+ -complete=file_in_path -bar LGrep lgetexpr Grep(<f-args>)

Aaaaand we are done with the usability issues mentioned above. Here is our little snippet in all its glory:

function! Grep(...)
    return system(join([&grepprg] + a:000), ' '))
endfunction
command! -nargs=+ -complete=file_in_path -bar Grep  cgetexpr Grep(<f-args>)
command! -nargs=+ -complete=file_in_path -bar LGrep lgetexpr Grep(<f-args>)

Dealing with not-so-edge case

Our custom :Grep command seems to do a good job: :Grep foo *.js is faster than ever and we don't have to press <Enter> anymore. This is all good but :grep expands wildcards by default (think :grep foo % or :grep bar ##) and we don't want to lose that, so we will have to modify our expression slightly:

return system(join([&grepprg] + [expandcmd(join(a:000, ' '))], ' '))

Here we use :help expandcmd() to expand any glob present in the "arguments" part of the command before it is executed, which makes :Grep foo % work like :grep foo %.

Neat!

Open the location/quickfix window automatically if there are valid entries in the list

There is one usability issue that we haven't mentioned before: when the search is done, we get a little message about the first item in our search but that's all. True, we can use :help :cn and friends to jump around quickfix entries but, if we want to see the results, we have to use :help :cwindow:

:Grep foo
:cwindow

or, shorter:

:Gr foo | cw

What if we could open the quickfix window automatically when the quickfix list changes?

Autocommands to the rescue! The first step is to define a self-clearing group so that our autocommands don't needlessly pile up if we re-:source our vimrc:

augroup quickfix
    autocmd!
    " our autocommands here
augroup END

Then, we add two autocommands:

  • one to execute :cwindow whenever :cgetexpr is executed,
  • another one to execute :lwindow whenever :lgetexpr is executed.

:cwindow opens the quickfix window if there are valid entries in the quickfix list and :lwindow does the same for the location list and the location window.

We need both if we don't want to lose the flexibility of the original commands we are trying to replace.

augroup quickfix
    autocmd!
    autocmd QuickFixCmdPost cgetexpr cwindow
    autocmd QuickFixCmdPost lgetexpr lwindow
augroup END

Note that this snippet can be easily generalized to cover every quickfix command by changing the patterns cgetexpr and lgetexpr to [^l]* (to match commands that don't start with l) and l* (to match commands that start with l), respectively.

Enlightenment

So far, we have managed to:

  • eliminate I/O-related usability issues,
  • improve wildcard expansion,
  • open the quickfix window automatically to show the results.

This leaves us us with a relatively straightforward snippet of vimcript and a much slicker experience:

function! Grep(...)
    return system(join([&grepprg] + [expandcmd(join(a:000, ' '))], ' '))
endfunction
command! -nargs=+ -complete=file_in_path -bar Grep  cgetexpr Grep(<f-args>)
command! -nargs=+ -complete=file_in_path -bar LGrep lgetexpr Grep(<f-args>)
augroup quickfix
    autocmd!
    autocmd QuickFixCmdPost cgetexpr cwindow
    autocmd QuickFixCmdPost lgetexpr lwindow
augroup END

That was, in a nutshell, where I was when this article, that took a different approach to the exact same problems, was posted on Reddit. The author's approach involved an abbreviation that allowed the user to type :grep as usual, and have it expanded to a more potent silent grep in an effort to reduce I/O-related noise.

While I don't use abbreviations much myself, I really like this idea of modifying the underlying behavior of common commands to unlock their potential. This is a strategy I have explored in the past that allows you to get more from the standard commands without having to learn new commands. You just do :foo bar, as usual, but you get a much more usable outcome.

It is that specific aspect that lacked in my lovely snippet: I had to use :Grep instead of :grep so it forced me to learn a new command (not a wildly different one, mind you, but still). Enlightenment came from Peter Rincker, who, in the spirit of the article, suggested the following missing piece:

cnoreabbrev <expr> grep (getcmdtype() ==# ':' && getcmdline() ==# 'grep') ? 'Grep' : 'grep'

which ties the whole thing up perfectly.

Now I can do the same :grep foo that I learned 10 years ago, but with a much cleaner and less labor-intensive user experience, and with a few lines of minimal, readable, maintainable configuration.

grep

NOTE: While I consider this short snippet to be extremely useful, I don't pretend it to be a plug and play solution to all your problems, real or imaginary. I would prefer this experiment to inspire you to experiment with your own workflow than to be yanked and put in your vimrc. I can't stop you from doing that, of course, but oh well.


My Vim-related gists.

set grepprg=ag\ --vimgrep
function! Grep(...)
return system(join([&grepprg] + [expandcmd(join(a:000, ' '))], ' '))
endfunction
command! -nargs=+ -complete=file_in_path -bar Grep cgetexpr Grep(<f-args>)
command! -nargs=+ -complete=file_in_path -bar LGrep lgetexpr Grep(<f-args>)
cnoreabbrev <expr> grep (getcmdtype() ==# ':' && getcmdline() ==# 'grep') ? 'Grep' : 'grep'
cnoreabbrev <expr> lgrep (getcmdtype() ==# ':' && getcmdline() ==# 'lgrep') ? 'LGrep' : 'lgrep'
augroup quickfix
autocmd!
autocmd QuickFixCmdPost cgetexpr cwindow
autocmd QuickFixCmdPost lgetexpr lwindow
augroup END
@george-b

This comment has been minimized.

Copy link

@george-b george-b commented May 29, 2019

return system(join(['ag --vimgrep', shellescape(args[0]), get(args, 1, '')], ' '))

It feels like calling ag is too explicit here, is there a reason for not referencing it as &grepprg?

@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented May 30, 2019

No reason whatsoever. You caught me between two edits.

@alfunx

This comment has been minimized.

Copy link

@alfunx alfunx commented Nov 15, 2019

This is amazing, thank you! The split(a:args, ' ') might cause problems if the pattern contains spaces. I'm sticking to an earlier version, where you had &grepprg . ' ' . a:args, which makes this behave like :grep. To avoid escaping, one could always go for quotes (e.g. :Grep 'some regex.*' *.c).

@trobjo

This comment has been minimized.

Copy link

@trobjo trobjo commented Dec 13, 2019

Very nice version of the quickfix list. I use a much simpler version, as I often grep for multiple words. The code looks like this:

set grepprg=rg\ --vimgrep\ --glob\ '!*{.git,node_modules,build,bin,obj,README.md,tags}'

function! Grep(args)
    return system(join([&grepprg, shellescape(a:args)], ' '))
endfunction

command! -nargs=+ -complete=file_in_path -bar Gr cgetexpr Grep(<q-args>)

augroup quickfix
    autocmd!
    autocmd QuickFixCmdPost cgetexpr cwindow
augroup END

My understanding of vimscript is not that great, so what is it you gain by having a more convoluted command and not being able to grep for more than one word. What am I missing?

@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Dec 13, 2019

Very nice version of the quickfix list. I use a much simpler version, as I often grep for multiple words. The code looks like this:

My understanding of vimscript is not that great, so what is it you gain by having a more convoluted command and not being able to grep for more than one word. What am I missing?

I rarely grep for multiple words but I often grep in specific files. The command as it is satisfies that specific need but I should look for a more generic solution.

@george-b

This comment has been minimized.

Copy link

@george-b george-b commented Dec 17, 2019

FWIW I use the following which makes multiple words and files (including %) pretty transparent.

function! Grep(args)
  let args = split(a:args)
  if len(args) > 1 && !empty(glob(args[-1]))
    let cmdline = shellescape(join(args[0:-2])) . ' ' . expand(args[-1])
  else
    let cmdline = shellescape(a:args)
  endif
  return system(&grepprg . ' ' . cmdline)
endfunction
@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Dec 17, 2019

@trobjo, @george-b, I just spent half an hour trying every "reasonable" combination of arguments I could think of with the snippet below:

function! Grep(...)
    return system(join(extend([&grepprg], a:000), ' '))
endfunction

and it worked for everything.

@zealotrush

This comment has been minimized.

Copy link

@zealotrush zealotrush commented Apr 8, 2020

Good job! Thank you!

I tried to make a Make version with the same spirit as your Grep, but it didn't work for any language other than C. After some searching, I found out that the [cl]getexpr function uses the global value of 'errorformat', which is designed for C syntax. Do you have any idea about this?

@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Apr 8, 2020

@zealotrush, the default value of &errorformat is designed to work for a number of compilers, actually. The most basic format that is supported is the "gcc" format:

filename:line:column message

which is supported by many compilers, linters or testing frameworks. If you can get your compiler to use that format then it will be understood automatically.

If you can't, then the next best thing to do is to prepend your patterns to &errorformat:

set errorformat^=...

If you want to get fancy, you can wrap :cgetexpr in a function that sets &errorformat, does :cgetexpr, and then resets &errorformat:

function! CGetExpr(args)
    let save_efm = &efm
    set errorformat^=...
    execute "cgetexpr " . args
    let &efm = save_efm
endfunction
@zealotrush

This comment has been minimized.

Copy link

@zealotrush zealotrush commented Apr 9, 2020

@romainl
Wow, that was a fast and detailed explanation! Thank you! I was working on the MATLAB linter. It doesn't provide much options, but I suppose the last two methods should work for me.

@zealotrush

This comment has been minimized.

Copy link

@zealotrush zealotrush commented Apr 9, 2020

I managed to make the following script work as what I wanted. Put it here, in case anyone interested.

function! s:make(...) abort
  if empty(a:000)
    let l:args = [expand("%:S")]
  else
    let l:args = a:000
  endif
  return system(join(extend([&makeprg], l:args), ' '))
endfunction

" Run [cl]getexpr using local errorformat, if it's available.
function! s:getexpr_efm(func, msg) abort
  let l:efm_save = &g:errorformat
  if !empty(&l:errorformat)
    let &g:errorformat = &l:errorformat
  endif
  execute a:func . " a:msg"
  let &g:errorformat = l:efm_save
endfunction

command! -nargs=* -complete=file_in_path -bar Make call s:getexpr_efm("cgetexpr", s:make(<args>))
command! -nargs=* -complete=file_in_path -bar LMake call s:getexpr_efm("lgetexpr", s:make(<args>))
@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Apr 9, 2020

@zealotrush, can you share a bit on how you are using it?

@zealotrush

This comment has been minimized.

Copy link

@zealotrush zealotrush commented Apr 9, 2020

@romainl Sure.

I don't compile or run code within Vim often. Instead, I use :make command for linting. The makeprg and errorformat are set in my ~/.vim/after/compiler/<linter>.vim files, and then the compiler is set in my ~/.vim/after/ftplugin/<filetype>.vim files (with proper undo behavior). So far, I can run :make % for code linting, and use the quickfix list for jumping back and forth among errors. I was pretty satisfied with my workflow, until I saw this gist. After trying out your Grep command, I can no longer stand the builtin grep and make commands, with all the window twisting and "press ENTER" things.

I put the code above in my ~/.vim/plugin/quickfix.vim file, so I could use :[L]Make for code linting, in a more elegant way. I also have some cnoreabbrevs to map :[l]make to :[L]Make, so that I don't have to change my habits. Because the new [L]Make command runs so smoothly, I also let LMake be a BufWritePost autocmd for file types that I daily work on. Now, every time I save a buffer, it lints automatically, and if any error occurs, the location list pops out right away. A bit intrusive, but I like it.

@jssee

This comment has been minimized.

Copy link

@jssee jssee commented Apr 10, 2020

@trobjo, @george-b, I just spent half an hour trying every "reasonable" combination of arguments I could think of with the snippet below:

function! Grep(...)
    return system(join(extend([&grepprg], a:000), ' '))
endfunction

and it worked for everything.

I really like how concise this is, but it doesn't seem to work for scenarios like @george-b mentioned where one might want to use % as a secondary arg

@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Apr 10, 2020

@jssee You can press <Tab> after % to expand it before executing the command but that is a good point.

@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Apr 10, 2020

@jssee here is an experimental version that leaves the first argument as-is and expands the rest like :grep would do:

function! Grep(...)
    return system(join([&grepprg] + [a:1] + [expandcmd(join(a:000[1:-1], ' '))], ' '))
endfunction
command! -nargs=+ -complete=file_in_path -bar Grep cgetexpr Grep(<f-args>)

For more readability:

function! Grep(...)
    let grep_cmd = [ &grepprg ]
    let first_arg = [ a:1 ]
    let rest_args = join(a:000[1:-1], ' ')
    let expanded_rest_args = [ expandcmd(rest_args) ]
    let all_components = [grep_cmd] + [first_arg] + [expanded_rest_args]
    let full_cmd = join(all_components, ' ')
    return system(full_cmd)
endfunction

Interestingly, I've timed the execution of four string building strategies:

  1. join(extend(list, list), ' '), the most explicit and "functional", but also the slowest,
  2. string . ' ' . string . ' ' . string, twice faster than #1 but inelegant,
  3. join([items] + [items] + [items], ' '), same ballpark as #2 but much cleaner,
  4. printf('%s %s %s', string, string, string) same ballpark as #2 and #3 but I'm not a fan of the syntax.

I was mainly worried about look and feel, here, because all the strategies above run in 35 to 80 microseconds, which is negligible compared to the time it takes to run the external command, measured in milliseconds.

@jssee

This comment has been minimized.

Copy link

@jssee jssee commented Apr 11, 2020

@romainl this is great. had I known that tab-to-expand was an option I would have been satisfied. I appreciate the followup though, now to decide if the edge-case-ness of saving 1 keystroke when searching in the current buffer warrants the the ever so slightly terser vim script...

@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Apr 11, 2020

@jssee I think I will update the snippet and the "article" to reflect this. Grepping the current file sounds like a common use case and the normal :grep foo % naturally expands the last % anyway, so, if the goal is to enhance :grep then this must be addressed.

@gpanders

This comment has been minimized.

Copy link

@gpanders gpanders commented Apr 14, 2020

I think this is 99% of the way there, but ideally quoted strings should be treated as a single argument so that it acts just like command line grep. That is, :Grep 'foo bar' file1 file2 file3 should treat the string foo bar as a single argument. As it stands right now, if I wanted to grep for foo % with :Grep 'foo %' file1 file2 file3 ... then the % would be expanded (since it's not part of the first argument as far as Vim is concerned).

Option flags (such as -w to search only for whole strings) should not be counted as arguments either. If I use :Grep -w PATTERN the -w should not be interpreted as the first argument, or else any %'s or #'s in the PATTERN will be expanded.

AFAIK Vim doesn't have a built-in way to split strings according to shell quotes. Some kind of "shell split" function is needed, i.e.:

shellsplit("'foo bar' baz qux") => ['foo bar', 'baz', 'qux']

Then the full function would be

function! Grep(args)
    let grep_cmd = [ &grepprg ]
    let full_args = shellsplit(a:args)
    let [opts, args] = s:separate_opts_from_args(full_args)
    let first_arg = [ args[0] ]
    let rest_args = join(args[1:-1], ' ')
    let expanded_rest_args = [ expandcmd(rest_args) ]
    let all_components = [grep_cmd] + [opts] + [first_arg] + [expanded_rest_args]
    let full_cmd = join(all_components, ' ')
    return system(full_cmd)
endfunction

I realize this is now getting fairly complicated. Is there any easier/simpler way to do this that I'm overlooking?

One possible implementation of shellsplit is

function! Shellsplit(str)
    return map(split(a:str, '\%(^\%("[^"]*"\|''[^'']*''\|[^"'']\)*\)\@<= '), {_, v -> substitute(v, '^["'']\|["'']$', '', 'g')})
endfunction
@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Apr 14, 2020

@gpanders the command passes the arguments as <f-args> which allows doing:

:Grep foo\ %

so it sounds to me like a non-issue.

@gpanders

This comment has been minimized.

Copy link

@gpanders gpanders commented Apr 14, 2020

@gpanders the command passes the arguments as <f-args> which allows doing:

:Grep foo\ %

so it sounds to me like a non-issue.

I understand it's not insurmountable, but inasmuch as it's important to you (which it may not be) to have :Grep usage in Vim mirror grep on the command line, backslash-escaping spaces is not an optimal solution.

I implemented the shellsplit and separate_opts_from_args functions I mentioned in my earlier post because I want :Grep to be just like grep, and I used what you wrote as a starting point, so thanks for doing this write up.

@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Apr 23, 2020

As I see it, :Grep should behave as closely as possible to how :grep behaves, with some leg room for eventual optimisations.

Wildcard expansion

With :grep (and other commands), wildcard expansion is done:

  • before executing the command
  • on everything that comes after the command name followed by a space.

Examples:

Before expansion After expansion
:grep foo filename :grep foo filename
:grep foo % :grep foo filename
:grep foo \% :grep foo %
:grep % % :grep filename filename
:grep \% % :grep % filename
:grep \% \% :grep % %

Contrary to my initial idea I think I should keep that behaviour and don't try to come up with new idioms, at least regarding wildcard expansion. To that effect, passing all the arguments as-is to expandcmd() does the trick, and in a very simple way.

Quotes

With :grep, quotes are passed as-is to the external command so splitting the command into a first segment and a second one will obviously make this unnecessarily complicated.

Conclusion

So it looks to me like splitting is a bad idea and I should revert to a simpler and more standard-abiding pattern:

function! Grep(...)
	return system(join([&grepprg] + [expandcmd(join(a:000, ' '))], ' '))
endfunction
@george-b

This comment has been minimized.

Copy link

@george-b george-b commented Jun 7, 2020

I noticed a typo.

diff --git a/grep.md b/grep.md
index d5c8ac8..72054c4 100644
--- a/grep.md
+++ b/grep.md
@@ -57,7 +57,7 @@ There are many ways to write the expression above, and choosing one or the other
 Expression | Performance | Stylish
 ---|---|---
 `&grepprg . ' ' . join(a:000, ' ')` | 0.000072s | No
-`join(extend([&grepprg, a:000), ' ')` | 0.000065s | Yes
+`join(extend([&grepprg], a:000), ' ')` | 0.000065s | Yes
 `join([&grepprg] + a:000, ' ')` | 0.000050s | Yes
 
 The third one it is, then:

Also out of curiosity is there a reason you explicitly join to use a separator of a single space when it is the default?

@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Jun 7, 2020

Thanks for spotting the typo. Also I tend to prefer explicit over implicit.

@vaklinzi

This comment has been minimized.

Copy link

@vaklinzi vaklinzi commented Jul 9, 2020

@romainl Thank you for the great gist. I am trying to use it with a word which contains a special character like $ but it doesn't return any results. How I can use it to search for $user for example? If I search from console line it works with:
$ ag '\$user'
but with Grep '\$user' there aren't any results.

@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Jul 9, 2020

@vaklinzi as is often the case with Vim's peculiar argument handling, I'm afraid you will have to use the dreaded triple-backslash hack, here:

:Grep '\\\$user'

which gets the job done in the most inelegant way possible.

--- EDIT ---

It looks like using :help fnameescape() before :help expandcmd() preserves the \ in $user so it may be a solution. It needs further testing, though.

@vaklinzi

This comment has been minimized.

Copy link

@vaklinzi vaklinzi commented Jul 9, 2020

Kapture 2020-07-09 at 16 02 42

Thank you for the fast response but it's still not working. Maybe I am doing something wrong...

@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Jul 9, 2020

@vaklinzi, I tested it before posting and it is definitely working, here: :Grep '\$user'

To be 100% sure I tested it with $ vim -Nu grep.vim and it also worked, here: :Grep '\$user'

FWIW:

$ ag --version
ag version 2.2.0

Features:
  +jit +lzma +zlib
@vaklinzi

This comment has been minimized.

Copy link

@vaklinzi vaklinzi commented Jul 9, 2020

Very strange. The only difference I see is that I use nvim instead vim and I changed the expandcmd() with expand() because it can't find the expandcmd() function.

image

@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Jul 9, 2020

I'm sorry but I only support Vim. Neovim and Vim are two different beasts and nothing that works in one should be assumed to work in the other.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.