前言概述

vim由于其丰富的扩展性、出色的跨平台性、高效率的操作性深受一大批粉丝的追捧,甚至就连vim和emacs之间孰优孰劣的话题都能被引起一场编辑器之间的圣战,足以见vim是多么的优秀。

vim的灵魂在于高定制性,高可玩性,一个原生的vim没有任何插件加持的情况下还是非常难以上手的,但是我们可以通过一些插件的点缀让它变得熠熠生辉,甚至可以配置出一个只属于自己的IDE。

  • 在观看本章节前,你应该熟悉vim的基本操作
  • 此外,还应当对vim的插件化管理vim-plug有所了解
  • 可参照之前的文章

vimcdoc 中文文档

vimcdoc提供了vim的中文文档,安装该插件后在命令模式中输入:help即可获取vim中文文档。

我们只需要把下面这一行放~/.config/nvim/init.vim文件中的call begin和call end之间即可:

Plug 'yianwillis/vimcdoc'

然后使用vim-plug进行下载,在vim命令模式中输入:PlugInstall,后续的插件安装我们将省略这一步骤:

:PlugInstall

效果如下:

indentLine 可视缩进

indentLine提供一个可视化的缩进服务(仅支持空格缩进):

" 可视化缩进
Plug 'Yggdroot/indentLine'

效果如下:

vim-monokai 主题配色

vim-monokai是我最喜欢的一款代码主题,之前在vscode上我也一直使用monokai主题:

" monokai主题
Plug 'crusoexia/vim-monokai'

然后需要再加上下面一行配置:

" 开启语法高亮
syntax on
" 主题选用monokai
colorscheme monokai

效果如下:

vim-airline 状态标签

vim-airline为vim提供了一个非常好看的状态栏以及标签栏:

" 加载vim-airline
Plug 'vim-airline/vim-airline'
" 使用的主题
Plug 'vim-airline/vim-airline-themes'

还需要做一些样式与按键上的调整:

" 关于标签栏的样式
" -- 启用标签栏
let g:airline#extensions#tabline#enabled = 1
" -- 设置标签栏分隔符
let g:airline#extensions#tabline#left_sep = ' '
let g:airline#extensions#tabline#left_alt_sep = '|'
" -- 设置标签栏格式
let g:airline#extensions#tabline#formatter = 'default'
" -- 设置标签栏样式
let g:airline_theme = 'desertink'
"-- 快捷键e切换到前一个标签
nmap e <Plug>AirlineSelectPrevTab
"-- 快捷键E切换到后一个标签
nmap E <Plug>AirlineSelectNextTab

效果如下:

nerdtree 资源管理

nerdtree为vim提供了一个文件资源管理器,我们可以通过h、j、k、l来进行文件的选择:

" nerdtree支持
Plug 'preservim/nerdtree'
" 显示nredtree中文件和目录的Git状态标志
Plug 'Xuyuanp/nerdtree-git-plugin'
" 支持nredtree中文件和目录的图标
Plug 'ryanoasis/vim-devicons'
" 支持高亮显示nredtree中的图标
Plug 'tiagofumo/vim-nerdtree-syntax-highlight'

默认的nerdtree是没有任何文件图标的,若想添加图标,请访问nerd-fonts下载字体图标。

需要按照不同的操作系统进行阅读自述文件,下面以MAC平台为例。

我们可以直接找到Droid Sans Mono Nerd这个字体,然后选择下载Droid Sans Mono Nerd Font Complete.otf,下载完成后双击即可安装字体。

安装字体完成后需要将Terminal的字体调整为Droid Sans Mono Nerd,下面是ITerm的设置:

接下来我们对nerdtree做一些基本的设置:

