代码之家  ›  专栏  ›  技术社区  ›  Jim Fischer

使用~/.vim/scripts.vim检测文件的类型

vim
  •  1
  • Jim Fischer  · 技术社区  · 2 年前

    我试图解决Vim编辑器的一个恼人的问题,在这个编辑器中,Bash脚本(而不是SH脚本)被分配了文件类型 sh 而不是 bash (出于我自己的目的,我希望文件类型为 猛击 而不是 sh .)

    [ 附注1 .在Vim编辑器中,如果我键入
    :setfiletype CTRL+d

    :echo getcompletion('ba', 'filetype')
    文件类型 猛击 列在可用/可识别的文件类型中。 尾注 ]

    [ 附注2 。我在Linux主机上工作。对于下面的讨论, 这两个Vim配置文件 不要 存在: ~/.vimrc ~/.gvimrc 文件 ~/.vimrc 存在,并且只包含一个命令:
    filetype on
    我没有名为的文件 ~/.gvimrc . 尾注 ]


    情景1) 当我使用Vim编辑文件扩展名为的文件时 .bash ,我希望Vim指定文件类型 猛击 ,不 sh ,到文件的缓冲区。我通过创建文件解决了这个问题 ~/.vim/filetype.vim 如清单1所示:

    清单1.~/.vim/filetype.vim

    " ~/.vim/filetype.vim
    if exists("did_load_filetypes")
      finish
    endif
    augroup filetypedetect
      au! BufRead,BufNewFile *.bash let b:is_bash = 1 | setfiletype bash
    augroup END
    

    情景2) 当我使用Vim编辑第一行为以下任何一行的文本文件时,

    #!/bin/bash
    #!/usr/bin/bash
    #!/bin/env bash
    #!/usr/bin/env bash
    

    我希望Vim指定文件类型 猛击 ,不 sh ,到文件的缓冲区。

    这是我正在努力寻找解决方案的情况,并希望得到一些帮助。

    [ 附注1 。为了接下来的讨论,我删除了文件 ~/.vim/filetype.vim 因此,它无法分配文件类型 猛击 到文件缓冲区的filetype属性。 尾注 ]

    Vim帮助主题 new-filetype-scripts 描述了创建用户定义的Vim脚本的过程( ~/.vim/scripts.vim ),在我看来,这个解决方案应该奏效。但是,当我尝试这种方法时,Vim脚本没有任何效果。当Vim打开一个文本文件,其第一行即shebang行 #! 如果是上面显示的四行中的任何一行,Vim总是指定文件类型 sh 到文件的缓冲区。

    不管怎样,我尝试了一种“蛮力”的方法来将文件类型设置为 猛击 如清单2和清单3所示,但Vim忽略了这一点,并将文件的类型设置为 sh :

    清单2.~/.vim/scripts.vim的“暴力”版本(方法1)

    " ~/.vim/scripts.vim
    setfiletype bash
    

    清单3.~/.vim/scripts.vim的“暴力”版本(方法2)

    " ~/.vim/scripts.vim
    set filetype=bash
    

    Vim帮助主题 新的文件类型脚本 明确指出,

    您的scripts.vim在默认检查文件类型之前加载,这意味着您的规则会覆盖$VIMRUNTIME/scripts.vim中的默认规则。

    所以,我不明白为什么Vim将文件缓冲区的filetype属性设置为 sh 而不是 猛击 .

    值得一提的是,我尝试创建了一个基于vim帮助主题中所示示例的vim脚本文件 新的文件类型脚本 (参见清单4),但该版本也没有将filetype属性设置为 猛击 :

    清单4.~/.vim/scripts.vim示例基于vim帮助示例 新的文件类型脚本 .

    if did_filetype()
        finish
    endif
    if getline(1) =~# '^#!.*/bin/env\s\<bash\>'
        setfiletype bash
    elseif getline(1) =~# '^#!.*/bin/bash'
        setfiletype bash
    endif
    

    当然,我也尝试过编码 if 语句的条件表达式使用“精确匹配”字符串,如清单5所示。但与以前一样,文件缓冲区的filetype属性始终设置为 sh 而不是 猛击 :

    清单5.~/.vim/scripts.vim,其if语句使用“精确匹配”条件表达式。

    if did_filetype()
        finish
    endif
    if getline(1) == '#!/bin/env bash'
        setfiletype bash
    elseif getline(1) == '#!/usr/bin/env bash'
        setfiletype bash
    elseif getline(1) == '#!/bin/bash'
        setfiletype bash
    elseif getline(1) == '#!/usr/bin/bash'
        setfiletype bash
    endif
    

    那么,我误解了什么?为什么我的 ~/.vim/scripts.vim file将文件缓冲区的filetype属性设置为 猛击 ?

    1 回复  |  直到 2 年前
        1
  •  2
  •   romainl    2 年前
    1. :help new-filetype-scripts 告诉我们:

      See $VIMRUNTIME/scripts.vim for more examples
      

      所以我们就这么做吧。最相关的部分 $VIMRUNTIME/scripts.vim 这是评论:

      " This file is called by an autocommand for every file that has just been
      " loaded into a buffer.  It checks if the type of file can be recognized by
      " the file contents.  The autocommand is in $VIMRUNTIME/filetype.vim.
      
    2. 在下面的某个地方 $VIMRUNTIME/filetype.vim (对我来说是2959的第2669行,YMMV)我们可以找到有问题的自动命令,我们称之为“自动命令A”:

      au BufNewFile,BufRead *
       \ if !did_filetype() && expand("<amatch>") !~ g:ft_ignore_pat
       \ | runtime! scripts.vim | endif
      

      这相对容易遵循:

      • BufNewFile BufRead 事件被触发,
      • 无论它们的匹配值是多少,
      • 来源全部 scripts.vim 您可以在中找到的文件 :help 'runtimepath ,
      • 如果没有设置文件类型,
      • 如果匹配的值不在某个默认列表中。

      这里有一个大问题,那就是 自动命令A 已安装 之后 另一个自动命令(对我来说是第2004行),我们称之为“自动命令B”:

      au BufNewFile,BufRead .bashrc,bashrc,bash.bashrc,.bash[_-]profile,.bash[_-]logout,.bash[_-]aliases,bash-fc[-.],*.ebuild,*.bash,*.eclass,PKGBUILD,APKBUILD call dist#ft#SetFileTypeSH("bash")
      

      这是一个问题,因为 自动命令B 首先执行,然后愚蠢地将文件类型设置为 sh 通过 dist#ft#SetFileTypeSH("bash") 功能。什么时候? 自动命令A 开始时,文件类型已经设置,因此 !did_filetype() 是假的,所以命令 runtime! scripts.vim 未执行。

      坦率地说,这似乎不必要地复杂,而且这样做的想法 let b:is_bash = 1 而不是真正设置一个合适的 bash 文件类型为 令人费解 至少可以这么说。

      在这一点上,唯一可能的结论是 ~/.vim/scripts.vim 不太可能自动获取,这使得 :帮助新的文件类型脚本 根本不正确,你尝试的解决方案存在设计缺陷。

      叹气

    3. 一个简单的解决方案是忽略 :帮助新的文件类型脚本 并将扩展逻辑和文件内容逻辑放在同一位置:

      " in ~/.vim/filetype.vim
      if exists("did_load_filetypes")
        finish
      endif
      augroup filetypedetect
        autocmd! BufRead,BufNewFile *.bash let b:is_bash = 1 | setfiletype bash
        autocmd! BufRead,BufNewFile * if getline(1) == '#!/bin/env bash'
            \ | setfiletype bash
            \ | elseif getline(1) == '#!/usr/bin/env bash'
            \ | setfiletype bash
            \ | elseif getline(1) == '#!/bin/bash'
            \ | setfiletype bash
            \ | elseif getline(1) == '#!/usr/bin/bash'
            \ | setfiletype bash
            \ | endif
      augroup END
      
    4. 如果你打开一个问题并设法说服团队解决这个问题,就会得到加分。

        2
  •  1
  •   Ry- Vincenzo Alcamo    2 年前

    $VIMRUNTIME/scripts.vim负责委托给您的scripts.vim (需要调查这一点,请参阅@romainls的答案),如果文件类型已经设置,它根本不会运行你的scripts.vim。

    根据帮助文件中的另一个示例,

    您还可以在命令中使用已检测到的文件类型。例如,要在检测到“pascal”时使用文件类型“mypascal”:

    au BufRead,BufNewFile * if &ft == 'pascal' | set ft=mypascal | endif
    

    怎么样:

    au! BufRead,BufNewFile *
      \   if &filetype == 'sh' && getline(1) =~# '\v^#!/(usr/)?bin/(env )?bash'
      \ |   set filetype=bash
      \ | endif
    
        3
  •  0
  •   Jim Fischer    2 年前

    感谢@Ry-和@romainl的宝贵意见和建议,这让我找到了自己的解决方案。正如@romainl所建议的,我的解决方案使用文件 ~/.vim/filetype.vim 而不是文件 ~/.vim/scripts.vim ; 请参阅下面的清单1。

    清单1.文件~/.vim/filetype.vim

    " ~/.vim/filetype.vim
    if exists("did_load_filetypes")
      finish
    endif
    augroup filetypedetect
      autocmd! BufRead,BufNewFile *.bash let b:is_bash = 1 | setfiletype bash
      autocmd! BufRead,BufNewFile *
        \ if getline(1) =~# '\v^#!/(usr/)?bin/(env[ \t]+)?<bash>'
        \ |   let b:is_bash = 1
        \ |   setfiletype bash
        \ | endif
    augroup END