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