1" Vim syntax support file 2" Maintainer: Ben Fritz <[email protected]> 3" Last Change: 2013 Jul 08 4" 5" Additional contributors: 6" 7" Original by Bram Moolenaar <[email protected]> 8" Modified by David Ne\v{c}as (Yeti) <[email protected]> 9" XHTML support by Panagiotis Issaris <[email protected]> 10" Made w3 compliant by Edd Barrett <[email protected]> 11" Added html_font. Edd Barrett <[email protected]> 12" Progress bar based off code from "progressbar widget" plugin by 13" Andreas Politz, heavily modified: 14" http://www.vim.org/scripts/script.php?script_id=2006 15" 16" See Mercurial change logs for more! 17 18" Transform a file into HTML, using the current syntax highlighting. 19 20" this file uses line continuations 21let s:cpo_sav = &cpo 22let s:ls = &ls 23set cpo&vim 24 25let s:end=line('$') 26 27" Font 28if exists("g:html_font") 29 let s:htmlfont = "'". g:html_font . "', monospace" 30else 31 let s:htmlfont = "monospace" 32endif 33 34let s:settings = tohtml#GetUserSettings() 35 36if !exists('s:FOLDED_ID') 37 let s:FOLDED_ID = hlID("Folded") | lockvar s:FOLDED_ID 38 let s:FOLD_C_ID = hlID("FoldColumn") | lockvar s:FOLD_C_ID 39 let s:LINENR_ID = hlID('LineNr') | lockvar s:LINENR_ID 40 let s:DIFF_D_ID = hlID("DiffDelete") | lockvar s:DIFF_D_ID 41 let s:DIFF_A_ID = hlID("DiffAdd") | lockvar s:DIFF_A_ID 42 let s:DIFF_C_ID = hlID("DiffChange") | lockvar s:DIFF_C_ID 43 let s:DIFF_T_ID = hlID("DiffText") | lockvar s:DIFF_T_ID 44 let s:CONCEAL_ID = hlID('Conceal') | lockvar s:CONCEAL_ID 45endif 46 47" Whitespace 48if s:settings.pre_wrap 49 let s:whitespace = "white-space: pre-wrap; " 50else 51 let s:whitespace = "" 52endif 53 54if !empty(s:settings.prevent_copy) 55 if s:settings.no_invalid 56 " User has decided they don't want invalid markup. Still works in 57 " OpenOffice, and for text editors, but when pasting into Microsoft Word the 58 " input elements get pasted too and they cannot be deleted (at least not 59 " easily). 60 let s:unselInputType = "" 61 else 62 " Prevent from copy-pasting the input elements into Microsoft Word where 63 " they cannot be deleted easily by deliberately inserting invalid markup. 64 let s:unselInputType = " type='invalid_input_type'" 65 endif 66endif 67 68" When not in gui we can only guess the colors. 69" TODO - is this true anymore? 70if has("gui_running") 71 let s:whatterm = "gui" 72else 73 let s:whatterm = "cterm" 74 if &t_Co == 8 75 let s:cterm_color = { 76 \ 0: "#808080", 1: "#ff6060", 2: "#00ff00", 3: "#ffff00", 77 \ 4: "#8080ff", 5: "#ff40ff", 6: "#00ffff", 7: "#ffffff" 78 \ } 79 else 80 let s:cterm_color = { 81 \ 0: "#000000", 1: "#c00000", 2: "#008000", 3: "#804000", 82 \ 4: "#0000c0", 5: "#c000c0", 6: "#008080", 7: "#c0c0c0", 83 \ 8: "#808080", 9: "#ff6060", 10: "#00ff00", 11: "#ffff00", 84 \ 12: "#8080ff", 13: "#ff40ff", 14: "#00ffff", 15: "#ffffff" 85 \ } 86 87 " Colors for 88 and 256 come from xterm. 88 if &t_Co == 88 89 call extend(s:cterm_color, { 90 \ 16: "#000000", 17: "#00008b", 18: "#0000cd", 19: "#0000ff", 91 \ 20: "#008b00", 21: "#008b8b", 22: "#008bcd", 23: "#008bff", 92 \ 24: "#00cd00", 25: "#00cd8b", 26: "#00cdcd", 27: "#00cdff", 93 \ 28: "#00ff00", 29: "#00ff8b", 30: "#00ffcd", 31: "#00ffff", 94 \ 32: "#8b0000", 33: "#8b008b", 34: "#8b00cd", 35: "#8b00ff", 95 \ 36: "#8b8b00", 37: "#8b8b8b", 38: "#8b8bcd", 39: "#8b8bff", 96 \ 40: "#8bcd00", 41: "#8bcd8b", 42: "#8bcdcd", 43: "#8bcdff", 97 \ 44: "#8bff00", 45: "#8bff8b", 46: "#8bffcd", 47: "#8bffff", 98 \ 48: "#cd0000", 49: "#cd008b", 50: "#cd00cd", 51: "#cd00ff", 99 \ 52: "#cd8b00", 53: "#cd8b8b", 54: "#cd8bcd", 55: "#cd8bff", 100 \ 56: "#cdcd00", 57: "#cdcd8b", 58: "#cdcdcd", 59: "#cdcdff", 101 \ 60: "#cdff00", 61: "#cdff8b", 62: "#cdffcd", 63: "#cdffff", 102 \ 64: "#ff0000" 103 \ }) 104 call extend(s:cterm_color, { 105 \ 65: "#ff008b", 66: "#ff00cd", 67: "#ff00ff", 68: "#ff8b00", 106 \ 69: "#ff8b8b", 70: "#ff8bcd", 71: "#ff8bff", 72: "#ffcd00", 107 \ 73: "#ffcd8b", 74: "#ffcdcd", 75: "#ffcdff", 76: "#ffff00", 108 \ 77: "#ffff8b", 78: "#ffffcd", 79: "#ffffff", 80: "#2e2e2e", 109 \ 81: "#5c5c5c", 82: "#737373", 83: "#8b8b8b", 84: "#a2a2a2", 110 \ 85: "#b9b9b9", 86: "#d0d0d0", 87: "#e7e7e7" 111 \ }) 112 elseif &t_Co == 256 113 call extend(s:cterm_color, { 114 \ 16: "#000000", 17: "#00005f", 18: "#000087", 19: "#0000af", 115 \ 20: "#0000d7", 21: "#0000ff", 22: "#005f00", 23: "#005f5f", 116 \ 24: "#005f87", 25: "#005faf", 26: "#005fd7", 27: "#005fff", 117 \ 28: "#008700", 29: "#00875f", 30: "#008787", 31: "#0087af", 118 \ 32: "#0087d7", 33: "#0087ff", 34: "#00af00", 35: "#00af5f", 119 \ 36: "#00af87", 37: "#00afaf", 38: "#00afd7", 39: "#00afff", 120 \ 40: "#00d700", 41: "#00d75f", 42: "#00d787", 43: "#00d7af", 121 \ 44: "#00d7d7", 45: "#00d7ff", 46: "#00ff00", 47: "#00ff5f", 122 \ 48: "#00ff87", 49: "#00ffaf", 50: "#00ffd7", 51: "#00ffff", 123 \ 52: "#5f0000", 53: "#5f005f", 54: "#5f0087", 55: "#5f00af", 124 \ 56: "#5f00d7", 57: "#5f00ff", 58: "#5f5f00", 59: "#5f5f5f", 125 \ 60: "#5f5f87", 61: "#5f5faf", 62: "#5f5fd7", 63: "#5f5fff", 126 \ 64: "#5f8700" 127 \ }) 128 call extend(s:cterm_color, { 129 \ 65: "#5f875f", 66: "#5f8787", 67: "#5f87af", 68: "#5f87d7", 130 \ 69: "#5f87ff", 70: "#5faf00", 71: "#5faf5f", 72: "#5faf87", 131 \ 73: "#5fafaf", 74: "#5fafd7", 75: "#5fafff", 76: "#5fd700", 132 \ 77: "#5fd75f", 78: "#5fd787", 79: "#5fd7af", 80: "#5fd7d7", 133 \ 81: "#5fd7ff", 82: "#5fff00", 83: "#5fff5f", 84: "#5fff87", 134 \ 85: "#5fffaf", 86: "#5fffd7", 87: "#5fffff", 88: "#870000", 135 \ 89: "#87005f", 90: "#870087", 91: "#8700af", 92: "#8700d7", 136 \ 93: "#8700ff", 94: "#875f00", 95: "#875f5f", 96: "#875f87", 137 \ 97: "#875faf", 98: "#875fd7", 99: "#875fff", 100: "#878700", 138 \ 101: "#87875f", 102: "#878787", 103: "#8787af", 104: "#8787d7", 139 \ 105: "#8787ff", 106: "#87af00", 107: "#87af5f", 108: "#87af87", 140 \ 109: "#87afaf", 110: "#87afd7", 111: "#87afff", 112: "#87d700" 141 \ }) 142 call extend(s:cterm_color, { 143 \ 113: "#87d75f", 114: "#87d787", 115: "#87d7af", 116: "#87d7d7", 144 \ 117: "#87d7ff", 118: "#87ff00", 119: "#87ff5f", 120: "#87ff87", 145 \ 121: "#87ffaf", 122: "#87ffd7", 123: "#87ffff", 124: "#af0000", 146 \ 125: "#af005f", 126: "#af0087", 127: "#af00af", 128: "#af00d7", 147 \ 129: "#af00ff", 130: "#af5f00", 131: "#af5f5f", 132: "#af5f87", 148 \ 133: "#af5faf", 134: "#af5fd7", 135: "#af5fff", 136: "#af8700", 149 \ 137: "#af875f", 138: "#af8787", 139: "#af87af", 140: "#af87d7", 150 \ 141: "#af87ff", 142: "#afaf00", 143: "#afaf5f", 144: "#afaf87", 151 \ 145: "#afafaf", 146: "#afafd7", 147: "#afafff", 148: "#afd700", 152 \ 149: "#afd75f", 150: "#afd787", 151: "#afd7af", 152: "#afd7d7", 153 \ 153: "#afd7ff", 154: "#afff00", 155: "#afff5f", 156: "#afff87", 154 \ 157: "#afffaf", 158: "#afffd7" 155 \ }) 156 call extend(s:cterm_color, { 157 \ 159: "#afffff", 160: "#d70000", 161: "#d7005f", 162: "#d70087", 158 \ 163: "#d700af", 164: "#d700d7", 165: "#d700ff", 166: "#d75f00", 159 \ 167: "#d75f5f", 168: "#d75f87", 169: "#d75faf", 170: "#d75fd7", 160 \ 171: "#d75fff", 172: "#d78700", 173: "#d7875f", 174: "#d78787", 161 \ 175: "#d787af", 176: "#d787d7", 177: "#d787ff", 178: "#d7af00", 162 \ 179: "#d7af5f", 180: "#d7af87", 181: "#d7afaf", 182: "#d7afd7", 163 \ 183: "#d7afff", 184: "#d7d700", 185: "#d7d75f", 186: "#d7d787", 164 \ 187: "#d7d7af", 188: "#d7d7d7", 189: "#d7d7ff", 190: "#d7ff00", 165 \ 191: "#d7ff5f", 192: "#d7ff87", 193: "#d7ffaf", 194: "#d7ffd7", 166 \ 195: "#d7ffff", 196: "#ff0000", 197: "#ff005f", 198: "#ff0087", 167 \ 199: "#ff00af", 200: "#ff00d7", 201: "#ff00ff", 202: "#ff5f00", 168 \ 203: "#ff5f5f", 204: "#ff5f87" 169 \ }) 170 call extend(s:cterm_color, { 171 \ 205: "#ff5faf", 206: "#ff5fd7", 207: "#ff5fff", 208: "#ff8700", 172 \ 209: "#ff875f", 210: "#ff8787", 211: "#ff87af", 212: "#ff87d7", 173 \ 213: "#ff87ff", 214: "#ffaf00", 215: "#ffaf5f", 216: "#ffaf87", 174 \ 217: "#ffafaf", 218: "#ffafd7", 219: "#ffafff", 220: "#ffd700", 175 \ 221: "#ffd75f", 222: "#ffd787", 223: "#ffd7af", 224: "#ffd7d7", 176 \ 225: "#ffd7ff", 226: "#ffff00", 227: "#ffff5f", 228: "#ffff87", 177 \ 229: "#ffffaf", 230: "#ffffd7", 231: "#ffffff", 232: "#080808", 178 \ 233: "#121212", 234: "#1c1c1c", 235: "#262626", 236: "#303030", 179 \ 237: "#3a3a3a", 238: "#444444", 239: "#4e4e4e", 240: "#585858", 180 \ 241: "#626262", 242: "#6c6c6c", 243: "#767676", 244: "#808080", 181 \ 245: "#8a8a8a", 246: "#949494", 247: "#9e9e9e", 248: "#a8a8a8", 182 \ 249: "#b2b2b2", 250: "#bcbcbc", 251: "#c6c6c6", 252: "#d0d0d0", 183 \ 253: "#dadada", 254: "#e4e4e4", 255: "#eeeeee" 184 \ }) 185 endif 186 endif 187endif 188 189" Return good color specification: in GUI no transformation is done, in 190" terminal return RGB values of known colors and empty string for unknown 191if s:whatterm == "gui" 192 function! s:HtmlColor(color) 193 return a:color 194 endfun 195else 196 function! s:HtmlColor(color) 197 if has_key(s:cterm_color, a:color) 198 return s:cterm_color[a:color] 199 else 200 return "" 201 endif 202 endfun 203endif 204 205" Find out the background and foreground color for use later 206let s:fgc = s:HtmlColor(synIDattr(hlID("Normal"), "fg#", s:whatterm)) 207let s:bgc = s:HtmlColor(synIDattr(hlID("Normal"), "bg#", s:whatterm)) 208if s:fgc == "" 209 let s:fgc = ( &background == "dark" ? "#ffffff" : "#000000" ) 210endif 211if s:bgc == "" 212 let s:bgc = ( &background == "dark" ? "#000000" : "#ffffff" ) 213endif 214 215if !s:settings.use_css 216 " Return opening HTML tag for given highlight id 217 function! s:HtmlOpening(id, extra_attrs) 218 let a = "" 219 if synIDattr(a:id, "inverse") 220 " For inverse, we always must set both colors (and exchange them) 221 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm)) 222 let a = a . '<span '.a:extra_attrs.'style="background-color: ' . ( x != "" ? x : s:fgc ) . '">' 223 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm)) 224 let a = a . '<font color="' . ( x != "" ? x : s:bgc ) . '">' 225 else 226 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm)) 227 if x != "" 228 let a = a . '<span '.a:extra_attrs.'style="background-color: ' . x . '">' 229 elseif !empty(a:extra_attrs) 230 let a = a . '<span '.a:extra_attrs.'>' 231 endif 232 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm)) 233 if x != "" | let a = a . '<font color="' . x . '">' | endif 234 endif 235 if synIDattr(a:id, "bold") | let a = a . "<b>" | endif 236 if synIDattr(a:id, "italic") | let a = a . "<i>" | endif 237 if synIDattr(a:id, "underline") | let a = a . "<u>" | endif 238 return a 239 endfun 240 241 " Return closing HTML tag for given highlight id 242 function! s:HtmlClosing(id, has_extra_attrs) 243 let a = "" 244 if synIDattr(a:id, "underline") | let a = a . "</u>" | endif 245 if synIDattr(a:id, "italic") | let a = a . "</i>" | endif 246 if synIDattr(a:id, "bold") | let a = a . "</b>" | endif 247 if synIDattr(a:id, "inverse") 248 let a = a . '</font></span>' 249 else 250 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm)) 251 if x != "" | let a = a . '</font>' | endif 252 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm)) 253 if x != "" || a:has_extra_attrs | let a = a . '</span>' | endif 254 endif 255 return a 256 endfun 257endif 258 259" Use a different function for formatting based on user options. This way we 260" can avoid a lot of logic during the actual execution. 261" 262" Build the function line by line containing only what is needed for the options 263" in use for maximum code sharing with minimal branch logic for greater speed. 264" 265" Note, 'exec' commands do not recognize line continuations, so must concatenate 266" lines rather than continue them. 267if s:settings.use_css 268 " save CSS to a list of rules to add to the output at the end of processing 269 270 " first, get the style names we need 271 let wrapperfunc_lines = [ 272 \ 'function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, make_unselectable, unformatted)', 273 \ '', 274 \ ' let l:style_name = synIDattr(a:style_id, "name", s:whatterm)' 275 \ ] 276 if &diff 277 let wrapperfunc_lines += [ 278 \ ' let l:diff_style_name = synIDattr(a:diff_style_id, "name", s:whatterm)'] 279 280 " Add normal groups and diff groups to separate lists so we can order them to 281 " allow diff highlight to override normal highlight 282 283 " if primary style IS a diff style, grab it from the diff cache instead 284 " (always succeeds because we pre-populate it) 285 let wrapperfunc_lines += [ 286 \ '', 287 \ ' if a:style_id == s:DIFF_D_ID || a:style_id == s:DIFF_A_ID ||'. 288 \ ' a:style_id == s:DIFF_C_ID || a:style_id == s:DIFF_T_ID', 289 \ ' let l:saved_style = get(s:diffstylelist,a:style_id)', 290 \ ' else' 291 \ ] 292 endif 293 294 " get primary style info from cache or build it on the fly if not found 295 let wrapperfunc_lines += [ 296 \ ' let l:saved_style = get(s:stylelist,a:style_id)', 297 \ ' if type(l:saved_style) == type(0)', 298 \ ' unlet l:saved_style', 299 \ ' let l:saved_style = s:CSS1(a:style_id)', 300 \ ' if l:saved_style != ""', 301 \ ' let l:saved_style = "." . l:style_name . " { " . l:saved_style . "}"', 302 \ ' endif', 303 \ ' let s:stylelist[a:style_id]= l:saved_style', 304 \ ' endif' 305 \ ] 306 if &diff 307 let wrapperfunc_lines += [ ' endif' ] 308 endif 309 310 " Build the wrapper tags around the text. It turns out that caching these 311 " gives pretty much zero performance gain and adds a lot of logic. 312 313 let wrapperfunc_lines += [ 314 \ '', 315 \ ' if l:saved_style == "" && empty(a:extra_attrs)' 316 \ ] 317 if &diff 318 let wrapperfunc_lines += [ 319 \ ' if a:diff_style_id <= 0' 320 \ ] 321 endif 322 " no surroundings if neither primary nor diff style has any info 323 let wrapperfunc_lines += [ 324 \ ' return a:text' 325 \ ] 326 if &diff 327 " no primary style, but diff style 328 let wrapperfunc_lines += [ 329 \ ' else', 330 \ ' return "<span class=\"" .l:diff_style_name . "\">".a:text."</span>"', 331 \ ' endif' 332 \ ] 333 endif 334 " open tag for non-empty primary style 335 let wrapperfunc_lines += [ 336 \ ' else'] 337 " non-empty primary style. handle either empty or non-empty diff style. 338 " 339 " separate the two classes by a space to apply them both if there is a diff 340 " style name, unless the primary style is empty, then just use the diff style 341 " name 342 let diffstyle = 343 \ (&diff ? '(a:diff_style_id <= 0 ? "" : " ". l:diff_style_name) .' 344 \ : "") 345 if s:settings.prevent_copy == "" 346 let wrapperfunc_lines += [ 347 \ ' return "<span ".a:extra_attrs."class=\"" . l:style_name .'.diffstyle.'"\">".a:text."</span>"' 348 \ ] 349 else 350 351 " 352 " Wrap the <input> in a <span> to allow fixing the stupid bug in some fonts 353 " which cause browsers to display a 1px gap between lines when these 354 " <input>s have a background color (maybe not really a bug, this isn't 355 " well-defined) 356 " 357 " use strwidth, because we care only about how many character boxes are 358 " needed to size the input, we don't care how many characters (including 359 " separately counted composing chars, from strchars()) or bytes (from 360 " len())the string contains. strdisplaywidth() is not needed because none of 361 " the unselectable groups can contain tab characters (fold column, fold 362 " text, line number). 363 " 364 " Note, if maxlength property needs to be added in the future, it will need 365 " to use strchars(), because HTML specifies that the maxlength parameter 366 " uses the number of unique codepoints for its limit. 367 let wrapperfunc_lines += [ 368 \ ' if a:make_unselectable', 369 \ ' return "<span ".a:extra_attrs."class=\"" . l:style_name .'.diffstyle.'"\">'. 370 \ '<input'.s:unselInputType.' class=\"" . l:style_name .'.diffstyle.'"\"'. 371 \ ' value=\"".substitute(a:unformatted,''\s\+$'',"","")."\"'. 372 \ ' onselect=''this.blur(); return false;'''. 373 \ ' onmousedown=''this.blur(); return false;'''. 374 \ ' onclick=''this.blur(); return false;'''. 375 \ ' readonly=''readonly'''. 376 \ ' size=\"".strwidth(a:unformatted)."\"'. 377 \ (s:settings.use_xhtml ? '/' : '').'></span>"', 378 \ ' else', 379 \ ' return "<span ".a:extra_attrs."class=\"" . l:style_name .'. diffstyle .'"\">".a:text."</span>"' 380 \ ] 381 endif 382 let wrapperfunc_lines += [ 383 \ ' endif', 384 \ 'endfun' 385 \ ] 386else 387 " Non-CSS method just needs the wrapper. 388 " 389 " Functions used to get opening/closing automatically return null strings if 390 " no styles exist. 391 if &diff 392 let wrapperfunc_lines = [ 393 \ 'function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, unusedarg, unusedarg2)', 394 \ ' return s:HtmlOpening(a:style_id, a:extra_attrs).(a:diff_style_id <= 0 ? "" :'. 395 \ 's:HtmlOpening(a:diff_style_id, "")).a:text.'. 396 \ '(a:diff_style_id <= 0 ? "" : s:HtmlClosing(a:diff_style_id, 0)).s:HtmlClosing(a:style_id, !empty(a:extra_attrs))', 397 \ 'endfun' 398 \ ] 399 else 400 let wrapperfunc_lines = [ 401 \ 'function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, unusedarg, unusedarg2)', 402 \ ' return s:HtmlOpening(a:style_id, a:extra_attrs).a:text.s:HtmlClosing(a:style_id, !empty(a:extra_attrs))', 403 \ 'endfun' 404 \ ] 405 endif 406endif 407 408" create the function we built line by line above 409exec join(wrapperfunc_lines, "\n") 410 411let s:diff_mode = &diff 412 413" Return HTML valid characters enclosed in a span of class style_name with 414" unprintable characters expanded and double spaces replaced as necessary. 415" 416" TODO: eliminate unneeded logic like done for BuildStyleWrapper 417function! s:HtmlFormat(text, style_id, diff_style_id, extra_attrs, make_unselectable) 418 " Replace unprintable characters 419 let unformatted = strtrans(a:text) 420 421 let formatted = unformatted 422 423 " Replace the reserved html characters 424 let formatted = substitute(formatted, '&', '\&', 'g') 425 let formatted = substitute(formatted, '<', '\<', 'g') 426 let formatted = substitute(formatted, '>', '\>', 'g') 427 let formatted = substitute(formatted, '"', '\"', 'g') 428 " ' is not valid in HTML but it is in XHTML, so just use the numeric 429 " reference for it instead. Needed because it could appear in quotes 430 " especially if unselectable regions is turned on. 431 let formatted = substitute(formatted, '"', '\'', 'g') 432 433 " Replace a "form feed" character with HTML to do a page break 434 " TODO: need to prevent this in unselectable areas? Probably it should never 435 " BE in an unselectable area... 436 let formatted = substitute(formatted, "\x0c", '<hr class="PAGE-BREAK">', 'g') 437 438 " Replace double spaces, leading spaces, and trailing spaces if needed 439 if ' ' != s:HtmlSpace 440 let formatted = substitute(formatted, ' ', s:HtmlSpace . s:HtmlSpace, 'g') 441 let formatted = substitute(formatted, '^ ', s:HtmlSpace, 'g') 442 let formatted = substitute(formatted, ' \+$', s:HtmlSpace, 'g') 443 endif 444 445 " Enclose in the correct format 446 return s:BuildStyleWrapper(a:style_id, a:diff_style_id, a:extra_attrs, formatted, a:make_unselectable, unformatted) 447endfun 448 449" set up functions to call HtmlFormat in certain ways based on whether the 450" element is supposed to be unselectable or not 451if s:settings.prevent_copy =~# 'n' 452 if s:settings.number_lines 453 if s:settings.line_ids 454 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr) 455 if a:lnr > 0 456 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'.(exists('g:html_diff_win_num') ? 'W'.g:html_diff_win_num : "").'L'.a:lnr.s:settings.id_suffix.'" ', 1) 457 else 458 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) 459 endif 460 endfun 461 else 462 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr) 463 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) 464 endfun 465 endif 466 elseif s:settings.line_ids 467 " if lines are not being numbered the only reason this function gets called 468 " is to put the line IDs on each line; "text" will be emtpy but lnr will 469 " always be non-zero, however we don't want to use the <input> because that 470 " won't work as nice for empty text 471 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr) 472 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'.(exists('g:html_diff_win_num') ? 'W'.g:html_diff_win_num : "").'L'.a:lnr.s:settings.id_suffix.'" ', 0) 473 endfun 474 endif 475else 476 if s:settings.line_ids 477 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr) 478 if a:lnr > 0 479 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'.(exists('g:html_diff_win_num') ? 'W'.g:html_diff_win_num : "").'L'.a:lnr.s:settings.id_suffix.'" ', 0) 480 else 481 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0) 482 endif 483 endfun 484 else 485 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr) 486 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0) 487 endfun 488 endif 489endif 490if s:settings.prevent_copy =~# 'd' 491 function! s:HtmlFormat_d(text, style_id, diff_style_id) 492 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) 493 endfun 494else 495 function! s:HtmlFormat_d(text, style_id, diff_style_id) 496 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0) 497 endfun 498endif 499if s:settings.prevent_copy =~# 'f' 500 " Note the <input> elements for fill spaces will have a single space for 501 " content, to allow active cursor CSS selection to work. 502 " 503 " Wrap the whole thing in a span for the 1px padding workaround for gaps. 504 function! s:FoldColumn_build(char, len, numfill, char2, class, click) 505 let l:input_open = "<input readonly='readonly'".s:unselInputType. 506 \ " onselect='this.blur(); return false;'". 507 \ " onmousedown='this.blur(); ".a:click." return false;'". 508 \ " onclick='return false;' size='". 509 \ string(a:len + (empty(a:char2) ? 0 : 1) + a:numfill) . 510 \ "' " 511 let l:common_attrs = "class='FoldColumn' value='" 512 let l:input_close = (s:settings.use_xhtml ? "' />" : "'>") 513 return "<span class='".a:class."'>". 514 \ l:input_open.l:common_attrs.repeat(a:char, a:len). 515 \ (!empty(a:char2) ? a:char2 : ""). 516 \ l:input_close . "</span>" 517 endfun 518 function! s:FoldColumn_fill() 519 return s:FoldColumn_build('', s:foldcolumn, 0, '', 'FoldColumn', '') 520 endfun 521else 522 " For normal fold columns, simply space-pad to the desired width (note that 523 " the FoldColumn definition includes a whitespace:pre rule) 524 function! s:FoldColumn_build(char, len, numfill, char2, class, click) 525 return "<a href='#' class='".a:class."' onclick='".a:click."'>". 526 \ repeat(a:char, a:len).a:char2.repeat(' ', a:numfill). 527 \ "</a>" 528 endfun 529 function! s:FoldColumn_fill() 530 return s:HtmlFormat(repeat(' ', s:foldcolumn), s:FOLD_C_ID, 0, "", 0) 531 endfun 532endif 533if s:settings.prevent_copy =~# 't' 534 " put an extra empty span at the end for dynamic folds, so the linebreak can 535 " be surrounded. Otherwise do it as normal. 536 " 537 " TODO: isn't there a better way to do this, than placing it here and using a 538 " substitute later? 539 if s:settings.dynamic_folds 540 function! s:HtmlFormat_t(text, style_id, diff_style_id) 541 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) . 542 \ s:HtmlFormat("", a:style_id, 0, "", 0) 543 endfun 544 else 545 function! s:HtmlFormat_t(text, style_id, diff_style_id) 546 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) 547 endfun 548 endif 549else 550 function! s:HtmlFormat_t(text, style_id, diff_style_id) 551 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0) 552 endfun 553endif 554 555" Return CSS style describing given highlight id (can be empty) 556function! s:CSS1(id) 557 let a = "" 558 if synIDattr(a:id, "inverse") 559 " For inverse, we always must set both colors (and exchange them) 560 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm)) 561 let a = a . "color: " . ( x != "" ? x : s:bgc ) . "; " 562 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm)) 563 let a = a . "background-color: " . ( x != "" ? x : s:fgc ) . "; " 564 else 565 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm)) 566 if x != "" | let a = a . "color: " . x . "; " | endif 567 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm)) 568 if x != "" 569 let a = a . "background-color: " . x . "; " 570 " stupid hack because almost every browser seems to have at least one font 571 " which shows 1px gaps between lines which have background 572 let a = a . "padding-bottom: 1px; " 573 elseif (a:id == s:FOLDED_ID || a:id == s:LINENR_ID || a:id == s:FOLD_C_ID) && !empty(s:settings.prevent_copy) 574 " input elements default to a different color than the rest of the page 575 let a = a . "background-color: " . s:bgc . "; " 576 endif 577 endif 578 if synIDattr(a:id, "bold") | let a = a . "font-weight: bold; " | endif 579 if synIDattr(a:id, "italic") | let a = a . "font-style: italic; " | endif 580 if synIDattr(a:id, "underline") | let a = a . "text-decoration: underline; " | endif 581 return a 582endfun 583 584if s:settings.dynamic_folds 585 " compares two folds as stored in our list of folds 586 " A fold is "less" than another if it starts at an earlier line number, 587 " or ends at a later line number, ties broken by fold level 588 function! s:FoldCompare(f1, f2) 589 if a:f1.firstline != a:f2.firstline 590 " put it before if it starts earlier 591 return a:f1.firstline - a:f2.firstline 592 elseif a:f1.lastline != a:f2.lastline 593 " put it before if it ends later 594 return a:f2.lastline - a:f1.lastline 595 else 596 " if folds begin and end on the same lines, put lowest fold level first 597 return a:f1.level - a:f2.level 598 endif 599 endfunction 600 601endif 602 603 604" Set some options to make it work faster. 605" Don't report changes for :substitute, there will be many of them. 606" Don't change other windows; turn off scroll bind temporarily 607let s:old_title = &title 608let s:old_icon = &icon 609let s:old_et = &l:et 610let s:old_bind = &l:scrollbind 611let s:old_report = &report 612let s:old_search = @/ 613let s:old_more = &more 614set notitle noicon 615setlocal et 616set nomore 617set report=1000000 618setlocal noscrollbind 619 620if exists(':ownsyntax') && exists('w:current_syntax') 621 let s:current_syntax = w:current_syntax 622elseif exists('b:current_syntax') 623 let s:current_syntax = b:current_syntax 624else 625 let s:current_syntax = 'none' 626endif 627 628if s:current_syntax == '' 629 let s:current_syntax = 'none' 630endif 631 632" Split window to create a buffer with the HTML file. 633let s:orgbufnr = winbufnr(0) 634let s:origwin_stl = &l:stl 635if expand("%") == "" 636 if exists('g:html_diff_win_num') 637 exec 'new Untitled_win'.g:html_diff_win_num.'.'.(s:settings.use_xhtml ? 'x' : '').'html' 638 else 639 exec 'new Untitled.'.(s:settings.use_xhtml ? 'x' : '').'html' 640 endif 641else 642 exec 'new %.'.(s:settings.use_xhtml ? 'x' : '').'html' 643endif 644 645" Resize the new window to very small in order to make it draw faster 646let s:old_winheight = winheight(0) 647let s:old_winfixheight = &l:winfixheight 648if s:old_winheight > 2 649 resize 1 " leave enough room to view one line at a time 650 norm! G 651 norm! zt 652endif 653setlocal winfixheight 654 655let s:newwin_stl = &l:stl 656 657" on the new window, set the least time-consuming fold method 658let s:old_fen = &foldenable 659setlocal foldmethod=manual 660setlocal nofoldenable 661 662let s:newwin = winnr() 663let s:orgwin = bufwinnr(s:orgbufnr) 664 665setlocal modifiable 666%d 667let s:old_paste = &paste 668set paste 669let s:old_magic = &magic 670set magic 671 672" set the fileencoding to match the charset we'll be using 673let &l:fileencoding=s:settings.vim_encoding 674 675" According to http://www.w3.org/TR/html4/charset.html#doc-char-set, the byte 676" order mark is highly recommend on the web when using multibyte encodings. But, 677" it is not a good idea to include it on UTF-8 files. Otherwise, let Vim 678" determine when it is actually inserted. 679if s:settings.vim_encoding == 'utf-8' 680 setlocal nobomb 681else 682 setlocal bomb 683endif 684 685let s:lines = [] 686 687if s:settings.use_xhtml 688 if s:settings.encoding != "" 689 call add(s:lines, "<?xml version=\"1.0\" encoding=\"" . s:settings.encoding . "\"?>") 690 else 691 call add(s:lines, "<?xml version=\"1.0\"?>") 692 endif 693 let s:tag_close = ' />' 694else 695 let s:tag_close = '>' 696endif 697 698let s:HtmlSpace = ' ' 699let s:LeadingSpace = ' ' 700let s:HtmlEndline = '' 701if s:settings.no_pre 702 let s:HtmlEndline = '<br' . s:tag_close 703 let s:LeadingSpace = s:settings.use_xhtml ? ' ' : ' ' 704 let s:HtmlSpace = '\' . s:LeadingSpace 705endif 706 707" HTML header, with the title and generator ;-). Left free space for the CSS, 708" to be filled at the end. 709call extend(s:lines, [ 710 \ "<html>", 711 \ "<head>"]) 712" include encoding as close to the top as possible, but only if not already 713" contained in XML information (to avoid haggling over content type) 714if s:settings.encoding != "" && !s:settings.use_xhtml 715 call add(s:lines, "<meta http-equiv=\"content-type\" content=\"text/html; charset=" . s:settings.encoding . '"' . s:tag_close) 716endif 717call extend(s:lines, [ 718 \ ("<title>".expand("%:p:~")."</title>"), 719 \ ("<meta name=\"Generator\" content=\"Vim/".v:version/100.".".v:version%100.'"'.s:tag_close), 720 \ ("<meta name=\"plugin-version\" content=\"".g:loaded_2html_plugin.'"'.s:tag_close) 721 \ ]) 722call add(s:lines, '<meta name="syntax" content="'.s:current_syntax.'"'.s:tag_close) 723call add(s:lines, '<meta name="settings" content="'. 724 \ join(filter(keys(s:settings),'s:settings[v:val]'),','). 725 \ ',prevent_copy='.s:settings.prevent_copy. 726 \ '"'.s:tag_close) 727call add(s:lines, '<meta name="colorscheme" content="'. 728 \ (exists('g:colors_name') 729 \ ? g:colors_name 730 \ : 'none'). '"'.s:tag_close) 731 732if s:settings.use_css 733 if s:settings.dynamic_folds 734 if s:settings.hover_unfold 735 " if we are doing hover_unfold, use css 2 with css 1 fallback for IE6 736 call extend(s:lines, [ 737 \ "<style type=\"text/css\">", 738 \ s:settings.use_xhtml ? "" : "<!--", 739 \ ".FoldColumn { text-decoration: none; white-space: pre; }", 740 \ "", 741 \ "body * { margin: 0; padding: 0; }", "", 742 \ ".open-fold > .Folded { display: none; }", 743 \ ".open-fold > .fulltext { display: inline; }", 744 \ ".closed-fold > .fulltext { display: none; }", 745 \ ".closed-fold > .Folded { display: inline; }", 746 \ "", 747 \ ".open-fold > .toggle-open { display: none; }", 748 \ ".open-fold > .toggle-closed { display: inline; }", 749 \ ".closed-fold > .toggle-open { display: inline; }", 750 \ ".closed-fold > .toggle-closed { display: none; }", 751 \ "", "", 752 \ '/* opening a fold while hovering won''t be supported by IE6 and other', 753 \ "similar browsers, but it should fail gracefully. */", 754 \ ".closed-fold:hover > .fulltext { display: inline; }", 755 \ ".closed-fold:hover > .toggle-filler { display: none; }", 756 \ ".closed-fold:hover > .Folded { display: none; }", 757 \ s:settings.use_xhtml ? "" : '-->', 758 \ '</style>']) 759 " TODO: IE7 doesn't *actually* support XHTML, maybe we should remove this. 760 " But if it's served up as tag soup, maybe the following will work, so 761 " leave it in for now. 762 call extend(s:lines, [ 763 \ "<!--[if lt IE 7]><style type=\"text/css\">", 764 \ ".open-fold .Folded { display: none; }", 765 \ ".open-fold .fulltext { display: inline; }", 766 \ ".open-fold .toggle-open { display: none; }", 767 \ ".closed-fold .toggle-closed { display: inline; }", 768 \ "", 769 \ ".closed-fold .fulltext { display: none; }", 770 \ ".closed-fold .Folded { display: inline; }", 771 \ ".closed-fold .toggle-open { display: inline; }", 772 \ ".closed-fold .toggle-closed { display: none; }", 773 \ "</style>", 774 \ "<![endif]-->", 775 \]) 776 else 777 " if we aren't doing hover_unfold, use CSS 1 only 778 call extend(s:lines, [ 779 \ "<style type=\"text/css\">", 780 \ s:settings.use_xhtml ? "" :"<!--", 781 \ ".FoldColumn { text-decoration: none; white-space: pre; }", 782 \ ".open-fold .Folded { display: none; }", 783 \ ".open-fold .fulltext { display: inline; }", 784 \ ".open-fold .toggle-open { display: none; }", 785 \ ".closed-fold .toggle-closed { display: inline; }", 786 \ "", 787 \ ".closed-fold .fulltext { display: none; }", 788 \ ".closed-fold .Folded { display: inline; }", 789 \ ".closed-fold .toggle-open { display: inline; }", 790 \ ".closed-fold .toggle-closed { display: none; }", 791 \ s:settings.use_xhtml ? "" : '-->', 792 \ '</style>' 793 \]) 794 endif 795 else 796 " if we aren't doing any dynamic folding, no need for any special rules 797 call extend(s:lines, [ 798 \ "<style type=\"text/css\">", 799 \ s:settings.use_xhtml ? "" : "<!--", 800 \ s:settings.use_xhtml ? "" : '-->', 801 \ "</style>", 802 \]) 803 endif 804endif 805 806" insert script tag; javascript is always needed for the line number 807" normalization for URL hashes 808call extend(s:lines, [ 809 \ "", 810 \ "<script type='text/javascript'>", 811 \ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"]) 812 813" insert javascript to toggle folds open and closed 814if s:settings.dynamic_folds 815 call extend(s:lines, [ 816 \ "", 817 \ "function toggleFold(objID)", 818 \ "{", 819 \ " var fold;", 820 \ " fold = document.getElementById(objID);", 821 \ " if(fold.className == 'closed-fold')", 822 \ " {", 823 \ " fold.className = 'open-fold';", 824 \ " }", 825 \ " else if (fold.className == 'open-fold')", 826 \ " {", 827 \ " fold.className = 'closed-fold';", 828 \ " }", 829 \ "}" 830 \ ]) 831endif 832 833if s:settings.line_ids 834 " insert javascript to get IDs from line numbers, and to open a fold before 835 " jumping to any lines contained therein 836 call extend(s:lines, [ 837 \ "", 838 \ "/* function to open any folds containing a jumped-to line before jumping to it */", 839 \ "function JumpToLine()", 840 \ "{", 841 \ " var lineNum;", 842 \ " lineNum = window.location.hash;", 843 \ " lineNum = lineNum.substr(1); /* strip off '#' */", 844 \ "", 845 \ " if (lineNum.indexOf('L') == -1) {", 846 \ " lineNum = 'L'+lineNum;", 847 \ " }", 848 \ " lineElem = document.getElementById(lineNum);" 849 \ ]) 850 if s:settings.dynamic_folds 851 call extend(s:lines, [ 852 \ "", 853 \ " /* navigate upwards in the DOM tree to open all folds containing the line */", 854 \ " var node = lineElem;", 855 \ " while (node && node.id != 'vimCodeElement".s:settings.id_suffix."')", 856 \ " {", 857 \ " if (node.className == 'closed-fold')", 858 \ " {", 859 \ " node.className = 'open-fold';", 860 \ " }", 861 \ " node = node.parentNode;", 862 \ " }", 863 \ ]) 864 endif 865 call extend(s:lines, [ 866 \ " /* Always jump to new location even if the line was hidden inside a fold, or", 867 \ " * we corrected the raw number to a line ID.", 868 \ " */", 869 \ " if (lineElem) {", 870 \ " lineElem.scrollIntoView(true);", 871 \ " }", 872 \ " return true;", 873 \ "}", 874 \ "if ('onhashchange' in window) {", 875 \ " window.onhashchange = JumpToLine;", 876 \ "}" 877 \ ]) 878endif 879 880" Small text columns like the foldcolumn and line number column need a weird 881" hack to work around Webkit's and (in versions prior to 9) IE's lack of support 882" for the 'ch' unit without messing up Opera, which also doesn't support it but 883" works anyway. 884" 885" The problem is that without the 'ch' unit, it is not possible to specify a 886" size of an <input> in terms of character widths. Only Opera seems to do the 887" "sensible" thing and make the <input> sized to fit exactly as many characters 888" as specified by its "size" attribute, but the spec actually says "at least 889" wide enough to fit 'size' characters", so the other browsers are technically 890" correct as well. 891" 892" Anyway, this leads to two diffculties: 893" 1. The foldcolumn is made up of multiple elements side-by-side with 894" different sizes, each of which has their own extra padding added. Thus, a 895" column made up of one item of size 1 and another of size 2 would not 896" necessarily be equal in size to another line's foldcolumn with a single 897" item of size 3. 898" 2. The extra padding added to the <input> elements adds up to make the 899" foldcolumn and line number column too wide, especially in Webkit 900" browsers. 901" 902" So, the full workaround is: 903" 1. Define a default size in em, equal to the number of characters in the 904" input element, in case javascript is disabled and the browser does not 905" support the 'ch' unit. Unfortunately this makes Opera no longer work 906" properly without javascript. 1em per character is much too wide but it 907" looks better in webkit browsers than unaligned columns. 908" 2. Insert the following javascript to run at page load, which checks for the 909" width of a single character (in an extraneous page element inserted 910" before the page title, and set to hidden) and compares it to the width of 911" another extra <input> element with only one character. If the width 912" matches, the script does nothing more, but if not, it will figure out the 913" fraction of an em unit which would correspond with a ch unit if there 914" were one, and set the containing element (<pre> or <div>) to a class with 915" pre-defined rules which is closest to that fraction of an em. Rules are 916" defined from 0.05 em to 1em per ch. 917if !empty(s:settings.prevent_copy) 918 call extend(s:lines, [ 919 \ '', 920 \ '/* simulate a "ch" unit by asking the browser how big a zero character is */', 921 \ 'function FixCharWidth() {', 922 \ ' /* get the hidden element which gives the width of a single character */', 923 \ ' var goodWidth = document.getElementById("oneCharWidth").clientWidth;', 924 \ ' /* get all input elements, we''ll filter on class later */', 925 \ ' var inputTags = document.getElementsByTagName("input");', 926 \ ' var ratio = 5;', 927 \ ' var inputWidth = document.getElementById("oneInputWidth").clientWidth;', 928 \ ' var emWidth = document.getElementById("oneEmWidth").clientWidth;', 929 \ ' if (inputWidth > goodWidth) {', 930 \ ' while (ratio < 100*goodWidth/emWidth && ratio < 100) {', 931 \ ' ratio += 5;', 932 \ ' }', 933 \ ' document.getElementById("vimCodeElement'.s:settings.id_suffix.'").className = "em"+ratio;', 934 \ ' }', 935 \ '}' 936 \ ]) 937endif 938 939" insert script closing tag 940call extend(s:lines, [ 941 \ '', 942 \ s:settings.use_xhtml ? '//]]>' : '-->', 943 \ "</script>" 944 \ ]) 945 946call extend(s:lines, ["</head>"]) 947if !empty(s:settings.prevent_copy) 948 call extend(s:lines, 949 \ ["<body onload='FixCharWidth();".(s:settings.line_ids ? " JumpToLine();" : "")."'>", 950 \ "<!-- hidden divs used by javascript to get the width of a char -->", 951 \ "<div id='oneCharWidth'>0</div>", 952 \ "<div id='oneInputWidth'><input size='1' value='0'".s:tag_close."</div>", 953 \ "<div id='oneEmWidth' style='width: 1em;'></div>" 954 \ ]) 955else 956 call extend(s:lines, ["<body".(s:settings.line_ids ? " onload='JumpToLine();'" : "").">"]) 957endif 958if s:settings.no_pre 959 " if we're not using CSS we use a font tag which can't have a div inside 960 if s:settings.use_css 961 call extend(s:lines, ["<div id='vimCodeElement".s:settings.id_suffix."'>"]) 962 endif 963else 964 call extend(s:lines, ["<pre id='vimCodeElement".s:settings.id_suffix."'>"]) 965endif 966 967exe s:orgwin . "wincmd w" 968 969" caches of style data 970" initialize to include line numbers if using them 971if s:settings.number_lines 972 let s:stylelist = { s:LINENR_ID : ".LineNr { " . s:CSS1( s:LINENR_ID ) . "}" } 973else 974 let s:stylelist = {} 975endif 976let s:diffstylelist = { 977 \ s:DIFF_A_ID : ".DiffAdd { " . s:CSS1( s:DIFF_A_ID ) . "}", 978 \ s:DIFF_C_ID : ".DiffChange { " . s:CSS1( s:DIFF_C_ID ) . "}", 979 \ s:DIFF_D_ID : ".DiffDelete { " . s:CSS1( s:DIFF_D_ID ) . "}", 980 \ s:DIFF_T_ID : ".DiffText { " . s:CSS1( s:DIFF_T_ID ) . "}" 981 \ } 982 983" set up progress bar in the status line 984if !s:settings.no_progress 985 " ProgressBar Indicator 986 let s:progressbar={} 987 988 " Progessbar specific functions 989 func! s:ProgressBar(title, max_value, winnr) 990 let pgb=copy(s:progressbar) 991 let pgb.title = a:title.' ' 992 let pgb.max_value = a:max_value 993 let pgb.winnr = a:winnr 994 let pgb.cur_value = 0 995 let pgb.items = { 'title' : { 'color' : 'Statusline' }, 996 \'bar' : { 'color' : 'Statusline' , 'fillcolor' : 'DiffDelete' , 'bg' : 'Statusline' } , 997 \'counter' : { 'color' : 'Statusline' } } 998 let pgb.last_value = 0 999 let pgb.needs_redraw = 0 1000 " Note that you must use len(split) instead of len() if you want to use 1001 " unicode in title. 1002 " 1003 " Subtract 3 for spacing around the title. 1004 " Subtract 4 for the percentage display. 1005 " Subtract 2 for spacing before this. 1006 " Subtract 2 more for the '|' on either side of the progress bar 1007 let pgb.subtractedlen=len(split(pgb.title, '\zs'))+3+4+2+2 1008 let pgb.max_len = 0 1009 set laststatus=2 1010 return pgb 1011 endfun 1012 1013 " Function: progressbar.calculate_ticks() {{{1 1014 func! s:progressbar.calculate_ticks(pb_len) 1015 if a:pb_len<=0 1016 let pb_len = 100 1017 else 1018 let pb_len = a:pb_len 1019 endif 1020 let self.progress_ticks = map(range(pb_len+1), "v:val * self.max_value / pb_len") 1021 endfun 1022 1023 "Function: progressbar.paint() 1024 func! s:progressbar.paint() 1025 " Recalculate widths. 1026 let max_len = winwidth(self.winnr) 1027 let pb_len = 0 1028 " always true on first call because of initial value of self.max_len 1029 if max_len != self.max_len 1030 let self.max_len = max_len 1031 1032 " Progressbar length 1033 let pb_len = max_len - self.subtractedlen 1034 1035 call self.calculate_ticks(pb_len) 1036 1037 let self.needs_redraw = 1 1038 let cur_value = 0 1039 let self.pb_len = pb_len 1040 else 1041 " start searching at the last found index to make the search for the 1042 " appropriate tick value normally take 0 or 1 comparisons 1043 let cur_value = self.last_value 1044 let pb_len = self.pb_len 1045 endif 1046 1047 let cur_val_max = pb_len > 0 ? pb_len : 100 1048 1049 " find the current progress bar position based on precalculated thresholds 1050 while cur_value < cur_val_max && self.cur_value > self.progress_ticks[cur_value] 1051 let cur_value += 1 1052 endwhile 1053 1054 " update progress bar 1055 if self.last_value != cur_value || self.needs_redraw || self.cur_value == self.max_value 1056 let self.needs_redraw = 1 1057 let self.last_value = cur_value 1058 1059 let t_color = self.items.title.color 1060 let b_fcolor = self.items.bar.fillcolor 1061 let b_color = self.items.bar.color 1062 let c_color = self.items.counter.color 1063 1064 let stl = "%#".t_color."#%-( ".self.title." %)". 1065 \"%#".b_color."#". 1066 \(pb_len>0 ? 1067 \ ('|%#'.b_fcolor."#%-(".repeat(" ",cur_value)."%)". 1068 \ '%#'.b_color."#".repeat(" ",pb_len-cur_value)."|"): 1069 \ ('')). 1070 \"%=%#".c_color."#%( ".printf("%3.d ",100*self.cur_value/self.max_value)."%% %)" 1071 call setwinvar(self.winnr, '&stl', stl) 1072 endif 1073 endfun 1074 1075 func! s:progressbar.incr( ... ) 1076 let self.cur_value += (a:0 ? a:1 : 1) 1077 " if we were making a general-purpose progress bar, we'd need to limit to a 1078 " lower limit as well, but since we always increment with a positive value 1079 " in this script, we only need limit the upper value 1080 let self.cur_value = (self.cur_value > self.max_value ? self.max_value : self.cur_value) 1081 call self.paint() 1082 endfun 1083 " }}} 1084 if s:settings.dynamic_folds 1085 " to process folds we make two passes through each line 1086 let s:pgb = s:ProgressBar("Processing folds:", line('$')*2, s:orgwin) 1087 endif 1088endif 1089 1090" First do some preprocessing for dynamic folding. Do this for the entire file 1091" so we don't accidentally start within a closed fold or something. 1092let s:allfolds = [] 1093 1094if s:settings.dynamic_folds 1095 let s:lnum = 1 1096 let s:end = line('$') 1097 " save the fold text and set it to the default so we can find fold levels 1098 let s:foldtext_save = &foldtext 1099 setlocal foldtext& 1100 1101 " we will set the foldcolumn in the html to the greater of the maximum fold 1102 " level and the current foldcolumn setting 1103 let s:foldcolumn = &foldcolumn 1104 1105 " get all info needed to describe currently closed folds 1106 while s:lnum <= s:end 1107 if foldclosed(s:lnum) == s:lnum 1108 " default fold text has '+-' and then a number of dashes equal to fold 1109 " level, so subtract 2 from index of first non-dash after the dashes 1110 " in order to get the fold level of the current fold 1111 let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2 1112 " store fold info for later use 1113 let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"} 1114 call add(s:allfolds, s:newfold) 1115 " open the fold so we can find any contained folds 1116 execute s:lnum."foldopen" 1117 else 1118 if !s:settings.no_progress 1119 call s:pgb.incr() 1120 if s:pgb.needs_redraw 1121 redrawstatus 1122 let s:pgb.needs_redraw = 0 1123 endif 1124 endif 1125 let s:lnum = s:lnum + 1 1126 endif 1127 endwhile 1128 1129 " close all folds to get info for originally open folds 1130 silent! %foldclose! 1131 let s:lnum = 1 1132 1133 " the originally open folds will be all folds we encounter that aren't 1134 " already in the list of closed folds 1135 while s:lnum <= s:end 1136 if foldclosed(s:lnum) == s:lnum 1137 " default fold text has '+-' and then a number of dashes equal to fold 1138 " level, so subtract 2 from index of first non-dash after the dashes 1139 " in order to get the fold level of the current fold 1140 let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2 1141 let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"} 1142 " only add the fold if we don't already have it 1143 if empty(s:allfolds) || index(s:allfolds, s:newfold) == -1 1144 let s:newfold.type = "open-fold" 1145 call add(s:allfolds, s:newfold) 1146 endif 1147 " open the fold so we can find any contained folds 1148 execute s:lnum."foldopen" 1149 else 1150 if !s:settings.no_progress 1151 call s:pgb.incr() 1152 if s:pgb.needs_redraw 1153 redrawstatus 1154 let s:pgb.needs_redraw = 0 1155 endif 1156 endif 1157 let s:lnum = s:lnum + 1 1158 endif 1159 endwhile 1160 1161 " sort the folds so that we only ever need to look at the first item in the 1162 " list of folds 1163 call sort(s:allfolds, "s:FoldCompare") 1164 1165 let &l:foldtext = s:foldtext_save 1166 unlet s:foldtext_save 1167 1168 " close all folds again so we can get the fold text as we go 1169 silent! %foldclose! 1170 1171 " Go through and remove folds we don't need to (or cannot) process in the 1172 " current conversion range 1173 " 1174 " If a fold is removed which contains other folds, which are included, we need 1175 " to adjust the level of the included folds as used by the conversion logic 1176 " (avoiding special cases is good) 1177 " 1178 " Note any time we remove a fold, either all of the included folds are in it, 1179 " or none of them, because we only remove a fold if neither its start nor its 1180 " end are within the conversion range. 1181 let leveladjust = 0 1182 for afold in s:allfolds 1183 let removed = 0 1184 if exists("g:html_start_line") && exists("g:html_end_line") 1185 if afold.firstline < g:html_start_line 1186 if afold.lastline <= g:html_end_line && afold.lastline >= g:html_start_line 1187 " if a fold starts before the range to convert but stops within the 1188 " range, we need to include it. Make it start on the first converted 1189 " line. 1190 let afold.firstline = g:html_start_line 1191 else 1192 " if the fold lies outside the range or the start and stop enclose 1193 " the entire range, don't bother parsing it 1194 call remove(s:allfolds, index(s:allfolds, afold)) 1195 let removed = 1 1196 if afold.lastline > g:html_end_line 1197 let leveladjust += 1 1198 endif 1199 endif 1200 elseif afold.firstline > g:html_end_line 1201 " If the entire fold lies outside the range we need to remove it. 1202 call remove(s:allfolds, index(s:allfolds, afold)) 1203 let removed = 1 1204 endif 1205 elseif exists("g:html_start_line") 1206 if afold.firstline < g:html_start_line 1207 " if there is no last line, but there is a first line, the end of the 1208 " fold will always lie within the region of interest, so keep it 1209 let afold.firstline = g:html_start_line 1210 endif 1211 elseif exists("g:html_end_line") 1212 " if there is no first line we default to the first line in the buffer so 1213 " the fold start will always be included if the fold itself is included. 1214 " If however the entire fold lies outside the range we need to remove it. 1215 if afold.firstline > g:html_end_line 1216 call remove(s:allfolds, index(s:allfolds, afold)) 1217 let removed = 1 1218 endif 1219 endif 1220 if !removed 1221 let afold.level -= leveladjust 1222 if afold.level+1 > s:foldcolumn 1223 let s:foldcolumn = afold.level+1 1224 endif 1225 endif 1226 endfor 1227 1228 " if we've removed folds containing the conversion range from processing, 1229 " getting foldtext as we go won't know to open the removed folds, so the 1230 " foldtext would be wrong; open them now. 1231 " 1232 " Note that only when a start and an end line is specified will a fold 1233 " containing the current range ever be removed. 1234 while leveladjust > 0 1235 exe g:html_start_line."foldopen" 1236 let leveladjust -= 1 1237 endwhile 1238endif 1239 1240" Now loop over all lines in the original text to convert to html. 1241" Use html_start_line and html_end_line if they are set. 1242if exists("g:html_start_line") 1243 let s:lnum = html_start_line 1244 if s:lnum < 1 || s:lnum > line("$") 1245 let s:lnum = 1 1246 endif 1247else 1248 let s:lnum = 1 1249endif 1250if exists("g:html_end_line") 1251 let s:end = html_end_line 1252 if s:end < s:lnum || s:end > line("$") 1253 let s:end = line("$") 1254 endif 1255else 1256 let s:end = line("$") 1257endif 1258 1259" stack to keep track of all the folds containing the current line 1260let s:foldstack = [] 1261 1262if !s:settings.no_progress 1263 let s:pgb = s:ProgressBar("Processing lines:", s:end - s:lnum + 1, s:orgwin) 1264endif 1265 1266if s:settings.number_lines 1267 let s:margin = strlen(s:end) + 1 1268else 1269 let s:margin = 0 1270endif 1271 1272if has('folding') && !s:settings.ignore_folding 1273 let s:foldfillchar = &fillchars[matchend(&fillchars, 'fold:')] 1274 if s:foldfillchar == '' 1275 let s:foldfillchar = '-' 1276 endif 1277endif 1278let s:difffillchar = &fillchars[matchend(&fillchars, 'diff:')] 1279if s:difffillchar == '' 1280 let s:difffillchar = '-' 1281endif 1282 1283let s:foldId = 0 1284 1285if !s:settings.expand_tabs 1286 " If keeping tabs, add them to printable characters so we keep them when 1287 " formatting text (strtrans() doesn't replace printable chars) 1288 let s:old_isprint = &isprint 1289 setlocal isprint+=9 1290endif 1291 1292while s:lnum <= s:end 1293 1294 " If there are filler lines for diff mode, show these above the line. 1295 let s:filler = diff_filler(s:lnum) 1296 if s:filler > 0 1297 let s:n = s:filler 1298 while s:n > 0 1299 let s:new = repeat(s:difffillchar, 3) 1300 1301 if s:n > 2 && s:n < s:filler && !s:settings.whole_filler 1302 let s:new = s:new . " " . s:filler . " inserted lines " 1303 let s:n = 2 1304 endif 1305 1306 if !s:settings.no_pre 1307 " HTML line wrapping is off--go ahead and fill to the margin 1308 " TODO: what about when CSS wrapping is turned on? 1309 let s:new = s:new . repeat(s:difffillchar, &columns - strlen(s:new) - s:margin) 1310 else 1311 let s:new = s:new . repeat(s:difffillchar, 3) 1312 endif 1313 1314 let s:new = s:HtmlFormat_d(s:new, s:DIFF_D_ID, 0) 1315 if s:settings.number_lines 1316 " Indent if line numbering is on. Indent gets style of line number 1317 " column. 1318 let s:new = s:HtmlFormat_n(repeat(' ', s:margin), s:LINENR_ID, 0, 0) . s:new 1319 endif 1320 if s:settings.dynamic_folds && !s:settings.no_foldcolumn && s:foldcolumn > 0 1321 " Indent for foldcolumn if there is one. Assume it's empty, there should 1322 " not be a fold for deleted lines in diff mode. 1323 let s:new = s:FoldColumn_fill() . s:new 1324 endif 1325 call add(s:lines, s:new.s:HtmlEndline) 1326 1327 let s:n = s:n - 1 1328 endwhile 1329 unlet s:n 1330 endif 1331 unlet s:filler 1332 1333 " Start the line with the line number. 1334 if s:settings.number_lines 1335 let s:numcol = repeat(' ', s:margin - 1 - strlen(s:lnum)) . s:lnum . ' ' 1336 endif 1337 1338 let s:new = "" 1339 1340 if has('folding') && !s:settings.ignore_folding && foldclosed(s:lnum) > -1 && !s:settings.dynamic_folds 1341 " 1342 " This is the beginning of a folded block (with no dynamic folding) 1343 let s:new = foldtextresult(s:lnum) 1344 if !s:settings.no_pre 1345 " HTML line wrapping is off--go ahead and fill to the margin 1346 let s:new = s:new . repeat(s:foldfillchar, &columns - strlen(s:new)) 1347 endif 1348 1349 " put numcol in a separate group for sake of unselectable text 1350 let s:new = (s:settings.number_lines ? s:HtmlFormat_n(s:numcol, s:FOLDED_ID, 0, s:lnum): "") . s:HtmlFormat_t(s:new, s:FOLDED_ID, 0) 1351 1352 " Skip to the end of the fold 1353 let s:new_lnum = foldclosedend(s:lnum) 1354 1355 if !s:settings.no_progress 1356 call s:pgb.incr(s:new_lnum - s:lnum) 1357 endif 1358 1359 let s:lnum = s:new_lnum 1360 1361 else 1362 " 1363 " A line that is not folded, or doing dynamic folding. 1364 " 1365 let s:line = getline(s:lnum) 1366 let s:len = strlen(s:line) 1367 1368 if s:settings.dynamic_folds 1369 " First insert a closing for any open folds that end on this line 1370 while !empty(s:foldstack) && get(s:foldstack,0).lastline == s:lnum-1 1371 let s:new = s:new."</span></span>" 1372 call remove(s:foldstack, 0) 1373 endwhile 1374 1375 " Now insert an opening for any new folds that start on this line 1376 let s:firstfold = 1 1377 while !empty(s:allfolds) && get(s:allfolds,0).firstline == s:lnum 1378 let s:foldId = s:foldId + 1 1379 let s:new .= "<span id='" 1380 let s:new .= (exists('g:html_diff_win_num') ? "win".g:html_diff_win_num : "") 1381 let s:new .= "fold".s:foldId.s:settings.id_suffix."' class='".s:allfolds[0].type."'>" 1382 1383 1384 " Unless disabled, add a fold column for the opening line of a fold. 1385 " 1386 " Note that dynamic folds require using css so we just use css to take 1387 " care of the leading spaces rather than using in the case of 1388 " html_no_pre to make it easier 1389 if !s:settings.no_foldcolumn 1390 " add fold column that can open the new fold 1391 if s:allfolds[0].level > 1 && s:firstfold 1392 let s:new = s:new . s:FoldColumn_build('|', s:allfolds[0].level - 1, 0, "", 1393 \ 'toggle-open FoldColumn','javascript:toggleFold("fold'.s:foldstack[0].id.s:settings.id_suffix.'");') 1394 endif 1395 " add the filler spaces separately from the '+' char so that it can be 1396 " shown/hidden separately during a hover unfold 1397 let s:new = s:new . s:FoldColumn_build("+", 1, 0, "", 1398 \ 'toggle-open FoldColumn', 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");') 1399 " If this is not the last fold we're opening on this line, we need 1400 " to keep the filler spaces hidden if the fold is opened by mouse 1401 " hover. If it is the last fold to open in the line, we shouldn't hide 1402 " them, so don't apply the toggle-filler class. 1403 let s:new = s:new . s:FoldColumn_build(" ", 1, s:foldcolumn - s:allfolds[0].level - 1, "", 1404 \ 'toggle-open FoldColumn'. (get(s:allfolds, 1, {'firstline': 0}).firstline == s:lnum ?" toggle-filler" :""), 1405 \ 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");') 1406 1407 " add fold column that can close the new fold 1408 " only add extra blank space if we aren't opening another fold on the 1409 " same line 1410 if get(s:allfolds, 1, {'firstline': 0}).firstline != s:lnum 1411 let s:extra_space = s:foldcolumn - s:allfolds[0].level 1412 else 1413 let s:extra_space = 0 1414 endif 1415 if s:firstfold 1416 " the first fold in a line has '|' characters from folds opened in 1417 " previous lines, before the '-' for this fold 1418 let s:new .= s:FoldColumn_build('|', s:allfolds[0].level - 1, s:extra_space, '-', 1419 \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");') 1420 else 1421 " any subsequent folds in the line only add a single '-' 1422 let s:new = s:new . s:FoldColumn_build("-", 1, s:extra_space, "", 1423 \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");') 1424 endif 1425 let s:firstfold = 0 1426 endif 1427 1428 " Add fold text, moving the span ending to the next line so collapsing 1429 " of folds works correctly. 1430 " Put numcol in a separate group for sake of unselectable text. 1431 let s:new = s:new . (s:settings.number_lines ? s:HtmlFormat_n(s:numcol, s:FOLDED_ID, 0, 0) : "") . substitute(s:HtmlFormat_t(foldtextresult(s:lnum), s:FOLDED_ID, 0), '</span>', s:HtmlEndline.'\n\0', '') 1432 let s:new = s:new . "<span class='fulltext'>" 1433 1434 " open the fold now that we have the fold text to allow retrieval of 1435 " fold text for subsequent folds 1436 execute s:lnum."foldopen" 1437 call insert(s:foldstack, remove(s:allfolds,0)) 1438 let s:foldstack[0].id = s:foldId 1439 endwhile 1440 1441 " Unless disabled, add a fold column for other lines. 1442 " 1443 " Note that dynamic folds require using css so we just use css to take 1444 " care of the leading spaces rather than using in the case of 1445 " html_no_pre to make it easier 1446 if !s:settings.no_foldcolumn 1447 if empty(s:foldstack) 1448 " add the empty foldcolumn for unfolded lines if there is a fold 1449 " column at all 1450 if s:foldcolumn > 0 1451 let s:new = s:new . s:FoldColumn_fill() 1452 endif 1453 else 1454 " add the fold column for folds not on the opening line 1455 if get(s:foldstack, 0).firstline < s:lnum 1456 let s:new = s:new . s:FoldColumn_build('|', s:foldstack[0].level, s:foldcolumn - s:foldstack[0].level, "", 1457 \ 'FoldColumn', 'javascript:toggleFold("fold'.s:foldstack[0].id.s:settings.id_suffix.'");') 1458 endif 1459 endif 1460 endif 1461 endif 1462 1463 " Now continue with the unfolded line text 1464 if s:settings.number_lines 1465 let s:new = s:new . s:HtmlFormat_n(s:numcol, s:LINENR_ID, 0, s:lnum) 1466 elseif s:settings.line_ids 1467 let s:new = s:new . s:HtmlFormat_n("", s:LINENR_ID, 0, s:lnum) 1468 endif 1469 1470 " Get the diff attribute, if any. 1471 let s:diffattr = diff_hlID(s:lnum, 1) 1472 1473 " initialize conceal info to act like not concealed, just in case 1474 let s:concealinfo = [0, ''] 1475 1476 " Loop over each character in the line 1477 let s:col = 1 1478 1479 " most of the time we won't use the diff_id, initialize to zero 1480 let s:diff_id = 0 1481 1482 while s:col <= s:len || (s:col == 1 && s:diffattr) 1483 let s:startcol = s:col " The start column for processing text 1484 if !s:settings.ignore_conceal && has('conceal') 1485 let s:concealinfo = synconcealed(s:lnum, s:col) 1486 endif 1487 if !s:settings.ignore_conceal && s:concealinfo[0] 1488 let s:col = s:col + 1 1489 " Speed loop (it's small - that's the trick) 1490 " Go along till we find a change in the match sequence number (ending 1491 " the specific concealed region) or until there are no more concealed 1492 " characters. 1493 while s:col <= s:len && s:concealinfo == synconcealed(s:lnum, s:col) | let s:col = s:col + 1 | endwhile 1494 elseif s:diffattr 1495 let s:diff_id = diff_hlID(s:lnum, s:col) 1496 let s:id = synID(s:lnum, s:col, 1) 1497 let s:col = s:col + 1 1498 " Speed loop (it's small - that's the trick) 1499 " Go along till we find a change in hlID 1500 while s:col <= s:len && s:id == synID(s:lnum, s:col, 1) 1501 \ && s:diff_id == diff_hlID(s:lnum, s:col) | 1502 \ let s:col = s:col + 1 | 1503 \ endwhile 1504 if s:len < &columns && !s:settings.no_pre 1505 " Add spaces at the end of the raw text line to extend the changed 1506 " line to the full width. 1507 let s:line = s:line . repeat(' ', &columns - virtcol([s:lnum, s:len]) - s:margin) 1508 let s:len = &columns 1509 endif 1510 else 1511 let s:id = synID(s:lnum, s:col, 1) 1512 let s:col = s:col + 1 1513 " Speed loop (it's small - that's the trick) 1514 " Go along till we find a change in synID 1515 while s:col <= s:len && s:id == synID(s:lnum, s:col, 1) | let s:col = s:col + 1 | endwhile 1516 endif 1517 1518 if s:settings.ignore_conceal || !s:concealinfo[0] 1519 " Expand tabs if needed 1520 let s:expandedtab = strpart(s:line, s:startcol - 1, s:col - s:startcol) 1521 if s:settings.expand_tabs 1522 let s:offset = 0 1523 let s:idx = stridx(s:expandedtab, "\t") 1524 while s:idx >= 0 1525 if has("multi_byte_encoding") 1526 if s:startcol + s:idx == 1 1527 let s:i = &ts 1528 else 1529 if s:idx == 0 1530 let s:prevc = matchstr(s:line, '.\%' . (s:startcol + s:idx + s:offset) . 'c') 1531 else 1532 let s:prevc = matchstr(s:expandedtab, '.\%' . (s:idx + 1) . 'c') 1533 endif 1534 let s:vcol = virtcol([s:lnum, s:startcol + s:idx + s:offset - len(s:prevc)]) 1535 let s:i = &ts - (s:vcol % &ts) 1536 endif 1537 let s:offset -= s:i - 1 1538 else 1539 let s:i = &ts - ((s:idx + s:startcol - 1) % &ts) 1540 endif 1541 let s:expandedtab = substitute(s:expandedtab, '\t', repeat(' ', s:i), '') 1542 let s:idx = stridx(s:expandedtab, "\t") 1543 endwhile 1544 end 1545 1546 " get the highlight group name to use 1547 let s:id = synIDtrans(s:id) 1548 else 1549 " use Conceal highlighting for concealed text 1550 let s:id = s:CONCEAL_ID 1551 let s:expandedtab = s:concealinfo[1] 1552 endif 1553 1554 " Output the text with the same synID, with class set to the highlight ID 1555 " name, unless it has been concealed completely. 1556 if strlen(s:expandedtab) > 0 1557 let s:new = s:new . s:HtmlFormat(s:expandedtab, s:id, s:diff_id, "", 0) 1558 endif 1559 endwhile 1560 endif 1561 1562 call extend(s:lines, split(s:new.s:HtmlEndline, '\n', 1)) 1563 if !s:settings.no_progress && s:pgb.needs_redraw 1564 redrawstatus 1565 let s:pgb.needs_redraw = 0 1566 endif 1567 let s:lnum = s:lnum + 1 1568 1569 if !s:settings.no_progress 1570 call s:pgb.incr() 1571 endif 1572endwhile 1573 1574if s:settings.dynamic_folds 1575 " finish off any open folds 1576 while !empty(s:foldstack) 1577 let s:lines[-1].="</span></span>" 1578 call remove(s:foldstack, 0) 1579 endwhile 1580 1581 " add fold column to the style list if not already there 1582 let s:id = s:FOLD_C_ID 1583 if !has_key(s:stylelist, s:id) 1584 let s:stylelist[s:id] = '.FoldColumn { ' . s:CSS1(s:id) . '}' 1585 endif 1586endif 1587 1588if s:settings.no_pre 1589 if !s:settings.use_css 1590 " Close off the font tag that encapsulates the whole <body> 1591 call extend(s:lines, ["</font>", "</body>", "</html>"]) 1592 else 1593 call extend(s:lines, ["</div>", "</body>", "</html>"]) 1594 endif 1595else 1596 call extend(s:lines, ["</pre>", "</body>", "</html>"]) 1597endif 1598 1599exe s:newwin . "wincmd w" 1600call setline(1, s:lines) 1601unlet s:lines 1602 1603" Mangle modelines so Vim doesn't try to use HTML text as a modeline if editing 1604" this file in the future; need to do this after generating all the text in case 1605" the modeline text has different highlight groups which all turn out to be 1606" stripped from the final output. 1607%s!\v(%(^|\s+)%([Vv]i%(m%([<=>]?\d+)?)?|ex)):!\1\:!ge 1608 1609" The generated HTML is admittedly ugly and takes a LONG time to fold. 1610" Make sure the user doesn't do syntax folding when loading a generated file, 1611" using a modeline. 1612call append(line('$'), "<!-- vim: set foldmethod=manual : -->") 1613 1614" Now, when we finally know which, we define the colors and styles 1615if s:settings.use_css 1616 1;/<style type="text/+1 1617endif 1618 1619" Normal/global attributes 1620" For Netscape 4, set <body> attributes too, though, strictly speaking, it's 1621" incorrect. 1622if s:settings.use_css 1623 if s:settings.no_pre 1624 call append('.', "body { color: " . s:fgc . "; background-color: " . s:bgc . "; font-family: ". s:htmlfont ."; }") 1625 + 1626 else 1627 call append('.', "pre { " . s:whitespace . "font-family: ". s:htmlfont ."; color: " . s:fgc . "; background-color: " . s:bgc . "; }") 1628 + 1629 yank 1630 put 1631 execute "normal! ^cwbody\e" 1632 " body should not have the wrap formatting, only the pre section 1633 if s:whitespace != '' 1634 exec 's#'.s:whitespace 1635 endif 1636 endif 1637 " fix browser inconsistencies (sometimes within the same browser) of different 1638 " default font size for different elements 1639 call append('.', '* { font-size: 1em; }') 1640 + 1641 " if we use any input elements for unselectable content, make sure they look 1642 " like normal text 1643 if !empty(s:settings.prevent_copy) 1644 call append('.', 'input { border: none; margin: 0; padding: 0; font-family: '.s:htmlfont.'; }') 1645 + 1646 " ch units for browsers which support them, em units for a somewhat 1647 " reasonable fallback. Also make sure the special elements for size 1648 " calculations aren't seen. 1649 call append('.', [ 1650 \ "input[size='1'] { width: 1em; width: 1ch; }", 1651 \ "input[size='2'] { width: 2em; width: 2ch; }", 1652 \ "input[size='3'] { width: 3em; width: 3ch; }", 1653 \ "input[size='4'] { width: 4em; width: 4ch; }", 1654 \ "input[size='5'] { width: 5em; width: 5ch; }", 1655 \ "input[size='6'] { width: 6em; width: 6ch; }", 1656 \ "input[size='7'] { width: 7em; width: 7ch; }", 1657 \ "input[size='8'] { width: 8em; width: 8ch; }", 1658 \ "input[size='9'] { width: 9em; width: 9ch; }", 1659 \ "input[size='10'] { width: 10em; width: 10ch; }", 1660 \ "input[size='11'] { width: 11em; width: 11ch; }", 1661 \ "input[size='12'] { width: 12em; width: 12ch; }", 1662 \ "input[size='13'] { width: 13em; width: 13ch; }", 1663 \ "input[size='14'] { width: 14em; width: 14ch; }", 1664 \ "input[size='15'] { width: 15em; width: 15ch; }", 1665 \ "input[size='16'] { width: 16em; width: 16ch; }", 1666 \ "input[size='17'] { width: 17em; width: 17ch; }", 1667 \ "input[size='18'] { width: 18em; width: 18ch; }", 1668 \ "input[size='19'] { width: 19em; width: 19ch; }", 1669 \ "input[size='20'] { width: 20em; width: 20ch; }", 1670 \ "#oneCharWidth, #oneEmWidth, #oneInputWidth { padding: 0; margin: 0; position: absolute; left: -999999px; visibility: hidden; }" 1671 \ ]) 1672 +21 1673 for w in range(5, 100, 5) 1674 let base = 0.01 * w 1675 call append('.', join(map(range(1,20), "'.em'.w.' input[size='''.v:val.'''] { width: '.string(v:val*base).'em; }'"))) 1676 + 1677 endfor 1678 if s:settings.prevent_copy =~# 'f' 1679 " Make the cursor show active fold columns as active areas, and empty fold 1680 " columns as not interactive. 1681 call append('.', ['input.FoldColumn { cursor: pointer; }', 1682 \ 'input.FoldColumn[value=""] { cursor: default; }' 1683 \ ]) 1684 +2 1685 endif 1686 " make line number column show as non-interactive if not selectable 1687 if s:settings.prevent_copy =~# 'n' 1688 call append('.', 'input.LineNr { cursor: default; }') 1689 + 1690 endif 1691 " make fold text and line number column within fold text show as 1692 " non-interactive if not selectable 1693 if (s:settings.prevent_copy =~# 'n' || s:settings.prevent_copy =~# 't') && !s:settings.ignore_folding 1694 call append('.', 'input.Folded { cursor: default; }') 1695 + 1696 endif 1697 endif 1698else 1699 execute '%s:<body\([^>]*\):<body bgcolor="' . s:bgc . '" text="' . s:fgc . '"\1>\r<font face="'. s:htmlfont .'"' 1700endif 1701 1702" Gather attributes for all other classes. Do diff first so that normal 1703" highlight groups are inserted before it. 1704if s:settings.use_css 1705 if s:diff_mode 1706 call append('.', filter(map(keys(s:diffstylelist), "s:diffstylelist[v:val]"), 'v:val != ""')) 1707 endif 1708 if !empty(s:stylelist) 1709 call append('.', filter(map(keys(s:stylelist), "s:stylelist[v:val]"), 'v:val != ""')) 1710 endif 1711endif 1712 1713" Add hyperlinks 1714" TODO: add option to not do this? Maybe just make the color the same as the 1715" text highlight group normally is? 1716%s+\(https\=://\S\{-}\)\(\([.,;:}]\=\(\s\|$\)\)\|[\\"'<>]\|>\|<\|"\)+<a href="\1">\1</a>\2+ge 1717 1718" The DTD 1719if s:settings.use_xhtml 1720 exe "normal! gg$a\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" 1721elseif s:settings.use_css && !s:settings.no_pre 1722 exe "normal! gg0i<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n" 1723else 1724 exe "normal! gg0i<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n" 1725endif 1726 1727if s:settings.use_xhtml 1728 exe "normal! gg/<html/e\na xmlns=\"http://www.w3.org/1999/xhtml\"\e" 1729endif 1730 1731" Cleanup 1732%s:\s\+$::e 1733 1734" Restore old settings (new window first) 1735" 1736" Don't bother restoring foldmethod in case it was syntax because the markup is 1737" so weirdly formatted it can take a LONG time. 1738let &l:foldenable = s:old_fen 1739let &report = s:old_report 1740let &title = s:old_title 1741let &icon = s:old_icon 1742let &paste = s:old_paste 1743let &magic = s:old_magic 1744let @/ = s:old_search 1745let &more = s:old_more 1746 1747" switch to original window to restore those settings 1748exe s:orgwin . "wincmd w" 1749 1750if !s:settings.expand_tabs 1751 let &l:isprint = s:old_isprint 1752endif 1753let &l:stl = s:origwin_stl 1754let &l:et = s:old_et 1755let &l:scrollbind = s:old_bind 1756 1757" and back to the new window again to end there 1758exe s:newwin . "wincmd w" 1759 1760let &l:stl = s:newwin_stl 1761exec 'resize' s:old_winheight 1762let &l:winfixheight = s:old_winfixheight 1763 1764let &ls=s:ls 1765 1766" Save a little bit of memory (worth doing?) 1767unlet s:htmlfont s:whitespace 1768unlet s:old_et s:old_paste s:old_icon s:old_report s:old_title s:old_search 1769unlet s:old_magic s:old_more s:old_fen s:old_winheight 1770unlet! s:old_isprint 1771unlet s:whatterm s:stylelist s:diffstylelist s:lnum s:end s:margin s:fgc s:bgc s:old_winfixheight 1772unlet! s:col s:id s:attr s:len s:line s:new s:expandedtab s:concealinfo s:diff_mode 1773unlet! s:orgwin s:newwin s:orgbufnr s:idx s:i s:offset s:ls s:origwin_stl 1774unlet! s:newwin_stl s:current_syntax 1775if !v:profiling 1776 delfunc s:HtmlColor 1777 delfunc s:HtmlFormat 1778 delfunc s:CSS1 1779 delfunc s:BuildStyleWrapper 1780 if !s:settings.use_css 1781 delfunc s:HtmlOpening 1782 delfunc s:HtmlClosing 1783 endif 1784 if s:settings.dynamic_folds 1785 delfunc s:FoldCompare 1786 endif 1787 1788 if !s:settings.no_progress 1789 delfunc s:ProgressBar 1790 delfunc s:progressbar.paint 1791 delfunc s:progressbar.incr 1792 unlet s:pgb s:progressbar 1793 endif 1794endif 1795 1796unlet! s:new_lnum s:diffattr s:difffillchar s:foldfillchar s:HtmlSpace 1797unlet! s:LeadingSpace s:HtmlEndline s:firstfold s:numcol s:foldcolumn 1798unlet s:foldstack s:allfolds s:foldId s:settings 1799 1800let &cpo = s:cpo_sav 1801unlet! s:cpo_sav 1802 1803" Make sure any patches will probably use consistent indent 1804" vim: ts=8 sw=2 sts=2 noet 1805