diff --git a/autoload/vaffle.vim b/autoload/vaffle.vim index 8371d4a..0f19359 100644 --- a/autoload/vaffle.vim +++ b/autoload/vaffle.vim @@ -21,10 +21,27 @@ endfunction function! vaffle#init(...) abort + let bufnr = bufnr('%') + let is_vaffle_buffer = vaffle#buffer#is_for_vaffle(bufnr) + + let path = get(a:000, 0, '') + let extracted_path = vaffle#buffer#extract_path_from_bufname(path) + if !empty(extracted_path) + let path = extracted_path + endif + if empty(path) + let path = getcwd() + endif + + let bufname = bufname('%') + if !is_vaffle_buffer && !isdirectory(bufname) + " Open new directory buffer and overwrite it + " (will be initialized by vaffle#event#on_bufenter) + execute printf('edit %s', fnameescape(path)) + return + endif + try - let path = (a:0 == 0) - \ ? getcwd() - \ : a:1 call vaffle#buffer#init(path) catch /:E37:/ call vaffle#util#echo_error( @@ -37,13 +54,18 @@ endfunction function! vaffle#refresh() abort call s:keep_buffer_singularity() + let env = vaffle#buffer#get_env() + let cursor_items = vaffle#item#get_cursor_items('n') if !empty(cursor_items) - call vaffle#env#save_cursor(cursor_items[0]) + call vaffle#buffer#save_cursor(cursor_items[0]) endif - let cwd = vaffle#env#get().dir - call vaffle#env#set_up(cwd) + let new_env = vaffle#env#create(env.dir) + call vaffle#env#inherit(new_env, env) + let new_env.items = vaffle#env#create_items(new_env) + call vaffle#buffer#set_env(new_env) + call vaffle#buffer#redraw() endfunction @@ -59,11 +81,10 @@ function! vaffle#open_current() abort return endif - call vaffle#env#save_cursor(item) + call vaffle#buffer#save_cursor(item) - call vaffle#file#open( - \ vaffle#env#get(), - \ [item]) + let env = vaffle#buffer#get_env() + call vaffle#file#open(env, [item]) endfunction @@ -75,23 +96,22 @@ function! vaffle#open_selected() abort return endif - call vaffle#env#save_cursor(items[0]) + call vaffle#buffer#save_cursor(items[0]) - call vaffle#file#open( - \ vaffle#env#get(), - \ items) + let env = vaffle#buffer#get_env() + call vaffle#file#open(env, items) endfunction function! vaffle#open_parent() abort call s:keep_buffer_singularity() - let env = vaffle#env#get() + let env = vaffle#buffer#get_env() let parent_dir = fnamemodify(env.dir, ':h') let cursor_items = vaffle#item#get_cursor_items('n') if !empty(cursor_items) - call vaffle#env#save_cursor(cursor_items[0]) + call vaffle#buffer#save_cursor(cursor_items[0]) endif let item = vaffle#item#create(parent_dir) @@ -130,7 +150,7 @@ endfunction function! vaffle#toggle_all() abort call s:keep_buffer_singularity() - let items = vaffle#env#get().items + let items = vaffle#buffer#get_env().items if empty(items) return endif @@ -143,7 +163,7 @@ endfunction function! vaffle#set_selected_all(selected) abort call s:keep_buffer_singularity() - for item in vaffle#env#get().items + for item in vaffle#buffer#get_env().items let item.selected = a:selected endfor @@ -155,7 +175,7 @@ function! vaffle#quit() abort call s:keep_buffer_singularity() " Try restoring previous buffer - let bufnr = vaffle#env#get().non_vaffle_bufnr + let bufnr = vaffle#buffer#get_env().non_vaffle_bufnr if bufexists(bufnr) execute printf('buffer! %d', bufnr) return @@ -194,7 +214,7 @@ function! vaffle#delete_selected() abort endif call vaffle#file#delete( - \ vaffle#env#get(), + \ vaffle#buffer#get_env(), \ items) call vaffle#refresh() endfunction @@ -219,7 +239,7 @@ function! vaffle#move_selected() abort endif call vaffle#file#move( - \ vaffle#env#get(), + \ vaffle#buffer#get_env(), \ items, dst_name) call vaffle#refresh() endfunction @@ -236,7 +256,7 @@ function! vaffle#mkdir() abort endif call vaffle#file#mkdir( - \ vaffle#env#get(), + \ vaffle#buffer#get_env(), \ name) call vaffle#refresh() endfunction @@ -253,7 +273,7 @@ function! vaffle#new_file() abort endif call vaffle#file#edit( - \ vaffle#env#get(), + \ vaffle#buffer#get_env(), \ name) endfunction @@ -277,7 +297,7 @@ function! vaffle#rename_selected() abort endif call vaffle#file#rename( - \ vaffle#env#get(), + \ vaffle#buffer#get_env(), \ items, [new_basename]) call vaffle#refresh() return @@ -290,19 +310,22 @@ endfunction function! vaffle#toggle_hidden() abort call s:keep_buffer_singularity() - call vaffle#env#set( - \ 'shows_hidden_files', - \ !vaffle#env#get().shows_hidden_files) + let env = vaffle#buffer#get_env() + let env.shows_hidden_files = !env.shows_hidden_files + call vaffle#buffer#set_env(env) let item = get( \ vaffle#item#get_cursor_items('n'), \ 0, \ {}) if !empty(item) - call vaffle#env#save_cursor(item) + call vaffle#buffer#save_cursor(item) endif - call vaffle#env#set_up_items() + let env = vaffle#buffer#get_env() + let env.items = vaffle#env#create_items(env) + call vaffle#buffer#set_env(env) + call vaffle#buffer#redraw() endfunction diff --git a/autoload/vaffle/buffer.vim b/autoload/vaffle/buffer.vim index 34b66ae..28bca70 100644 --- a/autoload/vaffle/buffer.vim +++ b/autoload/vaffle/buffer.vim @@ -38,16 +38,18 @@ endfunction function! s:generate_unique_bufname(path) abort - " Add prefix `#:` (waffle!) to avoid truncating path - " with statusline item `%t` - let bufname = fnameescape(printf('#:%s', a:path)) + let bufname = '' + let index = 0 - let index = 2 - while bufnr(bufname) >= 0 + while 1 " Add index to avoid duplicated buffer name - let bufname = fnameescape(printf('#%d:%s', + let bufname = fnameescape(printf('vaffle://%d/%s', \ index, \ a:path)) + if bufnr(bufname) < 0 + break + endif + let index += 1 endwhile @@ -55,26 +57,79 @@ function! s:generate_unique_bufname(path) abort endfunction +function! s:get_options_dict() abort + return { + \ 'bufhidden': { 'type': 'string', 'value': &bufhidden}, + \ 'buftype': { 'type': 'string', 'value': &buftype}, + \ 'matchpairs': { 'type': 'string', 'value': &matchpairs}, + \ 'swapfile': { 'type': 'bool', 'value': &swapfile}, + \ 'wrap': { 'type': 'bool', 'value': &wrap}, + \ } +endfunction + + +function! s:restore_options() abort + let options = vaffle#buffer#get_env().initial_options + for option_name in keys(options) + let option = options[option_name] + let command = (option.type ==? 'bool') + \ ? printf('setlocal %s%s', + \ (option.value ? '' : 'no'), + \ option_name) + \ : printf('setlocal %s=%s', + \ option_name, + \ option.value) + execute command + endfor +endfunction + + +function! s:perform_auto_cd_if_needed(path) abort + if !g:vaffle_auto_cd + return + endif + + try + execute printf('lcd %s', fnameescape(a:path)) + catch /:E472:/ + " E472: Command failed + " Permission denied, etc. + call vaffle#util#echo_error( + \ printf('Changing directory failed: ''%s''', a:path)) + return + endtry +endfunction + + +function! s:get_saved_cursor_lnum() abort + let env = vaffle#buffer#get_env() + let cursor_paths = env.cursor_paths + let cursor_path = get(cursor_paths, env.dir, '') + if empty(cursor_path) + return 1 + endif + + let items = filter( + \ copy(env.items), + \ 'v:val.path ==# cursor_path') + if empty(items) + return 1 + endif + + let cursor_item = items[0] + return index(env.items, cursor_item) + 1 +endfunction + + function! vaffle#buffer#init(path) abort - let prev_bufnr = bufnr('%') let path = vaffle#util#normalize_path(a:path) - " Create new `nofile` buffer to avoid unwanted sync + " Give unique name to buffer to avoid unwanted sync " between different windows - enew execute printf('silent file %s', \ s:generate_unique_bufname(path)) - let options = { - \ 'bufhidden': { 'type': 'string', 'value': &bufhidden}, - \ 'buftype': { 'type': 'string', 'value': &buftype}, - \ 'matchpairs': { 'type': 'string', 'value': &matchpairs}, - \ 'swapfile': { 'type': 'bool', 'value': &swapfile}, - \ 'wrap': { 'type': 'bool', 'value': &wrap}, - \ } - call vaffle#env#set( - \ 'initial_options', - \ options) + let initial_options = s:get_options_dict() setlocal bufhidden=wipe setlocal buftype=nowrite setlocal filetype=vaffle @@ -82,54 +137,55 @@ function! vaffle#buffer#init(path) abort setlocal noswapfile setlocal nowrap - " Delete unused directory buffer - if isdirectory(bufname(prev_bufnr)) - execute printf('bwipeout! %d', - \ prev_bufnr) - endif - - call vaffle#env#set_up(path) - call vaffle#buffer#redraw() - if g:vaffle_use_default_mappings call s:set_up_default_mappings() endif - if g:vaffle_auto_cd - try - execute printf('lcd %s', fnameescape(path)) - catch /:E472:/ - " E472: Command failed - " Permission denied, etc. - call vaffle#util#echo_error( - \ printf('Changing directory failed: ''%s''', path)) - return - endtry + let env = vaffle#env#create(path) + call vaffle#env#inherit(env, vaffle#buffer#get_env()) + + let env.initial_options = initial_options + let env.items = vaffle#env#create_items(env) + if env.non_vaffle_bufnr == bufnr('%') + " Exclude empty buffer used for Vaffle + " For example: + " :enew + " Created new empty buffer (bufnr: 2) + " Updated `non_vaffle_bufnr` (= 2) + " :Vaffle + " Used buffer (bufnr: 2) for Vaffle + " `non_vaffle_bufnr` is 2, but should not restore it + let env.non_vaffle_bufnr = -1 endif + + call vaffle#buffer#set_env(env) + + call vaffle#buffer#redraw() + + call s:perform_auto_cd_if_needed(path) +endfunction + + +function! vaffle#buffer#is_for_vaffle(bufnr) abort + let bufname = bufname(a:bufnr) + return (match(bufname, '^vaffle://\d\+/') >= 0) +endfunction + + +function! vaffle#buffer#extract_path_from_bufname(bufname) abort + let matches = matchlist(a:bufname, '^vaffle://\d\+/\(.*\)$') + return get(matches, 1, '') endfunction function! vaffle#buffer#restore_if_needed() abort - if !vaffle#env#should_restore() + if !vaffle#buffer#is_for_vaffle(bufnr('%')) return 0 endif - let options = vaffle#env#get().initial_options - for option_name in keys(options) - let option = options[option_name] - let command = (option.type ==? 'bool') - \ ? printf('setlocal %s%s', - \ (option.value ? '' : 'no'), - \ option_name) - \ : printf('setlocal %s=%s', - \ option_name, - \ option.value) - execute command - endfor + call s:restore_options() setlocal modifiable - call vaffle#env#set('restored', 1) - return 1 endfunction @@ -140,7 +196,8 @@ function! vaffle#buffer#redraw() abort " Clear buffer before drawing items silent keepjumps %d - let items = vaffle#env#get().items + let env = vaffle#buffer#get_env() + let items = env.items if !empty(items) let lnum = 1 for item in items @@ -155,11 +212,7 @@ function! vaffle#buffer#redraw() abort setlocal nomodifiable setlocal nomodified - let initial_lnum = 1 - let cursor_item = vaffle#env#restore_cursor() - if !empty(cursor_item) - let initial_lnum = index(items, cursor_item) + 1 - endif + let initial_lnum = s:get_saved_cursor_lnum() call cursor([initial_lnum, 1, 0, 1]) endfunction @@ -176,11 +229,32 @@ endfunction function! vaffle#buffer#duplicate() abort - call vaffle#env#restore_from_buffer() + " Split buffer doesn't have `w:vaffle` so restore it from `b:vaffle` + let w:vaffle = deepcopy(b:vaffle) + call vaffle#file#edit( - \ vaffle#env#get(), + \ vaffle#buffer#get_env(), \ '') endfunction +function! vaffle#buffer#get_env() abort + let w:vaffle = get(w:, 'vaffle', get(b:, 'vaffle', {})) + return w:vaffle +endfunction + + +function! vaffle#buffer#set_env(env) abort + let w:vaffle = a:env + let b:vaffle = w:vaffle +endfunction + + +function! vaffle#buffer#save_cursor(item) abort + let env = vaffle#buffer#get_env() + let env.cursor_paths[env.dir] = a:item.path + call vaffle#buffer#set_env(env) +endfunction + + let &cpo = s:save_cpo diff --git a/autoload/vaffle/env.vim b/autoload/vaffle/env.vim index 6bde0a5..13bf6b7 100644 --- a/autoload/vaffle/env.vim +++ b/autoload/vaffle/env.vim @@ -6,119 +6,58 @@ let s:save_cpo = &cpo set cpo&vim -function! vaffle#env#set_up(path) abort - let w:vaffle = get(w:, 'vaffle', {}) +function! vaffle#env#create(path) abort + let env = {} + let env.dir = vaffle#util#normalize_path(a:path) + let env.initial_options = {} + let env.cursor_paths = {} + let env.non_vaffle_bufnr = -1 + let env.shows_hidden_files = g:vaffle_show_hidden_files + let env.items = [] + return env +endfunction + - let w:vaffle.restored = 0 - let w:vaffle.dir = vaffle#util#normalize_path(a:path) - let w:vaffle.cursor_paths = get( - \ w:vaffle, +function! vaffle#env#inherit(env, old_env) abort + let a:env.cursor_paths = get( + \ a:old_env, \ 'cursor_paths', - \ {}) + \ a:env.cursor_paths) - let w:vaffle.non_vaffle_bufnr = get( - \ w:vaffle, + let a:env.non_vaffle_bufnr = get( + \ a:old_env, \ 'non_vaffle_bufnr', - \ -1) - if w:vaffle.non_vaffle_bufnr == bufnr('%') - let w:vaffle.non_vaffle_bufnr = -1 - endif + \ a:env.non_vaffle_bufnr) - let w:vaffle.shows_hidden_files = get( - \ w:vaffle, + let a:env.shows_hidden_files = get( + \ a:old_env, \ 'shows_hidden_files', - \ g:vaffle_show_hidden_files) - - call vaffle#env#set_up_items() + \ a:env.shows_hidden_files) endfunction -function! vaffle#env#set_up_items() abort - let paths = vaffle#compat#glob_list(w:vaffle.dir . '/*') - if w:vaffle.shows_hidden_files - let hidden_paths = vaffle#compat#glob_list(w:vaffle.dir . '/.*') +function! vaffle#env#create_items(env) abort + let paths = vaffle#compat#glob_list(a:env.dir . '/*') + if a:env.shows_hidden_files + let hidden_paths = vaffle#compat#glob_list(a:env.dir . '/.*') " Exclude '.' & '..' call filter(hidden_paths, 'match(v:val, ''/\.\.\?$'') < 0') call extend(paths, hidden_paths) end - let w:vaffle.items = map( + let items = map( \ copy(paths), \ 'vaffle#item#create(v:val)') - call sort(w:vaffle.items, 'vaffle#sorter#default#compare') + call sort(items, 'vaffle#sorter#default#compare') let index = 0 - for item in w:vaffle.items + for item in items let item.index = index let index += 1 endfor - let b:vaffle = w:vaffle -endfunction - - -function! vaffle#env#get() abort - let w:vaffle = get(w:, 'vaffle', get(b:, 'vaffle', {})) - return w:vaffle -endfunction - - -function! vaffle#env#set(key, value) abort - let w:vaffle = get(w:, 'vaffle', get(b:, 'vaffle', {})) - let w:vaffle[a:key] = a:value -endfunction - - -function! vaffle#env#save_cursor(item) abort - let cursor_paths = vaffle#env#get().cursor_paths - let cursor_paths[w:vaffle.dir] = a:item.path - call vaffle#env#set('cursor_paths', cursor_paths) -endfunction - - -function! vaffle#env#restore_cursor() abort - let cursor_paths = vaffle#env#get().cursor_paths - let cursor_path = get(cursor_paths, w:vaffle.dir, '') - if empty(cursor_path) - return {} - endif - - let items = filter( - \ copy(w:vaffle.items), - \ 'v:val.path ==# cursor_path') - if empty(items) - return {} - endif - - return items[0] -endfunction - - -function! vaffle#env#restore_from_buffer() abort - " Split buffer doesn't have `w:vaffle` so restore it from `b:vaffle` - let w:vaffle = deepcopy(b:vaffle) -endfunction - - -function! vaffle#env#should_restore() abort - if &filetype ==? 'vaffle' - " Active Vaffle buffer - return 0 - endif - - if !exists('w:vaffle') - " Buffer not for Vaffle - return 0 - endif - - if !exists('w:vaffle.restored') - \ || w:vaffle.restored - " Already restored - return 0 - endif - - return 1 + return items endfunction diff --git a/autoload/vaffle/event.vim b/autoload/vaffle/event.vim index af41e24..cd2412f 100644 --- a/autoload/vaffle/event.vim +++ b/autoload/vaffle/event.vim @@ -16,18 +16,18 @@ endfunction function! vaffle#event#on_bufenter() abort call s:newtralize_netrw() + let bufnr = bufnr('%') + let is_vaffle_buffer = vaffle#buffer#is_for_vaffle(bufnr) let path = expand('%:p') - let is_nofile_buffer = empty(path) - let is_normal_buffer_for_file = !empty(path) && !isdirectory(path) - if is_nofile_buffer - \ || is_normal_buffer_for_file - call vaffle#buffer#restore_if_needed() + let should_init = is_vaffle_buffer + \ || isdirectory(path) - " Store bufnr of non-vaffle buffer to restore initial state - if &filetype !=? 'vaffle' - call vaffle#env#set('non_vaffle_bufnr', bufnr('%')) - endif + " Store bufnr of non-directory buffer to back to initial buffer + if !should_init + let env = vaffle#buffer#get_env() + let env.non_vaffle_bufnr = bufnr + call vaffle#buffer#set_env(env) return endif @@ -36,4 +36,9 @@ function! vaffle#event#on_bufenter() abort endfunction +function! vaffle#event#on_bufleave() abort + call vaffle#buffer#restore_if_needed() +endfunction + + let &cpo = s:save_cpo diff --git a/autoload/vaffle/file.vim b/autoload/vaffle/file.vim index bf61dc2..d336d36 100644 --- a/autoload/vaffle/file.vim +++ b/autoload/vaffle/file.vim @@ -8,7 +8,12 @@ set cpo&vim function! vaffle#file#open(env, items) abort if len(a:items) == 1 - execute printf('edit %s', fnameescape(a:items[0].path)) + let path = a:items[0].path + if isdirectory(path) + call vaffle#init(path) + else + execute printf('edit %s', fnameescape(a:items[0].path)) + endif return endif diff --git a/autoload/vaffle/item.vim b/autoload/vaffle/item.vim index 8f015ad..eed7af2 100644 --- a/autoload/vaffle/item.vim +++ b/autoload/vaffle/item.vim @@ -7,7 +7,7 @@ set cpo&vim function! vaffle#item#get_cursor_items(mode) abort - let items = vaffle#env#get().items + let items = vaffle#buffer#get_env().items if empty(items) return [] endif @@ -23,7 +23,7 @@ endfunction function! vaffle#item#get_selected_items() abort - let items = vaffle#env#get().items + let items = vaffle#buffer#get_env().items let selected_items = filter( \ copy(items), \ 'v:val.selected') diff --git a/autoload/vaffle/rename_buffer.vim b/autoload/vaffle/rename_buffer.vim index a35ab83..ed52b56 100644 --- a/autoload/vaffle/rename_buffer.vim +++ b/autoload/vaffle/rename_buffer.vim @@ -62,7 +62,7 @@ function! vaffle#rename_buffer#new(items) abort execute printf('bwipeout %d', bufnr) endif - let parent_env = vaffle#env#get() + let parent_env = vaffle#buffer#get_env() let parent_bufnr = bufnr('%') vnew diff --git a/plugin/vaffle.vim b/plugin/vaffle.vim index f0ead38..f7c25bd 100644 --- a/plugin/vaffle.vim +++ b/plugin/vaffle.vim @@ -15,6 +15,7 @@ let g:loaded_vaffle = 1 augroup vaffle_vim autocmd! autocmd BufEnter * call vaffle#event#on_bufenter() + autocmd BufLeave * call vaffle#event#on_bufleave() augroup END