" nerdtree配置
" -- 为打开或关闭nerdtree设置一个快捷键
nnoremap 1 :NERDTreeToggle<CR>
" -- 自动开启nerdtree
autocmd vimenter * NERDTree
" -- 设定nerdtree的窗口大小
let g:NERDTreeWinSize = 25
" -- 打开nerdtree时自动显示bookmarks
let NERDTreeShowBookmarks=1
" -- 打开nvim时若没有任何文件,则自动打开nerdtree
autocmd vimenter * if !argc()|NERDTree|endif
" -- 当nerdtree为唯一视窗时,自动关闭
autocmd bufenter * if (winnr("$") == 1 && exists("b:NERDTree") && b:NERDTree.isTabTree()) | q | endif
" -- 每次打开一个新的标签,都默认打开nerdtree
autocmd BufWinEnter * if getcmdwintype() == '' | silent NERDTreeMirror | endif
" -- 设置树的图标,以区分已打开或未打开
let g:NERDTreeDirArrowExpandable = '▸'
let g:NERDTreeDirArrowCollapsible = '▾'
" -- 是否显示行号
let g:NERDTreeShowLineNumbers=0
" -- 是否显示隐藏文件
let g:NERDTreeHidden=0
" -- 让nerdtree更漂亮
let NERDTreeMinimalUI = 1
let NERDTreeDirArrows = 1
" -- 过滤不显示的文件
let NERDTreeIgnore=['\.pyc','\~$','\.swp']
" -- nerdtree的git文件状设置
let g:NERDTreeGitStatusIndicatorMapCustom = {
\ 'Modified' :'✹',
\ 'Staged' :'✚',
\ 'Untracked' :'✭',
\ 'Renamed' :'➜',
\ 'Unmerged' :'═',
\ 'Deleted' :'',
\ 'Dirty' :'✗',
\ 'Ignored' :'☒',
\ 'Clean' :'︎',
\ 'Unknown' :'?',
\ }
" -- 为不同的文件进行不同的高亮上色
let s:brown = "905532"
let s:aqua = "3AFFDB"
let s:blue = "689FB6"
let s:darkBlue = "44788E"
let s:purple = "834F79"
let s:lightPurple = "834F79"
let s:red = "AE403F"
let s:beige = "F5C06F"
let s:yellow = "F09F17"
let s:orange = "D4843E"
let s:darkOrange = "F16529"
let s:pink = "CB6F6F"
let s:salmon = "EE6E73"
let s:green = "8FAA54"
let s:lightGreen = "31B53E"
let s:white = "FFFFFF"
let s:rspec_red = 'FE405F'
let s:git_orange = 'F54D27' let g:NERDTreeExtensionHighlightColor = {} " this line is needed to avoid error
let g:NERDTreeExtensionHighlightColor['css'] = s:blue " sets the color of css files to blue let g:NERDTreeExactMatchHighlightColor = {} " this line is needed to avoid error
let g:NERDTreeExactMatchHighlightColor['.gitignore'] = s:git_orange " sets the color for .gitignore files let g:NERDTreePatternMatchHighlightColor = {} " this line is needed to avoid error
let g:NERDTreePatternMatchHighlightColor['.*_spec\.rb$'] = s:rspec_red " sets the color for files ending with _spec.rb let g:WebDevIconsDefaultFolderSymbolColor = s:beige " sets the color for folders that did not match any rule
let g:WebDevIconsDefaultFileSymbolColor = s:blue " sets the color for files that did not match any rule

下面是nerdtree的一些操作方式:

?:获取帮助文档
h、j、k、l:移动方式
t:以tab形式打开文件,并将光标移动至打开的文件中
T:以tab形式打开文件,不将光标移动至打开的文件中
o:以buffer形式打开文件,并将光标移动至打开的文件中
O:以tab形式打开文件,不将光标移动至打开的文件中
e:以文件管理的方式打开选中的目录
q:关闭nerdtree
r:刷新nerdtree
m:增删改查子项目

效果如下:

caw.vim 自动注释

关于注释插件的选择,推荐caw.vim

尽管网上有很多人都在推荐nerdcommenter这款注释插件,但我断言caw.vim比nerdcommenter更加优秀。

nredcommenter是根据文件类型来进行注释的,也就是说它无法做到单文件多语言的注释风格切换。

而caw.vim则是根据当前光标所在区域内容来自动切换语言注释风格,这依赖于context_filetype插件。

举一个简单的例子,在编写vim时我们会出现<template>、<script>、以及<style>等标签,这种情况下nredcommenter的注释总是//,而使用caw.vim,你在不同的标签区域内能就获得不同的注释方式,这是我选择它最关键的一点:

" 根据内容自动获取文件类型
Plug 'Shougo/context_filetype.vim'
" 自动进行注释
Plug 'tyru/caw.vim'

