How can I convince Vim or Neovim to show the enclosing namespace, class, and function that the cursor is in?

Some programming languages have no interesting namespaces or class names. In general, this question is about displaying whatever enclosing context exists for relevant programming language.

If there's no general solution then I'm also interested in C and C++ specifically.

  • 2
    tagbar should give you what you want and works with most languages that ctags understands. You'll want to use universal ctags instead of exuberant ctags since it's actively maintained. – Tommy A Apr 2 '17 at 21:35

The following snippet maps CTRL-g to show the current "context" (usually the current function):

nnoremap <C-g> :echo getline(search('\v^[[:alpha:]$_]', "bn", 1, 100))<CR>

This is the same hack used by git uses to show context hints. Credit to lost.vim for the idea.

As Tommy A mentioned, a tags-based solution like tagbar is more robust. And the most robust solution would be to use a real code-inspection engine, e.g. LSP).

| improve this answer | |
  • This'll work as long as sub-definitions (classes within namespaces, or functions within classes) aren't indented. It'll hardly work in real C++ code where functions are defined inline within class definitions. – Luc Hermitte May 29 '17 at 12:14
  • As mentioned, a robust solution requires robust inspection. This is a one-line hack. – Justin M. Keyes May 30 '17 at 20:24

Discl: Not an answer that will help you much.

A very long time ago, I wrote a ftplugin able to extract current scope and display it in the status line ; I have a trace of it somewhere in lh-cpp. It was slow as hell, and thus I have never really used it. In order to continously display an up-to-date context in the status line, we should be able to improve its performances a little bit thank to list functions (map()...), but I'm not very confident.

Another approach is to display the information on-demand. In that case performances aren't critical as they are regarding the statusline. Yet, today I would rely on lh-cpp and lh-dev where I've already defined everything to extract the current scope namespace(s) + class(es) + function.

It could look like:

" autoload/lh/cpp/analyse.vim (unpublished yet)
" Function: lh#cpp#analyse#context([line]) {{{3
function! lh#cpp#analyse#context(...) abort
  let line = get(a:, 1, line('.'))
  let fn = lh#dev#find_function_boundaries(line)
  if fn.lines[0] <= line && line <= fn.lines[1]
    " This is a function
    call lh#assert#value(fn).has_key('fn')
    let kinds = filter(['struct', 'class', 'namespace'], 'has_key(fn.fn, v:val)')
    call lh#assert#value(kinds).not().empty()
    let scope = join(
          \ [ get({'public': '+', 'private': '-', 'protected': '#'}, get(fn.fn, 'access', ''), '')
          \ , substitute(get(fn.fn, 'typeref', ''), '^typename:', '', '')
          \ , fn.fn[kinds[0]] . '::' . fn.fn.name . get(fn.fn, 'signature')
          \ ], ' ')
    " classes, structs, namespaces
    let scope = lh#cpp#AnalysisLib_Class#CurrentScope(line, 'any')
  return scope

" ftplugin/c/c_show_context.vim (unpublished yet)
nnoremap <silent> <buffer> <localleader>sc :echo lh#cpp#analyse#context()<cr>

Note: this almost works correclty on code such as this one: https://github.com/LucHermitte/NamedParameter/blob/master/include/named-parameters.hpp. We are now restricted by (universal!) ctags ability to understand C++(98/11/14/17/...) constructs.

Internally it extracts the current function boundaries (it launches ctags to do it correctly in C++), and the current scope (multiple and recursive calls to searchpair ()). I use it in my :GOTOIMPL feature.

May be we could achieve something faster nowadays thanks to clang. I don't know if some people have explored this path.

| improve this answer | |

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.