-"TODO get working on visual mode
+"TODO print messages when on visual mode. I only see VISUAL, not the messages.
-"- if inside a header
-" goes to the nearest head before current position
-" returns its initial hashes (#)
-"- else
-" goes to beginning of document
-" returns ''
-fu! b:Markdown_GoCurHeader()
- if search( '^#', 'bcW' ) != 0
- return matchstr( getline('.'), '\v^#+' )
- el
- norm! gg
- ec 'outside any header'
- return ''
- en
-endf
-
-"same as `b:Markdown_GoCurHeader`:function: but does not change cursor position
-fu! b:Markdown_GetHashesCurHeader()
- let line = search( '\v^#', 'nW' ) != 0
- retu matchstr( getline(line) '\v^#+' )
-endf
-
-"goes to next header of any level
-"returns its hashes
-fu! b:Markdown_GoNextHeader()
- if search( '\v^#', 'W' ) != 0
- return matchstr( getline('.'), '\v^#+' )
- el
- "norm! G
- ec 'no more headers'
- return ''
- en
-endf
+" This is how you should view things:
+"
+" |BUFFER
+" |
+" |Outside any header
+" |
+" a-+# a
+" |
+" |Inside a
+" |
+" a-+
+" b-+## b
+" |
+" |inside b
+" |
+" b-+
+" c-+### c
+" |
+" |Inside c
+" |
+" c-+
+" d-|# d
+" |
+" |Inside d
+" |
+" d-+
+
+let s:headerExpr = '\v^(\s*#|.+\n(\=+|-+)$)'
+
+" Return 0 if not found, else the actual line number.
+"
+function! b:Markdown_GetLineNumCurHeader()
+ echo s:headerExpr
+ return search(s:headerExpr, 'bcnW')
+endfunction
-"goes to previous header of any level
+" - if inside a header goes to it.
+" Return its line number.
+"
+" - if on top level outside any headers,
+" print a warning
+" Return `0`.
+"
+function! b:Markdown_GoCurHeader()
+ let l:lineNum = b:Markdown_GetLineNumCurHeader()
+ if l:lineNum != 0
+ call cursor(l:lineNum, 1)
+ else
+ echo 'error: outside any header'
+ "normal! gg
+ end
+ return l:lineNum
+endfunction
+
+" Put cursor on next header of any level.
+"
+" If there are no more headers, print a warning.
+"
+function! b:Markdown_GoNextHeader()
+ if search(s:headerExpr, 'W') == 0
+ "normal! G
+ echo 'error: no next header'
+ end
+endfunction
+
+" Put cursor on previous header (before current) of any level.
+"
+" If it does not exist, print a warning.
"
-"if there is no previous header, only print a warning
+function! b:Markdown_GoPreviousHeader()
+ let l:oldPos = getpos('.')
+ let l:curHeaderLineNumber = b:Markdown_GoCurHeader()
+ if l:curHeaderLineNumber == 0
+ call setpos('.', l:oldPos)
+ end
+ if search(s:headerExpr, 'bW') == 0
+ "normal! gg
+ call setpos('.', l:oldPos)
+ echo 'error: no previous header'
+ end
+endfunction
+
+"- if inside a header, cursor goes to it.
+" Return its hashes.
"
-"if the cursor is not exactly at the header,
-"it goes to exactly the header. So this could be used
-"if you want to go to the current header line.
-fu! b:Markdown_GoPreviousHeader()
- if search( '^#', 'bW' ) != 0
- return matchstr( getline('.'), '\v^#+' )
- el
- "norm! gg
- ec 'no more headers'
+"- if on top level outside any headers,
+" print a warning
+" return ''
+"
+function! b:Markdown_GoCurHeaderGetHashes()
+ let l:linenum = b:Markdown_GetLineNumCurHeader()
+ if l:linenum != 0
+ call cursor(l:linenum, 1)
+ return matchlist(getline(linenum), '\v^\s*(#+)')[1]
+ else
return ''
- en
-endf
-
-"if already at top level, go to beginning of buffer
-fu! b:Markdown_GoHeaderUp()
- let l:hashes = b:Markdown_GoCurHeader()
- if len( l:hashes ) > 1
- cal search( '^' . l:hashes[1:] . '[^#]', 'b' )
- el
- norm! gg
- en
-endf
-
-fu! b:Markdown_GoNextHeaderSameLevel()
-
- let l:hashes = b:Markdown_GoCurHeader()
-
- "go to next occurrence of that number of hashes
- cal search( '^' . l:hashes . '[^#]', 'W' )
-
-endf
-
-"if no more next siblings, print error message and do nothing.
-fu! b:Markdown_GoNextSiblingHeader()
-
- let l:hashes = b:Markdown_GoCurHeader()
+ end
+endfunction
+" Put cursor on previous header of any level.
+"
+" If it exists, return its lines number.
+"
+" Otherwise, print a warning and return `0`.
+"
+function! b:Markdown_GoHeaderUp()
+ let l:oldPos = getpos('.')
+ let l:hashes = b:Markdown_GoCurHeaderGetHashes()
+ if len(l:hashes) > 1
+ call search('^\s*' . l:hashes[1:] . '[^#]', 'b')
+ else
+ call setpos('.', l:oldPos)
+ echo 'error: already at top level'
+ end
+endfunction
+
+" If no more next siblings, print error message and do nothing.
+"
+function! b:Markdown_GoNextSiblingHeader()
+ let l:oldPos = getpos('.')
+ let l:hashes = b:Markdown_GoCurHeaderGetHashes()
+ let l:noSibling = 0
if l:hashes ==# ''
- retu
- en
-
- let l:nhashes = len(l:hashes)
- if l:nhashes == 1
- "special case, just add the largest possible value
- let l:nextLowerLevelLine = line('$') + 1
- el
- let l:nextLowerLevelLine = search( '\v^#{1,' . ( l:nhashes - 1 ) . '}[^#]' , 'nW' )
- en
-
- let l:nextSameLevelLine = search( '\v^' . l:hashes . '[^#]', 'nW' )
-
- if (
- \ l:nextSameLevelLine > 0
- \ &&
- \ (
- \ l:nextLowerLevelLine == 0
- \ ||
- \ l:nextLowerLevelLine > l:nextSameLevelLine
+ let l:noSibling = 1
+ else
+ let l:nhashes = len(l:hashes)
+ if l:nhashes == 1
+ "special case, just add the largest possible value
+ let l:nextLowerLevelLine = line('$') + 1
+ else
+ let l:nextLowerLevelLine = search('\v^\s*#{1,' . (l:nhashes - 1) . '}[^#]' , 'nW')
+ end
+ let l:nextSameLevelLine = search('\v^\s*' . l:hashes . '[^#]', 'nW')
+ if (
+ \ l:nextSameLevelLine > 0
+ \ &&
+ \ (
+ \ l:nextLowerLevelLine == 0
+ \ ||
+ \ l:nextLowerLevelLine > l:nextSameLevelLine
+ \ )
\ )
- \ )
- cal cursor( l:nextSameLevelLine, 0 )
- el
- ec 'no more siblings'
- en
-
-endf
-
-fu! b:Markdown_GoPreviousHeaderSameLevel()
-
- let l:hashes = b:Markdown_GoCurHeader()
-
- "go to next occurrence of that number of hashes
- cal search( '^' . l:hashes . '[^#]', 'bW' )
-
-endf
+ call cursor(l:nextSameLevelLine, 1)
+ else
+ let l:noSibling = 1
+ end
+ end
+ if l:noSibling
+ call setpos('.', l:oldPos)
+ echo 'error: no next sibling'
+ end
+endfunction
"if no more next siblings, print error message and do nothing.
-fu! b:Markdown_GoPreviousSiblingHeader()
-
- let l:hashes = b:Markdown_GoCurHeader()
-
+function! b:Markdown_GoPreviousSiblingHeader()
+ let l:oldPos = getpos('.')
+ let l:hashes = b:Markdown_GoCurHeaderGetHashes()
+ let l:noSibling = 0
if l:hashes ==# ''
- retu
- en
-
- let l:nhashes = len(l:hashes)
- if l:nhashes == 1
- "special case, just add the largest possible value
- let l:prevLowerLevelLine = -1
- el
- let l:prevLowerLevelLine = search( '\v^#{1,' . ( l:nhashes - 1 ) . '}[^#]' , 'bnW' )
- en
-
- let l:prevSameLevelLine = search( '\v^' . l:hashes . '[^#]', 'bnW' )
-
- if (
- \ l:prevSameLevelLine > 0
- \ &&
- \ (
- \ l:prevLowerLevelLine == 0
- \ ||
- \ l:prevLowerLevelLine < l:prevSameLevelLine
- \ )
- \ )
- cal cursor( l:prevSameLevelLine, 0 )
- el
- ec 'no more siblings'
- en
-
-endf
-
-"mnemonics: ']' next (like a right arrow)
-nn <buffer><silent> ]] :cal b:Markdown_GoNextHeader()<cr>
-"vnoremap <buffer><silent> ]] /^#<cr><esc>:nohl<cr>gv
-
-"mnemonics: '[' next (like a left arrow)
-nn <buffer><silent> ][ :cal b:Markdown_GoNextSiblingHeader()<cr>
-"vnoremap <buffer><silent> ][ <esc>:cal b:Markdown_GoNextHeaderSameLevel()<cr>
-
-nn <buffer><silent> [] :cal b:Markdown_GoPreviousSiblingHeader()<cr>
-
-nn <buffer><silent> [[ :cal b:Markdown_GoPreviousHeader()<cr>
-"vnoremap <buffer><silent> [[ ?^#<cr><esc>:nohl<cr>gv
-
-"go up one level. Menmonic: Up.
-nn <buffer><silent> ]u :cal b:Markdown_GoHeaderUp()<cr>
+ let l:noSibling = 1
+ else
+ let l:nhashes = len(l:hashes)
+ if l:nhashes == 1
+ "special case, just add the largest possible value
+ let l:prevLowerLevelLine = -1
+ else
+ let l:prevLowerLevelLine = search('\v^\s*#{1,' . (l:nhashes - 1) . '}[^#]' , 'bnW')
+ end
+ let l:prevSameLevelLine = search('\v^\s*' . l:hashes . '[^#]', 'bnW')
+ if (
+ \ l:prevSameLevelLine > 0
+ \ &&
+ \ (
+ \ l:prevLowerLevelLine == 0
+ \ ||
+ \ l:prevLowerLevelLine < l:prevSameLevelLine
+ \ )
+ \)
+ call cursor(l:prevSameLevelLine, 1)
+ else
+ let l:noSibling = 1
+ end
+ end
+ if l:noSibling
+ call setpos('.', l:oldPos)
+ echo 'error: no previous sibling'
+ end
+endfunction
+
+"wrapper to do move commands in visual mode
+function! s:VisMove(f)
+ norm! gv
+ call function(a:f)()
+endfunction
+
+"map in both normal and visual modes
+function! s:MapNormVis(rhs,lhs)
+ execute 'nn <buffer><silent> ' . a:rhs . ' :call ' . a:lhs . '()<cr>'
+ execute 'vn <buffer><silent> ' . a:rhs . ' <esc>:call <sid>VisMove(''' . a:lhs . ''')<cr>'
+endfunction
+
+call <sid>MapNormVis(']]', 'b:Markdown_GoNextHeader')
+call <sid>MapNormVis('[[', 'b:Markdown_GoPreviousHeader')
+call <sid>MapNormVis('][', 'b:Markdown_GoNextSiblingHeader')
+call <sid>MapNormVis('[]', 'b:Markdown_GoPreviousSiblingHeader')
+"menmonic: Up
+call <sid>MapNormVis(']u', 'b:Markdown_GoHeaderUp')
+"menmonic: Current
+call <sid>MapNormVis(']c', 'b:Markdown_GoCurHeader')