它的使用方式也非常简单,gcc即是注释、取消注释的快捷键。

由于我平常喜欢使用ctrl+/进行注释,所以这里修改一下它的映射:

" caw.vim注释快捷键修改
" -- ctrl+/设置为打开、关闭注释
" 注意!Unix操作系统中的ctrl+/会被认为是ctrl+_,所以下面有这样一条if判断
if has('win32')
nmap <C-/> gcc
vmap <C-/> gcc
else
nmap <C-_> gcc
vmap <C-_> gcc
endif

auto-pairs 括号补全

默认的vim中当你输入诸如{}、()、""、''等成对字符时,它是不会帮你进行补全的,因此我们需要使用auto-pairs来进行自动补全。

" 自动填充成对出现的符号
Plug 'jiangmiao/auto-pairs'

rainbow 括号高亮

默认的vim中当你选择上诸如{}、()等成对字符时,它是不会进行高亮显示另一半字符的,因此我们需要使用rainbow来进行高亮显示。

" 自动高亮成对出现的符号
Plug 'luochen1990/rainbow'

此外还需要做一些额外的配置项:

 rainbow设置
" -- 开启rainbow
let g:rainbow_active = 0
" -- 解决与nerdtree/vim-devicons冲突的问题
let g:rainbow_conf = {
\ 'separately': {
\ 'nerdtree': 0,
\ }
\}
" -- rainbow匹配成对符号的颜色设置
let g:rainbow_conf = {
\ 'guifgs': ['darkorange3', 'seagreen3', 'royalblue3', 'firebrick'],
\ 'ctermfgs': ['lightyellow', 'lightcyan','lightblue', 'lightmagenta'],
\ 'operators': '_,_',
\ 'parentheses': ['start=/(/ end=/)/ fold', 'start=/\[/ end=/\]/ fold', 'start=/{/ end=/}/ fold'],
\ 'separately': {
\ '*': {},
\ 'tex': {
\ 'parentheses': ['start=/(/ end=/)/', 'start=/\[/ end=/\]/'],
\ },
\ 'lisp': {
\ 'guifgs': ['darkorange3', 'seagreen3', 'royalblue3', 'firebrick'],
\ },
\ 'vim': {
\ 'parentheses': ['start=/(/ end=/)/', 'start=/\[/ end=/\]/', 'start=/{/ end=/}/ fold', 'start=/(/ end=/)/ containedin=vimFuncBody', 'start=/\[/ end=/\]/ containedin=vimFuncBody', 'start=/{/ end=/}/ fold containedin=vimFuncBody'],
\ },
\ 'html': {
\ 'parentheses': ['start=/\v\<((area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)[ >])@!\z([-_:a-zA-Z0-9]+)(\s+[-_:a-zA-Z0-9]+(\=("[^"]*"|'."'".'[^'."'".']*'."'".'|[^ '."'".'"><=`]*))?)*\>/ end=#</\z1># fold'],
\ },
\ 'css': 0,
\ }
\}

vim-easymotion 快速跳转

vim-easymotion为vim提供了快速查找并跳转到某个字符的功能。

" 快速跳转
Plug 'easymotion/vim-easymotion'

你只需要按下:

# 全局定位某个字符
<leader><leader>s<char>

即可完成搜索。当搜索完成后它会对搜索的字符进行高亮,此时只需按下高亮后的快捷键即可跳转到该字符上。

我们可以将它定义成快捷键ctrl+f:

" easymotion设置
" --将全局搜索修改成ctrl+f
nmap <C-f> <Plug>(easymotion-s)

其他的操作,可自行进行映射:

# 定位移动j、k、h、l
<leader><leader>j # 跳转到前一个词上
<leader><leader>b # 跳转到后一个词上
<leader><leader>w # 重复上一动作
<leader><leader>.

vim-surround 包裹修改

vim-surround插件非常的方便,如果你想修改、或者删除单引号和双引号,它带给你的体验将是无与伦比的。

Plug 'tpope/vim-surround'

以下是它的语法:

ds<existing>
cs<existing><desired>

示例如下:

