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