vim is powerful, but vimL is surely a hard script to learn. the balance is, do you want more power or more simplicity? (“BOTH” is not an option :) )

let me throw some code here:

au BufRead,BufNew,BufAdd *.asciidoc,README,TODO,CHANGELOG,NOTES setfiletype=asciidoc
au BufRead,BufNew,BufAdd *.txt call s:FTasciidoc(AsciidocChecklines)
au BufRead,BufNew,BufAdd *.log call s:FTlog()

it’s all about vim filetype setup (then you have syntax highlight , fold, file-specific keybindings, … whatever, all of the VIM magics ). and most of the complexity comes from the “auto-detection” techniques. ideally what I want are:

  • if you are sure what filetype you need, “hard code” it based on the file extention
  • otherwise, “look into” the file content to decide
    • if the .txt file “looks like” an asciidoc, set it to asciidoc filetype
    • if the .log file “looks like” a junose or junos log file, we have to narrow it down to tell whether it’s
      • a config file (mostly just a “show config” ) or
      • a troubleshooting log file (full of “show xxx”) – based on the nature, we can apply different syntax (highlighting for config use different rules than highlighting system logs)
  • when doing this , also consider the file size - a huge text file severely slow down the VIM performance (jump,search,change,update,etc) if equiped with syntax magic.

  • if it doesn’t look like known specific types, just set it to txt or whatever …

“code speaks louder than words”, here it is:

"ping
"this file merged some content detection files
" (2013-05-17) move fsize check into the autocmd function call 
" (otherwise won't work if prior to the function code)
"
"define a global var knob, to enable/disable debug info on asciidoc file detection
"
if !exists("MyAsciidocDebug")
    let MyAsciidocDebug = 0
endif

if !exists("MyJelDebug")
    let MyJelDebug = 0
endif

if !exists("AsciidocChecklines")
  let AsciidocChecklines = 10
endif

if !exists("JecCheckLines")
  let JecCheckLines = 200
endif

if !exists("JecCheckLineLength")
  let JecCheckLineLength = 100
endif

if !exists("AsciidocCheckLineLength")
  let AsciidocCheckLineLength= 50
endif

let s:largefilesize = 1000000

"w/o BufRead works for $vim file.txt, but not for :tab file.txt. that need BufNew, 
au BufRead,BufNew,BufAdd *.asciidoc,README,TODO,CHANGELOG,NOTES setfiletype=asciidoc
au BufRead,BufNew,BufAdd *.txt call s:FTasciidoc(AsciidocChecklines)
au BufRead,BufNew,BufAdd *.log call s:FTlog()


" This function checks for a valid AsciiDoc document title after first
" skipping any leading comments.
function! s:FTasciidoc(checklines)

  let s:fsize = getfsize(expand('%%:p')) "get current file size

