首页 > 其他分享 >YCM中previewwindow显示函数类型信息如何实现

YCM中previewwindow显示函数类型信息如何实现

时间:2024-10-16 21:13:13浏览次数:11  
标签:completion info 类型信息 extra window previewwindow YCM preview data

intro

在使用YCM的自动提示功能时,可以注意到选择complete提供的条目时,窗口的上面还有一个小窗口提示这个函数的声明信息,包括了函数的参数列表和类型信息。

这个对写代码非常有用,对于一段时间不看的函数,很容易记不得函数的参数列表和各自的类型信息,以至于在官方issue中希望提供一个可以显示指定函数原型的功能:Could Ycm display the function prototype on the cmdline

在具体的使用过程中,经常会发现因为函数参数是另一个函数返回值,可能就会导致preview中的函数原型信息丢失;或者代码写完之后,preview窗口不会自动关闭。

那么YCM的这个功能是如何实现的呢?preview window又有哪些特性呢?

preview window

特性

  • 唯一

previewwindow的帮助手册说明了previewwindow的一个重要特性:这种类型(具有这个属性)的window最多只能有一个(Only one window can have this option set)。

'previewwindow' 'pvw' boolean (default off)
local to window local-noglobal
{not available when compiled without the +quickfix
feature}
Identifies the preview window. Only one window can have this option
set. It's normally not set directly, but by using one of the commands
:ptag, :pedit, etc.

当有一个previewwindow已经打开的时候,在另一个窗口执行

E590: A preview window already exists: pvw

这也对应了通过pclose命令不需要参数即可关闭窗口。

/*
 * ":pclose": Close any preview window.
 */
    static void
ex_pclose(exarg_T *eap)
{
    win_T	*win;

    // First close any normal window.
    FOR_ALL_WINDOWS(win)
	if (win->w_p_pvw)
	{
	    ex_win_close(eap->forceit, win, NULL);
	    return;
	}
# ifdef FEAT_PROP_POPUP
    // Also when 'previewpopup' is empty, it might have been cleared.
    popup_close_preview();
# endif
}
  • 光标

当打开窗口时,光标不会自动转移到新打开的preview窗口。

:ped[it][!] [++opt] [+cmd] {file}
Edit {file} in the preview window. The preview window is
opened like with :ptag. The current window and cursor
position isn't changed. Useful example:
:pedit +/fputc /usr/include/stdio.h

关闭

SO上关于如何关闭preview的答案

The top window is called the preview window. So any of z, or :pc[lose][!] should work.

The below is the help for :help :pclose

CTRL-W z CTRL-W_z
CTRL-W CTRL-Z CTRL-W_CTRL-Z
:pc :pclose
:pc[lose][!] Close any "Preview" window currently open. When the 'hidden'
option is set, or when the buffer was changed and the [!] is
used, the buffer becomes hidden (unless there is another
window editing it). The command fails if any "Preview" buffer
cannot be closed. See also :close.
Another relevant help page would be :help preview-window

vim

doc

在vim的complete-items帮助中,描述了不同的约定的字典键值(key)对应的意义,其中提到了info对应的内容可能在previewwindow或者popupwindow中显示。

                                           complete-items  

Each list item can either be a string or a Dictionary. When it is a string it
is used as the completion. When it is a Dictionary it can contain these
items:
word the text that will be inserted, mandatory
abbr abbreviation of "word"; when not empty it is used in
the menu instead of "word"
menu extra text for the popup menu, displayed after "word"
or "abbr"
info more information about the item, can be displayed in a
preview or popup window
kind single letter indicating the type of completion
icase when non-zero case is to be ignored when comparing
items to be equal; when omitted zero is used, thus
items that only differ in case are added
equal when non-zero, always treat this item to be equal when
comparing. Which means, "equal=1" disables filtering
of this item.
dup when non-zero this match will be added even when an
item with the same word is already present.
empty when non-zero this match will be added even when it is
an empty string
user_data custom data which is associated with the item and
available in v:completed_item; it can be any type;
defaults to an empty string

source

将complete提供的信息转移到vim的C结构中。

///@file: insexpand.c
/*
 * Build a popup menu to show the completion matches.
 * Returns the popup menu entry that should be selected. Returns -1 if nothing
 * should be selected.
 */
    static int
ins_compl_build_pum(void)
{
///....
		compl_match_array[i].pum_text = compl->cp_str;
	    compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND];
	    compl_match_array[i].pum_info = compl->cp_text[CPT_INFO];
	    compl_match_array[i].pum_score = compl->cp_score;
///....
}

当选择某个complete选项时,vim(的C代码)会自动在previewwindow中更新对应的info信息。

///@file:popupmenu.c
		    for (p = pum_array[pum_selected].pum_info; *p != NUL; )
		    {
			e = vim_strchr(p, '\n');
			if (e == NULL)
			{
			    ml_append(lnum++, p, 0, FALSE);
			    break;
			}
			*e = NUL;
			ml_append(lnum++, p, (int)(e - p + 1), FALSE);
			*e = '\n';
			p = e + 1;
		    }

