Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions autoload/fern/fri.vim
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,28 @@ function! fern#fri#new(partial) abort
endfunction

function! fern#fri#parse(expr) abort

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get why you need to apply changes to fern#fri#parse().
Supporting the Windows UNC path should not touch FRI itself or let me know the reason.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix authority and path parsing.

Changed result of 'foo://bar/' below.

""" Old
echo fern#fri#parse('foo://bar/qux')
" => {'query': {}, 'authority': 'bar', 'fragment': '', 'scheme': 'foo', 'path': 'qux'}
echo fern#fri#parse('foo://bar/')
" => {'query': {}, 'authority': '', 'fragment': '', 'scheme': 'foo', 'path': 'bar'}
echo fern#fri#parse('foo://bar')
" => {'query': {}, 'authority': '', 'fragment': '', 'scheme': 'foo', 'path': 'bar'}

""" New
echo fern#fri#parse('foo://bar/qux')
" => {'query': {}, 'authority': 'bar', 'fragment': '', 'scheme': 'foo', 'path': 'qux'}
echo fern#fri#parse('foo://bar/')
" => {'query': {}, 'authority': 'bar', 'fragment': '', 'scheme': 'foo', 'path': ''}
echo fern#fri#parse('foo://bar')
" => {'query': {}, 'authority': '', 'fragment': '', 'scheme': 'foo', 'path': 'bar'}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. So this is kinda bug fix, right? It would be nice if you separate it to a different PR.

let remains = a:expr =~# '^fern:'
let is_fern = a:expr =~# '^fern:'
let remains = is_fern
\ ? matchstr(a:expr, '.\{-}\ze\$\?$')
\ : a:expr
let [scheme, remains] = s:split1(remains, escape('://', s:PATTERN))
if empty(remains)
if remains is# v:null
let remains = scheme
let scheme = ''
endif
let [authority, remains] = s:split1(remains, escape('/', s:PATTERN))
if empty(remains)
if remains is# v:null
let remains = authority
let authority = ''
endif
let [path, remains] = s:split1(remains, escape(';', s:PATTERN))
if empty(remains)
if remains is# v:null
let query = ''
let [path, fragment] = s:split1(path, escape('#', s:PATTERN))
let [path, remains] = s:split1(path, escape('#', s:PATTERN))
else
let [query, fragment] = s:split1(remains, escape('#', s:PATTERN))
let [query, remains] = s:split1(remains, escape('#', s:PATTERN))
endif
let fragment = remains isnot# v:null ? remains : ''
return {
\ 'scheme': scheme,
\ 'authority': s:decode(authority),
Expand Down Expand Up @@ -135,7 +137,7 @@ endfunction
function! s:split1(str, pattern) abort
let [_, s, e] = matchstrpos(a:str, a:pattern)
if s is# -1
return [a:str, '']
return [a:str, v:null]
elseif s is# 0
return ['', a:str[e :]]
endif
Expand Down
18 changes: 18 additions & 0 deletions autoload/fern/fri/from.vim
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ function! fern#fri#from#filepath(path) abort
if !fern#internal#filepath#is_absolute(a:path)
throw printf('The "path" must be an absolute path but "%s" has specfied', a:path)
endif
if fern#internal#filepath#is_uncpath(a:path)
return s:from_uncpath(a:path)
endif
let path = fern#internal#filepath#to_slash(a:path)
return fern#fri#from#path(path)
endfunction
Expand All @@ -19,3 +22,18 @@ function! fern#fri#from#path(path) abort
\ 'path': path[1:],
\})
endfunction

function! fern#fri#from#uncpath(path) abort
if !fern#internal#filepath#is_uncpath(a:path)
throw printf('The "path" must be an UNC path but "%s" has specfied', a:path)
endif
return s:from_uncpath(a:path)
endfunction

