工欲善其事必先利其器系列之(四):构建基于Vim的补全跳转插件YouCompleteMe的Docker镜像,一键部署开发环境

日期 作者 版本 备注
2021-03-24 dingbin V1.0

C++程序员日常开发中不可避免地遇到变更电脑,变更服务器等事情时重建搭建C++开发环境实在过于繁重,需要耗费很多不必要的宝贵时间。vim 的YouCompleteMe(简称YCM) 插件可以实现较好的C/C++语言自动补全跳转功能,是利用vim进行C /C++代码编辑的必备神器之一。然后实际中YCM的安装及其复杂,不仅依赖python动态链接库,还依赖特定高版本的gcc和clang或clangd和cmake等。准备这些前置组件也很费时费力。有鉴于此,本文介绍一种新的玩法,并给出实际操作细节和直接可用的docker镜像成果,即:构建基于Vim的C++补全跳转插件YouCompleteMe的Docker镜像,一键完成任意机器上C++开发环境搭建。具体操作方法介绍如下。

环境准备

时间: 2021-03-24

机器配置:CentOS Linux release 7.5.1804 (Core),8核 12G

创建基础的docker镜像

考虑安装当前时间下最新版本的vim和YoucompleteMe插件。参考YouCompleteMe官网介绍的安装方法要求vim/gcc/clang/clangd/cmake/python都在比较高的版本下才能安装成功。而我们为了制作docker 镜像,因此不可能选很旧的基础os。参考YCM官网的建议,我们基础OS选择ubuntu 20.04 LTS。hub.docker.com上直接现成的镜像文件。配置文件需要准备好如下3个配置文件,本文直接贴出原文内容如下:

vim配置文件.vimrc

set nocompatible              " be iMproved, required
filetype off                  " required
set encoding=utf-8
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()
Plugin 'VundleVim/Vundle.vim'
Plugin 'flazz/vim-colorschemes'
Plugin 'godlygeek/csapprox'
Plugin 'Valloric/YouCompleteMe'
call vundle#end()            " required
filetype plugin indent on    " required

set cst
set csto=1 
set nocsverb
set csverb

"set smd        "set showmode
"set sc         "set showcmd

set aw         "set autowrite
set awa        "set autowriteall

set  title

"使打开文件时使光标自动定位到上一次文件被关闭时的准确行
if has("autocmd")
  autocmd BufRead *.txt set tw=78
  au BufReadPost * exe "normal! g`\""
endif

"设置高亮关键字显示
syntax enable
syntax on     "等同于syn on

"禁止在搜索到文件两端时重新搜索
"set nows          "set nowrapscan              
set ws

"设置搜索式的匹配字符串高亮显示
set hls    "set hlsearch
"设置搜索式的匹配字符串不高亮显示
"set nohls  "set nohlsearch 

"高亮光标所在的当前行set cursorline
set cul         "相反的设置是set nocul
"set nocul


"搜索时在未完全输入完毕要检索的文本时就开始检索
set is       "set incsearch
"搜索时在未完全输入完毕要检索的文本时不开始检索
"set nois       "set noincsearch

"设置以backspace删除自动缩进的,行末回车,行首的字符,很有用
set backspace=indent,eol,start

set ic   "相反是 set noic
"set scs  "相反是 set noscs

"增强模式中的命令行自动完成操作,非常有用
set wildmenu

"设置代码折叠为按语法折叠
"set foldmethod=syntax
"设定折叠方式为手动
set foldmethod=manual
"设置启动vim时不要自动折叠代码
set foldlevel=100
set fdc=1      "set foldcolumn=1 

"设置帮助的语言为中文
set helplang=cn
"set helplang=en

"实现C程序的缩进(indent)
set cin   

"设置行号
set nu
"set nonu


"设置256色彩
set t_Co=256
"设置配色方案
"colorscheme 简写成colo
"colo colorful 
"colo white2 
colo  desert
"colo  night
"colo  navajo
"colo  lucius
"colo  blackdust
"colo  freya
"colo  darkslategray
"colo  torte
"colo  fruidle
"colo colorful

"设置编辑模式下状态栏标尺显示
set ru "set ruler
"设置编辑模式下状态栏标尺不显示
"set noru "set noruler

"记录历史的行数
set history=1000

"检测文件类型
"filetype on
filetype plugin on
" 开启文件类型检测 的插件和缩进开
filetype plugin indent on   

"设置可以至上一行和下一行的字符按键
set ww=b,s,<,>,[,]  ",h,l

"Alt组合键不映射到菜单上
set winaltkeys=no

