From 428c60a45e7a5fbf874d5f5f7e6c16cb95b10a62 Mon Sep 17 00:00:00 2001 From: Tinmarino Date: Sun, 2 Aug 2020 23:10:44 -0400 Subject: [PATCH] Feature: Markdown link: VimwikiTOC supports multiple heading with same name (Issue: #968, 666, #664) Anchor can be suffixed with -1 -2 according to the heading number (in file) user want to reach. Implemented with a dictionary for caching anchor names (without sufix) --- autoload/vimwiki/base.vim | 99 +++++++++++++++++++++++++++++++-------- test/command_toc.vader | 58 ++++++++++++++++++----- 2 files changed, 124 insertions(+), 33 deletions(-) diff --git a/autoload/vimwiki/base.vim b/autoload/vimwiki/base.vim index 76566c7..cd99a0a 100644 --- a/autoload/vimwiki/base.vim +++ b/autoload/vimwiki/base.vim @@ -692,16 +692,28 @@ endfunction " Called: normalize and unnormalize anchor function! s:get_punctuaction_regex() abort " From: https://gist.github.com/asabaylus/3071099#gistcomment-2563127 - return '[^0-9a-zA-Z\u4e00-\u9fff_ \-]' + if v:version <= 703 + " Retrocompatibility: Get invalid range for vim 7.03 + return '[^0-9a-zA-Z_ \-]' + else + return '[^0-9a-zA-Z\u4e00-\u9fff_ \-]' + endif endfunction " :param: anchor <= Heading line +" :param: (1) previous_anchors of previous normalized anchor +" -- to know if must append -2, updated on the fly " Return: anchor => link in TOC -function! s:normalize_anchor(anchor) abort +function! s:normalize_anchor(anchor, ...) abort " Note: See unormalize " 0 Get in let anchor = a:anchor + if a:0 + let previous_anchors = a:1 + else + let previous_anchors = {} + endif " A Trim space let anchor = vimwiki#u#trim(anchor) @@ -717,14 +729,25 @@ function! s:normalize_anchor(anchor) abort " 3 Change any space to a hyphen let anchor = substitute(anchor, ' ', '-', 'g') - " 4.2 TODO anchor: If that is not unique, add '-1', '-2', '-3',... to make it unique + " 4 Append '-1', '-2', '-3',... to make it unique <= If that not unique + if has_key(previous_anchors, anchor) + " Inc anchor number (before modifing the anchor) + let anchor_nb = previous_anchors[anchor] + 1 + let previous_anchors[anchor] = anchor_nb + " Append suffix + let anchor .= '-' . string(anchor_nb) + else + " Save anchor in dic + let previous_anchors[anchor] = 1 + endif return anchor endfunction " :param: anchor <= link -" Return: anchor to look for +" Return: [anchor_re , anchor_nb ] to look for +" -- Ex: ['toto", 2] => search for the second occurrence of toto function! s:unnormalize_anchor(anchor) abort " Note: " -- Pandoc keep the '_' in anchor @@ -736,26 +759,32 @@ function! s:unnormalize_anchor(anchor) abort " 4 Add '-1', '-2', '-3',... to make it unique if not unique " -- Save the trailing -12 - let sufix = substitute(anchor, '^.*-\(\d\+\)$', '\1', '') - if sufix !=# '' - let sufix = '[ \-]' . sufix + let anchor_nb = substitute(anchor, '^.*-\(\d\+\)$', '\1', '') + if anchor_nb ==# '' || anchor_nb == 0 + " No Sufix: number = 1 + let sufix = '' + let anchor_nb = 1 + else + " Yes Sufix: number <- read suffix + let sufix = '[ \-]' . anchor_nb + let anchor_nb = str2nr(anchor_nb) endif " -- Remove it let anchor = substitute(anchor, '\(-\d\+\)$', '', '') " For each char - let anchor_r = '' + let anchor_loop = '' for char in split(anchor, '\zs') " 3 Change any space to a hyphen if char ==# '-' - let anchor_r .= '[ \-]' + let anchor_loop .= '[ \-]' " 2 Remove anything that is not a letter, number, CJK character, hyphen or space " -- So add puncutation regex at each char else - let anchor_r .= char . punctuation_rx + let anchor_loop .= char . punctuation_rx endif endfor - let anchor = punctuation_rx . anchor_r + let anchor = punctuation_rx . anchor_loop " 1 Downcase the string let anchor = '\c' . anchor @@ -763,7 +792,7 @@ function! s:unnormalize_anchor(anchor) abort " 4.bis Add the optional suffix let anchor = anchor . '\(' . sufix . '\)\?' - return anchor + return [anchor, anchor_nb] endfunction @@ -771,32 +800,60 @@ endfunction " Called: edit_file " TODO treat the sufix: -2 -> Go to second anchor function! s:jump_to_anchor(anchor) abort + " Save cursor %% Initialize at top of line let oldpos = getpos('.') call cursor(1, 1) + " Get segments <= anchor let anchor = vimwiki#u#escape(a:anchor) - let segments = split(anchor, '#', 0) + " For markdown: there is only one segment for segment in segments " Craft segment pattern so that it is case insensitive and also matches dashes " in anchor link with spaces in heading - let segment = s:unnormalize_anchor(segment) + let [segment_re, segment_nb] = s:unnormalize_anchor(segment) let anchor_header = s:safesubstitute( \ vimwiki#vars#get_syntaxlocal('header_match'), - \ '__Header__', segment, '') + \ '__Header__', segment_re, '') let anchor_bold = s:safesubstitute( \ vimwiki#vars#get_syntaxlocal('bold_match'), - \ '__Text__', segment, '') + \ '__Text__', segment_re, '') let anchor_tag = s:safesubstitute( \ vimwiki#vars#get_syntaxlocal('tag_match'), - \ '__Tag__', segment, '') + \ '__Tag__', segment_re, '') - if !search(anchor_tag, 'Wc') && !search(anchor_header, 'Wc') && !search(anchor_bold, 'Wc') - call setpos('.', oldpos) + " Go: Move cursor: maybe more than onces (see markdown suffix) + let success_nb = 0 + let fail = 0 + for i in range(segment_nb) + " Search + let pos = 0 + let pos = pos != 0 ? pos : search(anchor_tag, 'Wc') + let pos = pos != 0 ? pos : search(anchor_header, 'Wc') + let pos = pos != 0 ? pos : search(anchor_bold, 'Wc') + + " Get the result and reloop or leave + if pos != 0 + " Avance, one line more to not rematch the same pattern if not last segment_nb + if success_nb < segment_nb-1 | let pos += 1 | endif + call cursor(pos, 1) + let success_nb += 1 + else + " Next segment (default syntax) + call setpos('.', oldpos) + let fail = 1 + break + endif + endfor + + " Check if happy + if success_nb == segment_nb || fail == 1 break endif + + " Or keep on (i.e more than once segment) let oldpos = getpos('.') endfor endfunction @@ -2296,6 +2353,8 @@ function! vimwiki#base#table_of_contents(create) abort let startindent = repeat(' ', vimwiki#lst#get_list_margin()) let indentstring = repeat(' ', vimwiki#u#sw()) let bullet = vimwiki#lst#default_symbol().' ' + " Keep previous anchor => if redundant => add suffix -2 + let previous_anchors = {} for [lvl, anchor, desc] in complete_header_infos if vimwiki#vars#get_wikilocal('syntax') ==# 'markdown' let link_tpl = vimwiki#vars#get_syntaxlocal('Weblink2Template') @@ -2306,7 +2365,7 @@ function! vimwiki#base#table_of_contents(create) abort endif " Normalize anchor - let anchor = s:normalize_anchor(anchor) + let anchor = s:normalize_anchor(anchor, previous_anchors) " Insert link in template let link = s:safesubstitute(link_tpl, '__LinkUrl__', diff --git a/test/command_toc.vader b/test/command_toc.vader index 5075325..0d29006 100644 --- a/test/command_toc.vader +++ b/test/command_toc.vader @@ -1,5 +1,6 @@ # VimwikiTOC {{{1 # +# TODO implement: If link in the heading (see README.md) # TODO (10min) test if g:vimwiki_to_header well readen # TODO (10min) test vimviki_toc_link_format # TODO (1h) test if really wiki dependant (for 2 diffrent wikis) @@ -10,21 +11,52 @@ # -- 3. changes any space to a hyphen => OK: from previous big # -- 4. If that is not unique, add "-1", "-2", "-3",... to make it unique => TODO not implemented # +# +# TODO if link in heading +#Given vimwiki (Two same heading {{{1): +# # Pre [link](anything no parenthesis) Post +# +#Execute (Set syntax markdown && Set sw=8): +# call SetSyntax('markdown') +# set sw=8 +# VimwikiTOC +# +#Expect (Suffix -1 and -2): +# +# +# +# # Start {{{1 -" TODO -"Given vimwiki (Two same heading {{{1): -" # One -" ## two -" ## Two -" -"Execute (Set syntax markdown && Set sw=8): -" call SetSyntax('markdown') -" set sw=8 -" VimwikiTOC -" -"Expect (Suffix -1 and -2): -" + + +Given vimwiki (Two same heading (#968) {{{1): + # One + toto + # ONE + like + ## oNe + you + +Execute (Set syntax markdown && Set sw=8): + call SetSyntax('markdown') + set sw=8 + VimwikiTOC + +Expect (Suffix -2 and -3): + # Contents + + - [One](#one) + - [ONE](#one-2) + - [oNe](#one-3) + + # One + toto + # ONE + like + ## oNe + you + Given vimwiki (Heading with many bad caracters {{{1): # One !@#@#(!%#&$^(!@