3

In Vim I make auto-completion of the filesystem paths just by the shortcut Ctrl + x, Ctrl + f, in insert mode.

In a Makefile I sometimes put a path's prefix in a variable. For instance STYLEDIR=/home/user/style.

Then I have to refer to a file in that directory on another point in the Makefile. For instance

pandoc --style $(STYLEDIR)/main.css ...

In this case I can't autocomplete with Vim, because Vim is not aware of the value of the STYLEDIR var. I start with $(STYLEDIR)/ and then Ctrl + x, Ctrl + f and doesn't show the completion.

Is there a way to have autocompletion of files in this case? Maybe a GNU Make plugin for Vim?

2

Get variable value

make -p -f/dev/null 2>/dev/null | grep -Po 'var\s*=\s*\K.+'

Complete in vim

I use :h complete() in this case, you need to tell it where complete starts and give it a list of candidates.

Assume your current line looks like this:

........$(var)/mid0/mid1/mid2/base
                                  ^---cursor position in insert mode

The idea is to get var, mid=/mid0/mid1/mid2/ and base, send var to :h system() to expand it, combine the expanded value with mid and base, send it to :h glob() , remove leading expanded var + mid from every item in glob result, and that's our final complete candidates:

inoremap <c-b> <c-r>=<sid>complete_make_var_path()<cr>

function! s:complete_make_var_path()

  " search $(..) backward, do nothing if not found
  let dolllar_pos = searchpos('\v\$\([^)]+\)', 'bn', line('.'))
  if dolllar_pos == [0, 0]
    return ''
  endif

  " get prefix from $ to current column -1
  let dir_base = getline('.')[ dolllar_pos[1] - 1 : col('.') - 2 ]

  " separate $(var)/mid0/mid1/mid2/base into var, mid, base
  let [var, mid, base] = matchlist(dir_base, '\v\$\(([^)]+)\)(.*/)?(.*)')[1:3]

  " expand var, erase trailing newline
  let evar = system('make -p -f/dev/null 2>/dev/null | grep -Po ''' . var . '\s*=\s*\K.+''' )
  if empty(evar)
    return ''
  endif
  let evar = evar[ 0 : len(evar) - 2 ]

  " glob candidates, remove leading evar+prefix
  let candidates = map(glob(evar . mid . base . '*', 0, 1), {i,v -> v[ len(evar . mid) : ]})
  call complete(col('.') - len(base), candidates)
  return ''
endfunction

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.