]> git.madduck.net Git - etc/vim.git/commitdiff

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Merge pull request #183 from cirosantilli/gx
authorCiro Santilli 六四事件 法轮功 <ciro.santilli@gmail.com>
Thu, 16 Apr 2015 08:44:24 +0000 (10:44 +0200)
committerCiro Santilli 六四事件 法轮功 <ciro.santilli@gmail.com>
Thu, 16 Apr 2015 08:44:24 +0000 (10:44 +0200)
gx works from anywhere inside Markdown links

README.md
ftplugin/mkd.vim
test/map.vader

index 0a652faeadb89ba24c6b7390b19767ba10498eb3..8389667052bbc3c604c974e9eeaa556e450de843 100644 (file)
--- a/README.md
+++ b/README.md
@@ -97,12 +97,33 @@ let g:vim_markdown_frontmatter=1
 
 The following work on normal and visual modes:
 
-- `]]`: go to next header. `<Plug>(Markdown_MoveToNextHeader)`
-- `[[`: go to previous header. Contrast with `]c`. `<Plug>(Markdown_MoveToPreviousHeader)`
-- `][`: go to next sibling header if any. `<Plug>(Markdown_MoveToNextSiblingHeader)`
-- `[]`: go to previous sibling header if any. `<Plug>(Markdown_MoveToPreviousSiblingHeader)`
-- `]c`: go to Current header. `<Plug>(Markdown_MoveToCurHeader)`
-- `]u`: go to parent header (Up). `<Plug>(Markdown_MoveToParentHeader)`
+-   `gx`: open the link under the cursor in the same browser as the standard `gx` command.
+
+    The standard `gx` is extended by allowing you to put your cursor anywhere inside a link.
+
+    For example, all the following cursor positions will work:
+
+        [Example](http://example.com)
+        ^  ^    ^^   ^       ^
+        1  2    34   5       6
+
+        <http://example.com>
+        ^  ^               ^
+        1  2               3
+
+    Known limitation: does not work for links that span multiple lines.
+
+-   `]]`: go to next header. `<Plug>(Markdown_MoveToNextHeader)`
+
+-   `[[`: go to previous header. Contrast with `]c`. `<Plug>(Markdown_MoveToPreviousHeader)`
+
+-   `][`: go to next sibling header if any. `<Plug>(Markdown_MoveToNextSiblingHeader)`
+
+-   `[]`: go to previous sibling header if any. `<Plug>(Markdown_MoveToPreviousSiblingHeader)`
+
+-   `]c`: go to Current header. `<Plug>(Markdown_MoveToCurHeader)`
+
+-   `]u`: go to parent header (Up). `<Plug>(Markdown_MoveToParentHeader)`
 
 ## Commands
 
index 63d7914d47d76f889313efc25fa4863f0be5b118..a2e7b5eb7fdaef2c7fd70c2c4c77ada51dead9f1 100644 (file)
@@ -411,6 +411,100 @@ function! s:TableFormat()
   call setpos('.', l:pos)
 endfunction
 
+" Parameters:
+"
+" - step +1 for right, -1 for left
+"
+" TODO: multiple lines.
+"
+function! s:FindCornerOfSyntax(lnum, col, step)
+    let l:col = a:col
+    let l:syn = synIDattr(synID(a:lnum, l:col, 1), 'name')
+    while synIDattr(synID(a:lnum, l:col, 1), 'name') ==# l:syn
+        let l:col += a:step
+    endwhile
+    return l:col - a:step
+endfunction
+
+" Return the next position of the given syntax name,
+" inclusive on the given position.
+"
+" TODO: multiple lines
+"
+function! s:FindNextSyntax(lnum, col, name)
+    let l:col = a:col
+    let l:step = 1
+    while synIDattr(synID(a:lnum, l:col, 1), 'name') !=# a:name
+        let l:col += l:step
+    endwhile
+    return [a:lnum, l:col]
+endfunction
+
+function! s:FindCornersOfSyntax(lnum, col)
+    return [<sid>FindLeftOfSyntax(a:lnum, a:col), <sid>FindRightOfSyntax(a:lnum, a:col)]
+endfunction
+
+function! s:FindRightOfSyntax(lnum, col)
+    return <sid>FindCornerOfSyntax(a:lnum, a:col, 1)
+endfunction
+
+function! s:FindLeftOfSyntax(lnum, col)
+    return <sid>FindCornerOfSyntax(a:lnum, a:col, -1)
+endfunction
+
+" Returns:
+"
+" - a string with the the URL for the link under the cursor
+" - an empty string if the cursor is not on a link
+"
+" `b:` instead of `s:` to make it testable.
+"
+" TODO
+"
+" - multiline support
+" - give an error if the separator does is not on a link
+"
+function! b:Markdown_GetUrlForPosition(lnum, col)
+    let l:lnum = a:lnum
+    let l:col = a:col
+    let l:syn = synIDattr(synID(l:lnum, l:col, 1), 'name')
+
+    if l:syn ==# 'mkdInlineURL' || l:syn ==# 'mkdURL' || l:syn ==# 'mkdLinkDefTarget'
+        " Do nothing.
+    elseif l:syn ==# 'mkdLink'
+        let [l:lnum, l:col] = <sid>FindNextSyntax(l:lnum, l:col, 'mkdURL')
+        let l:syn = 'mkdURL'
+    elseif l:syn ==# 'mkdDelimiter'
+        let l:line = getline(l:lnum)
+        let l:char = l:line[col - 1]
+        if l:char ==# '<'
+            let l:col += 1
+        elseif l:char ==# '>' || l:char ==# ')'
+            let l:col -= 1
+        elseif l:char ==# '[' || l:char ==# ']' || l:char ==# '('
+            let [l:lnum, l:col] = <sid>FindNextSyntax(l:lnum, l:col, 'mkdURL')
+        else
+            return ''
+        endif
+    else
+        return ''
+    endif
+
+    let [l:left, l:right] = <sid>FindCornersOfSyntax(l:lnum, l:col)
+    return getline(l:lnum)[l:left - 1 : l:right - 1]
+endfunction
+
+" Front end for GetUrlForPosition.
+"
+function! s:OpenUrlUnderCursor()
+    let l:url = b:Markdown_GetUrlForPosition(line('.'), col('.'))
+    if l:url != ''
+        call netrw#NetrwBrowseX(l:url, 0)
+    else
+        echomsg 'The cursor is not on a link.'
+    endif
+endfunction
+
 call <sid>MapNormVis('<Plug>(Markdown_MoveToNextHeader)', '<sid>Markdown_MoveToNextHeader')
 call <sid>MapNormVis('<Plug>(Markdown_MoveToPreviousHeader)', '<sid>Markdown_MoveToPreviousHeader')
 call <sid>MapNormVis('<Plug>(Markdown_MoveToNextSiblingHeader)', '<sid>Markdown_MoveToNextSiblingHeader')
@@ -419,6 +513,7 @@ call <sid>MapNormVis('<Plug>(Markdown_MoveToPreviousSiblingHeader)', '<sid>Markd
 call <sid>MapNormVis('<Plug>(Markdown_MoveToParentHeader)', '<sid>Markdown_MoveToParentHeader')
 " Menmonic: Current
 call <sid>MapNormVis('<Plug>(Markdown_MoveToCurHeader)', '<sid>Markdown_MoveToCurHeader')
+nnoremap <Plug>(OpenUrlUnderCursor) :call <sid>OpenUrlUnderCursor()<cr>
 
 if !get(g:, 'vim_markdown_no_default_key_mappings', 0)
     nmap <buffer> ]] <Plug>(Markdown_MoveToNextHeader)
