preservim / tagbar

Vim plugin that displays tags in a window, ordered by scope
https://preservim.github.io/tagbar
Other
6.12k stars 486 forks source link

Use `<CR>` to toggle fold and open file at the same time #778

Closed huangzonghao closed 3 years ago

huangzonghao commented 3 years ago

Is it possible to use <CR> to open/jump to a location when the cursor is on a tag, and toggle the fold when the cursor is on a fold?

raven42 commented 3 years ago

This is not possible with the current code.

However this could be added with a caveat. This will only work when the cursor is on lines that are not a scoped tag kind. For example, if we have a .cpp file like the following with the cursor in the tagbar window on the A : class line: image

Then if we hit <CR> it will cause it to jump to the tag, not fold/unfold the class.

The code to do this would be as follows. If this is desired, we can look at putting this into the tagbar code.

diff --git a/autoload/tagbar.vim b/autoload/tagbar.vim
index 87324e0..39e8895 100644
--- a/autoload/tagbar.vim
+++ b/autoload/tagbar.vim
@@ -2333,6 +2333,23 @@ function! s:JumpToTag(stay_in_tagbar) abort
     let autoclose = w:autoclose

     if empty(taginfo) || !taginfo.isNormalTag()
+        " Cursor line not on a tag. Check if this is the start of a foldable
+        " line and if so, initiate the CloseFold() / OpenFold(). First trim
+        " any whitespace from the start of the line so we don't have to worry
+        " about multiple nested folds that are indented
+        "
+        " NOTE: This will only work with folds that are not also tags. For
+        " example in a class definition that acts as the start of a fold when
+        " there are member functions in the class, that line is also a valid
+        " tag, so hitting <CR> on that line will cause it to jump to the tag,
+        " not fold/unfold that particular fold
+        let line   = substitute(getline('.'), '^\s*\(.\{-}\)\s*$', '\1', '')
+
+        if (match(line, g:tagbar#icon_open . '[-+ ]')) == 0
+            call s:CloseFold()
+        elseif (match(line, g:tagbar#icon_closed . '[-+ ]')) == 0
+            call s:OpenFold()
+        endif
         return
     endif

Another method which may also serve your purposes a little better is changing the key mapping in your .vimrc to use the <Left> and <Right> arrow keys to close and open folds. This means you can't move the cursor left and right within the tagbar window, but does make opening/closing folds a little easier. Otherwise there is the default mappings to open/close folds of +/- or zo/zc. Or there is also the vim default of za to toggle a fold.

.vimrc

    let g:tagbar_map_openfold = ['+', '<kPlus>', 'zo', '<Right>']
    let g:tagbar_map_closefold = ['-', '<kMinus>', 'zc', '<Left>']
huangzonghao commented 3 years ago

Thanks! I just tried the code and it worked as expected! And I totally understand that the caveat is kind of unavoidable given the fact that a fold can be a tag at the current setup. I guess from a software engineering point of view, we could either separate the two roles, so that the first tag under a class fold is always the tag to the class itself, or give reasonable priorities to the behaviors of different key mappings, e.g. g:tagbar_map_jump only treats a line as a fold when it can't be a tag, and g:tagbar_map_togglefold always considers the line to be a fold first.

raven42 commented 3 years ago

Trying to treat the <CR> key as a fold first would lead to inconsistent behavior. If a line is a valid tag but does not have any hierarchy under it, then it would cause a jump. If the line is a valid tag, but does have any hierarchy under it, then it would fold. Consider the case of an empty class and a non-empty class. If you hit enter on the non-empty class, it would fold/unfold that class definition in the tagbar window. If you hit enter on the empty class, it would jump to that tag. Now if you added something to that empty class and hit enter on it, now it would switch behavior to toggle the fold. It is not very intuitive unless you pay very close attention. We would be changing the behavior based on something happening to the file which can lead to confusion.

It would be more consistent to stick with a more simple definition of if enter is hit on a valid tag, it jumps to the tag. If it is hit on a line that does not have a valid tag, it will toggle the fold. This is more clearly defined and can be explained easily and understood easily.

Also, hitting <CR> to toggle a fold is not very vim like. All other folding and such use other key strokes such as za/zo/zc to toggle/open/close a fold. I feel we're ok to add the toggle fold when <CR> is hit on a line that does not contain a tag, but I don't want to change the behavior (even through a config option) when the cursor is on a line that does have a valid tag.