| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- " vim: set fdm=marker et ts=4 sw=4 sts=4:
- " functions for header navigation and information retrieval.
- function! markdown#headers#CheckValidHeader(lnum) abort "{{{1
- if a:lnum != 0
- if exists('g:vim_pandoc_syntax_exists')
- let synId = synIDattr(synID(a:lnum, 1, 1), 'name')
- if synId !~# '\v^(pandoc|pdc)' || synId ==# 'pandocDelimitedCodeBlock'
- return 0
- endif
- endif
- if match(getline(a:lnum), '^#') >= 0 || match(getline(a:lnum+1), '^[-=]') >= 0
- return 1
- endif
- endif
- return 0
- endfunction
- function! markdown#headers#NextHeader(...) abort "{{{1
- let origin_pos = getpos('.')
- if a:0 > 0
- let search_from = [0, a:1, 1, 0]
- else
- let search_from = getpos('.')
- endif
- call cursor(search_from[1], 2)
- let h_lnum = search('\(^.*\n[-=]\{2\}\|^#\)','nW')
- if h_lnum != 0 && markdown#headers#CheckValidHeader(h_lnum) != 1
- let h_lnum = markdown#headers#NextHeader(h_lnum)
- endif
- if h_lnum == 0
- if match(getline('.'), '^#') >= 0 || match(getline(line('.')+1), '^[-=]') >= 0
- let h_lnum = line('.')
- endif
- endif
- call cursor(origin_pos[1], origin_pos[2])
- return h_lnum
- endfunction
- function! markdown#headers#PrevHeader(...) abort "{{{1
- let origin_pos = getpos('.')
- if a:0 > 0
- let search_from = [0, a:1, 1, 0]
- else
- let search_from = origin_pos
- endif
- call cursor(search_from[1], 1)
- let h_lnum = search('\(^.*\n[-=]\{2\}\|^#\)', 'bnW')
- if h_lnum != 0 && markdown#headers#CheckValidHeader(h_lnum) != 1
- let h_lnum = markdown#headers#PrevHeader(h_lnum)
- " we might go back into the YAML frontmatter, we must recheck if we
- " are fine
- if markdown#headers#CheckValidHeader(h_lnum) != 1
- let h_lnum = 0
- endif
- endif
- if h_lnum == 0
- if match(getline('.'), '^#') >= 0 || match(getline(line('.')+1), '^[-=]') >= 0
- let h_lnum = line('.')
- endif
- endif
- call cursor(origin_pos[1], origin_pos[2])
- return h_lnum
- endfunction
- function! markdown#headers#ForwardHeader(count) abort "{{{1
- let lnum = line('.')
- for i in range(a:count)
- let lnum = markdown#headers#NextHeader(lnum)
- endfor
- return lnum
- endfunction
- function! markdown#headers#BackwardHeader(count) abort "{{{1
- let lnum = line('.')
- for i in range(a:count)
- let lnum = markdown#headers#PrevHeader(lnum)
- endfor
- return lnum
- endfunction
- function! markdown#headers#CurrentHeader(...) abort "{{{1
- if a:0 > 0
- let search_from = [0, a:1, 1, 0]
- else
- let search_from = getpos('.')
- endif
- " same as PrevHeader(), except don't search if we are already at a header
- if match(getline(search_from[1]), '^#') < 0 && match(getline(search_from[1]+1), '^[-=]') < 0
- return markdown#headers#PrevHeader(search_from[1])
- else
- return search_from[1]
- endif
- endfunction
- function! markdown#headers#CurrentHeaderParent(...) abort "{{{1
- let origin_pos = getpos('.')
- if a:0 > 0
- let search_from = [0, a:1, 1, 0]
- else
- let search_from = origin_pos
- endif
- let ch_lnum = markdown#headers#CurrentHeader(search_from[1])
- call cursor(ch_lnum, 1)
- let l = getline('.')
- if match(l, '^#') > -1
- let parent_level = len(matchstr(l, '#*')) - 1
- elseif match(getline(line('.')+1), '^-') > -1
- let parent_level = 1
- else
- let parent_level = 0
- endif
- " don't go further than level 1 headers
- if parent_level > 0
- if parent_level == 1
- let setext_regex = "^.*\\n="
- else
- let setext_regex = "^.*\\n[-=]"
- endif
- let arrival_lnum = search('\('.setext_regex.'\|^#\{1,'.parent_level.'}\s\)', 'bnW')
- if markdown#headers#CheckValidHeader(arrival_lnum) != 1
- let arrival_lnum = search('\('.setext_regex.'\|^#\{1,'.parent_level.'}\s\)', 'bnW')
- if markdown#headers#CheckValidHeader(arrival_lnum) != 1
- let arrival_lnum = 0
- endif
- endif
- else
- let arrival_lnum = 0
- endif
- call cursor(origin_pos[1], origin_pos[2])
- return arrival_lnum
- endfunction
- function! markdown#headers#CurrentHeaderAncestral(...) abort "{{{1
- let origin_pos = getpos('.')
- if a:0 > 0
- let search_from = [0, a:1, 1, 0]
- else
- let search_from = origin_pos
- endif
- let p_lnum = markdown#headers#CurrentHeaderParent(search_from[1])
- " we don't have a parent, so we are an ancestral
- " or we are not under a header
- if p_lnum == 0
- return markdown#headers#CurrentHeader(search_from[1])
- endif
- while p_lnum != 0
- call cursor(p_lnum, 1)
- let a_lnum = markdown#headers#CurrentHeaderParent()
- if a_lnum != 0
- let p_lnum = a_lnum
- else
- call cursor(origin_pos[1], origin_pos[2])
- return p_lnum
- endif
- endwhile
- call cursor(origin_pos[1], origin_pos[2])
- endfunction
- function! markdown#headers#CurrentHeaderAncestors(...) abort "{{{1
- let origin_pos = getpos('.')
- if a:0 > 0
- let search_from = [0, a:1, 1, 0]
- else
- let search_from = origin_pos
- endif
- let h_genealogy = []
- let head = markdown#headers#CurrentHeader(search_from[1])
- if head == 0
- return []
- else
- call add(h_genealogy, head)
- endif
- let p_lnum = markdown#headers#CurrentHeaderParent(search_from[1])
- " we don't have a parent, so we are an ancestral
- if p_lnum == 0
- return h_genealogy
- endif
- while p_lnum != 0
- call cursor(p_lnum, 1)
- call add(h_genealogy, p_lnum)
- let a_lnum = markdown#headers#CurrentHeaderParent()
- if a_lnum != 0
- let p_lnum = a_lnum
- else
- break
- endif
- endwhile
- call cursor(origin_pos[1], origin_pos[2])
- return h_genealogy
- endfunction
- function! markdown#headers#SiblingHeader(direction, ...) abort "{{{1
- let origin_pos = getpos('.')
- if a:0 > 0
- let search_from = [1, a:1, 1, 0]
- else
- let search_from = origin_pos
- endif
- call cursor(search_from[1], search_from[2])
- let parent_lnum = markdown#headers#CurrentHeaderParent()
- let ch_lnum = markdown#headers#CurrentHeader()
- if a:direction ==# 'b'
- call cursor(ch_lnum, 1)
- endif
- let l = getline(ch_lnum)
- if match(l, '^#') > -1
- let header_level = len(matchstr(l, '#*'))
- elseif match(l, '^-') > -1
- let header_level = 2
- else
- let header_level = 1
- endif
- if header_level == 1
- let arrival_lnum = search('\(^.*\n=\|^#\s\)', a:direction.'nW')
- elseif header_level == 2
- let arrival_lnum = search('\(^.*\n-\|^##\s\)', a:direction.'nW')
- else
- let arrival_lnum = search('^#\{'.header_level.'}', a:direction.'nW')
- endif
- " we might have overshot, check if the parent is still correct
- let arrival_parent_lnum = markdown#headers#CurrentHeaderParent(arrival_lnum)
- if arrival_parent_lnum != parent_lnum
- let arrival_lnum = 0
- endif
- call cursor(origin_pos[1], origin_pos[2])
- return arrival_lnum
- endfunction
- function! markdown#headers#NextSiblingHeader(...) abort "{{{1
- if a:0 > 0
- let search_from = a:1
- else
- let search_from = line('.')
- endif
- return markdown#headers#SiblingHeader('', search_from)
- endfunction
- function! markdown#headers#PrevSiblingHeader(...) abort "{{{1
- if a:0 > 0
- let search_from = a:1
- else
- let search_from = line('.')
- endif
- return markdown#headers#SiblingHeader('b', search_from)
- endfunction
- function! markdown#headers#FirstChild(...) abort "{{{1
- if a:0 > 0
- let search_from = [1, a:1, 1, 0]
- else
- let search_from = getpos('.')
- endif
- let ch_lnum = markdown#headers#CurrentHeader(search_from[1])
- let l = getline(ch_lnum)
- if match(l, '^#') > -1
- let children_level = len(matchstr(l, '#*')) + 1
- elseif match(getline(line('.')+1), '^-') > -1
- let children_level = 3
- else
- let children_level = 2
- endif
- call cursor(search_from[1], search_from[2])
- let next_lnum = markdown#headers#NextHeader()
- if children_level == 2
- let arrival_lnum = search('\(^.*\n-\|^##\s\)', 'nW')
- else
- let arrival_lnum = search('^#\{'.children_level.'}', 'nW')
- endif
- if arrival_lnum != next_lnum
- let arrival_lnum = 0
- endif
- return arrival_lnum
- endfunction
- function! markdown#headers#LastChild(...) abort "{{{1
- let origin_pos = getpos('.')
- if a:0 > 0
- let search_from = [1, a:1, 1, 0]
- else
- let search_from = origin_pos
- endif
- call cursor(search_from[1], search_from[2])
- let fc_lnum = markdown#headers#FirstChild()
- if fc_lnum != 0
- call cursor(fc_lnum, 1)
- let n_lnum = markdown#headers#NextSiblingHeader()
- if n_lnum != 0
- while n_lnum
- call cursor(n_lnum, 1)
- let a_lnum = markdown#headers#NextSiblingHeader()
- if a_lnum != 0
- let n_lnum = a_lnum
- else
- break
- endif
- endwhile
- else
- let n_lnum = fc_lnum
- endif
- else
- let n_lnum = 0
- endif
- call cursor(origin_pos[1], origin_pos[2])
- return n_lnum
- endfunction
- function! markdown#headers#NthChild(count, ...) abort "{{{1
- let origin_pos = getpos('.')
- if a:0 > 0
- let search_from = [1, a:1, 1, 0]
- else
- let search_from = origin_pos
- endif
- let fc_lnum = markdown#headers#FirstChild(search_from[1])
- call cursor(fc_lnum, 1)
- if a:count > 1
- for child in range(a:count-1)
- let arrival_lnum = markdown#headers#NextSiblingHeader()
- if arrival_lnum == 0
- break
- endif
- call cursor(arrival_lnum, 1)
- endfor
- else
- let arrival_lnum = fc_lnum
- endif
- call cursor(origin_pos[1], origin_pos[2])
- return arrival_lnum
- endfunction
- function! markdown#headers#ID(...) abort "{{{1
- let origin_pos = getpos('.')
- if a:0 > 0
- let search_from = [1, a:1, 1, 0]
- else
- let search_from = origin_pos
- endif
- let cheader_lnum = markdown#headers#CurrentHeader(search_from[1])
- let cheader = getline(cheader_lnum)
- call cursor(origin_pos[1], origin_pos[2])
- return markdown#headers#GetAutomaticID(cheader)
- endfunction
- " GetAutomaticID(header)
- " see http://johnmacfarlane.net/pandoc/README.html#extension-auto_identifiers
- function! markdown#headers#GetAutomaticID(header) abort " {{{1
- let header_metadata = matchstr(a:header, '{.*}')
- if header_metadata !=# ''
- let header_id = matchstr(header_metadata, '#[[:alnum:]-]*')[1:]
- endif
- if !exists('header_id') || header_id ==# ''
- let text = substitute(a:header, '\[\(.\{-}\)\]\[.*\]', '\1', '') " remove links
- let text = substitute(text, '\s{.*}', '', '') " remove attributes
- let text = substitute(text, '[!"#\$%\&''()\*+,/:;<=>?@\[\\\]\^`{|}\~]', '', 'g') " remove formatting and punctuation, except -_. (hyphen, underscore, period)
- let text = substitute(text, '.\{-}[[:alpha:]\u20AC-\uFFFF]\@=', '', '') " remove everything before the first letter
- let text = substitute(text, '\s', '-', 'g') " replace spaces with dashes
- let text = tolower(text) " turn lowercase
- if match(text, "[[:alpha:]\u20AC-\uFFFF]") > -1
- let header_id = text
- else
- let header_id = 'section'
- endif
- endif
- return header_id
- endfunction
- " GetAllIDs()
- " get all the header indentifiers and it's position, both specified and automatic generated
- function! markdown#headers#GetAllIDs() abort " {{{1
- let header_pos = {}
- " update the location list
- call pandoc#toc#Update()
- let headers = getloclist(0)
- for header in headers
- let header_pos[markdown#headers#GetAutomaticID(header.text)] = header.lnum
- endfor
- return header_pos
- endfunction
|