xref: /vim-8.2.3635/runtime/syntax/2html.vim (revision 60cce2fb)
1071d4279SBram Moolenaar" Vim syntax support file
2349b2fb0SBram Moolenaar" Maintainer: Ben Fritz <[email protected]>
3*60cce2fbSBram Moolenaar" Last Change: 2015 Sep 08
4349b2fb0SBram Moolenaar"
5349b2fb0SBram Moolenaar" Additional contributors:
6349b2fb0SBram Moolenaar"
7349b2fb0SBram Moolenaar"             Original by Bram Moolenaar <[email protected]>
8349b2fb0SBram Moolenaar"             Modified by David Ne\v{c}as (Yeti) <[email protected]>
9349b2fb0SBram Moolenaar"             XHTML support by Panagiotis Issaris <[email protected]>
10349b2fb0SBram Moolenaar"             Made w3 compliant by Edd Barrett <[email protected]>
11349b2fb0SBram Moolenaar"             Added html_font. Edd Barrett <[email protected]>
12349b2fb0SBram Moolenaar"             Progress bar based off code from "progressbar widget" plugin by
13349b2fb0SBram Moolenaar"               Andreas Politz, heavily modified:
14349b2fb0SBram Moolenaar"               http://www.vim.org/scripts/script.php?script_id=2006
15349b2fb0SBram Moolenaar"
16349b2fb0SBram Moolenaar"             See Mercurial change logs for more!
17071d4279SBram Moolenaar
18071d4279SBram Moolenaar" Transform a file into HTML, using the current syntax highlighting.
19071d4279SBram Moolenaar
205c73622aSBram Moolenaar" this file uses line continuations
215c73622aSBram Moolenaarlet s:cpo_sav = &cpo
22349b2fb0SBram Moolenaarlet s:ls  = &ls
236c35beaaSBram Moolenaarset cpo&vim
245c73622aSBram Moolenaar
25349b2fb0SBram Moolenaarlet s:end=line('$')
26349b2fb0SBram Moolenaar
27313b7237SBram Moolenaar" Font
28b02cbe34SBram Moolenaarif exists("g:html_font")
29*60cce2fbSBram Moolenaar  if type(g:html_font) == type([])
30*60cce2fbSBram Moolenaar    let s:htmlfont = "'". join(g:html_font,"','") . "', monospace"
31*60cce2fbSBram Moolenaar  else
32076e8b2aSBram Moolenaar    let s:htmlfont = "'". g:html_font . "', monospace"
33*60cce2fbSBram Moolenaar  endif
34313b7237SBram Moolenaarelse
35313b7237SBram Moolenaar  let s:htmlfont = "monospace"
36313b7237SBram Moolenaarendif
37313b7237SBram Moolenaar
38076e8b2aSBram Moolenaarlet s:settings = tohtml#GetUserSettings()
395c73622aSBram Moolenaar
406c35beaaSBram Moolenaarif !exists('s:FOLDED_ID')
416c35beaaSBram Moolenaar  let s:FOLDED_ID  = hlID("Folded")     | lockvar s:FOLDED_ID
426c35beaaSBram Moolenaar  let s:FOLD_C_ID  = hlID("FoldColumn") | lockvar s:FOLD_C_ID
436c35beaaSBram Moolenaar  let s:LINENR_ID  = hlID('LineNr')     | lockvar s:LINENR_ID
446c35beaaSBram Moolenaar  let s:DIFF_D_ID  = hlID("DiffDelete") | lockvar s:DIFF_D_ID
456c35beaaSBram Moolenaar  let s:DIFF_A_ID  = hlID("DiffAdd")    | lockvar s:DIFF_A_ID
466c35beaaSBram Moolenaar  let s:DIFF_C_ID  = hlID("DiffChange") | lockvar s:DIFF_C_ID
476c35beaaSBram Moolenaar  let s:DIFF_T_ID  = hlID("DiffText")   | lockvar s:DIFF_T_ID
486c35beaaSBram Moolenaar  let s:CONCEAL_ID = hlID('Conceal')    | lockvar s:CONCEAL_ID
496c35beaaSBram Moolenaarendif
506c35beaaSBram Moolenaar
518e5af3e5SBram Moolenaar" Whitespace
528e5af3e5SBram Moolenaarif s:settings.pre_wrap
538e5af3e5SBram Moolenaar  let s:whitespace = "white-space: pre-wrap; "
548e5af3e5SBram Moolenaarelse
558e5af3e5SBram Moolenaar  let s:whitespace = ""
568e5af3e5SBram Moolenaarendif
578e5af3e5SBram Moolenaar
586c35beaaSBram Moolenaarif !empty(s:settings.prevent_copy)
596c35beaaSBram Moolenaar  if s:settings.no_invalid
606c35beaaSBram Moolenaar    " User has decided they don't want invalid markup. Still works in
616c35beaaSBram Moolenaar    " OpenOffice, and for text editors, but when pasting into Microsoft Word the
626c35beaaSBram Moolenaar    " input elements get pasted too and they cannot be deleted (at least not
636c35beaaSBram Moolenaar    " easily).
646c35beaaSBram Moolenaar    let s:unselInputType = ""
656c35beaaSBram Moolenaar  else
666c35beaaSBram Moolenaar    " Prevent from copy-pasting the input elements into Microsoft Word where
676c35beaaSBram Moolenaar    " they cannot be deleted easily by deliberately inserting invalid markup.
686c35beaaSBram Moolenaar    let s:unselInputType = " type='invalid_input_type'"
696c35beaaSBram Moolenaar  endif
706c35beaaSBram Moolenaarendif
716c35beaaSBram Moolenaar
72071d4279SBram Moolenaar" When not in gui we can only guess the colors.
736c35beaaSBram Moolenaar" TODO - is this true anymore?
74071d4279SBram Moolenaarif has("gui_running")
75071d4279SBram Moolenaar  let s:whatterm = "gui"
76071d4279SBram Moolenaarelse
77071d4279SBram Moolenaar  let s:whatterm = "cterm"
78071d4279SBram Moolenaar  if &t_Co == 8
796c35beaaSBram Moolenaar    let s:cterm_color = {
806c35beaaSBram Moolenaar	    \   0: "#808080", 1: "#ff6060", 2: "#00ff00", 3: "#ffff00",
816c35beaaSBram Moolenaar	    \   4: "#8080ff", 5: "#ff40ff", 6: "#00ffff", 7: "#ffffff"
826c35beaaSBram Moolenaar	    \ }
83071d4279SBram Moolenaar  else
846c35beaaSBram Moolenaar    let s:cterm_color = {
856c35beaaSBram Moolenaar	    \   0: "#000000", 1: "#c00000", 2: "#008000", 3: "#804000",
866c35beaaSBram Moolenaar	    \   4: "#0000c0", 5: "#c000c0", 6: "#008080", 7: "#c0c0c0",
876c35beaaSBram Moolenaar	    \   8: "#808080", 9: "#ff6060", 10: "#00ff00", 11: "#ffff00",
886c35beaaSBram Moolenaar	    \   12: "#8080ff", 13: "#ff40ff", 14: "#00ffff", 15: "#ffffff"
896c35beaaSBram Moolenaar	    \ }
90313b7237SBram Moolenaar
91313b7237SBram Moolenaar    " Colors for 88 and 256 come from xterm.
92313b7237SBram Moolenaar    if &t_Co == 88
936c35beaaSBram Moolenaar      call extend(s:cterm_color, {
946c35beaaSBram Moolenaar	    \   16: "#000000", 17: "#00008b", 18: "#0000cd", 19: "#0000ff",
956c35beaaSBram Moolenaar	    \   20: "#008b00", 21: "#008b8b", 22: "#008bcd", 23: "#008bff",
966c35beaaSBram Moolenaar	    \   24: "#00cd00", 25: "#00cd8b", 26: "#00cdcd", 27: "#00cdff",
976c35beaaSBram Moolenaar	    \   28: "#00ff00", 29: "#00ff8b", 30: "#00ffcd", 31: "#00ffff",
986c35beaaSBram Moolenaar	    \   32: "#8b0000", 33: "#8b008b", 34: "#8b00cd", 35: "#8b00ff",
996c35beaaSBram Moolenaar	    \   36: "#8b8b00", 37: "#8b8b8b", 38: "#8b8bcd", 39: "#8b8bff",
1006c35beaaSBram Moolenaar	    \   40: "#8bcd00", 41: "#8bcd8b", 42: "#8bcdcd", 43: "#8bcdff",
1016c35beaaSBram Moolenaar	    \   44: "#8bff00", 45: "#8bff8b", 46: "#8bffcd", 47: "#8bffff",
1026c35beaaSBram Moolenaar	    \   48: "#cd0000", 49: "#cd008b", 50: "#cd00cd", 51: "#cd00ff",
1036c35beaaSBram Moolenaar	    \   52: "#cd8b00", 53: "#cd8b8b", 54: "#cd8bcd", 55: "#cd8bff",
1046c35beaaSBram Moolenaar	    \   56: "#cdcd00", 57: "#cdcd8b", 58: "#cdcdcd", 59: "#cdcdff",
1056c35beaaSBram Moolenaar	    \   60: "#cdff00", 61: "#cdff8b", 62: "#cdffcd", 63: "#cdffff",
1066c35beaaSBram Moolenaar	    \   64: "#ff0000"
1076c35beaaSBram Moolenaar	    \ })
1086c35beaaSBram Moolenaar      call extend(s:cterm_color, {
1096c35beaaSBram Moolenaar	    \   65: "#ff008b", 66: "#ff00cd", 67: "#ff00ff", 68: "#ff8b00",
1106c35beaaSBram Moolenaar	    \   69: "#ff8b8b", 70: "#ff8bcd", 71: "#ff8bff", 72: "#ffcd00",
1116c35beaaSBram Moolenaar	    \   73: "#ffcd8b", 74: "#ffcdcd", 75: "#ffcdff", 76: "#ffff00",
1126c35beaaSBram Moolenaar	    \   77: "#ffff8b", 78: "#ffffcd", 79: "#ffffff", 80: "#2e2e2e",
1136c35beaaSBram Moolenaar	    \   81: "#5c5c5c", 82: "#737373", 83: "#8b8b8b", 84: "#a2a2a2",
1146c35beaaSBram Moolenaar	    \   85: "#b9b9b9", 86: "#d0d0d0", 87: "#e7e7e7"
1156c35beaaSBram Moolenaar	    \ })
116313b7237SBram Moolenaar    elseif &t_Co == 256
1176c35beaaSBram Moolenaar      call extend(s:cterm_color, {
1186c35beaaSBram Moolenaar	    \   16: "#000000", 17: "#00005f", 18: "#000087", 19: "#0000af",
1196c35beaaSBram Moolenaar	    \   20: "#0000d7", 21: "#0000ff", 22: "#005f00", 23: "#005f5f",
1206c35beaaSBram Moolenaar	    \   24: "#005f87", 25: "#005faf", 26: "#005fd7", 27: "#005fff",
1216c35beaaSBram Moolenaar	    \   28: "#008700", 29: "#00875f", 30: "#008787", 31: "#0087af",
1226c35beaaSBram Moolenaar	    \   32: "#0087d7", 33: "#0087ff", 34: "#00af00", 35: "#00af5f",
1236c35beaaSBram Moolenaar	    \   36: "#00af87", 37: "#00afaf", 38: "#00afd7", 39: "#00afff",
1246c35beaaSBram Moolenaar	    \   40: "#00d700", 41: "#00d75f", 42: "#00d787", 43: "#00d7af",
1256c35beaaSBram Moolenaar	    \   44: "#00d7d7", 45: "#00d7ff", 46: "#00ff00", 47: "#00ff5f",
1266c35beaaSBram Moolenaar	    \   48: "#00ff87", 49: "#00ffaf", 50: "#00ffd7", 51: "#00ffff",
1276c35beaaSBram Moolenaar	    \   52: "#5f0000", 53: "#5f005f", 54: "#5f0087", 55: "#5f00af",
1286c35beaaSBram Moolenaar	    \   56: "#5f00d7", 57: "#5f00ff", 58: "#5f5f00", 59: "#5f5f5f",
1296c35beaaSBram Moolenaar	    \   60: "#5f5f87", 61: "#5f5faf", 62: "#5f5fd7", 63: "#5f5fff",
1306c35beaaSBram Moolenaar	    \   64: "#5f8700"
1316c35beaaSBram Moolenaar	    \ })
1326c35beaaSBram Moolenaar      call extend(s:cterm_color, {
1336c35beaaSBram Moolenaar	    \   65: "#5f875f", 66: "#5f8787", 67: "#5f87af", 68: "#5f87d7",
1346c35beaaSBram Moolenaar	    \   69: "#5f87ff", 70: "#5faf00", 71: "#5faf5f", 72: "#5faf87",
1356c35beaaSBram Moolenaar	    \   73: "#5fafaf", 74: "#5fafd7", 75: "#5fafff", 76: "#5fd700",
1366c35beaaSBram Moolenaar	    \   77: "#5fd75f", 78: "#5fd787", 79: "#5fd7af", 80: "#5fd7d7",
1376c35beaaSBram Moolenaar	    \   81: "#5fd7ff", 82: "#5fff00", 83: "#5fff5f", 84: "#5fff87",
1386c35beaaSBram Moolenaar	    \   85: "#5fffaf", 86: "#5fffd7", 87: "#5fffff", 88: "#870000",
1396c35beaaSBram Moolenaar	    \   89: "#87005f", 90: "#870087", 91: "#8700af", 92: "#8700d7",
1406c35beaaSBram Moolenaar	    \   93: "#8700ff", 94: "#875f00", 95: "#875f5f", 96: "#875f87",
1416c35beaaSBram Moolenaar	    \   97: "#875faf", 98: "#875fd7", 99: "#875fff", 100: "#878700",
1426c35beaaSBram Moolenaar	    \   101: "#87875f", 102: "#878787", 103: "#8787af", 104: "#8787d7",
1436c35beaaSBram Moolenaar	    \   105: "#8787ff", 106: "#87af00", 107: "#87af5f", 108: "#87af87",
1446c35beaaSBram Moolenaar	    \   109: "#87afaf", 110: "#87afd7", 111: "#87afff", 112: "#87d700"
1456c35beaaSBram Moolenaar	    \ })
1466c35beaaSBram Moolenaar      call extend(s:cterm_color, {
1476c35beaaSBram Moolenaar	    \   113: "#87d75f", 114: "#87d787", 115: "#87d7af", 116: "#87d7d7",
1486c35beaaSBram Moolenaar	    \   117: "#87d7ff", 118: "#87ff00", 119: "#87ff5f", 120: "#87ff87",
1496c35beaaSBram Moolenaar	    \   121: "#87ffaf", 122: "#87ffd7", 123: "#87ffff", 124: "#af0000",
1506c35beaaSBram Moolenaar	    \   125: "#af005f", 126: "#af0087", 127: "#af00af", 128: "#af00d7",
1516c35beaaSBram Moolenaar	    \   129: "#af00ff", 130: "#af5f00", 131: "#af5f5f", 132: "#af5f87",
1526c35beaaSBram Moolenaar	    \   133: "#af5faf", 134: "#af5fd7", 135: "#af5fff", 136: "#af8700",
1536c35beaaSBram Moolenaar	    \   137: "#af875f", 138: "#af8787", 139: "#af87af", 140: "#af87d7",
1546c35beaaSBram Moolenaar	    \   141: "#af87ff", 142: "#afaf00", 143: "#afaf5f", 144: "#afaf87",
1556c35beaaSBram Moolenaar	    \   145: "#afafaf", 146: "#afafd7", 147: "#afafff", 148: "#afd700",
1566c35beaaSBram Moolenaar	    \   149: "#afd75f", 150: "#afd787", 151: "#afd7af", 152: "#afd7d7",
1576c35beaaSBram Moolenaar	    \   153: "#afd7ff", 154: "#afff00", 155: "#afff5f", 156: "#afff87",
1586c35beaaSBram Moolenaar	    \   157: "#afffaf", 158: "#afffd7"
1596c35beaaSBram Moolenaar	    \ })
1606c35beaaSBram Moolenaar      call extend(s:cterm_color, {
1616c35beaaSBram Moolenaar	    \   159: "#afffff", 160: "#d70000", 161: "#d7005f", 162: "#d70087",
1626c35beaaSBram Moolenaar	    \   163: "#d700af", 164: "#d700d7", 165: "#d700ff", 166: "#d75f00",
1636c35beaaSBram Moolenaar	    \   167: "#d75f5f", 168: "#d75f87", 169: "#d75faf", 170: "#d75fd7",
1646c35beaaSBram Moolenaar	    \   171: "#d75fff", 172: "#d78700", 173: "#d7875f", 174: "#d78787",
1656c35beaaSBram Moolenaar	    \   175: "#d787af", 176: "#d787d7", 177: "#d787ff", 178: "#d7af00",
1666c35beaaSBram Moolenaar	    \   179: "#d7af5f", 180: "#d7af87", 181: "#d7afaf", 182: "#d7afd7",
1676c35beaaSBram Moolenaar	    \   183: "#d7afff", 184: "#d7d700", 185: "#d7d75f", 186: "#d7d787",
1686c35beaaSBram Moolenaar	    \   187: "#d7d7af", 188: "#d7d7d7", 189: "#d7d7ff", 190: "#d7ff00",
1696c35beaaSBram Moolenaar	    \   191: "#d7ff5f", 192: "#d7ff87", 193: "#d7ffaf", 194: "#d7ffd7",
1706c35beaaSBram Moolenaar	    \   195: "#d7ffff", 196: "#ff0000", 197: "#ff005f", 198: "#ff0087",
1716c35beaaSBram Moolenaar	    \   199: "#ff00af", 200: "#ff00d7", 201: "#ff00ff", 202: "#ff5f00",
1726c35beaaSBram Moolenaar	    \   203: "#ff5f5f", 204: "#ff5f87"
1736c35beaaSBram Moolenaar	    \ })
1746c35beaaSBram Moolenaar      call extend(s:cterm_color, {
1756c35beaaSBram Moolenaar	    \   205: "#ff5faf", 206: "#ff5fd7", 207: "#ff5fff", 208: "#ff8700",
1766c35beaaSBram Moolenaar	    \   209: "#ff875f", 210: "#ff8787", 211: "#ff87af", 212: "#ff87d7",
1776c35beaaSBram Moolenaar	    \   213: "#ff87ff", 214: "#ffaf00", 215: "#ffaf5f", 216: "#ffaf87",
1786c35beaaSBram Moolenaar	    \   217: "#ffafaf", 218: "#ffafd7", 219: "#ffafff", 220: "#ffd700",
1796c35beaaSBram Moolenaar	    \   221: "#ffd75f", 222: "#ffd787", 223: "#ffd7af", 224: "#ffd7d7",
1806c35beaaSBram Moolenaar	    \   225: "#ffd7ff", 226: "#ffff00", 227: "#ffff5f", 228: "#ffff87",
1816c35beaaSBram Moolenaar	    \   229: "#ffffaf", 230: "#ffffd7", 231: "#ffffff", 232: "#080808",
1826c35beaaSBram Moolenaar	    \   233: "#121212", 234: "#1c1c1c", 235: "#262626", 236: "#303030",
1836c35beaaSBram Moolenaar	    \   237: "#3a3a3a", 238: "#444444", 239: "#4e4e4e", 240: "#585858",
1846c35beaaSBram Moolenaar	    \   241: "#626262", 242: "#6c6c6c", 243: "#767676", 244: "#808080",
1856c35beaaSBram Moolenaar	    \   245: "#8a8a8a", 246: "#949494", 247: "#9e9e9e", 248: "#a8a8a8",
1866c35beaaSBram Moolenaar	    \   249: "#b2b2b2", 250: "#bcbcbc", 251: "#c6c6c6", 252: "#d0d0d0",
1876c35beaaSBram Moolenaar	    \   253: "#dadada", 254: "#e4e4e4", 255: "#eeeeee"
1886c35beaaSBram Moolenaar	    \ })
189313b7237SBram Moolenaar    endif
190071d4279SBram Moolenaar  endif
191071d4279SBram Moolenaarendif
192071d4279SBram Moolenaar
193071d4279SBram Moolenaar" Return good color specification: in GUI no transformation is done, in
194313b7237SBram Moolenaar" terminal return RGB values of known colors and empty string for unknown
195071d4279SBram Moolenaarif s:whatterm == "gui"
196071d4279SBram Moolenaar  function! s:HtmlColor(color)
197071d4279SBram Moolenaar    return a:color
198071d4279SBram Moolenaar  endfun
199071d4279SBram Moolenaarelse
200071d4279SBram Moolenaar  function! s:HtmlColor(color)
201313b7237SBram Moolenaar    if has_key(s:cterm_color, a:color)
202313b7237SBram Moolenaar      return s:cterm_color[a:color]
203071d4279SBram Moolenaar    else
204071d4279SBram Moolenaar      return ""
205071d4279SBram Moolenaar    endif
206071d4279SBram Moolenaar  endfun
207071d4279SBram Moolenaarendif
208071d4279SBram Moolenaar
2096c35beaaSBram Moolenaar" Find out the background and foreground color for use later
2106c35beaaSBram Moolenaarlet s:fgc = s:HtmlColor(synIDattr(hlID("Normal"), "fg#", s:whatterm))
2116c35beaaSBram Moolenaarlet s:bgc = s:HtmlColor(synIDattr(hlID("Normal"), "bg#", s:whatterm))
2126c35beaaSBram Moolenaarif s:fgc == ""
2136c35beaaSBram Moolenaar  let s:fgc = ( &background == "dark" ? "#ffffff" : "#000000" )
2146c35beaaSBram Moolenaarendif
2156c35beaaSBram Moolenaarif s:bgc == ""
2166c35beaaSBram Moolenaar  let s:bgc = ( &background == "dark" ? "#000000" : "#ffffff" )
2176c35beaaSBram Moolenaarendif
2186c35beaaSBram Moolenaar
219bebca9daSBram Moolenaarif !s:settings.use_css
220071d4279SBram Moolenaar  " Return opening HTML tag for given highlight id
221543b7ef7SBram Moolenaar  function! s:HtmlOpening(id, extra_attrs)
222071d4279SBram Moolenaar    let a = ""
223071d4279SBram Moolenaar    if synIDattr(a:id, "inverse")
224071d4279SBram Moolenaar      " For inverse, we always must set both colors (and exchange them)
225071d4279SBram Moolenaar      let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
226543b7ef7SBram Moolenaar      let a = a . '<span '.a:extra_attrs.'style="background-color: ' . ( x != "" ? x : s:fgc ) . '">'
227071d4279SBram Moolenaar      let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
228071d4279SBram Moolenaar      let a = a . '<font color="' . ( x != "" ? x : s:bgc ) . '">'
229071d4279SBram Moolenaar    else
230071d4279SBram Moolenaar      let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
231543b7ef7SBram Moolenaar      if x != ""
232543b7ef7SBram Moolenaar	let a = a . '<span '.a:extra_attrs.'style="background-color: ' . x . '">'
233543b7ef7SBram Moolenaar      elseif !empty(a:extra_attrs)
234543b7ef7SBram Moolenaar	let a = a . '<span '.a:extra_attrs.'>'
235543b7ef7SBram Moolenaar      endif
236071d4279SBram Moolenaar      let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
237071d4279SBram Moolenaar      if x != "" | let a = a . '<font color="' . x . '">' | endif
238071d4279SBram Moolenaar    endif
239071d4279SBram Moolenaar    if synIDattr(a:id, "bold") | let a = a . "<b>" | endif
240071d4279SBram Moolenaar    if synIDattr(a:id, "italic") | let a = a . "<i>" | endif
241071d4279SBram Moolenaar    if synIDattr(a:id, "underline") | let a = a . "<u>" | endif
242071d4279SBram Moolenaar    return a
243071d4279SBram Moolenaar  endfun
244071d4279SBram Moolenaar
245071d4279SBram Moolenaar  " Return closing HTML tag for given highlight id
246543b7ef7SBram Moolenaar  function! s:HtmlClosing(id, has_extra_attrs)
247071d4279SBram Moolenaar    let a = ""
248071d4279SBram Moolenaar    if synIDattr(a:id, "underline") | let a = a . "</u>" | endif
249071d4279SBram Moolenaar    if synIDattr(a:id, "italic") | let a = a . "</i>" | endif
250071d4279SBram Moolenaar    if synIDattr(a:id, "bold") | let a = a . "</b>" | endif
251071d4279SBram Moolenaar    if synIDattr(a:id, "inverse")
252071d4279SBram Moolenaar      let a = a . '</font></span>'
253071d4279SBram Moolenaar    else
254071d4279SBram Moolenaar      let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
255071d4279SBram Moolenaar      if x != "" | let a = a . '</font>' | endif
256071d4279SBram Moolenaar      let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
257543b7ef7SBram Moolenaar      if x != "" || a:has_extra_attrs | let a = a . '</span>' | endif
258071d4279SBram Moolenaar    endif
259071d4279SBram Moolenaar    return a
260071d4279SBram Moolenaar  endfun
261071d4279SBram Moolenaarendif
262071d4279SBram Moolenaar
2636c35beaaSBram Moolenaar" Use a different function for formatting based on user options. This way we
2646c35beaaSBram Moolenaar" can avoid a lot of logic during the actual execution.
2656c35beaaSBram Moolenaar"
2666c35beaaSBram Moolenaar" Build the function line by line containing only what is needed for the options
2676c35beaaSBram Moolenaar" in use for maximum code sharing with minimal branch logic for greater speed.
2686c35beaaSBram Moolenaar"
2696c35beaaSBram Moolenaar" Note, 'exec' commands do not recognize line continuations, so must concatenate
2706c35beaaSBram Moolenaar" lines rather than continue them.
2716c35beaaSBram Moolenaarif s:settings.use_css
2726c35beaaSBram Moolenaar  " save CSS to a list of rules to add to the output at the end of processing
2736c35beaaSBram Moolenaar
2746c35beaaSBram Moolenaar  " first, get the style names we need
2756c35beaaSBram Moolenaar  let wrapperfunc_lines = [
276543b7ef7SBram Moolenaar	\ 'function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, make_unselectable, unformatted)',
2776c35beaaSBram Moolenaar	\ '',
2786c35beaaSBram Moolenaar	\ '  let l:style_name = synIDattr(a:style_id, "name", s:whatterm)'
2796c35beaaSBram Moolenaar	\ ]
2806c35beaaSBram Moolenaar  if &diff
2816c35beaaSBram Moolenaar    let wrapperfunc_lines += [
2826c35beaaSBram Moolenaar	\ '  let l:diff_style_name = synIDattr(a:diff_style_id, "name", s:whatterm)']
2836c35beaaSBram Moolenaar
2846c35beaaSBram Moolenaar  " Add normal groups and diff groups to separate lists so we can order them to
2856c35beaaSBram Moolenaar  " allow diff highlight to override normal highlight
2866c35beaaSBram Moolenaar
2876c35beaaSBram Moolenaar  " if primary style IS a diff style, grab it from the diff cache instead
2886c35beaaSBram Moolenaar  " (always succeeds because we pre-populate it)
2896c35beaaSBram Moolenaar  let wrapperfunc_lines += [
2906c35beaaSBram Moolenaar	\ '',
2916c35beaaSBram Moolenaar	\ '  if a:style_id == s:DIFF_D_ID || a:style_id == s:DIFF_A_ID ||'.
2926c35beaaSBram Moolenaar	\ '          a:style_id == s:DIFF_C_ID || a:style_id == s:DIFF_T_ID',
2936c35beaaSBram Moolenaar	\ '    let l:saved_style = get(s:diffstylelist,a:style_id)',
2946c35beaaSBram Moolenaar	\ '  else'
2956c35beaaSBram Moolenaar	\ ]
2966c35beaaSBram Moolenaar  endif
2976c35beaaSBram Moolenaar
2986c35beaaSBram Moolenaar  " get primary style info from cache or build it on the fly if not found
2996c35beaaSBram Moolenaar  let wrapperfunc_lines += [
3006c35beaaSBram Moolenaar	\ '    let l:saved_style = get(s:stylelist,a:style_id)',
3016c35beaaSBram Moolenaar	\ '    if type(l:saved_style) == type(0)',
3026c35beaaSBram Moolenaar	\ '      unlet l:saved_style',
3036c35beaaSBram Moolenaar	\ '      let l:saved_style = s:CSS1(a:style_id)',
3046c35beaaSBram Moolenaar	\ '      if l:saved_style != ""',
3056c35beaaSBram Moolenaar	\ '        let l:saved_style = "." . l:style_name . " { " . l:saved_style . "}"',
3066c35beaaSBram Moolenaar	\ '      endif',
3076c35beaaSBram Moolenaar	\ '      let s:stylelist[a:style_id]= l:saved_style',
3086c35beaaSBram Moolenaar	\ '    endif'
3096c35beaaSBram Moolenaar	\ ]
3106c35beaaSBram Moolenaar  if &diff
3116c35beaaSBram Moolenaar    let wrapperfunc_lines += [ '  endif' ]
3126c35beaaSBram Moolenaar  endif
3136c35beaaSBram Moolenaar
3146c35beaaSBram Moolenaar  " Build the wrapper tags around the text. It turns out that caching these
3156c35beaaSBram Moolenaar  " gives pretty much zero performance gain and adds a lot of logic.
3166c35beaaSBram Moolenaar
3176c35beaaSBram Moolenaar  let wrapperfunc_lines += [
3186c35beaaSBram Moolenaar	\ '',
319543b7ef7SBram Moolenaar	\ '  if l:saved_style == "" && empty(a:extra_attrs)'
3206c35beaaSBram Moolenaar	\ ]
3216c35beaaSBram Moolenaar  if &diff
3226c35beaaSBram Moolenaar    let wrapperfunc_lines += [
3236c35beaaSBram Moolenaar	\ '    if a:diff_style_id <= 0'
3246c35beaaSBram Moolenaar	\ ]
3256c35beaaSBram Moolenaar  endif
3266c35beaaSBram Moolenaar  " no surroundings if neither primary nor diff style has any info
3276c35beaaSBram Moolenaar  let wrapperfunc_lines += [
3286c35beaaSBram Moolenaar	\ '       return a:text'
3296c35beaaSBram Moolenaar	\ ]
3306c35beaaSBram Moolenaar  if &diff
3316c35beaaSBram Moolenaar    " no primary style, but diff style
3326c35beaaSBram Moolenaar    let wrapperfunc_lines += [
3336c35beaaSBram Moolenaar	\ '     else',
3346c35beaaSBram Moolenaar	\ '       return "<span class=\"" .l:diff_style_name . "\">".a:text."</span>"',
3356c35beaaSBram Moolenaar	\ '     endif'
3366c35beaaSBram Moolenaar	\ ]
3376c35beaaSBram Moolenaar  endif
3386c35beaaSBram Moolenaar  " open tag for non-empty primary style
3396c35beaaSBram Moolenaar  let wrapperfunc_lines += [
3406c35beaaSBram Moolenaar	\ '  else']
3416c35beaaSBram Moolenaar  " non-empty primary style. handle either empty or non-empty diff style.
3426c35beaaSBram Moolenaar  "
3436c35beaaSBram Moolenaar  " separate the two classes by a space to apply them both if there is a diff
3446c35beaaSBram Moolenaar  " style name, unless the primary style is empty, then just use the diff style
3456c35beaaSBram Moolenaar  " name
3466c35beaaSBram Moolenaar  let diffstyle =
3476c35beaaSBram Moolenaar	  \ (&diff ? '(a:diff_style_id <= 0 ? "" : " ". l:diff_style_name) .'
3486c35beaaSBram Moolenaar	  \        : "")
3496c35beaaSBram Moolenaar  if s:settings.prevent_copy == ""
3506c35beaaSBram Moolenaar    let wrapperfunc_lines += [
351543b7ef7SBram Moolenaar	  \ '    return "<span ".a:extra_attrs."class=\"" . l:style_name .'.diffstyle.'"\">".a:text."</span>"'
3526c35beaaSBram Moolenaar	  \ ]
3536c35beaaSBram Moolenaar  else
3546c35beaaSBram Moolenaar
3556c35beaaSBram Moolenaar    "
3566c35beaaSBram Moolenaar    " Wrap the <input> in a <span> to allow fixing the stupid bug in some fonts
3576c35beaaSBram Moolenaar    " which cause browsers to display a 1px gap between lines when these
3586c35beaaSBram Moolenaar    " <input>s have a background color (maybe not really a bug, this isn't
3596c35beaaSBram Moolenaar    " well-defined)
3606c35beaaSBram Moolenaar    "
3616c35beaaSBram Moolenaar    " use strwidth, because we care only about how many character boxes are
3626c35beaaSBram Moolenaar    " needed to size the input, we don't care how many characters (including
3636c35beaaSBram Moolenaar    " separately counted composing chars, from strchars()) or bytes (from
3646c35beaaSBram Moolenaar    " len())the string contains. strdisplaywidth() is not needed because none of
3656c35beaaSBram Moolenaar    " the unselectable groups can contain tab characters (fold column, fold
3666c35beaaSBram Moolenaar    " text, line number).
3676c35beaaSBram Moolenaar    "
3686c35beaaSBram Moolenaar    " Note, if maxlength property needs to be added in the future, it will need
3696c35beaaSBram Moolenaar    " to use strchars(), because HTML specifies that the maxlength parameter
3706c35beaaSBram Moolenaar    " uses the number of unique codepoints for its limit.
3716c35beaaSBram Moolenaar    let wrapperfunc_lines += [
3726c35beaaSBram Moolenaar	  \ '    if a:make_unselectable',
373543b7ef7SBram Moolenaar	  \ '      return "<span ".a:extra_attrs."class=\"" . l:style_name .'.diffstyle.'"\">'.
3746c35beaaSBram Moolenaar	  \                '<input'.s:unselInputType.' class=\"" . l:style_name .'.diffstyle.'"\"'.
3756c35beaaSBram Moolenaar	  \                 ' value=\"".substitute(a:unformatted,''\s\+$'',"","")."\"'.
3766c35beaaSBram Moolenaar	  \                 ' onselect=''this.blur(); return false;'''.
3776c35beaaSBram Moolenaar	  \                 ' onmousedown=''this.blur(); return false;'''.
3786c35beaaSBram Moolenaar	  \                 ' onclick=''this.blur(); return false;'''.
3796c35beaaSBram Moolenaar	  \                 ' readonly=''readonly'''.
3806c35beaaSBram Moolenaar	  \                 ' size=\"".strwidth(a:unformatted)."\"'.
3816c35beaaSBram Moolenaar	  \                 (s:settings.use_xhtml ? '/' : '').'></span>"',
3826c35beaaSBram Moolenaar	  \ '    else',
383543b7ef7SBram Moolenaar	  \ '      return "<span ".a:extra_attrs."class=\"" . l:style_name .'. diffstyle .'"\">".a:text."</span>"'
3846c35beaaSBram Moolenaar	  \ ]
3856c35beaaSBram Moolenaar  endif
3866c35beaaSBram Moolenaar  let wrapperfunc_lines += [
3876c35beaaSBram Moolenaar	\ '  endif',
3886c35beaaSBram Moolenaar	\ 'endfun'
3896c35beaaSBram Moolenaar	\ ]
3906c35beaaSBram Moolenaarelse
3916c35beaaSBram Moolenaar  " Non-CSS method just needs the wrapper.
3926c35beaaSBram Moolenaar  "
3936c35beaaSBram Moolenaar  " Functions used to get opening/closing automatically return null strings if
3946c35beaaSBram Moolenaar  " no styles exist.
3956c35beaaSBram Moolenaar  if &diff
3966c35beaaSBram Moolenaar    let wrapperfunc_lines = [
397543b7ef7SBram Moolenaar	  \ 'function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, unusedarg, unusedarg2)',
398543b7ef7SBram Moolenaar	  \ '  return s:HtmlOpening(a:style_id, a:extra_attrs).(a:diff_style_id <= 0 ? "" :'.
399543b7ef7SBram Moolenaar	  \                                     's:HtmlOpening(a:diff_style_id, "")).a:text.'.
400543b7ef7SBram Moolenaar	  \   '(a:diff_style_id <= 0 ? "" : s:HtmlClosing(a:diff_style_id, 0)).s:HtmlClosing(a:style_id, !empty(a:extra_attrs))',
4016c35beaaSBram Moolenaar	  \ 'endfun'
4026c35beaaSBram Moolenaar	  \ ]
4036c35beaaSBram Moolenaar  else
4046c35beaaSBram Moolenaar    let wrapperfunc_lines = [
405543b7ef7SBram Moolenaar	  \ 'function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, unusedarg, unusedarg2)',
406543b7ef7SBram Moolenaar	  \ '  return s:HtmlOpening(a:style_id, a:extra_attrs).a:text.s:HtmlClosing(a:style_id, !empty(a:extra_attrs))',
4076c35beaaSBram Moolenaar	  \ 'endfun'
4086c35beaaSBram Moolenaar	  \ ]
4096c35beaaSBram Moolenaar  endif
4106c35beaaSBram Moolenaarendif
4116c35beaaSBram Moolenaar
4126c35beaaSBram Moolenaar" create the function we built line by line above
4136c35beaaSBram Moolenaarexec join(wrapperfunc_lines, "\n")
4146c35beaaSBram Moolenaar
4156c35beaaSBram Moolenaarlet s:diff_mode = &diff
4166c35beaaSBram Moolenaar
41735a9aaabSBram Moolenaar" Return HTML valid characters enclosed in a span of class style_name with
41835a9aaabSBram Moolenaar" unprintable characters expanded and double spaces replaced as necessary.
4196c35beaaSBram Moolenaar"
4206c35beaaSBram Moolenaar" TODO: eliminate unneeded logic like done for BuildStyleWrapper
421543b7ef7SBram Moolenaarfunction! s:HtmlFormat(text, style_id, diff_style_id, extra_attrs, make_unselectable)
42235a9aaabSBram Moolenaar  " Replace unprintable characters
4236c35beaaSBram Moolenaar  let unformatted = strtrans(a:text)
42435a9aaabSBram Moolenaar
4256c35beaaSBram Moolenaar  let formatted = unformatted
4268ada2ccaSBram Moolenaar
42735a9aaabSBram Moolenaar  " Replace the reserved html characters
4282a8a3ecbSBram Moolenaar  let formatted = substitute(formatted, '&', '\&amp;',  'g')
4292a8a3ecbSBram Moolenaar  let formatted = substitute(formatted, '<', '\&lt;',   'g')
4302a8a3ecbSBram Moolenaar  let formatted = substitute(formatted, '>', '\&gt;',   'g')
4312a8a3ecbSBram Moolenaar  let formatted = substitute(formatted, '"', '\&quot;', 'g')
4326c35beaaSBram Moolenaar  " &apos; is not valid in HTML but it is in XHTML, so just use the numeric
4336c35beaaSBram Moolenaar  " reference for it instead. Needed because it could appear in quotes
4346c35beaaSBram Moolenaar  " especially if unselectable regions is turned on.
4356c35beaaSBram Moolenaar  let formatted = substitute(formatted, '"', '\&#0039;', 'g')
4362a8a3ecbSBram Moolenaar
4372a8a3ecbSBram Moolenaar  " Replace a "form feed" character with HTML to do a page break
4386c35beaaSBram Moolenaar  " TODO: need to prevent this in unselectable areas? Probably it should never
4396c35beaaSBram Moolenaar  " BE in an unselectable area...
4402a8a3ecbSBram Moolenaar  let formatted = substitute(formatted, "\x0c", '<hr class="PAGE-BREAK">', 'g')
4412a8a3ecbSBram Moolenaar
4428df7f888SBram Moolenaar  " Replace double spaces, leading spaces, and trailing spaces if needed
44335a9aaabSBram Moolenaar  if ' ' != s:HtmlSpace
44435a9aaabSBram Moolenaar    let formatted = substitute(formatted, '  ', s:HtmlSpace . s:HtmlSpace, 'g')
4458424a624SBram Moolenaar    let formatted = substitute(formatted, '^ ', s:HtmlSpace, 'g')
4468df7f888SBram Moolenaar    let formatted = substitute(formatted, ' \+$', s:HtmlSpace, 'g')
44735a9aaabSBram Moolenaar  endif
44835a9aaabSBram Moolenaar
4496c35beaaSBram Moolenaar  " Enclose in the correct format
450543b7ef7SBram Moolenaar  return s:BuildStyleWrapper(a:style_id, a:diff_style_id, a:extra_attrs, formatted, a:make_unselectable, unformatted)
45135a9aaabSBram Moolenaarendfun
45235a9aaabSBram Moolenaar
4536c35beaaSBram Moolenaar" set up functions to call HtmlFormat in certain ways based on whether the
4546c35beaaSBram Moolenaar" element is supposed to be unselectable or not
4556c35beaaSBram Moolenaarif s:settings.prevent_copy =~# 'n'
456543b7ef7SBram Moolenaar  if s:settings.number_lines
45731c31679SBram Moolenaar    if s:settings.line_ids
458543b7ef7SBram Moolenaar      function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
459543b7ef7SBram Moolenaar	if a:lnr > 0
46031c31679SBram Moolenaar	  return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'.(exists('g:html_diff_win_num') ? 'W'.g:html_diff_win_num : "").'L'.a:lnr.s:settings.id_suffix.'" ', 1)
461543b7ef7SBram Moolenaar	else
462543b7ef7SBram Moolenaar	  return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
463543b7ef7SBram Moolenaar	endif
4646c35beaaSBram Moolenaar      endfun
4656c35beaaSBram Moolenaar    else
46631c31679SBram Moolenaar      function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
46731c31679SBram Moolenaar	return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
46831c31679SBram Moolenaar      endfun
46931c31679SBram Moolenaar    endif
47031c31679SBram Moolenaar  elseif s:settings.line_ids
471543b7ef7SBram Moolenaar    " if lines are not being numbered the only reason this function gets called
472543b7ef7SBram Moolenaar    " is to put the line IDs on each line; "text" will be emtpy but lnr will
473543b7ef7SBram Moolenaar    " always be non-zero, however we don't want to use the <input> because that
474543b7ef7SBram Moolenaar    " won't work as nice for empty text
475543b7ef7SBram Moolenaar    function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
47631c31679SBram Moolenaar      return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'.(exists('g:html_diff_win_num') ? 'W'.g:html_diff_win_num : "").'L'.a:lnr.s:settings.id_suffix.'" ', 0)
477543b7ef7SBram Moolenaar    endfun
478543b7ef7SBram Moolenaar  endif
479543b7ef7SBram Moolenaarelse
48031c31679SBram Moolenaar  if s:settings.line_ids
481543b7ef7SBram Moolenaar    function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
482543b7ef7SBram Moolenaar      if a:lnr > 0
48331c31679SBram Moolenaar	return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'.(exists('g:html_diff_win_num') ? 'W'.g:html_diff_win_num : "").'L'.a:lnr.s:settings.id_suffix.'" ', 0)
484543b7ef7SBram Moolenaar      else
485543b7ef7SBram Moolenaar	return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
486543b7ef7SBram Moolenaar      endif
4876c35beaaSBram Moolenaar    endfun
48831c31679SBram Moolenaar  else
48931c31679SBram Moolenaar    function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
49031c31679SBram Moolenaar      return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
49131c31679SBram Moolenaar    endfun
49231c31679SBram Moolenaar  endif
4936c35beaaSBram Moolenaarendif
4946c35beaaSBram Moolenaarif s:settings.prevent_copy =~# 'd'
4956c35beaaSBram Moolenaar  function! s:HtmlFormat_d(text, style_id, diff_style_id)
496543b7ef7SBram Moolenaar    return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
4976c35beaaSBram Moolenaar  endfun
4986c35beaaSBram Moolenaarelse
4996c35beaaSBram Moolenaar  function! s:HtmlFormat_d(text, style_id, diff_style_id)
500543b7ef7SBram Moolenaar    return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
5016c35beaaSBram Moolenaar  endfun
5026c35beaaSBram Moolenaarendif
5036c35beaaSBram Moolenaarif s:settings.prevent_copy =~# 'f'
5046c35beaaSBram Moolenaar  " Note the <input> elements for fill spaces will have a single space for
5056c35beaaSBram Moolenaar  " content, to allow active cursor CSS selection to work.
5066c35beaaSBram Moolenaar  "
5076c35beaaSBram Moolenaar  " Wrap the whole thing in a span for the 1px padding workaround for gaps.
5086c35beaaSBram Moolenaar  function! s:FoldColumn_build(char, len, numfill, char2, class, click)
5096c35beaaSBram Moolenaar    let l:input_open = "<input readonly='readonly'".s:unselInputType.
5106c35beaaSBram Moolenaar	  \ " onselect='this.blur(); return false;'".
5116c35beaaSBram Moolenaar	  \ " onmousedown='this.blur(); ".a:click." return false;'".
5126c35beaaSBram Moolenaar	  \ " onclick='return false;' size='".
5136c35beaaSBram Moolenaar	  \ string(a:len + (empty(a:char2) ? 0 : 1) + a:numfill) .
5146c35beaaSBram Moolenaar	  \ "' "
5156c35beaaSBram Moolenaar    let l:common_attrs = "class='FoldColumn' value='"
5166c35beaaSBram Moolenaar    let l:input_close = (s:settings.use_xhtml ? "' />" : "'>")
5176c35beaaSBram Moolenaar    return "<span class='".a:class."'>".
5186c35beaaSBram Moolenaar	  \ l:input_open.l:common_attrs.repeat(a:char, a:len).
5196c35beaaSBram Moolenaar	  \ (!empty(a:char2) ? a:char2 : "").
5206c35beaaSBram Moolenaar	  \ l:input_close . "</span>"
5216c35beaaSBram Moolenaar  endfun
5226c35beaaSBram Moolenaar  function! s:FoldColumn_fill()
5236c35beaaSBram Moolenaar    return s:FoldColumn_build('', s:foldcolumn, 0, '', 'FoldColumn', '')
5246c35beaaSBram Moolenaar  endfun
5256c35beaaSBram Moolenaarelse
5266c35beaaSBram Moolenaar  " For normal fold columns, simply space-pad to the desired width (note that
5276c35beaaSBram Moolenaar  " the FoldColumn definition includes a whitespace:pre rule)
5286c35beaaSBram Moolenaar  function! s:FoldColumn_build(char, len, numfill, char2, class, click)
5296c35beaaSBram Moolenaar    return "<a href='#' class='".a:class."' onclick='".a:click."'>".
5306c35beaaSBram Moolenaar	  \ repeat(a:char, a:len).a:char2.repeat(' ', a:numfill).
5316c35beaaSBram Moolenaar	  \ "</a>"
5326c35beaaSBram Moolenaar  endfun
5336c35beaaSBram Moolenaar  function! s:FoldColumn_fill()
534543b7ef7SBram Moolenaar    return s:HtmlFormat(repeat(' ', s:foldcolumn), s:FOLD_C_ID, 0, "", 0)
5356c35beaaSBram Moolenaar  endfun
5366c35beaaSBram Moolenaarendif
5376c35beaaSBram Moolenaarif s:settings.prevent_copy =~# 't'
5386c35beaaSBram Moolenaar  " put an extra empty span at the end for dynamic folds, so the linebreak can
5396c35beaaSBram Moolenaar  " be surrounded. Otherwise do it as normal.
5406c35beaaSBram Moolenaar  "
5416c35beaaSBram Moolenaar  " TODO: isn't there a better way to do this, than placing it here and using a
5426c35beaaSBram Moolenaar  " substitute later?
5436c35beaaSBram Moolenaar  if s:settings.dynamic_folds
5446c35beaaSBram Moolenaar    function! s:HtmlFormat_t(text, style_id, diff_style_id)
545543b7ef7SBram Moolenaar      return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) .
546543b7ef7SBram Moolenaar	    \ s:HtmlFormat("", a:style_id, 0, "", 0)
5476c35beaaSBram Moolenaar    endfun
5486c35beaaSBram Moolenaar  else
5496c35beaaSBram Moolenaar    function! s:HtmlFormat_t(text, style_id, diff_style_id)
550543b7ef7SBram Moolenaar      return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
5516c35beaaSBram Moolenaar    endfun
5526c35beaaSBram Moolenaar  endif
5536c35beaaSBram Moolenaarelse
5546c35beaaSBram Moolenaar  function! s:HtmlFormat_t(text, style_id, diff_style_id)
555543b7ef7SBram Moolenaar    return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
5566c35beaaSBram Moolenaar  endfun
5576c35beaaSBram Moolenaarendif
5586c35beaaSBram Moolenaar
559071d4279SBram Moolenaar" Return CSS style describing given highlight id (can be empty)
560071d4279SBram Moolenaarfunction! s:CSS1(id)
561071d4279SBram Moolenaar  let a = ""
562071d4279SBram Moolenaar  if synIDattr(a:id, "inverse")
563071d4279SBram Moolenaar    " For inverse, we always must set both colors (and exchange them)
564071d4279SBram Moolenaar    let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
565071d4279SBram Moolenaar    let a = a . "color: " . ( x != "" ? x : s:bgc ) . "; "
566071d4279SBram Moolenaar    let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
567071d4279SBram Moolenaar    let a = a . "background-color: " . ( x != "" ? x : s:fgc ) . "; "
568071d4279SBram Moolenaar  else
569071d4279SBram Moolenaar    let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
570071d4279SBram Moolenaar    if x != "" | let a = a . "color: " . x . "; " | endif
571071d4279SBram Moolenaar    let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
5726c35beaaSBram Moolenaar    if x != ""
5736c35beaaSBram Moolenaar      let a = a . "background-color: " . x . "; "
5746c35beaaSBram Moolenaar      " stupid hack because almost every browser seems to have at least one font
5756c35beaaSBram Moolenaar      " which shows 1px gaps between lines which have background
5766c35beaaSBram Moolenaar      let a = a . "padding-bottom: 1px; "
5776c35beaaSBram Moolenaar    elseif (a:id == s:FOLDED_ID || a:id == s:LINENR_ID || a:id == s:FOLD_C_ID) && !empty(s:settings.prevent_copy)
5786c35beaaSBram Moolenaar      " input elements default to a different color than the rest of the page
5796c35beaaSBram Moolenaar      let a = a . "background-color: " . s:bgc . "; "
5806c35beaaSBram Moolenaar    endif
581071d4279SBram Moolenaar  endif
582071d4279SBram Moolenaar  if synIDattr(a:id, "bold") | let a = a . "font-weight: bold; " | endif
583071d4279SBram Moolenaar  if synIDattr(a:id, "italic") | let a = a . "font-style: italic; " | endif
584071d4279SBram Moolenaar  if synIDattr(a:id, "underline") | let a = a . "text-decoration: underline; " | endif
585071d4279SBram Moolenaar  return a
586071d4279SBram Moolenaarendfun
587071d4279SBram Moolenaar
588076e8b2aSBram Moolenaarif s:settings.dynamic_folds
5895c73622aSBram Moolenaar  " compares two folds as stored in our list of folds
5905c73622aSBram Moolenaar  " A fold is "less" than another if it starts at an earlier line number,
5915c73622aSBram Moolenaar  " or ends at a later line number, ties broken by fold level
5925c73622aSBram Moolenaar  function! s:FoldCompare(f1, f2)
5935c73622aSBram Moolenaar    if a:f1.firstline != a:f2.firstline
5945c73622aSBram Moolenaar      " put it before if it starts earlier
5955c73622aSBram Moolenaar      return a:f1.firstline - a:f2.firstline
5965c73622aSBram Moolenaar    elseif a:f1.lastline != a:f2.lastline
5975c73622aSBram Moolenaar      " put it before if it ends later
5985c73622aSBram Moolenaar      return a:f2.lastline - a:f1.lastline
5995c73622aSBram Moolenaar    else
6005c73622aSBram Moolenaar      " if folds begin and end on the same lines, put lowest fold level first
6015c73622aSBram Moolenaar      return a:f1.level - a:f2.level
6025c73622aSBram Moolenaar    endif
6035c73622aSBram Moolenaar  endfunction
6045c73622aSBram Moolenaar
6055c73622aSBram Moolenaarendif
6065c73622aSBram Moolenaar
607071d4279SBram Moolenaar
608071d4279SBram Moolenaar" Set some options to make it work faster.
609071d4279SBram Moolenaar" Don't report changes for :substitute, there will be many of them.
6108df7f888SBram Moolenaar" Don't change other windows; turn off scroll bind temporarily
611071d4279SBram Moolenaarlet s:old_title = &title
612071d4279SBram Moolenaarlet s:old_icon = &icon
613071d4279SBram Moolenaarlet s:old_et = &l:et
6148df7f888SBram Moolenaarlet s:old_bind = &l:scrollbind
615071d4279SBram Moolenaarlet s:old_report = &report
616071d4279SBram Moolenaarlet s:old_search = @/
617349b2fb0SBram Moolenaarlet s:old_more = &more
618071d4279SBram Moolenaarset notitle noicon
619071d4279SBram Moolenaarsetlocal et
620349b2fb0SBram Moolenaarset nomore
621071d4279SBram Moolenaarset report=1000000
6228df7f888SBram Moolenaarsetlocal noscrollbind
623071d4279SBram Moolenaar
6247510fe74SBram Moolenaarif exists(':ownsyntax') && exists('w:current_syntax')
6257510fe74SBram Moolenaar  let s:current_syntax = w:current_syntax
6267510fe74SBram Moolenaarelseif exists('b:current_syntax')
6277510fe74SBram Moolenaar  let s:current_syntax = b:current_syntax
6287510fe74SBram Moolenaarelse
6297510fe74SBram Moolenaar  let s:current_syntax = 'none'
6307510fe74SBram Moolenaarendif
6317510fe74SBram Moolenaar
6327510fe74SBram Moolenaarif s:current_syntax == ''
6337510fe74SBram Moolenaar  let s:current_syntax = 'none'
6347510fe74SBram Moolenaarendif
6357510fe74SBram Moolenaar
636071d4279SBram Moolenaar" Split window to create a buffer with the HTML file.
637071d4279SBram Moolenaarlet s:orgbufnr = winbufnr(0)
638349b2fb0SBram Moolenaarlet s:origwin_stl = &l:stl
639071d4279SBram Moolenaarif expand("%") == ""
640543b7ef7SBram Moolenaar  if exists('g:html_diff_win_num')
641543b7ef7SBram Moolenaar    exec 'new Untitled_win'.g:html_diff_win_num.'.'.(s:settings.use_xhtml ? 'x' : '').'html'
642543b7ef7SBram Moolenaar  else
643076e8b2aSBram Moolenaar    exec 'new Untitled.'.(s:settings.use_xhtml ? 'x' : '').'html'
644543b7ef7SBram Moolenaar  endif
645071d4279SBram Moolenaarelse
646076e8b2aSBram Moolenaar  exec 'new %.'.(s:settings.use_xhtml ? 'x' : '').'html'
647071d4279SBram Moolenaarendif
648349b2fb0SBram Moolenaar
649349b2fb0SBram Moolenaar" Resize the new window to very small in order to make it draw faster
650349b2fb0SBram Moolenaarlet s:old_winheight = winheight(0)
651349b2fb0SBram Moolenaarlet s:old_winfixheight = &l:winfixheight
652349b2fb0SBram Moolenaarif s:old_winheight > 2
653349b2fb0SBram Moolenaar  resize 1 " leave enough room to view one line at a time
654349b2fb0SBram Moolenaar  norm! G
655349b2fb0SBram Moolenaar  norm! zt
656349b2fb0SBram Moolenaarendif
657349b2fb0SBram Moolenaarsetlocal winfixheight
658349b2fb0SBram Moolenaar
659349b2fb0SBram Moolenaarlet s:newwin_stl = &l:stl
660349b2fb0SBram Moolenaar
661349b2fb0SBram Moolenaar" on the new window, set the least time-consuming fold method
6628df7f888SBram Moolenaarlet s:old_fen = &foldenable
663349b2fb0SBram Moolenaarsetlocal foldmethod=manual
6648df7f888SBram Moolenaarsetlocal nofoldenable
665349b2fb0SBram Moolenaar
666071d4279SBram Moolenaarlet s:newwin = winnr()
667071d4279SBram Moolenaarlet s:orgwin = bufwinnr(s:orgbufnr)
668071d4279SBram Moolenaar
669349b2fb0SBram Moolenaarsetlocal modifiable
670071d4279SBram Moolenaar%d
671071d4279SBram Moolenaarlet s:old_paste = &paste
672071d4279SBram Moolenaarset paste
673071d4279SBram Moolenaarlet s:old_magic = &magic
674071d4279SBram Moolenaarset magic
675071d4279SBram Moolenaar
676166af9bbSBram Moolenaar" set the fileencoding to match the charset we'll be using
677166af9bbSBram Moolenaarlet &l:fileencoding=s:settings.vim_encoding
678166af9bbSBram Moolenaar
679166af9bbSBram Moolenaar" According to http://www.w3.org/TR/html4/charset.html#doc-char-set, the byte
680166af9bbSBram Moolenaar" order mark is highly recommend on the web when using multibyte encodings. But,
681166af9bbSBram Moolenaar" it is not a good idea to include it on UTF-8 files. Otherwise, let Vim
682166af9bbSBram Moolenaar" determine when it is actually inserted.
683166af9bbSBram Moolenaarif s:settings.vim_encoding == 'utf-8'
684166af9bbSBram Moolenaar  setlocal nobomb
685166af9bbSBram Moolenaarelse
686166af9bbSBram Moolenaar  setlocal bomb
687166af9bbSBram Moolenaarendif
688166af9bbSBram Moolenaar
689349b2fb0SBram Moolenaarlet s:lines = []
690349b2fb0SBram Moolenaar
691076e8b2aSBram Moolenaarif s:settings.use_xhtml
692bebca9daSBram Moolenaar  if s:settings.encoding != ""
693bebca9daSBram Moolenaar    call add(s:lines, "<?xml version=\"1.0\" encoding=\"" . s:settings.encoding . "\"?>")
6941cd871b5SBram Moolenaar  else
695349b2fb0SBram Moolenaar    call add(s:lines, "<?xml version=\"1.0\"?>")
6961cd871b5SBram Moolenaar  endif
69735a9aaabSBram Moolenaar  let s:tag_close = ' />'
698071d4279SBram Moolenaarelse
69935a9aaabSBram Moolenaar  let s:tag_close = '>'
70035a9aaabSBram Moolenaarendif
70135a9aaabSBram Moolenaar
70235a9aaabSBram Moolenaarlet s:HtmlSpace = ' '
703c1e37901SBram Moolenaarlet s:LeadingSpace = ' '
70435a9aaabSBram Moolenaarlet s:HtmlEndline = ''
705076e8b2aSBram Moolenaarif s:settings.no_pre
70635a9aaabSBram Moolenaar  let s:HtmlEndline = '<br' . s:tag_close
707543b7ef7SBram Moolenaar  let s:LeadingSpace = s:settings.use_xhtml ? '&#160;' : '&nbsp;'
708c1e37901SBram Moolenaar  let s:HtmlSpace = '\' . s:LeadingSpace
709071d4279SBram Moolenaarendif
710071d4279SBram Moolenaar
711071d4279SBram Moolenaar" HTML header, with the title and generator ;-). Left free space for the CSS,
712071d4279SBram Moolenaar" to be filled at the end.
713349b2fb0SBram Moolenaarcall extend(s:lines, [
714349b2fb0SBram Moolenaar      \ "<html>",
715bebca9daSBram Moolenaar      \ "<head>"])
716bebca9daSBram Moolenaar" include encoding as close to the top as possible, but only if not already
717bebca9daSBram Moolenaar" contained in XML information (to avoid haggling over content type)
718bebca9daSBram Moolenaarif s:settings.encoding != "" && !s:settings.use_xhtml
719bebca9daSBram Moolenaar  call add(s:lines, "<meta http-equiv=\"content-type\" content=\"text/html; charset=" . s:settings.encoding . '"' . s:tag_close)
720bebca9daSBram Moolenaarendif
721bebca9daSBram Moolenaarcall extend(s:lines, [
722349b2fb0SBram Moolenaar      \ ("<title>".expand("%:p:~")."</title>"),
723349b2fb0SBram Moolenaar      \ ("<meta name=\"Generator\" content=\"Vim/".v:version/100.".".v:version%100.'"'.s:tag_close),
724bebca9daSBram Moolenaar      \ ("<meta name=\"plugin-version\" content=\"".g:loaded_2html_plugin.'"'.s:tag_close)
725349b2fb0SBram Moolenaar      \ ])
7267510fe74SBram Moolenaarcall add(s:lines, '<meta name="syntax" content="'.s:current_syntax.'"'.s:tag_close)
727076e8b2aSBram Moolenaarcall add(s:lines, '<meta name="settings" content="'.
728076e8b2aSBram Moolenaar      \ join(filter(keys(s:settings),'s:settings[v:val]'),',').
7296c35beaaSBram Moolenaar      \ ',prevent_copy='.s:settings.prevent_copy.
730076e8b2aSBram Moolenaar      \ '"'.s:tag_close)
7316c35beaaSBram Moolenaarcall add(s:lines, '<meta name="colorscheme" content="'.
7326c35beaaSBram Moolenaar      \ (exists('g:colors_name')
7336c35beaaSBram Moolenaar      \ ? g:colors_name
7346c35beaaSBram Moolenaar      \ : 'none'). '"'.s:tag_close)
735313b7237SBram Moolenaar
736076e8b2aSBram Moolenaarif s:settings.use_css
737076e8b2aSBram Moolenaar  if s:settings.dynamic_folds
738076e8b2aSBram Moolenaar    if s:settings.hover_unfold
7395c73622aSBram Moolenaar      " if we are doing hover_unfold, use css 2 with css 1 fallback for IE6
740349b2fb0SBram Moolenaar      call extend(s:lines, [
741349b2fb0SBram Moolenaar	    \ "<style type=\"text/css\">",
742bebca9daSBram Moolenaar	    \ s:settings.use_xhtml ? "" : "<!--",
743349b2fb0SBram Moolenaar	    \ ".FoldColumn { text-decoration: none; white-space: pre; }",
744349b2fb0SBram Moolenaar	    \ "",
745349b2fb0SBram Moolenaar	    \ "body * { margin: 0; padding: 0; }", "",
746349b2fb0SBram Moolenaar	    \ ".open-fold   > .Folded { display: none;  }",
747349b2fb0SBram Moolenaar	    \ ".open-fold   > .fulltext { display: inline; }",
748349b2fb0SBram Moolenaar	    \ ".closed-fold > .fulltext { display: none;  }",
749349b2fb0SBram Moolenaar	    \ ".closed-fold > .Folded { display: inline; }",
750349b2fb0SBram Moolenaar	    \ "",
751349b2fb0SBram Moolenaar	    \ ".open-fold   > .toggle-open   { display: none;   }",
752349b2fb0SBram Moolenaar	    \ ".open-fold   > .toggle-closed { display: inline; }",
753349b2fb0SBram Moolenaar	    \ ".closed-fold > .toggle-open   { display: inline; }",
754349b2fb0SBram Moolenaar	    \ ".closed-fold > .toggle-closed { display: none;   }",
755349b2fb0SBram Moolenaar	    \ "", "",
756349b2fb0SBram Moolenaar	    \ '/* opening a fold while hovering won''t be supported by IE6 and other',
757349b2fb0SBram Moolenaar	    \ "similar browsers, but it should fail gracefully. */",
758349b2fb0SBram Moolenaar	    \ ".closed-fold:hover > .fulltext { display: inline; }",
759349b2fb0SBram Moolenaar	    \ ".closed-fold:hover > .toggle-filler { display: none; }",
760349b2fb0SBram Moolenaar	    \ ".closed-fold:hover > .Folded { display: none; }",
761bebca9daSBram Moolenaar	    \ s:settings.use_xhtml ? "" : '-->',
7628df7f888SBram Moolenaar	    \ '</style>'])
763bebca9daSBram Moolenaar      " TODO: IE7 doesn't *actually* support XHTML, maybe we should remove this.
764bebca9daSBram Moolenaar      " But if it's served up as tag soup, maybe the following will work, so
765bebca9daSBram Moolenaar      " leave it in for now.
766bebca9daSBram Moolenaar      call extend(s:lines, [
767349b2fb0SBram Moolenaar	    \ "<!--[if lt IE 7]><style type=\"text/css\">",
768349b2fb0SBram Moolenaar	    \ ".open-fold   .Folded      { display: none; }",
769349b2fb0SBram Moolenaar	    \ ".open-fold   .fulltext      { display: inline; }",
770349b2fb0SBram Moolenaar	    \ ".open-fold   .toggle-open   { display: none; }",
771349b2fb0SBram Moolenaar	    \ ".closed-fold .toggle-closed { display: inline; }",
772349b2fb0SBram Moolenaar	    \ "",
773349b2fb0SBram Moolenaar	    \ ".closed-fold .fulltext      { display: none; }",
774349b2fb0SBram Moolenaar	    \ ".closed-fold .Folded      { display: inline; }",
775349b2fb0SBram Moolenaar	    \ ".closed-fold .toggle-open   { display: inline; }",
776349b2fb0SBram Moolenaar	    \ ".closed-fold .toggle-closed { display: none; }",
777349b2fb0SBram Moolenaar	    \ "</style>",
778349b2fb0SBram Moolenaar	    \ "<![endif]-->",
779349b2fb0SBram Moolenaar	    \])
7805c73622aSBram Moolenaar    else
7815c73622aSBram Moolenaar      " if we aren't doing hover_unfold, use CSS 1 only
782349b2fb0SBram Moolenaar      call extend(s:lines, [
783349b2fb0SBram Moolenaar	    \ "<style type=\"text/css\">",
784bebca9daSBram Moolenaar	    \ s:settings.use_xhtml ? "" :"<!--",
785349b2fb0SBram Moolenaar	    \ ".FoldColumn { text-decoration: none; white-space: pre; }",
786349b2fb0SBram Moolenaar	    \ ".open-fold   .Folded      { display: none; }",
787349b2fb0SBram Moolenaar	    \ ".open-fold   .fulltext      { display: inline; }",
788349b2fb0SBram Moolenaar	    \ ".open-fold   .toggle-open   { display: none; }",
789349b2fb0SBram Moolenaar	    \ ".closed-fold .toggle-closed { display: inline; }",
790349b2fb0SBram Moolenaar	    \ "",
791349b2fb0SBram Moolenaar	    \ ".closed-fold .fulltext      { display: none; }",
792349b2fb0SBram Moolenaar	    \ ".closed-fold .Folded      { display: inline; }",
793349b2fb0SBram Moolenaar	    \ ".closed-fold .toggle-open   { display: inline; }",
794349b2fb0SBram Moolenaar	    \ ".closed-fold .toggle-closed { display: none; }",
795bebca9daSBram Moolenaar	    \ s:settings.use_xhtml ? "" : '-->',
796349b2fb0SBram Moolenaar	    \ '</style>'
797349b2fb0SBram Moolenaar	    \])
7985c73622aSBram Moolenaar    endif
7995c73622aSBram Moolenaar  else
8005c73622aSBram Moolenaar    " if we aren't doing any dynamic folding, no need for any special rules
801349b2fb0SBram Moolenaar    call extend(s:lines, [
802349b2fb0SBram Moolenaar	  \ "<style type=\"text/css\">",
803bebca9daSBram Moolenaar	  \ s:settings.use_xhtml ? "" : "<!--",
804bebca9daSBram Moolenaar	  \ s:settings.use_xhtml ? "" : '-->',
805349b2fb0SBram Moolenaar	  \ "</style>",
806349b2fb0SBram Moolenaar	  \])
807071d4279SBram Moolenaar  endif
8085c73622aSBram Moolenaarendif
8095c73622aSBram Moolenaar
810543b7ef7SBram Moolenaar" insert script tag; javascript is always needed for the line number
811543b7ef7SBram Moolenaar" normalization for URL hashes
8126c35beaaSBram Moolenaarcall extend(s:lines, [
8136c35beaaSBram Moolenaar      \ "",
8146c35beaaSBram Moolenaar      \ "<script type='text/javascript'>",
8156c35beaaSBram Moolenaar      \ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"])
8166c35beaaSBram Moolenaar
8175c73622aSBram Moolenaar" insert javascript to toggle folds open and closed
818076e8b2aSBram Moolenaarif s:settings.dynamic_folds
819349b2fb0SBram Moolenaar  call extend(s:lines, [
820349b2fb0SBram Moolenaar	\ "",
821349b2fb0SBram Moolenaar	\ "function toggleFold(objID)",
822349b2fb0SBram Moolenaar	\ "{",
823349b2fb0SBram Moolenaar	\ "  var fold;",
824349b2fb0SBram Moolenaar	\ "  fold = document.getElementById(objID);",
825349b2fb0SBram Moolenaar	\ "  if(fold.className == 'closed-fold')",
826349b2fb0SBram Moolenaar	\ "  {",
827349b2fb0SBram Moolenaar	\ "    fold.className = 'open-fold';",
828349b2fb0SBram Moolenaar	\ "  }",
829349b2fb0SBram Moolenaar	\ "  else if (fold.className == 'open-fold')",
830349b2fb0SBram Moolenaar	\ "  {",
831349b2fb0SBram Moolenaar	\ "    fold.className = 'closed-fold';",
832349b2fb0SBram Moolenaar	\ "  }",
8336c35beaaSBram Moolenaar	\ "}"
8346c35beaaSBram Moolenaar	\ ])
8356c35beaaSBram Moolenaarendif
8366c35beaaSBram Moolenaar
83731c31679SBram Moolenaarif s:settings.line_ids
838543b7ef7SBram Moolenaar  " insert javascript to get IDs from line numbers, and to open a fold before
839543b7ef7SBram Moolenaar  " jumping to any lines contained therein
840543b7ef7SBram Moolenaar  call extend(s:lines, [
841543b7ef7SBram Moolenaar	\ "",
842543b7ef7SBram Moolenaar	\ "/* function to open any folds containing a jumped-to line before jumping to it */",
843543b7ef7SBram Moolenaar	\ "function JumpToLine()",
844543b7ef7SBram Moolenaar	\ "{",
845543b7ef7SBram Moolenaar	\ "  var lineNum;",
846543b7ef7SBram Moolenaar	\ "  lineNum = window.location.hash;",
847543b7ef7SBram Moolenaar	\ "  lineNum = lineNum.substr(1); /* strip off '#' */",
848543b7ef7SBram Moolenaar	\ "",
849543b7ef7SBram Moolenaar	\ "  if (lineNum.indexOf('L') == -1) {",
850543b7ef7SBram Moolenaar	\ "    lineNum = 'L'+lineNum;",
851543b7ef7SBram Moolenaar	\ "  }",
852543b7ef7SBram Moolenaar	\ "  lineElem = document.getElementById(lineNum);"
853543b7ef7SBram Moolenaar	\ ])
854543b7ef7SBram Moolenaar  if s:settings.dynamic_folds
855543b7ef7SBram Moolenaar    call extend(s:lines, [
856543b7ef7SBram Moolenaar	  \ "",
857543b7ef7SBram Moolenaar	  \ "  /* navigate upwards in the DOM tree to open all folds containing the line */",
858543b7ef7SBram Moolenaar	  \ "  var node = lineElem;",
85931c31679SBram Moolenaar	  \ "  while (node && node.id != 'vimCodeElement".s:settings.id_suffix."')",
860543b7ef7SBram Moolenaar	  \ "  {",
861543b7ef7SBram Moolenaar	  \ "    if (node.className == 'closed-fold')",
862543b7ef7SBram Moolenaar	  \ "    {",
863543b7ef7SBram Moolenaar	  \ "      node.className = 'open-fold';",
864543b7ef7SBram Moolenaar	  \ "    }",
865543b7ef7SBram Moolenaar	  \ "    node = node.parentNode;",
866543b7ef7SBram Moolenaar	  \ "  }",
867543b7ef7SBram Moolenaar	  \ ])
868543b7ef7SBram Moolenaar  endif
869543b7ef7SBram Moolenaar  call extend(s:lines, [
870543b7ef7SBram Moolenaar	\ "  /* Always jump to new location even if the line was hidden inside a fold, or",
871543b7ef7SBram Moolenaar	\ "   * we corrected the raw number to a line ID.",
872543b7ef7SBram Moolenaar	\ "   */",
873543b7ef7SBram Moolenaar	\ "  if (lineElem) {",
874543b7ef7SBram Moolenaar	\ "    lineElem.scrollIntoView(true);",
875543b7ef7SBram Moolenaar	\ "  }",
876543b7ef7SBram Moolenaar	\ "  return true;",
877543b7ef7SBram Moolenaar	\ "}",
878543b7ef7SBram Moolenaar	\ "if ('onhashchange' in window) {",
879543b7ef7SBram Moolenaar	\ "  window.onhashchange = JumpToLine;",
880543b7ef7SBram Moolenaar	\ "}"
881543b7ef7SBram Moolenaar	\ ])
88231c31679SBram Moolenaarendif
883543b7ef7SBram Moolenaar
8846c35beaaSBram Moolenaar" Small text columns like the foldcolumn and line number column need a weird
8856c35beaaSBram Moolenaar" hack to work around Webkit's and (in versions prior to 9) IE's lack of support
8866c35beaaSBram Moolenaar" for the 'ch' unit without messing up Opera, which also doesn't support it but
8876c35beaaSBram Moolenaar" works anyway.
8886c35beaaSBram Moolenaar"
8896c35beaaSBram Moolenaar" The problem is that without the 'ch' unit, it is not possible to specify a
8906c35beaaSBram Moolenaar" size of an <input> in terms of character widths. Only Opera seems to do the
8916c35beaaSBram Moolenaar" "sensible" thing and make the <input> sized to fit exactly as many characters
8926c35beaaSBram Moolenaar" as specified by its "size" attribute, but the spec actually says "at least
8936c35beaaSBram Moolenaar" wide enough to fit 'size' characters", so the other browsers are technically
8946c35beaaSBram Moolenaar" correct as well.
8956c35beaaSBram Moolenaar"
8966c35beaaSBram Moolenaar" Anyway, this leads to two diffculties:
8976c35beaaSBram Moolenaar"   1. The foldcolumn is made up of multiple elements side-by-side with
8986c35beaaSBram Moolenaar"      different sizes, each of which has their own extra padding added. Thus, a
8996c35beaaSBram Moolenaar"      column made up of one item of size 1 and another of size 2 would not
9006c35beaaSBram Moolenaar"      necessarily be equal in size to another line's foldcolumn with a single
9016c35beaaSBram Moolenaar"      item of size 3.
9026c35beaaSBram Moolenaar"   2. The extra padding added to the <input> elements adds up to make the
9036c35beaaSBram Moolenaar"      foldcolumn and line number column too wide, especially in Webkit
9046c35beaaSBram Moolenaar"      browsers.
9056c35beaaSBram Moolenaar"
9066c35beaaSBram Moolenaar" So, the full workaround is:
9076c35beaaSBram Moolenaar"   1. Define a default size in em, equal to the number of characters in the
9086c35beaaSBram Moolenaar"      input element, in case javascript is disabled and the browser does not
9096c35beaaSBram Moolenaar"      support the 'ch' unit. Unfortunately this makes Opera no longer work
9106c35beaaSBram Moolenaar"      properly without javascript. 1em per character is much too wide but it
9116c35beaaSBram Moolenaar"      looks better in webkit browsers than unaligned columns.
9126c35beaaSBram Moolenaar"   2. Insert the following javascript to run at page load, which checks for the
9136c35beaaSBram Moolenaar"      width of a single character (in an extraneous page element inserted
9146c35beaaSBram Moolenaar"      before the page title, and set to hidden) and compares it to the width of
9156c35beaaSBram Moolenaar"      another extra <input> element with only one character. If the width
9166c35beaaSBram Moolenaar"      matches, the script does nothing more, but if not, it will figure out the
9176c35beaaSBram Moolenaar"      fraction of an em unit which would correspond with a ch unit if there
9186c35beaaSBram Moolenaar"      were one, and set the containing element (<pre> or <div>) to a class with
9196c35beaaSBram Moolenaar"      pre-defined rules which is closest to that fraction of an em. Rules are
9206c35beaaSBram Moolenaar"      defined from 0.05 em to 1em per ch.
9216c35beaaSBram Moolenaarif !empty(s:settings.prevent_copy)
9226c35beaaSBram Moolenaar  call extend(s:lines, [
9236c35beaaSBram Moolenaar	\ '',
9246c35beaaSBram Moolenaar	\ '/* simulate a "ch" unit by asking the browser how big a zero character is */',
9256c35beaaSBram Moolenaar	\ 'function FixCharWidth() {',
9266c35beaaSBram Moolenaar	\ '  /* get the hidden element which gives the width of a single character */',
9276c35beaaSBram Moolenaar	\ '  var goodWidth = document.getElementById("oneCharWidth").clientWidth;',
9286c35beaaSBram Moolenaar	\ '  /* get all input elements, we''ll filter on class later */',
9296c35beaaSBram Moolenaar	\ '  var inputTags = document.getElementsByTagName("input");',
9306c35beaaSBram Moolenaar	\ '  var ratio = 5;',
9316c35beaaSBram Moolenaar	\ '  var inputWidth = document.getElementById("oneInputWidth").clientWidth;',
9326c35beaaSBram Moolenaar	\ '  var emWidth = document.getElementById("oneEmWidth").clientWidth;',
9336c35beaaSBram Moolenaar	\ '  if (inputWidth > goodWidth) {',
9346c35beaaSBram Moolenaar	\ '    while (ratio < 100*goodWidth/emWidth && ratio < 100) {',
9356c35beaaSBram Moolenaar	\ '      ratio += 5;',
9366c35beaaSBram Moolenaar	\ '    }',
93731c31679SBram Moolenaar	\ '    document.getElementById("vimCodeElement'.s:settings.id_suffix.'").className = "em"+ratio;',
9386c35beaaSBram Moolenaar	\ '  }',
9396c35beaaSBram Moolenaar	\ '}'
9406c35beaaSBram Moolenaar	\ ])
9416c35beaaSBram Moolenaarendif
9426c35beaaSBram Moolenaar
943543b7ef7SBram Moolenaar" insert script closing tag
9446c35beaaSBram Moolenaarcall extend(s:lines, [
9456c35beaaSBram Moolenaar      \ '',
946bebca9daSBram Moolenaar      \ s:settings.use_xhtml ? '//]]>' : '-->',
947349b2fb0SBram Moolenaar      \ "</script>"
948349b2fb0SBram Moolenaar      \ ])
9495c73622aSBram Moolenaar
9506c35beaaSBram Moolenaarcall extend(s:lines, ["</head>"])
9516c35beaaSBram Moolenaarif !empty(s:settings.prevent_copy)
9526c35beaaSBram Moolenaar  call extend(s:lines,
95331c31679SBram Moolenaar	\ ["<body onload='FixCharWidth();".(s:settings.line_ids ? " JumpToLine();" : "")."'>",
9546c35beaaSBram Moolenaar	\ "<!-- hidden divs used by javascript to get the width of a char -->",
9556c35beaaSBram Moolenaar	\ "<div id='oneCharWidth'>0</div>",
9566c35beaaSBram Moolenaar	\ "<div id='oneInputWidth'><input size='1' value='0'".s:tag_close."</div>",
9576c35beaaSBram Moolenaar	\ "<div id='oneEmWidth' style='width: 1em;'></div>"
9586c35beaaSBram Moolenaar	\ ])
959071d4279SBram Moolenaarelse
96031c31679SBram Moolenaar  call extend(s:lines, ["<body".(s:settings.line_ids ? " onload='JumpToLine();'" : "").">"])
9616c35beaaSBram Moolenaarendif
9626c35beaaSBram Moolenaarif s:settings.no_pre
9636c35beaaSBram Moolenaar  " if we're not using CSS we use a font tag which can't have a div inside
9646c35beaaSBram Moolenaar  if s:settings.use_css
96531c31679SBram Moolenaar    call extend(s:lines, ["<div id='vimCodeElement".s:settings.id_suffix."'>"])
9666c35beaaSBram Moolenaar  endif
9676c35beaaSBram Moolenaarelse
96831c31679SBram Moolenaar  call extend(s:lines, ["<pre id='vimCodeElement".s:settings.id_suffix."'>"])
969071d4279SBram Moolenaarendif
970071d4279SBram Moolenaar
971071d4279SBram Moolenaarexe s:orgwin . "wincmd w"
972071d4279SBram Moolenaar
9736c35beaaSBram Moolenaar" caches of style data
9746c35beaaSBram Moolenaar" initialize to include line numbers if using them
9756c35beaaSBram Moolenaarif s:settings.number_lines
9766c35beaaSBram Moolenaar  let s:stylelist = { s:LINENR_ID : ".LineNr { " . s:CSS1( s:LINENR_ID ) . "}" }
9776c35beaaSBram Moolenaarelse
9786c35beaaSBram Moolenaar  let s:stylelist = {}
9796c35beaaSBram Moolenaarendif
9806c35beaaSBram Moolenaarlet s:diffstylelist = {
9816c35beaaSBram Moolenaar      \   s:DIFF_A_ID : ".DiffAdd { " . s:CSS1( s:DIFF_A_ID ) . "}",
9826c35beaaSBram Moolenaar      \   s:DIFF_C_ID : ".DiffChange { " . s:CSS1( s:DIFF_C_ID ) . "}",
9836c35beaaSBram Moolenaar      \   s:DIFF_D_ID : ".DiffDelete { " . s:CSS1( s:DIFF_D_ID ) . "}",
9846c35beaaSBram Moolenaar      \   s:DIFF_T_ID : ".DiffText { " . s:CSS1( s:DIFF_T_ID ) . "}"
9856c35beaaSBram Moolenaar      \ }
986071d4279SBram Moolenaar
987349b2fb0SBram Moolenaar" set up progress bar in the status line
988076e8b2aSBram Moolenaarif !s:settings.no_progress
989349b2fb0SBram Moolenaar  " ProgressBar Indicator
990349b2fb0SBram Moolenaar  let s:progressbar={}
991349b2fb0SBram Moolenaar
992349b2fb0SBram Moolenaar  " Progessbar specific functions
993349b2fb0SBram Moolenaar  func! s:ProgressBar(title, max_value, winnr)
994349b2fb0SBram Moolenaar    let pgb=copy(s:progressbar)
995349b2fb0SBram Moolenaar    let pgb.title = a:title.' '
996349b2fb0SBram Moolenaar    let pgb.max_value = a:max_value
997349b2fb0SBram Moolenaar    let pgb.winnr = a:winnr
998349b2fb0SBram Moolenaar    let pgb.cur_value = 0
999349b2fb0SBram Moolenaar    let pgb.items = { 'title'   : { 'color' : 'Statusline' },
1000349b2fb0SBram Moolenaar	  \'bar'     : { 'color' : 'Statusline' , 'fillcolor' : 'DiffDelete' , 'bg' : 'Statusline' } ,
1001349b2fb0SBram Moolenaar	  \'counter' : { 'color' : 'Statusline' } }
1002349b2fb0SBram Moolenaar    let pgb.last_value = 0
1003349b2fb0SBram Moolenaar    let pgb.needs_redraw = 0
1004349b2fb0SBram Moolenaar    " Note that you must use len(split) instead of len() if you want to use
1005349b2fb0SBram Moolenaar    " unicode in title.
1006349b2fb0SBram Moolenaar    "
1007349b2fb0SBram Moolenaar    " Subtract 3 for spacing around the title.
1008349b2fb0SBram Moolenaar    " Subtract 4 for the percentage display.
1009349b2fb0SBram Moolenaar    " Subtract 2 for spacing before this.
1010349b2fb0SBram Moolenaar    " Subtract 2 more for the '|' on either side of the progress bar
1011349b2fb0SBram Moolenaar    let pgb.subtractedlen=len(split(pgb.title, '\zs'))+3+4+2+2
1012349b2fb0SBram Moolenaar    let pgb.max_len = 0
1013349b2fb0SBram Moolenaar    set laststatus=2
1014349b2fb0SBram Moolenaar    return pgb
1015349b2fb0SBram Moolenaar  endfun
1016349b2fb0SBram Moolenaar
1017349b2fb0SBram Moolenaar  " Function: progressbar.calculate_ticks() {{{1
1018349b2fb0SBram Moolenaar  func! s:progressbar.calculate_ticks(pb_len)
1019349b2fb0SBram Moolenaar    if a:pb_len<=0
1020349b2fb0SBram Moolenaar      let pb_len = 100
1021349b2fb0SBram Moolenaar    else
1022349b2fb0SBram Moolenaar      let pb_len = a:pb_len
1023349b2fb0SBram Moolenaar    endif
1024349b2fb0SBram Moolenaar    let self.progress_ticks = map(range(pb_len+1), "v:val * self.max_value / pb_len")
1025349b2fb0SBram Moolenaar  endfun
1026349b2fb0SBram Moolenaar
1027349b2fb0SBram Moolenaar  "Function: progressbar.paint()
1028349b2fb0SBram Moolenaar  func! s:progressbar.paint()
1029349b2fb0SBram Moolenaar    " Recalculate widths.
1030349b2fb0SBram Moolenaar    let max_len = winwidth(self.winnr)
1031349b2fb0SBram Moolenaar    let pb_len = 0
1032349b2fb0SBram Moolenaar    " always true on first call because of initial value of self.max_len
1033349b2fb0SBram Moolenaar    if max_len != self.max_len
1034349b2fb0SBram Moolenaar      let self.max_len = max_len
1035349b2fb0SBram Moolenaar
1036349b2fb0SBram Moolenaar      " Progressbar length
1037349b2fb0SBram Moolenaar      let pb_len = max_len - self.subtractedlen
1038349b2fb0SBram Moolenaar
1039349b2fb0SBram Moolenaar      call self.calculate_ticks(pb_len)
1040349b2fb0SBram Moolenaar
1041349b2fb0SBram Moolenaar      let self.needs_redraw = 1
1042349b2fb0SBram Moolenaar      let cur_value = 0
1043349b2fb0SBram Moolenaar      let self.pb_len = pb_len
1044349b2fb0SBram Moolenaar    else
1045349b2fb0SBram Moolenaar      " start searching at the last found index to make the search for the
1046349b2fb0SBram Moolenaar      " appropriate tick value normally take 0 or 1 comparisons
1047349b2fb0SBram Moolenaar      let cur_value = self.last_value
1048349b2fb0SBram Moolenaar      let pb_len = self.pb_len
1049349b2fb0SBram Moolenaar    endif
1050349b2fb0SBram Moolenaar
1051349b2fb0SBram Moolenaar    let cur_val_max = pb_len > 0 ? pb_len : 100
1052349b2fb0SBram Moolenaar
1053349b2fb0SBram Moolenaar    " find the current progress bar position based on precalculated thresholds
1054349b2fb0SBram Moolenaar    while cur_value < cur_val_max && self.cur_value > self.progress_ticks[cur_value]
1055349b2fb0SBram Moolenaar      let cur_value += 1
1056349b2fb0SBram Moolenaar    endwhile
1057349b2fb0SBram Moolenaar
1058349b2fb0SBram Moolenaar    " update progress bar
1059349b2fb0SBram Moolenaar    if self.last_value != cur_value || self.needs_redraw || self.cur_value == self.max_value
1060349b2fb0SBram Moolenaar      let self.needs_redraw = 1
1061349b2fb0SBram Moolenaar      let self.last_value = cur_value
1062349b2fb0SBram Moolenaar
1063349b2fb0SBram Moolenaar      let t_color  = self.items.title.color
1064349b2fb0SBram Moolenaar      let b_fcolor = self.items.bar.fillcolor
1065349b2fb0SBram Moolenaar      let b_color  = self.items.bar.color
1066349b2fb0SBram Moolenaar      let c_color  = self.items.counter.color
1067349b2fb0SBram Moolenaar
1068349b2fb0SBram Moolenaar      let stl =  "%#".t_color."#%-( ".self.title." %)".
1069349b2fb0SBram Moolenaar	    \"%#".b_color."#".
1070349b2fb0SBram Moolenaar	    \(pb_len>0 ?
1071349b2fb0SBram Moolenaar	    \	('|%#'.b_fcolor."#%-(".repeat(" ",cur_value)."%)".
1072349b2fb0SBram Moolenaar	    \	 '%#'.b_color."#".repeat(" ",pb_len-cur_value)."|"):
1073349b2fb0SBram Moolenaar	    \	('')).
1074349b2fb0SBram Moolenaar	    \"%=%#".c_color."#%( ".printf("%3.d ",100*self.cur_value/self.max_value)."%% %)"
1075349b2fb0SBram Moolenaar      call setwinvar(self.winnr, '&stl', stl)
1076349b2fb0SBram Moolenaar    endif
1077349b2fb0SBram Moolenaar  endfun
1078349b2fb0SBram Moolenaar
1079349b2fb0SBram Moolenaar  func! s:progressbar.incr( ... )
1080349b2fb0SBram Moolenaar    let self.cur_value += (a:0 ? a:1 : 1)
1081349b2fb0SBram Moolenaar    " if we were making a general-purpose progress bar, we'd need to limit to a
1082349b2fb0SBram Moolenaar    " lower limit as well, but since we always increment with a positive value
1083349b2fb0SBram Moolenaar    " in this script, we only need limit the upper value
1084349b2fb0SBram Moolenaar    let self.cur_value = (self.cur_value > self.max_value ? self.max_value : self.cur_value)
1085349b2fb0SBram Moolenaar    call self.paint()
1086349b2fb0SBram Moolenaar  endfun
1087349b2fb0SBram Moolenaar  " }}}
1088076e8b2aSBram Moolenaar  if s:settings.dynamic_folds
1089349b2fb0SBram Moolenaar    " to process folds we make two passes through each line
1090349b2fb0SBram Moolenaar    let s:pgb = s:ProgressBar("Processing folds:", line('$')*2, s:orgwin)
1091349b2fb0SBram Moolenaar  endif
1092349b2fb0SBram Moolenaarendif
1093349b2fb0SBram Moolenaar
10945c73622aSBram Moolenaar" First do some preprocessing for dynamic folding. Do this for the entire file
10955c73622aSBram Moolenaar" so we don't accidentally start within a closed fold or something.
10965c73622aSBram Moolenaarlet s:allfolds = []
10975c73622aSBram Moolenaar
1098076e8b2aSBram Moolenaarif s:settings.dynamic_folds
10995c73622aSBram Moolenaar  let s:lnum = 1
11005c73622aSBram Moolenaar  let s:end = line('$')
11015c73622aSBram Moolenaar  " save the fold text and set it to the default so we can find fold levels
11025c73622aSBram Moolenaar  let s:foldtext_save = &foldtext
1103349b2fb0SBram Moolenaar  setlocal foldtext&
11045c73622aSBram Moolenaar
11055c73622aSBram Moolenaar  " we will set the foldcolumn in the html to the greater of the maximum fold
11065c73622aSBram Moolenaar  " level and the current foldcolumn setting
11075c73622aSBram Moolenaar  let s:foldcolumn = &foldcolumn
11085c73622aSBram Moolenaar
11095c73622aSBram Moolenaar  " get all info needed to describe currently closed folds
1110349b2fb0SBram Moolenaar  while s:lnum <= s:end
11115c73622aSBram Moolenaar    if foldclosed(s:lnum) == s:lnum
11125c73622aSBram Moolenaar      " default fold text has '+-' and then a number of dashes equal to fold
11135c73622aSBram Moolenaar      " level, so subtract 2 from index of first non-dash after the dashes
11145c73622aSBram Moolenaar      " in order to get the fold level of the current fold
11155c73622aSBram Moolenaar      let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2
11165c73622aSBram Moolenaar      " store fold info for later use
11175c73622aSBram Moolenaar      let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"}
11185c73622aSBram Moolenaar      call add(s:allfolds, s:newfold)
11195c73622aSBram Moolenaar      " open the fold so we can find any contained folds
11205c73622aSBram Moolenaar      execute s:lnum."foldopen"
11215c73622aSBram Moolenaar    else
1122076e8b2aSBram Moolenaar      if !s:settings.no_progress
1123349b2fb0SBram Moolenaar	call s:pgb.incr()
1124349b2fb0SBram Moolenaar	if s:pgb.needs_redraw
1125349b2fb0SBram Moolenaar	  redrawstatus
1126349b2fb0SBram Moolenaar	  let s:pgb.needs_redraw = 0
1127349b2fb0SBram Moolenaar	endif
1128349b2fb0SBram Moolenaar      endif
11295c73622aSBram Moolenaar      let s:lnum = s:lnum + 1
11305c73622aSBram Moolenaar    endif
11315c73622aSBram Moolenaar  endwhile
11325c73622aSBram Moolenaar
11335c73622aSBram Moolenaar  " close all folds to get info for originally open folds
11345c73622aSBram Moolenaar  silent! %foldclose!
11355c73622aSBram Moolenaar  let s:lnum = 1
11365c73622aSBram Moolenaar
11375c73622aSBram Moolenaar  " the originally open folds will be all folds we encounter that aren't
11385c73622aSBram Moolenaar  " already in the list of closed folds
1139349b2fb0SBram Moolenaar  while s:lnum <= s:end
11405c73622aSBram Moolenaar    if foldclosed(s:lnum) == s:lnum
11415c73622aSBram Moolenaar      " default fold text has '+-' and then a number of dashes equal to fold
11425c73622aSBram Moolenaar      " level, so subtract 2 from index of first non-dash after the dashes
11435c73622aSBram Moolenaar      " in order to get the fold level of the current fold
11445c73622aSBram Moolenaar      let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2
11455c73622aSBram Moolenaar      let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"}
11465c73622aSBram Moolenaar      " only add the fold if we don't already have it
11475c73622aSBram Moolenaar      if empty(s:allfolds) || index(s:allfolds, s:newfold) == -1
11485c73622aSBram Moolenaar	let s:newfold.type = "open-fold"
11495c73622aSBram Moolenaar	call add(s:allfolds, s:newfold)
11505c73622aSBram Moolenaar      endif
11515c73622aSBram Moolenaar      " open the fold so we can find any contained folds
11525c73622aSBram Moolenaar      execute s:lnum."foldopen"
11535c73622aSBram Moolenaar    else
1154076e8b2aSBram Moolenaar      if !s:settings.no_progress
1155349b2fb0SBram Moolenaar	call s:pgb.incr()
1156349b2fb0SBram Moolenaar	if s:pgb.needs_redraw
1157349b2fb0SBram Moolenaar	  redrawstatus
1158349b2fb0SBram Moolenaar	  let s:pgb.needs_redraw = 0
1159349b2fb0SBram Moolenaar	endif
1160349b2fb0SBram Moolenaar      endif
11615c73622aSBram Moolenaar      let s:lnum = s:lnum + 1
11625c73622aSBram Moolenaar    endif
11635c73622aSBram Moolenaar  endwhile
11645c73622aSBram Moolenaar
11655c73622aSBram Moolenaar  " sort the folds so that we only ever need to look at the first item in the
11665c73622aSBram Moolenaar  " list of folds
11675c73622aSBram Moolenaar  call sort(s:allfolds, "s:FoldCompare")
11685c73622aSBram Moolenaar
1169349b2fb0SBram Moolenaar  let &l:foldtext = s:foldtext_save
11705c73622aSBram Moolenaar  unlet s:foldtext_save
11715c73622aSBram Moolenaar
11725c73622aSBram Moolenaar  " close all folds again so we can get the fold text as we go
11735c73622aSBram Moolenaar  silent! %foldclose!
11742a8a3ecbSBram Moolenaar
1175251e1912SBram Moolenaar  " Go through and remove folds we don't need to (or cannot) process in the
1176251e1912SBram Moolenaar  " current conversion range
1177251e1912SBram Moolenaar  "
1178251e1912SBram Moolenaar  " If a fold is removed which contains other folds, which are included, we need
1179251e1912SBram Moolenaar  " to adjust the level of the included folds as used by the conversion logic
1180251e1912SBram Moolenaar  " (avoiding special cases is good)
1181251e1912SBram Moolenaar  "
1182251e1912SBram Moolenaar  " Note any time we remove a fold, either all of the included folds are in it,
1183251e1912SBram Moolenaar  " or none of them, because we only remove a fold if neither its start nor its
1184251e1912SBram Moolenaar  " end are within the conversion range.
1185251e1912SBram Moolenaar  let leveladjust = 0
11862a8a3ecbSBram Moolenaar  for afold in s:allfolds
11872a8a3ecbSBram Moolenaar    let removed = 0
11882a8a3ecbSBram Moolenaar    if exists("g:html_start_line") && exists("g:html_end_line")
11892a8a3ecbSBram Moolenaar      if afold.firstline < g:html_start_line
1190251e1912SBram Moolenaar	if afold.lastline <= g:html_end_line && afold.lastline >= g:html_start_line
11912a8a3ecbSBram Moolenaar	  " if a fold starts before the range to convert but stops within the
11922a8a3ecbSBram Moolenaar	  " range, we need to include it. Make it start on the first converted
11932a8a3ecbSBram Moolenaar	  " line.
11942a8a3ecbSBram Moolenaar	  let afold.firstline = g:html_start_line
11952a8a3ecbSBram Moolenaar	else
11962a8a3ecbSBram Moolenaar	  " if the fold lies outside the range or the start and stop enclose
11972a8a3ecbSBram Moolenaar	  " the entire range, don't bother parsing it
11982a8a3ecbSBram Moolenaar	  call remove(s:allfolds, index(s:allfolds, afold))
11992a8a3ecbSBram Moolenaar	  let removed = 1
1200251e1912SBram Moolenaar	  if afold.lastline > g:html_end_line
1201251e1912SBram Moolenaar	    let leveladjust += 1
1202251e1912SBram Moolenaar	  endif
12032a8a3ecbSBram Moolenaar	endif
12042a8a3ecbSBram Moolenaar      elseif afold.firstline > g:html_end_line
12052a8a3ecbSBram Moolenaar	" If the entire fold lies outside the range we need to remove it.
12062a8a3ecbSBram Moolenaar	call remove(s:allfolds, index(s:allfolds, afold))
12072a8a3ecbSBram Moolenaar	let removed = 1
12082a8a3ecbSBram Moolenaar      endif
12092a8a3ecbSBram Moolenaar    elseif exists("g:html_start_line")
12102a8a3ecbSBram Moolenaar      if afold.firstline < g:html_start_line
12112a8a3ecbSBram Moolenaar	" if there is no last line, but there is a first line, the end of the
12122a8a3ecbSBram Moolenaar	" fold will always lie within the region of interest, so keep it
12132a8a3ecbSBram Moolenaar	let afold.firstline = g:html_start_line
12142a8a3ecbSBram Moolenaar      endif
12152a8a3ecbSBram Moolenaar    elseif exists("g:html_end_line")
12162a8a3ecbSBram Moolenaar      " if there is no first line we default to the first line in the buffer so
12172a8a3ecbSBram Moolenaar      " the fold start will always be included if the fold itself is included.
12182a8a3ecbSBram Moolenaar      " If however the entire fold lies outside the range we need to remove it.
12192a8a3ecbSBram Moolenaar      if afold.firstline > g:html_end_line
12202a8a3ecbSBram Moolenaar	call remove(s:allfolds, index(s:allfolds, afold))
12212a8a3ecbSBram Moolenaar	let removed = 1
12222a8a3ecbSBram Moolenaar      endif
12232a8a3ecbSBram Moolenaar    endif
12242a8a3ecbSBram Moolenaar    if !removed
1225251e1912SBram Moolenaar      let afold.level -= leveladjust
12262a8a3ecbSBram Moolenaar      if afold.level+1 > s:foldcolumn
12272a8a3ecbSBram Moolenaar	let s:foldcolumn = afold.level+1
12282a8a3ecbSBram Moolenaar      endif
12292a8a3ecbSBram Moolenaar    endif
12302a8a3ecbSBram Moolenaar  endfor
1231251e1912SBram Moolenaar
1232251e1912SBram Moolenaar  " if we've removed folds containing the conversion range from processing,
1233251e1912SBram Moolenaar  " getting foldtext as we go won't know to open the removed folds, so the
1234251e1912SBram Moolenaar  " foldtext would be wrong; open them now.
1235251e1912SBram Moolenaar  "
1236251e1912SBram Moolenaar  " Note that only when a start and an end line is specified will a fold
1237251e1912SBram Moolenaar  " containing the current range ever be removed.
1238251e1912SBram Moolenaar  while leveladjust > 0
1239251e1912SBram Moolenaar    exe g:html_start_line."foldopen"
1240251e1912SBram Moolenaar    let leveladjust -= 1
1241251e1912SBram Moolenaar  endwhile
12425c73622aSBram Moolenaarendif
12435c73622aSBram Moolenaar
12445c73622aSBram Moolenaar" Now loop over all lines in the original text to convert to html.
1245071d4279SBram Moolenaar" Use html_start_line and html_end_line if they are set.
1246b02cbe34SBram Moolenaarif exists("g:html_start_line")
1247071d4279SBram Moolenaar  let s:lnum = html_start_line
1248071d4279SBram Moolenaar  if s:lnum < 1 || s:lnum > line("$")
1249071d4279SBram Moolenaar    let s:lnum = 1
1250071d4279SBram Moolenaar  endif
1251071d4279SBram Moolenaarelse
1252071d4279SBram Moolenaar  let s:lnum = 1
1253071d4279SBram Moolenaarendif
1254b02cbe34SBram Moolenaarif exists("g:html_end_line")
1255071d4279SBram Moolenaar  let s:end = html_end_line
1256071d4279SBram Moolenaar  if s:end < s:lnum || s:end > line("$")
1257071d4279SBram Moolenaar    let s:end = line("$")
1258071d4279SBram Moolenaar  endif
1259071d4279SBram Moolenaarelse
1260071d4279SBram Moolenaar  let s:end = line("$")
1261071d4279SBram Moolenaarendif
1262071d4279SBram Moolenaar
12635c73622aSBram Moolenaar" stack to keep track of all the folds containing the current line
12645c73622aSBram Moolenaarlet s:foldstack = []
12655c73622aSBram Moolenaar
1266076e8b2aSBram Moolenaarif !s:settings.no_progress
1267349b2fb0SBram Moolenaar  let s:pgb = s:ProgressBar("Processing lines:", s:end - s:lnum + 1, s:orgwin)
1268349b2fb0SBram Moolenaarendif
1269349b2fb0SBram Moolenaar
1270076e8b2aSBram Moolenaarif s:settings.number_lines
12715c73622aSBram Moolenaar  let s:margin = strlen(s:end) + 1
12725c73622aSBram Moolenaarelse
12735c73622aSBram Moolenaar  let s:margin = 0
12745c73622aSBram Moolenaarendif
12755c73622aSBram Moolenaar
1276076e8b2aSBram Moolenaarif has('folding') && !s:settings.ignore_folding
127735a9aaabSBram Moolenaar  let s:foldfillchar = &fillchars[matchend(&fillchars, 'fold:')]
127835a9aaabSBram Moolenaar  if s:foldfillchar == ''
127935a9aaabSBram Moolenaar    let s:foldfillchar = '-'
12807b0294cbSBram Moolenaar  endif
128135a9aaabSBram Moolenaarendif
128235a9aaabSBram Moolenaarlet s:difffillchar = &fillchars[matchend(&fillchars, 'diff:')]
128335a9aaabSBram Moolenaarif s:difffillchar == ''
128435a9aaabSBram Moolenaar  let s:difffillchar = '-'
12857b0294cbSBram Moolenaarendif
12867b0294cbSBram Moolenaar
12875c73622aSBram Moolenaarlet s:foldId = 0
12887b0294cbSBram Moolenaar
12892a8a3ecbSBram Moolenaarif !s:settings.expand_tabs
12902a8a3ecbSBram Moolenaar  " If keeping tabs, add them to printable characters so we keep them when
12912a8a3ecbSBram Moolenaar  " formatting text (strtrans() doesn't replace printable chars)
12922a8a3ecbSBram Moolenaar  let s:old_isprint = &isprint
12932a8a3ecbSBram Moolenaar  setlocal isprint+=9
12942a8a3ecbSBram Moolenaarendif
12952a8a3ecbSBram Moolenaar
1296071d4279SBram Moolenaarwhile s:lnum <= s:end
1297071d4279SBram Moolenaar
129847136d70SBram Moolenaar  " If there are filler lines for diff mode, show these above the line.
129947136d70SBram Moolenaar  let s:filler = diff_filler(s:lnum)
130047136d70SBram Moolenaar  if s:filler > 0
130147136d70SBram Moolenaar    let s:n = s:filler
130247136d70SBram Moolenaar    while s:n > 0
130335a9aaabSBram Moolenaar      let s:new = repeat(s:difffillchar, 3)
130447136d70SBram Moolenaar
1305076e8b2aSBram Moolenaar      if s:n > 2 && s:n < s:filler && !s:settings.whole_filler
130635a9aaabSBram Moolenaar	let s:new = s:new . " " . s:filler . " inserted lines "
130735a9aaabSBram Moolenaar	let s:n = 2
130835a9aaabSBram Moolenaar      endif
130935a9aaabSBram Moolenaar
1310076e8b2aSBram Moolenaar      if !s:settings.no_pre
131135a9aaabSBram Moolenaar	" HTML line wrapping is off--go ahead and fill to the margin
13126c35beaaSBram Moolenaar	" TODO: what about when CSS wrapping is turned on?
13135c73622aSBram Moolenaar	let s:new = s:new . repeat(s:difffillchar, &columns - strlen(s:new) - s:margin)
13145c73622aSBram Moolenaar      else
13155c73622aSBram Moolenaar	let s:new = s:new . repeat(s:difffillchar, 3)
131635a9aaabSBram Moolenaar      endif
131735a9aaabSBram Moolenaar
13186c35beaaSBram Moolenaar      let s:new = s:HtmlFormat_d(s:new, s:DIFF_D_ID, 0)
1319076e8b2aSBram Moolenaar      if s:settings.number_lines
13206c35beaaSBram Moolenaar	" Indent if line numbering is on. Indent gets style of line number
13216c35beaaSBram Moolenaar	" column.
1322543b7ef7SBram Moolenaar	let s:new = s:HtmlFormat_n(repeat(' ', s:margin), s:LINENR_ID, 0, 0) . s:new
13236c35beaaSBram Moolenaar      endif
13246c35beaaSBram Moolenaar      if s:settings.dynamic_folds && !s:settings.no_foldcolumn && s:foldcolumn > 0
13256c35beaaSBram Moolenaar	" Indent for foldcolumn if there is one. Assume it's empty, there should
13266c35beaaSBram Moolenaar	" not be a fold for deleted lines in diff mode.
13276c35beaaSBram Moolenaar	let s:new = s:FoldColumn_fill() . s:new
13285c73622aSBram Moolenaar      endif
1329349b2fb0SBram Moolenaar      call add(s:lines, s:new.s:HtmlEndline)
133035a9aaabSBram Moolenaar
133147136d70SBram Moolenaar      let s:n = s:n - 1
133247136d70SBram Moolenaar    endwhile
133347136d70SBram Moolenaar    unlet s:n
133447136d70SBram Moolenaar  endif
133547136d70SBram Moolenaar  unlet s:filler
133647136d70SBram Moolenaar
133747136d70SBram Moolenaar  " Start the line with the line number.
1338076e8b2aSBram Moolenaar  if s:settings.number_lines
13395c73622aSBram Moolenaar    let s:numcol = repeat(' ', s:margin - 1 - strlen(s:lnum)) . s:lnum . ' '
134047136d70SBram Moolenaar  endif
134147136d70SBram Moolenaar
13425c73622aSBram Moolenaar  let s:new = ""
13435c73622aSBram Moolenaar
1344076e8b2aSBram Moolenaar  if has('folding') && !s:settings.ignore_folding && foldclosed(s:lnum) > -1 && !s:settings.dynamic_folds
13457b0294cbSBram Moolenaar    "
13465c73622aSBram Moolenaar    " This is the beginning of a folded block (with no dynamic folding)
13476c35beaaSBram Moolenaar    let s:new = foldtextresult(s:lnum)
1348076e8b2aSBram Moolenaar    if !s:settings.no_pre
134935a9aaabSBram Moolenaar      " HTML line wrapping is off--go ahead and fill to the margin
135035a9aaabSBram Moolenaar      let s:new = s:new . repeat(s:foldfillchar, &columns - strlen(s:new))
13517b0294cbSBram Moolenaar    endif
13527b0294cbSBram Moolenaar
13536c35beaaSBram Moolenaar    " put numcol in a separate group for sake of unselectable text
1354543b7ef7SBram Moolenaar    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)
13557b0294cbSBram Moolenaar
13567b0294cbSBram Moolenaar    " Skip to the end of the fold
1357349b2fb0SBram Moolenaar    let s:new_lnum = foldclosedend(s:lnum)
1358349b2fb0SBram Moolenaar
1359076e8b2aSBram Moolenaar    if !s:settings.no_progress
1360349b2fb0SBram Moolenaar      call s:pgb.incr(s:new_lnum - s:lnum)
1361349b2fb0SBram Moolenaar    endif
1362349b2fb0SBram Moolenaar
1363349b2fb0SBram Moolenaar    let s:lnum = s:new_lnum
13647b0294cbSBram Moolenaar
13657b0294cbSBram Moolenaar  else
13667b0294cbSBram Moolenaar    "
13675c73622aSBram Moolenaar    " A line that is not folded, or doing dynamic folding.
13687b0294cbSBram Moolenaar    "
136935a9aaabSBram Moolenaar    let s:line = getline(s:lnum)
13707b0294cbSBram Moolenaar    let s:len = strlen(s:line)
13717b0294cbSBram Moolenaar
1372076e8b2aSBram Moolenaar    if s:settings.dynamic_folds
13735c73622aSBram Moolenaar      " First insert a closing for any open folds that end on this line
13745c73622aSBram Moolenaar      while !empty(s:foldstack) && get(s:foldstack,0).lastline == s:lnum-1
13755c73622aSBram Moolenaar	let s:new = s:new."</span></span>"
13765c73622aSBram Moolenaar	call remove(s:foldstack, 0)
13775c73622aSBram Moolenaar      endwhile
13785c73622aSBram Moolenaar
13792a8a3ecbSBram Moolenaar      " Now insert an opening for any new folds that start on this line
13805c73622aSBram Moolenaar      let s:firstfold = 1
13815c73622aSBram Moolenaar      while !empty(s:allfolds) && get(s:allfolds,0).firstline == s:lnum
13825c73622aSBram Moolenaar	let s:foldId = s:foldId + 1
1383349b2fb0SBram Moolenaar	let s:new .= "<span id='"
1384349b2fb0SBram Moolenaar	let s:new .= (exists('g:html_diff_win_num') ? "win".g:html_diff_win_num : "")
138531c31679SBram Moolenaar	let s:new .= "fold".s:foldId.s:settings.id_suffix."' class='".s:allfolds[0].type."'>"
1386349b2fb0SBram Moolenaar
13875c73622aSBram Moolenaar
13885c73622aSBram Moolenaar	" Unless disabled, add a fold column for the opening line of a fold.
13895c73622aSBram Moolenaar	"
13905c73622aSBram Moolenaar	" Note that dynamic folds require using css so we just use css to take
13915c73622aSBram Moolenaar	" care of the leading spaces rather than using &nbsp; in the case of
13925c73622aSBram Moolenaar	" html_no_pre to make it easier
1393076e8b2aSBram Moolenaar	if !s:settings.no_foldcolumn
13945c73622aSBram Moolenaar	  " add fold column that can open the new fold
13955c73622aSBram Moolenaar	  if s:allfolds[0].level > 1 && s:firstfold
13966c35beaaSBram Moolenaar	    let s:new = s:new . s:FoldColumn_build('|', s:allfolds[0].level - 1, 0, "",
139731c31679SBram Moolenaar		  \ 'toggle-open FoldColumn','javascript:toggleFold("fold'.s:foldstack[0].id.s:settings.id_suffix.'");')
13985c73622aSBram Moolenaar	  endif
13996c35beaaSBram Moolenaar	  " add the filler spaces separately from the '+' char so that it can be
14006c35beaaSBram Moolenaar	  " shown/hidden separately during a hover unfold
14016c35beaaSBram Moolenaar	  let s:new = s:new . s:FoldColumn_build("+", 1, 0, "",
140231c31679SBram Moolenaar		\ 'toggle-open FoldColumn', 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
14035c73622aSBram Moolenaar	  " If this is not the last fold we're opening on this line, we need
14045c73622aSBram Moolenaar	  " to keep the filler spaces hidden if the fold is opened by mouse
14055c73622aSBram Moolenaar	  " hover. If it is the last fold to open in the line, we shouldn't hide
14065c73622aSBram Moolenaar	  " them, so don't apply the toggle-filler class.
14076c35beaaSBram Moolenaar	  let s:new = s:new . s:FoldColumn_build(" ", 1, s:foldcolumn - s:allfolds[0].level - 1, "",
14086c35beaaSBram Moolenaar		\ 'toggle-open FoldColumn'. (get(s:allfolds, 1, {'firstline': 0}).firstline == s:lnum ?" toggle-filler" :""),
140931c31679SBram Moolenaar		\ 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
14105c73622aSBram Moolenaar
14115c73622aSBram Moolenaar	  " add fold column that can close the new fold
14126c35beaaSBram Moolenaar	  " only add extra blank space if we aren't opening another fold on the
14136c35beaaSBram Moolenaar	  " same line
14145c73622aSBram Moolenaar	  if get(s:allfolds, 1, {'firstline': 0}).firstline != s:lnum
14156c35beaaSBram Moolenaar	    let s:extra_space = s:foldcolumn - s:allfolds[0].level
14166c35beaaSBram Moolenaar	  else
14176c35beaaSBram Moolenaar	    let s:extra_space = 0
14185c73622aSBram Moolenaar	  endif
14196c35beaaSBram Moolenaar	  if s:firstfold
14206c35beaaSBram Moolenaar	    " the first fold in a line has '|' characters from folds opened in
14216c35beaaSBram Moolenaar	    " previous lines, before the '-' for this fold
14226c35beaaSBram Moolenaar	    let s:new .= s:FoldColumn_build('|', s:allfolds[0].level - 1, s:extra_space, '-',
142331c31679SBram Moolenaar		  \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
14246c35beaaSBram Moolenaar	  else
14256c35beaaSBram Moolenaar	    " any subsequent folds in the line only add a single '-'
14266c35beaaSBram Moolenaar	    let s:new = s:new . s:FoldColumn_build("-", 1, s:extra_space, "",
142731c31679SBram Moolenaar		  \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
14286c35beaaSBram Moolenaar	  endif
14295c73622aSBram Moolenaar	  let s:firstfold = 0
14305c73622aSBram Moolenaar	endif
14315c73622aSBram Moolenaar
14326c35beaaSBram Moolenaar	" Add fold text, moving the span ending to the next line so collapsing
14336c35beaaSBram Moolenaar	" of folds works correctly.
14346c35beaaSBram Moolenaar	" Put numcol in a separate group for sake of unselectable text.
1435543b7ef7SBram Moolenaar	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', '')
14365c73622aSBram Moolenaar	let s:new = s:new . "<span class='fulltext'>"
14375c73622aSBram Moolenaar
14385c73622aSBram Moolenaar	" open the fold now that we have the fold text to allow retrieval of
14395c73622aSBram Moolenaar	" fold text for subsequent folds
14405c73622aSBram Moolenaar	execute s:lnum."foldopen"
14415c73622aSBram Moolenaar	call insert(s:foldstack, remove(s:allfolds,0))
14425c73622aSBram Moolenaar	let s:foldstack[0].id = s:foldId
14435c73622aSBram Moolenaar      endwhile
14445c73622aSBram Moolenaar
14455c73622aSBram Moolenaar      " Unless disabled, add a fold column for other lines.
14465c73622aSBram Moolenaar      "
14475c73622aSBram Moolenaar      " Note that dynamic folds require using css so we just use css to take
14485c73622aSBram Moolenaar      " care of the leading spaces rather than using &nbsp; in the case of
14495c73622aSBram Moolenaar      " html_no_pre to make it easier
1450076e8b2aSBram Moolenaar      if !s:settings.no_foldcolumn
14515c73622aSBram Moolenaar	if empty(s:foldstack)
14527510fe74SBram Moolenaar	  " add the empty foldcolumn for unfolded lines if there is a fold
14537510fe74SBram Moolenaar	  " column at all
14547510fe74SBram Moolenaar	  if s:foldcolumn > 0
14556c35beaaSBram Moolenaar	    let s:new = s:new . s:FoldColumn_fill()
14567510fe74SBram Moolenaar	  endif
14575c73622aSBram Moolenaar	else
14585c73622aSBram Moolenaar	  " add the fold column for folds not on the opening line
14595c73622aSBram Moolenaar	  if get(s:foldstack, 0).firstline < s:lnum
14606c35beaaSBram Moolenaar	    let s:new = s:new . s:FoldColumn_build('|', s:foldstack[0].level, s:foldcolumn - s:foldstack[0].level, "",
146131c31679SBram Moolenaar		  \ 'FoldColumn', 'javascript:toggleFold("fold'.s:foldstack[0].id.s:settings.id_suffix.'");')
14625c73622aSBram Moolenaar	  endif
14635c73622aSBram Moolenaar	endif
14645c73622aSBram Moolenaar      endif
14655c73622aSBram Moolenaar    endif
14665c73622aSBram Moolenaar
14675c73622aSBram Moolenaar    " Now continue with the unfolded line text
1468076e8b2aSBram Moolenaar    if s:settings.number_lines
1469543b7ef7SBram Moolenaar      let s:new = s:new . s:HtmlFormat_n(s:numcol, s:LINENR_ID, 0, s:lnum)
147031c31679SBram Moolenaar    elseif s:settings.line_ids
1471543b7ef7SBram Moolenaar      let s:new = s:new . s:HtmlFormat_n("", s:LINENR_ID, 0, s:lnum)
1472071d4279SBram Moolenaar    endif
1473071d4279SBram Moolenaar
147447136d70SBram Moolenaar    " Get the diff attribute, if any.
147547136d70SBram Moolenaar    let s:diffattr = diff_hlID(s:lnum, 1)
147647136d70SBram Moolenaar
14777510fe74SBram Moolenaar    " initialize conceal info to act like not concealed, just in case
14787510fe74SBram Moolenaar    let s:concealinfo = [0, '']
14797510fe74SBram Moolenaar
1480071d4279SBram Moolenaar    " Loop over each character in the line
1481071d4279SBram Moolenaar    let s:col = 1
14828ada2ccaSBram Moolenaar
14838ada2ccaSBram Moolenaar    " most of the time we won't use the diff_id, initialize to zero
14848ada2ccaSBram Moolenaar    let s:diff_id = 0
14858ada2ccaSBram Moolenaar
148635a9aaabSBram Moolenaar    while s:col <= s:len || (s:col == 1 && s:diffattr)
1487071d4279SBram Moolenaar      let s:startcol = s:col " The start column for processing text
1488076e8b2aSBram Moolenaar      if !s:settings.ignore_conceal && has('conceal')
14897510fe74SBram Moolenaar	let s:concealinfo = synconcealed(s:lnum, s:col)
14907510fe74SBram Moolenaar      endif
1491076e8b2aSBram Moolenaar      if !s:settings.ignore_conceal && s:concealinfo[0]
14927510fe74SBram Moolenaar	let s:col = s:col + 1
14937510fe74SBram Moolenaar	" Speed loop (it's small - that's the trick)
14947510fe74SBram Moolenaar	" Go along till we find a change in the match sequence number (ending
14957510fe74SBram Moolenaar	" the specific concealed region) or until there are no more concealed
14967510fe74SBram Moolenaar	" characters.
14977510fe74SBram Moolenaar	while s:col <= s:len && s:concealinfo == synconcealed(s:lnum, s:col) | let s:col = s:col + 1 | endwhile
14987510fe74SBram Moolenaar      elseif s:diffattr
14998ada2ccaSBram Moolenaar	let s:diff_id = diff_hlID(s:lnum, s:col)
15008ada2ccaSBram Moolenaar	let s:id = synID(s:lnum, s:col, 1)
150147136d70SBram Moolenaar	let s:col = s:col + 1
150247136d70SBram Moolenaar	" Speed loop (it's small - that's the trick)
150347136d70SBram Moolenaar	" Go along till we find a change in hlID
15048ada2ccaSBram Moolenaar	while s:col <= s:len && s:id == synID(s:lnum, s:col, 1)
15058ada2ccaSBram Moolenaar	      \   && s:diff_id == diff_hlID(s:lnum, s:col) |
15068ada2ccaSBram Moolenaar	      \     let s:col = s:col + 1 |
15078ada2ccaSBram Moolenaar	      \ endwhile
1508076e8b2aSBram Moolenaar	if s:len < &columns && !s:settings.no_pre
15098ada2ccaSBram Moolenaar	  " Add spaces at the end of the raw text line to extend the changed
15108ada2ccaSBram Moolenaar	  " line to the full width.
15115c73622aSBram Moolenaar	  let s:line = s:line . repeat(' ', &columns - virtcol([s:lnum, s:len]) - s:margin)
151235a9aaabSBram Moolenaar	  let s:len = &columns
151335a9aaabSBram Moolenaar	endif
151447136d70SBram Moolenaar      else
1515071d4279SBram Moolenaar	let s:id = synID(s:lnum, s:col, 1)
1516071d4279SBram Moolenaar	let s:col = s:col + 1
1517071d4279SBram Moolenaar	" Speed loop (it's small - that's the trick)
1518071d4279SBram Moolenaar	" Go along till we find a change in synID
1519071d4279SBram Moolenaar	while s:col <= s:len && s:id == synID(s:lnum, s:col, 1) | let s:col = s:col + 1 | endwhile
152047136d70SBram Moolenaar      endif
1521071d4279SBram Moolenaar
1522076e8b2aSBram Moolenaar      if s:settings.ignore_conceal || !s:concealinfo[0]
15232a8a3ecbSBram Moolenaar	" Expand tabs if needed
152435a9aaabSBram Moolenaar	let s:expandedtab = strpart(s:line, s:startcol - 1, s:col - s:startcol)
15252a8a3ecbSBram Moolenaar	if s:settings.expand_tabs
15265c73622aSBram Moolenaar	  let s:offset = 0
15275c73622aSBram Moolenaar	  let s:idx = stridx(s:expandedtab, "\t")
15285c73622aSBram Moolenaar	  while s:idx >= 0
15295c73622aSBram Moolenaar	    if has("multi_byte_encoding")
15305c73622aSBram Moolenaar	      if s:startcol + s:idx == 1
15315c73622aSBram Moolenaar		let s:i = &ts
15325c73622aSBram Moolenaar	      else
15335c73622aSBram Moolenaar		if s:idx == 0
15345c73622aSBram Moolenaar		  let s:prevc = matchstr(s:line, '.\%' . (s:startcol + s:idx + s:offset) . 'c')
15355c73622aSBram Moolenaar		else
15365c73622aSBram Moolenaar		  let s:prevc = matchstr(s:expandedtab, '.\%' . (s:idx + 1) . 'c')
15375c73622aSBram Moolenaar		endif
15385c73622aSBram Moolenaar		let s:vcol = virtcol([s:lnum, s:startcol + s:idx + s:offset - len(s:prevc)])
15395c73622aSBram Moolenaar		let s:i = &ts - (s:vcol % &ts)
15405c73622aSBram Moolenaar	      endif
15415c73622aSBram Moolenaar	      let s:offset -= s:i - 1
15425c73622aSBram Moolenaar	    else
15435c73622aSBram Moolenaar	      let s:i = &ts - ((s:idx + s:startcol - 1) % &ts)
15445c73622aSBram Moolenaar	    endif
15455c73622aSBram Moolenaar	    let s:expandedtab = substitute(s:expandedtab, '\t', repeat(' ', s:i), '')
15465c73622aSBram Moolenaar	    let s:idx = stridx(s:expandedtab, "\t")
154735a9aaabSBram Moolenaar	  endwhile
15482a8a3ecbSBram Moolenaar	end
154935a9aaabSBram Moolenaar
15507510fe74SBram Moolenaar	" get the highlight group name to use
1551071d4279SBram Moolenaar	let s:id = synIDtrans(s:id)
15527510fe74SBram Moolenaar      else
15537510fe74SBram Moolenaar	" use Conceal highlighting for concealed text
15546c35beaaSBram Moolenaar	let s:id = s:CONCEAL_ID
15557510fe74SBram Moolenaar	let s:expandedtab = s:concealinfo[1]
15567510fe74SBram Moolenaar      endif
15577510fe74SBram Moolenaar
15586c35beaaSBram Moolenaar      " Output the text with the same synID, with class set to the highlight ID
15596c35beaaSBram Moolenaar      " name, unless it has been concealed completely.
15607510fe74SBram Moolenaar      if strlen(s:expandedtab) > 0
1561543b7ef7SBram Moolenaar	let s:new = s:new . s:HtmlFormat(s:expandedtab,  s:id, s:diff_id, "", 0)
15627510fe74SBram Moolenaar      endif
1563071d4279SBram Moolenaar    endwhile
15647b0294cbSBram Moolenaar  endif
1565071d4279SBram Moolenaar
15667510fe74SBram Moolenaar  call extend(s:lines, split(s:new.s:HtmlEndline, '\n', 1))
1567076e8b2aSBram Moolenaar  if !s:settings.no_progress && s:pgb.needs_redraw
1568349b2fb0SBram Moolenaar    redrawstatus
1569349b2fb0SBram Moolenaar    let s:pgb.needs_redraw = 0
1570349b2fb0SBram Moolenaar  endif
1571071d4279SBram Moolenaar  let s:lnum = s:lnum + 1
1572313b7237SBram Moolenaar
1573076e8b2aSBram Moolenaar  if !s:settings.no_progress
1574349b2fb0SBram Moolenaar    call s:pgb.incr()
1575349b2fb0SBram Moolenaar  endif
1576349b2fb0SBram Moolenaarendwhile
1577349b2fb0SBram Moolenaar
1578076e8b2aSBram Moolenaarif s:settings.dynamic_folds
15795c73622aSBram Moolenaar  " finish off any open folds
15805c73622aSBram Moolenaar  while !empty(s:foldstack)
1581349b2fb0SBram Moolenaar    let s:lines[-1].="</span></span>"
15825c73622aSBram Moolenaar    call remove(s:foldstack, 0)
15835c73622aSBram Moolenaar  endwhile
15845c73622aSBram Moolenaar
15855c73622aSBram Moolenaar  " add fold column to the style list if not already there
15866c35beaaSBram Moolenaar  let s:id = s:FOLD_C_ID
15876c35beaaSBram Moolenaar  if !has_key(s:stylelist, s:id)
15886c35beaaSBram Moolenaar    let s:stylelist[s:id] = '.FoldColumn { ' . s:CSS1(s:id) . '}'
15895c73622aSBram Moolenaar  endif
15905c73622aSBram Moolenaarendif
15915c73622aSBram Moolenaar
1592076e8b2aSBram Moolenaarif s:settings.no_pre
1593076e8b2aSBram Moolenaar  if !s:settings.use_css
15948ada2ccaSBram Moolenaar    " Close off the font tag that encapsulates the whole <body>
1595bebca9daSBram Moolenaar    call extend(s:lines, ["</font>", "</body>", "</html>"])
15968ada2ccaSBram Moolenaar  else
15976c35beaaSBram Moolenaar    call extend(s:lines, ["</div>", "</body>", "</html>"])
15988ada2ccaSBram Moolenaar  endif
1599071d4279SBram Moolenaarelse
1600349b2fb0SBram Moolenaar  call extend(s:lines, ["</pre>", "</body>", "</html>"])
1601071d4279SBram Moolenaarendif
1602071d4279SBram Moolenaar
1603349b2fb0SBram Moolenaarexe s:newwin . "wincmd w"
1604349b2fb0SBram Moolenaarcall setline(1, s:lines)
1605349b2fb0SBram Moolenaarunlet s:lines
1606071d4279SBram Moolenaar
16076c35beaaSBram Moolenaar" Mangle modelines so Vim doesn't try to use HTML text as a modeline if editing
16086c35beaaSBram Moolenaar" this file in the future; need to do this after generating all the text in case
16096c35beaaSBram Moolenaar" the modeline text has different highlight groups which all turn out to be
16106c35beaaSBram Moolenaar" stripped from the final output.
1611dd007ed5SBram Moolenaar%s!\v(%(^|\s+)%([Vv]i%(m%([<=>]?\d+)?)?|ex)):!\1\&#0058;!ge
16126c35beaaSBram Moolenaar
1613543b7ef7SBram Moolenaar" The generated HTML is admittedly ugly and takes a LONG time to fold.
1614543b7ef7SBram Moolenaar" Make sure the user doesn't do syntax folding when loading a generated file,
1615543b7ef7SBram Moolenaar" using a modeline.
1616543b7ef7SBram Moolenaarcall append(line('$'), "<!-- vim: set foldmethod=manual : -->")
1617543b7ef7SBram Moolenaar
1618071d4279SBram Moolenaar" Now, when we finally know which, we define the colors and styles
1619076e8b2aSBram Moolenaarif s:settings.use_css
1620071d4279SBram Moolenaar  1;/<style type="text/+1
1621071d4279SBram Moolenaarendif
1622071d4279SBram Moolenaar
1623071d4279SBram Moolenaar" Normal/global attributes
1624071d4279SBram Moolenaar" For Netscape 4, set <body> attributes too, though, strictly speaking, it's
1625071d4279SBram Moolenaar" incorrect.
1626076e8b2aSBram Moolenaarif s:settings.use_css
1627076e8b2aSBram Moolenaar  if s:settings.no_pre
16286c35beaaSBram Moolenaar    call append('.', "body { color: " . s:fgc . "; background-color: " . s:bgc . "; font-family: ". s:htmlfont ."; }")
16296c35beaaSBram Moolenaar    +
1630071d4279SBram Moolenaar  else
16316c35beaaSBram Moolenaar    call append('.', "pre { " . s:whitespace . "font-family: ". s:htmlfont ."; color: " . s:fgc . "; background-color: " . s:bgc . "; }")
16326c35beaaSBram Moolenaar    +
1633071d4279SBram Moolenaar    yank
1634071d4279SBram Moolenaar    put
1635071d4279SBram Moolenaar    execute "normal! ^cwbody\e"
16368e5af3e5SBram Moolenaar    " body should not have the wrap formatting, only the pre section
16378e5af3e5SBram Moolenaar    if s:whitespace != ''
16388e5af3e5SBram Moolenaar      exec 's#'.s:whitespace
16398e5af3e5SBram Moolenaar    endif
1640071d4279SBram Moolenaar  endif
16416c35beaaSBram Moolenaar  " fix browser inconsistencies (sometimes within the same browser) of different
16426c35beaaSBram Moolenaar  " default font size for different elements
16436c35beaaSBram Moolenaar  call append('.', '* { font-size: 1em; }')
16446c35beaaSBram Moolenaar  +
16456c35beaaSBram Moolenaar  " if we use any input elements for unselectable content, make sure they look
16466c35beaaSBram Moolenaar  " like normal text
16476c35beaaSBram Moolenaar  if !empty(s:settings.prevent_copy)
16486c35beaaSBram Moolenaar    call append('.', 'input { border: none; margin: 0; padding: 0; font-family: '.s:htmlfont.'; }')
16496c35beaaSBram Moolenaar    +
16506c35beaaSBram Moolenaar    " ch units for browsers which support them, em units for a somewhat
16516c35beaaSBram Moolenaar    " reasonable fallback. Also make sure the special elements for size
16526c35beaaSBram Moolenaar    " calculations aren't seen.
16536c35beaaSBram Moolenaar    call append('.', [
16546c35beaaSBram Moolenaar	  \ "input[size='1'] { width: 1em; width: 1ch; }",
16556c35beaaSBram Moolenaar	  \ "input[size='2'] { width: 2em; width: 2ch; }",
16566c35beaaSBram Moolenaar	  \ "input[size='3'] { width: 3em; width: 3ch; }",
16576c35beaaSBram Moolenaar	  \ "input[size='4'] { width: 4em; width: 4ch; }",
16586c35beaaSBram Moolenaar	  \ "input[size='5'] { width: 5em; width: 5ch; }",
16596c35beaaSBram Moolenaar	  \ "input[size='6'] { width: 6em; width: 6ch; }",
16606c35beaaSBram Moolenaar	  \ "input[size='7'] { width: 7em; width: 7ch; }",
16616c35beaaSBram Moolenaar	  \ "input[size='8'] { width: 8em; width: 8ch; }",
16626c35beaaSBram Moolenaar	  \ "input[size='9'] { width: 9em; width: 9ch; }",
16636c35beaaSBram Moolenaar	  \ "input[size='10'] { width: 10em; width: 10ch; }",
16646c35beaaSBram Moolenaar	  \ "input[size='11'] { width: 11em; width: 11ch; }",
16656c35beaaSBram Moolenaar	  \ "input[size='12'] { width: 12em; width: 12ch; }",
16666c35beaaSBram Moolenaar	  \ "input[size='13'] { width: 13em; width: 13ch; }",
16676c35beaaSBram Moolenaar	  \ "input[size='14'] { width: 14em; width: 14ch; }",
16686c35beaaSBram Moolenaar	  \ "input[size='15'] { width: 15em; width: 15ch; }",
16696c35beaaSBram Moolenaar	  \ "input[size='16'] { width: 16em; width: 16ch; }",
16706c35beaaSBram Moolenaar	  \ "input[size='17'] { width: 17em; width: 17ch; }",
16716c35beaaSBram Moolenaar	  \ "input[size='18'] { width: 18em; width: 18ch; }",
16726c35beaaSBram Moolenaar	  \ "input[size='19'] { width: 19em; width: 19ch; }",
16736c35beaaSBram Moolenaar	  \ "input[size='20'] { width: 20em; width: 20ch; }",
16746c35beaaSBram Moolenaar	  \ "#oneCharWidth, #oneEmWidth, #oneInputWidth { padding: 0; margin: 0; position: absolute; left: -999999px; visibility: hidden; }"
16756c35beaaSBram Moolenaar	  \ ])
16766c35beaaSBram Moolenaar    +21
16776c35beaaSBram Moolenaar    for w in range(5, 100, 5)
16786c35beaaSBram Moolenaar      let base = 0.01 * w
16796c35beaaSBram Moolenaar      call append('.', join(map(range(1,20), "'.em'.w.' input[size='''.v:val.'''] { width: '.string(v:val*base).'em; }'")))
16806c35beaaSBram Moolenaar      +
16816c35beaaSBram Moolenaar    endfor
16826c35beaaSBram Moolenaar    if s:settings.prevent_copy =~# 'f'
16836c35beaaSBram Moolenaar    " Make the cursor show active fold columns as active areas, and empty fold
16846c35beaaSBram Moolenaar    " columns as not interactive.
16856c35beaaSBram Moolenaar      call append('.', ['input.FoldColumn { cursor: pointer; }',
16866c35beaaSBram Moolenaar	    \ 'input.FoldColumn[value=""] { cursor: default; }'
16876c35beaaSBram Moolenaar	    \ ])
16886c35beaaSBram Moolenaar      +2
16896c35beaaSBram Moolenaar    endif
16906c35beaaSBram Moolenaar    " make line number column show as non-interactive if not selectable
16916c35beaaSBram Moolenaar    if s:settings.prevent_copy =~# 'n'
16926c35beaaSBram Moolenaar      call append('.', 'input.LineNr { cursor: default; }')
16936c35beaaSBram Moolenaar      +
16946c35beaaSBram Moolenaar    endif
16956c35beaaSBram Moolenaar    " make fold text and line number column within fold text show as
16966c35beaaSBram Moolenaar    " non-interactive if not selectable
16976c35beaaSBram Moolenaar    if (s:settings.prevent_copy =~# 'n' || s:settings.prevent_copy =~# 't') && !s:settings.ignore_folding
16986c35beaaSBram Moolenaar      call append('.', 'input.Folded { cursor: default; }')
16996c35beaaSBram Moolenaar      +
17006c35beaaSBram Moolenaar    endif
17016c35beaaSBram Moolenaar  endif
1702071d4279SBram Moolenaarelse
1703543b7ef7SBram Moolenaar  execute '%s:<body\([^>]*\):<body bgcolor="' . s:bgc . '" text="' . s:fgc . '"\1>\r<font face="'. s:htmlfont .'"'
1704071d4279SBram Moolenaarendif
1705071d4279SBram Moolenaar
17066c35beaaSBram Moolenaar" Gather attributes for all other classes. Do diff first so that normal
17076c35beaaSBram Moolenaar" highlight groups are inserted before it.
1708076e8b2aSBram Moolenaarif s:settings.use_css
17096c35beaaSBram Moolenaar  if s:diff_mode
17106c35beaaSBram Moolenaar    call append('.', filter(map(keys(s:diffstylelist), "s:diffstylelist[v:val]"), 'v:val != ""'))
17116c35beaaSBram Moolenaar  endif
17126c35beaaSBram Moolenaar  if !empty(s:stylelist)
17136c35beaaSBram Moolenaar    call append('.', filter(map(keys(s:stylelist), "s:stylelist[v:val]"), 'v:val != ""'))
1714071d4279SBram Moolenaar  endif
1715071d4279SBram Moolenaarendif
1716071d4279SBram Moolenaar
1717071d4279SBram Moolenaar" Add hyperlinks
17186c35beaaSBram Moolenaar" TODO: add option to not do this? Maybe just make the color the same as the
17196c35beaaSBram Moolenaar" text highlight group normally is?
17201cd871b5SBram Moolenaar%s+\(https\=://\S\{-}\)\(\([.,;:}]\=\(\s\|$\)\)\|[\\"'<>]\|&gt;\|&lt;\|&quot;\)+<a href="\1">\1</a>\2+ge
1721071d4279SBram Moolenaar
1722071d4279SBram Moolenaar" The DTD
1723076e8b2aSBram Moolenaarif s:settings.use_xhtml
1724076e8b2aSBram Moolenaar  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\">"
1725076e8b2aSBram Moolenaarelseif s:settings.use_css && !s:settings.no_pre
1726076e8b2aSBram Moolenaar  exe "normal! gg0i<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
17271cd871b5SBram Moolenaarelse
1728076e8b2aSBram Moolenaar  exe "normal! gg0i<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
17291cd871b5SBram Moolenaarendif
17301cd871b5SBram Moolenaar
1731076e8b2aSBram Moolenaarif s:settings.use_xhtml
17321cd871b5SBram Moolenaar  exe "normal! gg/<html/e\na xmlns=\"http://www.w3.org/1999/xhtml\"\e"
17331cd871b5SBram Moolenaarendif
1734071d4279SBram Moolenaar
1735071d4279SBram Moolenaar" Cleanup
1736071d4279SBram Moolenaar%s:\s\+$::e
1737071d4279SBram Moolenaar
17382a8a3ecbSBram Moolenaar" Restore old settings (new window first)
1739543b7ef7SBram Moolenaar"
1740543b7ef7SBram Moolenaar" Don't bother restoring foldmethod in case it was syntax because the markup is
1741543b7ef7SBram Moolenaar" so weirdly formatted it can take a LONG time.
17428df7f888SBram Moolenaarlet &l:foldenable = s:old_fen
1743071d4279SBram Moolenaarlet &report = s:old_report
1744071d4279SBram Moolenaarlet &title = s:old_title
1745071d4279SBram Moolenaarlet &icon = s:old_icon
1746071d4279SBram Moolenaarlet &paste = s:old_paste
1747071d4279SBram Moolenaarlet &magic = s:old_magic
1748071d4279SBram Moolenaarlet @/ = s:old_search
1749349b2fb0SBram Moolenaarlet &more = s:old_more
17502a8a3ecbSBram Moolenaar
17512a8a3ecbSBram Moolenaar" switch to original window to restore those settings
1752071d4279SBram Moolenaarexe s:orgwin . "wincmd w"
17532a8a3ecbSBram Moolenaar
17542a8a3ecbSBram Moolenaarif !s:settings.expand_tabs
17552a8a3ecbSBram Moolenaar  let &l:isprint = s:old_isprint
17562a8a3ecbSBram Moolenaarendif
1757166af9bbSBram Moolenaarlet &l:stl = s:origwin_stl
1758071d4279SBram Moolenaarlet &l:et = s:old_et
17598df7f888SBram Moolenaarlet &l:scrollbind = s:old_bind
17602a8a3ecbSBram Moolenaar
17612a8a3ecbSBram Moolenaar" and back to the new window again to end there
1762071d4279SBram Moolenaarexe s:newwin . "wincmd w"
17632a8a3ecbSBram Moolenaar
1764166af9bbSBram Moolenaarlet &l:stl = s:newwin_stl
1765349b2fb0SBram Moolenaarexec 'resize' s:old_winheight
1766349b2fb0SBram Moolenaarlet &l:winfixheight = s:old_winfixheight
1767071d4279SBram Moolenaar
1768349b2fb0SBram Moolenaarlet &ls=s:ls
1769349b2fb0SBram Moolenaar
1770071d4279SBram Moolenaar" Save a little bit of memory (worth doing?)
17718e5af3e5SBram Moolenaarunlet s:htmlfont s:whitespace
17728df7f888SBram Moolenaarunlet s:old_et s:old_paste s:old_icon s:old_report s:old_title s:old_search
1773543b7ef7SBram Moolenaarunlet s:old_magic s:old_more s:old_fen s:old_winheight
17742a8a3ecbSBram Moolenaarunlet! s:old_isprint
17756c35beaaSBram Moolenaarunlet s:whatterm s:stylelist s:diffstylelist s:lnum s:end s:margin s:fgc s:bgc s:old_winfixheight
17766c35beaaSBram Moolenaarunlet! s:col s:id s:attr s:len s:line s:new s:expandedtab s:concealinfo s:diff_mode
17778df7f888SBram Moolenaarunlet! s:orgwin s:newwin s:orgbufnr s:idx s:i s:offset s:ls s:origwin_stl
17788df7f888SBram Moolenaarunlet! s:newwin_stl s:current_syntax
177905159a0cSBram Moolenaarif !v:profiling
1780071d4279SBram Moolenaar  delfunc s:HtmlColor
178135a9aaabSBram Moolenaar  delfunc s:HtmlFormat
1782071d4279SBram Moolenaar  delfunc s:CSS1
17836c35beaaSBram Moolenaar  delfunc s:BuildStyleWrapper
1784076e8b2aSBram Moolenaar  if !s:settings.use_css
1785071d4279SBram Moolenaar    delfunc s:HtmlOpening
1786071d4279SBram Moolenaar    delfunc s:HtmlClosing
1787071d4279SBram Moolenaar  endif
1788076e8b2aSBram Moolenaar  if s:settings.dynamic_folds
17895c73622aSBram Moolenaar    delfunc s:FoldCompare
17905c73622aSBram Moolenaar  endif
17915c73622aSBram Moolenaar
1792076e8b2aSBram Moolenaar  if !s:settings.no_progress
1793349b2fb0SBram Moolenaar    delfunc s:ProgressBar
1794349b2fb0SBram Moolenaar    delfunc s:progressbar.paint
1795349b2fb0SBram Moolenaar    delfunc s:progressbar.incr
1796349b2fb0SBram Moolenaar    unlet s:pgb s:progressbar
1797349b2fb0SBram Moolenaar  endif
1798349b2fb0SBram Moolenaarendif
1799349b2fb0SBram Moolenaar
18008df7f888SBram Moolenaarunlet! s:new_lnum s:diffattr s:difffillchar s:foldfillchar s:HtmlSpace
18016c35beaaSBram Moolenaarunlet! s:LeadingSpace s:HtmlEndline s:firstfold s:numcol s:foldcolumn
18026c35beaaSBram Moolenaarunlet s:foldstack s:allfolds s:foldId s:settings
18035c73622aSBram Moolenaar
18045c73622aSBram Moolenaarlet &cpo = s:cpo_sav
1805349b2fb0SBram Moolenaarunlet! s:cpo_sav
18065c73622aSBram Moolenaar
1807349b2fb0SBram Moolenaar" Make sure any patches will probably use consistent indent
18087c86f4ccSBram Moolenaar"   vim: ts=8 sw=2 sts=2 noet
1809