[技术学习]snippetsEmu代码注释

" File: snippetsEmu.vim " Author: Felix Ingram " ( f.ingram.lists <AT> gmail.com ) " Description: An attempt to implement TextMate style Snippets. Features include " automatic cursor placement and command execution. " 描述: 尝试去实现TextMate的Snippets风格。 特点包括了自动光标补全和命令执行。 " $LastChangedDate$ " Version: 1.1 " 版本: 1.1 " $Revision$ " " This file contains some simple functions that attempt to emulate some of the " behaviour of 'Snippets' from the OS X editor TextMate, in particular the " variable bouncing and replacement behaviour. " 本文件包含了一些简单的函数以尝试模拟OS " X系统中编辑器TextMater的Snippets(插件)的一些行为。 " 这一句怎么翻译: variable bouncing and replacement behaviour. " " {{{ USAGE: " {{{ 使用说明: " " Place the file in your plugin directory. " 将本文件保存在插件目录(默认 ~/.vim/plugin) " Define snippets using the Snippet command. " 用Snippet命令定义snippets。 " Snippets are best defined in the 'after' subdirectory of your Vim home " Snippets 最好定义在vim家目录的after子目录中(在Unix中是'~/.vim/after')。 " directory ('~/.vim/after' on Unix). Filetype specific snippets can be defined " in '~/.vim/after/ftplugin/<filetype>_snippets.vim. Using the <buffer> argument will " By default snippets are buffer specific. To define general snippets available " globally use the 'Iabbr' command. " 每种文件类型的snippets说明定义在 " '~/.vim/after/ftplugin/<filetype>_snippets.vim'。使用<buffer>参数,snippets " 默认是缓冲区特定的。 使用Iabbr命令定义通用的snippets的全局变量。 " " Example One: " Snippet fori for <{datum}> in <{data}>:<CR><{datum}>.<{}> " " The above will expand to the following (indenting may differ): " " for <{datum}> in <{data}>: " <{datum}>.<{}> " " ============================================================ " 例一: " Snippet fori for <{datum}> in <{data}>:<CR><{datum}>.<{}> " " 以上的代码会扩展成下面的形式(缩进可能有所不同): " " for <{datum}> in <{data}>: " <{datum}>.<{}> " ============================================================ " " The cursor will be placed after the first '<{' in insert mode. " Pressing <Tab> will 'tab' to the next place marker (<{data}>) in " insert mode. Adding text between <{ and }> and then hitting <{Tab}> will " remove the angle brackets and replace all markers with a similar identifier. " " ============================================================ " 光标将以插入模式定位在第一个 '<{'。键入<Tab>键将在插入模式下跳到(tab)下一个 " 占位符(<{data}>)。在<{和}>之间添加文本,然后键入<{Tab}>将会删除尖括号并且以 " 一个相似的标识符替代所有的标记。 " ============================================================ " Example Two: " With the cursor at the pipe, hitting <Tab> will replace: " for <{MyVariableName|datum}> in <{data}>: " <{datum}>.<{}> " " with (the pipe shows the cursor placement): " " for MyVariableName in <{data}>: " MyVariableName.<{}> " " Enjoy. " =========================================================== " 例二: " 当光标放置在管道的位置,键入<Tab>键将替代: " for <{MyVariableName|datum}> in <{data}>: " <{datum}>.<{}> " " 以(管道显示光标位置): " for MyVariableName in <{data}>: " MyVariableName.<{}> " 享受snippets吧。 " =========================================================== " " For more information please see the documentation accompanying this plugin. " " =========================================================== " 更多的信息请参见本插件附加的文档 " =========================================================== " Additional Features: " " Commands in tags. Anything after a ':' in a tag will be run with Vim's " 'execute' command. The value entered by the user (or the tag name if no change " has been made) is passed in the @z register (the original contents of the " register are restored once the command has been run). " " =========================================================== " 附加的特性: " 标记命令。 在标记(tag)中,":"后面的任何东西都将会以Vim的execute命令来执行。 " 用户键入的值(value)(或者是标记名字如果没有修改)被传入到 " ============================================================ " " Example Two: " With the cursor at the pipe, hitting <Tab> will replace: " for <{MyVariableName|datum}> in <{data}>: " <{datum}>.<{}> " ============================================================ " 例二: " 当光标在管道上,键入<Tab>将替代: " for <{MyVariableName|datum}> in <{data}>: " <{datum}>.<{}> " ============================================================ " Named Tags. Naming a tag (the <{datum}> tag in the example above) and changing " the value will cause all other tags with the same name to be changed to the " same value (as illustrated in the above example). Not changing the value and " hitting <Tab> will cause the tag's name to be used as the default value. " " ============================================================== " 命令标记。 命名一个标识(如上例中的<{datum}>标记)和改变值将导致所有其它的 " 与之同名的标记都改变成同样的值(如上例所示)。既不更改值和又不键入<Tab>将 " 使得标记的名字使用默认的值。 " ============================================================== " " Test tags for pattern matching: " The following are examples of valid and invalid tags. Whitespace can only be " used in a tag name if the name is enclosed in quotes. " " ============================================================== " 为模式匹配测试标记 " 以下验证和非验证的标记。空格键仅当名字被引号包括时能被用在标记名字中。 " " Valid tags " <{}> " <{tagName}> " <{tagName:command}> " <{"Tag Name"}> " <{"Tag Name":command}> " " Invalid tags, random text " <{:}> " <{:command}> " <{Tag Name}> " <{Tag Name:command}> " <{"Tag Name":}> " <{Tag }> " <{OpenTag " " /{-} 匹配 0 个以上前面的匹配原。尽可能少 " *[:punct:]* [:punct:] 标点字符 " |//?| /? /? 0 或 1 尽可能多 (*) " Here's our magic search term (assumes '<{',':' and '}>' as our tag delimiters: " <{ /( " [ ^[:punct:] /t]/{-} " /|"./{-}" " /) " /( :[^}>]/{-1,}/) " /? " }> " }}} "判断版本号 if v:version < 700 echomsg "snippetsEmu plugin requires Vim version 7 or later" finish endif "判断老旧版本 if globpath(&rtp, 'plugin/snippetEmu.vim') != "" call confirm("It looks like you've got an old version of snippetsEmu installed. Please delete the file 'snippetEmu.vim' from the plugin directory. Note lack of 's'") endif let s:debug = 0 "调试标志 0:关闭 1:开启 let s:Disable = 0 "可用标志 0:可用 "调试函数 调试开则输出信息 function! s:Debug(func, text) if exists('s:debug') && s:debug == 1 echom "Snippy: ".a:func.": ".a:text endif endfunction "防止重加载,如果已经加载loaded_snippet为1(见下文),此时如果不是用于调试,则应退出,防止重加载 if (exists('loaded_snippet') || &cp) && !s:debug finish endif "call s:Debug("","Started the plugin") "加载标志 let loaded_snippet=1 " {{{ Set up variables " 设置界定符号 if !exists("g:snip_start_tag") let g:snip_start_tag = "<{" endif if !exists("g:snip_end_tag") let g:snip_end_tag = "}>" endif if !exists("g:snip_elem_delim") let g:snip_elem_delim = ":" endif if !exists("g:snippetsEmu_key") let g:snippetsEmu_key = "<Tab>" endif "call s:Debug("", "Set variables") " }}} " {{{ Set up menu " 设置菜单 " 从对应文件名中截取出类型名作为Snippets的二级菜单,其功能就是加载对应的脚本 for def_file in split(globpath(&rtp, "after/ftplugin/*_snippets.vim"), '/n') call s:Debug("","Adding ".def_file." definitions to menu") let snip = substitute(def_file, '.*[///]/(.*/)_snippets.vim', '/1', '') exec "nmenu <silent> S&nippets.".snip." :source ".def_file."<CR>" endfor " }}} " {{{ Sort out supertab function! s:GetSuperTabSNR() let a_sav = @a "需要使用寄存器a,先保存其中的值 redir @a "重定向到寄存器a,即使用寄存器a收集信息 "输出函数列表到寄存器a exec "silent function" redir END "重定向结束 let funclist = @a "保存寄存器a的值 let @a = a_sav "恢复寄存器a的值 try let func = split(split(matchstr(funclist,'.SNR./{-}SuperTab(command)'),'/n')[-1])[1] return matchlist(func, '/(.*/)S')[1] "返回< 何解? catch /E684/ endtry return "" endfunction function! s:SetupSupertab() if !exists('s:supInstalled') let s:supInstalled = 0 endif if s:supInskalled == 1 || globpath(&rtp, 'plugin/supertab.vim') != "" "call s:Debug("SetupSupertab", "Supertab installed") let s:SupSNR = s:GetSuperTabSNR() let s:supInstalled = 1 if s:SupSNR != "" let s:done_remap = 1 "备责选项 else let s:done_remap = 0 endif endif endfunction "检测SuperTab是否安装,以及采取措施 call s:SetupSupertab() " }}} " {{{ Map Jumper to the default key if not set already " 映射Jumper到缺省键,即键绑定 待绑定的键尚未绑定 function! s:SnipMapKeys() if (!hasmapto('<Plug>Jumper','i')) "检测插入模式下是否已经映射 if s:supInstalled == 1 "已经映射,且SuperTab已经安装,绑定 " <Plug> 在脚本外部是可见的。它被用来定义那些用户可能定义映射的映射。<Plug> 是 " 无法用键盘输入的特殊代码。 " 使用结构:<Plug> 脚本名 映射名,可以使得其它插件使用同样次序的字符来定 " 义映射的几率变得非常小。在我们上面的例子中,脚本名是 "Typecorr",映射 " 名是 "Add"。结果是 "<Plug>TypecorrAdd"。只有脚本名和映射名的第一个字 " 符是大写的,所以我们可以清楚地看到映射名从什么地方开始。 exec 'imap '.g:snippetsEmu_key.' <Plug>Jumper' " else "已经映射,且SuperTab未安装,唯一绑定 exec 'imap <unique> '.g:snippetsEmu_key.' <Plug>Jumper' endif endif if (!hasmapto( 'i<BS>'.g:snippetsEmu_key, 's')) " smap <unique><Tab> i<BS><Tab> exec 'smap <unique> '.g:snippetsEmu_key.' i<BS>'.g:snippetsEmu_key endif "将<SID>Jumper()的脚本保存到寄存器=中 绑定到<Plug>Jumper imap <silent> <script> <Plug>Jumper <C-R>=<SID>Jumper()<CR> endfunction "绑定键 call s:SnipMapKeys() "call s:Debug("", "Mapped keys") " }}} " {{{ SetLocalTagVars() "设置本地界定符 function! s:SetLocalTagVars() if exists("b:snip_end_tag") && exists("b:snip_start_tag") && exists("b:snip_elem_delim") return [b:snip_start_tag, b:snip_elem_delim, b:snip_end_tag] else return [g:snip_start_tag, g:snip_elem_delim, g:snip_end_tag] endif endfunction " }}} " {{{ SetSearchStrings() - Set the search string. Checks for buffer dependence "设定搜索字符串等 search_str见头部注释最后一行 function! s:SetSearchStrings() let [snip_start_tag, snip_elem_delim, snip_end_tag] = s:SetLocalTagVars() let b:search_str = snip_start_tag.'/([^'. /snip_start_tag.snip_end_tag. /'[:punct:] /t]/{-}/|"./{-}"/)/('. /snip_elem_delim. /'[^'.snip_end_tag.snip_start_tag.']/{-1,}/)/?'.snip_end_tag let b:search_commandVal = "[^".snip_elem_delim."]*" let b:search_endVal = "[^".snip_end_tag."]*" endfunction " }}} " {{{ SetCom(text, scope) - Set command function "设置命令函数 function! <SID>SetCom(text, scope) "将文本中的<CR>、<Esc>、<Tab>、<BS>、<Space>、<C-r>、<Bar>、"、/转义,是指符合vim中正则表达式的需要 let text = substitute(a:text, '/c<CR>/|<Esc>/|<Tab>/|<BS>/|<Space>/|<C-r>/|<Bar>/|/"/|//','//&',"g") "如果SupTab安装,则设置Tab键和映射 if s:supInstalled == 1 call s:SetupSupertab() call s:SnipMapKeys() endif "删除尾部回车符号(从键盘输入时带有回车符) let text = substitute(text, "/r$", "","") "按照空格分割命令 let tokens = split(text, ' ') call filter(tokens, 'v:val != ""') "去除为空的部分 if len(tokens) == 0 let output = join(s:ListSnippets("","","",eval(a:scope)) ,"/n") if output == "" echohl Title | echo "No snippets defined" | echohl None else echohl Title | echo "Defined snippets:" | echohl None echo output endif " NOTE - cases such as ":Snippet if " will intentionally(?) be parsed as a " snippet named "if" with contents of " " elseif len(tokens) == 1 let snip = s:Hash(tokens[0]) if exists(a:scope."trigger_".snip) " FIXME - is there a better approach? " echo doesn't handle ^M correctly let pretty = substitute(eval(a:scope."trigger_".snip), "/r", "/n","g") echo pretty else echohl Error | echo "Undefined snippet: ".snip | echohl None endif else "设置新的snippets let [lhs, rhs] = [s:Hash(tokens[0]), join(tokens[1:])] "赋值tokens[0]是snippets的标识,tokens[1:]是具体的内容 call s:SetSearchStrings() let g:search_str = b:search_str exe "let ".a:scope."trigger_".lhs.' = "'.rhs.'"' endif endfunction " }}} " {{{ RestoreSearch() " Checks whether more tags exist and restores hlsearch and @/ if not function! s:RestoreSearch() if !search(b:search_str, "n") "如果找不到 if exists("b:hl_on") && b:hl_on == 1 setlocal hlsearch endif if exists("b:search_sav") let @/ = b:search_sav endif endif endfunction "}}} " {{{ DeleteEmptyTag " 删除空标记,即<{}> function! s:DeleteEmptyTag() let [snip_start_tag, snip_elem_delim, snip_end_tag] = s:SetLocalTagVars() exec "normal zv".(s:StrLen(snip_start_tag) + s:StrLen(snip_end_tag))."x" endfunction " }}} " {{{ SetUpTags() function! s:SetUpTags() let [snip_start_tag, snip_elem_delim, snip_end_tag] = s:SetLocalTagVars() "判断光标后4个字符(此处取<{}>为界定符)是否是空标记 "若是,此时光标应落在<处 "getline(".")取当前行 col(".")光标所在列数(从1计数) if (strpart(getline("."), col(".")+strlen(snip_start_tag)-1, strlen(snip_end_tag)) == snip_end_tag) "call s:Debug("SetUpTags","Found an empty tag") let b:tag_name = "" "判断空界定符是否处于一行的行尾 if col(".") + s:StrLen(snip_start_tag.snip_end_tag) == s:StrLen(getline(".")) " We delete the empty tag here as otherwise we can't determine whether we " need to send 'a' or 'A' as deleting the empty tag will sit us on the " final character either way call s:DeleteEmptyTag() "空界定符处于一行的行尾,则删除空界定符 call s:RestoreSearch() "搜索 if col(".") == s:StrLen(getline(".")) "光标处于行尾,则进入普通模式,然后以附加的方式进入插入模式 return "/<Esc>a" endif else call s:DeleteEmptyTag() "空界定符不处于一行的行尾,则删除空界定符 call s:RestoreSearch() if col(".") == s:StrLen(getline(".")) "光标不处于行尾,则进入普通模式,然后以行尾附加的方式进入插入模式 return "/<Esc>A" endif endif return '' else " Not on an empty tag so it must be a normal tag " 不是空界定符,则一定是普通的界定符 " 取出标记名称,即<{}>之间的部分 let b:tag_name = s:ChopTags(matchstr(getline("."),b:search_str,col(".")-1)) "call s:Debug("SetUpTags","On a tag called: ".b:tag_name) " Check for exclusive selection mode. If exclusive is not set then we need to " move back a character. if &selection == "exclusive" let end_skip = "" else let end_skip = "/<Left>" endif let start_skip = repeat("/<Right>",s:StrLen(snip_start_tag)+1) "call s:Debug("SetUpTags","Start skip is: ".start_skip) "call s:Debug("SetUpTags","Col() is: ".col(".")) if col(".") == 1 "call s:Debug("SetUpTags","We're at the start of the line so don't need to skip the first char of start tag") let start_skip = strpart(start_skip, 0, strlen(start_skip)-strlen("/<Right>")) "call s:Debug("SetUpTags","Start skip is now: ".start_skip) endif "call s:Debug("SetUpTags","Returning: /<Esc>".start_skip."v/".snip_end_tag."/<CR>".end_skip."/<C-g>") "进入普通模式,向右越过开始界定符,进入可视模式,搜索选择直至结束界定符,将结束界定符剔出选择的文本 return "/<Esc>".start_skip."v/".snip_end_tag."/<CR>".end_skip."/<C-g>" endif endfunction " }}} " {{{ NextHop() - Jump to the next tag if one is available function! <SID>NextHop() let [snip_start_tag, snip_elem_delim, snip_end_tag] = s:SetLocalTagVars() "call s:Debug("NextHop", "Col() is: ".col(".")) "call s:Debug("NextHop", "Position of next match = ".match(getline("."), b:search_str)) " First check to see if we have any tags on lines above the current one " If the first match is after the current cursor position or not on this " line... if match(getline("."), b:search_str) >= col(".") || match(getline("."), b:search_str) == -1 "界定符位于光标后 或 当前行没有界定符 " Perform a search to jump to the next tag "call s:Debug("NextHop", "Seaching for a tag") if search(b:search_str) != 0 "查找成功,返回行号 return s:SetUpTags() else " there are no more matches "call s:Debug("NextHop", "No more tags in the buffer") " Restore hlsarch and @/ call s:RestoreSearch() return '' endif else "界定符位于光标前 " The match on the current line is on or before the cursor, so we need to " move the cursor back "call s:Debug("NextHop", "Moving the cursor back") "call s:Debug("NextHop", "Col is: ".col(".")) "call s:Debug("NextHop", "Moving back to column: ".match(getline("."), b:search_str)) "match返回匹配位置的列号-1 "如果一行内光标的位置在界定符之后,则光标前移一位直至光标到达开始界定符处 while col(".") > match(getline("."), b:search_str) + 1 call cursor(0,col('.')-1) "cursor函数第一参数为0,即当前行操作 endwhile "call s:Debug("NextHop", "Col is now: ".col(".")) " Now we just set up the tag as usual return s:SetUpTags() endif endfunction " }}} " {{{ RunCommand() - Execute commands stored in tags function! s:RunCommand(command, z) let [snip_start_tag, snip_elem_delim, snip_end_tag] = s:SetLocalTagVars() "call s:Debug("RunCommand", "RunCommand was passed this command: ".a:command." and this value: ".a:z) if a:command == '' return a:z endif " Save current value of 'z' " 使用寄存器z let snip_save = @z let @z=a:z " Call the command execute 'let ret = '. a:command " Replace the value let @z = snip_save return ret endfunction " }}} " {{{ MakeChanges() - Search the document making all the changes required " This function has been factored out to allow the addition of commands in tags " 当用户改变缺省的变量名后,协调整个snippet,将缺省变量名与更改后的同步 function! s:MakeChanges() " Make all the changes " Change all the tags with the same name and no commands defined let [snip_start_tag, snip_elem_delim, snip_end_tag] = s:SetLocalTagVars() "空标记,即<{}>,不含变量,跳过 if b:tag_name == "" "call s:Debug("MakeChanges", "Nothing to do: tag_name is empty") return endif "使用 "/V" 会使得其后的模式中只有反斜杠有特殊的意义。 "搜索待更改的变量名所在标记,即<{tag_name}> let tagmatch = '/V'.snip_start_tag.b:tag_name.snip_end_tag "call s:Debug("MakeChanges", "Matching on this value: ".tagmatch) "call s:Debug("MakeChanges", "Replacing with this value: ".s:replaceVal) try "call s:Debug("MakeChanges", "Running these commands: ".join(b:command_dict[b:tag_name], "', '")) catch /E175/ "call s:Debug("MakeChanges", "Could not find this key in the dict: ".b:tag_name) endtry let ind = 0 while search(tagmatch,"w") > 0 try let commandResult = s:RunCommand(b:command_dict[b:tag_name][0], s:replaceVal) catch /E175/ "call s:Debug("MakeChanges", "Could not find this key in the dict: ".b:tag_name) endtry "call s:Debug("MakeChanges", "Got this result: ".commandResult) "替换变量 let lines = split(substitute(getline("."), tagmatch, commandResult, ''),'/n') "写入替换后的行 if len(lines) > 1 "如果单行换成多行,注意先替换后追加(插入) call setline(".", lines[0]) call append(".", lines[1:]) else call setline(".", lines) endif try unlet b:command_dict[b:tag_name][0] "删除本次操作结果 catch /E175/ "call s:Debug("MakeChanges", "Could not find this key in the dict: ".b:tag_name) endtry endwhile endfunction " }}} " {{{ ChangeVals() - Set up values for MakeChanges() function! s:ChangeVals(changed) let [snip_start_tag, snip_elem_delim, snip_end_tag] = s:SetLocalTagVars() if a:changed == 1 let s:CHANGED_VAL = 1 else let s:CHANGED_VAL = 0 endif "call s:Debug("ChangeVals", "CHANGED_VAL: ".s:CHANGED_VAL) "call s:Debug("ChangeVals", "b:tag_name: ".b:tag_name) let elem_match = match(s:line, snip_elem_delim, s:curCurs) let tagstart = strridx(getline("."), snip_start_tag,s:curCurs)+strlen(snip_start_tag) "call s:Debug("ChangeVals", "About to access b:command_dict") try let commandToRun = b:command_dict[b:tag_name][0] "call s:Debug("ChangeVals", "Accessed command_dict") "call s:Debug("ChangeVals", "Running this command: ".commandToRun) unlet b:command_dict[b:tag_name][0] "call s:Debug("ChangeVals", "Command list is now: ".join(b:command_dict[b:tag_name], "', '")) catch /E175/ "call s:Debug("ChangeVals", "Could not find this key in the dict: ".b:tag_name) endtry let commandMatch = substitute(commandToRun, '/', '////', 'g') if s:CHANGED_VAL " The value has changed so we need to grab our current position back " to the start of the tag let replaceVal = strpart(getline("."), tagstart,s:curCurs-tagstart) "call s:Debug("ChangeVals", "User entered this value: ".replaceVal) let tagmatch = replaceVal "call s:Debug("ChangeVals", "Col is: ".col(".")) call cursor(0,col('.')-s:StrLen(tagmatch)) "call s:Debug("ChangeVals", "Col is: ".col(".")) else " The value hasn't changed so it's just the tag name " without any quotes that are around it "call s:Debug("ChangeVals", "Tag name is: ".b:tag_name) let replaceVal = substitute(b:tag_name, '^"/(.*/)"$', '/1', '') "call s:Debug("ChangeVals", "User did not enter a value. Replacing with this value: ".replaceVal) let tagmatch = '' "call s:Debug("ChangeVals", "Col is: ".col(".")) endif let tagmatch = '/V'.snip_start_tag.tagmatch.snip_end_tag "call s:Debug("ChangeVals", "Matching on this string: ".tagmatch) let tagsubstitution = s:RunCommand(commandToRun, replaceVal) let lines = split(substitute(getline("."), tagmatch, tagsubstitution, ""),'/n') if len(lines) > 1 call setline(".", lines[0]) call append(".", lines[1:]) else call setline(".", lines) endif " We use replaceVal instead of tagsubsitution as otherwise the command " result will be passed to subsequent tags let s:replaceVal = replaceVal let line = line('.') let col = col('.') call s:MakeChanges() call cursor(line, col) unlet s:CHANGED_VAL endfunction " }}} "{{{ SID() - Get the SID for the current script function! s:SID() return matchstr(expand('<sfile>'), '<SNR>/zs/d/+/ze_SID$') endfun "}}} "{{{ CheckForInTag() - Check whether we're in a tag function! s:CheckForInTag() let [snip_start_tag, snip_elem_delim, snip_end_tag] = s:SetLocalTagVars() if snip_start_tag != snip_end_tag " The tags are different so we can check to see whether the " end tag comes before a start tag " 开始界定符和结束界定符相同,只需检测结束界定符位于开始界定符前即可 " 获取光标位置后开始界定符第一次出现的位置 let s:startMatch = match(s:line, '/V'.snip_start_tag, s:curCurs) " 获取光标位置后结束界定符第一次出现的位置 let s:endMatch = match(s:line, '/V'.snip_end_tag, s:curCurs) if s:endMatch != -1 && ((s:endMatch < s:startMatch) || s:startMatch == -1) "光标后有结束界定符并且位置位于开始界定符之前(或开始界定符不存在),则处于标记中 " End has come before start so we're in a tag. return 1 else return 0 endif else " Start and end tags are the same so we need do tag counting to see " whether we're in a tag. " 开始界定符和结束界定符相同,需要计算界定符的数量来判断 let s:count = 0 let s:curSkip = s:curCurs while match(strpart(s:line,s:curSkip),snip_start_tag) != -1 "光标后有界定符 if match(strpart(s:line,s:curSkip),snip_start_tag) == 0 "光标处于界定符处 let s:curSkip = s:curSkip + 1 else "界定符处于光标后,光标移动到界定符后 let s:curSkip = s:curSkip + 1 + match(strpart(s:line,s:curSkip),snip_start_tag) endif let s:count = s:count + 1 "找到界定符,数量加1 endwhile if (s:count % 2) == 1 " Odd number of tags implies we're inside a tag. " 光标后有奇数个界定符,而界定符成对出现,故处于界定符中 return 1 else " We're not inside a tag. return 0 endif endif endfunction "}}} " {{{ SubSpecialVars(text) function! s:SubSpecialVars(text) let text = a:text "expand('%')当前文件名 let text = substitute(text, 'SNIP_FILE_NAME', expand('%'), 'g') let text = substitute(text, 'SNIP_ISO_DATE', strftime("%Y-%m-%d"), 'g') return text endfunction " }}} " {{{ SubCommandOutput(text) function! s:SubCommandOutput(text) let search = '``./{-}``' let text = a:text while match(text, search) != -1 let command_match = matchstr(text, search) "call s:Debug("SubCommandOutput", "Command found: ".command_match) let command = substitute(command_match, '^../(.*/)..$', '/1', '') "call s:Debug("SubCommandOutput", "Command being run: ".command) exec 'let output = '.command let output = escape(output, '/') let text = substitute(text, '/V'.escape(command_match, '/'), output, '') endwhile let text = substitute(text, '//`//`/(./{-}/)//`//`','``/1``','g') return text endfunction " }}} " {{{ RemoveAndStoreCommands(text) function! s:RemoveAndStoreCommands(text) let [snip_start_tag, snip_elem_delim, snip_end_tag] = s:SetLocalTagVars() let text = a:text if !exists("b:command_dict") let b:command_dict = {} endif let tmp_command_dict = {} try let ind = match(text, b:search_str) catch /E55: Unmatched //)/ call confirm("SnippetsEmu has caught an error while performing a search. This is most likely caused by setting the start and end tags to special characters. Try setting the 'fileencoding' of the file in which you defined them to 'utf-8'./n/nThe plugin will be disabled for the remainder of this Vim session.") let s:Disable = 1 return '' endtry while ind > -1 "call s:Debug("RemoveAndStoreCommands", "Text is: ".text) "call s:Debug("RemoveAndStoreCommands", "index is: ".ind) let tag = matchstr(text, b:search_str, ind) "call s:Debug("RemoveAndStoreCommands", "Tag is: ".tag) let commandToRun = matchstr(tag, snip_elem_delim.".*".snip_end_tag) if commandToRun != '' let tag_name = strpart(tag,strlen(snip_start_tag),match(tag,snip_elem_delim)-strlen(snip_start_tag)) "call s:Debug("RemoveAndStoreCommands", "Got this tag: ".tag_name) "call s:Debug("RemoveAndStoreCommands", "Adding this command: ".commandToRun) if tag_name != '' if has_key(tmp_command_dict, tag_name) call add(tmp_command_dict[tag_name], strpart(commandToRun, 1, strlen(commandToRun)-strlen(snip_end_tag)-1)) else let tmp_command_dict[tag_name] = [strpart(commandToRun, 1, strlen(commandToRun)-strlen(snip_end_tag)-1)] endif endif let text = substitute(text, '/V'.escape(commandToRun,'/'), snip_end_tag,'') else let tag_name = s:ChopTags(tag) if tag_name != '' if has_key(tmp_command_dict, tag_name) call add(tmp_command_dict[tag_name], '') else let tmp_command_dict[tag_name] = [''] endif endif endif "call s:Debug("RemoveAndStoreCommands", "".tag." found at ".ind) let ind = match(text, b:search_str, ind+strlen(snip_end_tag)) endwhile for key in keys(tmp_command_dict) if has_key(b:command_dict, key) for item in reverse(tmp_command_dict[key]) call insert(b:command_dict[key], item) endfor else let b:command_dict[key] = tmp_command_dict[key] endif endfor return text endfunction " }}} " {{{ ReturnKey() - Return our mapped key or Supertab key " 返回绑定的键 function! s:ReturnKey() if s:supInstalled "call s:Debug('ReturnKey', 'Snippy: SuperTab installed. Returning <C-n> instead of <Tab>') return "/<C-R>=".s:SupSNR."SuperTab('n')/<CR>" else " We need this hacky line as the one below doesn't seem to work. " Patches welcome exe "return /"".substitute(g:snippetsEmu_key, '^<', "////<","")."/"" "return substitute(g:snippetsEmu_key, '^<', "//<","") endif endfunction " }}} " {{{ Jumper() " We need to rewrite this function to reflect the new behaviour. Every jump " will now delete the markers so we need to allow for the following conditions " 1. Empty tags e.g. "<{}>". When we land inside then we delete the tags. " "<{:}>" is now an invalid tag (use "<{}>" instead) so we don't need to check for " this " 2. Tag with variable name. Save the variable name for the next jump. " 3. Tag with command. Tags no longer have default values. Everything after the " centre delimiter until the end tag is assumed to be a command. " " Jumper is performed when we want to perform a jump. If we've landed in a " 1. style tag then we'll be in free form text and just want to jump to the " next tag. If we're in a 2. or 3. style tag then we need to look for whether " the value has changed and make all the replacements. If we're in a 3. " style tag then we need to replace all the occurrences with their command " modified values. " function! <SID>Jumper() if s:Disable == 1 return substitute(g:snippetsEmu_key, '^<', "//<",'') endif let [snip_start_tag, snip_elem_delim, snip_end_tag] = s:SetLocalTagVars() " Set up some mapping in case we got called before Supertab " 检测映射 if s:supInstalled == 1 && s:done_remap != 1 call s:SetupSupertab() call s:SnipMapKeys() endif "设置本地搜索字符串 if !exists('b:search_str') && exists('g:search_str') let b:search_str = g:search_str endif if !exists('b:search_str') "失败,返回绑定键 return s:ReturnKey() endif let s:curCurs = col(".") - 1 "光标当前位置--列号 注意:插入模式下光标的位置-1才是实际数据的最后位置 let s:curLine = line(".") "当前行号 let s:line = getline(".") "当前行 let s:replaceVal = "" "变量替换值 " First we'll check that the user hasn't just typed a snippet to expand " 取原始词,即当前行中从开始到光标的字符串--strpart(getline("."), 0, s:curCurs) " matchstr 取出最后一个空格到结尾处的字符串 " '/(^/|/s/)/S/{-}$'意即行的第一个字符为非空白字符,并且直至行尾没有空白; " 或(|)行以空白开始,此时取最后一个单词(含有前面的空格) let origword = matchstr(strpart(getline("."), 0, s:curCurs), '/(^/|/s/)/S/{-}$') "去除前导的空格,上一语句必然导致有前导空格,除非是当前行的开始没有空格 let origword = substitute(origword, '/s', "", "") "call s:Debug("Jumper", "Original word was: ".origword) let word = s:Hash(origword) "转义 " The following code is lifted from the imaps.vim script - Many " thanks for the inspiration to add the TextMate compatibility let rhs = '' let found = 0 " Check for buffer specific expansions if exists('b:trigger_'.word) exe 'let rhs = b:trigger_'.word let found = 1 elseif exists('g:trigger_'.word) " also check for global definitions exe 'let rhs = g:trigger_'.word let found = 1 endif if found == 0 " Check using keyword boundary " 同上,取出行尾的关键字 let origword = matchstr(strpart(getline("."), 0, s:curCurs), '/k/{-}$') "call s:Debug("Jumper", "Original word was: ".origword) let word = s:Hash(origword) if exists('b:trigger_'.word) exe 'let rhs = b:trigger_'.word elseif exists('g:trigger_'.word) " also check for global definitions exe 'let rhs = g:trigger_'.word endif endif if rhs != '' " Save the value of hlsearch if &hls "call s:Debug("Jumper", "Hlsearch set") "检索结果高亮显示开 setlocal nohlsearch "关闭高亮显示 let b:hl_on = 1 else "call s:Debug("Jumper", "Hlsearch not set") let b:hl_on = 0 endif " Save the last search value let b:search_sav = @/ " Count the number of lines in the rhs let move_up = "" if len(split(rhs, "/<CR>")) - 1 != 0 let move_up = len(split(rhs, "/<CR>")) - 1 let move_up = move_up."/<Up>" endif " If this is a mapping, then erase the previous part of the map " by returning a number of backspaces. let bkspc = substitute(origword, '.', "/<BS>", "g") "call s:Debug("Jumper", "Backspacing ".s:StrLen(origword)." characters") let delEndTag = "" if s:CheckForInTag() "call s:Debug("Jumper", "We're doing a nested tag") "call s:Debug("Jumper", "B:tag_name: ".b:tag_name) if b:tag_name != '' try "call s:Debug("Jumper", "Commands for this tag are currently: ".join(b:command_dict[b:tag_name],"', '")) "call s:Debug("Jumper", "Removing command for '".b:tag_name."'") unlet b:command_dict[b:tag_name][0] "call s:Debug("Jumper", "Commands for this tag are now: ".join(b:command_dict[b:tag_name],"', '")) catch /E175/ "call s:Debug("Jumper", "Could not find this key in the dict: ".b:tag_name) endtry endif "call s:Debug("Jumper", "Deleting start tag") let bkspc = bkspc.substitute(snip_start_tag, '.', "/<BS>", "g") "call s:Debug("Jumper", "Deleting end tag") let delEndTag = substitute(snip_end_tag, '.', "/<Del>", "g") "call s:Debug("Jumper", "Deleting ".s:StrLen(delEndTag)." characters") endif " We've found a mapping so we'll substitute special variables let rhs = s:SubSpecialVars(rhs) let rhs = s:SubCommandOutput(rhs) " Now we'll chop out the commands from tags let rhs = s:RemoveAndStoreCommands(rhs) if s:Disable == 1 return substitute(g:snippetsEmu_key, '^<', "//<",'') endif " Save the value of 'backspace' let bs_save = &backspace set backspace=indent,eol,start return bkspc.delEndTag.rhs."/<Esc>".move_up."^:set backspace=".bs_save."/<CR>a/<C-r>=<SNR>".s:SID()."_NextHop()/<CR>" else " No definition so let's check to see whether we're in a tag if s:CheckForInTag() "call s:Debug("Jumper", "No mapping and we're in a tag") " We're in a tag so we need to do processing " 光标处于标记中,根据操作处理 if strpart(s:line, s:curCurs - strlen(snip_start_tag), strlen(snip_start_tag)) == snip_start_tag "call s:Debug("Jumper", "Value not changed") "标记名称(变量名)未变,如:<{i}>仍是<{i}>,调用更改变量函数,将所有的<{i}>换成i call s:ChangeVals(0) else "call s:Debug("Jumper", "Value changed") "标记名称(变量名)改变,如:<{var}>改成<{var}>,调用更改变量函数,将所有的<{var}>换成var call s:ChangeVals(1) endif "更改完变量,跳至下一个需要修改的地方,如:for循环中,步长变量设定后(i),跳至设定步长初始值处 return "/<C-r>=<SNR>".s:SID()."_NextHop()/<CR>" else " We're not in a tag so we'll see whether there are more tags " 光标不处于标记中,则检测是否有其余的标记,如:for循环扩展后,光标处于for(与第一个开始界定符之间 if search(b:search_str, "n") " More tags so let's perform nexthop " 有剩余的标记未处理,则跳入指定位置,如上述中的for循环,光标跳入第一个标记中,准备更改步长变量 let s:replaceVal = "" return "/<C-r>=<SNR>".s:SID()."_NextHop()/<CR>" else " No more tags so let's return a Tab after restoring hlsearch and @/ " 没有其余的标记 call s:RestoreSearch() if exists("b:command_dict") unlet b:command_dict endif "没有标记需要处理,返回<Tab>标识绑定键(<Tab>)原本的意义(或被更其他低级的绑定处理) return s:ReturnKey() endif endif endif endfunction " }}} "{{{ ListSnippets() - Return a list of snippets - used for command completion function! s:ListSnippets(ArgLead, CmdLine, CursorPos, scope) " Only allow completion for the second argument " TODO return sort(map(map(filter(keys(a:scope), 'v:val =~ "^trigger_'.a:ArgLead.'"'), 'v:val[8:]'), 's:UnHash(v:val)')) endfunction function! s:ListBufferSnippets(ArgLead, CmdLine, CursorPos) return s:ListSnippets(a:ArgLead, a:CmdLine, a:CursorPos, b:) endfunction function! s:ListGlobalSnippets(ArgLead, CmdLine, CursorPos) return s:ListSnippets(a:ArgLead, a:CmdLine, a:CursorPos, g:) endfunction " }}} " {{{ DelSnippet() - Delete a snippet function! s:DelSnippet(snippet, scope) if a:snippet != "" try exec "unlet ".a:scope."trigger_".s:Hash(a:snippet) catch /E108: No such variable:/ echom "Snippet '".a:snippet."' does not exist." endtry endif endfunction " }}} " {{{ Set up the 'Iabbr' and 'Snippet' commands "command! -nargs=+ Iabbr execute s:SetCom(<q-args>) "command! -nargs=+ Snippet execute s:SetCom("<buffer> ".<q-args>) command! -complete=customlist,s:ListGlobalSnippets -nargs=* / Iabbr call <SID>SetCom(<q-args>, "g:") command! -complete=customlist,s:ListBufferSnippets -nargs=* / Snippet call <SID>SetCom(<q-args>, "b:") command! -range CreateSnippet <line1>,<line2>call s:CreateSnippet() command! -range CreateBundleSnippet <line1>,<line2>call s:CreateBundleSnippet() command! -complete=customlist,s:ListBufferSnippets -nargs=* / DelSnippet call <SID>DelSnippet(<q-args>, "b:") command! -complete=customlist,s:ListGlobalSnippets -nargs=* / DelIabbr call <SID>DelSnippet(<q-args>, "g:") "}}} " {{{ Utility functions " This function will convert the selected range into a snippet function! s:CreateSnippet() range let [snip_start_tag, snip_elem_delim, snip_end_tag] = s:SetLocalTagVars() let snip = "" if &expandtab let tabs = indent(a:firstline)/&shiftwidth let tabstr = repeat(' ',&shiftwidth) else let tabs = indent(a:firstline)/&tabstop let tabstr = '/t' endif let tab_text = repeat(tabstr,tabs) for i in range(a:firstline, a:lastline) "First chop off the indent let text = substitute(getline(i),tab_text,'','') "Now replace 'tabs' with <Tab>s let text = substitute(text, tabstr, '<Tab>','g') "And trim the newlines let text = substitute(text, "/r", '','g') let snip = snip.text.'<CR>' endfor let tag = snip_start_tag.snip_end_tag let split_sav = &swb set swb=useopen if bufexists("Snippets") belowright sb Snippets else belowright sp Snippets endif resize 8 setlocal buftype=nofile setlocal bufhidden=hide setlocal noswapfile let @"=tag exe 'set swb='.split_sav let trig = inputdialog("Please enter the trigger word for your snippet: ", "My_snippet") if trig == "" let trig = "YOUR_SNIPPET_NAME_HERE" endif call append("$", "Snippet ".trig." ".snip) if getline(1) == "" 1 delete _ endif call cursor(line('$'),1) endfunction " This function will convert the selected range into a snippet suitable for " including in a bundle. function! s:CreateBundleSnippet() range let [snip_start_tag, snip_elem_delim, snip_end_tag] = s:SetLocalTagVars() let snip = "" if &expandtab let tabs = indent(a:firstline)/&shiftwidth let tabstr = repeat(' ',&shiftwidth) else let tabs = indent(a:firstline)/&tabstop let tabstr = '/t' endif let tab_text = repeat(tabstr,tabs) for i in range(a:firstline, a:lastline) let text = substitute(getline(i),tab_text,'','') let text = substitute(text, tabstr, '<Tab>','g') let text = substitute(text, "/r$", '','g') let text = substitute(text, '"', '//"','g') let text = substitute(text, '|', '<Bar>','g') let snip = snip.text.'<CR>' endfor let tag = '".st.et."' let split_sav = &swb set swb=useopen if bufexists("Snippets") belowright sb Snippets else belowright sp Snippets endif resize 8 setlocal buftype=nofile setlocal bufhidden=hide setlocal noswapfile let @"=tag exe 'set swb='.split_sav let trig = inputdialog("Please enter the trigger word for your snippet: ", "My_snippet") if trig == "" let trig = "YOUR_SNIPPET_NAME_HERE" endif call append("$", 'exe "Snippet '.trig." ".snip.'"') if getline(1) == "" 1 delete _ endif call cursor(line('$'),1) endfunction " This function will just return what's passed to it unless a change has been " made fun! D(text) if exists('s:CHANGED_VAL') && s:CHANGED_VAL == 1 return @z else return a:text endif endfun " s:Hash allows the use of special characters in snippets " This function is lifted straight from the imaps.vim plugin. Please let me know " if this is against licensing. " 将非字母和数字的符号使用他们的ASCII码代替,使用_标识 function! s:Hash(text) return substitute(a:text, '/([^[:alnum:]]/)', / '/="_".char2nr(submatch(1))."_"', 'g') endfunction " s:UnHash allows the use of special characters in snippets " This function is lifted straight from the imaps.vim plugin. Please let me know " if this is against licensing. function! s:UnHash(text) return substitute(a:text, '_/(/d/+/)_', / '/=nr2char(submatch(1))', 'g') endfunction " This function chops tags from any text passed to it " 取出标记中的文本 function! s:ChopTags(text) let text = a:text "call s:Debug("ChopTags", "ChopTags was passed this text: ".text) let [snip_start_tag, snip_elem_delim, snip_end_tag] = s:SetLocalTagVars() let text = strpart(text, strlen(snip_start_tag)) "截取开始界定符 let text = strpart(text, 0, strlen(text)-strlen(snip_end_tag)) "截取结束界定符 "call s:Debug("ChopTags", "ChopTags is returning this text: ".text) return text "返回标记中文本 endfunction " This function ensures we measure string lengths correctly function! s:StrLen(str) "call s:Debug("StrLen", "StrLen returned: ".strlen(substitute(a:str, '.', 'x', 'g'))." based on this text: ".a:str) return strlen(substitute(a:str, '.', 'x', 'g')) endfunction " }}} " vim: set tw=80 sw=2 sts=2 et foldmethod=marker :

你可能感兴趣的:(function,command,search,tags,textmate,returning)