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)
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
15 " function Format(start_line_number, end_line_number)
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
26 if exists('plugin_format_disable')
30 "---------------------------------------------------------------------------
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
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
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
62 "---------------------------------------------------------------------------
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
71 let g:format_no_begin = "!,.?)]}-_~:;"
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
80 let g:format_no_end = "([{"
85 if &encoding =~ '\v\c(cp932|euc-jp|utf-8)'
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
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
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
109 " current line is the start of a paragraph.
110 let first_par_line = 1
112 let second_indent = "default"
114 " Check 2 in the formatoptions
115 let do_second_indent = s:HasFormatOptions('2')
117 let showcmd_save = &showcmd
119 let wrap_save = &wrap
121 let lazyredraw_save = &lazyredraw
123 let save_iminsert = &iminsert
126 " Set cursor to the start line number.
127 call s:SetCursor(a:start_lnum)
129 " Get info about the previous and current line.
131 " current line is not part of paragraph
135 " the commet leader of current line
136 let leader = s:GetLeader()
137 let is_not_par = s:FmtCheckPar(leader)
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
152 " Advance to next paragraph.
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
165 " The last line to be formatted.
168 let next_is_not_par = 1
171 let next_leader = s:GetLeader()
172 let next_is_not_par = s:FmtCheckPar(next_leader)
177 let is_end_par = is_not_par || next_is_not_par
179 " Skip lines that are not in a paragraph.
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
188 " When the comment leader changes, it's the end of the paragraph
189 if !s:SameLeader(leader, next_leader)
193 " If we have got to the end of a paragraph, format it.
196 call s:FormatLine(second_indent)
197 let second_indent = "default"
198 let first_par_line = 1
201 " When still in same paragraph, join the lines together.
204 " join current line and next line without the comment leader
205 call s:DoJoin(next_leader)
206 let first_par_line = 0
210 let count_nr = count_nr - 1
221 let &iminsert = save_iminsert
225 " FormatLine(second_indent)
226 " Format currentline.
228 function! s:FormatLine(second_indent)
233 let textwidth = &textwidth
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.
254 " Set iskeyword option value to every printable ascii characters, so that
255 " "w" can stop at only multibyte-ascii boundary or white space.
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
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
280 while ch =~ "\\s" && virtcol(".") - 1 > leader_width
282 let ch = s:GetCharUnderCursor()
287 if virtcol(".") - 1 <= leader_width
288 call s:MoveToFirstWordEnd(leader_width)
290 if s:GetWidth() == virtcol(".")
291 let finish_format = 1
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)
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
307 let next_ch = s:GetCharUnderCursor()
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
319 let ch = s:GetCharUnderCursor()
321 if ch =~ "[!-~]\\{1}"
322 call s:MoveToWordBegin()
323 if virtcol(".") - 1 > leader_width
326 call s:MoveToFirstWordEnd(leader_width)
334 let ch = s:GetCharUnderCursor()
335 if s:IsTaboo(ch, no_end) && !force_fold
338 while s:IsTaboo(ch, no_end) && virtcol(".") -1 > leader_width
340 let ch = s:GetCharUnderCursor()
342 if virtcol(".") -1 <= leader_width
343 call s:MoveToFirstWordEnd(leader_width)
353 call s:InsertNewLine()
355 call s:AppendNewLine()
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
369 let leader_width = virtcol(".")
372 call s:SetCursor(line("."), textwidth)
375 execute "set formatoptions=" . formatoptions_save
376 execute "set iskeyword=" . iskeyword_save
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
386 function! s:GetLeader(...)
387 if !s:HasFormatOptions('q')
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
405 execute "normal! ox\<ESC>\"_x"
408 if getline(".") =~ "^$"
411 let leader_width = virtcol(".")
415 let leader = getline(".")
417 if line(".") == line("$")
423 execute "set formatoptions=" . formatoptions_save
434 execute "normal! " . col_save . "|"
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
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
459 return line == leader
463 " SameLeader(leader1, leader2)
464 " Return true if the two comment leaders given are the same. White-space is
467 function! s:SameLeader(leader1, leader2)
468 if g:format_indent_sensitive
469 return a:leader1 == a:leader2
471 return substitute(a:leader1, "\\s\\+$", "", "") == substitute(a:leader2, "\\s\\+$", "", "")
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)
483 execute "normal! " . a:1 . "|"
484 if a:1 > 2 && virtcol(".") > a:1
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)
499 return &formatoptions =~ a:x
503 " DoRangeJoin(next_leader)
504 " DoJoin driver, able to support range.
506 function! s:DoRangeJoin(next_leader) range
508 let repeat = count - 1
513 call s:DoJoin(a:next_leader)
514 let repeat = repeat - 1
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("$")
526 let showcmd_save = &showcmd
528 let wrap_save = &wrap
530 let lazyredraw_save = &lazyredraw
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*$", "", "")
540 if getline(".") =~ next_leader
541 call setline(line("."), substitute(getline("."), next_leader, "", ""))
543 let leader_width = s:GetLeader("get_leader_width")
544 let i = leader_width + 1
545 execute "normal! 0\"_d" . i . "|"
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)
573 let count_nr = count_nr - 1
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
588 " GetCharUnderCursor()
589 " Get (multibyte) character under current cursor.
591 function! s:GetCharUnderCursor()
592 return matchstr(getline("."), ".", col(".") - 1)
595 " Get a character at column next cursor.
596 function! s:GetCharNextCursor()
597 return matchstr(getline("."), '.\@<=.', col(".") - 1)
600 function! s:GetCharPrevCursor()
601 let pat = '.\%' . col('.') . 'c'
602 return matchstr(getline('.'), pat)
607 " Insert newline after cursor.
609 function! s:AppendNewLine()
610 execute "normal! a\<CR>\<ESC>"
615 " Insert newline before cursor.
617 function! s:InsertNewLine()
618 execute "normal! i\<CR>\<ESC>"
623 " Move to the word end.
625 function! s:MoveToWordEnd()
635 " Move to the word begin.
637 function! s:MoveToWordBegin()
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()
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
663 " Get option value. Priority is window-local > buffer-local > global.
664 function! s:GetOption(name)
665 let var = getwinvar(winnr(), a:name)
667 let var = getbufvar(bufnr('%'), a:name)
675 function! s:Feedkeys(string, mode, count)
679 call feedkeys(a:string, a:mode)
683 function! s:GetLinebreakOffset(curr_line, curr_col)
684 " ‹Ö‘¥ƒ‹�[ƒ‹‚ÉŠî‚¢‚Ä�AƒJ�[ƒ\ƒ‹ˆÊ’u‚Ì’²�®‚ð�s‚¤
685 " ‚Ԃ牺‚ª‚è‚Í�l—¶‚µ‚È‚¢
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.')
692 let prev_char = matchstr(a:curr_line, '.\%'.(a:curr_col - back_col).'c')
696 elseif s:IsTaboo(curr_char, no_begin) || s:IsTaboo(prev_char, no_end)
698 let curr_char = prev_char
699 let back_col += strlen(curr_char)
707 function! Format_Japanese()
709 call s:Format(v:lnum, v:lnum + v:count - 1)
711 elseif s:HasFormatOptions('a')
712 " Too difficult to implement.
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‚¤
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)
737 set formatexpr=Format_Japanese()