ycm

plugin

将completion.completions信息提供给vim内置的complete函数。

"@file: YouCompleteMe/autoload/youcompleteme.vim
function! s:Complete()
  " It's possible for us to be called (by our timer) when we're not _strictly_
  " in insert mode. This can happen when mode is temporarily switched, e.g.
  " due to Ctrl-r or Ctrl-o or a timer or something. If we're not in insert
  " mode _now_ do nothing (FIXME: or should we queue a timer ?)
  if count( [ 'i', 'R' ], mode() ) == 0
    return
  endif

  if s:completion.line != line( '.' )
    " Given
    "   scb: column where the completion starts before auto-wrapping
    "   cb: cursor column before auto-wrapping
    "   sca: column where the completion starts after auto-wrapping
    "   ca: cursor column after auto-wrapping
    " we have
    "   ca - sca = cb - scb
    "   sca = scb + ca - cb
    let s:completion.completion_start_column +=
          \ col( '.' ) - s:completion.column
  endif
  if len( s:completion.completions )
    let old_completeopt = &completeopt
    set completeopt+=noselect
    call complete( s:completion.completion_start_column,
                 \ s:completion.completions )
    let &completeopt = old_completeopt
  elseif pumvisible()
    call s:CloseCompletionMenu()
  endif
endfunction

client

ycm客户端从ycmd的回包中获得detailed_info字段,填充到info字段中。

# @file:YouCompleteMe/python/ycm/client/completion_request.py
def _GetCompletionInfoField( completion_data ): 
  info = completion_data.get( 'detailed_info', '' )
      
  if 'extra_data' in completion_data:
    docstring = completion_data[ 'extra_data' ].get( 'doc_string', '' )
    if docstring:
      if info:
        info += '\n' + docstring
      else:
        info = docstring

  # This field may contain null characters e.g. \x00 in Python docstrings. Vim
  # cannot evaluate such characters so they are removed.
  return info.replace( '\x00', '' )
def ConvertCompletionDataToVimData( completion_data ):
  # See :h complete-items for a description of the dictionary fields.
  extra_menu_info = completion_data.get( 'extra_menu_info', '' )
  preview_info = _GetCompletionInfoField( completion_data )

  # When we are using a popup for the preview_info, it needs to fit on the
  # screen alongside the extra_menu_info. Let's use some heuristics.  If the
  # length of the extra_menu_info is more than, say, 1/3 of screen, truncate it
  # and stick it in the preview_info.
  if vimsupport.UsingPreviewPopup():
    max_width = max( int( vimsupport.DisplayWidth() / 3 ), 3 )
    extra_menu_info_width = vimsupport.DisplayWidthOfString( extra_menu_info )
    if extra_menu_info_width > max_width:
      if not preview_info.startswith( extra_menu_info ):
        preview_info = extra_menu_info + '\n\n' + preview_info
      extra_menu_info = extra_menu_info[ : ( max_width - 3 ) ] + '...'

  return {
    'word'     : completion_data[ 'insertion_text' ],
    'abbr'     : completion_data.get( 'menu_text', '' ),
    'menu'     : extra_menu_info,
    'info'     : preview_info,
    'kind'     : ToUnicode( completion_data.get( 'kind', '' ) )[ :1 ].lower(),
    # Disable Vim filtering.
    'equal'    : 1,
    'dup'      : 1,
    'empty'    : 1,
    # We store the completion item extra_data as a string in the completion
    # user_data. This allows us to identify the _exact_ item that was completed
    # in the CompleteDone handler, by inspecting this item from v:completed_item
    #
    # We convert to string because completion user data items must be strings.
    #
    # Note: Not all versions of Vim support this (added in 8.0.1483), but adding
    # the item to the dictionary is harmless in earlier Vims.
    # Note: Since 8.2.0084 we don't need to use json.dumps() here.
    'user_data': json.dumps( completion_data.get( 'extra_data', {} ) )
  }  

ycmd

ycmd的Python脚本从llvm中获得detailed_info信息,对应ycm_core中的DetailedInfoForPreviewWindow字段。

#@file: YouCompleteMe/third_party/ycmd/ycmd/completers/cpp/clang_completer.py
def ConvertCompletionData( completion_data ):
  return responses.BuildCompletionData(
    insertion_text = completion_data.TextToInsertInBuffer(),
    menu_text = completion_data.MainCompletionText(),
    extra_menu_info = completion_data.ExtraMenuInfo(),
    kind = completion_data.kind_.name,
    detailed_info = completion_data.DetailedInfoForPreviewWindow(),
    extra_data = BuildExtraData( completion_data ) )

在ycm_core实现中

//YouCompleteMe/third_party/ycmd/cpp/ycm/ClangCompleter/CompletionData.h
  // This is used to show extra information in vim's preview window. This is the
  // window that vim usually shows at the top of the buffer. This should be used
  // for extra information about the completion.
  std::string DetailedInfoForPreviewWindow() const {
    return detailed_info_;
  }
