
On macOS, the `$TMPDIR` is in `/var`. However, `/var -> /private/var`. This means that fully resolved temp filenames weren't always getting checked against the proper prefix. This was affecting some of the Haskell plugins, though I'm sure it could have affected any program that resolved past the generated `$TMPDIR/foo` and wound up at a different filename.
144 lines
4.7 KiB
VimL
144 lines
4.7 KiB
VimL
" Author: w0rp <devw0rp@gmail.com>
|
|
" Description: Functions for working with paths in the filesystem.
|
|
|
|
function! ale#path#Simplify(path) abort
|
|
" //foo is turned into /foo to stop Windows doing stupid things with
|
|
" search paths.
|
|
return substitute(simplify(a:path), '^//\+', '/', 'g') " no-custom-checks
|
|
endfunction
|
|
|
|
" Given a buffer and a filename, find the nearest file by searching upwards
|
|
" through the paths relative to the given buffer.
|
|
function! ale#path#FindNearestFile(buffer, filename) abort
|
|
let l:buffer_filename = fnamemodify(bufname(a:buffer), ':p')
|
|
|
|
let l:relative_path = findfile(a:filename, l:buffer_filename . ';')
|
|
|
|
if !empty(l:relative_path)
|
|
return fnamemodify(l:relative_path, ':p')
|
|
endif
|
|
|
|
return ''
|
|
endfunction
|
|
|
|
" Given a buffer and a directory name, find the nearest directory by searching upwards
|
|
" through the paths relative to the given buffer.
|
|
function! ale#path#FindNearestDirectory(buffer, directory_name) abort
|
|
let l:buffer_filename = fnamemodify(bufname(a:buffer), ':p')
|
|
|
|
let l:relative_path = finddir(a:directory_name, l:buffer_filename . ';')
|
|
|
|
if !empty(l:relative_path)
|
|
return fnamemodify(l:relative_path, ':p')
|
|
endif
|
|
|
|
return ''
|
|
endfunction
|
|
|
|
" Given a buffer, a string to search for, an a global fallback for when
|
|
" the search fails, look for a file in parent paths, and if that fails,
|
|
" use the global fallback path instead.
|
|
function! ale#path#ResolveLocalPath(buffer, search_string, global_fallback) abort
|
|
" Search for a locally installed file first.
|
|
let l:path = ale#path#FindNearestFile(a:buffer, a:search_string)
|
|
|
|
" If the serach fails, try the global executable instead.
|
|
if empty(l:path)
|
|
let l:path = a:global_fallback
|
|
endif
|
|
|
|
return l:path
|
|
endfunction
|
|
|
|
" Output 'cd <directory> && '
|
|
" This function can be used changing the directory for a linter command.
|
|
function! ale#path#CdString(directory) abort
|
|
return 'cd ' . ale#Escape(a:directory) . ' && '
|
|
endfunction
|
|
|
|
" Output 'cd <buffer_filename_directory> && '
|
|
" This function can be used changing the directory for a linter command.
|
|
function! ale#path#BufferCdString(buffer) abort
|
|
return ale#path#CdString(fnamemodify(bufname(a:buffer), ':p:h'))
|
|
endfunction
|
|
|
|
" Return 1 if a path is an absolute path.
|
|
function! ale#path#IsAbsolute(filename) abort
|
|
" Check for /foo and C:\foo, etc.
|
|
return a:filename[:0] ==# '/' || a:filename[1:2] ==# ':\'
|
|
endfunction
|
|
|
|
" Given a filename, return 1 if the file represents some temporary file
|
|
" created by Vim.
|
|
function! ale#path#IsTempName(filename) abort
|
|
let l:prefix_list = [
|
|
\ $TMPDIR,
|
|
\ resolve($TMPDIR),
|
|
\ '/run/user',
|
|
\]
|
|
|
|
for l:prefix in l:prefix_list
|
|
if a:filename[:len(l:prefix) - 1] ==# l:prefix
|
|
return 1
|
|
endif
|
|
endfor
|
|
|
|
return 0
|
|
endfunction
|
|
|
|
" Given a buffer number and a relative or absolute path, return 1 if the
|
|
" two paths represent the same file on disk.
|
|
function! ale#path#IsBufferPath(buffer, complex_filename) abort
|
|
" If the path is one of many different names for stdin, we have a match.
|
|
if a:complex_filename ==# '-'
|
|
\|| a:complex_filename ==# 'stdin'
|
|
\|| a:complex_filename[:0] ==# '<'
|
|
return 1
|
|
endif
|
|
|
|
let l:test_filename = ale#path#Simplify(a:complex_filename)
|
|
|
|
if l:test_filename[:1] ==# './'
|
|
let l:test_filename = l:test_filename[2:]
|
|
endif
|
|
|
|
if l:test_filename[:1] ==# '..'
|
|
" Remove ../../ etc. from the front of the path.
|
|
let l:test_filename = substitute(l:test_filename, '\v^(\.\.[/\\])+', '/', '')
|
|
endif
|
|
|
|
" Use the basename for temporary files, as they are likely our files.
|
|
if ale#path#IsTempName(l:test_filename)
|
|
let l:test_filename = fnamemodify(l:test_filename, ':t')
|
|
endif
|
|
|
|
let l:buffer_filename = expand('#' . a:buffer . ':p')
|
|
|
|
return l:buffer_filename ==# l:test_filename
|
|
\ || l:buffer_filename[-len(l:test_filename):] ==# l:test_filename
|
|
endfunction
|
|
|
|
" Given a path, return every component of the path, moving upwards.
|
|
function! ale#path#Upwards(path) abort
|
|
let l:pattern = ale#Has('win32') ? '\v/+|\\+' : '\v/+'
|
|
let l:sep = ale#Has('win32') ? '\' : '/'
|
|
let l:parts = split(ale#path#Simplify(a:path), l:pattern)
|
|
let l:path_list = []
|
|
|
|
while !empty(l:parts)
|
|
call add(l:path_list, join(l:parts, l:sep))
|
|
let l:parts = l:parts[:-2]
|
|
endwhile
|
|
|
|
if ale#Has('win32') && a:path =~# '^[a-zA-z]:\'
|
|
" Add \ to C: for C:\, etc.
|
|
let l:path_list[-1] .= '\'
|
|
elseif a:path[0] ==# '/'
|
|
" If the path starts with /, even on Windows, add / and / to all paths.
|
|
call map(l:path_list, '''/'' . v:val')
|
|
call add(l:path_list, '/')
|
|
endif
|
|
|
|
return l:path_list
|
|
endfunction
|