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