Skip to content

Commit c3a600c

Browse files
committed
Merge pull request #183 from cirosantilli/gx
gx works from anywhere inside Markdown links
2 parents 0da5ba9 + 46c859c commit c3a600c

File tree

3 files changed

+171
-6
lines changed

3 files changed

+171
-6
lines changed

README.md

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,33 @@ let g:vim_markdown_frontmatter=1
9797

9898
The following work on normal and visual modes:
9999

100-
- `]]`: go to next header. `<Plug>(Markdown_MoveToNextHeader)`
101-
- `[[`: go to previous header. Contrast with `]c`. `<Plug>(Markdown_MoveToPreviousHeader)`
102-
- `][`: go to next sibling header if any. `<Plug>(Markdown_MoveToNextSiblingHeader)`
103-
- `[]`: go to previous sibling header if any. `<Plug>(Markdown_MoveToPreviousSiblingHeader)`
104-
- `]c`: go to Current header. `<Plug>(Markdown_MoveToCurHeader)`
105-
- `]u`: go to parent header (Up). `<Plug>(Markdown_MoveToParentHeader)`
100+
- `gx`: open the link under the cursor in the same browser as the standard `gx` command.
101+
102+
The standard `gx` is extended by allowing you to put your cursor anywhere inside a link.
103+
104+
For example, all the following cursor positions will work:
105+
106+
[Example](http://example.com)
107+
^ ^ ^^ ^ ^
108+
1 2 34 5 6
109+
110+
<http://example.com>
111+
^ ^ ^
112+
1 2 3
113+
114+
Known limitation: does not work for links that span multiple lines.
115+
116+
- `]]`: go to next header. `<Plug>(Markdown_MoveToNextHeader)`
117+
118+
- `[[`: go to previous header. Contrast with `]c`. `<Plug>(Markdown_MoveToPreviousHeader)`
119+
120+
- `][`: go to next sibling header if any. `<Plug>(Markdown_MoveToNextSiblingHeader)`
121+
122+
- `[]`: go to previous sibling header if any. `<Plug>(Markdown_MoveToPreviousSiblingHeader)`
123+
124+
- `]c`: go to Current header. `<Plug>(Markdown_MoveToCurHeader)`
125+
126+
- `]u`: go to parent header (Up). `<Plug>(Markdown_MoveToParentHeader)`
106127

107128
## Commands
108129

ftplugin/mkd.vim

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,100 @@ function! s:TableFormat()
411411
call setpos('.', l:pos)
412412
endfunction
413413

414+
" Parameters:
415+
"
416+
" - step +1 for right, -1 for left
417+
"
418+
" TODO: multiple lines.
419+
"
420+
function! s:FindCornerOfSyntax(lnum, col, step)
421+
let l:col = a:col
422+
let l:syn = synIDattr(synID(a:lnum, l:col, 1), 'name')
423+
while synIDattr(synID(a:lnum, l:col, 1), 'name') ==# l:syn
424+
let l:col += a:step
425+
endwhile
426+
return l:col - a:step
427+
endfunction
428+
429+
" Return the next position of the given syntax name,
430+
" inclusive on the given position.
431+
"
432+
" TODO: multiple lines
433+
"
434+
function! s:FindNextSyntax(lnum, col, name)
435+
let l:col = a:col
436+
let l:step = 1
437+
while synIDattr(synID(a:lnum, l:col, 1), 'name') !=# a:name
438+
let l:col += l:step
439+
endwhile
440+
return [a:lnum, l:col]
441+
endfunction
442+
443+
function! s:FindCornersOfSyntax(lnum, col)
444+
return [<sid>FindLeftOfSyntax(a:lnum, a:col), <sid>FindRightOfSyntax(a:lnum, a:col)]
445+
endfunction
446+
447+
function! s:FindRightOfSyntax(lnum, col)
448+
return <sid>FindCornerOfSyntax(a:lnum, a:col, 1)
449+
endfunction
450+
451+
function! s:FindLeftOfSyntax(lnum, col)
452+
return <sid>FindCornerOfSyntax(a:lnum, a:col, -1)
453+
endfunction
454+
455+
" Returns:
456+
"
457+
" - a string with the the URL for the link under the cursor
458+
" - an empty string if the cursor is not on a link
459+
"
460+
" `b:` instead of `s:` to make it testable.
461+
"
462+
" TODO
463+
"
464+
" - multiline support
465+
" - give an error if the separator does is not on a link
466+
"
467+
function! b:Markdown_GetUrlForPosition(lnum, col)
468+
let l:lnum = a:lnum
469+
let l:col = a:col
470+
let l:syn = synIDattr(synID(l:lnum, l:col, 1), 'name')
471+
472+
if l:syn ==# 'mkdInlineURL' || l:syn ==# 'mkdURL' || l:syn ==# 'mkdLinkDefTarget'
473+
" Do nothing.
474+
elseif l:syn ==# 'mkdLink'
475+
let [l:lnum, l:col] = <sid>FindNextSyntax(l:lnum, l:col, 'mkdURL')
476+
let l:syn = 'mkdURL'
477+
elseif l:syn ==# 'mkdDelimiter'
478+
let l:line = getline(l:lnum)
479+
let l:char = l:line[col - 1]
480+
if l:char ==# '<'
481+
let l:col += 1
482+
elseif l:char ==# '>' || l:char ==# ')'
483+
let l:col -= 1
484+
elseif l:char ==# '[' || l:char ==# ']' || l:char ==# '('
485+
let [l:lnum, l:col] = <sid>FindNextSyntax(l:lnum, l:col, 'mkdURL')
486+
else
487+
return ''
488+
endif
489+
else
490+
return ''
491+
endif
492+
493+
let [l:left, l:right] = <sid>FindCornersOfSyntax(l:lnum, l:col)
494+
return getline(l:lnum)[l:left - 1 : l:right - 1]
495+
endfunction
496+
497+
" Front end for GetUrlForPosition.
498+
"
499+
function! s:OpenUrlUnderCursor()
500+
let l:url = b:Markdown_GetUrlForPosition(line('.'), col('.'))
501+
if l:url != ''
502+
call netrw#NetrwBrowseX(l:url, 0)
503+
else
504+
echomsg 'The cursor is not on a link.'
505+
endif
506+
endfunction
507+
414508
call <sid>MapNormVis('<Plug>(Markdown_MoveToNextHeader)', '<sid>Markdown_MoveToNextHeader')
415509
call <sid>MapNormVis('<Plug>(Markdown_MoveToPreviousHeader)', '<sid>Markdown_MoveToPreviousHeader')
416510
call <sid>MapNormVis('<Plug>(Markdown_MoveToNextSiblingHeader)', '<sid>Markdown_MoveToNextSiblingHeader')
@@ -419,6 +513,7 @@ call <sid>MapNormVis('<Plug>(Markdown_MoveToPreviousSiblingHeader)', '<sid>Markd
419513
call <sid>MapNormVis('<Plug>(Markdown_MoveToParentHeader)', '<sid>Markdown_MoveToParentHeader')
420514
" Menmonic: Current
421515
call <sid>MapNormVis('<Plug>(Markdown_MoveToCurHeader)', '<sid>Markdown_MoveToCurHeader')
516+
nnoremap <Plug>(OpenUrlUnderCursor) :call <sid>OpenUrlUnderCursor()<cr>
422517
423518
if !get(g:, 'vim_markdown_no_default_key_mappings', 0)
424519
nmap <buffer> ]] <Plug>(Markdown_MoveToNextHeader)
@@ -427,6 +522,7 @@ if !get(g:, 'vim_markdown_no_default_key_mappings', 0)
427522
nmap <buffer> [] <Plug>(Markdown_MoveToPreviousSiblingHeader)
428523
nmap <buffer> ]u <Plug>(Markdown_MoveToParentHeader)
429524
nmap <buffer> ]c <Plug>(Markdown_MoveToCurHeader)
525+
nmap <buffer> gx <Plug>(OpenUrlUnderCursor)
430526
431527
vmap <buffer> ]] <Plug>(Markdown_MoveToNextHeader)
432528
vmap <buffer> [[ <Plug>(Markdown_MoveToPreviousHeader)

test/map.vader

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,51 @@
1+
Given mkd;
2+
a <http://b> c
3+
4+
Execute (gx autolink):
5+
let b:url = 'http://b'
6+
let b:line = getline(1)
7+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'a') + 1), ''
8+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, '<') + 1), b:url
9+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'h') + 1), b:url
10+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, '>') + 1), b:url
11+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'c') + 1), ''
12+
13+
Given mkd;
14+
a http://b.bb c
15+
16+
Execute (gx implicit autolink):
17+
let b:url = 'http://b.bb'
18+
let b:line = getline(1)
19+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'a') + 1), ''
20+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'h') + 1), b:url
21+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'c') + 1), ''
22+
23+
Given mkd;
24+
[a]: http://b "c"
25+
26+
Execute (gx link reference definition):
27+
let b:url = 'http://b'
28+
let b:line = getline(1)
29+
" TODO would be cool if all of the following gave the link.
30+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'a') + 1), ''
31+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'h') + 1), b:url
32+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'c') + 1), ''
33+
34+
Given mkd;
35+
a [b](c) d
36+
37+
Execute (gx autolink):
38+
let b:url = 'c'
39+
let b:line = getline(1)
40+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'a') + 1), ''
41+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, '[') + 1), b:url
42+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'b') + 1), b:url
43+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, ']') + 1), b:url
44+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, '(') + 1), b:url
45+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'c') + 1), b:url
46+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, ')') + 1), b:url
47+
AssertEqual b:Markdown_GetUrlForPosition(1, match(b:line, 'd') + 1), ''
48+
149
Given mkd;
250
# a
351

0 commit comments

Comments
 (0)