VIM中文重排功能插件

"								 Version 1.5
" format.vim -	Format multibyte text, for tha languages, which can split
"		line anywhere, unless prohibited. (for Vim 6.0 alpha)
"
" Last Change:	  12-Jan-2002.
" Maintainer:	  Muraoka Taro <[email protected]>
" Practised By:	  Takuhiro Nishioka <[email protected]>
" Base Idea:	  Muraoka Taro <[email protected]>
" Copyright:	  Public Domain
" Modified:	  Edward G.J. Lee <[email protected]> 2002.02.11
" ¦Û¦æ§ó§ï¬° cp950 ¥H¾A¦XÁcÅ餤¤å

scriptencoding cp950

" function Format(start_line_number, end_line_number)
" 
" Format() will allow format multibyte text.  In some of East Asian
" languages, the line can break anywhere, unless prohibited.  Original Vim's
" "gq" format command doesn't allow to break line at the midst of word.
" This function split line at each multibyte character.  And it can handle
" prohibited line break rules.
"
" This function is following Vim's "gq" command. But there will be lack of
" something.

if exists('plugin_format_disable')
  finish
endif

"---------------------------------------------------------------------------
"				    Options
"
" "format_command"
"
" Specifies the format command that format lines to the width the
" 'textwidth' option specifies. The "Q" command formerly did this, so if you
" still want to use "Q", set this to "Q"
"
"let format_command = "Q"
if !exists('format_command')
  let format_command = "gq"
endif

"
" "format_join_spaces"
"
" Delete a space , when joining lines, according to the following rules.
" This rule is also applied to "J" command
"   1	line end with AND next line start with a multibyte char
"   2	line end with OR  next line start with a multibyte char
"   3	same with original join command
"
if !exists("g:format_join_spaces")
  let g:format_join_spaces = 1
endif

"
" "format_follow_taboo_rule"
"
" Move to a point that will not break forbidden line break rules. If you
" don't want to do this, set this to "0".
"
if !exists("g:format_follow_taboo_rule")
  let g:format_follow_taboo_rule = 1
endif

"
" "format_allow_over_tw"
"
" The width that can over 'textwidth'. This variable is used for taboo rule.
"
if !exists("g:format_allow_over_tw")
  let g:format_allow_over_tw = 2
endif

"
" "format_indent_sensitive"
"
" When the indentation changes, it's the end of a paragraph. Note that if
" this option is set, second indentation is disabled.
"
if !exists("g:format_indent_sensitive")
  let g:format_indent_sensitive = 0
endif

"---------------------------------------------------------------------------
"				  Sub Options
"
" "g:format_no_begin"
"
" This option is space-separated list of characters, that are forbidden to
" be at beginning of line. Add two spaces for ASCII characters. See also
" TabooRuleMatch()
" ¥[¤J¤¤¤åÁ×ÂIÀY
let g:format_no_begin = "!  ,  .  ?  )  ]  }  -  _  ~  :  ;  ¡I  ¡A  ¡C  ¡H  ¡^  ¡G  ¡F  "

"
" "g:format_no_end"
"
" This option is space-separated list of characters, that are forbidden to
" be at end of line. Add two spaces for ASCII characters. See also
" TabooRuleMatch()
"
let g:format_no_end = "(  [  {  "

