Skip to content
Open
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
3 changes: 3 additions & 0 deletions autoload/wiki/buffer.vim
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function! s:init_buffer_commands() abort " {{{1
command! -buffer WikiLinkIncomingToggle call wiki#link#incoming_display_toggle()
command! -buffer WikiLinkIncomingHover call wiki#link#incoming_hover()
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 WikiToc call g:wiki_select_method.toc()
Expand Down Expand Up @@ -113,6 +114,7 @@ function! s:init_buffer_mappings() abort " {{{1
nnoremap <silent><buffer> <plug>(wiki-link-incoming-toggle) :WikiLinkIncomingToggle<cr>
nnoremap <silent><buffer> <plug>(wiki-link-incoming-hover) :WikiLinkIncomingHover<cr>
nnoremap <silent><buffer> <plug>(wiki-page-delete) :WikiPageDelete<cr>
nnoremap <silent><buffer> <plug>(wiki-page-refile) :WikiPageRefile<cr>
nnoremap <silent><buffer> <plug>(wiki-page-rename) :WikiPageRename<cr>
nnoremap <silent><buffer> <plug>(wiki-page-rename-section) :WikiPageRenameSection<cr>
nnoremap <silent><buffer> <plug>(wiki-toc-generate) :WikiTocGenerate<cr>
Expand Down Expand Up @@ -175,6 +177,7 @@ function! s:init_buffer_mappings() abort " {{{1
\ '<plug>(wiki-link-incoming-toggle)': '<leader>wli',
\ '<plug>(wiki-link-incoming-hover)': '<leader>wlI',
\ '<plug>(wiki-page-delete)': '<leader>wd',
\ '<plug>(wiki-page-refile)' : '<leader>wq',
\ '<plug>(wiki-page-rename)': '<leader>wr',
\ '<plug>(wiki-page-rename-section)': '<f2>',
\ '<plug>(wiki-toc-generate)': '<leader>wt',
Expand Down
45 changes: 42 additions & 3 deletions autoload/wiki/page.vim
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,39 @@ function! wiki#page#rename_section(...) abort "{{{1
call s:update_links_external(l:source, l:target)
endfunction

" }}}1
function! wiki#page#refile(...) abort "{{{1
let l:opts = extend(#{
\ target_page: '',
\ target_lnum: 0,
\}, a:0 > 0 ? a:1 : {})

" target_page could be given by user input with something like this:
" let l:opts.target_page = input('> ', '', 'customlist,wiki#complete#url')

let l:source = wiki#page#refile#collect_source(l:opts)
if empty(l:source)
return wiki#log#error('No source section recognized!')
endif

try
let l:target = wiki#page#refile#collect_target(l:opts, l:source)
catch /wiki.vim: target page not found/
return wiki#log#error('Target page was not found!')
catch /wiki.vim: anchor not recognized/
return wiki#log#error('Target anchor not recognized!')
endtry

