1" Function to left and rigt align text. 2" 3" Written by: Preben "Peppe" Guldberg <c928400@student.dtu.dk> 4" Created: 980806 14:13 (or around that time anyway) 5" Revised: 001103 00:36 (See "Revisions" below) 6 7 8" function Justify( [ textwidth [, maxspaces [, indent] ] ] ) 9" 10" Justify() will left and right align a line by filling in an 11" appropriate amount of spaces. Extra spaces are added to existing 12" spaces starting from the right side of the line. As an example, the 13" following documentation has been justified. 14" 15" The function takes the following arguments: 16 17" textwidth argument 18" ------------------ 19" If not specified, the value of the 'textwidth' option is used. If 20" 'textwidth' is zero a value of 80 is used. 21" 22" Additionally the arguments 'tw' and '' are accepted. The value of 23" 'textwidth' will be used. These are handy, if you just want to specify 24" the maxspaces argument. 25 26" maxspaces argument 27" ------------------ 28" If specified, alignment will only be done, if the longest space run 29" after alignment is no longer than maxspaces. 30" 31" An argument of '' is accepted, should the user like to specify all 32" arguments. 33" 34" To aid user defined commands, negative values are accepted aswell. 35" Using a negative value specifies the default behaviour: any length of 36" space runs will be used to justify the text. 37 38" indent argument 39" --------------- 40" This argument specifies how a line should be indented. The default is 41" to keep the current indentation. 42" 43" Negative values: Keep current amount of leading whitespace. 44" Positive values: Indent all lines with leading whitespace using this 45" amount of whitespace. 46" 47" Note that the value 0, needs to be quoted as a string. This value 48" leads to a left flushed text. 49" 50" Additionally units of 'shiftwidth'/'sw' and 'tabstop'/'ts' may be 51" added. In this case, if the value of indent is positive, the amount of 52" whitespace to be added will be multiplied by the value of the 53" 'shiftwidth' and 'tabstop' settings. If these units are used, the 54" argument must be given as a string, eg. Justify('','','2sw'). 55" 56" If the values of 'sw' or 'tw' are negative, they are treated as if 57" they were 0, which means that the text is flushed left. There is no 58" check if a negative number prefix is used to change the sign of a 59" negative 'sw' or 'ts' value. 60" 61" As with the other arguments, '' may be used to get the default 62" behaviour. 63 64 65" Notes: 66" 67" If the line, adjusted for space runs and leading/trailing whitespace, 68" is wider than the used textwidth, the line will be left untouched (no 69" whitespace removed). This should be equivalent to the behaviour of 70" :left, :right and :center. 71" 72" If the resulting line is shorter than the used textwidth it is left 73" untouched. 74" 75" All space runs in the line are truncated before the alignment is 76" carried out. 77" 78" If you have set 'noexpandtab', :retab! is used to replace space runs 79" with whitespace using the value of 'tabstop'. This should be 80" conformant with :left, :right and :center. 81" 82" If joinspaces is set, an extra space is added after '.', '?' and '!'. 83" If 'cpooptions' include 'j', extra space is only added after '.'. 84" (This may on occasion conflict with maxspaces.) 85 86 87" Related mappings: 88" 89" Mappings that will align text using the current text width, using at 90" most four spaces in a space run and keeping current indentation. 91nmap _j :%call Justify('tw',4)<CR> 92vmap _j :call Justify('tw',4)<CR> 93" 94" Mappings that will remove space runs and format lines (might be useful 95" prior to aligning the text). 96nmap ,gq :%s/\s\+/ /g<CR>gq1G 97vmap ,gq :s/\s\+/ /g<CR>gvgq 98 99 100" User defined command: 101" 102" The following is an ex command that works as a shortcut to the Justify 103" function. Arguments to Justify() can be added after the command. 104com! -range -nargs=* Justify <line1>,<line2>call Justify(<f-args>) 105" 106" The following commands are all equivalent: 107" 108" 1. Simplest use of Justify(): 109" :call Justify() 110" :Justify 111" 112" 2. The _j mapping above via the ex command: 113" :%Justify tw 4 114" 115" 3. Justify visualised text at 72nd column while indenting all 116" previously indented text two shiftwidths 117" :'<,'>call Justify(72,'','2sw') 118" :'<,'>Justify 72 -1 2sw 119" 120" This documentation has been justified using the following command: 121":se et|kz|1;/^" function Justify(/+,'z-g/^" /s/^" //|call Justify(70,3)|s/^/" / 122 123" Revisions: 124" 001103: If 'joinspaces' was set, calculations could be wrong. 125" Tabs at start of line could also lead to errors. 126" Use setline() instead of "exec 's/foo/bar/' - safer. 127" Cleaned up the code a bit. 128" 129" Todo: Convert maps to the new script specific form 130 131" Error function 132function! Justify_error(message) 133 echohl Error 134 echo "Justify([tw, [maxspaces [, indent]]]): " . a:message 135 echohl None 136endfunction 137 138 139" Now for the real thing 140function! Justify(...) range 141 142 if a:0 > 3 143 call Justify_error("Too many arguments (max 3)") 144 return 1 145 endif 146 147 " Set textwidth (accept 'tw' and '' as arguments) 148 if a:0 >= 1 149 if a:1 =~ '^\(tw\)\=$' 150 let tw = &tw 151 elseif a:1 =~ '^\d\+$' 152 let tw = a:1 153 else 154 call Justify_error("tw must be a number (>0), '' or 'tw'") 155 return 2 156 endif 157 else 158 let tw = &tw 159 endif 160 if tw == 0 161 let tw = 80 162 endif 163 164 " Set maximum number of spaces between WORDs 165 if a:0 >= 2 166 if a:2 == '' 167 let maxspaces = tw 168 elseif a:2 =~ '^-\d\+$' 169 let maxspaces = tw 170 elseif a:2 =~ '^\d\+$' 171 let maxspaces = a:2 172 else 173 call Justify_error("maxspaces must be a number or ''") 174 return 3 175 endif 176 else 177 let maxspaces = tw 178 endif 179 if maxspaces <= 1 180 call Justify_error("maxspaces should be larger than 1") 181 return 4 182 endif 183 184 " Set the indentation style (accept sw and ts units) 185 let indent_fix = '' 186 if a:0 >= 3 187 if (a:3 == '') || a:3 =~ '^-[1-9]\d*\(shiftwidth\|sw\|tabstop\|ts\)\=$' 188 let indent = -1 189 elseif a:3 =~ '^-\=0\(shiftwidth\|sw\|tabstop\|ts\)\=$' 190 let indent = 0 191 elseif a:3 =~ '^\d\+\(shiftwidth\|sw\|tabstop\|ts\)\=$' 192 let indent = substitute(a:3, '\D', '', 'g') 193 elseif a:3 =~ '^\(shiftwidth\|sw\|tabstop\|ts\)$' 194 let indent = 1 195 else 196 call Justify_error("indent: a number with 'sw'/'ts' unit") 197 return 5 198 endif 199 if indent >= 0 200 while indent > 0 201 let indent_fix = indent_fix . ' ' 202 let indent = indent - 1 203 endwhile 204 let indent_sw = 0 205 if a:3 =~ '\(shiftwidth\|sw\)' 206 let indent_sw = &sw 207 elseif a:3 =~ '\(tabstop\|ts\)' 208 let indent_sw = &ts 209 endif 210 let indent_fix2 = '' 211 while indent_sw > 0 212 let indent_fix2 = indent_fix2 . indent_fix 213 let indent_sw = indent_sw - 1 214 endwhile 215 let indent_fix = indent_fix2 216 endif 217 else 218 let indent = -1 219 endif 220 221 " Avoid substitution reports 222 let save_report = &report 223 set report=1000000 224 225 " Check 'joinspaces' and 'cpo' 226 if &js == 1 227 if &cpo =~ 'j' 228 let join_str = '\(\. \)' 229 else 230 let join_str = '\([.!?!] \)' 231 endif 232 endif 233 234 let cur = a:firstline 235 while cur <= a:lastline 236 237 let str_orig = getline(cur) 238 let save_et = &et 239 set et 240 exec cur . "retab" 241 let &et = save_et 242 let str = getline(cur) 243 244 let indent_str = indent_fix 245 let indent_n = strlen(indent_str) 246 " Shall we remember the current indentation 247 if indent < 0 248 let indent_orig = matchstr(str_orig, '^\s*') 249 if strlen(indent_orig) > 0 250 let indent_str = indent_orig 251 let indent_n = strlen(matchstr(str, '^\s*')) 252 endif 253 endif 254 255 " Trim trailing, leading and running whitespace 256 let str = substitute(str, '\s\+$', '', '') 257 let str = substitute(str, '^\s\+', '', '') 258 let str = substitute(str, '\s\+', ' ', 'g') 259 " Use substitute() hack to get strlen in characters instead of bytes 260 let str_n = strlen(substitute(str, '.', 'x', 'g')) 261 262 " Possible addition of space after punctuation 263 if exists("join_str") 264 let str = substitute(str, join_str, '\1 ', 'g') 265 endif 266 let join_n = strlen(substitute(str, '.', 'x', 'g')) - str_n 267 268 " Can extraspaces be added? 269 " Note that str_n may be less than strlen(str) [joinspaces above] 270 if strlen(substitute(str, '.', 'x', 'g')) < tw - indent_n && str_n > 0 271 " How many spaces should be added 272 let s_add = tw - str_n - indent_n - join_n 273 let s_nr = strlen(substitute(str, '\S', '', 'g') ) - join_n 274 let s_dup = s_add / s_nr 275 let s_mod = s_add % s_nr 276 277 " Test if the changed line fits with tw 278 if 0 <= (str_n + (maxspaces - 1)*s_nr + indent_n) - tw 279 280 " Duplicate spaces 281 while s_dup > 0 282 let str = substitute(str, '\( \+\)', ' \1', 'g') 283 let s_dup = s_dup - 1 284 endwhile 285 286 " Add extra spaces from the end 287 while s_mod > 0 288 let str = substitute(str, '\(\(\s\+\S\+\)\{' . s_mod . '}\)$', ' \1', '') 289 let s_mod = s_mod - 1 290 endwhile 291 292 " Indent the line 293 if indent_n > 0 294 let str = substitute(str, '^', indent_str, '' ) 295 endif 296 297 " Replace the line 298 call setline(cur, str) 299 300 " Convert to whitespace 301 if &et == 0 302 exec cur . 'retab!' 303 endif 304 305 endif " Change of line 306 endif " Possible change 307 308 let cur = cur + 1 309 endwhile 310 311 norm ^ 312 313 let &report = save_report 314 315endfunction 316 317" EOF vim: tw=78 ts=8 sw=4 sts=4 noet ai 318