zoukankan      html  css  js  c++  java
  • vim中文件类型识别、语法高亮及缩进实现流程

    一、文件类型

    在使用vim编辑一个文件的时候,如果能够识别出文件的类型,加上对应的高亮规则,可以使文件的查看更加醒目,这个功能几乎是使用vim浏览文件的一个核心诉求。
    另外,在进行文件编辑的时候,特别是使用vim写代码的时候(典型的场景是通过vim写C/C++代码),如果能够智能缩进,还可以减少敲代码。例如,在每行的开头自动添加缩进与前一行对齐;或者是当在输入注释时(前一行是//或者/*),通常默认的缩进也会在当前行前面加上注释(//或者*),这也是很多开源软件中看到/**/风格注释中每行开头都有一个*的原因。
    前面说的高亮和缩进,需要先识别出文件类型,然后根据文件类型确定语法、高亮、缩进。所以如何识别出这个文件的类型就是整个便利性的基础。不过话说回来,这些功能对vim这种编辑器来说都不是必须的,它们都是为了让使用更加便捷,所以这些功能的实现很多是通过插件来完成。

    二、vim的filetype命令

    在vim执行filetype命令时,vim执行的函数为ex_filetype,其中比较关键的时,它会找到filetype.vim脚本文件并执行,这个filetype.vim文件名是在代码中写死的。
    /*
    * ":filetype [plugin] [indent] {on,off,detect}"
    * on: Load the filetype.vim file to install autocommands for file types.
    * off: Load the ftoff.vim file to remove all autocommands for file types.
    * plugin on: load filetype.vim and ftplugin.vim
    * plugin off: load ftplugof.vim
    * indent on: load filetype.vim and indent.vim
    * indent off: load indoff.vim
    */
    static void
    ex_filetype(exarg_T *eap)
    {
    ……
    if (STRCMP(arg, "on") == 0 || STRCMP(arg, "detect") == 0)
    {
    if (*arg == 'o' || !filetype_detect)
    {
    source_runtime((char_u *)FILETYPE_FILE, DIP_ALL);
    filetype_detect = TRUE;
    if (plugin)
    {
    source_runtime((char_u *)FTPLUGIN_FILE, DIP_ALL);
    filetype_plugin = TRUE;
    }
    if (indent)
    {
    source_runtime((char_u *)INDENT_FILE, DIP_ALL);
    filetype_indent = TRUE;
    }
    }
    if (*arg == 'd')
    {
    (void)do_doautocmd((char_u *)"filetypedetect BufRead", TRUE, NULL);
    do_modelines(0);
    }
    }
    ……
    }
    其中用到的一些宏
    #ifndef FILETYPE_FILE
    # define FILETYPE_FILE "filetype.vim"
    #endif
    #ifndef FTPLUGIN_FILE
    # define FTPLUGIN_FILE "ftplugin.vim"
    #endif
    #ifndef INDENT_FILE
    # define INDENT_FILE "indent.vim"
    #endif

    三、filetype.vim的主要内容

    在该文件中,注册了对于打开文件,读取文件之类事件的自动处理函数。它们主要通过文件名、文件后缀之类的信息来猜测文件格式。
    runtimefiletype.vim
    " Shell scripts (sh, ksh, bash, bash2, csh); Allow .profile_foo etc.
    " Gentoo ebuilds and Arch Linux PKGBUILDs are actually bash scripts
    au BufNewFile,BufRead .bashrc*,bashrc,bash.bashrc,.bash[_-]profile*,.bash[_-]logout*,.bash[_-]aliases*,*.bash,*/{,.}bash[_-]completion{,.d,.sh}{,/*},*.ebuild,*.eclass,PKGBUILD* call dist#ft#SetFileTypeSH("bash")
    au BufNewFile,BufRead .kshrc*,*.ksh call dist#ft#SetFileTypeSH("ksh")
    au BufNewFile,BufRead */etc/profile,.profile*,*.sh,*.env call dist#ft#SetFileTypeSH(getline(1))
    当然也有一部分是通过文件的前几行来判断/确认
    " Shell script (Arch Linux) or PHP file (Drupal)
    au BufNewFile,BufRead *.install
    if getline(1) =~ '<?php' |
    setf php |
    else |
    call dist#ft#SetFileTypeSH("bash") |
    endif

    还有一些脚本类型文件的判断,通过第一行中的"#!"来判断脚本类型的。这也意味着可以对于"#!"开头的文件,vim通常能很好的识别出来脚本类型。
    在脚本类型scripts.vim文件检测中
    let s:line1 = getline(1)

    if s:line1 =~# "^#!"
    " A script that starts with "#!".

    " Check for a line like "#!/usr/bin/env VAR=val bash". Turn it into
    " "#!/usr/bin/bash" to make matching easier.
    if s:line1 =~# '^#!s*S*<envs'
    let s:line1 = substitute(s:line1, 'S+=S+', '', 'g')
    let s:line1 = substitute(s:line1, '<envs+', '', '')
    endif

    四、syntax on指令的执行

    vim-8.1srcsyntax.c
    static void
    syn_cmd_onoff(exarg_T *eap, char *name)
    {
    char_u buf[100];

    eap->nextcmd = check_nextcmd(eap->arg);
    if (!eap->skip)
    {
    STRCPY(buf, "so ");
    vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
    do_cmdline_cmd(buf);
    }
    }

    其中高亮文件夹
    #ifndef SYNTAX_FNAME
    # define SYNTAX_FNAME "$VIMRUNTIME/syntax/%s.vim"
    #endif
    在syntax.vim脚本中,注册了FileType事件,也就是当设置了文件类型之后会执行的命令,这个命令会触发set syntax=XXX命令的执行,而该命令进而触发颜色的高亮。
    vim-8.1 untimesyntaxsyntax.vim
    " Load the Syntax autocommands and set the default methods for highlighting.
    runtime syntax/synload.vim
    ……
    " Set up the connection between FileType and Syntax autocommands.
    " This makes the syntax automatically set when the file type is detected.
    augroup syntaxset
    au! FileType * exe "set syntax=" . expand("<amatch>")
    augroup END

    if (save_ei != NULL)
    {
    au_event_restore(save_ei);
    apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn,
    curbuf->b_fname, TRUE, curbuf);
    }

    五、缩进(indent)

    当执行filetype indent on时,执行的命令为:
    static void
    ex_filetype(exarg_T *eap)
    {
    ……
    if (*arg == 'o' || !filetype_detect)
    {
    source_runtime((char_u *)FILETYPE_FILE, DIP_ALL);
    filetype_detect = TRUE;
    if (plugin)
    {
    source_runtime((char_u *)FTPLUGIN_FILE, DIP_ALL);
    filetype_plugin = TRUE;
    }
    if (indent)
    {
    source_runtime((char_u *)INDENT_FILE, DIP_ALL);
    filetype_indent = TRUE;
    }
    }
    ……
    }

    在indent.vim脚本中,也注册了对于文件类型设置的侦听。
    runtimeindent.vim
    augroup filetypeindent
    au FileType * call s:LoadIndent()
    func! s:LoadIndent()

    六、通过set ft=XXXX触发的事件流

    从这个流程上看,该动作执行之后,触发的事件为EVENT_FILETYPE自动事件,如果希望处理这个事件,可以注册对于该事件的自动命令。由于indent和syntax都注册了对这里抛出的FileType事件的侦听,所以当设置了文件类型,语法和缩进开启的情况下,它们会自动生效。
    vim-8.1srcoption.c
    static char_u *
    did_set_string_option(
    int opt_idx, /* index in options[] table */
    char_u **varp, /* pointer to the option variable */
    int new_value_alloced, /* new value was allocated */
    char_u *oldval, /* previous value of the option */
    char_u *errbuf, /* buffer for errors, or NULL */
    int opt_flags) /* OPT_LOCAL and/or OPT_GLOBAL */
    {
    ……
    else if (varp == &(curbuf->b_p_ft))
    {
    /* 'filetype' is set, trigger the FileType autocommand.
    * Skip this when called from a modeline and the filetype was
    * already set to this value. */
    if (!(opt_flags & OPT_MODELINE) || value_changed)
    {
    static int ft_recursive = 0;

    ++ft_recursive;
    did_filetype = TRUE;
    // Only pass TRUE for "force" when the value changed or not
    // used recursively, to avoid endless recurrence.
    apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname,
    value_changed || ft_recursive == 1, curbuf);
    --ft_recursive;
    /* Just in case the old "curbuf" is now invalid. */
    if (varp != &(curbuf->b_p_ft))
    varp = NULL;
    }
    }
    ……
    }

  • 相关阅读:
    对象实例化内存布局与访问定位
    方法区

    本地方法栈
    本地方法接口
    虚拟机栈
    程序计数器
    运行时数据区概述及线程
    自学》2.网页弹窗计算商品价格
    自学》1.用网站发邮件
  • 原文地址:https://www.cnblogs.com/tsecer/p/15003566.html
Copyright © 2011-2022 走看看