diff --git a/autoload/wiki/buffer.vim b/autoload/wiki/buffer.vim index 0bb9bbe..0c087ae 100644 --- a/autoload/wiki/buffer.vim +++ b/autoload/wiki/buffer.vim @@ -59,6 +59,7 @@ function! s:init_buffer_commands() abort " {{{1 command! -buffer WikiLinkReturn call wiki#nav#return() command! -buffer WikiLinkTransform call wiki#link#transform_current() command! -buffer WikiPageDelete call wiki#page#delete() + command! -buffer WikiPageRefile call wiki#page#refile() command! -buffer WikiPageRename call wiki#page#rename() command! -buffer WikiPageRenameSection call wiki#page#rename_section() command! -buffer WikiTocGenerate call wiki#toc#create(0) @@ -109,6 +110,7 @@ function! s:init_buffer_mappings() abort " {{{1 nnoremap (wiki-link-return) :WikiLinkReturn nnoremap (wiki-link-transform) :WikiLinkTransform nnoremap (wiki-page-delete) :WikiPageDelete + nnoremap (wiki-page-refile) :WikiPageRefile nnoremap (wiki-page-rename) :WikiPageRename nnoremap (wiki-page-rename-section) :WikiPageRenameSection nnoremap (wiki-toc-generate) :WikiTocGenerate @@ -166,6 +168,7 @@ function! s:init_buffer_mappings() abort " {{{1 \ '(wiki-link-transform)': 'wf', \ '(wiki-link-transform-operator)': 'gl', \ '(wiki-page-delete)': 'wd', + \ '(wiki-page-refile)' : 'wq', \ '(wiki-page-rename)': 'wr', \ '(wiki-page-rename-section)': '', \ '(wiki-toc-generate)': 'wt', diff --git a/autoload/wiki/page.vim b/autoload/wiki/page.vim index 1fe2fd3..b7010f3 100644 --- a/autoload/wiki/page.vim +++ b/autoload/wiki/page.vim @@ -169,6 +169,84 @@ function! wiki#page#rename_section(...) abort "{{{1 call s:update_links(l:old_anchor, l:new_anchor, 'rename_section') endfunction +" }}}1 +function! wiki#page#refile(...) abort "{{{1 + " This function refiles a page section from one page to another. + " + " Input: An optional dictionary with the following keys: + " target_page: The target page name + " target_lnum: The line number in target page where to refile + + " Parse options + let l:opts = extend(#{ + \ target_page: '', + \ target_lnum: 0, + \}, a:0 > 0 ? a:1 : {}) + if empty(l:opts.target_page) + let l:opts.target_page = input('> ', '', 'customlist,wiki#complete#link') + endif + + let l:resolved_page = wiki#url#resolve(l:opts.target_page) + if !filereadable(l:resolved_page.path) + echo wiki#log#warn(l:resolved_page) + return wiki#log#error('Target page was not found!') + endif + + " Get the relevant sections and start/end line numbers + let l:sections = wiki#toc#gather_entries() + let l:idx = indexof(l:sections, { _, x -> x.lnum > line('.') }) - 1 + if l:idx < 0 + return wiki#log#error('No current section recognized!') + endif + + let l:sections = l:sections[l:idx:] + let l:sec = l:sections[0] + let l:lnum_start = l:sec.lnum + let l:lnum_end = line('$') + if len(l:sections) > 1 + let l:idx = indexof(l:sections[1:], { _, x -> x.level <= l:sec.level }) + if l:idx >= 0 + let l:sections = l:sections[:l:idx + 1] + let l:lnum_end = l:sections[1].lnum - 1 + endif + endif + + call wiki#log#info( + \ printf('Refiling section "%s" to page "%s"', + \ l:sec.header, l:opts.target_page)) + + " Get the lines of the section to refile + let l:lines = getline(l:lnum_start, l:lnum_end) + + " Append lines to target page + let l:current_bufnr = bufnr('') + silent execute 'edit' fnameescape(l:resolved_page.path) + call append(l:opts.target_lnum, l:lines) + silent update + execute 'buffer' l:current_bufnr + + " Delete lines from source page + call deletebufline('', l:lnum_start, l:lnum_end) + + " Update local links + let l:pos = getcurpos() + keepjumps execute printf( + \ '%%s/\V\ze%s/%s/e%s', + \ l:sec.anchor, + \ wiki#paths#to_wiki_url(l:resolved_page.path, wiki#get_root()), + \ &gdefault ? '' : 'g') + call cursor(l:pos[1:]) + silent update + + " Update links + " * It is enough to update the "outer" anchor because the more specific + " anchors should be covered by these as well. + call s:update_links( + \ #{anchor: l:sec.anchor, path: expand('%:p')}, + \ #{anchor: l:sec.anchor, path: l:resolved_page.path}, + \ 'refile') +endfunction + " }}}1 function! wiki#page#export(line1, line2, ...) abort " {{{1 let l:cfg = deepcopy(g:wiki_export) @@ -381,6 +459,35 @@ function! s:update_link_rename_section(anchor_old, anchor_new) abort "{{{1 return [len(l:all_links), len(l:files_with_links)] endfunction +" }}}1 +function! s:update_link_refile(old, new) abort "{{{1 + let l:replacement_patterns + \ = s:get_replacement_patterns(a:old.path, a:new.path) + + let l:graph = wiki#graph#builder#get() + let l:all_links = filter( + \ l:graph.get_links_to(a:old.path), + \ { _, x -> x.anchor =~# '^' . a:old.anchor }) + let l:files_with_links = wiki#u#group_by(l:all_links, 'filename_from') + for [l:file, l:file_links] in items(l:files_with_links) + let l:lines = readfile(l:file) + + for l:link in l:file_links + for [l:pattern, l:replace] in l:replacement_patterns + let l:lines[l:link.lnum - 1] = substitute( + \ l:lines[l:link.lnum - 1], + \ l:pattern, + \ l:replace, + \ 'g') + endfor + endfor + + call writefile(l:lines, l:file, 's') + endfor + + return [len(l:all_links), len(l:files_with_links)] +endfunction + " }}}1 function! s:get_replacement_patterns(path_old, path_new) abort " {{{1 " Update "absolute" links (i.e. assume link is rooted)