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