编辑器中转到包含scope的快捷键

发布时间 2023-09-09 17:12:56作者: tsecer

问题

众所周知,程序员大部分时间并不是在写代码而是在浏览和调试代码。对于一个大型项目,或者不是很熟悉的模块,浏览代码是通常需要各种跳转。

一个常见的场景时:当通过搜索(例如linux下的grep工具)或者ctag跳转到某个引用的时候,我们可能并不知道此时所在何处。比方说,是在哪个函数内,是在哪个类内部,甚至是在某个C++的lambda函数内。

此时就需要编辑器有一个功能,能够快速的告诉光标当前所处的scope/block信息。或者用类似于GPS的概念来说,就是告诉我当前我的位置在哪里。

visual assist

从visual assist中内置命令,其中有一些很有用但是之前被忽略的、默认配置了快捷键的功能。

在浏览代码的时候,经常使用的是Alt配合上左右箭头来进行前一个位置和后一个位置的跳转。和这个功能类似,Alt匹配的另外两个上下方向键却是之前忽略的命令:scope的导航。

Command Description Default Shortcut
ScopeNext Move to Next Scope Alt+Down Arrow †
ScopePrevious Move to Previous Scope Alt+Up Arrow †
SmartSelectExtend Smart Select to initiate or extend a selection Shift+Alt+] †
SmartSelectExtendBlock Smart Select to initiate or extend a block selection Alt+]
SmartSelectShrink Smart Select to decrease a selection Shift+Alt+[ †
SmartSelectShrinkBlock Smart Select to decrease a block selection Alt+[

visual studio

va毕竟是第三方插件,对于使用原汁原味的visual Studio的用户来说,可能更喜欢或者必须使用VS自带的快捷键功能,那么VS也提供了类似的功能。

SO上说明了这个对应功能的位置,可能是由于这个功能添加比较晚,简短的组合键已经被占用,所以这个功能使用了一个相对复杂并且没有VA直观的组合键。

I have a fresh install of VS2017. As of 15.9.1, the default for me is Alt+Shift+[.

This is the shortcut for EditorContextMenus.Navigate.GoToContainingBlock. So you may have to execute this shortcut multiple times if you are a few block layers deep, but it'll get you where you want to go.

不过,由于VA的很多功能其实也默认使用ALT+SHIFT的功能组合(例如,查找引用的ALT+SHIFT+F,ALT+SHIFT+O开文件搜索),所以其实使用这组快捷键也并不是很违和。

Expand selection Shift+Alt+=
Expand selection to containing block Shift+Alt+]

vim

由于vim主要是一个文本编辑器(text editor),本身并没有内置的语法分析功能,所以对于类似的block和选择功能支持相对来说可能更多,但是也更繁琐。
个人最为熟悉的还是使用方括弧来表达的block跳转,但是这种跳转明显没有VS自带block智能。

5. Text object motions                                  object-motions          
                                                                                
                                                        (                       
(                       [count] sentences backward.  exclusive motion.          
                                                                                
                                                        )                       
)                       [count] sentences forward.  exclusive motion.           
                                                                                
                                                        {                       
{                       [count] paragraphs backward.  exclusive motion.         
                                                                                
                                                        }                       
}                       [count] paragraphs forward.  exclusive motion.          
                                                                                
                                                        ]]                      
]]                      [count] sections forward or to the next '{' in the      
                        first column.  When used after an operator, then also   
                        stops below a '}' in the first column.  exclusive       
                        Note that exclusive-linewise often applies.             
                                                                                
                                                        ][                      
][                      [count] sections forward or to the next '}' in the      
                        first column.  exclusive                                
                        Note that exclusive-linewise often applies.             
                                                                                
                                                        [[                                                                                                                                   
[[                      [count] sections backward or to the previous '{' in     
                        the first column.  exclusive                            
                        Note that exclusive-linewise often applies.             
                                                                                
                                                        []                      
[]                      [count] sections backward or to the previous '}' in     
                        the first column.  exclusive            

matchit

vim的matchit扩展提供了相对原生的功能更加智能的,类似于VS的功能。它虽然不是vim默认使能的,但是它是vim自带的,也就是在vim的官方工程中是包含的,这也意味着这个功能其实是可以随时实时使用的。奇怪的是在我的版本中默认并没有这个功能。

  • 搜索路径
    在vim可执行文件构建的过程中,在可执行文件中预先包含了可执行文件的路径,在vim的ex模式下执行version命令,可以看到预制的搜索路径
fall-back for $VIM: "/home/harry/share/vim"

这个路径在构建

auto/pathdef.c: Makefile auto/config.mk                                             
        -@echo creating $@                                                          
        -@echo '/* pathdef.c */' > $@                                               
        -@echo '/* This file is automatically created by Makefile' >> $@            
        -@echo ' * DO NOT EDIT!  Change Makefile only. */' >> $@                    
        -@echo '#include "vim.h"' >> $@                                             
        -@echo 'char_u *default_vim_dir = (char_u *)"$(VIMRCLOC)";' | $(QUOTESED) >> $@
        -@echo 'char_u *default_vimruntime_dir = (char_u *)"$(VIMRUNTIMEDIR)";' | $(QUOTESED) >> $@
        -@echo 'char_u *all_cflags = (char_u *)"$(CC) -c -I$(srcdir) $(ALL_CFLAGS)";' | $(QUOTESED) >>  $@
        -@echo 'char_u *all_lflags = (char_u *)"$(CC) $(ALL_LIB_DIRS) $(LDFLAGS) -o $(VIMTARGET) $(ALL_LIBS) ";' | $(QUOTESED) >>  $@
        -@echo 'char_u *compiled_user = (char_u *)"' | tr -d $(NL) >> $@            
        -@if test -n "$(COMPILEDBY)"; then \                                        
                echo "$(COMPILEDBY)" | tr -d $(NL) >> $@; \                         
                else ((logname) 2>/dev/null || whoami) | tr -d $(NL) >> $@; fi  
        -@echo '";' >> $@                                                           
        -@echo 'char_u *compiled_sys = (char_u *)"' | tr -d $(NL) >> $@             
        -@if test -z "$(COMPILEDBY)"; then hostname | tr -d $(NL) >> $@; fi         
        -@echo '";' >> $@                                                           
        -@sh $(srcdir)/pathdef.sh            
  • packpath

通过packpath可以看到,确认matchit所在的文件夹也是在这个路径中的。

                                'packpath' 'pp'                                 
'packpath' 'pp'         string  (default: see 'runtimepath')                    
                        {not in Vi}                                             
        Directories used to find packages.  See packages.        
  • packages
    在packages帮助中,明确说明了在opt文件夹下的内容是不会被自动加载的,而只是会自动加载start文件夹下的内容。但是同时也说明了可以通过packadd命令
The directory name "foo" is arbitrary, you can pick anything you like.          
                                                                                
You would now have these files under ~/.vim:                                    
        pack/foo/README.txt                                                     
        pack/foo/start/foobar/plugin/foo.vim                                    
        pack/foo/start/foobar/syntax/some.vim                                   
        pack/foo/opt/foodebug/plugin/debugger.vim                               
                                                                                
When Vim starts up, after processing your .vimrc, it scans all directories in   
'packpath' for plugins under the "pack/*/start" directory.  First all those     
directories are added to 'runtimepath'.  Then all the plugins are loaded.       
See packload-two-steps for how these two steps can be useful.                   
                                                                                
In the example Vim will find "pack/foo/start/foobar/plugin/foo.vim" and adds    
"~/.vim/pack/foo/start/foobar" to 'runtimepath'.                                
                                                                                
If the "foobar" plugin kicks in and sets the 'filetype' to "some", Vim will     
find the syntax/some.vim file, because its directory is in 'runtimepath'.       
                                                                                
Vim will also load ftdetect files, if there are any.                            
                                                                                                                                                                                             
Note that the files under "pack/foo/opt" are not loaded automatically, only the 
ones under "pack/foo/start".  See pack-add below for how the "opt" directory    
is used.                                                                        
  • packadd

对于位于opt文件夹下的包,需要通过packadd来添加,这个帮助手册也说明了,它只会去opt文件夹查找。

Optional plugins                                                                
                                                        pack-add                
To load an optional plugin from a pack use the :packadd command:                
        :packadd foodebug                                                       
This searches for "pack/*/opt/foodebug" in 'packpath' and will find             
~/.vim/pack/foo/opt/foodebug/plugin/debugger.vim and source it.                 
                                                                                
This could be done if some conditions are met.  For example, depending on       
whether Vim supports a feature or a dependency is missing.                      
                                                                                
You can also load an optional plugin at startup, by putting this command in     
your .vimrc:                                                                    
        :packadd! foodebug                                                      
The extra "!" is so that the plugin isn't loaded if Vim was started with        
--noplugin.                                                                     
                                                                                                                                                                                             
It is perfectly normal for a package to only have files in the "opt"            
directory.  You then need to load each plugin when you want to use it.  
  • .vimrc

所以,为了自动使能该插件,需要在.vimrc文件中添加一个对于这个opt插件的添加

packadd matchit
  • script语法

vim的script帮助文档说明了其中的命令是Ex命令,并且这些命令开始的冒号是可以省略的(The ":" characters are not really needed here. You only need to use them when you type a command. In a Vim script file they can be left out),反过来说,就是添加了冒号也不会报错。

41.1    Introduction                            vim-script-intro script         
                                                                                
Your first experience with Vim scripts is the vimrc file.  Vim reads it when    
it starts up and executes the commands.  You can set options to values you      
prefer.  And you can use any colon command in it (commands that start with a    
":"; these are sometimes referred to as Ex commands or command-line commands).  
   Syntax files are also Vim scripts.  As are files that set options for a      
specific file type.  A complicated macro can be defined by a separate Vim       
script file.  You can think of other uses yourself.                             
                                                                                
Let's start with a simple example:                                              
                                                                                
        :let i = 1                                                              
        :while i < 5                                                            
        :  echo "count is" i                                                    
        :  let i += 1                                                           
        :endwhile                                                               
                                                                                
        Note:                                                                   
        The ":" characters are not really needed here.  You only need to use    
        them when you type a command.  In a Vim script file they can be left    
        out.  We will use them here anyway to make clear these are colon                                                                                                                     
        commands and make them stand out from Normal mode commands.             
        Note:                                                                   
        You can try out the examples by yanking the lines from the text here    
        and executing them with :@"                                            

vim实现中首先会跳过任意多的空白和冒号,之后如果遇到的是一个双引号则跳过整行,这意味着任意多的冒号可以在任意多的双引号之前(尽管这没有什么意义,但是是正确语法)。

    static char_u *
do_one_cmd(
    char_u		**cmdlinep,
    int			sourcing,
#ifdef FEAT_EVAL
    struct condstack	*cstack,
#endif
    char_u		*(*fgetline)(int, void *, int),
    void		*cookie)		/* argument for fgetline() */
{
///...
    for (;;)
    {
/*
 * 1. Skip comment lines and leading white space and colons.
 */
	while (*ea.cmd == ' ' || *ea.cmd == '\t' || *ea.cmd == ':')
	    ++ea.cmd;

	/* in ex mode, an empty line works like :+ */
	if (*ea.cmd == NUL && exmode_active
			&& (getline_equal(fgetline, cookie, getexmodeline)
			    || getline_equal(fgetline, cookie, getexline))
			&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
	{
	    ea.cmd = (char_u *)"+";
	    ex_pressedreturn = TRUE;
	}

	/* ignore comment and empty lines */
	if (*ea.cmd == '"')
	    goto doend;
	if (*ea.cmd == NUL)
	{
	    ex_pressedreturn = TRUE;
	    goto doend;
	}
///...
}
  • 验证

vim内置命令scriptnames可以显示所以执行过的脚本文件,还可以通过function来显示所有函数功能。

                                                :scr :scriptnames               
:scr[iptnames]          List all sourced script names, in the order they were   
                        first sourced.  The number is used for the script ID    
                        <SID>.                                                  
                        {not in Vi} {not available when compiled without the    
                        +eval feature}                                          
  • 效果

(跟VS/VX相比)不是很智能,但是还是比内置的[[ ]] 方便一点。