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