vim72-20100325-kaoriya-w64j.zip
[MacVim/KaoriYa.git] / runtime / plugin / format.vim
blob51f391713ef8a25cc6c2db65f35160063b8c2f5a
1 " vim:set ts=8 sts=2 sw=2 tw=0 et:
3 " format.vim -  Format multibyte text, for the languages, which can split
4 "               line anywhere, unless prohibited. (for Vim 7.0)
6 " Version:        1.7rc2
7 " Last Change:    03-Mar-2007.
8 " Maintainer:     MURAOKA Taro <koron@tka.att.ne.jp>
9 " Practised By:   Takuhiro Nishioka <takuhiro@super.win.ne.jp>
10 " Base Idea:      MURAOKA Taro <koron@tka.att.ne.jp>
11 " Copyright:      Public Domain
13 scriptencoding cp932
15 " function Format(start_line_number, end_line_number)
16
17 " Format() will allow format multibyte text.  In some of East Asian
18 " languages, the line can break anywhere, unless prohibited.  Original Vim's
19 " "gq" format command doesn't allow to break line at the midst of word.
20 " This function split line at each multibyte character.  And it can handle
21 " prohibited line break rules.
23 " This function is following Vim's "gq" command. But there will be lack of
24 " something.
26 if exists('plugin_format_disable')
27   finish
28 endif
30 "---------------------------------------------------------------------------
31 "                                   Options
34 " "format_follow_taboo_rule"
36 " Move to a point that will not break forbidden line break rules. If you
37 " don't want to do this, set this to "0".
39 if !exists("g:format_follow_taboo_rule")
40   let g:format_follow_taboo_rule = 1
41 endif
44 " "format_allow_over_tw"
46 " The width that can over 'textwidth'. This variable is used for taboo rule.
48 if !exists("g:format_allow_over_tw")
49   let g:format_allow_over_tw = 2
50 endif
53 " "format_indent_sensitive"
55 " When the indentation changes, it's the end of a paragraph. Note that if
56 " this option is set, second indentation is disabled.
58 if !exists("g:format_indent_sensitive")
59   let g:format_indent_sensitive = 0
60 endif
62 "---------------------------------------------------------------------------
63 "                                 Sub Options
65 " "g:format_no_begin"
67 " This option is space-separated list of characters, that are forbidden to
68 " be at beginning of line. Add two spaces for ASCII characters. See also
69 " IsTaboo()
71 let g:format_no_begin = "!,.?)]}-_~:;"
74 " "g:format_no_end"
76 " This option is space-separated list of characters, that are forbidden to
77 " be at end of line. Add two spaces for ASCII characters. See also
78 " IsTaboo()
80 let g:format_no_end = "([{"
83 " For Japanese.
85 if &encoding =~ '\v\c(cp932|euc-jp|utf-8)'
86   let no_b = ''
87   let no_b = no_b . "�‘�‹�f�h�ñ�Œ���Ž�A�B�X�r�t�v�x�z�l"
88   let no_b = no_b . "‚Ÿ‚¡‚£‚¥‚§‚Á‚á‚ã‚å‚ì�J�K�T�U"
89   let no_b = no_b . "ƒ@ƒBƒDƒFƒHƒbƒƒƒ…ƒ‡ƒŽƒ•ƒ–"
90   let no_b = no_b . "�E�[�R�S�I�“�j�C�D�F�G�H�n�p�c�`"
91   let g:format_no_begin = g:format_no_begin . no_b
92   unlet! no_b
94   let no_e = ''
95   let no_e = no_e . "�’�e�g�q�s�u�w�y�k���i�m�o��"
96   let g:format_no_end = g:format_no_end . no_e
97   unlet! no_e
98 endif
100 "---------------------------------------------------------------------------
103 " Format(start_lnum, end_lnum)
104 "   Format the area from the start line number to the end line number.
106 function! s:Format(start_lnum, end_lnum)
107   let count_nr = a:end_lnum - a:start_lnum + 1
108   let advance = 1
109   " current line is the start of a paragraph.
110   let first_par_line = 1
111   " the second indent
112   let second_indent = "default"
114   " Check 2 in the formatoptions
115   let do_second_indent = s:HasFormatOptions('2')
117   let showcmd_save = &showcmd
118   set noshowcmd
119   let wrap_save = &wrap
120   set nowrap
121   let lazyredraw_save = &lazyredraw
122   set lazyredraw
123   let save_iminsert = &iminsert
124   set iminsert=0
126   " Set cursor to the start line number.
127   call s:SetCursor(a:start_lnum)
129   " Get info about the previous and current line.
130   if a:start_lnum == 1
131     " current line is not part of paragraph
132     let is_not_par = 1
133   else
134     normal! k
135     " the commet leader of current line
136     let leader = s:GetLeader()
137     let is_not_par = s:FmtCheckPar(leader)
138     normal! j
139   endif
141   " the commet leader of next line
142   let next_leader = s:GetLeader()
143   " next line not part of paragraph
144   let next_is_not_par = s:FmtCheckPar(next_leader)
146   " at end of paragraph
147   let is_end_par = is_not_par || next_is_not_par
149   " operation top
150   let op_top = 1
151   while count_nr > 0
152     " Advance to next paragraph.
153     if advance
154       if op_top
155         let op_top = 0
156       else
157         normal! j
158       endif
159       let leader = next_leader
160       let is_not_par = next_is_not_par
161       " previous line is end of paragraph
162       let prev_is_end_par = is_end_par
163     endif
165     " The last line to be formatted.
166     if count_nr == 1
167       let next_leader = ""
168       let next_is_not_par = 1
169     else
170       normal! j
171       let next_leader = s:GetLeader()
172       let next_is_not_par = s:FmtCheckPar(next_leader)
173       normal! k
174     endif
176     let advance = 1
177     let is_end_par = is_not_par || next_is_not_par
179     " Skip lines that are not in a paragraph.
180     if !is_not_par
182       " For the first line of a paragraph, check indent of second line.
183       " Don't do this for comments and empty lines.
184       if first_par_line && do_second_indent && prev_is_end_par && leader =~ "^\\s*$" && next_leader =~ "^\\s*$" && getline(line(".") + 1) !~ "^$"
185         let second_indent = next_leader
186       endif
188       " When the comment leader changes, it's the end of the paragraph
189       if !s:SameLeader(leader, next_leader)
190         let is_end_par = 1
191       endif
193       " If we have got to the end of a paragraph, format it.
194       if is_end_par
195         " do the formatting
196         call s:FormatLine(second_indent)
197         let second_indent = "default"
198         let first_par_line = 1
199       endif
201       " When still in same paragraph, join the lines together.
202       if !is_end_par
203         let advance = 0
204         " join current line and next line without the comment leader
205         call s:DoJoin(next_leader)
206         let first_par_line = 0
207       endif
209     endif
210     let count_nr = count_nr - 1
211   endwhile
212   if wrap_save
213     set wrap
214   endif
215   if !lazyredraw_save
216     set nolazyredraw
217   endif
218   if showcmd_save
219     set showcmd
220   endif
221   let &iminsert = save_iminsert
222 endfunction
225 " FormatLine(second_indent)
226 "   Format currentline.
228 function! s:FormatLine(second_indent)
229   " check textwidth
230   if &textwidth == 0
231     let textwidth = 76
232   else
233     let textwidth = &textwidth
234   endif
236   let do_second_indent = s:HasFormatOptions("2")
237   let fo_do_comments = s:HasFormatOptions("q")
238   let second_indent = a:second_indent
240   " save the original option's value
241   let formatoptions_save = &formatoptions
242   let iskeyword_save = &iskeyword
244   let leader_width = s:GetLeader("get_leader_width")
246   " When fo_do_comments is TRUE, set formatoptions value so that the comment
247   " leader is set for next line.
248   if fo_do_comments
249     set formatoptions+=r
250   else
251     set formatoptions-=r
252   endif
254   " Set iskeyword option value to every printable ascii characters, so that
255   " "w" can stop at only multibyte-ascii boundary or white space.
256   set iskeyword="!-~"
258   call s:SetCursor(line("."), textwidth)
259   let no_begin = s:GetOption('format_no_begin')
260   let no_end = s:GetOption('format_no_end')
261   while s:GetWidth() > virtcol(".")
262     let finish_format = 0
263     let force_fold = 0
264     let do_insert = 0
265     let max_width = textwidth + s:GetOption('format_allow_over_tw')
267     let ch = s:GetCharUnderCursor()
269     " English word folding
270     if ch =~ "[!-~]\\{1}" && s:GetCharNextCursor() =~ "[!-~]\\{1}"
271       call s:MoveToWordBegin()
272       if virtcol(".") - 1 > leader_width
273         " move to previous word end
274         normal! ge
275       endif
276     endif
278     " Skip white spaces
279     if ch =~ "\\s"
280       while ch =~ "\\s" && virtcol(".") - 1 > leader_width
281         normal! h
282         let ch = s:GetCharUnderCursor()
283       endwhile
284       let force_fold = 1
285     endif
287     if virtcol(".") - 1 <= leader_width
288       call s:MoveToFirstWordEnd(leader_width)
289       let force_fold = 1
290       if s:GetWidth() == virtcol(".")
291         let finish_format = 1
292       endif
293     endif
295     " Taboo rule
296     if !finish_format && !force_fold && g:format_follow_taboo_rule
297       let next_ch = s:GetCharNextCursor()
298       if s:IsTaboo(next_ch, no_begin)
299         normal! l
300         while s:IsTaboo(next_ch, no_begin)
301           " if cursor is at the line end, break.
302           if s:GetWidth() == virtcol(".")
303             let finish_format = 1
304             break
305           endif
306           normal! l
307           let next_ch = s:GetCharUnderCursor()
308         endwhile
309         if !finish_format
310           normal! h
311         endif
312       endif
314       let ch = s:GetCharUnderCursor()
315       if virtcol(".") > max_width
316         let finish_format = 0
317         while s:IsTaboo(ch, no_begin) && virtcol(".") - 1 > leader_width
318           normal! h
319           let ch = s:GetCharUnderCursor()
320         endwhile
321         if ch =~ "[!-~]\\{1}"
322           call s:MoveToWordBegin()
323           if virtcol(".") - 1 > leader_width
324             normal! ge
325           else
326             call s:MoveToFirstWordEnd(leader_width)
327             let force_fold = 1
328           endif
329         else
330           let do_insert = 1
331         endif
332       endif
334       let ch = s:GetCharUnderCursor()
335       if s:IsTaboo(ch, no_end) && !force_fold
336         let do_insert = 0
337         let @b = no_end
338         while s:IsTaboo(ch, no_end) && virtcol(".") -1 > leader_width
339           normal! h
340           let ch = s:GetCharUnderCursor()
341         endwhile
342         if virtcol(".") -1 <= leader_width
343           call s:MoveToFirstWordEnd(leader_width)
344         endif
345       endif
346     endif
348     if finish_format
349       break
350     endif
352     if do_insert
353       call s:InsertNewLine()
354     else
355       call s:AppendNewLine()
356     endif
358     if do_second_indent && second_indent != "default"
359       call setline(line("."), second_indent . substitute(getline("."), "^\\s*", "", ""))
360       let do_second_indent = 0
361       if strlen(second_indent) > 0
362         normal! h
363       endif
364     endif
366     if virtcol(".") == 1
367       let leader_width = 0
368     else
369       let leader_width = virtcol(".")
370     endif
372     call s:SetCursor(line("."), textwidth)
373   endwhile
375   execute "set formatoptions=" . formatoptions_save
376   execute "set iskeyword=" . iskeyword_save
377 endfunction
380 " GetLeader(...)
381 "   Get the comment leader string from current line. If argument
382 "   is specified, then return the comment leader width. Note that
383 "   returned comment leader and the current line's comment leader is
384 "   not always same.
386 function! s:GetLeader(...)
387   if !s:HasFormatOptions('q')
388     if a:0 == 1
389       return 0
390     endif
391     return ""
392   endif
394   let col_save = virtcol(".")
396   let formatoptions_save = &formatoptions
397   let autoindent_save = &autoindent
398   let cindent_save = &cindent
399   let smartindent_save = &smartindent
400   set formatoptions+=o
401   set autoindent
402   set nocindent
403   set nosmartindent
405   execute "normal! ox\<ESC>\"_x"
407   if a:0 == 1
408     if getline(".") =~ "^$"
409       let leader_width = 0
410     else
411       let leader_width = virtcol(".")
412     endif
413   endif
415   let leader = getline(".")
417   if line(".") == line("$")
418     normal! "_dd
419   else
420     normal! "_ddk
421   endif
423   execute "set formatoptions=" . formatoptions_save
424   if !autoindent_save
425     set noautoindent
426   endif
427   if cindent_save
428     set cindent
429   endif
430   if smartindent_save
431     set smartindent
432   endif
434   execute "normal! " . col_save . "|"
436   if a:0 == 1
437     return leader_width
438   else
439     return leader
440   endif
441 endfunction
444 " FmtCheckPar(leader)
445 "   Blank lines, lines containing only white space or the comment leader,
446 "   are left untouched by the formatting. The function returns true in this
447 "   case.
449 function! s:FmtCheckPar(leader)
450   let three_start = substitute(&com, '.*s[^:]*:\([^,]*\),.*', '\1', '')
451   let three_end = substitute(&com, '.*e[^:]*:\([^,]*\),.*', '\1', '')
452   let line = substitute(getline("."), "\\s*$", "", "")
453   let line = substitute(line, "^\\s*", "", "")
454   let leader = substitute(a:leader, "\\s*$", "", "")
455   let leader = substitute(leader, "^\\s*", "", "")
456   if line == three_start || line == three_end
457     return 1
458   endif
459   return line == leader
460 endfunction
463 " SameLeader(leader1, leader2)
464 "   Return true if the two comment leaders given are the same. White-space is
465 "   ignored.
467 function! s:SameLeader(leader1, leader2)
468   if g:format_indent_sensitive
469     return a:leader1 == a:leader2
470   else
471     return substitute(a:leader1, "\\s\\+$", "", "") == substitute(a:leader2, "\\s\\+$", "", "")
472   endif
473 endfunction
476 " SetCursor(lnum, width)
477 "   Set cursor to the line number, then move the cursor to within the width
478 "   and the most right virtual column.
480 function! s:SetCursor(lnum, ...)
481   call cursor(a:lnum, 0)
482   if a:0 >= 1
483     execute "normal! " . a:1 . "|"
484     if a:1 > 2 && virtcol(".") > a:1
485       normal! h
486     endif
487   endif
488 endfunction
491 " HasFormatOptions(x)
492 "   Return true if format option 'x' is in effect. Take care of no
493 "   formatting when 'paste' is set.
495 function! s:HasFormatOptions(x)
496   if &paste || (a:x == "2" && !&autoindent) || (a:x == "2" && g:format_indent_sensitive)
497     return 0
498   endif
499   return &formatoptions =~ a:x
500 endfunction
503 " DoRangeJoin(next_leader)
504 "   DoJoin driver, able to support range.
506 function! s:DoRangeJoin(next_leader) range
507   if  count > 2
508     let repeat = count - 1
509   else
510     let repeat = 1
511   endif
512   while repeat
513     call s:DoJoin(a:next_leader)
514     let repeat = repeat - 1
515   endwhile
516 endfunction
519 " DoJoin(next_leader)
520 "   Join line and next line. The comment leader will be removed.
522 function! s:DoJoin(next_leader)
523   if line(".") == line("$")
524     return
525   endif
526   let showcmd_save = &showcmd
527   set noshowcmd
528   let wrap_save = &wrap
529   set nowrap
530   let lazyredraw_save = &lazyredraw
531   set lazyredraw
533   normal! $
534   let end_char = s:GetCharUnderCursor()
536   if s:HasFormatOptions("q") && a:next_leader != ""
537     let next_leader = escape(a:next_leader, '^.*\$~[]')
538     let next_leader = "^" . substitute(next_leader, "\\s*$", "", "")
539     normal! j0
540     if getline(".") =~ next_leader
541       call setline(line("."), substitute(getline("."), next_leader, "", ""))
542     else
543       let leader_width = s:GetLeader("get_leader_width")
544       let i = leader_width + 1
545       execute "normal! 0\"_d" . i . "|"
546     endif
547     normal! k
548   endif
550   normal! J
552   if wrap_save
553     set wrap
554   endif
555   if !lazyredraw_save
556     set nolazyredraw
557   endif
558   if showcmd_save
559     set showcmd
560   endif
561 endfunction
564 " DoJoinRange(start_lnum, end_lnum)
565 "   Join lines from start_lnum to end_lnum, according to the
566 "   "$fomrat_join_spaces"
568 function! s:DoJoinRange(start_lnum, end_lnum)
569   let count_nr = a:end_lnum - a:start_lnum
570   call s:SetCursor(a:start_lnum)
571   while count_nr > 0
572     call s:DoJoin("")
573     let count_nr = count_nr - 1
574   endwhile
575 endfunction
578 " GetWidth()
579 "   Return the current line width. If the line is empty returns 0. Note that
580 "   if the character at the line end is a multibyte character, this returns
581 "   real width minus 1, same as virtcol().
583 function! s:GetWidth()
584   return virtcol("$") - 1
585 endfunction
588 " GetCharUnderCursor()
589 "   Get (multibyte) character under current cursor.
591 function! s:GetCharUnderCursor()
592   return matchstr(getline("."), ".", col(".") - 1)
593 endfunction
595 " Get a character at column next cursor.
596 function! s:GetCharNextCursor()
597   return matchstr(getline("."), '.\@<=.', col(".") - 1)
598 endfunction
600 function! s:GetCharPrevCursor()
601   let pat = '.\%' . col('.') . 'c'
602   return matchstr(getline('.'), pat)
603 endfunction
606 " AppendNewLine()
607 "   Insert newline after cursor.
609 function! s:AppendNewLine()
610   execute "normal! a\<CR>\<ESC>"
611 endfunction
614 " InsertNewLine()
615 "   Insert newline before cursor.
617 function! s:InsertNewLine()
618   execute "normal! i\<CR>\<ESC>"
619 endfunction
622 " MoveToWordEnd()
623 "   Move to the word end.
625 function! s:MoveToWordEnd()
626   if line(".") == 1
627     normal! wge
628   else
629     normal! gee
630   endif
631 endfunction
634 " MoveToWordBegin()
635 "   Move to the word begin.
637 function! s:MoveToWordBegin()
638   if line(".") == 1
639     normal! wb
640   else
641     normal! gew
642   endif
643 endfunction
646 " MoveToFirstWordEnd()
647 "   Move to the first word end after the comment leader.
649 function! s:MoveToFirstWordEnd(leader_width)
650   let i = a:leader_width + 1
651   execute "normal! " . i . "|"
652   call s:MoveToWordEnd()
653 endfunction
656 " IsTaboo(char, taboo_rule_list)
657 "   Return true when the character matches one of taboo_rule_list
659 function! s:IsTaboo(char, taboo_rule_list)
660   return match(a:taboo_rule_list, '\V'.escape(a:char, '\')) >= 0
661 endfunction
663 " Get option value.  Priority is window-local > buffer-local > global.
664 function! s:GetOption(name)
665   let var = getwinvar(winnr(), a:name)
666   if var == ''
667     let var = getbufvar(bufnr('%'), a:name)
668     if var == ''
669       let var = g:{a:name}
670     endif
671   endif
672   return var
673 endfunction
675 function! s:Feedkeys(string, mode, count)
676   let i = 0
677   while i < a:count
678     let i += 1
679     call feedkeys(a:string, a:mode)
680   endwhile
681 endfunction
683 function! s:GetLinebreakOffset(curr_line, curr_col)
684   " ‹Ö‘¥ƒ‹�[ƒ‹‚ÉŠî‚¢‚Ä�AƒJ�[ƒ\ƒ‹ˆÊ’u‚Ì’²�®‚ð�s‚¤
685   " ‚Ԃ牺‚ª‚è‚Í�l—¶‚µ‚È‚¢
686   let back_count = 0
687   let no_begin = s:GetOption('format_no_begin')
688   let no_end = s:GetOption('format_no_end')
689   let curr_char = matchstr(a:curr_line, '\%'.a:curr_col.'c.')
690   let back_col = 0
691   while 1
692     let prev_char = matchstr(a:curr_line, '.\%'.(a:curr_col - back_col).'c')
693     if curr_char == ''
694       let back_count = 0
695       break
696     elseif s:IsTaboo(curr_char, no_begin) || s:IsTaboo(prev_char, no_end)
697       let back_count += 1
698       let curr_char = prev_char
699       let back_col += strlen(curr_char)
700     else
701       break
702     endif
703   endwhile
704   return back_count
705 endfunction
707 function! Format_Japanese()
708   if mode() == 'n'
709     call s:Format(v:lnum, v:lnum + v:count - 1)
710     return 0
711   elseif s:HasFormatOptions('a')
712     " Too difficult to implement.
713     return 1
714   else
715     let curcol = col('.')
716     " v:char‚ð“ü—Í‚µ‚½Œã‚Å&textwidth‚ð’´‚¦‚é�ê�‡‚ɉü�sˆÊ’u‚Ì•â�³‚ð�s‚¤
717     let new_line = getline('.') . v:char
718     if curcol + strlen(v:char) > &textwidth
719       let back_count = s:GetLinebreakOffset(new_line, curcol)
720       " ƒJ�[ƒ\ƒ‹ˆÚ“®‚Ɖü�s‚Ì‘}“ü‚ð�s‚¤
721       if back_count > 0
722         " –{ŠÖ�”�I—¹Œã‚É“ü—Í‚³‚ê‚év:char•ª‚à�l—¶‚É“ü‚ê‚é
723         let l:count = back_count + 1
724         call s:Feedkeys("\<LEFT>", 'n', l:count)
725         call feedkeys("\<CR>", 'n')
726         call s:Feedkeys("\<RIGHT>", 'n', l:count)
727         return 0
728       else
729         return 1
730       endif
731     else
732       return 1
733     endif
734   endif
735 endfunction
737 set formatexpr=Format_Japanese()