1

I'm looking for functionality which may or may not exist. Basically, I'd like to have a location list split at the top of each writable buffer which shows the last line of each enclosing indentation level. So for the code:

def some_function(x, y, z):
    some code
    some more code
    even more code
    if var == 3:
        do something
        do another_thi|g

If my cursor were at |, I would see in this window something like

> def some_function(x, y, z):
>    ...
>    if var == 3

If this doesn't exist, could such a plugin even be made for Vim (bear with me; I'm not a real programmer)? It seems like it would be very useful for orienting oneself in one's code after e.g., coming back from the coffee pot.

  • Are you looking for :help folds ? – D. Ben Knoble May 23 at 19:44
  • I'm aware of folds, and I use them, however I think what I'm thinking of is a little different (though I hadn't thought of them in this context when I posed the question). I'd like a separate window that automatically shows enclosing folds as I move my cursor about. – mnosefish May 23 at 19:48
  • I guess i just dont see why not use set foldmethod=indent—are you looking for more of a “code outline” ? Can you edit to clarify how folds don’t satisfy your need and explain more clearly what you desire? – D. Ben Knoble May 23 at 19:49
  • @D.BenKnoble I’m guessing the OP is looking for more context to say “this is how execution ends up in this block of code”. I think fdm=indent differs fundamentally from what they describe because I think in that, if the inner fold is open then the outer ones must be too? (Correct me if I’m wrong: I’m not at a computer right now.) – Rich May 23 at 22:20
  • 1
    It's not exactly a preview window as you described but Vista.vim allows you to get the name of the nearest function in your statusline and coc.nvim allows to do the same maybe before developing your own plugin it could be worth it to look at them :) – statox May 24 at 7:23
1

I was intending to write an answer explaining how you might go about doing this, but when I embarked on doing so I ended up with a prototype implementation instead. Writing code is often more fun than writing documentation, I'm afraid!

function! ShowContext() abort
  let items = []
  let l = line('.')
  let indent = 1000
  while l >= 0
    if indent(l) < indent
      call add(items, getline(l))
      let indent = indent(l)
      if indent == 0
        break
      endif
    endif
    let l -= 1
  endwhile
  lgetexpr reverse(items)
  above lopen
  wincmd p
endfunction

augroup ContextList
  autocmd!
  autocmd CursorHold * call ShowContext()
augroup END

There are a lot of rough edges you'll need to file off, but hopefully this shows that your goal is certainly attainable. Let me know in the comments if there's anything in particular you don't understand or struggle with improving, and I'll be happy to explain more.

  • Thanks! I will try that as soon as I get a moment! – mnosefish May 28 at 19:45
  • I just got around to trying this and it is exactly what I needed! Is there a way to toggle the autocommand on and off? I have <leader>lo set to toggle the location list, but this doesn't seem to work, probably because the location list is being opened from within the function. – mnosefish May 31 at 14:05
  • Simplest fix is to just remove the line above lopen from the function, but then it's still updating the location list: just not opening it. Cleanest way to toggle the autocommand is to remove it entirely with autocmd! ContextList and then recreate it when you need it with the code already in the answer. – Rich May 31 at 14:14
  • I have a bead on getting a toggle to work. Also, it's doing a thing where it resets the cursor to the beginning of the line when the autocommand fires. Any ideas on that? – mnosefish May 31 at 14:19
  • The cursor movement is because I mistakenly used lexpr at the end of the function. It should be lgetexpr. – Rich May 31 at 15:12
1

Expanding on Rich's answer, here is my ready-to-roll solution with keymap and ignoring blank lines.

function! ShowContext() abort
  let items = []
  let l = line('.')
  let indent = 1000
  while l >= 0
    " added test to ignore blank lines
    if indent(l) < indent && getline(l) != ''
      call add(items, getline(l))
      let indent = indent(l)
      if indent == 0
        break
      endif
    endif
    let l -= 1
  endwhile
  lgetexpr reverse(items)
endfunction

let w:contextlist_open = 0
function! Toggle_contextlist()
    if w:contextlist_open == 1
        let w:contextlist_open = 0
        autocmd! ContextList
        above lclose
    else
        let w:contextlist_open = 1
        augroup ContextList
            autocmd!
            autocmd CursorHold * call ShowContext()
        augroup END
        above lopen
        wincmd p
    endif
endfunction
nnoremap <leader>lc :call Toggle_contextlist()<CR>

autocmd BufWinEnter * let w:locallist_open = 0
autocmd BufWinEnter * let w:contextlist_open = 0

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.