Permalink
Fetching contributors…
Cannot retrieve contributors at this time
18895 lines (16609 sloc) 622 KB
" Vimball Archiver by Charles E. Campbell, Jr., Ph.D.
UseVimball
finish
syntax/vlworkspace.vim [[[1
82
" Description: Vim syntax file for VimLite workspace buffer
" Maintainer: fanhe <fanhed@163.com>
" License: This file is placed in the public domain
" Create: 2011-07-19
" Last Change: 2011-07-19
" 允许装载自定义语法文件
if exists("b:current_syntax")
finish
endif
" 树结构标志
syn match VLWTreeLead '|'
syn match VLWTreeLead '`'
" 可展开和可折叠的标志
syn match VLWClosable '\V\[|`]~'hs=s+1 contains=VLWTreeLead
syn match VLWOpenable '\V\[|`]+'hs=s+1 contains=VLWTreeLead
" 文件前缀标志
syn match VLWFilePre '[|`]-'hs=s+1 contains=VLWTreeLead
" 忽略的文件
syn match VLWIgnoredFile '[|`]#.\+'hs=s+1 contains=VLWTreeLead
" 工作空间名字只能由 [a-zA-Z_ +-.] 组成
syn match VLWorkspace '^[a-zA-Z0-9_ +-.]\+$'
syn match VLWProject '^[|`][+~].\+'
\contains=VLWOpenable,VLWClosable,VLWTreeLead
syn match VLWVirtualDirectory '\s[|`][+~].\+$'hs=s+3
\contains=VLWOpenable,VLWClosable,VLWTreeLead
" 帮助信息
syn match VLWFlag '\~'
syn match VLWHelpKey '" \{1,2\}[^ ]*:'hs=s+2,he=e-1
syn match VLWHelpKey '" \{1,2\}[^ ]*,'hs=s+2,he=e-1
syn match VLWHelpTitle '" .*\~'hs=s+2,he=e-1 contains=VLWFlag
syn match VLWHelp '^".*' contains=VLWHelpKey,VLWHelpTitle,VLWFlag
if exists('g:VLWorkspaceHighlightSourceFile') && g:VLWorkspaceHighlightSourceFile
" c/c++ 源文件、头文件
syn match VLWCSource '\V\c\[|`]-\.\+.c\$'hs=s+2
\contains=VLWFilePre,VLWTreeLead
syn match VLWCHeader '\V\c\[|`]-\.\+.h\$'hs=s+2
\contains=VLWFilePre,VLWTreeLead
syn match VLWCppSource '\V\c\[|`]-\.\+.cpp\$'hs=s+2
\contains=VLWFilePre,VLWTreeLead
syn match VLWCppSource '\V\c\[|`]-\.\+.c++\$'hs=s+2
\contains=VLWFilePre,VLWTreeLead
syn match VLWCppSource '\V\c\[|`]-\.\+.cxx\$'hs=s+2
\contains=VLWFilePre,VLWTreeLead
syn match VLWCppSource '\V\c\[|`]-\.\+.cc\$'hs=s+2
\contains=VLWFilePre,VLWTreeLead
syn match VLWCppHeader '\V\c\[|`]-\.\+.hpp\$'hs=s+2
\contains=VLWFilePre,VLWTreeLead
hi def link VLWCSource Function
hi def link VLWCHeader Constant
hi def link VLWCppSource VLWCSource
hi def link VLWCppHeader VLWCHeader
endif
hi def link VLWorkspace PreProc
hi def link VLWProject Type
hi def link VLWVirtualDirectory Statement
hi def link VLWTreeLead Special
hi def link VLWFilePre Linenr
hi def link VLWIgnoredFile Ignore
hi def link VLWClosable VLWFilePre
hi def link VLWOpenable Title
hi def link VLWFlag Ignore
hi def link VLWHelp Comment
hi def link VLWHelpKey Identifier
hi def link VLWHelpCommand Identifier
hi def link VLWHelpTitle Title
plugin/vpyclewn.vim [[[1
17
" pyclewn run time file
" Maintainer: <xdegaye at users dot sourceforge dot net>
"
" Configure VIM to be used with pyclewn and netbeans
"
" pyclewn version
let g:vpyclewn_version = "pyclewn-1.6.py2"
" enable balloon_eval
if has("balloon_eval")
set ballooneval
set balloondelay=100
endif
" The 'Pyclewn' command starts pyclewn and vim netbeans interface.
command -nargs=* -complete=file VPyclewn call vpyclewn#StartClewn(<f-args>)
plugin/VLCalltips.vim [[[1
335
" Description: vim script for display function calltips
" Maintainer: fanhe <fanhed@163.com>
" Create: 2011 Jun 18
" License: GPLv2
if exists('g:loaded_VLCalltips')
finish
endif
let g:loaded_VLCalltips = 1
" 这个插件的类
let s:VLCalltips = {}
let s:VLCalltips.keepCursor = 1 " 不自动结束没有参数的函数 calltips
function! s:InitVariable(varName, defaultVal) "{{{2
if !exists(a:varName)
let {a:varName} = a:defaultVal
return 1
endif
return 0
endfunction
"}}}
function! g:InitVLCalltips() "{{{1
call s:InitVariable('g:VLCalltips_IndicateArgument', 1)
call s:InitVariable('g:VLCalltips_EnableSyntaxTest', 0)
call s:InitVariable('g:VLCalltips_DispCalltipsKey', '<A-p>')
call s:InitVariable('g:VLCalltips_NextCalltipsKey', '<A-j>')
call s:InitVariable('g:VLCalltips_PrevCalltipsKey', '<A-k>')
"exec 'inoremap <silent> <buffer> ' . g:VLCalltips_DispCalltipsKey
"\. ' <C-r>=<SID>Test()<CR>'
exec 'inoremap <silent> <buffer> ' . g:VLCalltips_DispCalltipsKey
\. ' <C-r>=g:VLCalltips_Start()<CR>'
exec 'inoremap <silent> <buffer> ' . g:VLCalltips_NextCalltipsKey
\. ' <C-r>=<SID>HighlightNextCalltips()<CR>'
exec 'inoremap <silent> <buffer> ' . g:VLCalltips_PrevCalltipsKey
\. ' <C-r>=<SID>HighlightPrevCalltips()<CR>'
endfunction
"}}}
let s:lCalltips = [] "保存函数原型或者原型形参信息的列表(C++ 重载)
let s:nCurIndex = 0 "当前函数原型的索引
let s:nArgIndex = 0 "当前形参索引, 0 开始
function! s:Test()
let lLi = []
call add(lLi, 'int printf(const char *fmt, ...)')
call add(lLi, 'int printf(const char *fmt, int a, int b)')
call g:DisplayVLCalltips(lLi, 0)
return ''
endfunction
" 接口函数,注册回调函数
" 回调函数必须接受一个参数,并且必须返回一个列表,项目位函数的完整声明(如上)
function! g:VLCalltips_RegisterCallback(func, data) "{{{2
let Cbk = a:func
if type(a:func) == type("")
let Cbk = function(a:func)
endif
let s:VLCalltips.callback = Cbk
let s:VLCalltips.callbackData = a:data
endfunction
function! g:VLCalltips_Start() "{{{2
if !empty(get(s:VLCalltips, 'callback'))
let lCalltips = s:VLCalltips.callback(s:VLCalltips.callbackData)
call g:DisplayVLCalltips(lCalltips, 0)
endif
return ''
endfunction
function! g:VLCalltips_KeepCursor() "{{{2
let s:VLCalltips.keepCursor = 1
endfunction
function! g:VLCalltips_UnkeepCursor() "{{{2
let s:VLCalltips.keepCursor = 0
endfunction
" 接口函数, 用于外部调用
function! g:DisplayVLCalltips(lCalltips, nCurIndex) "{{{2
if empty(a:lCalltips)
return ''
endif
"let bKeepCursor = a:0 > 0 ? a:1 : 0
let bKeepCursor = s:VLCalltips.keepCursor
call s:StopCalltips()
if type(a:lCalltips) == type('')
let s:lCalltips = [a:lCalltips]
let s:nCurIndex = 0
else
let s:lCalltips = copy(a:lCalltips)
let s:nCurIndex = a:nCurIndex
endif
if !empty(s:lCalltips)
augroup DispCalltipsGroup
autocmd!
autocmd CursorMovedI <buffer> call <SID>AutoUpdateCalltips()
autocmd InsertLeave <buffer> call <SID>StopCalltips()
augroup END
"设置必要的选项
let s:bak_showmode = &showmode
let s:bak_ruler = &ruler
let s:bak_cmdheight = &cmdheight
set noshowmode
set noruler
let s:nArgIndex = s:GetArgIndex()
"如果函数无参数, 自动结束
if !bKeepCursor && len(s:lCalltips) == 1
\&& s:lCalltips[0] =~# '(\s*)\|(\s*void\s*)'
call s:StopCalltips()
call search(')', 'Wc')
normal! l
else
call s:DisplayCalltips()
endif
endif
return ''
endfunction
function! s:AutoUpdateCalltips() "{{{2
"函数无参数,自动结束
"if len(s:lCalltips) == 1 && s:lCalltips[0] =~# '()\|(\s*void\s*)'
"call s:StopCalltips()
"call search(')', 'Wc')
"normal! l
"endif
"精确模式
let nIdx = s:GetArgIndex()
if nIdx == -2
"不在括号内, 停止
call s:StopCalltips()
elseif nIdx == -1
"没有找到函数名称, 可能在括号内输入了括号
"TODO: 如果开始位置前于初始化时的开始位置, 必定停止
elseif nIdx >= 0
let s:nArgIndex = nIdx
call s:DisplayCalltips()
endif
return ''
endfunction
function! s:StopCalltips() "{{{2
call filter(s:lCalltips, 0)
let s:nCurIndex = 0
let s:nArgIndex = 0
silent! autocmd! DispCalltipsGroup
if exists('s:bak_showmode')
let &showmode = s:bak_showmode
let &ruler = s:bak_ruler
let &cmdheight = s:bak_cmdheight
unlet s:bak_showmode
unlet s:bak_ruler
unlet s:bak_cmdheight
"目的在于刷新
echo ""
endif
endfunction
function! s:HighlightNextCalltips() "{{{2
let nLen = len(s:lCalltips)
let s:nCurIndex = (s:nCurIndex + 1) % nLen
call s:DisplayCalltips()
return ''
endfunction
function! s:HighlightPrevCalltips() "{{{2
let nLen = len(s:lCalltips)
let s:nCurIndex = (s:nCurIndex - 1 + nLen) % nLen
call s:DisplayCalltips()
return ''
endfunction
function! s:DisplayCalltips() "{{{2
if empty(s:lCalltips)
return ''
endif
let nCalltipsCount = len(s:lCalltips)
let sCurCalltips = s:lCalltips[s:nCurIndex]
let nArgStartIdx = stridx(sCurCalltips, '(') + 1
let nArgEndIdx = strridx(sCurCalltips, ')') - 1
if !g:VLCalltips_IndicateArgument
let sContent = sCurCalltips
\. ' ('. (s:nCurIndex + 1) . '/' . nCalltipsCount . ')'
" 12 很诡异...
let nHeight = len(sContent) / (&columns - 12) + 1
let &cmdheight = nHeight
echohl Type
echo sContent[: nArgStartIdx-1]
echohl SpecialChar
echon sContent[nArgStartIdx : nArgEndIdx]
echohl Type
echon sContent[nArgEndIdx+1 :]
echohl None
return ''
endif
let nHlStartIdx = nArgStartIdx
let i = 0
while i < s:nArgIndex
let nHlStartIdx = stridx(sCurCalltips, ',', nHlStartIdx)
if nHlStartIdx != -1
let nHlStartIdx += 1
else
"指定的参数超过了该函数的参数数量
let nHlStartIdx = nArgEndIdx + 1
break
endif
let i += 1
endwhile
let nHlStopIdx = nArgEndIdx
let nHlStopIdx = stridx(sCurCalltips, ',', nHlStartIdx)
if nHlStopIdx != -1
"vim 的子串索引包括尾端,和 python 不一致!
let nHlStopIdx -= 1
else
"当前参数索引是最后的参数,所以 nHlStopIdx 为 -1
let nHlStopIdx = nArgEndIdx
endif
let sContent = sCurCalltips
\. ' ('. (s:nCurIndex + 1) . '/' . nCalltipsCount . ')'
" 12 很诡异...
let nHeight = len(sContent) / (&columns - 12) + 1
let &cmdheight = nHeight
"处理可变参数
let nVaArgIdx = match(sCurCalltips, '\V...)')
if nVaArgIdx != -1 && nHlStartIdx > nVaArgIdx
"存在可变参数,且参数索引到达最后了,锁定为最后的参数
let nHlStartIdx = nVaArgIdx
endif
echohl Type
echo sContent[: nHlStartIdx-1]
echohl SpecialChar
echon sContent[nHlStartIdx : nHlStopIdx]
echohl Type
echon sContent[nHlStopIdx + 1 :]
echohl None
endfunction
function! s:GetArgIndex() "{{{2
" 精确地确定光标所在位置所属的函数参数索引
" 不在括号内,返回 -2,函数名为空,返回 -1
" 确定函数括号开始的位置
if g:VLCalltips_EnableSyntaxTest
let sSkipExpr = 'synIDattr(synID(line("."), col("."), 0), "name") '
\. '=~? "string\\|character"'
else
let sSkipExpr = ''
endif
let lStartPos = searchpairpos('(', '', ')', 'nWb', sSkipExpr)
" 如果刚好在括号内,加 'c' 参数
let lEndPos = searchpairpos('(', '', ')', 'nWc', sSkipExpr)
let lCurPos = [line('.'), col('.')]
" 不在括号内
if lStartPos[0] == 0 && lStartPos[1] == 0
return -2
else
if !g:VLCalltips_IndicateArgument
return 0
endif
endif
"let lines = getline(lStartPos[0], lEndPos[0])
" 获取函数名称和名称开始的列,暂时只处理 '(' "与函数名称同行的情况,
" 允许之间有空格
" TODO: 处理更复杂的情况: 1.函数名称与 ( 不在同行 2.函数名称前有逗号
let sStartLine = getline(lStartPos[0])
let sFuncName = matchstr(sStartLine[: lStartPos[1]-1], '\w\+\ze\s*($')
let nFuncIdx = match(sStartLine[: lStartPos[1]-1], '\w\+\ze\s*($')
let nArgIdx = -1
if sFuncName != ''
" 计算光标所在的位置所属的函数参数索引(从 0 开始)
let nArgIdx = 0
for nLine in range(lStartPos[0], lCurPos[0])
let sLine = getline(nLine)
let nStart = 0
let nEnd = len(sLine)
if nLine == lCurPos[0]
" 光标所在行
let nEnd = lCurPos[1] - 1 "(a,b|,c)
endif
while nStart < nEnd
let nStart = stridx(sLine, ',', nStart)
if nStart != -1 && nStart < nEnd
" 确保不是字符串里的逗号
if !(g:VLCalltips_EnableSyntaxTest
\&& synIDattr(synID(nLine, nStart + 1, 0),
\ "name") =~? 'string\|character')
let nArgIdx += 1
endif
else
break
endif
let nStart += 1
endwhile
endfor
endif
return nArgIdx
endfunction
" vim:fdm=marker:fen:expandtab:smarttab:fdl=1:
plugin/videm.vim [[[1
17
" Vim Script
" Author: fanhe <fanhed@163.com>
" License: GPLv2
" Create: 2012-08-05
" Change: 2012-08-05
if exists('g:loaded_videm')
finish
endif
let g:loaded_videm = 1
" 命令导出
command! -nargs=? -complete=file VLWorkspaceOpen
\ call videm#wsp#InitWorkspace('<args>')
" vim: fdm=marker fen et sts=4 fdl=1
plugin/VLUtils.vim [[[1
240
" Vim script utilities for VimLite
" Last Change: 2011 Apr 11
" Maintainer: fanhe <fanhed@163.com>
" License: This file is placed in the public domain.
if exists('g:loaded_VLUtils')
finish
endif
let g:loaded_VLUtils = 1
"Function: g:InitVariable(varName, defaultVal) {{{2
"初始化变量
"仅在没有变量定义时才赋值
"Arg: varName: 变量名
"Arg: defaultVal: 默认值
"Return: 1 表示赋值为默认, 否则为 0
function g:InitVariable(varName, defaultVal)
if !exists(a:varName)
let {a:varName} = a:defaultVal
return 1
endif
return 0
endfunction
"Function: g:EchoHl(msg, ...) {{{2
"高亮显示 msg,默认高亮组为 WarningMsg
function g:EchoHl(msg, ...)
let l:hlGroup = 'WarningMsg'
if exists('a:1')
let l:hlGroup = a:1
endif
exec 'echohl ' . l:hlGroup
echo a:msg
echohl None
endfunction
"FUNCTION: g:Exec(cmd) {{{2
"与 exec 命令相同,但是运行时 set eventignore=all
"主要用于“安全”地运行某些命令,例如窗口跳转
function g:Exec(cmd)
let bak = &ei
set eventignore=all
try
exec a:cmd
catch
finally
let &ei = bak
endtry
endfunction
"Function: g:BufInWinCount(bufNumber) 打开指定缓冲区的窗口数目 {{{2
function g:BufInWinCount(bufNumber)
let cnt = 0
let winnum = 1
while 1
let bufnum = winbufnr(winnum)
if bufnum < 0
break
endif
if bufnum ==# a:bufNumber
let cnt = cnt + 1
endif
let winnum = winnum + 1
endwhile
return cnt
endfunction
"FUNCTION: g:IsWindowUsable(winNumber) 判断窗口是否可用 "{{{2
function g:IsWindowUsable(winNumber)
"如果仅有一个窗口打开,即自己是唯一窗口,怎样处理由外层决定
"if winnr("$") == 1
"return 0
"endif
"特殊窗口,如特殊缓冲类型的窗口、预览窗口
let specialWindow = getwinvar(a:winNumber, '&buftype') != ''
\|| getwinvar(a:winNumber, '&previewwindow')
if specialWindow
return 0
endif
"窗口缓冲是否已修改
let modified = getwinvar(a:winNumber, '&modified')
"如果可允许隐藏,则无论缓冲是否修改
if &hidden
return 1
endif
"如果缓冲区没有修改,或者,已修改,但是同时有其他窗口打开着,则表示可用
if !modified || g:BufInWinCount(winbufnr(a:winNumber)) >= 2
return 1
else
return 0
endif
endfunction
"FUNCTION: g:GetFirstUsableWindow() 获取第一个"常规"(非特殊)的窗口 {{{2
"特殊情况:特殊的缓冲区类型、预览缓冲区、已修改的缓冲并且不能隐藏
function g:GetFirstUsableWindow()
let i = 1
while i <= winnr("$")
if g:IsWindowUsable(i)
return i
endif
let i += 1
endwhile
return -1
endfunction
function g:GetMaxWidthWinNr() "{{{2
let i = 1
let nResult = 0
let nMaxWidth = 0
while i <= winnr("$")
let nCurWidth = winwidth(i)
if nCurWidth > nMaxWidth
let nMaxWidth = nCurWidth
let nResult = i
endif
let i += 1
endwhile
return nResult
endfunction
function g:GetMaxHeightWinNr() "{{{2
let i = 1
let nResult = 0
let nMaxHeight = 0
while i <= winnr("$")
let nCurHeight = winheight(i)
if nCurHeight > nMaxHeight
let nMaxHeight = nCurHeight
let nResult = i
endif
let i += 1
endwhile
return nResult
endfunction
function g:NormalizeCmdArg(arg) "{{{2
return substitute(a:arg, ' ', '\\ ', "g")
endfunction
function s:SID() "{{{2
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')
endfunction
function s:GetSFuncref(sFuncName) "{{{2
return function('<SNR>'.s:SID().'_'.a:sFuncName[2:])
endfunction
let g:Timer = {'t1': 0, 't2': 0} "{{{1
function g:Timer.Start() "{{{2
let self.t1 = reltime()
endfunction
function g:Timer.End() "{{{2
let self.t2 = reltime()
endfunction
function g:Timer.EchoMes() "{{{2
echom join(reltime(self.t1, self.t2), '.')
endfunction
function g:Timer.EndEchoMes() "{{{2
call self.End()
call self.EchoMes()
endfunction
function g:EchoSyntaxStack() "{{{2
let names = []
let li = synstack(line("."), col("."))
let li = empty(li) ? [] : li
for id in li
"echo synIDattr(id, "name")
call add(names, synIDattr(id, 'name'))
endfor
echo join(names, ', ')
return ''
endfunction
function g:Progress(n, ...) "{{{2
let n = a:n
if a:0 > 0
let m = a:1
else
let m = 100
endif
let nRange = 10
let nRatio = n * nRange / m
echoh Pmenu
echon repeat(' ', nRatio)
echoh None
echon repeat(' ', nRange - nRatio)
echon printf("%4d%%", n * 100 / m)
redraw
endfunction
function g:GetCmdOutput(sCmd) "{{{2
let bak_lang = v:lang
" 把消息统一为英文
exec ":lan mes en_US.UTF-8"
try
redir => sOutput
silent! exec a:sCmd
catch
" 把错误消息设置为最后的 ':' 后的字符串?
"let v:errmsg = substitute(v:exception, '^[^:]\+:', '', '')
finally
redir END
endtry
exec ":lan mes " . bak_lang
return sOutput
endfunction
" vim:fdm=marker:fen:fdl=1:et:
plugin/VIMClangCC.vim [[[1
734
" Vim Script
" Author: fanhe <fanhed@163.com>
" License: GPLv2
" Create: 2011-12-16
" Change: 2011-12-16
if !has('python')
echohl ErrorMsg
echom "Error: ".expand('<sfile>:p')." required vim compiled with +python"
echohl None
finish
endif
if exists("g:loaded_VIMClangCC")
finish
endif
let g:loaded_VIMClangCC = 1
autocmd FileType c,cpp call g:InitVIMClangCodeCompletion()
" 标识是否第一次初始化
let s:bFirstInit = 1
let s:dCalltipsData = {}
let s:dCalltipsData.usePrevTags = 0 " 是否使用最近一次的 tags 缓存
" 关联的文件,一般用于头文件关联源文件
" 在头文件头部和尾部添加的额外的内容,用于修正在头文件时的头文件包含等等问题
" {头文件: {'line': 在关联文件中对应的行(#include), 'filename': 关联文件}, ...}
let g:dRelatedFile = {}
let s:sPluginPath = substitute(expand('<sfile>:p:h'), '\\', '/', 'g')
if has('win32') || has('win64')
let s:sDefaultPyModPath = fnamemodify($VIM . '\vimlite\VimLite', ":p")
else
let s:sDefaultPyModPath = fnamemodify("~/.vimlite/VimLite", ":p")
endif
function! s:InitVariable(varName, defaultVal) "{{{2
if !exists(a:varName)
let {a:varName} = a:defaultVal
return 1
else
return 0
endif
endfunction
"}}}
command! -nargs=0 -bar VIMCCCInitForcibly call <SID>VIMCCCInitForcibly()
" 临时启用选项函数 {{{2
function! s:SetOpts()
let s:bak_cot = &completeopt
if g:VIMCCC_ItemSelectionMode == 0 " 不选择
set completeopt-=menu,longest
set completeopt+=menuone
elseif g:VIMCCC_ItemSelectionMode == 1 " 选择并插入文本
set completeopt-=menuone,longest
set completeopt+=menu
elseif g:VIMCCC_ItemSelectionMode == 2 " 选择但不插入文本
set completeopt-=menu,longest
set completeopt+=menuone
else
set completeopt-=menu
set completeopt+=menuone,longest
endif
return ''
endfunction
function! s:RestoreOpts()
if exists('s:bak_cot')
let &completeopt = s:bak_cot
unlet s:bak_cot
else
return ""
endif
let sRet = ""
if pumvisible()
if g:VIMCCC_ItemSelectionMode == 0 " 不选择
let sRet = "\<C-p>"
elseif g:VIMCCC_ItemSelectionMode == 1 " 选择并插入文本
let sRet = ""
elseif g:VIMCCC_ItemSelectionMode == 2 " 选择但不插入文本
let sRet = "\<C-p>\<Down>"
else
let sRet = "\<Down>"
endif
endif
return sRet
endfunction
function! s:CheckIfSetOpts()
let sLine = getline('.')
let nCol = col('.') - 1
" 若是成员补全,添加 longest
if sLine[nCol-2:] =~ '->' || sLine[nCol-1:] =~ '\.'
\|| sLine[nCol-2:] =~ '::'
call s:SetOpts()
endif
return ''
endfunction
"}}}
function! s:SID() " 获取脚本 ID,这个函数名不能变 {{{2
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')
endfunction
let s:sid = s:SID()
function! s:GetSFuncRef(sFuncName) "获取局部于脚本的函数的引用 {{{2
let sFuncName = a:sFuncName =~ '^s:' ? a:sFuncName[2:] : a:sFuncName
return function('<SNR>'.s:sid.'_'.sFuncName)
endfunction
"}}}
function! s:StartQucikCalltips() "{{{2
let s:dCalltipsData.usePrevTags = 1
call g:VLCalltips_Start()
return ''
endfunction
"}}}
function! s:ShouldComplete() "{{{2
if (getline('.') =~ '#\s*include')
" 写头文件,忽略
return 0
else
" 检测光标所在的位置,如果在注释、双引号、浮点数时,忽略
let nRow = line('.')
let nCol = col('.') - 1 " 是前一列 eg. ->|
if nCol < 1
" TODO: 支持续行的补全
return 0
endif
if g:VIMCCC_EnableSyntaxTest
let lStack = synstack(nRow, nCol)
let lStack = empty(lStack) ? [] : lStack
for nID in lStack
if synIDattr(nID, 'name')
\=~? 'comment\|string\|float\|character'
return 0
endif
endfor
else
" TODO
endif
return 1
endif
endfunction
"}}}
function! s:LaunchVIMClangCodeCompletion() "{{{2
if s:ShouldComplete()
return "\<C-x>\<C-o>"
else
return ''
endif
endfunction
"}}}
function! s:CompleteByChar(char) "{{{2
if a:char ==# '.'
return a:char . s:LaunchVIMClangCodeCompletion()
elseif a:char ==# '>'
if getline('.')[col('.') - 2] != '-'
return a:char
else
return a:char . s:LaunchVIMClangCodeCompletion()
endif
elseif a:char ==# ':'
if getline('.')[col('.') - 2] != ':'
return a:char
else
return a:char . s:LaunchVIMClangCodeCompletion()
endif
endif
endfunction
"}}}
" 强制启动
function! s:VIMCCCInitForcibly() "{{{2
let bak = g:VIMCCC_Enable
let g:VIMCCC_Enable = 1
call g:InitVIMClangCodeCompletion()
let g:VIMCCC_Enable = bak
endfunction
"}}}
" 可选参数存在且非零,不 '冷启动'(异步新建不存在的当前文件对应的翻译单元)
function! g:InitVIMClangCodeCompletion(...) "{{{2
" MayComplete to '.'
call s:InitVariable('g:VIMCCC_MayCompleteDot', 1)
" MayComplete to '->'
call s:InitVariable('g:VIMCCC_MayCompleteArrow', 1)
" MayComplete to '::'
call s:InitVariable('g:VIMCCC_MayCompleteColon', 1)
" 把回车映射为:
" 在补全菜单中选择并结束补全时, 若选择的是函数, 自动显示函数参数提示
call s:InitVariable('g:VIMCCC_MapReturnToDispCalltips', 1)
" When completeopt does not contain longest option, this setting
" controls the behaviour of the popup menu selection
" when starting the completion
" 0 = don't select first item
" 1 = select first item (inserting it to the text)
" 2 = select first item (without inserting it to the text)
" default = 2
call s:InitVariable('g:VIMCCC_ItemSelectionMode', 2)
" 使用语法测试
call s:InitVariable('g:VIMCCC_EnableSyntaxTest', 1)
" 本插件的 python 模块路径
call s:InitVariable('g:VIMCCC_PythonModulePath', s:sDefaultPyModPath)
" Clang library path
call s:InitVariable('g:VIMCCC_ClangLibraryPath', '')
" If clang should complete preprocessor macros and constants
call s:InitVariable('g:VIMCCC_CompleteMacros', 0)
" If clang should complete code patterns, i.e loop constructs etc.
call s:InitVariable('g:VIMCCC_CompletePatterns', 0)
" Update quickfix list periodically
call s:InitVariable('g:VIMCCC_PeriodicQuickFix', 0)
" Ignore case in code completion
call s:InitVariable('g:VIMCCC_IgnoreCase', &ignorecase)
" 跳转至符号声明处的默认快捷键
call s:InitVariable('g:VIMCCC_GotoDeclarationKey', '<C-p>')
" 跳转至符号实现处的默认快捷键
call s:InitVariable('g:VIMCCC_GotoImplementationKey', '<C-]>')
" 用于外部控制
call s:InitVariable('g:VIMCCC_Enable', 0)
if !g:VIMCCC_Enable
return
endif
if s:bFirstInit
command! -nargs=0 -bar VIMCCCQuickFix
\call <SID>VIMCCCUpdateClangQuickFix(expand('%:p'))
command! -nargs=+ VIMCCCSetArgs call <SID>VIMCCCSetArgsCmd(<f-args>)
endif
let g:VIMCCC_CodeCompleteFlags = 0
if g:VIMCCC_CompleteMacros
let g:VIMCCC_CodeCompleteFlags += 1
endif
if g:VIMCCC_CompletePatterns
let g:VIMCCC_CodeCompleteFlags += 2
endif
call s:InitPythonInterfaces()
setlocal omnifunc=VIMClangCodeCompletion
if g:VIMCCC_PeriodicQuickFix
augroup VIMCCC_AUGROUP
autocmd! CursorHold,CursorHoldI <buffer> VIMCCCUpdateQuickFix
augroup END
endif
" 初始化函数参数提示服务
call g:VLCalltips_RegisterCallback(
\s:GetSFuncRef('s:RequestCalltips'), s:dCalltipsData)
call g:InitVLCalltips()
if g:VIMCCC_MayCompleteDot
inoremap <silent> <buffer> .
\<C-r>=<SID>SetOpts()<CR>
\<C-r>=<SID>CompleteByChar('.')<CR>
\<C-r>=<SID>RestoreOpts()<CR>
endif
if g:VIMCCC_MayCompleteArrow
inoremap <silent> <buffer> >
\<C-r>=<SID>SetOpts()<CR>
\<C-r>=<SID>CompleteByChar('>')<CR>
\<C-r>=<SID>RestoreOpts()<CR>
endif
if g:VIMCCC_MayCompleteColon
inoremap <silent> <buffer> :
\<C-r>=<SID>SetOpts()<CR>
\<C-r>=<SID>CompleteByChar(':')<CR>
\<C-r>=<SID>RestoreOpts()<CR>
endif
if g:VIMCCC_ItemSelectionMode > 4
inoremap <silent> <buffer> <C-n>
\<C-r>=<SID>CheckIfSetOpts()<CR>
\<C-r>=<SID>LaunchVIMClangCodeCompletion()<CR>
\<C-r>=<SID>RestoreOpts()<CR>
else
"inoremap <silent> <buffer> <C-n>
"\<C-r>=<SID>SetOpts()<CR>
"\<C-r>=<SID>LaunchVIMClangCodeCompletion()<CR>
"\<C-r>=<SID>RestoreOpts()<CR>
endif
if g:VIMCCC_MapReturnToDispCalltips
"inoremap <silent> <expr> <buffer> <CR> pumvisible() ?
"\"\<C-y>\<C-r>=<SID>RequestCalltips(1)\<Cr>" :
"\"\<CR>"
inoremap <silent> <expr> <buffer> <CR> pumvisible() ?
\"\<C-y><C-r>=<SID>StartQucikCalltips()\<Cr>" :
\"\<CR>"
endif
exec 'nnoremap <silent> <buffer> ' . g:VIMCCC_GotoDeclarationKey
\. ' :call <SID>VIMCCCGotoDeclaration()<CR>'
exec 'nnoremap <silent> <buffer> ' . g:VIMCCC_GotoImplementationKey
\. ' :call <SID>VIMCCCSmartJump()<CR>'
if a:0 > 0 && a:1
" 可控制不 '冷启动'
else
" '冷启动'
py VIMCCCIndex.AsyncUpdateTranslationUnit(vim.eval("expand('%:p')"))
endif
let s:bFirstInit = 0
endfunction
"}}}
function! s:RequestCalltips(...) "{{{2
let bUsePrevTags = a:0 > 0 ? a:1.usePrevTags : 0
let s:dCalltipsData.usePrevTags = 0 " 清除这个标志
if bUsePrevTags
" 从全能补全菜单选择条目后,使用上次的输出
let sLine = getline('.')
let nCol = col('.')
if sLine[nCol-3:] =~ '^()'
let sFuncName = matchstr(sLine[: nCol-4], '\~\?\w\+$')
normal! h
py vim.command("let lCalltips = %s"
\% VIMCCCIndex.GetCalltipsFromCacheFilteredResults(
\ vim.eval("sFuncName")))
call g:VLCalltips_UnkeepCursor()
return lCalltips
endif
else
" 普通情况,请求 calltips
" 确定函数括号开始的位置
let lOrigCursor = getpos('.')
let lStartPos = searchpairpos('(', '', ')', 'nWb',
\'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"')
" 考虑刚好在括号内,加 'c' 参数
let lEndPos = searchpairpos('(', '', ')', 'nWc',
\'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"')
let lCurPos = lOrigCursor[1:2]
" 不在括号内
if lStartPos ==# [0, 0]
return ''
endif
" 获取函数名称和名称开始的列,只能处理 '(' "与函数名称同行的情况,
" 允许之间有空格
let sStartLine = getline(lStartPos[0])
let sFuncName = matchstr(sStartLine[: lStartPos[1]-1], '\~\?\w\+\ze\s*($')
let nFuncStartIdx = match(sStartLine[: lStartPos[1]-1], '\~\?\w\+\ze\s*($')
let sFileName = expand('%:p')
" 补全时,行号要加上前置字符串所占的行数,否则位置就会错误
let nRow = lStartPos[0]
let nCol = nFuncStartIdx + 1
let lCalltips = []
if sFuncName !=# ''
let calltips = []
" 找到了函数名,开始全能补全
let sFileName = s:ToClangFileName(sFileName)
py VIMCCCIndex.GetTUCodeCompleteResults(
\vim.eval("sFileName"),
\int(vim.eval("nRow")),
\int(vim.eval("nCol")),
\[GetCurUnsavedFile()])
py vim.command("let lCalltips = %s"
\% VIMCCCIndex.GetCalltipsFromCacheCCResults(
\ vim.eval("sFuncName")))
endif
call setpos('.', lOrigCursor)
call g:VLCalltips_KeepCursor()
return lCalltips
endif
return ''
endfunction
"}}}
function! VIMCCCSetRelatedFile(sRelatedFile, ...) "{{{2
let sFileName = a:0 > 0 ? a:1 : expand('%:p')
if sFileName ==# ''
" 不允许空文件名,因为空字符串不能为字典的键值
return
endif
let g:dRelatedFile[sFileName] = {'line': 0, 'filename': a:sRelatedFile}
py import IncludeParser
py vim.command("let g:dRelatedFile[sFileName]['line'] = %d"
\% IncludeParser.FillHeaderWithSource(vim.eval("sFileName"), '',
\ vim.eval("a:sRelatedFile"))[0])
endfunction
"}}}
" 设置 clang 命令参数,参数可以是字符串或者列表
" 设置完毕后立即异步重建当前文件的翻译单元
function! VIMCCCSetArgs(lArgs) "{{{2
if type(a:lArgs) == type('')
let lArgs = split(a:lArgs)
else
let lArgs = a:lArgs
endif
let sFileName = expand('%:p')
let sFileName = s:ToClangFileName(sFileName)
py VIMCCCIndex.SetParseArgs(vim.eval("lArgs"))
py VIMCCCIndex.AsyncUpdateTranslationUnit(vim.eval("sFileName"),
\[GetCurUnsavedFile()], True, True)
endfunction
"}}}
function! s:VIMCCCSetArgsCmd(...) "{{{2
call VIMCCCSetArgs(a:000)
endfunction
"}}}
" 统一处理,主要处理 .h -> .hpp 的问题
function! s:ToClangFileName(sFileName) "{{{2
let sFileName = a:sFileName
let sResult = sFileName
if fnamemodify(sFileName, ':e') ==? 'h'
" 把 c 的头文件转为 cpp 的头文件,即 .h -> .hpp
" 暂时简单的处理,前头加两个 '_',后面加两个 'p'
let sDirName = fnamemodify(sFileName, ':h')
let sBaseName = fnamemodify(sFileName, ':t')
let sResult = sDirName . '/' . '__' . sBaseName . 'pp'
endif
return sResult
endfunction
"}}}
function! s:FromClangFileName(sClangFileName) "{{{2
let sClangFileName = a:sClangFileName
let sResult = sClangFileName
if fnamemodify(sClangFileName, ':e') ==? 'hpp'
\&& fnamemodify(sClangFileName, ':t')[:1] ==# '__'
let sDirName = fnamemodify(sClangFileName, ':h')
let sBaseName = fnamemodify(sClangFileName, ':t')
let sResult = sDirName . '/' . sBaseName[2:-3]
endif
return sResult
endfunction
"}}}
function! VIMClangCodeCompletion(findstart, base) "{{{2
if a:findstart
"call vlutils#TimerStart() " 计时
let nRow = line('.')
let lPos = searchpos('\<\|\.\|->\|::', 'cb', nRow)
let nCol = col('.')
if lPos == [0, 0]
let nStartCol = nCol
else
let sLine = getline('.')
if sLine[nCol - 1] ==# '.'
let nStartCol = nCol + 1
elseif sLine[nCol -1 : nCol] ==# '->'
\|| sLine[nCol - 1: nCol] ==# '::'
let nStartCol = nCol + 2
else
" BUG: 返回 5 后,下次调用此函数是,居然 col('.') 返回 6
"let nStartCol = nCol
let nStartCol = nCol - 1
endif
endif
return nStartCol
endif
"===========================================================================
" 补全操作开始
"===========================================================================
let b:base = a:base
let sBase = a:base
let sFileName = expand('%:p')
let nRow = line('.')
let nCol = col('.') "列
let sFileName = s:ToClangFileName(sFileName)
py lResults = VIMCCCIndex.GetVimCodeCompleteResults(
\vim.eval("sFileName"),
\int(vim.eval("nRow")),
\int(vim.eval("nCol")),
\[GetCurUnsavedFile()],
\vim.eval("sBase"),
\vim.eval("g:VIMCCC_IgnoreCase") != '0',
\int(vim.eval("g:VIMCCC_CodeCompleteFlags")))
"call vlutils#TimerEndEcho()
py vim.command("let lResults = %s" % lResults)
"call vlutils#TimerEndEcho()
" 调试用
let b:lResults = lResults
"call vlutils#TimerEndEcho()
return lResults
"===========================================================================
" 补全操作结束
"===========================================================================
endfunction
" 更新本地当前窗口的 quickfix 列表
function! s:VIMCCCUpdateClangQuickFix(sFileName) "{{{2
let sFileName = a:sFileName
"py t = UpdateQuickFixThread(vim.eval("sFileName"), [GetCurUnsavedFile()], True)
"py t.start()
"return
" quickfix 里面就不需要添加前置内容了,暂时的处理
py VIMCCCIndex.UpdateTranslationUnit(vim.eval("sFileName"),
\[GetCurUnsavedFile(False)], True)
py vim.command("let lQF = %s"
\% VIMCCCIndex.GetVimQucikFixListFromRecentTU())
"call setqflist(lQF)
call setloclist(0, lQF)
silent! lopen
endfunction
"}}}
function! s:VIMCCCGotoDeclaration() "{{{2
let sFileName = expand('%:p')
let nFileLineCount = line('$')
let dict = get(g:dRelatedFile, sFileName, {})
let nLineOffset = 0
if !empty(dict) && dict.line > 0
let nLineOffset = dict.line - 1
endif
let nRow = line('.')
let nRow += nLineOffset
let nCol = col('.')
let sFileName = s:ToClangFileName(sFileName)
py vim.command("let dLocation = %s"
\% VIMCCCIndex.GetSymbolDeclarationLocation(vim.eval("sFileName"),
\ int(vim.eval("nRow")), int(vim.eval("nCol")),
\ [GetCurUnsavedFile(UF_Related)], True))
"echom string(dLocation)
if !empty(dLocation) && dLocation.filename ==# sFileName
\&& !empty(dict) && dict.line > 0
if dLocation.line < dict.line
" 在关联的文件中,前部
let dLocation.filename = dict.filename
elseif dLocation.line >= dict.line + nFileLineCount
" 在关联的文件中,后部
let dLocation.line -= (nFileLineCount - 1)
let dLocation.filename = dict.filename
else
" 在当前文件中
let dLocation.line -= nLineOffset
endif
"let dLocation.offset = 0 " 这个暂时无视
endif
"echom string(dLocation)
call s:GotoLocation(dLocation)
endfunction
"}}}
function! s:VIMCCCGotoImplementation() "{{{2
let sFileName = expand('%:p')
let nFileLineCount = line('$')
let dict = get(g:dRelatedFile, sFileName, {})
let nLineOffset = 0
if !empty(dict) && dict.line > 0
let nLineOffset = dict.line - 1
endif
let nRow = line('.')
let nRow += nLineOffset
let nCol = col('.')
let sFileName = s:ToClangFileName(sFileName)
py vim.command("let dLocation = %s"
\% VIMCCCIndex.GetSymbolDefinitionLocation(vim.eval("sFileName"),
\ int(vim.eval("nRow")), int(vim.eval("nCol")),
\ [GetCurUnsavedFile(UF_Related)], True))
"echom string(dLocation)
if !empty(dLocation) && dLocation.filename ==# sFileName
\&& !empty(dict) && dict.line > 0
if dLocation.line < dict.line
" 在关联的文件中,前部
let dLocation.filename = dict.filename
elseif dLocation.line >= dict.line + nFileLineCount
" 在关联的文件中,后部
let dLocation.line -= (nFileLineCount - 1)
let dLocation.filename = dict.filename
else
" 在当前文件中
let dLocation.line -= nLineOffset
endif
"let dLocation.offset = 0 " 这个暂时无视
endif
"echom string(dLocation)
call s:GotoLocation(dLocation)
endfunction
"}}}
" 智能跳转, 可跳转到包含的文件, 符号的实现处
function! s:VIMCCCSmartJump() "{{{2
let sLine = getline('.')
if matchstr(sLine, '^\s*#\s*include') !=# ''
" 跳转到包含的文件
if exists(':VLWOpenIncludeFile') == 2
VLWOpenIncludeFile
endif
else
" 跳转到符号的实现处
call s:VIMCCCGotoImplementation()
endif
endfunction
"}}}
function! s:GotoLocation(dLocation) "{{{2
let dLocation = a:dLocation
if empty(dLocation)
return
endif
let sFileName = dLocation.filename
let nRow = dLocation.line
let nCol = dLocation.column
" 仅在这里需要反转?
let sFileName = s:FromClangFileName(sFileName)
if bufnr(sFileName) == bufnr('%')
" 同一个缓冲区,仅跳至指定的行号和列号
normal! m'
call cursor(nRow, nCol)
else
let sCmd = printf("e +%d %s", nRow, sFileName)
exec sCmd
call cursor(nRow, nCol)
endif
endfunction
"}}}
function! s:InitPythonInterfaces() "{{{2
if !s:bFirstInit
return
endif
py import sys
py import vim
py sys.path.append(vim.eval("g:VIMCCC_PythonModulePath"))
py sys.argv = [vim.eval("g:VIMCCC_ClangLibraryPath")]
"silent! exec 'pyfile ' . s:VIMCCC_PythonModulePath . '/VIMClangCC.py'
py from VIMClangCC import *
python << PYTHON_EOF
#import threading
#class UpdateQuickFixThread(threading.Thread):
# def __init__(self, sFileName, lUnsavedFiles = [], bReparse = False):
# threading.Thread.__init__(self)
# self.sFileName = sFileName
# self.lUnsavedFiles = lUnsavedFiles
# self.bReparse = bReparse
#
# def run(self):
# global VIMCCCIndex
# VIMCCCIndex.UpdateTranslationUnit(self.sFileName, self.lUnsavedFiles,
# self.bReparse)
# vim.command("call setqflist(%s)"
# % VIMCCCIndex.GetVimQucikFixListFromRecentTU())
UF_None = 0
UF_Related = 1
UF_RelatedPrepend = 2
def GetCurUnsavedFile(nFlags = UF_None):
sFileName = vim.eval("expand('%:p')")
sClangFileName = vim.eval("s:ToClangFileName('%s')" % sFileName)
if nFlags == UF_None:
return (sClangFileName, '\n'.join(vim.current.buffer[:]))
if nFlags == UF_Related:
import IncludeParser
d = vim.eval("get(g:dRelatedFile, expand('%:p'), {})")
if d:
nOffsetRow = d['line']
sSourceFile = d['filename']
# 如果没有在关联文件找到要找的 #include 行,nOffsetRow 为 0
# 文本内容都是以 NL 结尾的
nOffsetRow, sResult = IncludeParser.FillHeaderWithSource(
sFileName, '\n'.join(vim.current.buffer[:]) + '\n', sSourceFile)
#print nOffsetRow
#print sResult
vim.command(
"let g:dRelatedFile[expand('%%:p')]['line'] = %d" % nOffsetRow)
return (sClangFileName, sResult)
else:
return GetCurUnsavedFile(UF_None)
def GetCurCursorPos():
return vim.current.window.cursor
def GetCurRow():
#return vim.current.window.cursor[0]
return int(vim.eval("line('.')"))
def GetCurCol():
# NOTE: 在补全函数中,与 col('.') 的结果不一致!
# 这个返回结果为进入补全函数前的位置
#return vim.current.window.cursor[1]
return int(vim.eval("col('.')"))
# 本插件只操作这个实例,其他事都不管
VIMCCCIndex = VIMClangCCIndex()
PYTHON_EOF
endfunction
"}}}
" vim: fdm=marker fen et sts=4 fdl=1
plugin/VimTagsManager.vim [[[1
161
" Vim script utilities for VimLite
" Last Change: 2011 May 10
" Maintainer: fanhe <fanhed@163.com>
" License: GPLv2
if exists('g:loaded_VimTagsManager')
finish
endif
let g:loaded_VimTagsManager = 1
let s:start = 0
command! -nargs=* -complete=file VTMParseFiles call g:VTMParseFiles(<f-args>)
function s:InitVariable(varName, defaultVal) "{{{2
if !exists(a:varName)
let {a:varName} = a:defaultVal
return 1
endif
return 0
endfunction
"}}}
"call s:InitVariable('g:VimTagsManager_DbFile',
"\'~/Desktop/VimLite/CtagsDatabase/TestTags3.db')
call s:InitVariable('g:VimTagsManager_DbFile', 'VimLiteTags.db')
call s:InitVariable('g:VimTagsManager_SrcDir', expand('~/.vimlite/omnicpp'))
call s:InitVariable('g:VimTagsManager_InclAllCondCmplBrch', 1)
let s:hasStarted = 0
function! g:GetTagsByScopeAndKind(scope, kind) "{{{2
py vim.command("let tags = %s" % ToVimEval(vtm.GetTagsByScopeAndKind(
\vim.eval('a:scope'), vim.eval('a:kind'))))
return tags
endfunction
function! g:GetTagsByScopesAndKinds(scopes, kinds) "{{{2
py vim.command("let tags = %s" % ToVimEval(vtm.GetTagsByScopesAndKinds(
\vim.eval('a:scopes'), vim.eval('a:kinds'))))
return tags
endfunction
function! g:GetTagsByScopeAndName(scope, name) "{{{2
py vim.command("let tags = %s" % ToVimEval(vtm.GetTagsByScopeAndName(
\vim.eval('a:scope'), vim.eval('a:name'))))
return tags
endfunction
"}}}
" 可选参数控制是否允许部分匹配(且不区分大小写), 默认允许
function! g:GetTagsByScopesAndName(scopes, name, ...) "{{{2
let bPartialMatch = a:0 > 0 ? a:1 : 1
py vim.command("let tags = %s" % ToVimEval(vtm.GetTagsByScopeAndName(
\vim.eval('a:scopes'), vim.eval('a:name'),
\int(vim.eval('bPartialMatch')))))
return tags
endfunction
"}}}
" 可选参数控制是否允许部分匹配(且不区分大小写), 默认允许
function! g:GetOrderedTagsByScopesAndName(scopes, name, ...) "{{{2
let bPartialMatch = a:0 > 0 ? a:1 : 1
py vim.command("let tags = %s" % ToVimEval(vtm.GetOrderedTagsByScopesAndName(
\vim.eval('a:scopes'), vim.eval('a:name'),
\int(vim.eval('bPartialMatch')))))
return tags
endfunction
"}}}
function! g:GetTagsByPath(path) "{{{2
py vim.command("let tags = %s"
\% ToVimEval(vtm.GetTagsByPath(vim.eval('a:path'))))
return tags
endfunction
function! g:GetTagsByKindAndPath(kind, path) "{{{2
py vim.command("let tags = %s" % ToVimEval(vtm.GetTagsByKindAndPath(
\vim.eval('a:kind'), vim.eval('a:path'))))
return tags
endfunction
function! g:VTMParseFiles(...) "{{{2
if exists('s:hasConnected')
if !s:hasConnected
py vtm.OpenDatabase(vim.eval('s:absDbFile'))
endif
else
echohl WarningMsg
echo 'VimTagsManager has not yet started!'
echohl None
return
endif
"若传进来的第一个参数为列表, 仅解析此列表
if a:0 > 0
if type(a:1) == type([])
py vtm.ParseFiles(vim.eval('a:1'))
return
endif
endif
py vtm.ParseFiles(vim.eval('a:000'))
endfunction
function! g:VTMOpenDatabase(dbFile) "{{{2
if !s:hasStarted
call VimTagsManagerInit()
endif
py vtm.OpenDatabase(os.path.expanduser(vim.eval('a:dbFile')))
endfunction
function! VimTagsManagerInit() "{{{1
if s:hasStarted
return
else
let s:hasStarted = 1
endif
python << PYTHON_EOF
# -*- encoding:utf-8 -*-
import sys, os, os.path
import vim
import json # 用来转字典
sys.path.extend([os.path.expanduser(vim.eval('g:VimTagsManager_SrcDir'))])
from VimTagsManager import VimTagsManager
from VimTagsManager import AppendCtagsOpt
from VimUtils import ToVimEval
# 添加选项
if vim.eval('g:VimTagsManager_InclAllCondCmplBrch') != '0':
AppendCtagsOpt('-m')
PYTHON_EOF
py vtm = VimTagsManager()
if filereadable(g:VimTagsManager_DbFile)
" 若已存在数据库文件, 直接打开之
let s:hasConnected = 1
py vtm.OpenDatabase(
\os.path.expanduser(vim.eval('g:VimTagsManager_DbFile')))
else
" 没有存在的数据库文件, 暂时连接内存数据库
" 当请求 ParseFiles() 时才新建硬盘的数据库
let s:hasConnected = 0
py vim.command("let s:absDbFile = '%s'"
\% os.path.abspath(os.path.expanduser(vim.eval(
\ 'g:VimTagsManager_DbFile'))).replace("'", "''"))
" 连接内存数据库
py vtm.OpenDatabase(':memory:')
endif
endfunction
" vim:fdm=marker:fen:expandtab:smarttab:fdl=1:
doc/VimLite.txt [[[1
773
*VimLite.txt* A C/C++ IDE inspired by CodeLite
_ _______ _ _____ _________________~
| | / /_ _// | /, / / /_ _/_ __/ ____/~
| |/ / / / / ,|// / / / / / / / __/ ~
| / /_/ /_/ /|_/ / /____/ /_ / / / /___ ~
|__/_____/_/ /_/_____/____//_/ /_____/ ~
VimLite User Manual
==============================================================================
CONTENTS *VimLite-Contents*
1. Introduction |VimLite-Introduction|
2. Download |VimLite-Download|
3. Prerequisites |VimLite-Prerequisites|
4. Project Manager |VimLite-ProjectManager|
4.1. KeyMappings |VimLite-ProjectManager-KeyMappings|
4.2. Commands |VimLite-ProjectManager-Commands|
4.3. Cscope |VimLite-ProjectManager-Cscope|
5. Code Completion |VimLite-CodeCompletion|
5.1. OmniCpp |VimLite-CodeCompletion-OmniCpp|
5.1.1. Commands |VimLite-CodeCompletion-OmniCpp-Cmds|
5.1.2. Macros Handling |VimLite-CodeCompletion-OmniCpp-Macros|
5.1.3. Limitation |VimLite-CodeCompletion-OmniCpp-Limit|
5.2. VIMCCC |VimLite-CodeCompletion-VIMCCC|
6. Debugger |VimLite-Debugger|
6.1. KeyMappings |VimLite-Debugger-KeyMappings|
6.2. Commands |VimLite-Debugger-Commands|
7. Options |VimLite-Options|
7.1. Project Manager Options |VimLite-Options-ProjectManager|
7.2. Calltips Options |VimLite-Options-Calltips|
7.3. OmniCpp Options |VimLite-Options-OmniCpp|
7.4. VIMCCC Options |VimLite-Options-VIMCCC|
7.5. Debugger Options |VimLite-Options-Debugger|
8. Tips |VimLite-Tips|
8.1. Configuration Sample |VimLite-ConfigSample|
8.2. Use cl.exe On Windows |VimLite-Windows|
9. Limitation |VimLite-Limitation|
==============================================================================
1. Introduction *VimLite-Introduction*
VimLite is a C/C++ IDE.
VimLite consists mainly of the following three modules:
* Project Manager: The project manager module is
compatible with CodeLite. It auto
generates makefile for you.
* Code Completion: An enhanced OmniCpp plugin and a clang
code completion plugin.
OmniCpp supports the following
completion: namespace, structure,
class member, using, using namespace,
class template, stl, etc.
VIMCCC which based on libclang
supports mostly accurate code
completion.
* Debugger Integration: Gdb integration, by pyclewn.
==============================================================================
2. Download *VimLite-Download*
The latest release of VimLite can be found from this url:
http://www.vim.org/scripts/script.php?script_id=3647
And VimLite in google code can be found from this url:
http://code.google.com/p/vimlite/
==============================================================================
3. Prerequisites *VimLite-Prerequisites*
VimLite depends following software: >
python
python-lxml (optional)
make
cscope
gcc
gdb
<
On ubuntu 10.04, just run: >
sudo apt-get install python python-lxml build-essential gdb cscope
<
Make sure your vim compile with this features: >
+python
+netbeans_intg
<
And make sure you have these settings in your vimrc file: >
set nocp
filetype plugin on
syntax on
<
==============================================================================
4. Project Manager *VimLite-ProjectManager*
Workspaces and Projects~
One workspace holds a number of projects, for instance, various pieces of a
large design. Create a workspace by selecting "New Workspace..." on workspace
popup menu (The popup menu when the cursor in workspace line).
A project is one piece of a large design within that workspace. Create a
project by selecting 'Create a New Project' on workspace popup menu. Please
create a worksapce before do this.
For instance, one project might be a DLL, another project could be a static
library, and yet another project could be a GUI design which would be
eventually integrated together in one workspace to be released as a piece of
software. All these projects could be part of one workspace.
The project itself contains all information it needs to produce its own output
piece of the overall software.
Also, a project contains no information about the workspace, and thus one
project can be part of multiple workspaces. The workspace holds pointers to
the projects which are members of that workspace.
The workspace information file is <workspace-name>.workspace.
The project information file is <project-name>.project.
Configurations~
Each project has at least two build configurations: Debug and Release. In
practice you can have many more configurations. You can select what
configuration the project is using by selecting 'Settings' on project popup
menu.
This information is global among all the projects in the workspace and so is
kept in the workspace information file. This means all projects be in the same
configuration in a workspace.
NOTE: Almost all commands are listed in popup menu, please help info around.
------------------------------------------------------------------------------
4.1. KeyMappings *VimLite-ProjectManager-KeyMappings*
Press <F1> in workspace buffer for quick help information.
Key Description Option~
------------------------------------------------------------------------------
<2-LeftMouse> Fold / expand node
<CR> Same as <2-LeftMouse>
o Same as <2-LeftMouse> |g:VLWOpenNodeKey|
go Preview file |g:VLWOpenNode2Key|
t Open file in new tab |g:VLWOpenNodeInNewTabKey|
T Open file in new tab silently |g:VLWOpenNodeInNewTab2Key|
i Open file split |g:VLWOpenNodeSplitKey|
gi Preview file split |g:VLWOpenNodeSplit2Key|
s Open file vsplit |g:VLWOpenNodeVSplitKey|
gs Preview file vsplit |g:VLWOpenNodeVSplit2Key|
P Go to root node |g:VLWGotoRootKey|
p Go to parent node |g:VLWGotoParentKey|
<C-n> Go to next sibling node |g:VLWGotoPrevSibling|
<C-p> Go to prev sibling node |g:VLWGotoNextSibling|
. Show text menu |g:VLWShowMenuKey|
, Popup gui menu |g:VLWPopupMenuKey|
R Refresh buffer |g:VLWRefreshBufferKey|
<F1> Toggle quick help info |g:VLWToggleHelpInfo|
------------------------------------------------------------------------------
*g:VLWOpenNodeKey*
If workspace node is selected, a build config menu will be shown. >
let g:VLWOpenNodeKey = 'o'
<
*g:VLWOpenNode2Key*
>
let g:VLWOpenNode2Key = 'go'
<
*g:VLWOpenNodeInNewTabKey*
>
let g:VLWOpenNodeInNewTabKey = 't'
<
*g:VLWOpenNodeInNewTab2Key*
>
let g:VLWOpenNodeInNewTab2Key = 'T'
<
*g:VLWOpenNodeSplitKey*
>
let g:VLWOpenNodeSplitKey = 'i'
<
*g:VLWOpenNodeSplit2Key*
>
let g:VLWOpenNodeSplit2Key = 'gi'
<
*g:VLWOpenNodeVSplitKey*
>
let g:VLWOpenNodeVSplitKey = 's'
<
*g:VLWOpenNodeVSplit2Key*
>
let g:VLWOpenNodeVSplit2Key = 'gs'
<
*g:VLWGotoParentKey*
>
let g:VLWGotoParentKey = 'p'
<
*g:VLWGotoRootKey*
>
let g:VLWGotoRootKey = 'P'
<
*g:VLWGotoNextSibling*
>
let g:VLWGotoNextSibling = '<C-n>'
<
*g:VLWGotoPrevSibling*
>
let g:VLWGotoPrevSibling = '<C-p>'
<
*g:VLWRefreshBufferKey*
>
let g:VLWRefreshBufferKey = 'R'
<
*g:VLWShowMenuKey*
The key to popup general menu. >
let g:VLWShowMenuKey = '.'
<
*g:VLWPopupMenuKey*
The key to popup gui menu, often it is set '<RightRelease>' if you like to use
mouse. >
let g:VLWPopupMenuKey = ','
<
*g:VLWToggleHelpInfo*
>
let g:VLWToggleHelpInfo = '<F1>'
------------------------------------------------------------------------------
4.2. Commands *VimLite-ProjectManager-Commands*
VLWorkspaceOpen [workspace_file] Open a workspace file or default
workspace.
VLWBuildActiveProject Build active projcet.
VLWCleanActiveProject Clean active project.
VLWRunActiveProject Run active project.
VLWBuildAndRunActiveProject Build active project and run if build
successfully.
VLWSwapSourceHeader Toggle editing source and header
VLWLocateCurrentFile Locate editing file in the worksapce
buffer.
VLWFindFiles [name] Find workspace files
VLWFindFilesNoCase [name] Find workspace files with no case
sensitive
VLWOpenIncludeFile Open included file when locates the
cursor in '#include' line.
------------------------------------------------------------------------------
4.3. Cscope *VimLite-ProjectManager-Cscope*
VimLite uses cscope database to achieve some features, such as jump to
definition, jump to declaration, search workspace files, etc.
Run ':h cscope' for more info.
Commands:~
VLWInitCscopeDatabase Initialize cscope database. VimLite
will generate database forcibly.
VLWUpdateCscopeDatabase Update database. Only be valided when
has been connected to the workspace
cscope database.
==============================================================================
5. Code Completion *VimLite-CodeCompletion*
Popup menu format: ~
>
+------------------------+
|method1() f + MyClass|
|_member1 m + MyClass|
|_member2 m # MyClass|
|_member3 m - MyClass|
+------------------------+
^ ^ ^ ^
(1) (2)(3) (4)
<
(1) Name of the symbol, when a match ends with '()' it's a function.
(2) Kind of the symbol, possible kinds are: >
* c = classes
* d = macro definitions
* e = enumerators (values inside an enumeration)
* f = function definitions
* g = enumeration names
* m = class, struct, and union members
* n = namespaces
* p = function prototypes
* s = structure names
* t = typedefs
* u = union names
* v = variable definitions
* x = using types
(3) Access, possible values are: >
* + = public
* # = protected
* - = private
Note: Enumerators have no access information
(4) Parent scope where the symbol is defined.
Note: If the parent scope is '<global>' it's a global symbol.
Note: Anonymous scope may starts with "__anon".
Global Scope Completion~
The global scope completion allows you to complete global symbols for the base
you are currently typing. The base can start with '::' or not.
Note: Global scope completion only works with a non empty base, if you run a
completion just after a '::' the completion will fail. The reason is that if
there is no base to complete the script will try to display all the tags in
the database. For small project it could be not a problem but for others you
may wait 5 minutes or more for a result.
eg1: >
pthread_cr<C-x><C-o> => pthread_create
<
Where pthread_create is a global function.
eg2: >
::globa<C-x><C-o> => ::global_func()
+----------------+
|global_func() f|
|global_var1 v|
|global_var2 v|
+----------------+
<
Where global_var1, global_var2 and global_func are global symbols
eg3: >
::<C-x><C-o> => [NO MATCH]
<
No match because a global completion from an empty base is not allowed.
Member Completion~
You can complete members of a container(class, struct, namespace, enum).
VimLite Code Completion will auto popup complete menu when you type ':' or '.'
or '>'. Of cause you can use <C-x><C-o> to start completing.
eg: >
MyNamespace::<C-x><C-o>
+--------------------------------+
|E_ENUM0 e MyNamespace|
|E_ENUM1 e MyNamespace|
|E_ENUM2 e MyNamespace|
|MyClass c MyNamespace|
|MyEnum g MyNamespace|
|MyStruct s MyNamespace|
|MyUnion u MyNamespace|
|SubNamespace n MyNamespace|
|doSomething( f MyNamespace|
|myVar v MyNamespace|
|something_t t MyNamespace|
+--------------------------------+
------------------------------------------------------------------------------
5.1. OmniCpp *VimLite-CodeCompletion-OmniCpp*
OmniCpp needs a tags database to support completion, you need parse the
workspace before starting code completion. Put the cursor on workspace line
in VLWorkspace buffer window, popup the menu, select "Parse Workspace (Quick)".
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5.1.1. Commands *VimLite-CodeCompletion-OmniCpp-Cmds*
VLWParseFiles <file1> <file2> ... Parse files.
VLWParseCurrentFile Parse current editing file.
NOTE: You ought to save current file
before run this command.
VLWDeepParseCurrentFile Parse current editing file and the
files it includes.
NOTE: You ought to save current file
before run this command.
VLWEnvVarSetttings Open 'Environment Variables Setting'
dialog.
VLWTagsSetttings Open 'Tags Setting' dialog. This also
can set the search paths for clang.
VLWCompilersSettings Open 'Compilers Settings' dialog.
VLWBuildersSettings Open 'Builders Settings' dialog.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5.1.2. Macros Handling *VimLite-CodeCompletion-OmniCpp-Macros*
List in 'VimLite -> TagsSettings -> Macros' are to be pre-processed by OmniCpp
parser. Usually, you would like to add new macros which confuse the parse.
Usually, you will add gcc predefined macros if you use gcc, run (On Linux) >
cpp -dM /dev/null
and put the outputs to 'VimLite -> TagsSettings -> Macros'.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5.1.3. Limitation *VimLite-CodeCompletion-OmniCpp-Limit*
OmniCpp is not a C/C++ compiler, so...
Some C++ features are not supported, some implemented features may not work
properly in some conditions. They are multiple reasons like a lack of
information in the database, performance issues and so on...
Here is a list for whiches are not supported:~
1. Does not support local variables completion, use <C-x><C-n> instead.
2. Does not support using [namespace] statement in included files. This
behavior is not recommended.
3. Does not support friend. Solution is just displaying all class members.
4. Does not support complex typedef. Solution is configuring corresponding
type replacement. eg: >
typedef typename _Alloc::template rebind<value_type>::other _Pair_alloc_type;
5. Does not support overload functions with different return types.
6. Does not support function template.
7. Does not support '.' and '->' overload for performance considerations.
------------------------------------------------------------------------------
5.1. VIMCCC *VimLite-CodeCompletion-VIMCCC*
You can set g:VLWorkspaceUseVIMCCC to 1 to enable clang code completion. >
let g:VLWorkspaceUseVIMCCC = 1
You need to install liblang to let it work. After installation, VIMCCC code
completion will auto work, but not when the current build configuration of the
project is a custom build. VIMCCC currently work with libclang 3.0.
There are only two commands of VIMCCC.
VIMCCCQuickFix Retrieve clang diagnostics to quickfix,
if the diagnostics are not empty, open
the quickfix window.
VIMCCCSetArgs {arg1} [arg2] ... Set clang compiler options
VLWTagsSetttings Open 'Tags Setting' dialog. This also
can set the search paths for clang.
==============================================================================
6. Debugger *VimLite-Debugger*
VimLite integrate pyclewn in it.
Start to debug the active projcet, just press the icon in toolbar or run: >
:VLWDbgStart
<
NOTE: You must start degger before setting a breakpoint.
------------------------------------------------------------------------------
6.1. KeyMappings *VimLite-Debugger-KeyMappings*
<CR>
<2-LeftMouse> Fold and unfold the watching var.
Only valid in var watching buffer.
dd Delete watching var on the current line.
Only valid in var watching buffer.
p Jump to parent of the expanded var.
Only valid in var watching buffer.
<C-w> Watch the selecting text.
Only valid in the Visual mode and can
be changed by var g:VLWDbgWatchVarKey.
------------------------------------------------------------------------------
6.2. Commands *VimLite-Debugger-Commands*
VLWDbgStart Start debugger.
VLWDbgStop Stop debugger.
VLWDbgStepIn Step in.
VLWDbgNext Next.
VLWDbgStepOut Step out.
VLWDbgContinue Continue
VLWDbgToggleBp Toggle breakpoint of the cursor line.
Please run ':view ~/.vimlite/doc/vpyclewn.txt' for more commands. If you are
on Windows run ':view $VIM\vimlite\doc\vpyclewn.txt' instead.
Because of avoiding conflicts with official pyclewn, currently the help file
of pyclewn.txt rename to vpyclewn.txt and do not put into vim's doc folder.
==============================================================================
7. Options *VimLite-Options*
Right hand side is the default value, you can modify other values for the same
type. If type of the right hand side value is integer, 0 for False, non-zero
for True.
------------------------------------------------------------------------------
7.1. Project Manager Options *VimLite-Options-ProjectManager*
Workspace window width. >
let g:VLWorkspaceWinSize = 30
Set the wrokspace buffer name. >
let g:VLWorkspaceBufName = '==VLWorkspace=='
Highlight the workspace buffer cursor line. >
let g:VLWrokspaceHighlightCursorline = 1
If not 0, when the curser put on one source file buffer, the cursor of
worksapce buffer's cursor will go the the corresponding source file line. >
let g:VLWorkspaceLinkToEidtor = 1
Will install a menu named 'VimLite'. >
let g:VLWorkspaceEnableMenuBarMenu = 1
Will install some toolbar icons. >
let g:VLWorkspaceEnableToolBarMenu = 1
Symbol Database, 0 -> none, 1 -> cscope, 2 -> GNU global
NOTE: Cscope will not update symbol database automatically but GNU global do,
when VimLite just connect a valid symbol database
NOTE: GNU global has a bug when a directory is a symbol link, so if you use
symbol links in your project, do *NOT* use GNU global. >
let g:VLWorkspaceSymbolDatabase = 1
Cscope progam path, you can specify another value such as /usr/local/cscope >
let g:VLWorkspaceCscopeProgram = &cscopeprg
If not 0, VimLite will pass all project's include search paths to cscope, so
cscope will generate a datebase which contains all headers. >
let g:VLWorkspaceCscopeContainExternalHeader = 1
Enable fast symbol lookup via an inverted index. This option causes cscope to
create 2 more files ('<name>.in.out'' and '<name>.po.out') in addition to the
normal database. This allows a faster symbol search algorithm that provides
noticeably faster lookup performance for large projects. >
let g:VLWorkspaceCreateCscopeInvertedIndex = 0
GNU global configuration, you can read their manual for help. >
let g:VLWorkspaceGtagsGlobalProgram = "global"
let g:VLWorkspaceGtagsProgram = "gtags"
let g:VLWorkspaceGtagsCscopeProgram = "gtags-cscope"
Highlight the .h/.hpp and .c/.cpp file. >
let g:VLWorkspaceHighlightSourceFile = 1
Insert worksapce name into title. >
let g:VLWorkspaceDispWspNameInTitle = 1
Auto save all modified files before build projects. >
let g:VLWorkspaceSaveAllBeforeBuild = 0
Use VIMCCC code completion instead of OmniCpp which based on modified ctags. >
let g:VLWorkspaceUseVIMCCC = 0
The active project highlight group name. >
let g:VLWorkspaceActiveProjectHlGroup = 'SpecialKey'
Auto parse the editing file when saving it. It will be done asynchronously.
Only work for files belong to workspace. >
let g:VLWorkspaceParseFileAfterSave = 1
------------------------------------------------------------------------------
7.2. Calltips Options *VimLite-Options-Calltips*
The key to trigger function calltips. >
let g:VLCalltips_DispCalltipsKey = '<A-p>'
The key to display the next calltips. >
let g:VLCalltips_NextCalltipsKey = '<A-j>'
The key to display the prev calltips. >
let g:VLCalltips_PrevCalltipsKey = '<A-k>'
Enable indicating which function argument is being edited. If you feel this
feature is slow, you may set it to 0. >
let g:VLCalltips_IndicateArgument = 1
Enable syntax testing. As well known, syntax testing in vim is very slow, if
function calltips in your vim crash your speed, you may not set this to 1. >
let g:VLCalltips_EnableSyntaxTest = 0
------------------------------------------------------------------------------
7.3. OmniCpp Options *VimLite-Options-OmniCpp*
Auto trigger code completion when input '.' (dot). >
let g:VLOmniCpp_MayCompleteDot = 1
Auto trigger code completion when input '>' (right arrow). >
let g:VLOmniCpp_MayCompleteArrow = 1
Auto trigger code completion when input ':' (colon). >
let g:VLOmniCpp_MayCompleteColon = 1
When completeopt does not contain longest option, this setting controls the
behaviour of the popup menu selection.
* 0 -> don't select first item.
* 1 -> select first item (inserting it to the text).
* 2 -> select first item (without inserting it to the text). >
let g:VLOmniCpp_ItemSelectionMode = 2
Map <CR> (return) key to auto trigger function calltips after select a
function item in the code completion popup menu. >
let g:VLOmniCpp_MapReturnToDispCalltips = 1
Use libCxxParser.so, an simple C++ parser written in C++. It is very fast and
provide more accurate local variables parsing. You need compile and install it
first if you want it. >
let g:VLOmniCpp_UseLibCxxParser = 0
The libCxxParser.so path. On Windows, you need set this variable correctly. >
let g:VLOmniCpp_LibCxxParserPath = $HOME . "/.vimlite/lib/libCxxParser.so"
Goto declaration of symbols key.
It does not need cscope and is more accurate.
NOTE: You should keep the ctags database is up to date. >
let g:VLOmniCpp_GotoDeclarationKey = '<C-p>'
Goto implementation of symbols key. It also jump to included file when locates
the cursor in '#include' line.
It does not need cscope and is more accurate.
NOTE: You should keep the ctags database is up to date. >
let g:VLOmniCpp_GotoImplementationKey = '<C-]>'
Include all condition compilation branches, also, #if 0 is not included.
For example, if this option value is not 0, the following code will include
'a' and 'b' symbols.
#if 1
int a;
#else
int b;
#endif
>
let g:VimTagsManager_InclAllCondCmplBrch = 1
------------------------------------------------------------------------------
7.4. VIMCCC Options *VimLite-Options-VIMCCC*
If enable VIMCCC without VimLite. >
let g:VIMCCC_Enable = 0
Clang library path. >
let g:VIMCCC_ClangLibraryPath = ''
VIMCCC python module path, make sure you set it correctly.
On Linux, the default value is: >
let g:VIMCCC_PythonModulePath = '~/.vimlite/VimLite'
On Windows, the default value is: >
let g:VIMCCC_PythonModulePath = $VIM . '\vimlite\VimLite'
If equal to 1, it will periodically update the quickfix window. >
let g:VIMCCC_PeriodicQuickFix = 0
If equal to 1, clang should complete preprocessor macros and constants. >
let g:VIMCCC_CompleteMacros = 0
If equal to 1, clang should complete code patterns, i.e loop constructs etc. >
let g:VIMCCC_CompletePatterns = 0
Ignore case in code completion. >
let g:VIMCCC_IgnoreCase = &ignorecase
Auto trigger code completion when input '.' (dot). >
let g:VIMCCC_MayCompleteDot = 1
Auto trigger code completion when input '>' (right arrow). >
let g:VIMCCC_MayCompleteArrow = 1
Auto trigger code completion when input ':' (colon). >
let g:VIMCCC_MayCompleteColon = 1
When completeopt does not contain longest option, this setting controls the
behaviour of the popup menu selection.
* 0 -> don't select first item.
* 1 -> select first item (inserting it to the text).
* 2 -> select first item (without inserting it to the text).
>
let g:VIMCCC_ItemSelectionMode = 2
Map <CR> (return) key to auto trigger function calltips after select a
function item in the code completion popup menu. >
let g:VIMCCC_MapReturnToDispCalltips = 1
Goto declaration of symbols key.
NOTE: Currently, it does not work as mean of name as libclang limitation.
* For a cursor that is a reference, retrieve a cursor representing the
* entity that it references.
>
let g:VIMCCC_GotoDeclarationKey = '<C-p>'
Goto implementation of symbols key. It also jump to included file when locates
the cursor in '#include' line.
NOTE: Currently, it does not work as mean of name as libclang limitation.
* For a cursor that is either a reference to or a declaration
* of some entity, retrieve a cursor that describes the definition of
* that entity.
NOTE: Currently, it can goto implementation of symbols, but you need goto the
declaration of symbols, and then goto the implementation, as liblang
limitation.
>
let g:VIMCCC_GotoImplementationKey = '<C-]>'
------------------------------------------------------------------------------
7.5. Debugger Options *VimLite-Options-Debugger*
The frame sign background color, can be #xxxxxx format. >
let g:VLWDbgFrameSignBackground = 'DarkMagenta'
Save breakpoints. Currently, VimLite can only treats all breakpoints as
normal location breakpoint, it means VimLite only saves breakpoints as
command "break {file}:{line}". So the condition of breakpoint will be lost. >
let g:VLWDbgSaveBreakpointsInfo = 1
==============================================================================
8. Tips *VimLite-Tips*
------------------------------------------------------------------------------
8.1. Configuration Sample *VimLite-ConfigSample*
Configuration sample of VimLite >
let g:VLWorkspaceSaveAllBeforeBuild = 1
let g:VLOmniCpp_ItemSelectionMode = 10
let g:VLWorkspaceParseFileAfterSave = 1
let g:VLCalltips_IndicateArgument = 0
let g:VLCalltips_EnableSyntaxTest = 0
let g:VLWDbgSaveBreakpointsInfo = 0
If use VIMCCC >
let g:VLWorkspaceUseVIMCCC = 1
let g:VIMCCC_ItemSelectionMode = 10
let g:VIMCCC_CompleteMacros = 1
------------------------------------------------------------------------------
8.2. Use cl.exe On Windows *VimLite-Windows*
Run :VLWCompilersSettings to open settings dialog, select VC++ compiler and
set 'Environment Setup Command:' to following (I have installed VS 2010 in
'D:\Program Files' folder): >
"D:\Program Files\Microsoft Visual Studio 10.0\Common7\Tools\vsvars32.bat"
And then you can use cl.exe in project, just select 'VC++' compiler.
NOTE: Currently, OmniCpp of VimLite can not support code completion with
headers of Visual Studio, use VIMCCC instead.
NOTE: Compile the program with cl.exe, this implies that VimLite can not debug
the program with gdb, use windbg instead.
==============================================================================
9. Limitation *VimLite-Limitation*
------------------------------------------------------------------------------
On Windows, a general file and path which pattern is '[A-Za-z0-9_]\+' are
supported only. Any special file and path name will cause some problems,
especially in debugging. And Chinese file and path name are not supported
because of string encoding issue of python 2.x.
On Linux, special file and path name are supported, but the debugger only
supports general file and path name.
Generally, a general file or path name is strongly recommended.
------------------------------------------------------------------------------
vim:tw=78:ft=help:norl:et:ts=4:sw=4:sts=4
after/syntax/qf.vim [[[1
21
" Vim after syntax file
" Language: Quickfix window
" Maintainer: fanhe <fanhed@163.org>
" Create: 2011-07-04
setlocal nowrap
" A bunch of useful C keywords
syn match qfFileName "^[^|]*" nextgroup=qfSeparator
syn match qfSeparator "|" nextgroup=qfLineNr contained
syn match qfLineNr "[^|]*|\s*"he=e-2 contained nextgroup=qfError,qfWarning
syn match qfError "error:.\+" contained
syn match qfError "undefined reference to .\+" contained
syn match qfWarning "warning:.\+" contained
" The default highlighting.
hi def link qfFileName Directory
hi def link qfLineNr LineNr
hi def link qfError Error
hi def link qfWarning WarningMsg
after/syntax/dbgvar.vim [[[1
25
" Vim syntax file
" Language: debugger variables window syntax file
" Maintainer: <xdegaye at users dot sourceforge dot net>
" Last Change: Oct 8 2007
if exists("b:current_syntax")
finish
endif
syn region dbgVarChged display contained matchgroup=dbgIgnore start="={\*}"ms=s+1 end="$"
syn region dbgDeScoped display contained matchgroup=dbgIgnore start="={-}"ms=s+1 end="$"
syn region dbgVarUnChged display contained matchgroup=dbgIgnore start="={=}"ms=s+1 end="$"
syn match dbgItem display transparent "^.*$"
\ contains=dbgVarUnChged,dbgDeScoped,dbgVarChged,dbgVarNum
syn match dbgVarNum display contained "^\s*\d\+:"he=e-1
high def link dbgVarChged Special
high def link dbgDeScoped Comment
high def link dbgVarNum Identifier
high def link dbgIgnore Ignore
let b:current_syntax = "dbgvar"
autoload/vlutils.vim [[[1
372
" Description: Common function utilities
" Maintainer: fanhe <fanhed@163.com>
" License: GPLv2
" Create: 2011-07-15
" Last Change: 2011-07-15
if exists('s:loaded')
finish
endif
let s:loaded = 1
" 初始化
function! vlutils#Init() "{{{2
return 1
endfunction
"}}}2
" 初始化变量, 仅在没有变量定义时才赋值
" Param1: sVarName - 变量名, 必须是可作为标识符的字符串
" Param2: defaultVal - 默认值, 可为任何类型
" Return: 1 表示赋值为默认, 否则为 0
function! vlutils#InitVariable(sVarName, defaultVal) "{{{2
if !exists(a:sVarName)
let {a:sVarName} = a:defaultVal
return 1
endif
return 0
endfunction
"}}}
" 与 exec 命令相同,但是运行时 set eventignore=all
" 主要用于“安全”地运行某些命令,例如窗口跳转
function! vlutils#Exec(sCmd) "{{{2
try
exec 'noautocmd' a:sCmd
catch
endtry
endfunction
"}}}
" 把路径分割符替换为 posix 的 '/'
function! vlutils#PosixPath(sPath) "{{{2
if has('win32') || has('win64')
return substitute(a:sPath, '\', '/', 'g')
else
return a:sPath
endif
endfunction
"}}}
" 打开指定缓冲区的窗口数目
function! vlutils#BufInWinCount(nBufNr) "{{{2
let nCount = 0
let nWinNr = 1
while 1
let nWinBufNr = winbufnr(nWinNr)
if nWinBufNr < 0
break
endif
if nWinBufNr ==# a:nBufNr
let nCount += 1
endif
let nWinNr += 1
endwhile
return nCount
endfunction
"}}}
" 判断窗口是否可用
" 可用 - 即可用其他窗口替换本窗口而不会令本窗口的内容消失
function! vlutils#IsWindowUsable(nWinNr) "{{{2
let nWinNr = a:nWinNr
" 特殊窗口,如特殊缓冲类型的窗口、预览窗口
let bIsSpecialWindow = getwinvar(nWinNr, '&buftype') !=# ''
\|| getwinvar(nWinNr, '&previewwindow')
if bIsSpecialWindow
return 0
endif
" 窗口缓冲是否已修改
let bModified = getwinvar(nWinNr, '&modified')
" 如果可允许隐藏,则无论缓冲是否修改
if &hidden
return 1
endif
" 如果缓冲区没有修改,或者,已修改,但是同时有其他窗口打开着,则表示可用
if !bModified || vlutils#BufInWinCount(winbufnr(nWinNr)) >= 2
return 1
else
return 0
endif
endfunction
"}}}
" 获取第一个"可用"(常规, 非特殊)的窗口
" 特殊: 特殊的缓冲区类型、预览缓冲区、已修改的缓冲并且不能隐藏
" Return: 窗口编号 - -1 表示没有可用的窗口
function! vlutils#GetFirstUsableWinNr() "{{{2
let i = 1
while i <= winnr("$")
if vlutils#IsWindowUsable(i)
return i
endif
let i += 1
endwhile
return -1
endfunction
"}}}
" 获取宽度最大的窗口编号
function! vlutils#GetMaxWidthWinNr() "{{{2
let i = 1
let nResult = 0
let nMaxWidth = 0
while i <= winnr("$")
let nCurWidth = winwidth(i)
if nCurWidth > nMaxWidth
let nMaxWidth = nCurWidth
let nResult = i
endif
let i += 1
endwhile
return nResult
endfunction
"}}}
" 获取高度最大的窗口编号
function! vlutils#GetMaxHeightWinNr() "{{{2
let i = 1
let nResult = 0
let nMaxHeight = 0
while i <= winnr("$")
let nCurHeight = winheight(i)
if nCurHeight > nMaxHeight
let nMaxHeight = nCurHeight
let nResult = i
endif
let i += 1
endwhile
return nResult
endfunction
"}}}
" '优雅地'打开一个文件, 在需要的时候会分割窗口
" 水平分割和垂直分割的具体方式由 'splitbelow' 和 'splitright' 选项控制
" vlutils#OpenFile... 系列函数的分割都是这样控制的
" 只有一个窗口时会垂直分割窗口, 否则是水平分割
" 规则:
" 1. 需要打开的文件已经在某个窗口打开, 跳至那个窗口, 结束
" 2. 如果上一个窗口(wincmd p)可用, 用此窗口打开文件, 结束
" 3. 如果没有可用的窗口, 且窗口数为 1, 垂直分割打开
" 4. 如果没有可用的窗口, 且窗口数多于 1, 跳至宽度最大的窗口水平分割打开
function! vlutils#OpenFile(sFile, ...) "{{{2
let sFile = a:sFile
if sFile ==# ''
return
endif
let bKeepCursorPos = 0
if a:0 > 0
let bKeepCursorPos = a:1
endif
" 跳回原始窗口的算法
" 1. 先保存此窗口的编号, 再保存此窗口对应的缓冲的编号
" 2. 打开文件后, 检查保存的窗口是否对应原来的缓冲编号, 如果对应, 跳回,
" 否则, 继续算法
" 3. 查找到对应保存的缓冲编号的窗口, 若返回有效编号, 跳回, 否则, 不操作
let nBackWinNr = winnr()
let nBackBufNr = bufnr('%')
let nBufWinNr = bufwinnr('^' . sFile . '$')
if nBufWinNr != -1
" 文件已经在某个窗口中打开, 直接跳至那个窗口
exec nBufWinNr 'wincmd w'
else
" 文件没有在某个窗口中打开
let nPrevWinNr = winnr('#')
if !vlutils#IsWindowUsable(nPrevWinNr)
if vlutils#GetFirstUsableWinNr() == -1
" 上一个窗口不可用并且没有可用的窗口, 需要分割窗口
if winnr('$') == 1
" 窗口总数为 1, 垂直分割
" TODO: 分割方式应该可控制...
exec 'vsplit' fnameescape(sFile)
else
"有多个窗口, 找一个宽度最大的窗口然后水平分割窗口
let nMaxWidthWinNr = vlutils#GetMaxWidthWinNr()
call vlutils#Exec(nMaxWidthWinNr . 'wincmd w')
exec 'split' fnameescape(sFile)
endif
else
call vlutils#Exec(vlutils#GetFirstUsableWinNr() . "wincmd w")
exec 'edit' fnameescape(sFile)
endif
else
call vlutils#Exec('wincmd p')
exec 'edit' fnameescape(sFile)
endif
endif
if bKeepCursorPos
if winbufnr(nBackWinNr) == nBackBufNr
" NOTE: 是否必要排除自动命令?
call vlutils#Exec(nBackWinNr . 'wincmd w')
elseif bufwinnr(nBackBufNr) != -1
call vlutils#Exec(bufwinnr(nBackBufNr) . 'wincmd w')
else
" 不操作
endif
endif
endfunction
"}}}
" 在新的标签页中打开文件
" OptParam: 默认 0, 1 表示不切换到新标签那里, 即保持光标在原始位置
function! vlutils#OpenFileInNewTab(sFile, ...) "{{{2
let sFile = a:sFile
if sFile ==# ''
return
endif
let bKeepCursorPos = 0
if a:0 > 0
let bKeepCursorPos = a:1
endif
let nCurTabNr = tabpagenr()
exec 'tabedit' fnameescape(sFile)
if bKeepCursorPos
" 跳回原来的标签
" 为什么用 ':tabnext' 也可以? 理应用 ':tab'
exec 'tabnext' nCurTabNr
endif
endfunction
"}}}
" '优雅地'水平分割打开文件
function! vlutils#OpenFileSplit(sFile, ...) "{{{2
let sFile = a:sFile
if sFile ==# ''
return
endif
let bKeepCursorPos = 0
if a:0 > 0
let bKeepCursorPos = a:1
endif
let nBackWinNr = winnr()
let nBackBufNr = bufnr('%')
" 跳到宽度最大的窗口再水平分割
call vlutils#Exec(vlutils#GetMaxWidthWinNr() . 'wincmd w')
exec 'split' fnameescape(sFile)
if bKeepCursorPos
if winbufnr(nBackWinNr) == nBackBufNr
" NOTE: 是否必要排除自动命令?
call vlutils#Exec(nBackWinNr . 'wincmd w')
elseif bufwinnr(nBackBufNr) != -1
call vlutils#Exec(bufwinnr(nBackBufNr) . 'wincmd w')
else
" 不操作
endif
endif
endfunction
"}}}
" '优雅地'垂直分割打开文件
function! vlutils#OpenFileVSplit(sFile, ...) "{{{2
let sFile = a:sFile
if sFile ==# ''
return
endif
let bKeepCursorPos = 0
if a:0 > 0
let bKeepCursorPos = a:1
endif
let nBackWinNr = winnr()
let nBackBufNr = bufnr('%')
" 跳到宽度最大的窗口再水平分割
call vlutils#Exec(vlutils#GetMaxHeightWinNr() . 'wincmd w')
exec 'vsplit' fnameescape(sFile)
if bKeepCursorPos
if winbufnr(nBackWinNr) == nBackBufNr
" NOTE: 是否必要排除自动命令?
call vlutils#Exec(nBackWinNr . 'wincmd w')
elseif bufwinnr(nBackBufNr) != -1
call vlutils#Exec(bufwinnr(nBackBufNr) . 'wincmd w')
else
" 不操作
endif
endif
endfunction
"}}}
" 生成带编号菜单选择列表
" NOTE: 第一项(即li[0])不会添加编号
function! vlutils#GenerateMenuList(li) "{{{2
let li = a:li
let nLen = len(li)
let lResult = []
if nLen > 0
call add(lResult, li[0])
let l = len(string(nLen -1))
let n = 1
for str in li[1:]
call add(lResult, printf('%*d. %s', l, n, str))
let n += 1
endfor
endif
return lResult
endfunction
"}}}
" 获取选择的文本,从 mark 插件复制过来的...
function! vlutils#GetVisualSelection() "{{{2
let save_clipboard = &clipboard
set clipboard= " Avoid clobbering the selection and clipboard registers.
let save_reg = getreg('"')
let save_regmode = getregtype('"')
silent normal! gvy
let res = getreg('"')
call setreg('"', save_reg, save_regmode)
let &clipboard = save_clipboard
return res
endfunction
"}}}
" 是否在 Windows 平台
function! vlutils#IsWindowsOS() "{{{2
return has('win32') || has('win64') || has('win32unix')
endfunction
"}}}
" 简单的计时器静态类
let s:TimerData = {'t1': 0, 't2': 0} "{{{1
function! vlutils#TimerStart() "{{{2
let s:TimerData.t1 = reltime()
endfunction
function! vlutils#TimerEnd() "{{{2
let s:TimerData.t2 = reltime()
endfunction
function! vlutils#TimerEchoMes() "{{{2
echom string((str2float(reltimestr(s:TimerData.t2))
\- str2float(reltimestr(s:TimerData.t1))))
endfunction
function! vlutils#TimerEndEcho() "{{{2
call vlutils#TimerEnd()
call vlutils#TimerEchoMes()
endfunction
"}}}1
" 模拟 python 的 os 和 os.path 模块
" os, os.path {{{2
let os = {}
let path = {}
let os.path = path
if has('win32') || has('win64')
let os.extsep = '.'
let os.sep = '\'
else
let os.altsep = ''
let os.extsep = '.'
let os.sep = '/'
endif
let g:vlutils#os = os
let vlutils#os = os
"}}}2
" vim:fdm=marker:fen:fdl=1:et:ts=4:sw=4:sts=4:
autoload/omnicpp/resolvers.vim [[[1
2530
" Description: Omnicpp completion resolving functions
" Maintainer: fanhe <fanhed@163.com>
" Create: 2011 May 15
" License: GPLv2
" 基本数据结构
"
" TypeInfo, 字典
" {
" 'name': <name>
" 'til': <template instantiated list>
" 'types': [<single type>, ...]
" }
" VarInfo, 字典
" {
" 'kind': <'container'|'variable'|'function'|'cast'|'unknown'>
" 'name': <name>
" 'tag': <tag>
" 'typeinfo': <TypeInfo>
" }
" PrmtInfo(模版参数, 函数参数) 字典
" {
" 'kind': <'typename'|'class'|Non Type>
" 'name': <parameter name>
" 'default': <default value>
" }
function! s:StripString(s) "{{{2
let s = substitute(a:s, '^\s\+', '', 'g')
let s = substitute(s, '\s\+$', '', 'g')
return s
endfunc
"}}}
function! s:NewPrmtInfo() "{{{2
return {'kind': '', 'name': '', 'default': ''}
endfunc
"}}}
" 类模版信息
" template<typename T1, class T2 = std::vector<int> >
" 对应以下数据结构
" {
" list: [{'typename': 'T1', 'realtype': ''},
" {'typename': 'T2', 'realtype': 'std::vector<int>'}]
" dict: {'T1': '', 'T2': 'std::vector<int>'}
" }
function! s:NewTpltInfo() "{{{2
return {'list': [], 'dict': {}}
endfunc
"}}}
function! s:NewTypeInfo() "{{{2
return {'name': '', 'til': [], 'types': []}
endfunc
"}}}
function! s:NewOmniScope() "{{{2
return {'kind': 'unknown', 'name': '', 'til': []}
endfunc
"}}}
" 获取全能补全请求前的语句的 OmniScopeStack
" 以下情况一律先清理所有不必要的括号, 清理 [], 把 C++ 的 cast 转为 C 形式
" case01. A::B C::D::|
" case02. A::B()->C().| orig: A::B::C(" a(\")z ", '(').|
" case03. A::B().C->|
" case04. A->B().|
" case05. A->B.|
" case06. Z Y = ((A*)B)->C.|
" case07. (A*)B()->C.|
" case08. static_cast<A*>(B)->C.| -> 处理成标准 C 的形式 ((A*)B)->C.|
" case09. A(B.C()->|)
"
" case10. ::A->|
" case11. A(::B.|)
" case12. (A**)::B.|
"
" Return: OmniInfo
" OmniInfo
" {
" 'omniss': <OmniSS>
" 'precast': <this|<global>|precast>
" }
"
" 列表 OmniSS, 每个条目为 OmniScope
" 'til' 一般在 'kind' 为 'container' 时才有效
" OmniScope
" {
" 'kind': <'container'|'variable'|'function'|'cast'|'unknown'>
" 'name': <name> <- 必然是单元类型 eg. A<a,b,c>
" 'til' : <template initialization list>
" 'tag' : {} <- 在解析的时候添加
" 'typeinfo': {} <- 在解析的时候添加
" }
"
" 如 case3, [{'kind': 'container', 'name': 'A'},
" {'kind': 'function', 'name': 'B'},
" {'kind': 'variable', 'name': 'C'}]
" 如 case6, [{'kind': 'variable', 'name': 'B'},
" {'kind': 'variable', 'name': 'C'}]
"
" 判断 cast 的开始: 1. )单词, 2. )(
" 判断 precast: 从 )( 匹配的结束位置寻找匹配的 ), 如果匹配的 ')' 右边也为 ')'
" 判断 postcast: 从 )( 匹配的结束位置寻找匹配的 ), 如果匹配的 ')' 右边不为 ')'
" TODO:
" 1. A<B>::C<D, E>::F g; g.|
" 2. A<B>::C<D, E>::F.g.| (g 为静态变量)
"
" 1 的方法, 需要记住整条路径每个作用域的 til
" 2 的方法, OmniInfo 增加 til 域
function! omnicpp#resolvers#GetOmniInfo(...) "{{{2
if a:0 > 0
if type(a:1) == type('')
let lTokens = omnicpp#tokenizer#Tokenize(
\omnicpp#utils#SimplifyCodeForOmni(a:1))
elseif type(a:1) == type([])
let lTokens = a:1
else
return []
endif
else
let lTokens = omnicpp#utils#TokenizeStatementBeforeCursor(1)
endif
let lRevTokens = reverse(lTokens[:])
let lOmniSS = []
" 'til' 对应于 'precast' 的值
let dOmniInfo = {'omniss': [], 'precast': '', 'til': []}
" 反向遍历分析
let idx = 0
let nLen = len(lRevTokens)
" 0 -> 期望操作符 ('->', '.', '::')
" 1 -> 期望单词 ( A|:: )
" 2 -> 期望左括号 ( A(|). )
let nState = 0
" 只有四种需要分辨的符号
" 1. 作用域或成员操作符 ('::', '.', '->')
" 2. 单词
" 3. 左括号
" 4. 右括号
let dPrevToken = {'kind': '', 'value': ''}
let til = [] " 保存最近一次分析的 til
while idx < nLen
let dToken = lRevTokens[idx]
if dToken.kind == 'cppOperatorPunctuator'
\&& dToken.value =~ '->\|::\|\.'
if nState == 0
" 期望操作符, 遇到操作符
" 切换状态
let nState = 1 " 期望单词
elseif nState == 1
" 期望单词, 遇到操作符
" 语法错误
echom 'syntax error: ' . dToken.value
let lOmniSS = []
break
elseif nState == 2
" 括号状态都已经私自处理完, 这里不再需要了
endif
elseif dToken.kind == 'cppWord'
\|| (dToken.kind == 'cppKeyword' && dToken.value ==# 'this')
if nState == 0
" 期望操作符, 遇到单词
" 结束. eg A::B C::|
" ^
break
elseif nState == 1
" 成功获取一个单词
let dOmniScope = s:NewOmniScope()
let dOmniScope.name = dToken.value
if dPrevToken.value == '::'
let dOmniScope.kind = 'container'
let dOmniScope.til = til
let til = []
elseif dPrevToken.value =~# '->\|\.'
let dOmniScope.kind = 'variable'
else
endif
" 切换至期望操作符
let nState = 0
if dOmniScope.name ==# 'this'
let dOmniInfo.precast = 'this'
else
let lOmniSS = [dOmniScope] + lOmniSS
endif
elseif nState == 2
" 括号状态都已经私自处理完, 这里不再需要了
endif
elseif dToken.kind == 'cppOperatorPunctuator' && dToken.value == '('
" eg1: A(B.C()->|)
" ^
" eg2: A(::B.C()->|)
" ^
if dPrevToken.kind == 'cppOperatorPunctuator'
\&& dPrevToken.value == '::'
let dOmniInfo.precast = '<global>'
endif
" 结束
break
elseif dToken.kind == 'cppOperatorPunctuator' && dToken.value == ')'
if nState == 0
" 期待操作符
" 遇到右括号
" 必定是一个 postcast
" 无须处理, 直接完成
" eg. (A*)B->|
" ^
break
elseif nState == 1
" 期待单词
" 遇到右括号
" 可能是 precast 或者 postcast((A)::B.|) 或者是一个函数
" ^
if g:VLOmniCpp_UsePython
" 使用 python 的 Tokenize 时,不剔除函数的参数,在这里完成
let j = idx + 1
let tmp = 1
while j < nLen
let dTmpToken = lRevTokens[j]
if dTmpToken.value ==# ')'
let tmp += 1
elseif dTmpToken.value ==# '('
let tmp -= 1
if tmp == 0
break
endif
endif
let j += 1
endwhile
if j + 1 < nLen && lRevTokens[j+1].kind == 'cppWord'
" TODO: Func<T>(0)
" ^
" 确定是函数
if j - 1 >= idx + 1
call remove(lRevTokens, idx + 1, j - 1)
let nLen = len(lRevTokens)
endif
endif
endif
" 判定是否函数
if lRevTokens[idx+1].value == '('
\&& lRevTokens[idx+2].kind == 'cppWord'
" 是函数
let dOmniScope = s:NewOmniScope()
let dOmniScope.kind = 'function'
let dOmniScope.name = lRevTokens[idx+2].value
let lOmniSS = [dOmniScope] + lOmniSS
let idx = idx + 2
let dToken = lRevTokens[idx]
" 切换至期待操作符
let nState = 0
elseif (lRevTokens[idx+1].kind == 'cppWord'
\|| lRevTokens[idx+1].value ==# ')')
\ && dPrevToken.value != '::'
" 是一个 precast
" eg. ((A*)B.b)->C.|
" ^|
" eg. ((A*)B.b())->C.|
" ^|
" eg. static_cast<A *>(B.b())->C.|
" ^|
" 先添加变量 scope
let dOmniScope = s:NewOmniScope()
let dOmniScope.kind = 'variable'
"let dOmniScope.name = lRevTokens[idx+1].value
let dOmniScope.name = "<CODE>" " 无需名字
let lOmniSS = [dOmniScope] + lOmniSS
" 直接处理完 precast. eg. ((A*)B)|
" 寻找匹配的左括号的位置
let j = idx + 1
let tmp = 1
while j < nLen
let dTmpToken = lRevTokens[j]
if dTmpToken.value == ')'
let tmp += 1
elseif dTmpToken.value == '('
let tmp -= 1
if tmp == 0
break
endif
endif
let j += 1
endwhile
"let j -= 1
if lRevTokens[j-1].value ==# '('
" 传统的类型转换
" 获取需要解析变量类型的 tokens
let k = j - 2
let tmp = 1
while k >= 0
let dTmpToken = lRevTokens[k]
if dTmpToken.value == ')'
let tmp -= 1
if tmp == 0
break
endif
elseif dTmpToken.value == '('
let tmp += 1
endif
let k -= 1
endwhile
let dTmpTypeInfo = omnicpp#utils#GetVariableType(
\reverse(lRevTokens[k+1 : j-2]))
let dOmniInfo.precast = dTmpTypeInfo.name
let dOmniInfo.til = dTmpTypeInfo.til
" 应该保存整个 typeinfo 备用
let dOmniInfo.pretypeinfo = dTmpTypeInfo
elseif (j + 1) < nLen && lRevTokens[j+1].value ==# '>'
" C++ 形式的类型转换
" 获取需要解析变量类型的 tokens
let k = j + 2
let tmp = 1
while k < nLen
let dTmpToken = lRevTokens[k]
if dTmpToken.value == '>'
let tmp += 1
elseif dTmpToken.value == '<'
let tmp -= 1
if tmp == 0
break
endif
endif
let k += 1
endwhile
let dTmpTypeInfo = omnicpp#utils#GetVariableType(
\reverse(lRevTokens[j+1 : k+1]))
let dOmniInfo.precast = dTmpTypeInfo.name
let dOmniInfo.til = dTmpTypeInfo.til
" 应该保存整个 typeinfo 备用
let dOmniInfo.pretypeinfo = dTmpTypeInfo
else
echom 'syntax error: ' . dToken.value
let lOmniSS = []
break
endif
" 处理完毕
break
elseif dPrevToken.kind == 'cppOperatorPunctuator'
\&& dPrevToken.value == '::'
" postcast
" eg. (A**)::B.|
" |^^
let dOmniInfo.precast = '<global>'
break
else
echom 'syntax error: ' . dToken.value
let lOmniSS = []
break
endif
elseif nState == 2
" 括号状态都已经私自处理完, 这里不再需要了
endif
elseif dToken.kind == 'cppOperatorPunctuator' && dToken.value == ']'
" Option: 可以在处理前剔除所有 []
" 处理数组下标
" eg. A[B][C[D]].|
if nState == 1 "期待单词时遇到 ']'
" 跳到匹配的 '['
let j = idx + 1
let tmp = 1
while j < nLen
let dTmpToken = lRevTokens[j]
if dTmpToken.value == ']'
let tmp += 1
elseif dTmpToken.value == '['
let tmp -= 1
if tmp == 0
break
endif
endif
let j += 1
endwhile
let dToken = dPrevToken " 保持 dPrevToken
let idx = j
else
echom 'syntax error: ' . dToken.value
let lOmniSS = []
break
endif
elseif dToken.kind == 'cppOperatorPunctuator' && dToken.value == '>'
" 处理模板实例化
" eg. A<B, C>::|
if nState == 0 " 期待操作符时遇到 '>'
" eg. if ( 1 > A.| )
" 结束
break
elseif nState == 1 " 期待单词时遇到 '>'
" 跳到匹配的 '<'
let j = idx + 1
let tmp = 1
while j < nLen
let dTmpToken = lRevTokens[j]
if dTmpToken.value == '>'
let tmp += 1
elseif dTmpToken.value == '<'
let tmp -= 1
if tmp == 0
break
endif
endif
let j += 1
endwhile
" 分析 til
let lTmpTokens = reverse(lRevTokens[idx : j+1])
let til = omnicpp#utils#GetVariableType(lTmpTokens).til
let dToken = dPrevToken " 保持 dPrevToken
let idx = j
else
echom 'syntax error: ' . dToken.value
let lOmniSS = []
break
endif
else
" 遇到了其他字符, 结束. 前面判断的结果多数情况下是有用
if !empty(dPrevToken) && dPrevToken.kind == 'cppOperatorPunctuator'
\&& dPrevToken.value == '::'
let dOmniInfo.precast = '<global>'
endif
" 检查是否 new 语法. eg. new A::B|
if dToken.kind == 'cppKeyword' && dToken.value ==# 'new'
let dOmniInfo.new = 1
endif
break
endif
let dPrevToken = dToken
let idx += 1
endwhile
" eg. ::A->|
if !empty(dPrevToken) && dPrevToken.kind == 'cppOperatorPunctuator'
\&& dPrevToken.value == '::'
let dOmniInfo.precast = '<global>'
endif
let dOmniInfo.omniss = lOmniSS
return dOmniInfo
endfunc
"}}}
" 从 scope 和 name 组合生成 path, 处理烦人的 '<global>'
function! s:GenPath(sScope, sName) "{{{2
if a:sScope != '<global>'
return a:sScope . '::' . a:sName
else
return a:sName
endif
endfunc
"}}}
" Return: ScopeInfo
" {
" 'function': [], <- 函数作用域, 一般只用名空间信息
" 'container': [], <- 容器的作用域列表, 包括名空间
" 'global': [] <- 全局(文件)的作用域列表, 包括名空间
" }
" NOTE: 理论上可能会有嵌套名空间的情况, 但是为了简化, 不允许使用嵌套名空间
" eg. using namespace A; using namespace B; -> A::B::C <-> C
function! omnicpp#resolvers#ResolveScopeStack(lScopeStack) "{{{2
let dResult = {'function': [], 'container': [], 'global': []}
let lFunctionScopes = []
let lContainerScopes = []
let lGlobalScopes = []
let lCtnPartScp = []
let g:dOCppNSAlias = {} " 名空间别名 {'abc': 'std'}
let g:dOCppUsing = {} " using. eg. {'cout': 'std::out', 'cin': 'std::cin'}
let nSSLen = len(a:lScopeStack)
let idx = 0
while idx < nSSLen
let dScope = a:lScopeStack[idx]
if dScope.kind == 'file'
call extend(g:dOCppNSAlias, dScope.nsinfo.nsalias)
call extend(g:dOCppUsing, dScope.nsinfo.using)
call extend(lGlobalScopes, dScope.nsinfo.usingns)
" 把 <global> 放最后,虽然理论上当有名字二义性的时候是编译错误
" 但是在模糊模式里面,要把全局搜索域放到最后
call add(lGlobalScopes, '<global>')
elseif dScope.kind == 'container'
call add(lCtnPartScp, dScope.name)
call extend(g:dOCppNSAlias, dScope.nsinfo.nsalias)
call extend(g:dOCppUsing, dScope.nsinfo.using)
call extend(lContainerScopes, dScope.nsinfo.usingns)
elseif dScope.kind == 'function'
" 仅收集 nsinfo
call extend(g:dOCppNSAlias, dScope.nsinfo.nsalias)
call extend(g:dOCppUsing, dScope.nsinfo.using)
call extend(lFunctionScopes, dScope.nsinfo.usingns)
elseif dScope.kind == 'other'
" 仅收集 nsinfo
while idx < nSSLen
let dScope = a:lScopeStack[idx]
call extend(g:dOCppNSAlias, dScope.nsinfo.nsalias)
call extend(g:dOCppUsing, dScope.nsinfo.using)
" TODO: 应该加到哪里
call extend(lFunctionScopes, dScope.nsinfo.usingns)
let idx += 1
endwhile
break
endif
let idx += 1
endwhile
let dResult.function = lFunctionScopes
let dResult.container = lContainerScopes
let dResult.global = lGlobalScopes
let idx = 0
while idx < len(lCtnPartScp)
" 处理嵌套类
" eg.
" void A::B::C::D()
" {
" |
" }
" ['A', 'B', 'C'] -> ['A', 'A::B', 'A::B::C']
" ['A', 'A::B', 'A::B::C'] 中的每个元素也必须展开其基类
let sCtn = join(lCtnPartScp[:idx], '::')
" 展开继承(typedef?)的类
let lEpdScp = s:ExpandClassScope(sCtn, dResult)
call extend(lContainerScopes, lEpdScp, 0)
let idx += 1
endwhile
let dResult.function = lFunctionScopes
let dResult.container = lContainerScopes
let dResult.global = lGlobalScopes
return dResult
endfunc
"}}}
" 解析 OmniInfo, 返回用于获取 tags 的 omniTagScopes
function! omnicpp#resolvers#ResolveOmniInfo(lScopeStack, dOmniInfo) "{{{2
if empty(a:lScopeStack)
\|| (empty(a:dOmniInfo.omniss) && a:dOmniInfo.precast ==# '')
return []
endif
let lResult = []
let dOmniInfo = a:dOmniInfo
let lOmniSS = dOmniInfo.omniss
let dScopeInfo = omnicpp#resolvers#ResolveScopeStack(a:lScopeStack)
let lTagScopes = dScopeInfo.container + dScopeInfo.global
\+ dScopeInfo.function
let lSearchScopes = lTagScopes[:]
let lMemberStack = lOmniSS[:]
" 预处理, 主要针对 this-> 和 cast
if dOmniInfo.precast !=# ''
" 生成 SearchScopes
let dNewOmniScope = s:NewOmniScope()
if dOmniInfo.precast ==# 'this'
" 亦即把 this-> 替换成 MyCls::
let dNewOmniScope.name = lSearchScopes[0]
" precast 为 this 的时候, 直接从 <global> 搜索
" 因为已把 this 替换为绝对路径的容器.
" 即使是嵌套容器的情形, 也能正常工作
" 或者, 作为另外一个解决方案, 直接在这里解析完毕
" void A::B()
" {
" this->|
" }
let lSearchScopes = dScopeInfo.global
let dNewOmniScope.kind = 'container'
let lMemberStack = [dNewOmniScope] + lOmniSS[0:]
elseif dOmniInfo.precast ==# '<global>'
" 仅搜索全局作用域以及在全局作用域中的 using namespace
let lSearchScopes = dScopeInfo.global
else
let dNewOmniScope.name = dOmniInfo.precast
let dNewOmniScope.kind = 'container'
if has_key(dOmniInfo, 'pretypeinfo')
let dNewOmniScope.pretypeinfo = dOmniInfo.pretypeinfo
endif
let lMemberStack = [dNewOmniScope] + lOmniSS[1:]
endif
endif
let lResult = omnicpp#resolvers#ResolveOmniScopeStack(
\lSearchScopes, lMemberStack, dScopeInfo)
return lResult
endfunc
"}}}
" 递归解析补全请求的补全 scope
" 返回用于获取 tags 的 TagScopes
" NOTE: 不支持嵌套定义的模版类, 也不打算支持, 因诸多原因.
" 基本算法:
" 1) 预处理第一个非容器成员(变量, 函数), 使第一个成员变为容器
" 2) 处理 MemberStack 时, 第一个是变量或者函数或者经过类型替换后, 重头开始
" 3) 解析变量和函数的时候, 需要搜索变量和函数的 path 中的每段 scope
" 4) 每解析一次都要检查是否存在类型替换
" 5) 重复 2), 3), 4)
function! omnicpp#resolvers#ResolveOmniScopeStack(
\lSearchScopes, lMemberStack, dScopeInfo) "{{{2
" 每次解析变量的 TypeInfo 时, 应该添加此变量的作用域到此类型的搜索域
" eg.
" namespace A
" {
" class B {
" };
"
" class C {
" B b;
" };
" }
" b 变量的 TypeInfo {'name': 'B', 'til': []}
" 搜索变量 B 时需要添加 b 的 scope 的所有可能情况, A::B -> ['A::C', 'A']
let lSearchScopes = a:lSearchScopes
let dScopeInfo = a:dScopeInfo
let lOrigScopes = dScopeInfo.container + dScopeInfo.global
\+ dScopeInfo.function
" dMember = {'kind': x, 'name': y, 'til': z, 'tag': A, 'typeinfo': B}
" kind: 类型
" name: 名字, 必为单元类型
" til: name 对应的 til, 原始信息, 不可信, 不可改
" tag: 对应的 tag, 与 typeinfo 相对应
" typeinfo: 对应的 typeinfo, 必须是全局路径的, 不能是局部路径.
" 这个变量是非常重要的数据, 作为很多处理的依据
" typeinfo 需要 tag.pah 和前面的 dMember 来最终确认
" 其中前面的 dMember
" 任何情况下, 解析当前的符号时, 只需要上一个符号的 typeinfo
" 即可完成所有任务(如嵌套模版类)
" 所以, 最终目标是只需要获取当前的 typeinfo 即可
let lMemberStack = a:lMemberStack
let idx = 0
let bNeedExpandUsing = 1 " 是否需要考虑 using 指令
let dTypeInfo = s:NewTypeInfo() " 一般用于保存变量局部类型信息
while idx < len(lMemberStack)
let dMember = lMemberStack[idx]
" 当前搜索用名称, member 的类型不是 'container', 需要解析
" sCurName 可以为单元类型(std) 也可为复合类型(map::iterator)
let sCurName = dMember.name
let dMember.typeinfo = s:NewTypeInfo()
if dMember.kind == 'container'
let sCurName = dMember.name
if idx == 0 && bNeedExpandUsing
" 起点容器需要展开名空间信息
let sTmpName = s:ExpandUsingAndNSAlias(dMember.name)
if dMember.name !=# sTmpName
" 展开了 using, 需要重建 typeinfo
" eg.
" using A::B;
" B<C,D> b;
"
" Original: B<C,D>
" Reuslt: A::B<C,D>
let sCode = sTmpName
if !empty(dMember.til)
let sCode .= '< ' . join(dMember.til, ', ') . ' >'
endif
let dTypeInfo = omnicpp#utils#GetVariableType(sCode)
let sCurName = dTypeInfo.name
endif
endif
elseif dMember.kind == 'variable'
if idx == 0
let lSearchScopes = lOrigScopes
" 第一次进入, 因为 ctags 不支持局部变量,
" 所以如果起点为变量时, 需要分析是否在当前局部作用域内定义了
let dTypeInfo = omnicpp#resolvers#ResolveFirstVariable(
\lSearchScopes, dMember.name)
if len(dTypeInfo.types) > 1
" 复合类型, 重新开始
let sCode = omnicpp#utils#GenCodeFromTypeInfo(dTypeInfo)
" 解析完毕的类型必然是容器, 所以附加 '::'
let dTmpOmniInfo = omnicpp#resolvers#GetOmniInfo(
\sCode . '::')
if dTmpOmniInfo.precast ==# '<global>'
" 可能带 '::' 前缀
let lOrigScopes = dScopeInfo.global
let lSearchScopes = lOrigScopes
endif
" 替换当前 dMember
call remove(lMemberStack, idx)
call extend(lMemberStack, dTmpOmniInfo.omniss, 0)
else
" 非复合类型, 也可能是解析失败
" 修正当前 dMember
let lMemberStack[idx].kind = 'container'
let lMemberStack[idx].name = dTypeInfo.name
let lMemberStack[idx].til = dTypeInfo.til
endif
" 重头再来, 会从容器开始
let idx = 0
let bNeedExpandUsing = 1
continue
else
" variable 常规处理
let dTmpTag = s:GetFirstMatchTag(lSearchScopes, dMember.name)
if empty(dTmpTag)
let lSearchScopes = []
break
endif
" 从 text 域解析变量
let dTypeInfo = s:GetVariableTypeInfo(dTmpTag.text, dTmpTag.name)
let dMember.typeinfo = dTypeInfo
let dMember.tag = dTmpTag
let sCurName = dTypeInfo.name
" 解释出了当前变量的类型,需要检查是否需要处理模板
" TODO: 要处理复杂的模板参数: T1::T2 t;
" 暂时只处理最简单的情形: T t;
let sCode = omnicpp#resolvers#ResolveTemplate(
\lMemberStack[idx-1], dMember)
if sCode != ''
" 处理了模版替换, 需要重头再来
" 解析完毕的类型必然是容器, 所以附加 '::'
let dTmpOmniInfo = omnicpp#resolvers#GetOmniInfo(
\sCode . '::')
" 替换 dMember
call remove(lMemberStack, 0, idx)
call extend(lMemberStack, dTmpOmniInfo.omniss, 0)
" TODO: 是否需要添加局部搜索域?
let lSearchScopes = lOrigScopes
if dTmpOmniInfo.precast ==# '<global>'
" 可能带 '::' 前缀
let lOrigScopes = dScopeInfo.global
let lSearchScopes = lOrigScopes
endif
" 重头再来
let idx = 0
let bNeedExpandUsing = 1
continue
endif
" 变量的情况, 需要添加变量的 path 的所有途经的 scope
" eg. std::map::iterator = iterator -> std::map, std
" TODO: 是否需要添加上 lOrigScopes ?
" 若是模版变量, 应该需要添加 lOrigScopes
" 因为可能在声明的时候用到了 using
" eg.
" using namespace std;
" map<string, int> foo;
call extend(
\lSearchScopes,
\s:ExpandSearchScopeStatckFromScope(
\ dTmpTag.scope),
\0)
endif
elseif dMember.kind == 'function'
if idx == 0
" 若为起点, 特殊处理
" 先处理名空间问题
let dMember.name = s:ExpandUsingAndNSAlias(dMember.name)
let dTmpTag = s:GetFirstMatchTag(lSearchScopes, dMember.name)
if empty(dTmpTag)
let lSearchScopes = []
break
endif
" 添加局部搜索域
call extend(
\lSearchScopes,
\s:ExpandSearchScopeStatckFromScope(dTmpTag.scope),
\0)
let dTypeInfo = omnicpp#utils#GetVariableType(
\get(dTmpTag, 'return', ''))
let sCode = omnicpp#utils#GenCodeFromTypeInfo(dTypeInfo)
" 解析完毕的类型必然是容器, 所以附加 '::'
let dTmpOmniInfo = omnicpp#resolvers#GetOmniInfo(
\sCode . '::')
" 可能带 '::' 前缀
if dTmpOmniInfo.precast ==# '<global>'
let lOrigScopes = dScopeInfo.global
let lSearchScopes = lOrigScopes
endif
" 替换当前 dMember
call remove(lMemberStack, idx)
call extend(lMemberStack, dTmpOmniInfo.omniss, 0)
" 重头再来
let idx = 0
let bNeedExpandUsing = 0 " 函数的返回不能依赖 using
continue
else
" function 常规处理
let dTmpTag = s:GetFirstMatchTag(lSearchScopes, dMember.name)
if empty(dTmpTag)
let lSearchScopes = []
break
endif
" NOTE: 从 'return' 提取的 typeinfo 不是全局路径
let dTypeInfo = omnicpp#utils#GetVariableType(
\get(dTmpTag, 'return', ''))
let dMember.typeinfo = dTypeInfo
let dMember.tag = dTmpTag
let sCurName = dTypeInfo.name
try
let sCode = omnicpp#resolvers#ResolveTemplate(
\lMemberStack[idx-1], dMember)
if sCode !=# ''
" 处理了模版替换, 需要重头再来
" 解析完毕的类型必然是容器, 所以附加 '::'
let dTmpOmniInfo = omnicpp#resolvers#GetOmniInfo(
\sCode . '::')
" 替换 dMember
call remove(lMemberStack, 0, idx)
call extend(lMemberStack, dTmpOmniInfo.omniss, 0)
" TODO: 是否需要添加局部搜索域?
let lSearchScopes = lOrigScopes
if dTmpOmniInfo.precast ==# '<global>'
" 可能带 '::' 前缀
let lOrigScopes = dScopeInfo.global
let lSearchScopes = lOrigScopes
endif
" 重头再来
let idx = 0
let bNeedExpandUsing = 1
continue
endif
catch
" 语法错误
echom 'Is there any syntax error?'
let lSearchScopes = []
break
endtry
" 函数的情况, 需要添加函数的 path 的所有途经的 scope
" 即原始 Scopes + 函数 path 途经的 Scopes
" NOTE: 不需要添加 lOrigScopes
call extend(
\lSearchScopes,
\s:ExpandSearchScopeStatckFromScope(dTmpTag.scope),
\0)
endif
endif
"=======================================================================
" lSearchScopes 的展开已经完成, 剔除重复的搜索域
call omnicpp#utils#FilterListSameElement(lSearchScopes)
let dCurTag = s:GetFirstMatchTag(lSearchScopes, sCurName)
if empty(dCurTag)
" 处理匿名容器, 因为匿名容器是不存在对应的 tag 的
" 所以如果有需要的时候, 手动构造匿名容器的 tag, 然后继续
if dMember.kind == 'variable' && idx != 0
\&& has_key(dTmpTag, 'typeref')
" 匿名容器时, 不能从 cmd 域解析变量
" 直接从 typeref 域获取
" eg.
" struct {
" struct {
" int n;
" } s;
" } st;
" st.s.|
let lTyperef = split(dTmpTag.typeref, '\w\zs:\ze\w')
let sKind = lTyperef[0]
let sPath = lTyperef[1]
let dTypeInfo = omnicpp#utils#GetVariableType(sPath)
" 在此构造无名容器的 tag 作为获取成功的 tag
call remove(dTmpTag, 'typeref')
let dTmpTag.name = matchstr(sPath, '__anon\d\+_\w*$')
let dTmpTag.kind = sKind[0] " 类型是单字母的
let dTmpTag.path = sPath
if sPath =~# '::'
let dTmpTag.scope = join(
\split(sPath, '::')[: -2], '::')
else
let dTmpTag.scope = '<global>'
endif
let dMember.typeinfo = dTypeInfo
let dCurTag = dTmpTag
else
let lSearchScopes = []
break
endif
endif
let dMember.tag = dCurTag
if dMember.kind == 'container'
\|| ((dMember.kind == 'function'
\ || dMember.kind == 'variable')
\ && dMember.tag.scope ==# lMemberStack[idx-1].tag.path)
" 容器类型必须处理
" 并且以下条件满足时,也需要拼接 til
" dMember.tag.scope ==# lMemberStack[idx-1].tag.path
" eg. GNU 头文件中的 std::map::iterator
" 从 dCurTag 和 上一个的 typeinfo.types 的 til 生成当前的 typeinfo
" eg. A::B::C 和 [['X', 'Y'], ['Z']]
" ->
" A<X, Y>::B<Z>::C 的 typeinfo
" NOTE: 此 typeinfo 必须为全局路径
let dTypeInfo = omnicpp#utils#GetVariableType(dCurTag.path)
let dTypeInfo.types[-1].til = dMember.til[:]
let dTypeInfo.til = dTypeInfo.types[-1].til
let dMember.typeinfo = dTypeInfo
if idx > 0
let dPrevTypeInfo = lMemberStack[idx-1].typeinfo
" 最外层的 til 为 dMember.til
let dTypeInfo.types[-1].til = dMember.til[:]
let nTmpIdx = 0
for dTmpUnitType in dPrevTypeInfo.types
let dTypeInfo.types[nTmpIdx].til = dTmpUnitType.til
let nTmpIdx += 1
endfor
let dTypeInfo.til = dTypeInfo.types[-1].til
let dMember.typeinfo = dTypeInfo
endif
endif
" 解析 tag 前处理一次类型替换
let dReplTypeInfo = s:ResolveTypeReplacement(dMember.typeinfo)
if !empty(dReplTypeInfo)
" 替换成功了, 需要重新整理 lMemberStack
let sCode = omnicpp#utils#GenCodeFromTypeInfo(dReplTypeInfo)
" 解析完毕的类型必然是容器, 所以附加 '::'
let dTmpOmniInfo = omnicpp#resolvers#GetOmniInfo(
\sCode . '::')
" 可能带 '::' 前缀
if dTmpOmniInfo.precast ==# '<global>'
let lOrigScopes = dScopeInfo.global
let lSearchScopes = lOrigScopes
endif
" 替换当前 dMember
call remove(lMemberStack, 0, idx)
call extend(lMemberStack, dTmpOmniInfo.omniss, 0)
" 重头再来
let idx = 0
let bNeedExpandUsing = 0 " 类型替换不能依赖 using
let lSearchScopes = lOrigScopes
continue
endif
let lSearchScopes = s:GetTopLevelSearchScopesFromTag(
\dMember.tag, dScopeInfo, dMember.typeinfo)
" 可能修改了 tag(例如 typedef), 需要再一次检查类型替换
let dReplTypeInfo = s:ResolveTypeReplacement(dMember.typeinfo)
if !empty(dReplTypeInfo)
" 替换成功了, 需要重新整理 lMemberStack
let sCode = omnicpp#utils#GenCodeFromTypeInfo(dReplTypeInfo)
" 解析完毕的类型必然是容器, 所以附加 '::'
let dTmpOmniInfo = omnicpp#resolvers#GetOmniInfo(
\sCode . '::')
" 可能带 '::' 前缀
if dTmpOmniInfo.precast ==# '<global>'
let lOrigScopes = dScopeInfo.global