"同时支持GBK和UTF-8编码
set fileencodings=utf-8,ucs-bom,gbk,cp936
set fileencoding=utf-8
set encoding=utf-8
"如果在终端环境下使用Vim,需要设置termencoding和终端所使用的编码一致。例如:
set termencoding=utf-8 

"设置更新ctags文件的快捷键
"map    : !  ${HOME}/tools/gen_tags_by_dingbin.bash "%:p:h" z6tags  
"map    : !  ${HOME}/tools/gen_tags_by_dingbin.bash "%:p:h" oatags  
"map    : !  ${HOME}/tools/gen_tags_by_dingbin.bash "%:p:h" qttags  

"====================================================================================================================
"map     : cs show : let @i = &tags  :let @j = $CSCOPE_TAGS  : !genvimtags -vimCtags i -vimCscope j  :so ${VIM_SCRIPT_HOME}/configure/vimSourceFile 
"map     : cs show : let @i = &tags  :let @j = $CSCOPE_TAGS  : !genvimtags  -g -vimCtags i -vimCscope j  :so ${VIM_SCRIPT_HOME}/configure/vimSourceFile  
"====================================================================================================================

"设置grep插件的快捷键
"nnoremap   :Grep

"-----------------------------------------------------
"自动缩进的时候, 缩进尺寸为 4 个空格。
"即shiftWidth,自动缩进的空格数,用于<<,>>,cindent
set shiftwidth=4         

"softtabstop,sts,默认是0,
"执行编辑操作,如插入  或者使用  时,把  算作空格的数目
set softtabstop=4     "set sts=4

"编辑时将所有 Tab 替换为空格。
"该选项只在编辑时将 Tab 替换为空格, 如果打开一个已经存在的文件, 并不会将已有的Tab 替换为空格。 
"设置了该选项后,如果想输入tab制表符,先输入CTRL-V或CTRL-Q,再输入tab制表符
set et    "相反的设置是set noet
"Tab 宽度为 4 个字符。
set ts=4      "即set tabstop=4

"Tab操作快捷方式!
"nnoremap   :tabnext:TlistClose:TlistToggle
"nnoremap   :tabprev:TlistClose:TlistToggle
"nnoremap   :tabnext:TlistClose
"nnoremap   :tabprev:TlistClose


""关于tab的快捷键
"map tn :tabnext
"map tp :tabprevious
"map td :tabnew .
"map te :tabedit
"map tc :tabclose
""map tf :tabnew %:p:TlistClose:TlistToggle
"map tf :tabnew %:p:TlistClose

"覆盖文件时不备份
set nobackup                                                        
"成功保存文件之前备份             
set wb          "set writebackup
"set nowb         "set nowritebackup

"插入括号时,短暂地跳转到匹配的对应括号
set sm            "set showmatch               
"短暂跳转到匹配括号的时间
set matchtime=2

"设置魔术
set magic                   

"允许在有未保存的修改时切换缓冲区,此时的修改由 vim 负责保存
set hidden                  

"开启新行时使用智能自动缩进
set smartindent             
set autoindent
"打开普通文件类型的自动缩进。 该自动缩进不如 cindent 智能, 但它可以为你编辑非 C/C++ 文件提供一定帮助。
set ai

"选中状态下 Ctrl+c 复制
"vmap  "+y
"注意使用的过程中可以通过e和b键盘以word为单位前进或后退以选择文本
"set keymodel=                    "不使用此功能
set keymodel=startsel,stopsel     "使用此功能

