diff --git a/autoload/neomake/utils.vim b/autoload/neomake/utils.vim index 69e2ad4738..c6489834fc 100644 --- a/autoload/neomake/utils.vim +++ b/autoload/neomake/utils.vim @@ -532,7 +532,7 @@ function! neomake#utils#path_sep() abort return neomake#utils#IsRunningWindows() ? ';' : ':' endfunction -" Find a file matching `a:glob` (using `globpath()`) by going up the +" Find a file/dir matching `a:glob` (using `globpath()`) by going up the " directories from the start directory (a:1, defaults to `expand('%:p:h')`, " i.e. the directory of the current buffer's file).) function! neomake#utils#FindGlobFile(glob, ...) abort @@ -552,6 +552,43 @@ function! neomake#utils#FindGlobFile(glob, ...) abort return '' endfunction +" Find the nearest file of the given filenames (upwards). +" (provides better performance than `neomake#utils#FindGlobFile`). +" a:1: optional start dir, defaulting to "current buffer" ("."). +function! neomake#utils#find_nearest_file_upwards(fnames, ...) abort + if !empty(&suffixesadd) + let saved_suffixesadd = &suffixesadd + let &suffixesadd = '' + endif + " NOTE: findfile requires an absolute path, except for special '.'. + let path = (a:0 ? fnamemodify(a:1, ':p') : '.') . ';' + let found = '' + let found_parts_count = 0 + for fname in a:fnames + let ffname = findfile(fname, path) + if empty(ffname) + continue + endif + + let abs_ffname = fnamemodify(ffname, ':p') + if isdirectory(abs_ffname) + " Remove trailing slash. + let abs_ffname = fnamemodify(ffname, ':h') + endif + let abs_dir = fnamemodify(abs_ffname, ':h') + let part_count = len(split(abs_dir, neomake#utils#Slash())) + if part_count > found_parts_count + let found = abs_ffname + let found_parts_count = part_count + let path = fnamemodify(found, ':h') . '.' + endif + endfor + if exists('l:saved_suffixesadd') + let &suffixesadd = saved_suffixesadd + endif + return found +endfunction + function! neomake#utils#JSONdecode(json) abort return neomake#compat#json_decode(a:json) endfunction diff --git a/tests/utils.vader b/tests/utils.vader index 3cf5581c72..04eda21dc4 100644 --- a/tests/utils.vader +++ b/tests/utils.vader @@ -761,7 +761,7 @@ Execute (neomake#utils#fnamemodify handles empty bufname): AssertEqual neomake#utils#fnamemodify(bufnr('%'), ':p'), '' bwipe -Execute (neomake#utils#FindGlobFile): +Execute (neomake#utils#FindGlobFile / neomake#utils#find_nearest_file_upwards): let tempdir = tempname() let slash = neomake#utils#Slash() let subdir = tempdir . slash . 'sub' @@ -778,13 +778,32 @@ Execute (neomake#utils#FindGlobFile): let anotherfile_in_subdir = subdir.slash.'common-file-2' call writefile([], anotherfile_in_subdir) + " tree: + " ├── common-file + " ├── sub + " │   ├── common-file + " │   ├── common-file-2 + " │   └── sub-file + " └── temp-file + new exe 'lcd' subdir AssertEqual neomake#utils#FindGlobFile('doesnotexist'), '' AssertEqual neomake#utils#FindGlobFile('sub-file'), subfile + AssertEqual neomake#utils#FindGlobFile('sub-file', subdir), subfile AssertEqual neomake#utils#FindGlobFile('sub-file', tempdir), '' AssertEqual neomake#utils#FindGlobFile('common-file'), file_in_subdir AssertEqual neomake#utils#FindGlobFile('common-file', tempdir), file_in_tempdir + " Behaves the same. + AssertEqual neomake#utils#find_nearest_file_upwards(['doesnotexist']), '' + AssertEqual neomake#utils#find_nearest_file_upwards(['sub-file']), subfile + AssertEqual neomake#utils#find_nearest_file_upwards(['sub-file'], subdir), subfile + AssertEqual neomake#utils#find_nearest_file_upwards(['sub-file'], tempdir), '' + AssertEqual neomake#utils#find_nearest_file_upwards(['common-file']), file_in_subdir + AssertEqual neomake#utils#find_nearest_file_upwards(['common-file'], tempdir), file_in_tempdir + + AssertEqual neomake#utils#find_nearest_file_upwards(['sub-file', 'temp-file']), subfile + AssertEqual neomake#utils#find_nearest_file_upwards(['common-file', 'temp-file']), file_in_subdir exe 'lcd' tempdir AssertEqual neomake#utils#FindGlobFile('sub-file'), '' @@ -794,10 +813,51 @@ Execute (neomake#utils#FindGlobFile): AssertEqual neomake#utils#FindGlobFile('common-file'), file_in_tempdir AssertEqual neomake#utils#FindGlobFile('common-file'), file_in_tempdir AssertEqual neomake#utils#FindGlobFile('common-file', subdir), file_in_subdir + " Behaves the same. + AssertEqual neomake#utils#find_nearest_file_upwards(['sub-file']), '' + AssertEqual neomake#utils#find_nearest_file_upwards(['sub-file'], subdir), subfile + AssertEqual neomake#utils#find_nearest_file_upwards(['sub-file'], tempdir), '' + AssertEqual neomake#utils#find_nearest_file_upwards(['sub-file'], tempname()), '' + AssertEqual neomake#utils#find_nearest_file_upwards(['common-file']), file_in_tempdir + AssertEqual neomake#utils#find_nearest_file_upwards(['common-file']), file_in_tempdir + AssertEqual neomake#utils#find_nearest_file_upwards(['common-file'], subdir), file_in_subdir + + " Handles relative path/startdir. + AssertEqual neomake#utils#FindGlobFile('sub-file', 'sub'), 'sub'.slash.'sub-file' + AssertEqual neomake#utils#find_nearest_file_upwards(['sub-file'], 'sub'), subfile + + " FindGlobFile handles directories. + AssertEqual neomake#utils#FindGlobFile('sub'), subdir + " find_nearest_file_upwards does not handle directories. + AssertEqual neomake#utils#find_nearest_file_upwards(['sub']), '' " Only the first found file gets returned. AssertEqual neomake#utils#FindGlobFile('common-file{,-2}', subdir), file_in_subdir AssertEqual neomake#utils#FindGlobFile('common-file{-2,}', subdir), anotherfile_in_subdir + " Behaves the same. + AssertEqual neomake#utils#find_nearest_file_upwards(['common-file', 'common-file-2'], subdir), file_in_subdir + AssertEqual neomake#utils#find_nearest_file_upwards(['common-file-2', 'common-file'], subdir), anotherfile_in_subdir + bwipe + +Execute (neomake#utils#find_nearest_file_upwards handles &suffixesadd): + let tempdir = tempname() + let slash = neomake#utils#Slash() + call mkdir(tempdir, 'p', 0700) + let file_with_ext = tempdir.slash.'file_with_ext.txt' + call writefile([], file_with_ext) + + new + exe 'lcd' tempdir + setlocal suffixesadd=.txt + + " Test behavior with used findfile. + AssertEqual findfile('file_with_ext', fnameescape(tempdir).';'), 'file_with_ext.txt' + + AssertEqual neomake#utils#find_nearest_file_upwards(['file_with_ext']), '' + AssertEqual neomake#utils#find_nearest_file_upwards(['file_with_ext.txt']), file_with_ext + + " Gets reset. + AssertEqual findfile('file_with_ext', '.;'), 'file_with_ext.txt' bwipe Execute (neomake#utils#shellescape):