" ping: add another check, set asciidoc if there is a "= ABC" preceded with a blank line
  let n = 1                         "starting from 1st line

  if g:MyAsciidocDebug | echom 'asciidoc scan partI: check ^= xx in first '.a:checklines." lines" | endif

  while n <= a:checklines           "checking 1st 10 lines
    let line = getline(n)           "get a line
    let n = n + 1                   "increase number# earlier to make sure it get increased

    if g:MyAsciidocDebug | echom 'get a line:" ' . line . '" at number ' . (n-1) | endif
    if len(line) > g:AsciidocCheckLineLength   "skip pattern search if too long
      if g:MyAsciidocDebug | echom "line " . (n-1) . "is too long to be a heading, skip" | endif
      continue                      
    endif                     

    if line =~ '^=\+\s\S\+'         "if exact one space bet = and text(make it strict)
      if getline(n-2) =~ '^\s*$'    "and an empty line exist right before this line
        if (s:fsize <= s:largefilesize)
          if g:MyAsciidocDebug | echom "asciidoc mark ^= xxx found in 10lines, not a huge file,ft set to asciidoc2..." | endif
          set filetype=asciidoc2
          "return after successfully set
          return                    
        else
          if g:MyAsciidocDebug | echom "asciidoc mark ^= xxx found in 10lines, a huge file(over 2M!),ft set to asciidoc" | endif
          set filetype=asciidoc
          "return after successfully set
          return                    
        endif
        return                      
      else
        if g:MyAsciidocDebug | echom "asciidoc mark = ^xxx found, but no empty line ahead, ft not set to asciidoc" | endif
      endif
    else
    endif
  endwhile                          

  if g:MyAsciidocDebug | echom "asciidoc mark not found in first " . a:checklines . "lines, don't set ft" | endif
  if g:MyAsciidocDebug | echom 'asciidoc scan partI finished' | endif




  " otherwise,
  " scan the file line by line, skipping all comment blocks and comment lines
  " for a title/header text
  if g:MyAsciidocDebug | echom 'asciidoc scan partII: check text and dashes in first '.a:checklines." lines" | endif
  let in_comment_block = 0
  let n = 1                         "starting from 1st line

  while n < a:checklines            " 

    let line = getline(n)           "get a line
    if g:MyAsciidocDebug | echom 'get a line:" ' . line . '" at number ' . (n-1) | endif

    if len(line) > g:AsciidocCheckLineLength   "skip pattern search if too long
      if g:MyAsciidocDebug | echom "line " . n . "is too long to be a heading, skip" | endif
        let n = n + 1
      continue                      
    endif                     

    let n = n + 1                   "anchor move to next line
    if line =~ '^/\{4,}$'           "if '////..', then it's a comment block
      if ! in_comment_block         "if haven't seen it before
        if g:MyAsciidocDebug | echom "looks we found first comment in comment block" | endif
        let in_comment_block = 1    "mark it as block start
      else                          "otherwise,
        if g:MyAsciidocDebug | echom "looks we found 2nd comment in comment block" | endif
        let in_comment_block = 0    "mark it as block end
      endif
      continue                      "go check next line
    else
        if g:MyAsciidocDebug | echom "not a comment block" | endif
    endif                           "in either case

    if in_comment_block             "if we are still in block
      if g:MyAsciidocDebug | echom "we are still in a comment block,continue" | endif
      continue                      "move on
    endif

    if line !~ '\(^//\)\|\(^\s*$\)' "if we find sth else (means out of block)
      if g:MyAsciidocDebug | echom "found sth else other than a comment block mark/empty line,break" | endif
      break                         "meaning it's neither '//' nor blank line,break the loop
    endif                           "as this point we got some texts that

  endwhile                          "we assume to be a title or header

  "the title/header texts must be more than 3CH
  if line !~ '.\{3,}'               "if the line is too short (<3)
    if g:MyAsciidocDebug | echom "line " . (n-1) . "is too short and not looks like a heading line, skip" | endif
    return                          
  endif

  "there must be a '---' or '===' line under the text,also need >3 CH
  let len = len(line)               "now check 'next' line, get length & content
  let line = getline(n)             "see line 23(n=n+1) for why it's next line
  if line !~ '[-=]\{3,}'            "if there are no 3 or more '-' or '='
    if g:MyAsciidocDebug | echom "no 3 or more contineous -/= found under the above text,return" | endif
    return                         
  endif

  "the length difference of text and mark line in the header/title must less than 3
  if len < len(line) - 3 || len > len(line) + 3
    if g:MyAsciidocDebug | echom "length diff bet text and -/= under it is bigger than 3,return" | endif
    return
  endif

  "if such a title/head is found, then it's an asciidoc
  "setfiletype asciidoc

  "ping, add fsize check...
  if (s:fsize <= s:largefilesize)
    set filetype=asciidoc2      
    if g:MyAsciidocDebug | echom "asciidoc mark2 found & file is small, set ft asciidoc2" | endif
  else
    set filetype=asciidoc
    if g:MyAsciidocDebug | echom "asciidoc mark2 found & file is big, set ft asciidoc" | endif
  endif

  if g:MyAsciidocDebug | echom 'asciidoc scan partII finished' | endif

endfunction


function! s:FTlog()

" ping: (2013-05-17) add file check
  let s:largefilesize = 1000000
  let s:fsize = getfsize(expand('%%:p')) "get current file size

  if (s:fsize <= s:largefilesize)

      let n = 1                         "starting from 1st line
      let endline = line('$')
      if g:MyJelDebug | echom "max line number is" endline | endif
      while n < g:JecCheckLines         "checking some lines from 1st line

          let line = getline(n)           "get a line

          if g:MyJelDebug 
            echom "get a line" line
            echom "looking for show config in the line..."
          endif

          if len(line) > g:JecCheckLineLength              "skip pattern search if too long
            let n = n + 1                 "very efficient/time saving!
            continue                      
          endif                     

          if line =~ '\S\S\+[^\]]\{0,45}\S[#>%]\s*show config'      "searching 'show config'
              let ratio = (endline - n) * 100 / endline               "if match, check how much its output occupy the file
              if ratio >= 60                "if 'show config' capture is alot
                  set filetype=jec            "set ft to jec(junos e config)
                  if g:MyJelDebug
                    echom "show config detected, set ft to jec" 
                  endif
                  return                      
              else                          "otherwise if no much output, 
                  if g:MyJelDebug             "don't set jec
                    echom "show config detected but no much output, set ft to jel" 
                  endif
                  set filetype=jel
                  return
              endif

          else                            "or if no show config detected,don't set

          endif

          let n = n + 1                   "otherwise check next line

      endwhile                          

      if g:MyJelDebug | echom "no show config detected, set ft to jel" | endif
      set filetype=jel

  else
    echom "file too large (current size:" . s:fsize . " Bytes > maxsize for ft check " . s:largefilesize . " Bytes!) , won't set ft"
  endif

endfunction

" vim: et sw=2 ts=2 sts=2:


blog comments powered by Disqus

Published

17 May 2013

Tags