function! s:from_uncpath(path) abort
let terms = filter(split(a:path[2:], '\'), { -> !empty(v:val) })
let path = join(terms[1:], '/')
let fri = fern#fri#from#path('/' . path)
let fri.authority = terms[0]
return fri
endfunction
23 changes: 23 additions & 0 deletions autoload/fern/fri/to.vim
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ function! fern#fri#to#filepath(fri) abort
if a:fri.scheme !=# 'file'
throw printf('The "scheme" must be "file" but "%s" has specified', a:fri.scheme)
endif
if !empty(a:fri.authority) && fern#internal#filepath#is_unc_compat()
return s:to_uncpath(a:fri)
endif
let path = fern#fri#to#path(a:fri)
let path = fern#internal#filepath#from_slash(path)
return path
Expand All @@ -12,3 +15,23 @@ function! fern#fri#to#path(fri) abort
let path = fern#fri#decode(path)
return path
endfunction

function! fern#fri#to#uncpath(fri) abort
if !fern#internal#filepath#is_unc_compat()
throw printf('The system does not support UNC path')
endif
if a:fri.scheme !=# 'file'
throw printf('The "scheme" must be "file" but "%s" has specified', a:fri.scheme)
endif
if empty(a:fri.authority)
throw printf('The "authority" must be non-empty')
endif
return s:to_uncpath(a:fri)
endfunction

function! s:to_uncpath(fri) abort
let path = fern#fri#decode(a:fri.path)
let path = fern#internal#filepath#from_slash(path)
let res = printf('\\%s\%s', a:fri.authority, path)
return res
endfunction
13 changes: 7 additions & 6 deletions autoload/fern/internal/command/do.vim
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
function! fern#internal#command#do#command(mods, fargs) abort
function! fern#internal#command#do#command(mods, qargs) abort

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function interface must not be changed while the function itself is OK.

let winid_saved = win_getid()
try
let stay = fern#internal#args#pop(a:fargs, 'stay', v:false)
let drawer = fern#internal#args#pop(a:fargs, 'drawer', v:false)
let fargs = fern#internal#args#split(a:qargs)
let stay = fern#internal#args#pop(fargs, 'stay', v:false)
let drawer = fern#internal#args#pop(fargs, 'drawer', v:false)

if len(a:fargs) is# 0
if len(fargs) is# 0
\ || type(stay) isnot# v:t_bool
\ || type(drawer) isnot# v:t_bool
throw 'Usage: FernDo {expr...} [-drawer] [-stay]'
endif

" Does all options are handled?
call fern#internal#args#throw_if_dirty(a:fargs)
call fern#internal#args#throw_if_dirty(fargs)

let found = fern#internal#window#find(
\ funcref('s:predicator', [drawer]),
Expand All @@ -21,7 +22,7 @@ function! fern#internal#command#do#command(mods, fargs) abort
return
endif
call win_gotoid(win_getid(found))
execute join([a:mods] + a:fargs, ' ')
execute join([a:mods] + fargs, ' ')
catch
echohl ErrorMsg
echo v:exception
Expand Down
25 changes: 13 additions & 12 deletions autoload/fern/internal/command/fern.vim
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
let s:Promise = vital#fern#import('Async.Promise')
let s:drawer_opener = 'topleft vsplit'

function! fern#internal#command#fern#command(mods, fargs) abort
function! fern#internal#command#fern#command(mods, qargs) abort
try
let stay = fern#internal#args#pop(a:fargs, 'stay', v:false)
let wait = fern#internal#args#pop(a:fargs, 'wait', v:false)
let reveal = fern#internal#args#pop(a:fargs, 'reveal', '')
let drawer = fern#internal#args#pop(a:fargs, 'drawer', v:false)
let fargs = fern#internal#args#split(a:qargs)
let stay = fern#internal#args#pop(fargs, 'stay', v:false)
let wait = fern#internal#args#pop(fargs, 'wait', v:false)
let reveal = fern#internal#args#pop(fargs, 'reveal', '')
let drawer = fern#internal#args#pop(fargs, 'drawer', v:false)
if drawer
let opener = s:drawer_opener
let width = fern#internal#args#pop(a:fargs, 'width', '')
let keep = fern#internal#args#pop(a:fargs, 'keep', v:false)
let toggle = fern#internal#args#pop(a:fargs, 'toggle', v:false)
let width = fern#internal#args#pop(fargs, 'width', '')
let keep = fern#internal#args#pop(fargs, 'keep', v:false)
let toggle = fern#internal#args#pop(fargs, 'toggle', v:false)
else
let opener = fern#internal#args#pop(a:fargs, 'opener', g:fern#opener)
let opener = fern#internal#args#pop(fargs, 'opener', g:fern#opener)
let width = ''
let keep = v:false
let toggle = v:false
endif

if len(a:fargs) isnot# 1
if len(fargs) isnot# 1
\ || type(stay) isnot# v:t_bool
\ || type(wait) isnot# v:t_bool
\ || type(reveal) isnot# v:t_string
Expand All @@ -36,7 +37,7 @@ function! fern#internal#command#fern#command(mods, fargs) abort
endif

" Does all options are handled?
call fern#internal#args#throw_if_dirty(a:fargs)
call fern#internal#args#throw_if_dirty(fargs)

" Force project drawer style when
" - The current buffer is project drawer style fern
Expand All @@ -46,7 +47,7 @@ function! fern#internal#command#fern#command(mods, fargs) abort
let opener = s:drawer_opener
endif

let expr = fern#util#expand(a:fargs[0])
let expr = fern#util#expand(fargs[0])
let path = fern#fri#format(
\ expr =~# '^[^:]\+://'
\ ? fern#fri#parse(expr)
Expand Down
14 changes: 13 additions & 1 deletion autoload/fern/internal/filepath.vim
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ function! fern#internal#filepath#is_absolute(path) abort
\ : s:is_absolute_unix(a:path)
endfunction

function! fern#internal#filepath#is_unc_compat() abort
return g:fern#internal#filepath#is_windows

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If Neovim 0.4.4 does not support the UNC path, we should check it here, right?

Additionally, it would be nice to make an issue on the Neovim side and put the issue URL here to explain why we need to exclude Neovim.

endfunction

function! fern#internal#filepath#is_uncpath(path) abort
return g:fern#internal#filepath#is_windows && s:is_uncpath(a:path)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should NOT check g:fern#internal#filepath#is_windows here while the function purpose is to check if the path is a UNC path or not.

endfunction

function! fern#internal#filepath#join(paths) abort
let paths = map(
\ copy(a:paths),
Expand Down Expand Up @@ -59,8 +67,12 @@ function! s:from_slash_windows(path) abort
return path[:2] =~# '^\w:$' ? path . '\' : path
endfunction

function! s:is_uncpath(path) abort
return a:path =~# '^\\\\[^\\]\+'
endfunction

function! s:is_absolute_windows(path) abort
return a:path ==# '' || a:path[:2] =~# '^\w:\\$'
return a:path ==# '' || a:path[:2] =~# '^\w:\\$' || s:is_uncpath(a:path)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it's not correct. It just checks if the path is a UNC path and it does not check if the path is an absolute UNC path, right?

endfunction

function! s:is_absolute_unix(path) abort
Expand Down
113 changes: 74 additions & 39 deletions autoload/fern/scheme/file/complete.vim
Original file line number Diff line number Diff line change
@@ -1,47 +1,41 @@
let s:PATHSEP = fnamemodify('.', ':p')[-1 :]

function! fern#scheme#file#complete#url(arglead, cmdline, cursorpos) abort
let path = '/' . fern#fri#parse(a:arglead).path
let path = fern#internal#filepath#to_slash(path)
let suffix = a:arglead =~# '/$' ? '/' : ''
let rs = getcompletion(fern#internal#filepath#from_slash(path) . suffix, 'dir')
call map(rs, { -> fern#internal#filepath#to_slash(v:val) })
call map(rs, { -> s:to_fri(v:val) })
let suffix = a:arglead =~# '/$' ? s:PATHSEP : ''
let fpath = fern#fri#to#filepath(fern#fri#parse(a:arglead))
let rs = s:complete_filepath(fpath . suffix, 'dir')
call map(rs, { -> fern#fri#from#filepath(v:val) })
call map(rs, { -> fern#fri#format(v:val) })
return rs
endfunction

function! fern#scheme#file#complete#reveal(arglead, cmdline, cursorpos) abort
let base = '/' . fern#fri#parse(matchstr(a:cmdline, '\<file:///\S*')).path
let base_url = matchstr(a:cmdline, '\<file://\S*')
let fbase = fern#fri#to#filepath(fern#fri#parse(base_url))
let path = matchstr(a:arglead, '^-reveal=\zs.*')
let suffix = matchstr(path, '/$')
let rs = s:complete_reveal(path, base, suffix)
let suffix = path =~# '/$' ? s:PATHSEP : ''
let fpath = path ==# '' ? '' : fern#internal#filepath#from_slash(path)
let rs = s:complete_reveal(fpath . suffix, fbase)
call map(rs, { -> fern#internal#filepath#to_slash(v:val) })
call map(rs, { -> printf('-reveal=%s', v:val) })
return rs
endfunction

function! fern#scheme#file#complete#filepath(arglead, cmdline, cursorpos) abort
return map(getcompletion(a:arglead, 'dir'), { -> escape(v:val, ' ') })
let rs = s:complete_filepath(a:arglead, 'dir')
call map(rs, { -> escape(v:val, ' ') })
return rs
endfunction

function! fern#scheme#file#complete#filepath_reveal(arglead, cmdline, cursorpos) abort
let base = fern#internal#filepath#to_slash(s:get_basepath(a:cmdline))
let path = matchstr(a:arglead, '^-reveal=\zs.*')
let suffix = matchstr(path, '[/\\]$')
let path = path ==# '' ? '' : fern#internal#filepath#to_slash(path)
let rs = s:complete_reveal(path, base, suffix)
call map(rs, { -> v:val ==# '' ? '' : fern#internal#filepath#from_slash(v:val) })
let fbase = s:get_basepath(a:cmdline)
let fpath = matchstr(a:arglead, '^-reveal=\zs.*')
let rs = s:complete_reveal(fpath, fbase)
call map(rs, { -> substitute(v:val, '[/\\]$', '', '') })
call map(rs, { -> printf('-reveal=%s', escape(v:val, ' ')) })
return rs
endfunction

function! s:to_fri(path) abort
return fern#fri#format({
\ 'scheme': 'file',
\ 'authority': '',
\ 'path': a:path[1:],
\ 'query': {},
\ 'fragment': '',
\})
endfunction

function! s:get_basepath(cmdline) abort
let fargs = fern#internal#args#split(a:cmdline)
let fargs = fargs[index(fargs, 'Fern') + 1:]
Expand All @@ -50,20 +44,61 @@ function! s:get_basepath(cmdline) abort
return fnamemodify(base, ':p')
endfunction

function! s:complete_reveal(path, base, suffix) abort
let [path, base, suffix] = [a:path, a:base, a:suffix]
if path ==# ''
let path = a:base
let suffix = '/'
elseif path[:0] ==# '/'
let base = ''
else
let path = fern#internal#path#absolute(path, a:base)
function! s:complete_filepath(path, type) abort
if fern#internal#filepath#is_uncpath(a:path)
let fri = fern#fri#from#filepath(a:path)
if fri.path ==# '' || fri.path !~# '/' && a:path !~# '[/\\]$'
return s:windows_share_nodes(fri)
endif
endif
let rs = getcompletion(fern#internal#filepath#from_slash(path) . suffix, 'file')
call map(rs, { -> fern#internal#filepath#to_slash(v:val) })
if base !=# ''
call map(rs, { -> fern#internal#path#relative(v:val, base) })
return getcompletion(a:path, a:type)
endfunction

function! s:complete_reveal(fpath, fbase) abort
let [fpath, fbase] = [a:fpath, a:fbase]
if fpath !=# '' && fern#internal#filepath#is_absolute(fpath)
return s:complete_filepath(fpath, 'file')
endif
if fpath ==# ''
if fbase !~# '[/\\]$'
let fbase .= s:PATHSEP
endif
let base = fern#internal#filepath#to_slash(fbase)
let rs = s:complete_filepath(fbase, 'file')
call map(rs, { -> fern#internal#filepath#to_slash(v:val) })
else
let suffix = matchstr(fpath, '[/\\]$')
let path = fern#internal#filepath#to_slash(fpath)
let fri = fern#fri#from#filepath(fbase)
let base = '/' . fri.path
let fri.path = fern#internal#path#absolute(path, base)[1 :]
let rs = s:complete_filepath(fern#fri#to#filepath(fri) . suffix, 'file')
call map(rs, { -> '/' . fern#fri#from#filepath(v:val).path })
endif
call map(rs, { -> fern#internal#path#relative(v:val, base) })
call map(rs, { -> v:val ==# '' ? '' : fern#internal#filepath#from_slash(v:val) })
return rs
endfunction

if has('win32')
let s:Promise = vital#fern#import('Async.Promise')
let s:CancellationToken = vital#fern#import('Async.CancellationToken')

function! s:windows_share_nodes(fri) abort
let waiter = fern#scheme#file#util#list_shares(
\ s:CancellationToken.none, a:fri.authority)
let [rs, err] = s:Promise.wait(waiter, {'timeout': 2000})
if err isnot# v:null
return []
endif
if a:fri.path !=# ''
let path = tolower(a:fri.path)
let plen = strlen(path)
call filter(rs, { -> tolower(v:val[: plen]) ==# path })
endif
call map(rs, { -> extend(deepcopy(a:fri), {'path': v:val}) })
call map(rs, { -> fern#fri#to#uncpath(v:val) . '\' })
call filter(rs, { -> getftype(v:val) ==# 'dir' })
return rs
endfunction
endif
Loading