5

One thing I often do here is to add links to documentation when writing :he foo, and I usually link to http://vimhelp.appspot.com. The problem is, that site's search is about slightly worse than the Stack Exchange sites' search. http://vimdoc.sourceforge.net, on the other hand, has excellent search functionality, searching the tags similar to the way Vim does.

What I'd like is a function which can convert something like :H magic to http://vimhelp.appspot.com/pattern.txt.html#%2Fmagic, that is:

  • find the actual text of the help tag (:he subst can go to the help for :substitute)
  • URL encode special characters in the tag (:, /, etc.)
  • copy the URL to the clipboard or some other register
  • Bonus: if run without args, pick the argument I used for my last call to :he, or the last tag I jumped to in the help.

I've come close:

function! Help2url (word)
    python <<EOF
import vim
from urllib import quote_plus 
topmatch = vim.eval ('taglist(a:word)[0]')
filename = topmatch['filename'].split ('/')[-1]
tag = quote_plus (topmatch['name'])
url = 'http://vimhelp.appspot.com/{:s}.html#{:s}'.format (filename, tag)
EOF
endfunction

This still needs to:

  • use the help system's tags.
  • copy to the clipboard
  • work out the order that Vim uses.

The last point needs explaining. taglist('magic') returns:

[{'cmd': '/*''magic''*', 'static': 0, 'name': '''magic''', 'kind': '', 'filename
': '/usr/share/vim/vim74/doc/options.txt'}, {'cmd': '/*''nomagic''*', 'static':
0, 'name': '''nomagic''', 'kind': '', 'filename': '/usr/share/vim/vim74/doc/opti
ons.txt'}, {'cmd': '/*\/magic*', 'static': 0, 'name': '/magic', 'kind': '', 'fil
ename': '/usr/share/vim/vim74/doc/pattern.txt'}, {'cmd': '/*:smagic*', 'static':
 0, 'name': ':smagic', 'kind': '', 'filename': '/usr/share/vim/vim74/doc/change.
txt'}, {'cmd': '/*:snomagic*', 'static': 0, 'name': ':snomagic', 'kind': '', 'fi
lename': '/usr/share/vim/vim74/doc/change.txt'}]

The first match is 'magic', whereas :he magic goes to /magic. So, simply taking the first element of the list returned from taglist() doesn't seem to cut it.

Solutions not using Python are welcome. I'm just more comfortable with it.

  • 1
    Looking at the sources, it's probably safe to forget about it. Getting from a keyword to a tag goes something like this: first a number of special cases are recognized, and the keyword is transformed accordingly. Then a special regexp is crafted from the keyword (in a ~200 lines function), and the regexp is used to look up tags in the help tag files. Then the results are assigned numbers heuristically, based on how the regexp has matched, where, etc., and the results are sorted by these numbers. You can probably emulate all this dance in VimL if you're determined, but it won't be nice. – lcd047 Aug 16 '15 at 11:48
  • Also see: Is there a simpler way to link to the help? at meta – Martin Tournoij Aug 25 '15 at 8:04
  • @Carpetsmoker I knew I'd seen this somewhere before, but I couldn't find it here and didn't think to search Vi and Vim Meta. Should I delete this? – muru Aug 25 '15 at 13:56
  • @muru I don't think so; if anything, the meta thread should be closed, IMHO. – Martin Tournoij Aug 25 '15 at 14:55
5

EDIT 2

I've improved the function, the encoding should be working on all of the help topics and it is now available as a plugin.

EDIT

Here is an updated version of the function allowing to call vimhelp.appspot.com. It has several advantages:

  • You can type a tag in the short form, the link created will be the full one (ex: GOD @<! will generate a line [:h /\@<!](link)
  • The function encodes the characters so that the url will work. These substitutions are based on a list which can easily evolve.
  • It has no python dependency.
  • It uses vimhelp.appspot.com instead of vimdoc.sourceforge as did the old version.

Here is the code:

function! GetOnlineDoc(string)
    " Go to specified help tag locally
    execute "h " . a:string

    " Get the help filename
    let f = expand("%:t")

    " Get the tag
    let tag = expand('<cword>')

    " URL encoding (taken from https://github.com/idbrii/vim-textconv/blob/master/autoload/textconv/urlencode.vim)
    let dict = [
        \['%21','!'],
        \['%22',' '],
        \['%23','#'],
        \['%24','\$'],
        \['%26','&'],
        \['%27',"'"],
        \['%28','('],
        \['%29',')'],
        \['%2A','*'],
        \['%2B','+'],
        \['%2C',','],
        \['%2D','-'],
        \['%2F','/'],
        \['%3A',':'],
        \['%3B',';'],
        \['%3C','<'],
        \['%3D','='],
        \['%3E','>'],
        \['%3F','?'],
        \['%40','@'],
        \['%5B','['],
        \['%5C','\'],
        \['%5D',']'],
        \['%5E','\^'],
        \['%60','`'],
        \['%7B','{'],
        \['%7C','|'],
        \['%7D','}'],
        \['%7E','\~']
    \]

    let s = tag
    for i in dict
        let s = substitute(s, i[1], i[0], 'g')
    endfor

    " Create the link
    let link = "[`:h " . tag . "`](http://vimhelp.appspot.com/" . f . ".html#" . s . ")"

    " Put it in the clipboard register
    if has('win32')
        let @* = link
    else
        let @+ = link
    endif

    " Optional, close the opened help file
    "execute "bd"
endfunction

And the same line is still in use to create the command:

command! -nargs=1 -complete=help GOD call GetOnlineDoc(<f-args>)

Original answer

I use a full vimscript solution which is a combination of a function and a command. It use the site http://vimdoc.sourceforge.net instead of the one you requested but it doesn't work too bad:

function! GetOnlineDoc(string)

    " Go to specified help tag locally
    execute "h " . a:string

    " Get the help filename without the ".txt" extension
    let filename = expand("%:t:r")

    " Create the link
    let link = "http://vimdoc.sourceforge.net/htmldoc/" . filename . ".html#" . a:string

    " Put it in the clipboard register
    let @* = link

    " Optional, close the opened help file
    "execute "bd"
endfunction

command! -nargs=1 -complete=help GOD call GetOnlineDoc(<f-args>)

NOTE 1 Depending on your system, you might need to replace the line let @* = link with let @+ = link to get the link in the clipboard.

NOTE 2 It doesn't search for the last tag searched in history when no argument is provided but I think it is possible to add this feature.

The function simply opens the help topic of the tag taken as argument, get the help file name and forge the corresponding url on the site.

The command has the parameter -complete=help to avoid passing to the function a tag which doesn't exists. For your example with 'magic' and /magic the user simply has to select the right tag to get the corresponding url.

It still has two main issues:

  • Some tags with special characters might not always be treated correctly (I haven't experienced this problem really often but it still might happen). But it still work for tags like :ls or /magic

  • It doesn't check if the forged url actually exists.

  • Ah, but I don't want to use the vimdoc site - that's why I added the point about URL encoding. – muru Aug 16 '15 at 16:23
  • Oh I thought you was just stating that you used vimhelp but I didn't understood that you really didn't want to use vimdoc, sorry for that. Just out of curiousity why do you only want to refer to vimhelp and not vimdoc? – statox Aug 16 '15 at 16:33
  • vimdoc's problem is that a) its old and b) it's uncertain which characters need to be encoded. Some special characters work without it. I'd rather avoid that guessing. Besides, getting a vimdoc link is easy: the site searches tags, so its like using :he. Vimhelp uses Google, so the search is crap. – muru Aug 16 '15 at 16:37
  • Ok I hadn't noticed a real issue with the encoding on vimdoc, but it's good to know! I hope you'll get another answer because forging url for vimhelp indeed is not really easy to do ;-) – statox Aug 16 '15 at 16:42
3

This is a copy of my post at the meta question Is there a simpler way to link to the help?.


Update 20150825: an improved of this now lives at http://code.arp242.net/helplink.vim. It has a number of improvements, and you can install it as a plugin :-)

I will leave the original since it's a lot simpler and might be more helpful to show how this can be done.


I've been using this function; it will (backwards) look for the first tag, and return that in Markdown link. It also puts in the clipboard...

So you can just use :help from Vim, and when you want to link something use :echo Helplink().

" Make help link (markdown format)
" TODO: Find all tags, and let the user choose which one to use
fun! Helplink()
    " Get the name of the tag, With help from:
    " https://vi.stackexchange.com/questions/434/get-name-of-nearest-tag-to-the-cursor
    if !search('\*\zs[^*]\+\*$', 'bW')
        echoerr "No tag found"
    endif
    let l:line = getline('.')
    let l:start = col('.') - 1
    call search('\*', '', line('.'))
    let l:len =  col('.') - l:start - 1
    let l:tagname = strpart(l:line, l:start, l:len)

    " TODO: Also support Python 2 and maybe Ruby...
    let l:tagname_esc = system('python3 -c "import sys, urllib.parse as p;  empty=' . "''" . '; print(p.quote(sys.argv[1], safe=empty), end=empty)" ' . shellescape(b:tagname))
    let l:file = split(expand('%'), '/')[-1]
    let l:url = "http://vimhelp.appspot.com/" . l:file . ".html#" . l:tagname_esc
    let l:md = '[`:help ' . l:tagname . '`](' . l:url . ')'

    " Copy it to the clipboard
    let @+ = l:md

    return l:md
endfun
2

I managed to improve my original function to fulfil two of my objectives:

function! Help2UrlPy (word)
    setlocal tags=$VIMRUNTIME/doc/tags
    python <<EOF
import vim
from urllib import quote_plus
topmatch = vim.eval ('taglist(a:word)[0]')
filename = topmatch['filename'].split ('/')[-1]
tag = quote_plus (topmatch['name'])
url = 'http://vimhelp.appspot.com/{:s}.html#{:s}'.format (filename, tag)
vim.command ('let @* = "{:s}"'.format (url))
print url
EOF
endfunction
  • setlocal tags=$VIMRUNTIME/doc/tags lets me point to the tags file for the Vim documentation, thus excluding any plugin-generated help and sticking to the official docs.
  • vim.command ('let @* = "{:s}"'.format (url)) copies to the clipboard by assigning to the * register.

However, to get the correct tag I was forced to use another way:

function! Help2Url (...)
    exec 'silent! help ' . ( a:0 ? a:1 : '' )
    if ! a:0
        exec "normal \<c-T>"
    endif
    let l:tagfile = expand ('%:t')
    let @* = expand ('<cword>')
    python import urllib
    let @* = pyeval ('urllib.quote_plus ("' . @* . '")')
    let @* = printf ('http://vimhelp.appspot.com/%s.html#%s', l:tagfile, @*)
endfunction

command! -nargs=? -complete=help H call Help2Url(<f-args>)

Now, I just let Vim do the heavy lifting for me. When we open help using :h foo, the cursor is placed at the tag that Vim picked, and I get the word using <cword>. And if I run it without any arguments, :h opens the main help page, with the last search result in the tag stack. So, I jump back with <c-t>, and again, get the <cword>. All requirements fulfilled.

An older version of this used the tag stack, but the tag is not actually loaded to the tag stack when you do :he foo. And if I do search for the tag using <c-]>, my position will remain unchanged, but the tag stack will now have the tag I wanted as the last entry. So, I run help on the argument, search for the tag, and the manipulate the output of tags to get the last entry.

exec "normal \<c-]>"
let l:tagfile = expand ('%:t')
redir @*
silent! tags
redir END
let @* = split (split (@*, '\n\+')[-2], '\v(\s|\@)+')[2] 

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.