aboutsummaryrefslogtreecommitdiff
path: root/autoload/hints.vim
blob: 757fc5f270006f17d574a070538508cd873eb59f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
" 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