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