///@file: YouCompleteMe/third_party/ycmd/cpp/ycm/ycm_core.cpp
  py::class_< CompletionData >( mod, "CompletionData" )
    .def( py::init<>() )
    .def( "TextToInsertInBuffer", &CompletionData::TextToInsertInBuffer )
    .def( "MainCompletionText", &CompletionData::MainCompletionText )
    .def( "ExtraMenuInfo", &CompletionData::ExtraMenuInfo )
    .def( "DetailedInfoForPreviewWindow",
          &CompletionData::DetailedInfoForPreviewWindow )
    .def( "DocString", &CompletionData::DocString )
    .def_readonly( "kind_", &CompletionData::kind_ )
    .def_readonly( "fixit_", &CompletionData::fixit_ );

outro

对于开始提到的函数参数是函数返回值,如果不嫌麻烦的话,可以再次触发一次即可(因为前面YCM的issue中,作者也说到了不太可能会增加单独显示指定函数原型的功能);
当不再需要preview window时,通过快捷键或者cmd(:pc)关闭即可(当然把光标定位到窗口内部在关闭当前窗口也可以)。

看似简单直观的功能,中间使用大量脚本/代码/模块/协议及职责分工,这可能就是通常所说的“哪有岁月静好,只是有人负重前行"吧。

标签:completion,info,类型信息,extra,window,previewwindow,YCM,preview,data
From: https://www.cnblogs.com/tsecer/p/18470919

相关文章

  • 获取表的所有列及其类型信息
    if(stripos($column_type,'enum')!==false){if($row[$column_name]===null||$row[$column_name]===''){$values[]='NULL';}else{$values[]="'".str_replace(array("\r&......
  • Metacritic 网站中的游戏开发者和类型信息爬取
    为了从Metacritic网站上爬取游戏的开发者和类型信息,你可以使用Python的网络爬虫工具,比如requests和BeautifulSoup,或者更高级的工具如Scrapy或Selenium。本文将介绍一种基于requests和BeautifulSoup的简单爬虫示例,并假设目标是从Metacritic的单个游戏页面上提取开发......
  • 【YashanDB数据库】ycm托管数据库时报错OM host ip:127.0.0.1 is not support join to
    问题现象托管数据库时检查报错OM的IP是127.0.0.1,不支持托管到YCMOM问题的风险及影响导致数据库无法托管监控问题影响的版本问题发生原因安装数据库时修改了OM的监听ip为127.0.0.1解决方法及规避方式后台修改OM的ip为本机的ip或者0.0.0.0问题分析和处理过程1、修改en......
  • 【YashanDB知识库】ycm托管数据库时,数据库非OM安装无法托管
    问题现象ps-ef|grepyas查看无yasom和yasagent进程,且在{数据库安装目录}/om/{数据库名称}的目录下没有conf、data、log等目录,确定数据库不是用yasboot安装,是用脚本安装的问题的风险及影响非yasboot安装,ycm无法完成托管,无法监控问题影响的版本不涉及ycm的版本问题问题发......
  • 【YashanDB知识库】ycm纳管主机安装YCM-AGENT时报错“任务提交失败,无法连接主机”
    问题现象执行安装ycm-agent命令纳管主机时报错问题的风险及影响会导致ycm-agent纳管不成功,YCM无法监控主机和数据库问题影响的版本yashandb-cloud-manager-23.2.1.100-linux-aarch64.tar问题发生原因因为10.149.223.121对ycm的主机没有开放端口9070或9071解决方法及规避......
  • 在pip包中分发pythonnet dll类型信息
    我已经能够使用C#通过以下方式加载pythonnetdll:fromimportlib.resourcesimportpathimportsys#Assuming'my_package.lib'isthesub-packagecontainingtheDLLswithpath('pyrp.lib','')aslib_path:sys.path.append......
  • ssycms模板文件结构
    模板文件结构资源目录结构├─tempalte模板文件夹  ├─default  模板名称  │├─css  CSS静态资源目录  │  ├─html  HTML模板目录  │  ├─images图片资源目录  │  ├─jsJavaScript资源目录  │  └─template.json模板配......
  • ssycms 常用的文章列表调用代码
    常用的文章列表调用代码最新文章{tag:articleid="val"cid='$itemInfo["cid"]'limit='10'}<ahref="{$val.url}"class="item-link"title="{$val.title}">{$val.title}</a>{/tag:article}热门文......
  • ssycms 文章列表
    文章列表文章调用代码示例id="val"表示:调用该循环中数据需以val开头,在实际使用中,如包含多层循环,需要修改此值即可区分不同循环中的对应的值。如:官方默认模板常用v和val来区分查询分类列表的同时,也查询出分类下的文章列表{tag:articlekey='index'id="val"cid="分类ID"limi......
  • ssycms 分类列表
    分类列表调用全部分类(包含一级、二级)id="v"表示:调用该循环中数据需以v开头,在实际使用中,如包含多层循环,需要修改此值即可区分不同循环中的对应的值,官方默认模板常用v和val来区分在查询分类列表的同时,也查询出分类下的文章列表{tag:articlecategoryid="v"}<liclass='head......