@@ -427,6 +522,7 @@ if !get(g:, 'vim_markdown_no_default_key_mappings', 0)
     nmap <buffer> [] <Plug>(Markdown_MoveToPreviousSiblingHeader)
     nmap <buffer> ]u <Plug>(Markdown_MoveToParentHeader)
     nmap <buffer> ]c <Plug>(Markdown_MoveToCurHeader)
+    nmap <buffer> gx <Plug>(OpenUrlUnderCursor)
 
     vmap <buffer> ]] <Plug>(Markdown_MoveToNextHeader)
     vmap <buffer> [[ <Plug>(Markdown_MoveToPreviousHeader)
index 6cf0cee790caeb775d31a84c382122fb22cdcbea..51bde78b4e12bde8d98518347b84d3f85e726ee2 100644 (file)
@@ -1,3 +1,51 @@
+Given mkd;
+a <http://b> c
+
+Execute (gx autolink):
+  let b:url = 'http://b'
+  let b:line = getline(1)
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'a') + 1), ''
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, '<') + 1), b:url
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'h') + 1), b:url
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, '>') + 1), b:url
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'c') + 1), ''
+
+Given mkd;
+a http://b.bb c
+
+Execute (gx implicit autolink):
+  let b:url = 'http://b.bb'
+  let b:line = getline(1)
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'a') + 1), ''
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'h') + 1), b:url
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'c') + 1), ''
+
+Given mkd;
+[a]: http://b "c"
+
+Execute (gx link reference definition):
+  let b:url = 'http://b'
+  let b:line = getline(1)
+  " TODO would be cool if all of the following gave the link.
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'a') + 1), ''
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'h') + 1), b:url
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'c') + 1), ''
+
+Given mkd;
+a [b](c) d
+
+Execute (gx autolink):
+  let b:url = 'c'
+  let b:line = getline(1)
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'a') + 1), ''
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, '[') + 1), b:url
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'b') + 1), b:url
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, ']') + 1), b:url
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, '(') + 1), b:url
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'c') + 1), b:url
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, ')') + 1), b:url
+  AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'd') + 1), ''
+
 Given mkd;
 # a