"visual 模式下标签查找(*,#)可用
vnoremap  *  y/"  
vnoremap  #  y?"  
nnoremap  g[  :tag =expand("")
vnoremap  g[  :tag *

:inoremap ( ()i
:inoremap [ []i
:inoremap ;; A;

"插入模式下使Ctrl+ h,j,k,l四个键效果等同于移动上、下、左、右方向键
inoremap  
inoremap  
inoremap  
inoremap  
"插入模式下使ctrl+ w,b两个键效果等同于普通模式下ctrl+w(前进一个单词),ctrl+b(
"后退一个单词)
inoremap  b
inoremap  w
"插入模式下使ctrl+ f(front),Ctrl+e(end)两个键效果等同于普通模式下,键
inoremap   
inoremap   
"插入模式下ctrl + d,等效于普通模式下backspace键,
"插入模式下ctrl + x,等效于普通模式下delete键,
inoremap  
inoremap  

"指定在选择文本时,光标所在位置也属于被选中的范围
set selection=inclusive
"-----------------------------------------------------

"-----------------------------------------------------
"指定不折行。 如果一行太长, 超过屏幕宽度, 则向右边延伸到屏幕外面。如果使用图形界面的话, 指定不折行视觉效果会好得多。
set nowrap
"set wrap

"设置显示时一行的文本宽宽
set tw=80       "set textwidth=80

"添加水平滚动条。 如果你指定了不折行, 那为窗口添加一个水平滚动条就非常有必要了。
set guioptions-=b
"去除vim的GUI版本的toolbar
set guioptions+=T
"去除vim的GUI版本的menubar
set guioptions+=m

"设置断行,当一行的文字太长时,为自动智能在一个单词的中间
"空白处断开到下一行显示,注意:这里的断行只用显示,并不在行末加
set lbr           "set linebreak
"打开断行模块对亚洲语言支持。 m 表示允许在两个汉字之间断行, 即使汉字之间没有出现空格。
"B 表示将两行合并为一行的时候, 汉字与汉字之间不要补空格。 该命令支持的更多的选项请参看用户手册。
set fo+=mB

"配置字体
"set fileencoding=gbk
"set guifont=Courier\ 11 
"set guifont=Inconsolata\ Medium\ 9  "史上最好看的vim字体
"set guifont=Bitstream_Vera_Sans_Mono:h10:cANSI
"set gfw=幼圆:h10.5:cGB2312
set guifont=Bitstream\ Vera\ Sans\ Mono\ 13

"当右键单击窗口的时候, 弹出快捷菜单
set mousemodel=popup
"不使用选择模式
set selectmode=

"设置屏幕滚动的ctrl+d,ctrl+u的行数,默认是窗口的一半,即半屏
set scr=4

"-----------------------------------------------------

"""""""""""""""""""""""""""""""
""Taglist plugin settings 
"""""""""""""""""""""""""""""""
"let Tlist_Auto_Highlight_Tag = 1
"let Tlist_Hightlight_Tag_On_BufEnter = 1
"let Tlist_GainFocus_On_ToggleOpen = 0   "if 0 focus at code window, if 1 focus at tag window
"
""启动vim自动打开taglist
"let Tlist_Auto_Open = 0 
"
"" 不同时显示多个文件的 tag ,只显示当前文件的
"let Tlist_Show_One_File = 1
"
"" 如果 taglist 窗口是最后一个窗口,则退出 vim
"let Tlist_Exit_OnlyWindow = 1
"
""让当前不被编辑的文件的方法列表自动折叠起来 
"let Tlist_File_Fold_Auto_Close = 0
"
""把taglist窗口放在屏幕的右侧,缺省在左侧 
"let Tlist_Use_Right_Window=1 
"
""显示taglist菜单
"let Tlist_Show_Menu = 1
"
""taglist window width
"let Tlist_WinWidth = 40
"
"let Tlist_Process_File_Always = 1
"
"" 是否允许打开taglist窗口时自动加宽窗口, 默认为允许.
"let Tlist_Inc_Winwidth = 1
"" 精简模式, 不同的tag类别之间没有空行, 默认为有空行
"let Tlist_Compact_Format = 1
"" tag是以何种顺序排序, 以"order"还是"name", 默认是order, 可以用s切换,
"let Tlist_Sort_Type = "order"
"
""用 + 数字切换Tab窗口
""用过Gnome-terminal的人都知道, 在一个Terminal里打开多个TAB窗口, 用ALT+数字就可以
""切换到相应的窗口. 很喜欢这个功能, 映射如下:
"
"noremap  1gt
"noremap  2gt
"noremap  3gt
"noremap  4gt
"noremap  5gt
"noremap  6gt
"noremap  7gt
"noremap  8gt
"noremap  9gt
"noremap  10gt
"
"
"""""""""""""""""""""""""""""""
""Winmanager plugin settings 
"""""""""""""""""""""""""""""""
""设置界面分割
"let g:winManagerWindowLayout='FileExplorer|TagList'
""let g:winManagerWindowLayout = BufExplorer,FileExplorer|TagList"
""let g:winManagerWindowLayout = TagList|FileExplorer,BufExplorer"
"
""设置winmanager的宽度,默认为25
""let g:winManagerWidth = 30
"
""定义打开关闭winmanager按键
"nmap wm :WMToggle
""nmap   :WMToggle
"nmap   :TlistToggle
"
""1)将插件显示在右侧
""打开winmanager.vim,在function! StartWindowsManager()函数中修改:
""将:wincmd H 修改为wincmd L:
"" for now assume that the explorer windows always stay on the left.
"" TODO: make this optional later
"" make the explorers window always stay on the right ---by chenyong
""        wincmd H
""wincmd L
"
""2)进入vim自动打开winmanager
""在进入vim时自动打开winmanager
"let g:AutoOpenWinManager = 0
""相应的在winmanager.vim中修改:
""set auto open Winmanager 
""if g:AutoOpenWinManager 
""autocmd VimEnter * nested call s:StartWindowsManager()|1wincmd w  
""endif
"
""3)退出缓冲区时,自动退出vim
""这个功能是参考了taglist的自动退出功能,在taglist.vim中修改的。
""函数:function! s:Tlist_Window_Exit_Only_Window()中的winbunr(2)改为winbunr(3),
""即只剩2个窗口时关闭,考虑到2个窗口肯定是同时存在,所以这样还是可行的: 
""同时在vimrc中需要设置:
""let Tlist_Exit_OnlyWindow=1
"
"
"""""""""""""""""""""""""""""""
""QuickFix plugin settings 
"""""""""""""""""""""""""""""""
"nmap  :cw:cn
"nmap  :cw:cp
"nmap  :cclose
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""
""设定是否使用 quickfix 窗口来显示 cscope 结果
"""""""""""""""""""""""""""""""""""""""""""""""""""""
":set cscopequickfix=s-,g-,c-,d-,i-,t-,e-,f-
"
"
""cscope find"的用法:
""cs find c|d|e|f|g|i|s|t name
""0 或 s     查找本 C 符号(可以跳过注释)
""1 或 g     查找本定义
""2 或 d     查找本函数调用的函数
""3 或 c     查找调用本函数的函数
""4 或 t     查找本字符串
""6 或 e     查找本 egrep 模式
""7 或 f     查找本文件
""8 或 i     查找包含本文件的文件
"nmap s :cs find s =expand("")
"nmap g :cs find g =expand("")
"nmap c :cs find c =expand("")
"nmap t :cs find t =expand("")
"nmap e :cs find e =expand("")
"nmap f :cs find f =expand("")
"nmap i :cs find i ^=expand("")$
"nmap d :cs find d =expand("")
"
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""
""头文件与源文件相互切换a.vim settings
"""""""""""""""""""""""""""""""""""""""""""""""""""""
""nnoremap   :A 
""A         在新Buffer中切换到c\h文件
""AS     横向分割窗口并打开c\h文件
""AV     纵向分割窗口并打开c\h文件
""AT     新建一个标签页并打开c\h文件
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""
""  F11 : set csto=1, F12 : set csto=0
"""""""""""""""""""""""""""""""""""""""""""""""""""""
"nnoremap   :set csto=1  
"nnoremap   :set csto=0  
"
"
"" set cs hotkey
"nmap s :cs find s =expand("")
"nmap g :cs find g =expand("")
"nmap c :cs find c =expand("")
"nmap t :cs find t =expand("")
"nmap e :cs find e =expand("")
"nmap f :cs find f =expand("")
"nmap i :cs find i ^=expand("")$
"nmap d :cs find d =expand("")
"
"" Using 'CTRL-spacebar' then a search type makes the vim window
"" split horizontally, with search result displayed in
"" the new window.
"
"nmap s :scs find s =expand("")
"nmap g :scs find g =expand("")
"nmap c :scs find c =expand("")
"nmap t :scs find t =expand("")
"nmap e :scs find e =expand("")
"nmap f :scs find f =expand("")
"nmap i :scs find i ^=expand("")$
"nmap d :scs find d =expand("")
"
"" Hitting CTRL-space *twice* before the search type does a vertical
"" split instead of a horizontal one
"
"nmap s
"            \:vert scs find s =expand("")
"nmap g
"            \:vert scs find g =expand("")
"nmap c
"            \:vert scs find c =expand("")
"nmap t
"            \:vert scs find t =expand("")
"nmap e
"            \:vert scs find e =expand("")
"nmap i
"            \:vert scs find i ^=expand("")$
"nmap d
"            \:vert scs find d =expand("")

function AddFileTitle()
call append(0," ")
call setline(1,"////////////////////////////////////////////////////////////////////////")
call append(1,"/// @Filename         " . expand("%:p:t"))
call append(2,"/// @Description      ")
call append(3,"/// @Author           DingBin")
call append(4,"/// @Last modified    " . strftime("%Y-%m-%d %H:%M"))
call append(5,"////////////////////////////////////////////////////////////////////////")
endfunction

function AddFileComment()
call append(line(".") -1," ")
call setline(line(".") -1,"////////////////////////////////////////////////////////////////////////")
call append(line(".") - 1,"/// @brief     :")
call append(line(".") - 1,"/// @param     :")
call append(line(".") - 1,"/// @return    :")
call append(line(".") - 1,"////////////////////////////////////////////////////////////////////////")
endfunction

function AddoaArrayFor()
call append(line(".") -1," ")
call setline(line(".") -1,"for (oaUInt4 i = 0; i < .getNumElements(); i++ )")
endfunction

map time :0,50/\/\/\/ *@Last modified/s#@Last modified.*$#\=strftime("@Last modified    %Y-%m-%d %H:%M")#c

map addt :call AddFileTitle()
map addc :call AddFileComment()
map addf :call AddoaArrayFor()

"nnoremap   1gt 
"nnoremap   2gt 
"nnoremap   3gt
"nnoremap   4gt 
"nnoremap   5gt 
"nnoremap   6gt 
"nnoremap   7gt 
"nnoremap   8gt 
"nnoremap   9gt 



"Mode Settings
let &t_SI.="\e[5 q" "SI = INSERT mode
"let &t_SR.="\e[4 q" "SR = REPLACE mode
let &t_EI.="\e[1 q" "EI = NORMAL mode (ELSE)

"Cursor settings:
"  1 -> blinking block
"  2 -> solid block
"  3 -> blinking underscore
"  4 -> solid underscore
"  5 -> blinking vertical bar
"  6 -> solid vertical bar

"let &t_SI = "\]50;CursorShape=1\x7"
"let &t_SR = "\]50;CursorShape=2\x7"
"let &t_EI = "\]50;CursorShape=0\x7"

"-----------------------------------------------------
"关于状态行配置
"总是显示状态行
set laststatus=2            "set ls=2    
"-----  状态栏设置_BEGIN_ -----
"显示当前文件名,文件格式,文件类型
set statusline=%2*%n%m%r%h%w%*\ %F\ %1*[FORMAT=%2*%{&ff}:%{&fenc!=''?&fenc:&enc}%1*]\ [TYPE=%2*%Y%1*]\ [COL=%2*%03v%1*]\ [ROW=%2*%03l%1*/%3*%L(%p%%)%1*]\ 
"设置状态栏根据不同状态显示不同颜色
function! InsertStatuslineColor(mode)
    if a:mode == 'i'
        hi statusline guibg=peru
    elseif a:mode == 'r'
        hi statusline guibg=blue
    else
        hi statusline guibg=red
    endif
endfunction
au InsertEnter * call InsertStatuslineColor(v:insertmode)
au InsertLeave * hi statusline guibg=lightGreen guifg=black
hi statusline guibg=lightGreen
"状态行颜色
highlight CursorLine cterm=NONE ctermbg=lightGray ctermfg=green 
highlight StatusLineNC cterm=NONE ctermbg=lightGray ctermfg=green 
"highlight CursorLine cterm=NONE ctermbg=lightGray ctermfg=green guifg=lightGray guibg=green
"highlight StatusLineNC cterm=NONE ctermbg=lightGray ctermfg=green   guibg=lightGray guifg=green 
"highlight CursorLine cterm=NONE ctermbg=lightGray ctermfg=green guifg=red guibg=Green
"highlight StatusLineNC cterm=NONE ctermbg=lightGray ctermfg=green guifg=Gray guibg=White
"highlight CursorLine cterm=NONE  guifg=red guibg=Green
"highlight StatusLineNC cterm=NONE guifg=Gray guibg=White
"----- 状态栏设置_END_ -----





"ycm configuration
let g:ycm_confirm_extra_conf = 0
let g:ycm_global_ycm_extra_conf='~/.ycm_extra_conf.py'

let g:ycm_key_list_select_completion = ['','','']
let g:ycm_key_list_previous_completion = ['','','']

let g:ycm_complete_in_comments = 1 "在注释输入中也能补全
let g:ycm_complete_in_strings = 1 "在字符串输入中也能补全
let g:ycm_collect_identifiers_from_comments_and_strings = 0 "注释和字符串中的文字也会被收入补全
let g:ycm_collect_identifiers_from_tags_files=1 " 开启 YCM 基于标签引擎
let g:ycm_min_num_of_chars_for_completion=1 " 从第1个键入字符就开始罗列匹配项
let g:ycm_cache_omnifunc=0 " 禁止缓存匹配项,每次都重新生成匹配项

"主动补全
"let g:ycm_key_invoke_completion = ''
let g:ycm_key_invoke_completion = ''


let g:ycm_seed_identifiers_with_syntax=1 " 语法关键字补全
"nnoremap lo :lopen "open locationlist
"nnoremap lc :lclose    "close locationlist

"inoremap  
let g:ycm_max_num_identifier_candidates = 50
let g:ycm_auto_trigger = 1
let g:ycm_error_symbol = '>>'
let g:ycm_warning_symbol = '>'


"YcmCompleter RefactorRename :重命名
"YcmCompleter GoToSymbol  

nnoremap jo :YcmCompleter GoTo "跳转
nnoremap jd :YcmCompleter GoToDefinitionElseDeclaration "跳转到定义或声明
nnoremap jf :YcmCompleter GoToDefinition  "跳转到定义
nnoremap jl :YcmCompleter GoToDeclaration "跳转到声明
nnoremap jt :YcmCompleter GetType "get类型

nnoremap :js :YcmCompleter GoToSymbol 

nmap ji :YcmCompleter GoToInclude   "跳转到include、声明或定义
nmap jm :YcmCompleter GoToImprecise "跳转到实现
nmap jr :YcmCompleter GoToReferences "跳转到引用
nmap fi :YcmCompleter FixIt "根据Ycm的建议修复错误

nnoremap  :YcmForceCompileAndDiagnostics "重新编译和诊断
nmap  :YcmDiags  "F4进行诊断

bash 配置文件.bashrc

# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth

# append to the history file, don't overwrite it
shopt -s histappend

# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000

# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize

# If set, the pattern "**" used in a pathname expansion context will
# match all files and zero or more directories and subdirectories.
#shopt -s globstar

# make less more friendly for non-text input files, see lesspipe(1)
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"

# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
    debian_chroot=$(cat /etc/debian_chroot)
fi

# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
    xterm-color|*-256color) color_prompt=yes;;
esac

# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yes

if [ -n "$force_color_prompt" ]; then
    if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
    # We have color support; assume it's compliant with Ecma-48
    # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
    # a case would tend to support setf rather than setaf.)
    color_prompt=yes
    else
    color_prompt=
    fi
fi

if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt

# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
    PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
    ;;
*)
    ;;
esac

# enable color support of ls and also add handy aliases
#if [ -x /usr/bin/dircolors ]; then
#    test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
#    alias ls='ls --color=auto'
#    #alias dir='dir --color=auto'
#    #alias vdir='vdir --color=auto'
#
#    alias grep='grep --color=auto'
#    alias fgrep='fgrep --color=auto'
#    alias egrep='egrep --color=auto'
#fi

# colored GCC warnings and errors
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'

# some more ls aliases
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'

# Add an "alert" alias for long running commands.  Use like so:
#   sleep 10; alert
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'

# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.

if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi

# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if ! shopt -oq posix; then
  if [ -f /usr/share/bash-completion/bash_completion ]; then
    . /usr/share/bash-completion/bash_completion
  elif [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
  fi
fi


###################################################################################
export EDITOR=vim
alias tmux="tmux -2"
myCdls()
{
    echo $@
    if [ ""$@"" == "" ] ; then
        \cd $HOME
    else
        \cd "$@"
    fi
    pwd
    ls -lh
}
alias cd=myCdls

ycm基础的数据库配置文件.ycm_extra_conf.py

# This file is NOT licensed under the GPLv3, which is the license for the rest
# of YouCompleteMe.
#
# Here's the license text for this file:
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to 

from distutils.sysconfig import get_python_inc
import platform
import os
import subprocess
import ycm_core

DIR_OF_THIS_SCRIPT = os.path.abspath( os.path.dirname( __file__ ) )
DIR_OF_THIRD_PARTY = os.path.join( DIR_OF_THIS_SCRIPT, 'third_party' )
SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]

# These are the compilation flags that will be used in case there's no
# compilation database set (by default, one is not set).
# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
flags = [
'-Wall',
'-Wextra',
'-Wno-long-long',
'-Wno-variadic-macros',
'-Wthread-safety',
'-Wthread-safety-beta',
'-fexceptions',
'-DNDEBUG',
'-D_GLIBCXX_USE_CXX11_ABI=0', # esearch uses old ABI
# THIS IS IMPORTANT! Without a "-std=" flag, clang won't know which
# language to use when compiling headers. So it will guess. Badly. So C++
# headers will be compiled as C headers. You don't want that so ALWAYS specify
# a "-std=".
# For a C project, you would set this to something like 'c99' instead of
# 'c++11'.
'-std=c++17',
# ...and the same thing goes for the magic -x option which specifies the
# language that the files to be compiled are written in. This is mostly
# relevant for c++ headers.
# For a C project, you would set this to 'c' instead of 'c++'.
'-x', 'c++',
'-I', '/usr/local/include'
]


# Set this to the absolute path to the folder (NOT the file!) containing the
# compile_commands.json file to use that instead of 'flags'. See here for
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
#
# You can get CMake to generate this file for you by adding:
#   set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
# to your CMakeLists.txt file.
#
# Most projects will NOT need to set this to anything; you can just change the
# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
compilation_database_folder = ''

if os.path.exists( compilation_database_folder ):
  database = ycm_core.CompilationDatabase( compilation_database_folder )
else:
  database = None


def IsHeaderFile( filename ):
  extension = os.path.splitext( filename )[ 1 ]
  return extension in [ '.h', '.hxx', '.hpp', '.hh' ]


def FindCorrespondingSourceFile( filename ):
  if IsHeaderFile( filename ):
    basename = os.path.splitext( filename )[ 0 ]
    for extension in SOURCE_EXTENSIONS:
      replacement_file = basename + extension
      if os.path.exists( replacement_file ):
        return replacement_file
  return filename


def Settings( **kwargs ):
  if kwargs[ 'language' ] == 'cfamily':
    # If the file is a header, try to find the corresponding source file and
    # retrieve its flags from the compilation database if using one. This is
    # necessary since compilation databases don't have entries for header files.
    # In addition, use this source file as the translation unit. This makes it
    # possible to jump from a declaration in the header file to its definition
    # in the corresponding source file.
    filename = FindCorrespondingSourceFile( kwargs[ 'filename' ] )

    if not database:
      return {
        'flags': flags,
        'include_paths_relative_to_dir': DIR_OF_THIS_SCRIPT,
        'override_filename': filename
      }

    compilation_info = database.GetCompilationInfoForFile( filename )
    if not compilation_info.compiler_flags_:
      return {}

    # Bear in mind that compilation_info.compiler_flags_ does NOT return a
    # python list, but a "list-like" StringVec object.
    final_flags = list( compilation_info.compiler_flags_ )

    # NOTE: This is just for YouCompleteMe; it's highly likely that your project
    # does NOT need to remove the stdlib flag. DO NOT USE THIS IN YOUR
    # ycm_extra_conf IF YOU'RE NOT 100% SURE YOU NEED IT.
    try:
      final_flags.remove( '-stdlib=libc++' )
    except ValueError:
      pass

    return {
      'flags': final_flags,
      'include_paths_relative_to_dir': compilation_info.compiler_working_dir_,
      'override_filename': filename
    }
  return {}


def GetStandardLibraryIndexInSysPath( sys_path ):
  for path in sys_path:
    if os.path.isfile( os.path.join( path, 'os.py' ) ):
      return sys_path.index( path )
  raise RuntimeError( 'Could not find standard library path in Python path.' )


def PythonSysPath( **kwargs ):
  sys_path = kwargs[ 'sys_path' ]
  for folder in os.listdir( DIR_OF_THIRD_PARTY ):
    if folder == 'python-future':
      folder = os.path.join( folder, 'src' )
      sys_path.insert( GetStandardLibraryIndexInSysPath( sys_path ) + 1,
                       os.path.realpath( os.path.join( DIR_OF_THIRD_PARTY,
                                                       folder ) ) )
      continue

    if folder == 'cregex':
      interpreter_path = kwargs[ 'interpreter_path' ]
      major_version = subprocess.check_output( [
        interpreter_path, '-c', 'import sys; print( sys.version_info[ 0 ] )' ]
      ).rstrip().decode( 'utf8' )
      folder = os.path.join( folder, 'regex_{}'.format( major_version ) )

    sys_path.insert( 0, os.path.realpath( os.path.join( DIR_OF_THIRD_PARTY,
                                                        folder ) ) )
  return sys_path

最后是Dockerfile 编写如下:

##################################################################################################################docker build --no-cache -t xxx:1.0 #################################################################################################################FROM ubuntu:20.04

MAINTAINER dingbin [email protected]


WORKDIR /root

COPY Dockerfile ./
COPY .bashrc ./
COPY .vimrc ./
COPY .ycm_extra_conf.py ./

ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Asia/Shanghai

RUN set -ex && apt-get update && apt-get upgrade -y &&
apt-get install -y tzdata &&
apt-get install -y net-tools rsync inetutils-ping git curl wget unzip &&
apt-get install -y build-essential cmake vim-nox python3-dev &&
git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim &&
vim -c PluginInstall -c q -c q && \ cd ~/.vim/bundle/YouCompleteMe &&
git submodule update --init --recursive && ./install.py --clangd-completer

CMD vim

将以上4个文件放置一个目录下,在任意一台安装了docker的x86_64机器上该目录下执行命令:

docker build  --no-cache -t    ycm-base:1.0 

即可创建一个本地docker镜像名为:ycm-base:1.0

本文作者已经按如上步骤创建好了一个可用的ycm 基础镜像,并上传到了hub.docker.com名为:apollo008/vim8-ycm:3.0 。地址是:https://hub.docker.com/repository/docker/apollo008/vim8-ycm-customize-user 读者可直接通过docker命令:docker pull apollo008/vim8-ycm:3.0 下载该镜像到本地使用即可。

创建定制用户的docker镜像

以上docker镜像创建的是root用户的。如果使用root账户登录进入docker容器中编辑文件后,必然是root用户所有,导致容器外其他用户没有权限编辑或阅读该文件。解决得办法是才上述基础vim-ycm镜像的基础上,用户自己在本地docker环境中迅速创建一个使用自己用户名(尤其是uid)的账户的ycm docker镜像,然后本地以该特定用户访问该docker容器即可。之所以分成两个容器,是因为基础容器制作费时,切可能依赖外网,导致安装难以成功,所以制作好上传至公共镜像仓库,不同用户在即镜像基础上定制一下自己的特定uid和username的本地镜像即可,操作步骤如下。
Dockerfile 内容如下:

FROM apollo008/vim8-ycm:3.0

MAINTAINER dingbin [email protected]

WORKDIR /root

ARG APP_UID
ARG APP_GID
ARG APP_USER
ARG APP_GROUP

RUN set -ex && apt-get install -y sudo openssh-server && \
    groupadd -o -g  ${APP_GID} ${APP_GROUP} && \
    useradd -m -s /bin/bash -o -u ${APP_UID} -g ${APP_GID}  ${APP_USER} && \
    mkdir -p /etc/sudoers.d && \
    echo root:123456 | chpasswd  && \
    echo ${APP_USER}:123456 | chpasswd  && \
    echo "${APP_USER} ALL=(ALL)   NOPASSWD: ALL" > /etc/sudoers.d/${APP_USER} && \ 
    chmod 0440 /etc/sudoers.d/${APP_USER}  && \
    cp /root/.vim  /home/${APP_USER}/ -rf && \
    cp /root/.vimrc  /home/${APP_USER}/ -rf && \
    cp /root/.ycm_extra_conf.py  /home/${APP_USER}/ -rf && \
    chown -R ${APP_USER}:${APP_GROUP}  /home/${APP_USER}/.vim && \
    chown -R ${APP_USER}:${APP_GROUP}  /home/${APP_USER}/.vimrc && \
    chown -R ${APP_USER}:${APP_GROUP}  /home/${APP_USER}/.ycm_extra_conf.py


    COPY --chown=${APP_USER}:${APP_GROUP}   Dockerfile  /home/${APP_USER}/
    COPY --chown=${APP_USER}:${APP_GROUP}   .bashrc    /home/${APP_USER}/

    WORKDIR /home/${APP_USER}
    USER ${APP_USER}


CMD vim

该docker镜像内置的vim8.1和ycm是ubuntu 20.04 LTS 截至目前2021.03.22最新版的vim和ycm。 ~/.vimrc 中相应的配置都已完善,包括较为合理美观的配色方案。效果如下图。

工欲善其事必先利其器系列之(四):构建基于Vim的补全跳转插件YouCompleteMe的Docker镜像,一键部署开发环境_第1张图片
本镜像用法是: 在本地执行如下命令 创建自己用户名比如work/user等的docker镜像。
docker build --no-cache -t myycm:latest --build-arg APP_USER=work --build-arg APP_GROUP=work --build-arg APP_UID=1000 --build-arg APP_GID=1001 .

镜像生成后推荐用法是:docker run -itd --name ycm1 -v hostPath:InDockerPath myycm:latest /bin/bash
docker exec -it ycm1 /bin/bash 然后在docker容器内的交互bash 上执行 vim xxx.cpp 编辑文件。避免频繁打开 docker container 相应速度慢。

你可能感兴趣的:(工欲善其事必先利其器系列之(四):构建基于Vim的补全跳转插件YouCompleteMe的Docker镜像,一键部署开发环境)