You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 23:10:45