2

I'm using VimWiki for a writing project and for certain sections (like "chapters", contained in sub-directories) I want to create indexes that work similarly to the way VimWiki's diary index works. Unfortunately in reading through the plugin's source I haven't been able to understand how the VimwikiDiaryGenerateLinks function works in order to replicate it for something similar.

My exact goal is this:
In my vimwiki section index page I want to automatically create links to files that match a pattern...

for all files like:

~/Documents/wiki/subdir1/string_xyz123.mkd
~/Documents/wiki/subdir1/string_xyz456.mkd
~/Documents/wiki/subdir1/string_xyz789.mkd

I want to create a list of links in vimwiki markdown like this:

- [xyz123](string_xyz123.mkd)
- [xyz456](string_xyz456.mkd)
- [xyz789](string_xyz789.mkd)

Sometimes I'll create new string_xyz789.mkd pages from someplace other than this index file, so I want this index file to keep itself updated automatically (the way VimWiki's "diary" index file automatically lists all the diary entries that exist).

The index page as a whole will be structured somewhat like this:

# Page Title

Some various content... perhaps multiple paragraphs...

## Automatically generated list of links
- [xyz123](string_xyz123.mkd)
- [xyz456](string_xyz456.mkd)
- [xyz789](string_xyz789.mkd)

Other content that I type manually.

...and only the auto-generated list of links should be updated. I need to be able to type content before and after the list as if I'm just editing a normal wiki page.

Does VimWiki provide a solution for this?

If not could you suggest how to approach scripting this myself?

1
  • 1
    This auto-generated list of links is like a very-simplistic instance of 'viewport' concept provided by the taskwiki plugin github.com/tools-life/taskwiki#viewports. Instead of showing tasks, I'm trying to just show files without all the fancy task-related features.
    – alec
    Commented Dec 21, 2020 at 17:12

1 Answer 1

2

I haven't had time to test more than the first code-block, but the others are mostly mechanical transformations.

function MyLinks(pat, strip) abort
  return glob(a:pat, v:false, v:true)
        \ ->map({_, v -> printf('[%s](%s)', fnamemodify(v, ':t:r')->substitute(printf('^%s', a:strip), '', ''), fnamemodify(v, ':t'))})
        \ ->join("\n")
endfunction

Extracting formatting functions:

function MakeLink(text, filename) abort
  return printf('[%s](%s)', a:text, a:filename)
endfunction

function Basename(filename) abort
  return fnamemodify(a:filename, ':t')
endfunction

function NoExt(filename) abort
  return fnamemodify(a:filename, ':r')
endfunction

function MyLinks(pat, strip) abort
  return glob(a:pat, v:false, v:true)
        \ ->map({_, v -> MakeLink(NoExt(Basename(v))->substitute(printf('^%s', a:strip), '', ''), Basename(v))})
        \ ->join("\n")
endfunction

You could avoid the lambda by doing

function MyLinks(pat, strip) abort
  return glob(a:pat, v:false, v:true)
        \ ->map(printf('MakeLink(NoExt(Basename(v:val))->substitute("^%s", "", ""), Basename(v:val))', a:strip))
        \ ->join("\n")
endfunction

(and see :help method for how to get rid of all the other ->s in older vim's; in most cases, you substitute the left expression as the first argument in the right function call, giving

function MyLinks(pat, strip) abort
  return join(
        \ map(
        \   glob(a:pat, v:false, v:true),
        \   printf('MakeLink(substitute(NoExt(Basename(v:val)), "^%s", "", ""), Basename(v:val))', a:strip),
        \ "\n")
endfunction

)

You supply pat (a glob, see :help glob()) and strip (a pattern to strip, :help substitute()). An example:

:put =MyLinks($HOME.'/Documents/wiki/subdir1/string_xyz*.mkd', 'string_')

How? We

  1. glob the pattern as a list (change v:false to v:true depending on how you feel about 'suffixes' and 'wildignore')
  2. map the results into a link format using fnamemodify and substitute
  3. join the list with newlines to make it putable text (this last step is unnecessary for put = or append(); I almost just left it as list of [k, v] pairs)

If needed you could modify to give more substitute parameters for more control

function MyLinks(pat, spat, ssub, sflags) abort
  return glob(a:pat, v:false, v:true)
        \ ->map({_, v -> printf('[%s](%s)', fnamemodify(v, ':t:r')->substitute(a:spat, a:ssub, a:sflags), fnamemodify(v, ':t'))})
        \ ->join("\n")
endfunction

Up to you to make any necessary transformations, for now, as it takes me a bit of work.


For automatically updating it, you could try something like

augroup UpdateThing
  autocmd!
  autocmd BufEnter my-index /## Automatically generated list of links/+;'}- delete
        \ | put! =MyLinks('...', '...')
augroup END

How?

On BufEnter of my-index, we delete from the line below ## Automatically generated list of links to the end of the paragraph and put our list of links.

(The original version used two commands to delete: the first /…/+ moved to the line after the header, while the second .,'}- delete deleted from that line to the line before the paragraph break. The newer version uses ; to separate addresses rather than ,—the difference is documented at :help :;. Effectively, it allows the second address to start relative to the first.)

1
  • Comments are not for extended discussion; this conversation has been moved to chat.
    – D. Ben Knoble
    Commented Dec 24, 2020 at 21:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.