" Display qutebrowser-like hints. " Max number of tags is 105, which is LCM(21, 5) because 21 and 5 are coprime let s:cons = "tnshrdlcumwfgypkbvjxqz" " 21 let s:vowel = "aeiou" " 5 let s:signs_to_unplace = [] let s:signs_to_undefine = [] let s:sign_index = 5555 if !exists('g:hints_ns') let g:hints_ns = nvim_create_namespace("") endif " Generate hints on the current file. This may use specific plugins for each " filetype. function! s:generate_hints() abort let marklist = getmarklist(bufnr()) let plugin = hints#plugins#getPluginForFiletype(&ft) call plugin.Before(expand("%")) let cur_line = line('.') let line = 1 let hints = {} let tag_hints = {} for i in marklist let tag_hints[i.mark] = i.pos[1] endfor let hints_left = len(s:vowel) * len(s:cons) let last_line = line('$') let cnt = 0 while line <= last_line if plugin.TagLine(line, getline(line)) let tag=printf("%s%s", \ s:cons[cnt % len(s:cons)], s:vowel[cnt % len(s:vowel)]) let cnt += 1 " We want to keep the hints closest to the cursor line. if !has_key(hints, tag) || abs(cur_line - hints[tag]) > abs(cur_line - line) let hints[tag] = line endif endif let line += 1 endwhile return extend(tag_hints, hints, 'force') endfunction function! s:display_hints(hints) abort let s = s:sign_index for [tag, line] in items(a:hints) call add(s:signs_to_undefine, "tag_" . tag) call add(s:signs_to_unplace, s) "call nvim_buf_set_virtual_text( " \ 0, g:hints_ns, line - 1, [[tag, "Number"]], {}) exec printf("sign define tag_%s text=%s texthl=Number", tag, tag) exec printf("sign place %d line=%d name=tag_%s file=%s", s, line, tag, expand('%:p')) let s += 1 endfor endfunction function! s:cleanup_hints() abort call nvim_buf_clear_namespace(0, g:hints_ns, 0, -1) for s in s:signs_to_unplace try exec printf("sign unplace %d", s) catch endtry endfor for s in s:signs_to_undefine try exec printf("sign undefine %s", s) catch endtry endfor endfunction function! s:get(hint_dict, str) abort if a:str =~ '^[A-Z]\+$' let [a, _] = s:get(a:hint_dict, tolower(a:str)) return [a, "^"] endif if has_key(a:hint_dict, a:str) return [a:hint_dict[a:str], col('.') . '|'] endif if len(a:str) == 1 || a:str[0] == a:str[1] let [a, _] = s:get(a:hint_dict, "'" . a:str[0]) return [a, "`" . a:str[0]] endif return [-1, ''] endfunction function! hints#runHints(visual) abort let hints = s:generate_hints() call s:display_hints(hints) redraw let c1 = getchar() if c1 == 0x1b " Escape call s:cleanup_hints() return endif if stridx(s:cons, tolower(nr2char(c1))) >= 0 || nr2char(c1) == "'" let c2 = getchar() else let c2 = "" endif call s:cleanup_hints() let [line, act] = s:get(hints, nr2char(c1) . nr2char(c2)) if line >= 0 if a:visual == 'o' norm! V endif norm m' call cursor(line, 1) if len(act) > 0 exec "norm! " . act endif endif endfunction