aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <rahm@google.com>2022-08-09 00:33:40 -0600
committerJosh Rahm <rahm@google.com>2022-08-09 00:33:40 -0600
commit54ce931f0bcede9affde3a6934656fa6d4ad9961 (patch)
tree29d6c48b81f6ee5a5206fe5f97a52c5530fae537
parent3340b11176d467961ae8426091d53c6ad1a01d2c (diff)
downloadfieldmarshal.vim-54ce931f0bcede9affde3a6934656fa6d4ad9961.tar.gz
fieldmarshal.vim-54ce931f0bcede9affde3a6934656fa6d4ad9961.tar.bz2
fieldmarshal.vim-54ce931f0bcede9affde3a6934656fa6d4ad9961.zip
Add hints.vim
Hints work like qutebrowser. <C-f> will enter hint mode, which will prompt the user to type a two-letter sequence to jump the cursor to a specific line.
-rw-r--r--autoload/hints.vim107
-rw-r--r--autoload/hints/plugins.vim59
-rw-r--r--plugin/hints.vim3
3 files changed, 169 insertions, 0 deletions
diff --git a/autoload/hints.vim b/autoload/hints.vim
new file mode 100644
index 0000000..e7b9a1b
--- /dev/null
+++ b/autoload/hints.vim
@@ -0,0 +1,107 @@
+" Display qutebrowser-like hints.
+
+" Max number of tags is 105, which is LCM(21, 5)
+let s:cons = "bcdfghjklmnpqrstvwxyz" " 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 plugin = hints#plugins#getPluginForFiletype(&ft)
+ call plugin.Before(expand("%"))
+
+ let ci = 0
+ let vi = 0
+
+ let line = line('w0')
+ let endline = line('w$')
+
+ let hints = {}
+
+ while line <= endline
+ if plugin.TagLine(line, getline(line))
+ let tag=printf("%s%s",
+ \ s:cons[ci % len(s:cons)], s:vowel[vi % len(s:vowel)])
+ let hints[tag] = line
+
+ " Advance _both_ the consonants and vowels. We want the tags to generally
+ " have as little in common as possible.
+ let ci += 1
+ let vi += 1
+ endif
+
+ let line += 1
+ endwhile
+
+ return hints
+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=LineNr", 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! 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
+
+ let c2 = getchar()
+
+ call s:cleanup_hints()
+
+ let line = get(hints, nr2char(c1) . nr2char(c2), -1)
+
+ if line >= 0
+ norm m'
+ if len(a:visual)
+ exec "norm " . a:visual
+ endif
+ call cursor(line, 1)
+ norm ^
+ endif
+endfunction
diff --git a/autoload/hints/plugins.vim b/autoload/hints/plugins.vim
new file mode 100644
index 0000000..de8cc6c
--- /dev/null
+++ b/autoload/hints/plugins.vim
@@ -0,0 +1,59 @@
+let s:ftplugins = {}
+let s:default_plugin = {}
+
+let s:vim_plugin = {}
+function! s:vim_plugin.Before(file)
+endfunction
+function! s:vim_plugin.TagLine(linenr, line)
+ return a:line =~ '\<\(if\|function\|return\|end\w*\|while\|for\|let\|else\|try\)\>'
+endfunction
+
+let s:java_plugin = {}
+
+function! s:java_plugin.Before(file) dict
+ let self.last_line = ''
+endfunction
+
+let s:WHITESPACE_OR_COMMENT='\(^\s*$\)\|\(^\s*//\)\|\(^\s*\*/\)'
+function! s:java_plugin.TagLine(linenr, line) dict
+ if self.last_line =~ s:WHITESPACE_OR_COMMENT
+ \ && !(a:line =~ s:WHITESPACE_OR_COMMENT)
+ let self.last_line = a:line
+ return v:true
+ endif
+ let self.last_line = a:line
+
+ return
+ \ a:line =~ '^\s*}$' ||
+ \ a:line =~ '\<\(public\|private\|protected\|class\|static\|try\|while\|for\|if\|else\|catch\)\>'
+endfunction
+
+call hints#plugins#registerFt("vim", s:vim_plugin)
+call hints#plugins#registerFt("java", s:java_plugin)
+
+function! hints#plugins#registerFt(filetype, plugin) abort
+ let s:ftplugins[a:filetype] = a:plugin
+endfunction
+
+function! hints#plugins#getPluginForFiletype(filetype) abort
+ return get(s:ftplugins, a:filetype, s:default_plugin)
+endfunction
+
+function! hints#plugins#getDefaultPlugin() abort
+ return s:default_plugin
+endfunction
+
+function! s:default_plugin.Before(file)
+ let self.last_line = ''
+endfunction
+
+let s:ISSPACE= '^\s*$'
+function! s:default_plugin.TagLine(linenr, line)
+ if self.last_line =~ s:ISSPACE && !(a:line =~ s:ISSPACE)
+ let self.last_line = a:line
+ return v:true
+ endif
+
+ let self.last_line = a:line
+ return v:false
+endfunction
diff --git a/plugin/hints.vim b/plugin/hints.vim
new file mode 100644
index 0000000..47282ad
--- /dev/null
+++ b/plugin/hints.vim
@@ -0,0 +1,3 @@
+noremap <silent> <C-f> :<C-u>call hints#runHints("")<cr>
+onoremap <silent> <C-f> :<C-u> call hints#runHints("V")<cr>
+vnoremap <silent> <C-f> :<C-u> call hints#runHints("V")<cr>