1" Vim syntax support file 2" Maintainer: Ben Fritz <[email protected]> 3" Last Change: 2013 May 31 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 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr) 454 if a:lnr > 0 455 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.'" ', 1) 456 else 457 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) 458 endif 459 endfun 460 else 461 " if lines are not being numbered the only reason this function gets called 462 " is to put the line IDs on each line; "text" will be emtpy but lnr will 463 " always be non-zero, however we don't want to use the <input> because that 464 " won't work as nice for empty text 465 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr) 466 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.'" ', 0) 467 endfun 468 endif 469else 470 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr) 471 if a:lnr > 0 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.'" ', 0) 473 else 474 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0) 475 endif 476 endfun 477endif 478if s:settings.prevent_copy =~# 'd' 479 function! s:HtmlFormat_d(text, style_id, diff_style_id) 480 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) 481 endfun 482else 483 function! s:HtmlFormat_d(text, style_id, diff_style_id) 484 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0) 485 endfun 486endif 487if s:settings.prevent_copy =~# 'f' 488 " Note the <input> elements for fill spaces will have a single space for 489 " content, to allow active cursor CSS selection to work. 490 " 491 " Wrap the whole thing in a span for the 1px padding workaround for gaps. 492 function! s:FoldColumn_build(char, len, numfill, char2, class, click) 493 let l:input_open = "<input readonly='readonly'".s:unselInputType. 494 \ " onselect='this.blur(); return false;'". 495 \ " onmousedown='this.blur(); ".a:click." return false;'". 496 \ " onclick='return false;' size='". 497 \ string(a:len + (empty(a:char2) ? 0 : 1) + a:numfill) . 498 \ "' " 499 let l:common_attrs = "class='FoldColumn' value='" 500 let l:input_close = (s:settings.use_xhtml ? "' />" : "'>") 501 return "<span class='".a:class."'>". 502 \ l:input_open.l:common_attrs.repeat(a:char, a:len). 503 \ (!empty(a:char2) ? a:char2 : ""). 504 \ l:input_close . "</span>" 505 endfun 506 function! s:FoldColumn_fill() 507 return s:FoldColumn_build('', s:foldcolumn, 0, '', 'FoldColumn', '') 508 endfun 509else 510 " For normal fold columns, simply space-pad to the desired width (note that 511 " the FoldColumn definition includes a whitespace:pre rule) 512 function! s:FoldColumn_build(char, len, numfill, char2, class, click) 513 return "<a href='#' class='".a:class."' onclick='".a:click."'>". 514 \ repeat(a:char, a:len).a:char2.repeat(' ', a:numfill). 515 \ "</a>" 516 endfun 517 function! s:FoldColumn_fill() 518 return s:HtmlFormat(repeat(' ', s:foldcolumn), s:FOLD_C_ID, 0, "", 0) 519 endfun 520endif 521if s:settings.prevent_copy =~# 't' 522 " put an extra empty span at the end for dynamic folds, so the linebreak can 523 " be surrounded. Otherwise do it as normal. 524 " 525 " TODO: isn't there a better way to do this, than placing it here and using a 526 " substitute later? 527 if s:settings.dynamic_folds 528 function! s:HtmlFormat_t(text, style_id, diff_style_id) 529 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) . 530 \ s:HtmlFormat("", a:style_id, 0, "", 0) 531 endfun 532 else 533 function! s:HtmlFormat_t(text, style_id, diff_style_id) 534 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) 535 endfun 536 endif 537else 538 function! s:HtmlFormat_t(text, style_id, diff_style_id) 539 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0) 540 endfun 541endif 542 543" Return CSS style describing given highlight id (can be empty) 544function! s:CSS1(id) 545 let a = "" 546 if synIDattr(a:id, "inverse") 547 " For inverse, we always must set both colors (and exchange them) 548 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm)) 549 let a = a . "color: " . ( x != "" ? x : s:bgc ) . "; " 550 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm)) 551 let a = a . "background-color: " . ( x != "" ? x : s:fgc ) . "; " 552 else 553 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm)) 554 if x != "" | let a = a . "color: " . x . "; " | endif 555 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm)) 556 if x != "" 557 let a = a . "background-color: " . x . "; " 558 " stupid hack because almost every browser seems to have at least one font 559 " which shows 1px gaps between lines which have background 560 let a = a . "padding-bottom: 1px; " 561 elseif (a:id == s:FOLDED_ID || a:id == s:LINENR_ID || a:id == s:FOLD_C_ID) && !empty(s:settings.prevent_copy) 562 " input elements default to a different color than the rest of the page 563 let a = a . "background-color: " . s:bgc . "; " 564 endif 565 endif 566 if synIDattr(a:id, "bold") | let a = a . "font-weight: bold; " | endif 567 if synIDattr(a:id, "italic") | let a = a . "font-style: italic; " | endif 568 if synIDattr(a:id, "underline") | let a = a . "text-decoration: underline; " | endif 569 return a 570endfun 571 572if s:settings.dynamic_folds 573 " compares two folds as stored in our list of folds 574 " A fold is "less" than another if it starts at an earlier line number, 575 " or ends at a later line number, ties broken by fold level 576 function! s:FoldCompare(f1, f2) 577 if a:f1.firstline != a:f2.firstline 578 " put it before if it starts earlier 579 return a:f1.firstline - a:f2.firstline 580 elseif a:f1.lastline != a:f2.lastline 581 " put it before if it ends later 582 return a:f2.lastline - a:f1.lastline 583 else 584 " if folds begin and end on the same lines, put lowest fold level first 585 return a:f1.level - a:f2.level 586 endif 587 endfunction 588 589endif 590 591 592" Set some options to make it work faster. 593" Don't report changes for :substitute, there will be many of them. 594" Don't change other windows; turn off scroll bind temporarily 595let s:old_title = &title 596let s:old_icon = &icon 597let s:old_et = &l:et 598let s:old_bind = &l:scrollbind 599let s:old_report = &report 600let s:old_search = @/ 601let s:old_more = &more 602set notitle noicon 603setlocal et 604set nomore 605set report=1000000 606setlocal noscrollbind 607 608if exists(':ownsyntax') && exists('w:current_syntax') 609 let s:current_syntax = w:current_syntax 610elseif exists('b:current_syntax') 611 let s:current_syntax = b:current_syntax 612else 613 let s:current_syntax = 'none' 614endif 615 616if s:current_syntax == '' 617 let s:current_syntax = 'none' 618endif 619 620" Split window to create a buffer with the HTML file. 621let s:orgbufnr = winbufnr(0) 622let s:origwin_stl = &l:stl 623if expand("%") == "" 624 if exists('g:html_diff_win_num') 625 exec 'new Untitled_win'.g:html_diff_win_num.'.'.(s:settings.use_xhtml ? 'x' : '').'html' 626 else 627 exec 'new Untitled.'.(s:settings.use_xhtml ? 'x' : '').'html' 628 endif 629else 630 exec 'new %.'.(s:settings.use_xhtml ? 'x' : '').'html' 631endif 632 633" Resize the new window to very small in order to make it draw faster 634let s:old_winheight = winheight(0) 635let s:old_winfixheight = &l:winfixheight 636if s:old_winheight > 2 637 resize 1 " leave enough room to view one line at a time 638 norm! G 639 norm! zt 640endif 641setlocal winfixheight 642 643let s:newwin_stl = &l:stl 644 645" on the new window, set the least time-consuming fold method 646let s:old_fen = &foldenable 647setlocal foldmethod=manual 648setlocal nofoldenable 649 650let s:newwin = winnr() 651let s:orgwin = bufwinnr(s:orgbufnr) 652 653setlocal modifiable 654%d 655let s:old_paste = &paste 656set paste 657let s:old_magic = &magic 658set magic 659 660" set the fileencoding to match the charset we'll be using 661let &l:fileencoding=s:settings.vim_encoding 662 663" According to http://www.w3.org/TR/html4/charset.html#doc-char-set, the byte 664" order mark is highly recommend on the web when using multibyte encodings. But, 665" it is not a good idea to include it on UTF-8 files. Otherwise, let Vim 666" determine when it is actually inserted. 667if s:settings.vim_encoding == 'utf-8' 668 setlocal nobomb 669else 670 setlocal bomb 671endif 672 673let s:lines = [] 674 675if s:settings.use_xhtml 676 if s:settings.encoding != "" 677 call add(s:lines, "<?xml version=\"1.0\" encoding=\"" . s:settings.encoding . "\"?>") 678 else 679 call add(s:lines, "<?xml version=\"1.0\"?>") 680 endif 681 let s:tag_close = ' />' 682else 683 let s:tag_close = '>' 684endif 685 686let s:HtmlSpace = ' ' 687let s:LeadingSpace = ' ' 688let s:HtmlEndline = '' 689if s:settings.no_pre 690 let s:HtmlEndline = '<br' . s:tag_close 691 let s:LeadingSpace = s:settings.use_xhtml ? ' ' : ' ' 692 let s:HtmlSpace = '\' . s:LeadingSpace 693endif 694 695" HTML header, with the title and generator ;-). Left free space for the CSS, 696" to be filled at the end. 697call extend(s:lines, [ 698 \ "<html>", 699 \ "<head>"]) 700" include encoding as close to the top as possible, but only if not already 701" contained in XML information (to avoid haggling over content type) 702if s:settings.encoding != "" && !s:settings.use_xhtml 703 call add(s:lines, "<meta http-equiv=\"content-type\" content=\"text/html; charset=" . s:settings.encoding . '"' . s:tag_close) 704endif 705call extend(s:lines, [ 706 \ ("<title>".expand("%:p:~")."</title>"), 707 \ ("<meta name=\"Generator\" content=\"Vim/".v:version/100.".".v:version%100.'"'.s:tag_close), 708 \ ("<meta name=\"plugin-version\" content=\"".g:loaded_2html_plugin.'"'.s:tag_close) 709 \ ]) 710call add(s:lines, '<meta name="syntax" content="'.s:current_syntax.'"'.s:tag_close) 711call add(s:lines, '<meta name="settings" content="'. 712 \ join(filter(keys(s:settings),'s:settings[v:val]'),','). 713 \ ',prevent_copy='.s:settings.prevent_copy. 714 \ '"'.s:tag_close) 715call add(s:lines, '<meta name="colorscheme" content="'. 716 \ (exists('g:colors_name') 717 \ ? g:colors_name 718 \ : 'none'). '"'.s:tag_close) 719 720if s:settings.use_css 721 if s:settings.dynamic_folds 722 if s:settings.hover_unfold 723 " if we are doing hover_unfold, use css 2 with css 1 fallback for IE6 724 call extend(s:lines, [ 725 \ "<style type=\"text/css\">", 726 \ s:settings.use_xhtml ? "" : "<!--", 727 \ ".FoldColumn { text-decoration: none; white-space: pre; }", 728 \ "", 729 \ "body * { margin: 0; padding: 0; }", "", 730 \ ".open-fold > .Folded { display: none; }", 731 \ ".open-fold > .fulltext { display: inline; }", 732 \ ".closed-fold > .fulltext { display: none; }", 733 \ ".closed-fold > .Folded { display: inline; }", 734 \ "", 735 \ ".open-fold > .toggle-open { display: none; }", 736 \ ".open-fold > .toggle-closed { display: inline; }", 737 \ ".closed-fold > .toggle-open { display: inline; }", 738 \ ".closed-fold > .toggle-closed { display: none; }", 739 \ "", "", 740 \ '/* opening a fold while hovering won''t be supported by IE6 and other', 741 \ "similar browsers, but it should fail gracefully. */", 742 \ ".closed-fold:hover > .fulltext { display: inline; }", 743 \ ".closed-fold:hover > .toggle-filler { display: none; }", 744 \ ".closed-fold:hover > .Folded { display: none; }", 745 \ s:settings.use_xhtml ? "" : '-->', 746 \ '</style>']) 747 " TODO: IE7 doesn't *actually* support XHTML, maybe we should remove this. 748 " But if it's served up as tag soup, maybe the following will work, so 749 " leave it in for now. 750 call extend(s:lines, [ 751 \ "<!--[if lt IE 7]><style type=\"text/css\">", 752 \ ".open-fold .Folded { display: none; }", 753 \ ".open-fold .fulltext { display: inline; }", 754 \ ".open-fold .toggle-open { display: none; }", 755 \ ".closed-fold .toggle-closed { display: inline; }", 756 \ "", 757 \ ".closed-fold .fulltext { display: none; }", 758 \ ".closed-fold .Folded { display: inline; }", 759 \ ".closed-fold .toggle-open { display: inline; }", 760 \ ".closed-fold .toggle-closed { display: none; }", 761 \ "</style>", 762 \ "<![endif]-->", 763 \]) 764 else 765 " if we aren't doing hover_unfold, use CSS 1 only 766 call extend(s:lines, [ 767 \ "<style type=\"text/css\">", 768 \ s:settings.use_xhtml ? "" :"<!--", 769 \ ".FoldColumn { text-decoration: none; white-space: pre; }", 770 \ ".open-fold .Folded { display: none; }", 771 \ ".open-fold .fulltext { display: inline; }", 772 \ ".open-fold .toggle-open { display: none; }", 773 \ ".closed-fold .toggle-closed { display: inline; }", 774 \ "", 775 \ ".closed-fold .fulltext { display: none; }", 776 \ ".closed-fold .Folded { display: inline; }", 777 \ ".closed-fold .toggle-open { display: inline; }", 778 \ ".closed-fold .toggle-closed { display: none; }", 779 \ s:settings.use_xhtml ? "" : '-->', 780 \ '</style>' 781 \]) 782 endif 783 else 784 " if we aren't doing any dynamic folding, no need for any special rules 785 call extend(s:lines, [ 786 \ "<style type=\"text/css\">", 787 \ s:settings.use_xhtml ? "" : "<!--", 788 \ s:settings.use_xhtml ? "" : '-->', 789 \ "</style>", 790 \]) 791 endif 792endif 793 794" insert script tag; javascript is always needed for the line number 795" normalization for URL hashes 796call extend(s:lines, [ 797 \ "", 798 \ "<script type='text/javascript'>", 799 \ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"]) 800 801" insert javascript to toggle folds open and closed 802if s:settings.dynamic_folds 803 call extend(s:lines, [ 804 \ "", 805 \ "function toggleFold(objID)", 806 \ "{", 807 \ " var fold;", 808 \ " fold = document.getElementById(objID);", 809 \ " if(fold.className == 'closed-fold')", 810 \ " {", 811 \ " fold.className = 'open-fold';", 812 \ " }", 813 \ " else if (fold.className == 'open-fold')", 814 \ " {", 815 \ " fold.className = 'closed-fold';", 816 \ " }", 817 \ "}" 818 \ ]) 819endif 820 821" insert javascript to get IDs from line numbers, and to open a fold before 822" jumping to any lines contained therein 823call extend(s:lines, [ 824 \ "", 825 \ "/* function to open any folds containing a jumped-to line before jumping to it */", 826 \ "function JumpToLine()", 827 \ "{", 828 \ " var lineNum;", 829 \ " lineNum = window.location.hash;", 830 \ " lineNum = lineNum.substr(1); /* strip off '#' */", 831 \ "", 832 \ " if (lineNum.indexOf('L') == -1) {", 833 \ " lineNum = 'L'+lineNum;", 834 \ " }", 835 \ " lineElem = document.getElementById(lineNum);" 836 \ ]) 837if s:settings.dynamic_folds 838 call extend(s:lines, [ 839 \ "", 840 \ " /* navigate upwards in the DOM tree to open all folds containing the line */", 841 \ " var node = lineElem;", 842 \ " while (node && node.id != 'vimCodeElement')", 843 \ " {", 844 \ " if (node.className == 'closed-fold')", 845 \ " {", 846 \ " node.className = 'open-fold';", 847 \ " }", 848 \ " node = node.parentNode;", 849 \ " }", 850 \ ]) 851endif 852call extend(s:lines, [ 853 \ " /* Always jump to new location even if the line was hidden inside a fold, or", 854 \ " * we corrected the raw number to a line ID.", 855 \ " */", 856 \ " if (lineElem) {", 857 \ " lineElem.scrollIntoView(true);", 858 \ " }", 859 \ " return true;", 860 \ "}", 861 \ "if ('onhashchange' in window) {", 862 \ " window.onhashchange = JumpToLine;", 863 \ "}" 864 \ ]) 865 866" Small text columns like the foldcolumn and line number column need a weird 867" hack to work around Webkit's and (in versions prior to 9) IE's lack of support 868" for the 'ch' unit without messing up Opera, which also doesn't support it but 869" works anyway. 870" 871" The problem is that without the 'ch' unit, it is not possible to specify a 872" size of an <input> in terms of character widths. Only Opera seems to do the 873" "sensible" thing and make the <input> sized to fit exactly as many characters 874" as specified by its "size" attribute, but the spec actually says "at least 875" wide enough to fit 'size' characters", so the other browsers are technically 876" correct as well. 877" 878" Anyway, this leads to two diffculties: 879" 1. The foldcolumn is made up of multiple elements side-by-side with 880" different sizes, each of which has their own extra padding added. Thus, a 881" column made up of one item of size 1 and another of size 2 would not 882" necessarily be equal in size to another line's foldcolumn with a single 883" item of size 3. 884" 2. The extra padding added to the <input> elements adds up to make the 885" foldcolumn and line number column too wide, especially in Webkit 886" browsers. 887" 888" So, the full workaround is: 889" 1. Define a default size in em, equal to the number of characters in the 890" input element, in case javascript is disabled and the browser does not 891" support the 'ch' unit. Unfortunately this makes Opera no longer work 892" properly without javascript. 1em per character is much too wide but it 893" looks better in webkit browsers than unaligned columns. 894" 2. Insert the following javascript to run at page load, which checks for the 895" width of a single character (in an extraneous page element inserted 896" before the page title, and set to hidden) and compares it to the width of 897" another extra <input> element with only one character. If the width 898" matches, the script does nothing more, but if not, it will figure out the 899" fraction of an em unit which would correspond with a ch unit if there 900" were one, and set the containing element (<pre> or <div>) to a class with 901" pre-defined rules which is closest to that fraction of an em. Rules are 902" defined from 0.05 em to 1em per ch. 903if !empty(s:settings.prevent_copy) 904 call extend(s:lines, [ 905 \ '', 906 \ '/* simulate a "ch" unit by asking the browser how big a zero character is */', 907 \ 'function FixCharWidth() {', 908 \ ' /* get the hidden element which gives the width of a single character */', 909 \ ' var goodWidth = document.getElementById("oneCharWidth").clientWidth;', 910 \ ' /* get all input elements, we''ll filter on class later */', 911 \ ' var inputTags = document.getElementsByTagName("input");', 912 \ ' var ratio = 5;', 913 \ ' var inputWidth = document.getElementById("oneInputWidth").clientWidth;', 914 \ ' var emWidth = document.getElementById("oneEmWidth").clientWidth;', 915 \ ' if (inputWidth > goodWidth) {', 916 \ ' while (ratio < 100*goodWidth/emWidth && ratio < 100) {', 917 \ ' ratio += 5;', 918 \ ' }', 919 \ ' document.getElementById("vimCodeElement").className = "em"+ratio;', 920 \ ' }', 921 \ '}' 922 \ ]) 923endif 924 925" insert script closing tag 926call extend(s:lines, [ 927 \ '', 928 \ s:settings.use_xhtml ? '//]]>' : '-->', 929 \ "</script>" 930 \ ]) 931 932call extend(s:lines, ["</head>"]) 933if !empty(s:settings.prevent_copy) 934 call extend(s:lines, 935 \ ["<body onload='FixCharWidth(); JumpToLine();'>", 936 \ "<!-- hidden divs used by javascript to get the width of a char -->", 937 \ "<div id='oneCharWidth'>0</div>", 938 \ "<div id='oneInputWidth'><input size='1' value='0'".s:tag_close."</div>", 939 \ "<div id='oneEmWidth' style='width: 1em;'></div>" 940 \ ]) 941else 942 call extend(s:lines, ["<body onload='JumpToLine();'>"]) 943endif 944if s:settings.no_pre 945 " if we're not using CSS we use a font tag which can't have a div inside 946 if s:settings.use_css 947 call extend(s:lines, ["<div id='vimCodeElement'>"]) 948 endif 949else 950 call extend(s:lines, ["<pre id='vimCodeElement'>"]) 951endif 952 953exe s:orgwin . "wincmd w" 954 955" caches of style data 956" initialize to include line numbers if using them 957if s:settings.number_lines 958 let s:stylelist = { s:LINENR_ID : ".LineNr { " . s:CSS1( s:LINENR_ID ) . "}" } 959else 960 let s:stylelist = {} 961endif 962let s:diffstylelist = { 963 \ s:DIFF_A_ID : ".DiffAdd { " . s:CSS1( s:DIFF_A_ID ) . "}", 964 \ s:DIFF_C_ID : ".DiffChange { " . s:CSS1( s:DIFF_C_ID ) . "}", 965 \ s:DIFF_D_ID : ".DiffDelete { " . s:CSS1( s:DIFF_D_ID ) . "}", 966 \ s:DIFF_T_ID : ".DiffText { " . s:CSS1( s:DIFF_T_ID ) . "}" 967 \ } 968 969" set up progress bar in the status line 970if !s:settings.no_progress 971 " ProgressBar Indicator 972 let s:progressbar={} 973 974 " Progessbar specific functions 975 func! s:ProgressBar(title, max_value, winnr) 976 let pgb=copy(s:progressbar) 977 let pgb.title = a:title.' ' 978 let pgb.max_value = a:max_value 979 let pgb.winnr = a:winnr 980 let pgb.cur_value = 0 981 let pgb.items = { 'title' : { 'color' : 'Statusline' }, 982 \'bar' : { 'color' : 'Statusline' , 'fillcolor' : 'DiffDelete' , 'bg' : 'Statusline' } , 983 \'counter' : { 'color' : 'Statusline' } } 984 let pgb.last_value = 0 985 let pgb.needs_redraw = 0 986 " Note that you must use len(split) instead of len() if you want to use 987 " unicode in title. 988 " 989 " Subtract 3 for spacing around the title. 990 " Subtract 4 for the percentage display. 991 " Subtract 2 for spacing before this. 992 " Subtract 2 more for the '|' on either side of the progress bar 993 let pgb.subtractedlen=len(split(pgb.title, '\zs'))+3+4+2+2 994 let pgb.max_len = 0 995 set laststatus=2 996 return pgb 997 endfun 998 999 " Function: progressbar.calculate_ticks() {{{1 1000 func! s:progressbar.calculate_ticks(pb_len) 1001 if a:pb_len<=0 1002 let pb_len = 100 1003 else 1004 let pb_len = a:pb_len 1005 endif 1006 let self.progress_ticks = map(range(pb_len+1), "v:val * self.max_value / pb_len") 1007 endfun 1008 1009 "Function: progressbar.paint() 1010 func! s:progressbar.paint() 1011 " Recalculate widths. 1012 let max_len = winwidth(self.winnr) 1013 let pb_len = 0 1014 " always true on first call because of initial value of self.max_len 1015 if max_len != self.max_len 1016 let self.max_len = max_len 1017 1018 " Progressbar length 1019 let pb_len = max_len - self.subtractedlen 1020 1021 call self.calculate_ticks(pb_len) 1022 1023 let self.needs_redraw = 1 1024 let cur_value = 0 1025 let self.pb_len = pb_len 1026 else 1027 " start searching at the last found index to make the search for the 1028 " appropriate tick value normally take 0 or 1 comparisons 1029 let cur_value = self.last_value 1030 let pb_len = self.pb_len 1031 endif 1032 1033 let cur_val_max = pb_len > 0 ? pb_len : 100 1034 1035 " find the current progress bar position based on precalculated thresholds 1036 while cur_value < cur_val_max && self.cur_value > self.progress_ticks[cur_value] 1037 let cur_value += 1 1038 endwhile 1039 1040 " update progress bar 1041 if self.last_value != cur_value || self.needs_redraw || self.cur_value == self.max_value 1042 let self.needs_redraw = 1 1043 let self.last_value = cur_value 1044 1045 let t_color = self.items.title.color 1046 let b_fcolor = self.items.bar.fillcolor 1047 let b_color = self.items.bar.color 1048 let c_color = self.items.counter.color 1049 1050 let stl = "%#".t_color."#%-( ".self.title." %)". 1051 \"%#".b_color."#". 1052 \(pb_len>0 ? 1053 \ ('|%#'.b_fcolor."#%-(".repeat(" ",cur_value)."%)". 1054 \ '%#'.b_color."#".repeat(" ",pb_len-cur_value)."|"): 1055 \ ('')). 1056 \"%=%#".c_color."#%( ".printf("%3.d ",100*self.cur_value/self.max_value)."%% %)" 1057 call setwinvar(self.winnr, '&stl', stl) 1058 endif 1059 endfun 1060 1061 func! s:progressbar.incr( ... ) 1062 let self.cur_value += (a:0 ? a:1 : 1) 1063 " if we were making a general-purpose progress bar, we'd need to limit to a 1064 " lower limit as well, but since we always increment with a positive value 1065 " in this script, we only need limit the upper value 1066 let self.cur_value = (self.cur_value > self.max_value ? self.max_value : self.cur_value) 1067 call self.paint() 1068 endfun 1069 " }}} 1070 if s:settings.dynamic_folds 1071 " to process folds we make two passes through each line 1072 let s:pgb = s:ProgressBar("Processing folds:", line('$')*2, s:orgwin) 1073 endif 1074endif 1075 1076" First do some preprocessing for dynamic folding. Do this for the entire file 1077" so we don't accidentally start within a closed fold or something. 1078let s:allfolds = [] 1079 1080if s:settings.dynamic_folds 1081 let s:lnum = 1 1082 let s:end = line('$') 1083 " save the fold text and set it to the default so we can find fold levels 1084 let s:foldtext_save = &foldtext 1085 setlocal foldtext& 1086 1087 " we will set the foldcolumn in the html to the greater of the maximum fold 1088 " level and the current foldcolumn setting 1089 let s:foldcolumn = &foldcolumn 1090 1091 " get all info needed to describe currently closed folds 1092 while s:lnum <= s:end 1093 if foldclosed(s:lnum) == s:lnum 1094 " default fold text has '+-' and then a number of dashes equal to fold 1095 " level, so subtract 2 from index of first non-dash after the dashes 1096 " in order to get the fold level of the current fold 1097 let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2 1098 " store fold info for later use 1099 let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"} 1100 call add(s:allfolds, s:newfold) 1101 " open the fold so we can find any contained folds 1102 execute s:lnum."foldopen" 1103 else 1104 if !s:settings.no_progress 1105 call s:pgb.incr() 1106 if s:pgb.needs_redraw 1107 redrawstatus 1108 let s:pgb.needs_redraw = 0 1109 endif 1110 endif 1111 let s:lnum = s:lnum + 1 1112 endif 1113 endwhile 1114 1115 " close all folds to get info for originally open folds 1116 silent! %foldclose! 1117 let s:lnum = 1 1118 1119 " the originally open folds will be all folds we encounter that aren't 1120 " already in the list of closed folds 1121 while s:lnum <= s:end 1122 if foldclosed(s:lnum) == s:lnum 1123 " default fold text has '+-' and then a number of dashes equal to fold 1124 " level, so subtract 2 from index of first non-dash after the dashes 1125 " in order to get the fold level of the current fold 1126 let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2 1127 let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"} 1128 " only add the fold if we don't already have it 1129 if empty(s:allfolds) || index(s:allfolds, s:newfold) == -1 1130 let s:newfold.type = "open-fold" 1131 call add(s:allfolds, s:newfold) 1132 endif 1133 " open the fold so we can find any contained folds 1134 execute s:lnum."foldopen" 1135 else 1136 if !s:settings.no_progress 1137 call s:pgb.incr() 1138 if s:pgb.needs_redraw 1139 redrawstatus 1140 let s:pgb.needs_redraw = 0 1141 endif 1142 endif 1143 let s:lnum = s:lnum + 1 1144 endif 1145 endwhile 1146 1147 " sort the folds so that we only ever need to look at the first item in the 1148 " list of folds 1149 call sort(s:allfolds, "s:FoldCompare") 1150 1151 let &l:foldtext = s:foldtext_save 1152 unlet s:foldtext_save 1153 1154 " close all folds again so we can get the fold text as we go 1155 silent! %foldclose! 1156 1157 " Go through and remove folds we don't need to (or cannot) process in the 1158 " current conversion range 1159 " 1160 " If a fold is removed which contains other folds, which are included, we need 1161 " to adjust the level of the included folds as used by the conversion logic 1162 " (avoiding special cases is good) 1163 " 1164 " Note any time we remove a fold, either all of the included folds are in it, 1165 " or none of them, because we only remove a fold if neither its start nor its 1166 " end are within the conversion range. 1167 let leveladjust = 0 1168 for afold in s:allfolds 1169 let removed = 0 1170 if exists("g:html_start_line") && exists("g:html_end_line") 1171 if afold.firstline < g:html_start_line 1172 if afold.lastline <= g:html_end_line && afold.lastline >= g:html_start_line 1173 " if a fold starts before the range to convert but stops within the 1174 " range, we need to include it. Make it start on the first converted 1175 " line. 1176 let afold.firstline = g:html_start_line 1177 else 1178 " if the fold lies outside the range or the start and stop enclose 1179 " the entire range, don't bother parsing it 1180 call remove(s:allfolds, index(s:allfolds, afold)) 1181 let removed = 1 1182 if afold.lastline > g:html_end_line 1183 let leveladjust += 1 1184 endif 1185 endif 1186 elseif afold.firstline > g:html_end_line 1187 " If the entire fold lies outside the range we need to remove it. 1188 call remove(s:allfolds, index(s:allfolds, afold)) 1189 let removed = 1 1190 endif 1191 elseif exists("g:html_start_line") 1192 if afold.firstline < g:html_start_line 1193 " if there is no last line, but there is a first line, the end of the 1194 " fold will always lie within the region of interest, so keep it 1195 let afold.firstline = g:html_start_line 1196 endif 1197 elseif exists("g:html_end_line") 1198 " if there is no first line we default to the first line in the buffer so 1199 " the fold start will always be included if the fold itself is included. 1200 " If however the entire fold lies outside the range we need to remove it. 1201 if afold.firstline > g:html_end_line 1202 call remove(s:allfolds, index(s:allfolds, afold)) 1203 let removed = 1 1204 endif 1205 endif 1206 if !removed 1207 let afold.level -= leveladjust 1208 if afold.level+1 > s:foldcolumn 1209 let s:foldcolumn = afold.level+1 1210 endif 1211 endif 1212 endfor 1213 1214 " if we've removed folds containing the conversion range from processing, 1215 " getting foldtext as we go won't know to open the removed folds, so the 1216 " foldtext would be wrong; open them now. 1217 " 1218 " Note that only when a start and an end line is specified will a fold 1219 " containing the current range ever be removed. 1220 while leveladjust > 0 1221 exe g:html_start_line."foldopen" 1222 let leveladjust -= 1 1223 endwhile 1224endif 1225 1226" Now loop over all lines in the original text to convert to html. 1227" Use html_start_line and html_end_line if they are set. 1228if exists("g:html_start_line") 1229 let s:lnum = html_start_line 1230 if s:lnum < 1 || s:lnum > line("$") 1231 let s:lnum = 1 1232 endif 1233else 1234 let s:lnum = 1 1235endif 1236if exists("g:html_end_line") 1237 let s:end = html_end_line 1238 if s:end < s:lnum || s:end > line("$") 1239 let s:end = line("$") 1240 endif 1241else 1242 let s:end = line("$") 1243endif 1244 1245" stack to keep track of all the folds containing the current line 1246let s:foldstack = [] 1247 1248if !s:settings.no_progress 1249 let s:pgb = s:ProgressBar("Processing lines:", s:end - s:lnum + 1, s:orgwin) 1250endif 1251 1252if s:settings.number_lines 1253 let s:margin = strlen(s:end) + 1 1254else 1255 let s:margin = 0 1256endif 1257 1258if has('folding') && !s:settings.ignore_folding 1259 let s:foldfillchar = &fillchars[matchend(&fillchars, 'fold:')] 1260 if s:foldfillchar == '' 1261 let s:foldfillchar = '-' 1262 endif 1263endif 1264let s:difffillchar = &fillchars[matchend(&fillchars, 'diff:')] 1265if s:difffillchar == '' 1266 let s:difffillchar = '-' 1267endif 1268 1269let s:foldId = 0 1270 1271if !s:settings.expand_tabs 1272 " If keeping tabs, add them to printable characters so we keep them when 1273 " formatting text (strtrans() doesn't replace printable chars) 1274 let s:old_isprint = &isprint 1275 setlocal isprint+=9 1276endif 1277 1278while s:lnum <= s:end 1279 1280 " If there are filler lines for diff mode, show these above the line. 1281 let s:filler = diff_filler(s:lnum) 1282 if s:filler > 0 1283 let s:n = s:filler 1284 while s:n > 0 1285 let s:new = repeat(s:difffillchar, 3) 1286 1287 if s:n > 2 && s:n < s:filler && !s:settings.whole_filler 1288 let s:new = s:new . " " . s:filler . " inserted lines " 1289 let s:n = 2 1290 endif 1291 1292 if !s:settings.no_pre 1293 " HTML line wrapping is off--go ahead and fill to the margin 1294 " TODO: what about when CSS wrapping is turned on? 1295 let s:new = s:new . repeat(s:difffillchar, &columns - strlen(s:new) - s:margin) 1296 else 1297 let s:new = s:new . repeat(s:difffillchar, 3) 1298 endif 1299 1300 let s:new = s:HtmlFormat_d(s:new, s:DIFF_D_ID, 0) 1301 if s:settings.number_lines 1302 " Indent if line numbering is on. Indent gets style of line number 1303 " column. 1304 let s:new = s:HtmlFormat_n(repeat(' ', s:margin), s:LINENR_ID, 0, 0) . s:new 1305 endif 1306 if s:settings.dynamic_folds && !s:settings.no_foldcolumn && s:foldcolumn > 0 1307 " Indent for foldcolumn if there is one. Assume it's empty, there should 1308 " not be a fold for deleted lines in diff mode. 1309 let s:new = s:FoldColumn_fill() . s:new 1310 endif 1311 call add(s:lines, s:new.s:HtmlEndline) 1312 1313 let s:n = s:n - 1 1314 endwhile 1315 unlet s:n 1316 endif 1317 unlet s:filler 1318 1319 " Start the line with the line number. 1320 if s:settings.number_lines 1321 let s:numcol = repeat(' ', s:margin - 1 - strlen(s:lnum)) . s:lnum . ' ' 1322 endif 1323 1324 let s:new = "" 1325 1326 if has('folding') && !s:settings.ignore_folding && foldclosed(s:lnum) > -1 && !s:settings.dynamic_folds 1327 " 1328 " This is the beginning of a folded block (with no dynamic folding) 1329 let s:new = foldtextresult(s:lnum) 1330 if !s:settings.no_pre 1331 " HTML line wrapping is off--go ahead and fill to the margin 1332 let s:new = s:new . repeat(s:foldfillchar, &columns - strlen(s:new)) 1333 endif 1334 1335 " put numcol in a separate group for sake of unselectable text 1336 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) 1337 1338 " Skip to the end of the fold 1339 let s:new_lnum = foldclosedend(s:lnum) 1340 1341 if !s:settings.no_progress 1342 call s:pgb.incr(s:new_lnum - s:lnum) 1343 endif 1344 1345 let s:lnum = s:new_lnum 1346 1347 else 1348 " 1349 " A line that is not folded, or doing dynamic folding. 1350 " 1351 let s:line = getline(s:lnum) 1352 let s:len = strlen(s:line) 1353 1354 if s:settings.dynamic_folds 1355 " First insert a closing for any open folds that end on this line 1356 while !empty(s:foldstack) && get(s:foldstack,0).lastline == s:lnum-1 1357 let s:new = s:new."</span></span>" 1358 call remove(s:foldstack, 0) 1359 endwhile 1360 1361 " Now insert an opening for any new folds that start on this line 1362 let s:firstfold = 1 1363 while !empty(s:allfolds) && get(s:allfolds,0).firstline == s:lnum 1364 let s:foldId = s:foldId + 1 1365 let s:new .= "<span id='" 1366 let s:new .= (exists('g:html_diff_win_num') ? "win".g:html_diff_win_num : "") 1367 let s:new .= "fold".s:foldId."' class='".s:allfolds[0].type."'>" 1368 1369 1370 " Unless disabled, add a fold column for the opening line of a fold. 1371 " 1372 " Note that dynamic folds require using css so we just use css to take 1373 " care of the leading spaces rather than using in the case of 1374 " html_no_pre to make it easier 1375 if !s:settings.no_foldcolumn 1376 " add fold column that can open the new fold 1377 if s:allfolds[0].level > 1 && s:firstfold 1378 let s:new = s:new . s:FoldColumn_build('|', s:allfolds[0].level - 1, 0, "", 1379 \ 'toggle-open FoldColumn','javascript:toggleFold("fold'.s:foldstack[0].id.'");') 1380 endif 1381 " add the filler spaces separately from the '+' char so that it can be 1382 " shown/hidden separately during a hover unfold 1383 let s:new = s:new . s:FoldColumn_build("+", 1, 0, "", 1384 \ 'toggle-open FoldColumn', 'javascript:toggleFold("fold'.s:foldId.'");') 1385 " If this is not the last fold we're opening on this line, we need 1386 " to keep the filler spaces hidden if the fold is opened by mouse 1387 " hover. If it is the last fold to open in the line, we shouldn't hide 1388 " them, so don't apply the toggle-filler class. 1389 let s:new = s:new . s:FoldColumn_build(" ", 1, s:foldcolumn - s:allfolds[0].level - 1, "", 1390 \ 'toggle-open FoldColumn'. (get(s:allfolds, 1, {'firstline': 0}).firstline == s:lnum ?" toggle-filler" :""), 1391 \ 'javascript:toggleFold("fold'.s:foldId.'");') 1392 1393 " add fold column that can close the new fold 1394 " only add extra blank space if we aren't opening another fold on the 1395 " same line 1396 if get(s:allfolds, 1, {'firstline': 0}).firstline != s:lnum 1397 let s:extra_space = s:foldcolumn - s:allfolds[0].level 1398 else 1399 let s:extra_space = 0 1400 endif 1401 if s:firstfold 1402 " the first fold in a line has '|' characters from folds opened in 1403 " previous lines, before the '-' for this fold 1404 let s:new .= s:FoldColumn_build('|', s:allfolds[0].level - 1, s:extra_space, '-', 1405 \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'.s:foldId.'");') 1406 else 1407 " any subsequent folds in the line only add a single '-' 1408 let s:new = s:new . s:FoldColumn_build("-", 1, s:extra_space, "", 1409 \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'.s:foldId.'");') 1410 endif 1411 let s:firstfold = 0 1412 endif 1413 1414 " Add fold text, moving the span ending to the next line so collapsing 1415 " of folds works correctly. 1416 " Put numcol in a separate group for sake of unselectable text. 1417 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', '') 1418 let s:new = s:new . "<span class='fulltext'>" 1419 1420 " open the fold now that we have the fold text to allow retrieval of 1421 " fold text for subsequent folds 1422 execute s:lnum."foldopen" 1423 call insert(s:foldstack, remove(s:allfolds,0)) 1424 let s:foldstack[0].id = s:foldId 1425 endwhile 1426 1427 " Unless disabled, add a fold column for other lines. 1428 " 1429 " Note that dynamic folds require using css so we just use css to take 1430 " care of the leading spaces rather than using in the case of 1431 " html_no_pre to make it easier 1432 if !s:settings.no_foldcolumn 1433 if empty(s:foldstack) 1434 " add the empty foldcolumn for unfolded lines if there is a fold 1435 " column at all 1436 if s:foldcolumn > 0 1437 let s:new = s:new . s:FoldColumn_fill() 1438 endif 1439 else 1440 " add the fold column for folds not on the opening line 1441 if get(s:foldstack, 0).firstline < s:lnum 1442 let s:new = s:new . s:FoldColumn_build('|', s:foldstack[0].level, s:foldcolumn - s:foldstack[0].level, "", 1443 \ 'FoldColumn', 'javascript:toggleFold("fold'.s:foldstack[0].id.'");') 1444 endif 1445 endif 1446 endif 1447 endif 1448 1449 " Now continue with the unfolded line text 1450 if s:settings.number_lines 1451 let s:new = s:new . s:HtmlFormat_n(s:numcol, s:LINENR_ID, 0, s:lnum) 1452 else 1453 let s:new = s:new . s:HtmlFormat_n("", s:LINENR_ID, 0, s:lnum) 1454 endif 1455 1456 " Get the diff attribute, if any. 1457 let s:diffattr = diff_hlID(s:lnum, 1) 1458 1459 " initialize conceal info to act like not concealed, just in case 1460 let s:concealinfo = [0, ''] 1461 1462 " Loop over each character in the line 1463 let s:col = 1 1464 1465 " most of the time we won't use the diff_id, initialize to zero 1466 let s:diff_id = 0 1467 1468 while s:col <= s:len || (s:col == 1 && s:diffattr) 1469 let s:startcol = s:col " The start column for processing text 1470 if !s:settings.ignore_conceal && has('conceal') 1471 let s:concealinfo = synconcealed(s:lnum, s:col) 1472 endif 1473 if !s:settings.ignore_conceal && s:concealinfo[0] 1474 let s:col = s:col + 1 1475 " Speed loop (it's small - that's the trick) 1476 " Go along till we find a change in the match sequence number (ending 1477 " the specific concealed region) or until there are no more concealed 1478 " characters. 1479 while s:col <= s:len && s:concealinfo == synconcealed(s:lnum, s:col) | let s:col = s:col + 1 | endwhile 1480 elseif s:diffattr 1481 let s:diff_id = diff_hlID(s:lnum, s:col) 1482 let s:id = synID(s:lnum, s:col, 1) 1483 let s:col = s:col + 1 1484 " Speed loop (it's small - that's the trick) 1485 " Go along till we find a change in hlID 1486 while s:col <= s:len && s:id == synID(s:lnum, s:col, 1) 1487 \ && s:diff_id == diff_hlID(s:lnum, s:col) | 1488 \ let s:col = s:col + 1 | 1489 \ endwhile 1490 if s:len < &columns && !s:settings.no_pre 1491 " Add spaces at the end of the raw text line to extend the changed 1492 " line to the full width. 1493 let s:line = s:line . repeat(' ', &columns - virtcol([s:lnum, s:len]) - s:margin) 1494 let s:len = &columns 1495 endif 1496 else 1497 let s:id = synID(s:lnum, s:col, 1) 1498 let s:col = s:col + 1 1499 " Speed loop (it's small - that's the trick) 1500 " Go along till we find a change in synID 1501 while s:col <= s:len && s:id == synID(s:lnum, s:col, 1) | let s:col = s:col + 1 | endwhile 1502 endif 1503 1504 if s:settings.ignore_conceal || !s:concealinfo[0] 1505 " Expand tabs if needed 1506 let s:expandedtab = strpart(s:line, s:startcol - 1, s:col - s:startcol) 1507 if s:settings.expand_tabs 1508 let s:offset = 0 1509 let s:idx = stridx(s:expandedtab, "\t") 1510 while s:idx >= 0 1511 if has("multi_byte_encoding") 1512 if s:startcol + s:idx == 1 1513 let s:i = &ts 1514 else 1515 if s:idx == 0 1516 let s:prevc = matchstr(s:line, '.\%' . (s:startcol + s:idx + s:offset) . 'c') 1517 else 1518 let s:prevc = matchstr(s:expandedtab, '.\%' . (s:idx + 1) . 'c') 1519 endif 1520 let s:vcol = virtcol([s:lnum, s:startcol + s:idx + s:offset - len(s:prevc)]) 1521 let s:i = &ts - (s:vcol % &ts) 1522 endif 1523 let s:offset -= s:i - 1 1524 else 1525 let s:i = &ts - ((s:idx + s:startcol - 1) % &ts) 1526 endif 1527 let s:expandedtab = substitute(s:expandedtab, '\t', repeat(' ', s:i), '') 1528 let s:idx = stridx(s:expandedtab, "\t") 1529 endwhile 1530 end 1531 1532 " get the highlight group name to use 1533 let s:id = synIDtrans(s:id) 1534 else 1535 " use Conceal highlighting for concealed text 1536 let s:id = s:CONCEAL_ID 1537 let s:expandedtab = s:concealinfo[1] 1538 endif 1539 1540 " Output the text with the same synID, with class set to the highlight ID 1541 " name, unless it has been concealed completely. 1542 if strlen(s:expandedtab) > 0 1543 let s:new = s:new . s:HtmlFormat(s:expandedtab, s:id, s:diff_id, "", 0) 1544 endif 1545 endwhile 1546 endif 1547 1548 call extend(s:lines, split(s:new.s:HtmlEndline, '\n', 1)) 1549 if !s:settings.no_progress && s:pgb.needs_redraw 1550 redrawstatus 1551 let s:pgb.needs_redraw = 0 1552 endif 1553 let s:lnum = s:lnum + 1 1554 1555 if !s:settings.no_progress 1556 call s:pgb.incr() 1557 endif 1558endwhile 1559 1560if s:settings.dynamic_folds 1561 " finish off any open folds 1562 while !empty(s:foldstack) 1563 let s:lines[-1].="</span></span>" 1564 call remove(s:foldstack, 0) 1565 endwhile 1566 1567 " add fold column to the style list if not already there 1568 let s:id = s:FOLD_C_ID 1569 if !has_key(s:stylelist, s:id) 1570 let s:stylelist[s:id] = '.FoldColumn { ' . s:CSS1(s:id) . '}' 1571 endif 1572endif 1573 1574if s:settings.no_pre 1575 if !s:settings.use_css 1576 " Close off the font tag that encapsulates the whole <body> 1577 call extend(s:lines, ["</font>", "</body>", "</html>"]) 1578 else 1579 call extend(s:lines, ["</div>", "</body>", "</html>"]) 1580 endif 1581else 1582 call extend(s:lines, ["</pre>", "</body>", "</html>"]) 1583endif 1584 1585exe s:newwin . "wincmd w" 1586call setline(1, s:lines) 1587unlet s:lines 1588 1589" Mangle modelines so Vim doesn't try to use HTML text as a modeline if editing 1590" this file in the future; need to do this after generating all the text in case 1591" the modeline text has different highlight groups which all turn out to be 1592" stripped from the final output. 1593%s!\v(%(^|\s+)%(vim?|ex)):!\1\:!ge 1594 1595" The generated HTML is admittedly ugly and takes a LONG time to fold. 1596" Make sure the user doesn't do syntax folding when loading a generated file, 1597" using a modeline. 1598call append(line('$'), "<!-- vim: set foldmethod=manual : -->") 1599 1600" Now, when we finally know which, we define the colors and styles 1601if s:settings.use_css 1602 1;/<style type="text/+1 1603endif 1604 1605" Normal/global attributes 1606" For Netscape 4, set <body> attributes too, though, strictly speaking, it's 1607" incorrect. 1608if s:settings.use_css 1609 if s:settings.no_pre 1610 call append('.', "body { color: " . s:fgc . "; background-color: " . s:bgc . "; font-family: ". s:htmlfont ."; }") 1611 + 1612 else 1613 call append('.', "pre { " . s:whitespace . "font-family: ". s:htmlfont ."; color: " . s:fgc . "; background-color: " . s:bgc . "; }") 1614 + 1615 yank 1616 put 1617 execute "normal! ^cwbody\e" 1618 " body should not have the wrap formatting, only the pre section 1619 if s:whitespace != '' 1620 exec 's#'.s:whitespace 1621 endif 1622 endif 1623 " fix browser inconsistencies (sometimes within the same browser) of different 1624 " default font size for different elements 1625 call append('.', '* { font-size: 1em; }') 1626 + 1627 " if we use any input elements for unselectable content, make sure they look 1628 " like normal text 1629 if !empty(s:settings.prevent_copy) 1630 call append('.', 'input { border: none; margin: 0; padding: 0; font-family: '.s:htmlfont.'; }') 1631 + 1632 " ch units for browsers which support them, em units for a somewhat 1633 " reasonable fallback. Also make sure the special elements for size 1634 " calculations aren't seen. 1635 call append('.', [ 1636 \ "input[size='1'] { width: 1em; width: 1ch; }", 1637 \ "input[size='2'] { width: 2em; width: 2ch; }", 1638 \ "input[size='3'] { width: 3em; width: 3ch; }", 1639 \ "input[size='4'] { width: 4em; width: 4ch; }", 1640 \ "input[size='5'] { width: 5em; width: 5ch; }", 1641 \ "input[size='6'] { width: 6em; width: 6ch; }", 1642 \ "input[size='7'] { width: 7em; width: 7ch; }", 1643 \ "input[size='8'] { width: 8em; width: 8ch; }", 1644 \ "input[size='9'] { width: 9em; width: 9ch; }", 1645 \ "input[size='10'] { width: 10em; width: 10ch; }", 1646 \ "input[size='11'] { width: 11em; width: 11ch; }", 1647 \ "input[size='12'] { width: 12em; width: 12ch; }", 1648 \ "input[size='13'] { width: 13em; width: 13ch; }", 1649 \ "input[size='14'] { width: 14em; width: 14ch; }", 1650 \ "input[size='15'] { width: 15em; width: 15ch; }", 1651 \ "input[size='16'] { width: 16em; width: 16ch; }", 1652 \ "input[size='17'] { width: 17em; width: 17ch; }", 1653 \ "input[size='18'] { width: 18em; width: 18ch; }", 1654 \ "input[size='19'] { width: 19em; width: 19ch; }", 1655 \ "input[size='20'] { width: 20em; width: 20ch; }", 1656 \ "#oneCharWidth, #oneEmWidth, #oneInputWidth { padding: 0; margin: 0; position: absolute; left: -999999px; visibility: hidden; }" 1657 \ ]) 1658 +21 1659 for w in range(5, 100, 5) 1660 let base = 0.01 * w 1661 call append('.', join(map(range(1,20), "'.em'.w.' input[size='''.v:val.'''] { width: '.string(v:val*base).'em; }'"))) 1662 + 1663 endfor 1664 if s:settings.prevent_copy =~# 'f' 1665 " Make the cursor show active fold columns as active areas, and empty fold 1666 " columns as not interactive. 1667 call append('.', ['input.FoldColumn { cursor: pointer; }', 1668 \ 'input.FoldColumn[value=""] { cursor: default; }' 1669 \ ]) 1670 +2 1671 endif 1672 " make line number column show as non-interactive if not selectable 1673 if s:settings.prevent_copy =~# 'n' 1674 call append('.', 'input.LineNr { cursor: default; }') 1675 + 1676 endif 1677 " make fold text and line number column within fold text show as 1678 " non-interactive if not selectable 1679 if (s:settings.prevent_copy =~# 'n' || s:settings.prevent_copy =~# 't') && !s:settings.ignore_folding 1680 call append('.', 'input.Folded { cursor: default; }') 1681 + 1682 endif 1683 endif 1684else 1685 execute '%s:<body\([^>]*\):<body bgcolor="' . s:bgc . '" text="' . s:fgc . '"\1>\r<font face="'. s:htmlfont .'"' 1686endif 1687 1688" Gather attributes for all other classes. Do diff first so that normal 1689" highlight groups are inserted before it. 1690if s:settings.use_css 1691 if s:diff_mode 1692 call append('.', filter(map(keys(s:diffstylelist), "s:diffstylelist[v:val]"), 'v:val != ""')) 1693 endif 1694 if !empty(s:stylelist) 1695 call append('.', filter(map(keys(s:stylelist), "s:stylelist[v:val]"), 'v:val != ""')) 1696 endif 1697endif 1698 1699" Add hyperlinks 1700" TODO: add option to not do this? Maybe just make the color the same as the 1701" text highlight group normally is? 1702%s+\(https\=://\S\{-}\)\(\([.,;:}]\=\(\s\|$\)\)\|[\\"'<>]\|>\|<\|"\)+<a href="\1">\1</a>\2+ge 1703 1704" The DTD 1705if s:settings.use_xhtml 1706 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\">" 1707elseif s:settings.use_css && !s:settings.no_pre 1708 exe "normal! gg0i<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n" 1709else 1710 exe "normal! gg0i<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n" 1711endif 1712 1713if s:settings.use_xhtml 1714 exe "normal! gg/<html/e\na xmlns=\"http://www.w3.org/1999/xhtml\"\e" 1715endif 1716 1717" Cleanup 1718%s:\s\+$::e 1719 1720" Restore old settings (new window first) 1721" 1722" Don't bother restoring foldmethod in case it was syntax because the markup is 1723" so weirdly formatted it can take a LONG time. 1724let &l:foldenable = s:old_fen 1725let &report = s:old_report 1726let &title = s:old_title 1727let &icon = s:old_icon 1728let &paste = s:old_paste 1729let &magic = s:old_magic 1730let @/ = s:old_search 1731let &more = s:old_more 1732 1733" switch to original window to restore those settings 1734exe s:orgwin . "wincmd w" 1735 1736if !s:settings.expand_tabs 1737 let &l:isprint = s:old_isprint 1738endif 1739let &l:stl = s:origwin_stl 1740let &l:et = s:old_et 1741let &l:scrollbind = s:old_bind 1742 1743" and back to the new window again to end there 1744exe s:newwin . "wincmd w" 1745 1746let &l:stl = s:newwin_stl 1747exec 'resize' s:old_winheight 1748let &l:winfixheight = s:old_winfixheight 1749 1750let &ls=s:ls 1751 1752" Save a little bit of memory (worth doing?) 1753unlet s:htmlfont s:whitespace 1754unlet s:old_et s:old_paste s:old_icon s:old_report s:old_title s:old_search 1755unlet s:old_magic s:old_more s:old_fen s:old_winheight 1756unlet! s:old_isprint 1757unlet s:whatterm s:stylelist s:diffstylelist s:lnum s:end s:margin s:fgc s:bgc s:old_winfixheight 1758unlet! s:col s:id s:attr s:len s:line s:new s:expandedtab s:concealinfo s:diff_mode 1759unlet! s:orgwin s:newwin s:orgbufnr s:idx s:i s:offset s:ls s:origwin_stl 1760unlet! s:newwin_stl s:current_syntax 1761if !v:profiling 1762 delfunc s:HtmlColor 1763 delfunc s:HtmlFormat 1764 delfunc s:CSS1 1765 delfunc s:BuildStyleWrapper 1766 if !s:settings.use_css 1767 delfunc s:HtmlOpening 1768 delfunc s:HtmlClosing 1769 endif 1770 if s:settings.dynamic_folds 1771 delfunc s:FoldCompare 1772 endif 1773 1774 if !s:settings.no_progress 1775 delfunc s:ProgressBar 1776 delfunc s:progressbar.paint 1777 delfunc s:progressbar.incr 1778 unlet s:pgb s:progressbar 1779 endif 1780endif 1781 1782unlet! s:new_lnum s:diffattr s:difffillchar s:foldfillchar s:HtmlSpace 1783unlet! s:LeadingSpace s:HtmlEndline s:firstfold s:numcol s:foldcolumn 1784unlet s:foldstack s:allfolds s:foldId s:settings 1785 1786let &cpo = s:cpo_sav 1787unlet! s:cpo_sav 1788 1789" Make sure any patches will probably use consistent indent 1790" vim: ts=8 sw=2 sts=2 noet 1791