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