|
From: Dasn C. <Da...@my...> - 2003-08-25 22:11:41
|
I happened to find this plugin file which can help us format multibyte text=
. I hope it can be useful.
"=09=09=09=09=09=09=09=09 Version 1.5
" format.vim -=09Format multibyte text, for tha languages, which can split
"=09=09line anywhere, unless prohibited. (for Vim 6.0 alpha)
"
" Last Change:=09 12-Jan-2002.
" Maintainer:=09 Muraoka Taro <ko...@tk...>
" Practised By:=09 Takuhiro Nishioka <tak...@su...>
" Base Idea:=09 Muraoka Taro <ko...@tk...>
" Copyright:=09 Public Domain
" Modified:=09 Edward G.J. Lee <ed...@sp...> 2002.02.11
" =D7=D4=D0=D0=B8=FC=B8=C4=CE=AA cp950 =D2=D4=CA=CA=BA=CF=B7=B1=CC=E5=D6=D0=
=CE=C4
scriptencoding cp950
" function Format(start_line_number, end_line_number)
"=20
" 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
"--------------------------------------------------------------------------=
-
"=09=09=09=09 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 yo=
u
" still want to use "Q", set this to "Q"
"
"let format_command =3D "Q"
if !exists('format_command')
let format_command =3D "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=09line end with AND next line start with a multibyte char
" 2=09line end with OR next line start with a multibyte char
" 3=09same with original join command
"
if !exists("g:format_join_spaces")
let g:format_join_spaces =3D 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 =3D 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 =3D 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 =3D 0
endif
"--------------------------------------------------------------------------=
-
"=09=09=09=09 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()
let g:format_no_begin =3D "! , . ? ) ] } - _ ~ : ; =A3=A1 =A3=
=AC =A1=A3 =A3=BF =A3=A9 =A3=BA =A3=BB "
"
" "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 =3D "( [ { "
"
" For Japanese. There are encoding name aliases, so we cannot directly
" compare option 'encoding' with locale name (ex: 'japan').
"
"let s:save_encoding =3D &encoding
"let &encoding =3D 'japan'
"if &encoding =3D=3D s:save_encoding
" let no_b =3D ''
" let no_b =3D no_b . "?? ?? ?f ?h ?? ?? ?? ?? ?A ?B ?X ?r ?t ?v ?x ?z ?l =
"
" let no_b =3D no_b . "?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?J ?K ?T ?U "
" let no_b =3D no_b . "?@ ?B ?D ?F ?H ?b ?? ?? ?? ?? ?? ?? "
" let no_b =3D no_b . "?E ?[ ?R ?S ?I ?? ?j ?C ?D ?F ?G ?H ?n ?p ?c ?` "
" let no_e =3D ''
" let no_e =3D no_e . "?? ?e ?g ?q ?s ?u ?w ?y ?k ?? ?i ?m ?o ?? "
" let g:format_no_begin =3D g:format_no_begin . no_b
" let g:format_no_end =3D g:format_no_end . no_e
" unlet no_b no_e
"endif
"let &encoding =3D 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 =3D "apawaWasa]a[a)a(aba>a}a{aB"
let m_list =3D m_list . "ipiwiWisi]i[i)i(ibi>i}i{iB"
let m_list =3D m_list . "gqq "
let m_list =3D m_list . "h j k l 0 ^ $ ; , - + w W e E b B "
let m_list =3D m_list . "( ) { } n N % H M L G / ? "
let m_list =3D m_list . "gjgkggg0g^gmgegEg$]][[][[[[]''``"
let m_list =3D m_list . "'[`[']`]'<`<'>`>'\"`\""
let m_list =3D m_list . "[([{])]}]m]M[m[M[#]#[*[/"
let m_list =3D 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 =3D 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 =3D m_list . "2j3j4j5j6j7j8j9j"
let m_list =3D m_list . "2k3k4k5k6k7k8k9k"
let m_list =3D m_list . "2H3H4H5H6H7H8H9H"
let m_list =3D m_list . "2L3L4L5L6L7L8L9L"
let s:format_motion_list =3D m_list
unlet m_list
"--------------------------------------------------------------------------=
-
"
" DoMappings()
" Do mappings.
"
function! DoMappings()
" Normal mode mappings.
let length =3D strlen(s:format_motion_list)
let i =3D 0
while i <=3D length - 2
let motion =3D strpart(s:format_motion_list, i, 2)
let motion =3D substitute(motion, " $", "", "")
execute "nmap <silent> " . g:format_command . motion . " :call " . "<SI=
D>FormatWorkhorse(\"" . motion . "\")<CR>"
let i =3D i + 2
endwhile
" Visual mode mapping.
execute "vmap <silent> ".g:format_command." <ESC>:call"." <SID>Format(lin=
e(\"'<\"), 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 numbe=
r
" of the area to Format()
"
function! s:FormatWorkhorse(motion)
if a:motion =3D=3D "gq" || a:motion =3D=3D "q"
execute "normal! V\<ESC>"
elseif a:motion =3D=3D "/" || a:motion =3D=3D "?"
execute "let pattern =3D 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 =3D a:end_lnum - a:start_lnum + 1
let advance =3D 1
" current line is the start of a paragraph.
let first_par_line =3D 1
" the second indent
let second_indent =3D "default"
" Check 2 in the formatoptions
let do_second_indent =3D s:HasFormatOptions('2')
let showcmd_save =3D &showcmd
set noshowcmd
let wrap_save =3D &wrap
set nowrap
let lazyredraw_save =3D &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 =3D=3D 1
" current line is not part of paragraph
let is_not_par =3D 1
else
normal! k
" the commet leader of current line
let leader =3D s:GetLeader()
let is_not_par =3D s:FmtCheckPar(leader)
normal! j
endif
" the commet leader of next line
let next_leader =3D s:GetLeader()
" next line not part of paragraph
let next_is_not_par =3D s:FmtCheckPar(next_leader)
" at end of paragraph
let is_end_par =3D is_not_par || next_is_not_par
" operation top
let op_top =3D 1
while count_nr > 0
" Advance to next paragraph.
if advance
if op_top
=09let op_top =3D 0
else
=09normal! j
endif
let leader =3D next_leader
let is_not_par =3D next_is_not_par
" previous line is end of paragraph
let prev_is_end_par =3D is_end_par
endif
" The last line to be formatted.
if count_nr =3D=3D 1
let next_leader =3D ""
let next_is_not_par =3D 1
else
normal! j
let next_leader =3D s:GetLeader()
let next_is_not_par =3D s:FmtCheckPar(next_leader)
normal! k
endif
let advance =3D 1
let is_end_par =3D 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
=09 \&& do_second_indent
=09 \&& prev_is_end_par
=09 \&& leader =3D~ "^\\s*$"
=09 \&& next_leader =3D~ "^\\s*$"
=09 \&& getline(line(".") + 1) !~ "^$"
=09let second_indent =3D next_leader
endif
" When the comment leader changes, it's the end of the paragraph
if !s:SameLeader(leader, next_leader)
=09let is_end_par =3D 1
endif
" If we have got to the end of a paragraph, format it.
if is_end_par
=09" do the formatting
=09call s:FormatLine(second_indent)
=09let second_indent =3D "default"
=09let first_par_line =3D 1
endif
" When still in same paragraph, join the lines together.
if !is_end_par
=09let advance =3D 0
=09" join current line and next line without the comment leader
=09call s:DoJoin(next_leader)
=09let first_par_line =3D 0
endif
endif
let count_nr =3D 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 =3D=3D 0
let textwidth =3D 76
else
let textwidth =3D &textwidth
endif
let do_second_indent =3D s:HasFormatOptions("2")
let fo_do_comments =3D s:HasFormatOptions("q")
let second_indent =3D a:second_indent
" save the original option's value
let formatoptions_save =3D &formatoptions
let iskeyword_save =3D &iskeyword
let leader_width =3D s:GetLeader("get_leader_width")
" When fo_do_comments is TRUE, set formatoptions value so that the commen=
t
" leader is set for next line.
if fo_do_comments
set formatoptions+=3Dr
else
set formatoptions-=3Dr
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=3D"!-~"
call s:SetCursor(line("."), textwidth)
while s:GetWidth() > virtcol(".")
let finish_format =3D 0
let force_fold =3D 0
let do_insert =3D 0
let max_width =3D virtcol(".") + g:format_allow_over_tw
let ch =3D s:GetCharUnderCursor()
normal! l
let next_ch =3D s:GetCharUnderCursor()
normal! h
" English word folding
if ch =3D~ "[!-~]\\{1}" && next_ch =3D~ "[!-~]\\{1}"
call s:MoveToWordBegin()
if virtcol(".") - 1 > leader_width
=09" move to previous word end
=09normal! ge
endif
endif
" Skip white spaces
if ch =3D~ "\\s"
while ch =3D~ "\\s" && virtcol(".") - 1 > leader_width
=09normal! h
=09let ch =3D s:GetCharUnderCursor()
endwhile
let force_fold =3D 1
endif
if virtcol(".") - 1 <=3D leader_width
call s:MoveToFirstWordEnd(leader_width)
let force_fold =3D 1
if s:GetWidth() =3D=3D virtcol(".")
=09let finish_format =3D 1
endif
endif
" Taboo rule
if !finish_format && !force_fold && g:format_follow_taboo_rule
normal! l
let next_ch =3D s:GetCharUnderCursor()
normal! h
if s:TabooRuleMatch(g:format_no_begin, next_ch)
=09normal! l
=09while s:TabooRuleMatch(g:format_no_begin, next_ch)
=09 " if cursor is at the line end, break.
=09 if s:GetWidth() =3D=3D virtcol(".")
=09 let finish_format =3D 1
=09 break
=09 endif
=09 normal! l
=09 let next_ch =3D s:GetCharUnderCursor()
=09endwhile
=09if !finish_format
=09 normal! h
=09endif
endif
let ch =3D s:GetCharUnderCursor()
if virtcol(".") > max_width
=09let finish_format =3D 0
=09while s:TabooRuleMatch(g:format_no_begin, ch)
=09 \&& virtcol(".") - 1 > leader_width
=09 normal! h
=09 let ch =3D s:GetCharUnderCursor()
=09endwhile
=09if ch =3D~ "[!-~]\\{1}"
=09 call s:MoveToWordBegin()
=09 if virtcol(".") - 1 > leader_width
=09 normal! ge
=09 else
=09 call s:MoveToFirstWordEnd(leader_width)
=09 let force_fold =3D 1
=09 endif
=09else
=09 let do_insert =3D 1
=09endif
endif
let ch =3D s:GetCharUnderCursor()
if s:TabooRuleMatch(g:format_no_end, ch) && !force_fold
=09let do_insert =3D 0
=09while s:TabooRuleMatch(g:format_no_end, ch)
=09 \&& virtcol(".") -1 > leader_width
=09 normal! h
=09 let ch =3D s:GetCharUnderCursor()
=09endwhile
=09if virtcol(".") -1 <=3D leader_width
=09 call s:MoveToFirstWordEnd(leader_width)
=09endif
endif
endif
if finish_format
break
endif
if do_insert
call s:InsertNewLine()
else
call s:AppendNewLine()
endif
if do_second_indent && second_indent !=3D "default"
call setline(line(".")
=09 \, second_indent . substitute(getline("."), "^\\s*", "", ""))
let do_second_indent =3D 0
if strlen(second_indent) > 0
=09normal! h
endif
endif
if virtcol(".") =3D=3D 1
let leader_width =3D 0
else
let leader_width =3D virtcol(".")
endif
call s:SetCursor(line("."), textwidth)
endwhile
execute "set formatoptions=3D" . formatoptions_save
execute "set iskeyword=3D" . 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 =3D=3D 1
return 0
endif
return ""
endif
let col_save =3D virtcol(".")
let formatoptions_save =3D &formatoptions
let autoindent_save =3D &autoindent
let cindent_save =3D &cindent
let smartindent_save =3D &smartindent
set formatoptions+=3Do
set autoindent
set nocindent
set nosmartindent
execute "normal! ox\<ESC>\"_x"
if a:0 =3D=3D 1
if getline(".") =3D~ "^$"
let leader_width =3D 0
else
let leader_width =3D virtcol(".")
endif
endif
let leader =3D getline(".")
if line(".") =3D=3D line("$")
normal! "_dd
else
normal! "_ddk
endif
execute "set formatoptions=3D" . 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 =3D=3D 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 =3D substitute(&com, '.*s[^:]*:\([^,]*\),.*', '\1', '')
let three_end =3D substitute(&com, '.*e[^:]*:\([^,]*\),.*', '\1', '')
let line =3D substitute(getline("."), "\\s*$", "", "")
let line =3D substitute(line, "^\\s*", "", "")
let leader =3D substitute(a:leader, "\\s*$", "", "")
let leader =3D substitute(leader, "^\\s*", "", "")
if line =3D=3D three_start || line =3D=3D three_end
return 1
endif
return line =3D=3D 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 =3D=3D a:leader2
else
return substitute(a:leader1, "\\s\\+$", "", "")
=09\=3D=3D 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 =3D=3D 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 =3D=3D "2" && !&autoindent)
\|| (a:x =3D=3D "2" && g:format_indent_sensitive)
return 0
endif
return &formatoptions =3D~ a:x
endfunction
"
" DoRangeJoin(next_leader)
" DoJoin driver, able to support range.
"
function! s:DoRangeJoin(next_leader) range
if count > 2
let repeat =3D count - 1
else
let repeat =3D 1
endif
while repeat
call s:DoJoin(a:next_leader)
let repeat =3D 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(".") =3D=3D line("$")
return
endif
let showcmd_save =3D &showcmd
set noshowcmd
let wrap_save =3D &wrap
set nowrap
let lazyredraw_save =3D &lazyredraw
set lazyredraw
normal! $
let end_char =3D s:GetCharUnderCursor()
if s:HasFormatOptions("q") && a:next_leader !=3D ""
let next_leader =3D escape(a:next_leader, '^.*\$~[]')
let next_leader =3D "^" . substitute(next_leader, "\\s*$", "", "")
normal! j0
if getline(".") =3D~ next_leader
call setline(line("."), substitute(getline("."), next_leader, "", "")=
)
else
let leader_width =3D s:GetLeader("get_leader_width")
let i =3D leader_width + 1
execute "normal! 0\"_d" . i . "|"
endif
normal! k
endif
normal! J
if s:GetWidth() > virtcol(".") && s:GetCharUnderCursor() =3D=3D " "
normal! l
let begin_char =3D s:GetCharUnderCursor()
normal! h
if g:format_join_spaces =3D=3D 1
=09\&& (strlen(end_char) > 1 && strlen(begin_char) > 1)
normal! "_x
elseif g:format_join_spaces =3D=3D 2
=09\&& (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 =3D a:end_lnum - a:start_lnum
call s:SetCursor(a:start_lnum)
while count_nr > 0
call s:DoJoin("")
let count_nr =3D count_nr - 1
endwhile
endfunction
"
" GetWidth()
" Return the current line width. If the line is empty returns 0. Note tha=
t
" 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 =3D getline(".")
let idx =3D col(".") - 1
let ch =3D str[idx]
if char2nr(ch) >=3D 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(".") =3D=3D 1
normal! wge
else
normal! gee
endif
endfunction
"
" MoveToWordBegin()
" Move to the word begin.
"
function! s:MoveToWordBegin()
if line(".") =3D=3D 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 =3D 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 =3D a:char . " "
else
let ch =3D a:char . " "
endif
" escape the special character
return a:taboo_rule_list =3D~ escape(ch, '^.*\$~[]')
endfunction
call DoMappings()
" vi:set ts=3D8 sts=3D2 sw=3D2 tw=3D0:
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
/*
* Don't frown.
* Don't complain.
* Don't touch yourself.
*/
|