# 删除以下的[]
[1, 2, 3] -> ds[ # 将以下的[]修改为()
[1, 2, 3] -> cs[(

tagbar 大纲预览

tagbar为vim提供了当前文件下的类、函数预览功能。它依赖于ctags,所以我们需要先下载ctags。

$ wget http://prdownloads.sourceforge.net/ctags/ctags-5.8.tar.gz
$ tar -xvf ctags-5.8.tar.gz
$ cd ctags-5.8
$ ./configure --prefix=/usr/local/application
$ make -j
$ make install # 安装到完成后会在指定路径下创建一个bin目录
$ rm -rf ctags-5.8
$ rm -rf ctags-5.8.tar.gz

然后把安装路径添加到环境变量中,我目前使用的是bash,所以直接添加到/etc/profile里即可:

PATH=/usr/local/application/bin:$PATH

source /etc/profile

在终端输入ctags,如出现以下字样则按照成功:

ctags: No files specified. Try "ctags --help".

接下来我们就要安装tagbar了:

Plug 'majutsushi/tagbar'

下面再设置一下按键和tagbar的宽度就搞定了:

let g:tagbar_width=30
nnoremap 3 <F4> :TagbarToggle<CR>

效果图示:

SimpyFold 代码折叠

SimpyFold是一款非常好用的代码折叠工具,使用它能够让我们编辑代码时逻辑结果更加清楚。

" 代码折叠
Plug 'tmhedberg/simpylfold'

然后添加一些额外的配置:

" 自动按照缩进进行代码折叠
set foldmethod=indent " 启用预览被折叠的代码
let g:SimpylFold_docstring_preview = 1

使用这个插件非常简单,你只需要遵循vim自带的代码折叠机制即可:

  • zf:创建折叠
  • zo:打开折叠
  • zc:关闭折叠
  • zR:打开所有
  • zM:关闭所有
  • zj:移动到下一个折叠
  • zk:移动到上一个折叠

coc.nvim 智能补全

coc.nvim是只有nvim才能使用的智能补全插件平台。

了解它之前我们我们必须了解它的模式,它本身仅是一个平台,类似于vim-plug,我们可以在coc.nvim中安装很多我们需要的其他coc插件。

首先,coc.nvim必须基于Node.Js,所以请确保你的机器上安装了Node.js,如果没有安装或者你根本不知道Node.Js是啥,可以直接运行下面这段代码:

$ curl -sL install-node.now.sh/lts | bash

然后我们就需要使用vim-plug装上coc.nvim平台了:

" coc智能代码补全平台
Plug 'neoclide/coc.nvim', {'branch': 'release'}

安装完成之后,还需要使用npm安装一些2个东西:

$ npm install -g neovim yarn

此外,coc平台插件还需要依赖Python,所以我们需要在Python中安装neovim:

$ pip3 install neovim
$ pip2 install neovim

最后一步,必须让vim关联上Python,所以在init.vim配置文件中你需要设置:

" coc配置pythonx
if has('python3')
set pyx=3
elseif has('python2')
set pyx=2
endif

现在让我们打开vim,并在命令模式下执行:checkhealth命令,检查是否安装成功:

:checkhealth

主要查看health#nvim#check下面的这3个选项是否是ok,一般情况下只要按照上述步骤做了就没什么问题:

下面添加一些coc的配置项:

" 允许未保存文件时跳转文件
set hidden
" 增加响应
set updatetime=100
" 让弹窗栏更加简洁
set shortmess+=c
" 让我们的tab作为切换扩展的按键
inoremap <silent><expr> <TAB>
\ pumvisible() ? "\<C-n>" :
\ <SID>check_back_space() ? "\<TAB>" :
\ coc#refresh()
inoremap <expr><S-TAB> pumvisible() ? "\<C-p>" : "\<C-h>" function! s:check_back_space() abort
let col = col('.') - 1
return !col || getline('.')[col - 1] =~# '\s'
endfunction
" 按下ctrl+space键跳出或关闭自动补全
if has('nvim')
inoremap <silent><expr> <c-space> coc#refresh()
else
inoremap <silent><expr> <c-@> coc#refresh()
endif
" 当使用enter键作为补全选择时,它将不会自动切换到下一行
inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm()
\: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
" 使用[g或者]g来查找上一个或者下一个代码报错
nmap <silent> [g <Plug>(coc-diagnostic-prev)
nmap <silent> ]g <Plug>(coc-diagnostic-next)
" 使用gd来跳转到函数定义处、gy获取类型定义,
nmap <silent> gd <Plug>(coc-definition)
nmap <silent> gy <Plug>(coc-type-definition)
nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)
" 通过gh来显示文档
nnoremap gh :call <SID>show_documentation()<CR>
function! s:show_documentation()
if (index(['vim','help'], &filetype) >= 0)
execute 'h '.expand('<cword>')
elseif (coc#rpc#ready())
call CocActionAsync('doHover')
else
execute '!' . &keywordprg . " " . expand('<cword>')
endif
endfunction
" 高亮相同的词汇
autocmd CursorHold * silent call CocActionAsync('highlight') " 符号重命名
nmap <leader>rn <Plug>(coc-rename) " 格式化插件
xmap <leader>f <Plug>(coc-format-selected)
nmap <leader>f <Plug>(coc-format-selected)
augroup mygroup
autocmd!
" Setup formatexpr specified filetype(s).
autocmd FileType typescript,json setl formatexpr=CocAction('formatSelected')
" Update signature help on jump placeholder.
autocmd User CocJumpPlaceholder call CocActionAsync('showSignatureHelp')
augroup end

我们说过coc.nvim类似于vim-plug,所以他也有一套自己的命令:

  • CocInstall:安装一个coc插件
  • CocList:获取所有已安装的coc插件
  • CocConfig:创建一份Coc配置文件
  • CocCommand:运行一个coc插件命令

先安装一个coc-marketplace,通过它你可以发现很多coc插件:

:CocInstall coc-marketplace

下载完成后,输入以下命令即可选取你需要的coc插件:

:CocList marketplace

其实通过CocInstall来下载coc插件并不是一个比较好的选择,因为一旦切换了开发环境,那么所有的coc插件都需要重新下载一次。

因此我推荐将coc插件的下载存放在init.vim文件中:

" coc下载插件
let g:coc_global_extensions = [
\ 'coc-marketplace',
\ 'coc-json',
\ 'coc-vimlsp'
\ ]

下次当你打开vim时,它会自动对没有安装的coc插件进行下载。

通过CocList extensions来查看已经安装了哪些插件,其中*号是已启用,+号是未启用,它是自动根据文件类型来进行选择的。

按下tab键它会提示给你一些管理coc插件的选项:

Choose action:
[t]oggle, (c)onfiguration, (o)pen, (d)isable, (e)nable, (l)ock, (h)elp, (r)eload
, (f)ix, (u)ninstall:

下面是我安装的插件,其中*代表已加载的,+号代表未加载的。

其他的功能

现在一款IDE已经初具雏形,但是对于debug调试、git管理等相关功能还没有添加上,后续如果有时间还会继续进行更新。

从零开始搭建你的nvim ide的更多相关文章

  1. AI应用开发实战 - 从零开始搭建macOS开发环境

    AI应用开发实战 - 从零开始搭建macOS开发环境 本视频配套的视频教程请访问:https://www.bilibili.com/video/av24368929/ 建议和反馈,请发送到 https ...

  2. GO学习-(2) 从零开始搭建Go语言开发环境

    从零开始搭建Go语言开发环境 一步一步,从零搭建Go语言开发环境. 安装Go语言及搭建Go语言开发环境 下载 下载地址 Go官网下载地址:https://golang.org/dl/ Go官方镜像站( ...

  3. 在阿里云服务器(ECS)上从零开始搭建nginx服务器

    本文介绍了如何在阿里云服务器上从零开始搭建nginx服务器.阿里云服务器(ECS)相信大家都不陌生,感兴趣的同学可以到http://www.aliyun.com/product/ecs去购买,或到体验 ...

  4. React Native的环境搭建以及开发的IDE

    (一)前言 前面的课程我们已经对React Native的环境搭建以及开发的IDE做了相关的讲解,今天我们的主要讲解的是应用设备运行(Running)以及调试方法(Debugging).本节的前提条件 ...

  5. 从零开始搭建Docker Swarm集群

    从零开始搭建Docker Swarm集群 检查节点Docker配置 1. 打开Docker配置文件(示例是centos 7)vim /etc/sysconfig/docker2. 添加-H tcp:/ ...

  6. Linux编程之从零开始搭建RPC分布式系统

    我一毕业进公司就接触到了RPC,主要是使用前辈们搭建好的RPC框架以及封装好的RPC函数进行业务开发,虽说使用RPC框架开发已经近半年了,但一直想知道如何从零开始搭建起这么一个好用的分布式通信系统框架 ...

  7. mybatis学习笔记(五) -- maven+spring+mybatis从零开始搭建整合详细过程(附demo和搭建过程遇到的问题解决方法)

    文章介绍结构一览 一.使用maven创建web项目 1.新建maven项目 2.修改jre版本 3.修改Project Facts,生成WebContent文件夾 4.将WebContent下的两个文 ...

  8. 从零开始搭建框架SSM+Redis+Mysql(二)之MAVEN项目搭建

    从零开始搭建框架SSM+Redis+Mysql(二)之MAVEN项目搭建 废话不说,直接撸步骤!!! 1.创建主项目:ncc-parent 选择maven创建项目,注意在创建项目中,packing选择 ...

  9. 从零开始搭建框架SSM+Redis+Mysql(一)之摘要

    从零开始搭建框架SSM+Redis+Mysql(一)之摘要 本文章为本人实际的操作后的回忆笔记,如果有步骤错漏,希望来信307793969@qq.com或者评论指出. 本文章只体现过程,仅体现操作流程 ...

随机推荐

  1. POJ1804——Brainman(水题)

    解题思路: 一个乱序序列的 逆序数 = 在只允许相邻两个元素交换的条件下,得到有序序列的交换次数 直接求逆序数 把S[i]和s[i+1~n]的元素逐个比较,如果s[i] > s[k] (k∈[i ...

  2. 洛谷P1094——纪念品分组(简单贪心)

    https://www.luogu.org/problem/show?pid=1094 题目描述 元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作.为使得参加晚会的同学所获得 的纪念品价值相对均 ...

  3. Shell系列(5)- 输出输入重定向及wc命令

    输出重定向: 在Linux当中,0代表输入:1代表正确输出:2代表错误输出 类型 符号 作用 正确输出重定向 命令 > 文件 以覆盖得方式,把命令得正确输出,输出到指定文件或设备当中 命令 &g ...

  4. filter_var() 验证邮箱、ip、url的格式 php

    验证邮箱格式的正确与否:你的第一解决方案是什么呢? 不管你们怎么思考的:反正我首先想到的就是字符串查找看是否有@符号: 但是对于结尾的.com或者.net 亦或者.cn等等越来越多的域名验证感觉棘手: ...

  5. Appium WebView控件定位

    背景 移动应用可以粗分为三种:原生应用(native app), 网页应用(web app,或HTML5 app),以及它们的混血儿--混合模式移动应用(hybrid app). 什么是Hybrid ...

  6. setTimeout 与setInterval的区别

    setTimeout(code,millisec) 方法用于在指定的毫秒数后调用函数或计算表达式 setInterval(code,millisec) 方法可按照指定的周期(以毫秒计)来调用函数或计算 ...

  7. 『Python』matplotlib实现GUI效果

    1. 类RadioButtons的使用方法 类似单选框 import numpy as np import matplotlib.pyplot as plt import matplotlib as ...

  8. 华为云计算IE面试笔记-云磁盘和普通磁盘的区别。

    1. 定义 云硬盘:一种虚拟块存储服务,主要为ECS和BMS提供块存储空间 普通磁盘:也称本地硬盘,指挂载在计算实例物理机上的本地硬盘 2. 性能 吞吐量具体情况具体分析.(若云磁盘用的SSD本地磁盘 ...

  9. P2611-[ZJOI2012]小蓝的好友【Treap,扫描线】

    正题 题目链接:https://www.luogu.com.cn/problem/P2611 题目大意 \(r*c\)的网格上有\(n\)个标记点,然后求有多少个矩形包含至少一个标记点. \(1\le ...

  10. 使用AC自动机解决文章匹配多个候选词问题

    解决的问题 KMP算法用于单个字符串匹配,AC自动机用于文章中匹配多个候选词. 流程 第一步,先将候选词先建立前缀树. 第二步,以宽度优先遍历的方式把前缀树的每个节点设置fail指针, 头节点的fai ...