/vim-twoslash-queries

Pin semantic information with comments

Primary LanguageVim ScriptMIT LicenseMIT

vim-twoslash-queries

(Neo)Vim port of vscode-twoslash-queries, uses ^? comments to "pin" YCM symbol info. It should work with all languages for which you have semantic completer installed.

Screen recording 2023-01-15 19

Usage

Under a line with a symbol you would like to preview, add a comment with ^? pointing somewhere at the symbol. Examples:

const x = 1 + 2;
//    ^?
def add(a, b):
    return a + b

add(1, 2)
#^?

Then run TwoslashQueriesUpdate, invoke <Plug>(TwoslashQueriesUpdate) or save the file with onsave enabled. The above becomes:

const x = 1 + 2;
//    ^?:  const x: number
def add(a, b):
    return a + b

x = add(1, 2)
    #^?:  def add(a, b)

You can have multiple such comments in the file (that's the whole point!). Aside from whitespace, there should be nothing on the line before the comment and between the comment character(s) and ^?.

Installation

With vim-plug add to your .vimrc:

call plug#begin()
Plug 'ycm-core/YouCompleteMe'
Plug 'dkaszews/vim-twoslash-queries'
call plug#end()

Configuration

All configuration is done via twoslash_queries_config dictionary. At global level, they are grouped by &filetype with * used as fallback. At buffer level, they all apply to current &filetype. Options are looked up in the following order:

  1. If b:twoslash_queries_config exists and b:twoslash_queries_config[optname] exists, return it.
  2. If g:twoslash_queries_config[&filetype] exists and b:twoslash_queries_config[&filetype][optname] exists, return it.
  3. Return twoslash_queries_config['*'][optname].

The global config is merged with a default one for any &filetypes that are not defined, and for any options in * that are not defined. You can add your own options to it for use with custom functions, but it is recommended to add some prefix (e.g. 'my_') to avoid collisions with future updates.

Examples:

" In Python, pin documentation instead of default `GetHover`
let g:twoslash_queries_config = { 'python': { 'commands': [ 'GetDoc' ] } }

" In all languages, enable automatic update on save
let g:twoslash_queries_config = { '*': { 'onsave': 1 } }

" Disable automatic update on save in current buffer only
let b:twoslash_queries_config = { 'onsave': 0 }

Options

function

Function to call when querying a symbol. Takes array of cursor coordinates pointed by the ^? (previous line, column of '^'), returns a string.

function ([lnum: number, col: number]): string, default twoslash_queries#invoke_ycm_at

Examples:

function! g:DictionaryLookup(cursor)
    let l:word = twoslash_queries#invoke_at(a:cursor, {-> expand('<cWORD>')})
    return get(g:my_dictionary, l:word, 'No definition')
endfunction

let g:twoslash_queries_config['markdown'] = { 'function': 'g:DictionaryLookup' }

comment

Character(s) denoting a single line comment. For example, in C-like languages it is //, in Python, bash and PowerShell it #.

string, default '//', Python '#'

ycm_cmds

Array of YcmCompleter subcommands in order of preference. First subcommand to return a non-empty result is used, errors are silenced.

string array, default [ 'GetHover', 'GetType', 'GetDoc' ]

onsave

Run TwoslashQueriesUpdate automatically whenever the buffer is saved.

bool, default 0

Commands and functions

All functions are prefixed with twoslash_queries#. All commands are prefixed with TwoslashQueries and have a <Plug>(...) normal mapping with the same name.

TwoslashQueriesUpdate, twoslash_queries#update()

Update all pins with queries, as shown in usage.

twoslash_queries#update_on_save()

Update if onsave is enabled for current buffer/filetype. This is used by a BufPostWrite autocommand.

twoslash_queries#get_opt(name)

Return value of option name with the same lookup rules described in configuration.

twoslash_queries#invoke_at(cursor, fun, args...)

Invoke given function at given cursor coordinates, then restore the cursor and window scroll to original. If cursor is an empty array, the cursor is not moved, but any movement by fun is still undone.

Examples:

" Append ' test' at 123th line, 10th column
call twoslash_queries#invoke_at([123, 10], 'execute', 'normal a test')

" Return documentation for symbol located at beginning of 50th line
let l:doc = twoslash_queries_invoke_at([50, 1], 'youcompleteme#GetCommandResponse', 'GetDoc')

twoslash_queries#invoke_ycm_at(cursor)

Invoke preferred YCM command at given cursor coordinates, with preference described in options/commands.

FAQ

Do I need YCM, or can I use a different completer plugin?

YCM is configured out of the box, but you can easily use a different plugin by overriding function option. For example, you can use coc.nvim by providing a wrapper around CocAction('definitions'). Feel free to create a PR if you think others could find it useful!

Why TwoslashQueriesUpdate freezes vim for a second?

Current implementation is blocking, as the cursor needs to jump around. This may be more noticeable in bigger files and with more pins.

Why my buffer never shows it is saved?

The query result is appended as a comment, so if you have onsave enabled, the buffer will get modified immediately after getting saved. This should have no real impact on your workflow.

Can I add multiple pins for a single line?

Currently not, but it is on the roadmap.

Can the pin adjust to the symbol as it moves around?

Currently not, but alternative pin syntax to work around this is on the roadmap.

How can I quickly see query result that is off the screen?

Ability to display query results in a separate buffer is on the roadmap.