call wiki#log#info(
\ printf('Moving section "%s" into "%s"',
\ l:source.header, wiki#paths#to_node(l:target.path)))

call wiki#page#refile#move(l:source, l:target)

call s:update_links_local(l:source, l:target)
call s:update_links_external(l:source, l:target)
endfunction

" }}}1
function! wiki#page#export(line1, line2, ...) abort " {{{1
let l:cfg = deepcopy(g:wiki_export)
Expand Down Expand Up @@ -287,14 +320,20 @@ endfunction
" }}}1
function! s:update_links_local(old, new) abort "{{{1
" Arguments:
" old: dict(anchor)
" new: dict(anchor)
" old: dict(anchor, path?)
" new: dict(anchor, path?)
let l:pos = getcurpos()

let l:anchor = !has_key(a:old, 'path') || a:old.path ==# a:new.path
\ ? a:new.anchor
\ : wiki#paths#to_wiki_url(a:new.path) . a:new.anchor

keeppattern keepjumps execute printf('%%s/\V%s/%s/e%s',
\ a:old.anchor,
\ a:new.anchor,
\ l:anchor,
\ &gdefault ? '' : 'g')
silent update

call cursor(l:pos[1:])
endfunction

Expand Down
133 changes: 133 additions & 0 deletions autoload/wiki/page/refile.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
" A wiki plugin for Vim
"
" Maintainer: Karl Yngve Lervåg
" Email: karl.yngve@gmail.com
"

function! wiki#page#refile#collect_source(...) abort " {{{1
" Returns:
" source: dict(path, lnum, lnum_end, header, anchor, level)

let l:source = wiki#toc#get_section()
if !empty(l:source)
let l:source.path = expand('%:p')
endif

return l:source
endfunction

" }}}1
function! wiki#page#refile#collect_target(opts, source) abort " {{{1
" Arguments:
" opts: dict(
" target_page,
" target_lnum,
" target_anchor_before?,
" )
" source: dict(path, lnum, lnum_end, header, anchor)
" Returns:
" target: dict(path, lnum, anchor, level)

let l:path = wiki#u#eval_filename(a:opts.target_page)
if !filereadable(l:path)
throw 'wiki.vim: target page not found'
endif


if has_key(a:opts, 'target_anchor_before')
return s:collect_target_by_anchor_before(
\ l:path,
\ a:opts.target_anchor_before,
\ a:source
\)
endif

return s:collect_target_by_lnum(l:path, a:opts.target_lnum, a:source)
endfunction

" }}}1
function! wiki#page#refile#move(source, target) abort " {{{1
" Arguments:
" source: dict(path, lnum, lnum_end)
" target: dict(path, lnum)

if a:target.level < a:source.level
call execute(printf('%d,%dg/^#/normal! 0%dx',
\ a:source.lnum, a:source.lnum_end, a:source.level - a:target.level))
elseif a:target.level > a:source.level
call execute(printf('%d,%dg/^#/normal! 0%di#',
\ a:source.lnum, a:source.lnum_end, a:target.level - a:source.level))
endif

if a:target.path ==# a:source.path
call execute(printf('%d,%dm %d',
\ a:source.lnum, a:source.lnum_end, a:target.lnum))
silent write
else
let l:lines = getline(a:source.lnum, a:source.lnum_end)
call deletebufline('', a:source.lnum, a:source.lnum_end)
silent write

let l:current_bufnr = bufnr('')
let l:was_loaded = bufloaded(a:target.path)
keepalt execute 'silent edit' fnameescape(a:target.path)
call append(a:target.lnum, l:lines)
silent write
if !l:was_loaded
keepalt execute 'bwipeout'
endif
keepalt execute 'buffer' l:current_bufnr
endif
endfunction

" }}}1

function! s:collect_target_by_anchor_before(path, anchor, source) abort " {{{1
let l:section = {}
for l:section in wiki#toc#gather_entries(#{ path: a:path })
if l:section.anchor ==# a:anchor
break
endif
endfor

if empty(l:section)
throw 'wiki.vim: anchor not recognized'
endif

let l:anchors = get(l:section, 'anchors', [])
if len(l:anchors) > 0
call remove(l:anchors, -1)
endif
let l:anchors += [a:source.anchors[-1]]

return #{
\ path: a:path,
\ lnum: l:section.lnum - 1,
\ anchor: '#' . join(l:anchors, '#'),
\ level: len(l:anchors)
\}
endfunction

" }}}1
function! s:collect_target_by_lnum(path, lnum, source) abort " {{{1
let l:anchors = [a:source.anchors[-1]]

if a:source.level > 1
let l:section = wiki#toc#get_section(#{ path: a:path, at_lnum: a:lnum })
let l:target_anchors = get(l:section, 'anchors', [])
call extend(
\ l:anchors,
\ l:target_anchors[:a:source.level - 2],
\ 0
\)
endif

return #{
\ path: a:path,
\ lnum: a:lnum,
\ anchor: '#' . join(l:anchors, '#'),
\ level: len(l:anchors)
\}
endfunction

" }}}1
65 changes: 45 additions & 20 deletions autoload/wiki/test.vim
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,9 @@ function! wiki#test#finished() abort " {{{1
let l:msg = l:match[3]

if l:msg =~# 'Expected .*but got'
echo printf("%s:%d\n", l:file, l:lnum)

let l:intro = matchstr(l:msg, '.\{-}\ze\s*\(: \)\?Expected ')
if !empty(l:intro)
echo printf(" %s\n", l:intro)
endif

let l:expect = matchstr(l:msg, 'Expected \zs.*\zebut got')
let l:observe = matchstr(l:msg, 'Expected .*but got \zs.*')
echo printf(" Expected: %s\n", l:expect)
echo printf(" Observed: %s\n\n", l:observe)
call s:print_expected_but_got(l:file, l:lnum, l:msg)
elseif l:msg =~# 'Pattern.*does\( not\)\? match'
echo printf("%s:%d\n", l:file, l:lnum)

let l:intro = matchstr(l:msg, '.\{-}\ze\s*\(: \)\?Pattern ')
if !empty(l:intro)
echo printf(" %s\n", l:intro)
endif

let l:expect = matchstr(l:msg, 'Pattern.*does\( not\)\? match.*')
echo printf(" %s\n", l:expect)
call s:print_pattern_does_not_match(l:file, l:lnum, l:msg)
else
echo printf("%s:%d: %s\n", l:file, l:lnum, l:msg)
endif
Expand Down Expand Up @@ -63,3 +45,46 @@ function! wiki#test#completion(context, ...) abort " {{{1
endfunction

