VIM/GVIM 7.4新建文件时自动填充头部模板信息失败排查
Vim新建文件自动填充头部模板字段失败排查与解决
问题背景
我在~/.vimrc中通过以下命令加载默认头部模板:
autocmd bufnewfile * so /home/username/.vim/header.txt
模板内容如下:
###################################################### # File name: # File path: # Purpose: # Creation date: # Last modified: # Created by: ######################################################
目前已实现保存文件时(通过autocmd BufWritePre)更新Last modified:字段,功能正常。但新建文件时(通过autocmd BufNewFile)自动填充File name:、File Path:、Creation date:和Created by:字段的尝试全部失败,且无任何错误提示。
现有正常工作代码(保存时更新字段)
autocmd! BufWritePre * :call s:timestamp() " to update timestamp when saving if it's in the first 20 lines of a file function! s:timestamp() let pat = '\(\(Last\)\?\s*\([Cc]hanged\?\|[Mm]odified\|[Uu]pdated\?\)\s*:\s*\).*' let rep = '\1' . strftime("%a %d %b %Y %I:%M:%S %p %Z") call s:subst(1, 20, pat, rep) let pat = '\(Created by\?\s*:\s*\).*' let rep = '\1' . $USER call s:subst(1, 20, pat, rep) endfunction
新建文件时无效的填充代码
autocmd! BufNewFile * :call s:creationdata() function! s:creationdata() let pat = '\(\(Creation\)\s*\([Dd]ate\)\s*:\s*\).*' let rep = '\1' . strftime("%a %d %b %Y %I:%M:%S %p %Z") call s:subst(1, 20, pat, rep) let pat = '\(\(File\)\?\s*\([Pp]ath\)\?\s*:\s*\).*' let rep = '\1' .escape(expand("%:p:h"), '/\'. (&magic ? '&~' : '')) call s:subst(1, 20, pat, rep) let pat = '\(\(File\)\?\s*\([Nn]ame\)\?\s*:\s*\).*' let rep = '\1' . expand("%") call s:subst(1, 20, pat, rep) let pat = '\(Created by\?\s*:\s*\).*' let rep = '\1' . $USER call s:subst(1, 20, pat, rep) endfunction
通用subst函数定义
function! s:subst(start, end, pat, rep) let lineno = a:start while lineno <= a:end let curline = getline(lineno) if match(curline, a:pat) != -1 let newline = substitute( curline, a:pat, a:rep, '' ) if( newline != curline ) " Only substitute if we made a change "silent! undojoin keepjumps call setline(lineno, newline) endif endif let lineno = lineno + 1 endwhile endfunction
其他尝试方法(均无效)
方法1:执行全局替换(Vim7.4报错)
" Method 1: doesn't work; not compatible with vim 7.4? Gives Error: " E16: Invalid rangeError detected while processing BufNewFile Auto commands for "*": " E16: Invalid range: 1,10g/File name:.*/s//File name: foo " E16: Invalid range: 1,10g/Creation date:.*/s//Creation date: 13-12-2022 autocmd bufnewfile * exe "1," . 10 . "g/File name:.*/s//File name: " .expand("%") autocmd bufnewfile * exe "1," . 10 . "g/Creation date:.*/s//Creation date: " .strftime("%d-%m-%Y")
方法2:合并自动命令(仅保存时生效)
" Method 2: only works on saving file, not on creating a new file. No error message. autocmd BufNewFile,BufWritePre,FileWritePre * ks|call LastMod()|'s fun LastMod() if line("$") > 20 let l = 20 else let l = line("$") endif exe "1," . l . "g/Last modified:/s/Last modified: .*/Last modified: " . \ strftime("%Y %b %d") exe "1," . l . "g/Creation date:/s/Creation date: .*/Creation date: " . \ strftime("%Y %b %d") endfun
问题原因及解决办法
核心原因1:自动命令执行顺序与冲突
使用autocmd! BufNewFile * :call s:creationdata()时,!会清除所有同事件同模式的自动命令,包括之前加载模板的那条。即使没有用!,未分组的自动命令也可能出现执行顺序混乱(先调用填充函数,再加载模板,导致无内容可替换)。
修复:将自动命令分组管理,确保加载模板在填充函数之前执行:
augroup HeaderTemplate autocmd! autocmd BufNewFile * so /home/username/.vim/header.txt autocmd BufNewFile * call s:creationdata() augroup END
核心原因2:新建文件时expand("%")为空
未保存的新建文件没有路径和完整文件名,expand("%")返回空字符串,导致文件名和路径字段无法填充。
修复:改用expand("%:t")获取缓冲区显示的文件名(即使未保存),路径字段留空或提示用户:
" 文件名部分 let file_name = expand("%:t") == '' ? '未命名' : expand("%:t") " 路径部分 let file_path = expand("%:p:h") == '' ? '未保存,请先保存文件' : expand("%:p:h")
核心原因3:正则匹配未覆盖模板格式
模板中的行以# 开头,但原正则未包含该前缀,导致无法匹配到目标行。
修复:调整正则表达式,匹配模板中的实际行格式:
" 匹配Creation date行 let pat = '\(#\s*Creation\s*[Dd]ate\s*:\s*\).*' " 匹配File path行 let pat = '\(#\s*File\s*[Pp]ath\s*:\s*\).*' " 匹配File name行 let pat = '\(#\s*File\s*[Nn]ame\s*:\s*\).*' " 匹配Created by行 let pat = '\(#\s*Created\s*by\s*:\s*\).*'
最终修复后的完整代码示例
augroup HeaderTemplate autocmd! " 新建文件时加载模板 autocmd BufNewFile * so /home/username/.vim/header.txt " 新建文件时填充初始字段 autocmd BufNewFile * call s:creationdata() " 保存时更新修改时间 autocmd BufWritePre * call s:timestamp() augroup END " 新建文件时填充初始信息 function! s:creationdata() " 填充创建日期 let pat = '\(#\s*Creation\s*[Dd]ate\s*:\s*\).*' let rep = '\1' . strftime("%a %d %b %Y %I:%M:%S %p %Z") call s:subst(1, 20, pat, rep) " 填充文件名(未保存时取缓冲区名称) let file_name = expand("%:t") == '' ? '未命名' : expand("%:t") let pat = '\(#\s*File\s*[Nn]ame\s*:\s*\).*' let rep = '\1' . file_name call s:subst(1, 20, pat, rep) " 填充文件路径(未保存时提示) let file_path = expand("%:p:h") == '' ? '未保存,请先保存文件' : expand("%:p:h") let pat = '\(#\s*File\s*[Pp]ath\s*:\s*\).*' let rep = '\1' . file_path call s:subst(1, 20, pat, rep) " 填充创建者 let pat = '\(#\s*Created\s*by\s*:\s*\).*' let rep = '\1' . $USER call s:subst(1, 20, pat, rep) endfunction " 保存时更新修改时间和创建者(确保一致性) function! s:timestamp() let pat = '\(#\s*\(Last\)\?\s*\([Cc]hanged\?\|[Mm]odified\|[Uu]pdated\?\)\s*:\s*\).*' let rep = '\1' . strftime("%a %d %b %Y %I:%M:%S %p %Z") call s:subst(1, 20, pat, rep) let pat = '\(#\s*Created\s*by\s*:\s*\).*' let rep = '\1' . $USER call s:subst(1, 20, pat, rep) endfunction " 通用替换函数 function! s:subst(start, end, pat, rep) let lineno = a:start while lineno <= a:end let curline = getline(lineno) if match(curline, a:pat) != -1 let newline = substitute(curline, a:pat, a:rep, '') if newline != curline silent! undojoin keepjumps call setline(lineno, newline) endif endif let lineno = lineno + 1 endwhile endfunction
内容的提问来源于stack exchange,提问作者randyest
相关产品推荐
相关产品推荐

