Skip to content
Merged
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
1 change: 1 addition & 0 deletions autoload/fern.vim
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ call s:Config.config(expand('<sfile>:p'), {
\ 'comparators': get(g:, 'fern#internal#core#comparators', {}),
\ 'drawer_width': 30,
\ 'drawer_keep': v:false,
\ 'mark_symbol': '*',
\})
22 changes: 17 additions & 5 deletions autoload/fern/helper/async.vim
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
let s:Promise = vital#fern#import('Async.Promise')
let s:AsyncLambda = vital#fern#import('Async.Lambda')

function! fern#helper#async#new(helper) abort
let async = extend({ 'helper': a:helper }, s:async)
Expand All @@ -17,17 +18,28 @@ function! s:async_redraw() abort dict
let helper = self.helper
let fern = helper.fern
return s:Promise.resolve()
\.then({ -> fern.renderer.render(
\ fern.visible_nodes,
\ fern.marks,
\ )
\})
\.then({ -> fern.renderer.render(fern.visible_nodes) })
\.then({ v -> fern#internal#buffer#replace(helper.bufnr, v) })
\.then({ -> helper.async.remark() })
\.then({ -> fern#hook#emit('viewer:redraw', helper) })
\.finally({ -> Profile() })
endfunction
let s:async.redraw = funcref('s:async_redraw')

function! s:async_remark() abort dict
let Profile = fern#profile#start('fern#helper:helper.async.remark')
let helper = self.helper
let fern = helper.fern
let marks = fern.marks
return s:Promise.resolve(fern.visible_nodes)
\.then(s:AsyncLambda.map_f({ n, i -> index(marks, n.__key) isnot# -1 ? i + 1 : 0 }))
\.then(s:AsyncLambda.filter_f({ v -> v isnot# 0 }))
\.then({ v -> fern#internal#mark#replace(helper.bufnr, v) })
\.then({ -> fern#hook#emit('viewer:remark', helper) })
\.finally({ -> Profile() })
endfunction
let s:async.remark = funcref('s:async_remark')

function! s:async_set_mark(key, value) abort dict
let helper = self.helper
let fern = helper.fern
Expand Down
29 changes: 29 additions & 0 deletions autoload/fern/internal/mark.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
function! fern#internal#mark#replace(bufnr, lnums) abort
call execute(printf('sign unplace * group=fern-mark buffer=%d', a:bufnr))
call map(a:lnums, { _, v -> execute(printf(
\ 'sign place %d group=fern-mark line=%d name=FernSignMarked buffer=%d',
\ v,
\ v,
\ a:bufnr,
\))})
endfunction

function! s:define_signs() abort
execute printf(
\ 'sign define FernSignMarked text=%s linehl=FernMarkedLine texthl=FernMarkedText',
\ g:fern#mark_symbol,
\)
endfunction

function! s:define_highlight() abort
highlight default link FernMarkedLine Title
highlight default link FernMarkedText Title
endfunction

augroup fern_mark_internal
autocmd!
autocmd ColorScheme * call s:define_highlight()
augroup END

call s:define_signs()
call s:define_highlight()
1 change: 1 addition & 0 deletions autoload/fern/internal/node.vim
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ endfunction
function! s:new(node, ...) abort
let node = extend(a:node, {
\ 'label': get(a:node, 'label', a:node.name),
\ 'badge': get(a:node, 'badge', ''),
\ 'hidden': get(a:node, 'hidden', 0),
\ 'bufname': get(a:node, 'bufname', v:null),
\ 'concealed': get(a:node, 'concealed', {}),
Expand Down
4 changes: 2 additions & 2 deletions autoload/fern/internal/spinner.vim
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function! s:update(timer, spinner, bufnr) abort
return
endif
let frame = a:spinner.next()
call execute(printf('sign unplace * group=fern buffer=%d', a:bufnr))
call execute(printf('sign unplace * group=fern-spinner buffer=%d', a:bufnr))
let info = getwininfo(winid)[0]
let rng = sort([info.topline, info.botline], 'n')
for lnum in range(rng[0], rng[1])
Expand All @@ -36,7 +36,7 @@ function! s:update(timer, spinner, bufnr) abort
continue
endif
call execute(printf(
\ 'sign place %d group=fern line=%d name=%s buffer=%d',
\ 'sign place %d group=fern-spinner line=%d name=%s buffer=%d',
\ lnum,
\ lnum,
\ frame,
Expand Down
20 changes: 20 additions & 0 deletions autoload/fern/internal/viewer.vim
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,18 @@ function! s:init() abort
call fern#internal#drawer#init()
call fern#internal#spinner#start()
call helper.fern.renderer.highlight()
" Notify plugins
call fern#hook#emit('renderer:highlight', helper)
" Notify users
doautocmd <nomodeline> User FernHighlight

" now the buffer is ready so set filetype to emit FileType
setlocal filetype=fern
call helper.fern.renderer.syntax()
" Notify plugins
call fern#hook#emit('renderer:syntax', helper)
" Notify users
doautocmd <nomodeline> User FernSyntax
call fern#internal#action#init()

let reveal = split(fri.fragment, '/')
Expand Down Expand Up @@ -106,7 +114,10 @@ function! s:BufReadCmd() abort
let helper = fern#helper#new()
setlocal filetype=fern
call helper.fern.renderer.syntax()
" Notify plugins
call fern#hook#emit('renderer:syntax', helper)
" Notify users
doautocmd <nomodeline> User FernSyntax
let root = helper.sync.get_root_node()
let cursor = get(b:, 'fern_cursor', getcurpos())
call s:Promise.resolve()
Expand All @@ -121,5 +132,14 @@ endfunction
function! s:ColorScheme() abort
let helper = fern#helper#new()
call helper.fern.renderer.highlight()
" Notify plugins
call fern#hook#emit('renderer:highlight', helper)
" Notify users
doautocmd <nomodeline> User FernHighlight
endfunction

augroup fern-internal-viewer-internal
autocmd!
autocmd User FernSyntax :
autocmd User FernHighlight :
augroup END
7 changes: 4 additions & 3 deletions autoload/fern/mapping/mark.vim
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function! s:map_mark_set(helper) abort
return s:Promise.reject('no node found on a cursor line')
endif
return a:helper.async.set_mark(node.__key, 1)
\.then({ -> a:helper.async.redraw() })
\.then({ -> a:helper.async.remark() })
endfunction

function! s:map_mark_unset(helper) abort
Expand All @@ -39,7 +39,7 @@ function! s:map_mark_unset(helper) abort
return s:Promise.reject('no node found on a cursor line')
endif
return a:helper.async.set_mark(node.__key, 0)
\.then({ -> a:helper.async.redraw() })
\.then({ -> a:helper.async.remark() })
endfunction

function! s:map_mark_toggle(helper) abort
Expand All @@ -55,5 +55,6 @@ function! s:map_mark_toggle(helper) abort
endfunction

function! s:map_mark_clear(helper) abort
return a:helper.update_marks([])
return a:helper.async.update_marks([])
\.then({ -> a:helper.async.remark() })
endfunction
2 changes: 1 addition & 1 deletion autoload/fern/mapping/open.vim
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function! s:map_open(helper, opener) abort
noautocmd call win_gotoid(winid)
noautocmd call win_gotoid(winid_fern)
return a:helper.async.update_marks([])
\.then({ -> a:helper.async.redraw() })
\.then({ -> a:helper.async.remark() })
catch
return s:Promise.reject(v:exception)
endtry
Expand Down
60 changes: 34 additions & 26 deletions autoload/fern/renderer/default.vim
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
let s:Config = vital#fern#import('Config')
let s:AsyncLambda = vital#fern#import('Async.Lambda')

let s:PATTERN = '^$~.*[]\'
let s:ESCAPE_PATTERN = '^$~.*[]\'
let s:STATUS_NONE = g:fern#STATUS_NONE
let s:STATUS_COLLAPSED = g:fern#STATUS_COLLAPSED

Expand All @@ -15,19 +15,17 @@ function! fern#renderer#default#new() abort
\}
endfunction

function! s:render(nodes, marks) abort
function! s:render(nodes) abort
let options = {
\ 'leading': g:fern#renderer#default#leading,
\ 'root_symbol': g:fern#renderer#default#root_symbol,
\ 'leaf_symbol': g:fern#renderer#default#leaf_symbol,
\ 'expanded_symbol': g:fern#renderer#default#expanded_symbol,
\ 'collapsed_symbol': g:fern#renderer#default#collapsed_symbol,
\ 'marked_symbol': g:fern#renderer#default#marked_symbol,
\ 'unmarked_symbol': g:fern#renderer#default#unmarked_symbol,
\}
let base = len(a:nodes[0].__key)
let Profile = fern#profile#start('fern#renderer#default#s:render')
return s:AsyncLambda.map(copy(a:nodes), { v, -> s:render_node(v, a:marks, base, options) })
return s:AsyncLambda.map(copy(a:nodes), { v, -> s:render_node(v, base, options) })
\.finally({ -> Profile() })
endfunction

Expand All @@ -40,45 +38,46 @@ function! s:lnum(index) abort
endfunction

function! s:syntax() abort
syntax clear
execute printf(
\ 'syntax match FernLeaf /^\s*%s/',
\ escape(g:fern#renderer#default#leaf_symbol, s:PATTERN),
\ 'syntax match FernRootSymbol /\%%1l%s/ nextgroup=FernRootText',
\ escape(g:fern#renderer#default#root_symbol, s:ESCAPE_PATTERN),
\)
execute printf(
\ 'syntax match FernBranch /^\s*\%%(%s\|%s\).*/',
\ escape(g:fern#renderer#default#collapsed_symbol, s:PATTERN),
\ escape(g:fern#renderer#default#expanded_symbol, s:PATTERN),
\ 'syntax match FernLeafSymbol /^\s*%s/ nextgroup=FernLeafText',
\ escape(g:fern#renderer#default#leaf_symbol, s:ESCAPE_PATTERN),
\)
syntax match FernRoot /\%1l.*/
execute printf(
\ 'syntax match FernMarked /^%s.*/',
\ escape(g:fern#renderer#default#marked_symbol, s:PATTERN),
\ 'syntax match FernBranchSymbol /^\s*\%%(%s\|%s\)/ nextgroup=FernBranchText',
\ escape(g:fern#renderer#default#collapsed_symbol, s:ESCAPE_PATTERN),
\ escape(g:fern#renderer#default#expanded_symbol, s:ESCAPE_PATTERN),
\)
syntax match FernRootText /.*\ze .*$/ contained nextgroup=FernBadge
syntax match FernLeafText /.*\ze .*$/ contained nextgroup=FernBadge
syntax match FernBranchText /.*\ze .*$/ contained nextgroup=FernBadge
syntax match FernBadge /.*/ contained
endfunction

function! s:highlight() abort
highlight default link FernRoot Directory
highlight default link FernLeaf Directory
highlight default link FernBranch Directory
highlight default link FernMarked Title
highlight default link FernRootSymbol Directory
highlight default link FernRootText Directory
highlight default link FernLeafSymbol Directory
highlight default link FernLeafText None
highlight default link FernBranchSymbol Directory
highlight default link FernBranchText Directory
endfunction

function! s:render_node(node, marks, base, options) abort
let prefix = index(a:marks, a:node.__key) is# -1
\ ? a:options.unmarked_symbol
\ : a:options.marked_symbol
function! s:render_node(node, base, options) abort
let level = len(a:node.__key) - a:base
if level is# 0
return prefix . a:options.root_symbol . a:node.label
return a:options.root_symbol . a:node.label . ' ' . a:node.badge
endif
let leading = repeat(a:options.leading, level - 1)
let symbol = a:node.status is# s:STATUS_NONE
\ ? a:options.leaf_symbol
\ : a:node.status is# s:STATUS_COLLAPSED
\ ? a:options.collapsed_symbol
\ : a:options.expanded_symbol
return prefix . leading . symbol . a:node.label
return leading . symbol . a:node.label . ' ' . a:node.badge
endfunction

call s:Config.config(expand('<sfile>:p'), {
Expand All @@ -87,6 +86,15 @@ call s:Config.config(expand('<sfile>:p'), {
\ 'leaf_symbol': '| ',
\ 'collapsed_symbol': '|+ ',
\ 'expanded_symbol': '|- ',
\ 'marked_symbol': '* ',
\ 'unmarked_symbol': ' ',
\})

" Obsolete warnings
if exists('g:fern#renderer#default#marked_symbol')
call fern#util#obsolete(
\ 'g:fern#renderer#default#marked_symbol',
\ 'g:fern#mark_symbol',
\)
endif
if exists('g:fern#renderer#default#unmarked_symbol')
call fern#util#obsolete('g:fern#renderer#default#unmarked_symbol')
endif
4 changes: 2 additions & 2 deletions autoload/fern/scheme/dict/mapping/clipboard.vim
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function! s:map_clipboard_move(helper) abort
\}
return s:Promise.resolve()
\.then({ -> a:helper.async.update_marks([]) })
\.then({ -> a:helper.async.redraw() })
\.then({ -> a:helper.async.remark() })
\.then({ -> a:helper.sync.echo(printf('%d items are saved in clipboard to move', len(nodes))) })
endfunction

Expand All @@ -46,7 +46,7 @@ function! s:map_clipboard_copy(helper) abort
\}
return s:Promise.resolve()
\.then({ -> a:helper.async.update_marks([]) })
\.then({ -> a:helper.async.redraw() })
\.then({ -> a:helper.async.remark() })
\.then({ -> a:helper.sync.echo(printf('%d items are saved in clipboard to copy', len(nodes))) })
endfunction

Expand Down
4 changes: 2 additions & 2 deletions autoload/fern/scheme/file/mapping/clipboard.vim
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function! s:map_clipboard_move(helper) abort
\}
return s:Promise.resolve()
\.then({ -> a:helper.async.update_marks([]) })
\.then({ -> a:helper.async.redraw() })
\.then({ -> a:helper.async.remark() })
\.then({ -> a:helper.sync.echo(printf('%d items are saved in clipboard to move', len(nodes))) })
endfunction

Expand All @@ -46,7 +46,7 @@ function! s:map_clipboard_copy(helper) abort
\}
return s:Promise.resolve()
\.then({ -> a:helper.async.update_marks([]) })
\.then({ -> a:helper.async.redraw() })
\.then({ -> a:helper.async.remark() })
\.then({ -> a:helper.sync.echo(printf('%d items are saved in clipboard to copy', len(nodes))) })
endfunction

Expand Down
2 changes: 1 addition & 1 deletion autoload/fern/scheme/file/mapping/terminal.vim
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function! s:map_terminal(helper, opener) abort
enew | call s:term(node._path)
endfor
return a:helper.async.update_marks([])
\.then({ -> a:helper.async.redraw() })
\.then({ -> a:helper.async.remark() })
catch
return s:Promise.reject(v:exception)
endtry
Expand Down
16 changes: 15 additions & 1 deletion doc/fern-develop.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ A node instance is a tree item which has the following attributes:

"label" A |String| used to display the node in a tree view.

"badge" A |String| used to display the node badge in a tree view.
Only first character is used in the default renderer.

"hidden" A 1/0 to indicate if the node should be hidden. All hidden
nodes become visible once fern enter hidden mode.

Expand Down Expand Up @@ -192,9 +195,12 @@ Renderer is an instance which has the following methods to render a list of
nodes as a tree.

*fern-develop-renderer.render()*
.render({nodes}, {marks})
.render({nodes})
Return a promise which is resolved to a list of |String|.

Change (v1.6.0):~
Second argumet ({marks}) has removed.

*fern-develop-renderer.index()*
.index({lnum})
Return a corresponding index (|Number|) of {lnum}. It is used to find
Expand Down Expand Up @@ -438,6 +444,10 @@ Following methods are executed asynchronously and return a promise.
.async.redraw()
Return a promise to redraw the content.

*fern-develop-helper.async.remark()*
.async.remark()
Return a promise to remark the content.

*fern-develop-helper.async.set_mark()*
.async.set_mark({key}, {value})
Return a promise to set mark to a node identified by the {key}.
Expand Down Expand Up @@ -519,6 +529,10 @@ Following hook will be emitted by |fern#hook#emit()| from fern itself.
Called when fern viewer has redrawed.
The {helper} is a helper instance described in |fern-develop-helper|.

"viewer:remark" ({helper})
Called when fern viewer has remarked.
The {helper} is a helper instance described in |fern-develop-helper|.

"viewer:ready" ({helper})
Called when fern viewer has ready, mean that the buffer has opened and
all content has rendererd.
Expand Down
Loading