" }}}1

function! s:print_expected_but_got(file, lnum, msg) abort " {{{1
echo printf("%s:%d\n", a:file, a:lnum)

let l:intro = matchstr(a:msg, '.\{-}\ze\s*\(: \)\?Expected ')
if !empty(l:intro)
echo printf(" %s\n", l:intro)
endif

call s:print_msg_with_title(
\ 'Expected', matchstr(a:msg, 'Expected \zs.*\zebut got'))
call s:print_msg_with_title(
\ 'Observed', matchstr(a:msg, 'Expected .*but got \zs.*'))

echo ''
endfunction

" }}}1
function! s:print_pattern_does_not_match(file, lnum, msg) abort " {{{1
echo printf("%s:%d\n", a:file, a:lnum)

let l:intro = matchstr(a:msg, '.\{-}\ze\s*\(: \)\?Pattern ')
if !empty(l:intro)
echo printf(" %s\n", l:intro)
endif

let l:expect = matchstr(a:msg, 'Pattern.*does\( not\)\? match.*')
echo printf(" %s\n", l:expect)
endfunction

" }}}1
function! s:print_msg_with_title(title, msg) abort " {{{1
if a:msg[0] ==# '['
echo printf(" %s:", a:title)
for l:line in json_decode(substitute(escape(a:msg, '"'), "'", '"', 'g'))
echo ' |' .. l:line
endfor
else
echo printf(" %s: %s\n", a:title, a:msg)
endif
endfunction

" }}}1
14 changes: 14 additions & 0 deletions test/test-refile/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
MYVIM ?= nvim --clean --headless
export QUIT = 1

tests := $(wildcard test*.vim)

.PHONY: cleanup $(tests)

test: $(tests)

$(tests):
@rm -rf wiki-tmp
@cp -r wiki wiki-tmp
@$(MYVIM) -u $@
@rm -rf wiki-tmp
24 changes: 24 additions & 0 deletions test/test-refile/test-bad-input.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
source ../init.vim
runtime plugin/wiki.vim

let g:wiki_log_verbose = 0

silent edit wiki-tmp/index.wiki

" Refile something not within a section should return with error message
normal 2G
silent call wiki#page#refile()
let s:log = wiki#log#get()
call assert_equal(1, len(s:log))
call assert_equal('error', s:log[0].type)
call assert_equal('No source section recognized!', s:log[0].msg[0])

" Refile to nonexisting target should return with error message
normal! 13G
silent call wiki#page#refile(#{target_page: 'targetDoesNotExist'})
let s:log = wiki#log#get()
call assert_equal(2, len(s:log))
call assert_equal('error', s:log[1].type)
call assert_equal('Target page was not found!', s:log[1].msg[0])

call wiki#test#finished()
11 changes: 11 additions & 0 deletions test/test-refile/test-by-anchor-before-1.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
source ../init.vim
runtime plugin/wiki.vim

silent edit wiki-tmp/index.wiki
normal! 13G
silent call wiki#page#refile(#{target_anchor_before: '#Intro'})
call assert_equal(
\ readfile('wiki-tmp/ref-by-anchor-before-1.wiki'),
\ readfile('wiki-tmp/index.wiki'))

call wiki#test#finished()
14 changes: 14 additions & 0 deletions test/test-refile/test-by-anchor-before-2.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
source ../init.vim
runtime plugin/wiki.vim

silent edit wiki-tmp/source-2.wiki
normal! 8G
silent call wiki#page#refile(#{
\ target_page: 'target-2',
\ target_anchor_before: '#First#Foo'
\})
call assert_equal(
\ readfile('wiki-tmp/ref-by-anchor-before-2.wiki'),
\ readfile('wiki-tmp/target-2.wiki'))

call wiki#test#finished()
18 changes: 18 additions & 0 deletions test/test-refile/test-by-lnum-1.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
source ../init.vim
runtime plugin/wiki.vim

silent edit wiki-tmp/source-1.wiki
normal! 10G
silent call wiki#page#refile(#{target_lnum: 20})

" Check that content was properly removed from index and moved to targetA
call assert_equal(
\ readfile('wiki-tmp/ref-same-file.wiki'),
\ readfile('wiki-tmp/source-1.wiki'))

" Check that all links to the previous location are updated
call assert_equal(
\ '[[source-1#Tasks#Bar#Subheading]]',
\ readfile('wiki-tmp/links.wiki')[10])

call wiki#test#finished()
Loading