"
" For Japanese.  There are encoding name aliases, so we cannot directly
" compare option 'encoding' with locale name (ex: 'japan').
"
"let s:save_encoding = &encoding
"let &encoding = 'japan'
"if &encoding == s:save_encoding
"  let no_b = ''
"  let no_b = no_b . "‘ ‹ f h ñ Œ  Ž A B X r t v x z l "
"  let no_b = no_b . "‚Ÿ ‚¡ ‚£ ‚¥ ‚§ ‚Á ‚á ‚ã ‚å ‚ì J K T U "
"  let no_b = no_b . "ƒ@ ƒB ƒD ƒF ƒH ƒb ƒƒ ƒ… ƒ‡ ƒŽ ƒ• ƒ– "
"  let no_b = no_b . "E [ R S I “ j C D F G H n p c ` "

"  let no_e = ''
"  let no_e = no_e . "’ e g q s u w y k  i m o  "

"  let g:format_no_begin = g:format_no_begin . no_b
"  let g:format_no_end = g:format_no_end . no_e
"  unlet no_b no_e
"endif
"let &encoding = s:save_encoding
"unlet s:save_encoding

"
" "s:format_motion_list"
"
" Specifiles the motion command that could follow "format_command". These
" are pairs of two letters. Note that not all motion commands can be
" specified here. Use visual mode for other motions.
"
let m_list = "apawaWasa]a[a)a(aba>a}a{aB"
let m_list = m_list . "ipiwiWisi]i[i)i(ibi>i}i{iB"
let m_list = m_list . "gqq "
let m_list = m_list . "h j k l 0 ^ $ ; , - + w W e E b B "
let m_list = m_list . "( ) { } n N % H M L G / ? "
let m_list = m_list . "gjgkggg0g^gmgegEg$]][[][[[[]''``"
let m_list = m_list . "'[`[']`]'<`<'>`>'/"`/""
let m_list = m_list . "[([{])]}]m]M[m[M[#]#[*[/"
let m_list = m_list . "'a'b'c'd'e'f'g'h'i'j'k'l'm'n'o'p'q'r's't'u'v'w'x'y'z"
let m_list = m_list . "`a`b`c`d`e`f`g`h`i`j`k`l`m`n`o`p`q`r`s`t`u`v`w`x`y`z"
let m_list = m_list . "2j3j4j5j6j7j8j9j"
let m_list = m_list . "2k3k4k5k6k7k8k9k"
let m_list = m_list . "2H3H4H5H6H7H8H9H"
let m_list = m_list . "2L3L4L5L6L7L8L9L"
let s:format_motion_list = m_list
unlet m_list

"---------------------------------------------------------------------------
"
" DoMappings()
"   Do mappings.
"
function! DoMappings()
  " Normal mode mappings.
  let length = strlen(s:format_motion_list)
  let i = 0
  while i <= length - 2
    let motion = strpart(s:format_motion_list, i, 2)
    let motion = substitute(motion, " $", "", "")
    execute "nmap <silent> " . g:format_command . motion . " :call " . "<SID>FormatWorkhorse(/"" . motion . "/")<CR>"
    let i = i + 2
  endwhile

  " Visual mode mapping.
  execute "vmap <silent> ".g:format_command." <ESC>:call"." <SID>Format(line(/"'</"), line(/"'>/"))<CR>"

  " Change "J" to follow "g:format_join_spaces"
  nmap <silent> J :call <SID>DoRangeJoin("")<CR>
  vmap <silent> J <ESC>:call <SID>DoJoinRange(line("'<"), line("'>"))<CR>
endfunction

"
" FormatWorkhorse(motion)
"   Select the area that moves over, then pass the start and end line number
"   of the area to Format()
"
function! s:FormatWorkhorse(motion)
  if a:motion == "gq" || a:motion == "q"
    execute "normal! V/<ESC>"
  elseif a:motion == "/" || a:motion == "?"
    execute "let pattern = input(/"" . a:motion . "/")"
    execute "normal! v" . a:motion . pattern . "/<CR>/<ESC>"
  else
    execute "normal! v" . a:motion . "/<ESC>"
  endif
  call s:Format(line("'<"), line("'>"))
endfunction

"
" Format(start_lnum, end_lnum)
"   Format the area from the start line number to the end line number.
"
function! s:Format(start_lnum, end_lnum)
  let count_nr = a:end_lnum - a:start_lnum + 1
  let advance = 1
  " current line is the start of a paragraph.
  let first_par_line = 1
  " the second indent
  let second_indent = "default"

  " Check 2 in the formatoptions
  let do_second_indent = s:HasFormatOptions('2')

  let showcmd_save = &showcmd
  set noshowcmd
  let wrap_save = &wrap
  set nowrap
  let lazyredraw_save = &lazyredraw
  set lazyredraw

  " Set cursor to the start line number.
  call s:SetCursor(a:start_lnum)

  " Get info about the previous and current line.
  if a:start_lnum == 1
    " current line is not part of paragraph
    let is_not_par = 1
  else
    normal! k
    " the commet leader of current line
    let leader = s:GetLeader()
    let is_not_par = s:FmtCheckPar(leader)
    normal! j
  endif

  " the commet leader of next line
  let next_leader = s:GetLeader()
  " next line not part of paragraph
  let next_is_not_par = s:FmtCheckPar(next_leader)

  " at end of paragraph
  let is_end_par = is_not_par || next_is_not_par

  " operation top
  let op_top = 1
  while count_nr > 0
    " Advance to next paragraph.
    if advance
      if op_top
	let op_top = 0
      else
	normal! j
      endif
      let leader = next_leader
      let is_not_par = next_is_not_par
      " previous line is end of paragraph
      let prev_is_end_par = is_end_par
    endif

    " The last line to be formatted.
    if count_nr == 1
      let next_leader = ""
      let next_is_not_par = 1
    else
      normal! j
      let next_leader = s:GetLeader()
      let next_is_not_par = s:FmtCheckPar(next_leader)
      normal! k
    endif

    let advance = 1
    let is_end_par = is_not_par || next_is_not_par

    " Skip lines that are not in a paragraph.
    if !is_not_par

      " For the first line of a paragraph, check indent of second line.
      " Don't do this for comments and empty lines.
      if first_par_line
	  /&& do_second_indent
	  /&& prev_is_end_par
	  /&& leader =~ "^//s*$"
	  /&& next_leader =~ "^//s*$"
	  /&& getline(line(".") + 1) !~ "^$"
	let second_indent = next_leader
      endif

      " When the comment leader changes, it's the end of the paragraph
      if !s:SameLeader(leader, next_leader)
	let is_end_par = 1
      endif

      " If we have got to the end of a paragraph, format it.
      if is_end_par
	" do the formatting
	call s:FormatLine(second_indent)
	let second_indent = "default"
	let first_par_line = 1
      endif

      " When still in same paragraph, join the lines together.
      if !is_end_par
	let advance = 0
	" join current line and next line without the comment leader
	call s:DoJoin(next_leader)
	let first_par_line = 0
      endif

    endif
    let count_nr = count_nr - 1
  endwhile
  if wrap_save
    set wrap
  endif
  if !lazyredraw_save
    set nolazyredraw
  endif
  if showcmd_save
    set showcmd
  endif
endfunction

"
" FormatLine(second_indent)
"   Format currentline.
"
function! s:FormatLine(second_indent)
  " check textwidth
  if &textwidth == 0
    let textwidth = 76
  else
    let textwidth = &textwidth
  endif

  let do_second_indent = s:HasFormatOptions("2")
  let fo_do_comments = s:HasFormatOptions("q")
  let second_indent = a:second_indent

  " save the original option's value
  let formatoptions_save = &formatoptions
  let iskeyword_save = &iskeyword

  let leader_width = s:GetLeader("get_leader_width")

  " When fo_do_comments is TRUE, set formatoptions value so that the comment
  " leader is set for next line.
  if fo_do_comments
    set formatoptions+=r
  else
    set formatoptions-=r
  endif

  " Set iskeyword option value to every printable ascii characters, so that
  " "w" can stop at only multibyte-ascii boundary or white space.
  set iskeyword="!-~"

  call s:SetCursor(line("."), textwidth)
  while s:GetWidth() > virtcol(".")
    let finish_format = 0
    let force_fold = 0
    let do_insert = 0
    let max_width = virtcol(".") + g:format_allow_over_tw

    let ch = s:GetCharUnderCursor()
    normal! l
    let next_ch = s:GetCharUnderCursor()
    normal! h

    " English word folding
    if ch =~ "[!-~]//{1}" && next_ch =~ "[!-~]//{1}"
      call s:MoveToWordBegin()
      if virtcol(".") - 1 > leader_width
	" move to previous word end
	normal! ge
      endif
    endif

    " Skip white spaces
    if ch =~ "//s"
      while ch =~ "//s" && virtcol(".") - 1 > leader_width
	normal! h
	let ch = s:GetCharUnderCursor()
      endwhile
      let force_fold = 1
    endif

    if virtcol(".") - 1 <= leader_width
      call s:MoveToFirstWordEnd(leader_width)
      let force_fold = 1
      if s:GetWidth() == virtcol(".")
	let finish_format = 1
      endif
    endif

    " Taboo rule
    if !finish_format && !force_fold && g:format_follow_taboo_rule
      normal! l
      let next_ch = s:GetCharUnderCursor()
      normal! h
      if s:TabooRuleMatch(g:format_no_begin, next_ch)
	normal! l
	while s:TabooRuleMatch(g:format_no_begin, next_ch)
	  " if cursor is at the line end, break.
	  if s:GetWidth() == virtcol(".")
	    let finish_format = 1
	    break
	  endif
	  normal! l
	  let next_ch = s:GetCharUnderCursor()
	endwhile
	if !finish_format
	  normal! h
	endif
      endif

      let ch = s:GetCharUnderCursor()
      if virtcol(".") > max_width
	let finish_format = 0
	while s:TabooRuleMatch(g:format_no_begin, ch)
	    /&& virtcol(".") - 1 > leader_width
	  normal! h
	  let ch = s:GetCharUnderCursor()
	endwhile
	if ch =~ "[!-~]//{1}"
	  call s:MoveToWordBegin()
	  if virtcol(".") - 1 > leader_width
	    normal! ge
	  else
	    call s:MoveToFirstWordEnd(leader_width)
	    let force_fold = 1
	  endif
	else
	  let do_insert = 1
	endif
      endif

      let ch = s:GetCharUnderCursor()
      if s:TabooRuleMatch(g:format_no_end, ch) && !force_fold
	let do_insert = 0
	while s:TabooRuleMatch(g:format_no_end, ch)
	    /&& virtcol(".") -1 > leader_width
	  normal! h
	  let ch = s:GetCharUnderCursor()
	endwhile
	if virtcol(".") -1 <= leader_width
	  call s:MoveToFirstWordEnd(leader_width)
	endif
      endif
    endif

    if finish_format
      break
    endif

    if do_insert
      call s:InsertNewLine()
    else
      call s:AppendNewLine()
    endif

    if do_second_indent && second_indent != "default"
      call setline(line(".")
	  /, second_indent . substitute(getline("."), "^//s*", "", ""))
      let do_second_indent = 0
      if strlen(second_indent) > 0
	normal! h
      endif
    endif

    if virtcol(".") == 1
      let leader_width = 0
    else
      let leader_width = virtcol(".")
    endif

    call s:SetCursor(line("."), textwidth)
  endwhile

  execute "set formatoptions=" . formatoptions_save
  execute "set iskeyword=" . iskeyword_save
endfunction

"
" GetLeader(...)
"   Get the comment leader string from current line. If argument
"   is specified, then return the comment leader width. Note that
"   returned comment leader and the current line's comment leader is
"   not always same.
"
function! s:GetLeader(...)
  if !s:HasFormatOptions('q')
    if a:0 == 1
      return 0
    endif
    return ""
  endif

  let col_save = virtcol(".")

  let formatoptions_save = &formatoptions
  let autoindent_save = &autoindent
  let cindent_save = &cindent
  let smartindent_save = &smartindent
  set formatoptions+=o
  set autoindent
  set nocindent
  set nosmartindent

  execute "normal! ox/<ESC>/"_x"

  if a:0 == 1
    if getline(".") =~ "^$"
      let leader_width = 0
    else
      let leader_width = virtcol(".")
    endif
  endif

  let leader = getline(".")

  if line(".") == line("$")
    normal! "_dd
  else
    normal! "_ddk
  endif

  execute "set formatoptions=" . formatoptions_save
  if !autoindent_save
    set noautoindent
  endif
  if cindent_save
    set cindent
  endif
  if smartindent_save
    set smartindent
  endif

  execute "normal! " . col_save . "|"

  if a:0 == 1
    return leader_width
  else
    return leader
  endif
endfunction

"
" FmtCheckPar(leader)
"   Blank lines, lines containing only white space or the comment leader,
"   are left untouched by the formatting. The function returns true in this
"   case.
"
function! s:FmtCheckPar(leader)
  let three_start = substitute(&com, '.*s[^:]*:/([^,]*/),.*', '/1', '')
  let three_end = substitute(&com, '.*e[^:]*:/([^,]*/),.*', '/1', '')
  let line = substitute(getline("."), "//s*$", "", "")
  let line = substitute(line, "^//s*", "", "")
  let leader = substitute(a:leader, "//s*$", "", "")
  let leader = substitute(leader, "^//s*", "", "")
  if line == three_start || line == three_end
    return 1
  endif
  return line == leader
endfunction

"
" SameLeader(leader1, leader2)
"   Return true if the two comment leaders given are the same. White-space is
"   ignored.
"
function! s:SameLeader(leader1, leader2)
  if g:format_indent_sensitive
    return a:leader1 == a:leader2
  else
    return substitute(a:leader1, "//s//+$", "", "")
	/== substitute(a:leader2, "//s//+$", "", "")
  endif
endfunction


"
" SetCursor(lnum, width)
"   Set cursor to the line number, then move the cursor to within the width
"   and the most right virtual column.
"
function! s:SetCursor(lnum, ...)
  execute a:lnum

  if a:0 == 1
    execute "normal! " . a:1 . "|"
    if a:1 > 2 && virtcol(".") > a:1
      normal! h
    endif
  endif
endfunction

"
" HasFormatOptions(x)
"   Return true if format option 'x' is in effect. Take care of no
"   formatting when 'paste' is set.
"
function! s:HasFormatOptions(x)
  if &paste
      /|| (a:x == "2" && !&autoindent)
      /|| (a:x == "2" && g:format_indent_sensitive)
    return 0
  endif
  return &formatoptions =~ a:x
endfunction

"
" DoRangeJoin(next_leader)
"   DoJoin driver, able to support range.
"
function! s:DoRangeJoin(next_leader) range
  if  count > 2
    let repeat = count - 1
  else
    let repeat = 1
  endif

  while repeat
    call s:DoJoin(a:next_leader)
    let repeat = repeat - 1
  endwhile
endfunction

"
" DoJoin(next_leader)
"   Join line and next line ,according to g:format_join_spaces. The comment
"   leader will be removed.
"
function! s:DoJoin(next_leader)
  if line(".") == line("$")
    return
  endif

  let showcmd_save = &showcmd
  set noshowcmd
  let wrap_save = &wrap
  set nowrap
  let lazyredraw_save = &lazyredraw
  set lazyredraw


  normal! $
  let end_char = s:GetCharUnderCursor()

  if s:HasFormatOptions("q") && a:next_leader != ""
    let next_leader = escape(a:next_leader, '^.*/$~[]')
    let next_leader = "^" . substitute(next_leader, "//s*$", "", "")
    normal! j0
    if getline(".") =~ next_leader
      call setline(line("."), substitute(getline("."), next_leader, "", ""))
    else
      let leader_width = s:GetLeader("get_leader_width")
      let i = leader_width + 1
      execute "normal! 0/"_d" . i . "|"
    endif
    normal! k
  endif

  normal! J

  if s:GetWidth() > virtcol(".") && s:GetCharUnderCursor() == " "
    normal! l
    let begin_char = s:GetCharUnderCursor()
    normal! h
    if g:format_join_spaces == 1
	/&& (strlen(end_char) > 1 && strlen(begin_char) > 1)
      normal! "_x
    elseif g:format_join_spaces == 2
	/&& (strlen(end_char) > 1 || strlen(begin_char) > 1)
      normal! "_x
    endif
  endif
  if wrap_save
    set wrap
  endif
  if !lazyredraw_save
    set nolazyredraw
  endif
  if showcmd_save
    set showcmd
  endif
endfunction

"
" DoJoinRange(start_lnum, end_lnum)
"   Join lines from start_lnum to end_lnum, according to the
"   "$fomrat_join_spaces"
"
function! s:DoJoinRange(start_lnum, end_lnum)
  let count_nr = a:end_lnum - a:start_lnum
  call s:SetCursor(a:start_lnum)
  while count_nr > 0
    call s:DoJoin("")
    let count_nr = count_nr - 1
  endwhile
endfunction

"
" GetWidth()
"   Return the current line width. If the line is empty returns 0. Note that
"   if the character at the line end is a multibyte character, this returns
"   real width minus 1, same as virtcol().
"
function! s:GetWidth()
  return virtcol("$") - 1
endfunction

"
" GetCharUnderCursor()
"   Get (multibyte) character under current cursor.
"
function! s:GetCharUnderCursor()
  let str = getline(".")
  let idx = col(".") - 1
  let ch = str[idx]
  if char2nr(ch) >= 128
    return strpart(str, idx, 2)
  else
    return ch
  endif
endfunction

"
" AppendNewLine()
"   Insert newline after cursor.
"
function! s:AppendNewLine()
  execute "normal! a/<CR>/<ESC>"
endfunction

"
" InsertNewLine()
"   Insert newline before cursor.
"
function! s:InsertNewLine()
  execute "normal! i/<CR>/<ESC>"
endfunction

"
" MoveToWordEnd()
"   Move to the word end.
"
function! s:MoveToWordEnd()
  if line(".") == 1
    normal! wge
  else
    normal! gee
  endif
endfunction

"
" MoveToWordBegin()
"   Move to the word begin.
"
function! s:MoveToWordBegin()
  if line(".") == 1
    normal! wb
  else
    normal! gew
  endif
endfunction

"
" MoveToFirstWordEnd()
"   Move to the first word end after the comment leader.
"
function! s:MoveToFirstWordEnd(leader_width)
  let i = a:leader_width + 1
  execute "normal! " . i . "|"
  call s:MoveToWordEnd()
endfunction

"
" TabooRuleMatch(taboo_rule_list, char)
"   Return true when the character matches one of taboo_rule_list
"
function! s:TabooRuleMatch(taboo_rule_list, char)
  " add spaces to char so to match exactly one of the list
  if strlen(a:char) > 1
    let ch = a:char . " "
  else
    let ch = a:char . "  "
  endif

  " escape the special character
  return a:taboo_rule_list =~ escape(ch, '^.*/$~[]')
endfunction

call DoMappings()

" vi:set ts=8 sts=2 sw=2 tw=0:

你可能感兴趣的:(function,list,command,vim,character,encoding)