blob: 187b1be1b0da8d1de457c687aae2470d558ed54f [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001" Vim syntax support file
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02002" Maintainer: Ben Fritz <fritzophrenic@gmail.com>
Bram Moolenaardd007ed2013-07-09 15:44:17 +02003" Last Change: 2013 Jul 08
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02004"
5" Additional contributors:
6"
Bram Moolenaar7510fe72010-07-25 12:46:44 +02007" Original by Bram Moolenaar <Bram@vim.org>
8" Modified by David Ne\v{c}as (Yeti) <yeti@physics.muni.cz>
9" XHTML support by Panagiotis Issaris <takis@lumumba.luc.ac.be>
10" Made w3 compliant by Edd Barrett <vext01@gmail.com>
11" Added html_font. Edd Barrett <vext01@gmail.com>
12" Progress bar based off code from "progressbar widget" plugin by
13" Andreas Politz, heavily modified:
14" http://www.vim.org/scripts/script.php?script_id=2006
Bram Moolenaar349b2fb2010-07-16 20:35:36 +020015"
Bram Moolenaar7510fe72010-07-25 12:46:44 +020016" See Mercurial change logs for more!
Bram Moolenaar071d4272004-06-13 20:20:40 +000017
18" Transform a file into HTML, using the current syntax highlighting.
19
Bram Moolenaar5c736222010-01-06 20:54:52 +010020" this file uses line continuations
21let s:cpo_sav = &cpo
Bram Moolenaar349b2fb2010-07-16 20:35:36 +020022let s:ls = &ls
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020023set cpo&vim
Bram Moolenaar5c736222010-01-06 20:54:52 +010024
Bram Moolenaar349b2fb2010-07-16 20:35:36 +020025let s:end=line('$')
Bram Moolenaar349b2fb2010-07-16 20:35:36 +020026
Bram Moolenaar313b7232007-05-05 17:56:55 +000027" Font
Bram Moolenaarb02cbe32010-07-11 22:38:52 +020028if exists("g:html_font")
Bram Moolenaar076e8b22010-08-05 21:54:00 +020029 let s:htmlfont = "'". g:html_font . "', monospace"
Bram Moolenaar313b7232007-05-05 17:56:55 +000030else
31 let s:htmlfont = "monospace"
32endif
33
Bram Moolenaar076e8b22010-08-05 21:54:00 +020034let s:settings = tohtml#GetUserSettings()
Bram Moolenaar5c736222010-01-06 20:54:52 +010035
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020036if !exists('s:FOLDED_ID')
37 let s:FOLDED_ID = hlID("Folded") | lockvar s:FOLDED_ID
38 let s:FOLD_C_ID = hlID("FoldColumn") | lockvar s:FOLD_C_ID
39 let s:LINENR_ID = hlID('LineNr') | lockvar s:LINENR_ID
40 let s:DIFF_D_ID = hlID("DiffDelete") | lockvar s:DIFF_D_ID
41 let s:DIFF_A_ID = hlID("DiffAdd") | lockvar s:DIFF_A_ID
42 let s:DIFF_C_ID = hlID("DiffChange") | lockvar s:DIFF_C_ID
43 let s:DIFF_T_ID = hlID("DiffText") | lockvar s:DIFF_T_ID
44 let s:CONCEAL_ID = hlID('Conceal') | lockvar s:CONCEAL_ID
45endif
46
Bram Moolenaar8e5af3e2011-04-28 19:02:44 +020047" Whitespace
48if s:settings.pre_wrap
49 let s:whitespace = "white-space: pre-wrap; "
50else
51 let s:whitespace = ""
52endif
53
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020054if !empty(s:settings.prevent_copy)
55 if s:settings.no_invalid
56 " User has decided they don't want invalid markup. Still works in
57 " OpenOffice, and for text editors, but when pasting into Microsoft Word the
58 " input elements get pasted too and they cannot be deleted (at least not
59 " easily).
60 let s:unselInputType = ""
61 else
62 " Prevent from copy-pasting the input elements into Microsoft Word where
63 " they cannot be deleted easily by deliberately inserting invalid markup.
64 let s:unselInputType = " type='invalid_input_type'"
65 endif
66endif
67
Bram Moolenaar071d4272004-06-13 20:20:40 +000068" When not in gui we can only guess the colors.
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020069" TODO - is this true anymore?
Bram Moolenaar071d4272004-06-13 20:20:40 +000070if has("gui_running")
71 let s:whatterm = "gui"
72else
73 let s:whatterm = "cterm"
74 if &t_Co == 8
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020075 let s:cterm_color = {
76 \ 0: "#808080", 1: "#ff6060", 2: "#00ff00", 3: "#ffff00",
77 \ 4: "#8080ff", 5: "#ff40ff", 6: "#00ffff", 7: "#ffffff"
78 \ }
Bram Moolenaar071d4272004-06-13 20:20:40 +000079 else
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020080 let s:cterm_color = {
81 \ 0: "#000000", 1: "#c00000", 2: "#008000", 3: "#804000",
82 \ 4: "#0000c0", 5: "#c000c0", 6: "#008080", 7: "#c0c0c0",
83 \ 8: "#808080", 9: "#ff6060", 10: "#00ff00", 11: "#ffff00",
84 \ 12: "#8080ff", 13: "#ff40ff", 14: "#00ffff", 15: "#ffffff"
85 \ }
Bram Moolenaar313b7232007-05-05 17:56:55 +000086
87 " Colors for 88 and 256 come from xterm.
88 if &t_Co == 88
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020089 call extend(s:cterm_color, {
90 \ 16: "#000000", 17: "#00008b", 18: "#0000cd", 19: "#0000ff",
91 \ 20: "#008b00", 21: "#008b8b", 22: "#008bcd", 23: "#008bff",
92 \ 24: "#00cd00", 25: "#00cd8b", 26: "#00cdcd", 27: "#00cdff",
93 \ 28: "#00ff00", 29: "#00ff8b", 30: "#00ffcd", 31: "#00ffff",
94 \ 32: "#8b0000", 33: "#8b008b", 34: "#8b00cd", 35: "#8b00ff",
95 \ 36: "#8b8b00", 37: "#8b8b8b", 38: "#8b8bcd", 39: "#8b8bff",
96 \ 40: "#8bcd00", 41: "#8bcd8b", 42: "#8bcdcd", 43: "#8bcdff",
97 \ 44: "#8bff00", 45: "#8bff8b", 46: "#8bffcd", 47: "#8bffff",
98 \ 48: "#cd0000", 49: "#cd008b", 50: "#cd00cd", 51: "#cd00ff",
99 \ 52: "#cd8b00", 53: "#cd8b8b", 54: "#cd8bcd", 55: "#cd8bff",
100 \ 56: "#cdcd00", 57: "#cdcd8b", 58: "#cdcdcd", 59: "#cdcdff",
101 \ 60: "#cdff00", 61: "#cdff8b", 62: "#cdffcd", 63: "#cdffff",
102 \ 64: "#ff0000"
103 \ })
104 call extend(s:cterm_color, {
105 \ 65: "#ff008b", 66: "#ff00cd", 67: "#ff00ff", 68: "#ff8b00",
106 \ 69: "#ff8b8b", 70: "#ff8bcd", 71: "#ff8bff", 72: "#ffcd00",
107 \ 73: "#ffcd8b", 74: "#ffcdcd", 75: "#ffcdff", 76: "#ffff00",
108 \ 77: "#ffff8b", 78: "#ffffcd", 79: "#ffffff", 80: "#2e2e2e",
109 \ 81: "#5c5c5c", 82: "#737373", 83: "#8b8b8b", 84: "#a2a2a2",
110 \ 85: "#b9b9b9", 86: "#d0d0d0", 87: "#e7e7e7"
111 \ })
Bram Moolenaar313b7232007-05-05 17:56:55 +0000112 elseif &t_Co == 256
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200113 call extend(s:cterm_color, {
114 \ 16: "#000000", 17: "#00005f", 18: "#000087", 19: "#0000af",
115 \ 20: "#0000d7", 21: "#0000ff", 22: "#005f00", 23: "#005f5f",
116 \ 24: "#005f87", 25: "#005faf", 26: "#005fd7", 27: "#005fff",
117 \ 28: "#008700", 29: "#00875f", 30: "#008787", 31: "#0087af",
118 \ 32: "#0087d7", 33: "#0087ff", 34: "#00af00", 35: "#00af5f",
119 \ 36: "#00af87", 37: "#00afaf", 38: "#00afd7", 39: "#00afff",
120 \ 40: "#00d700", 41: "#00d75f", 42: "#00d787", 43: "#00d7af",
121 \ 44: "#00d7d7", 45: "#00d7ff", 46: "#00ff00", 47: "#00ff5f",
122 \ 48: "#00ff87", 49: "#00ffaf", 50: "#00ffd7", 51: "#00ffff",
123 \ 52: "#5f0000", 53: "#5f005f", 54: "#5f0087", 55: "#5f00af",
124 \ 56: "#5f00d7", 57: "#5f00ff", 58: "#5f5f00", 59: "#5f5f5f",
125 \ 60: "#5f5f87", 61: "#5f5faf", 62: "#5f5fd7", 63: "#5f5fff",
126 \ 64: "#5f8700"
127 \ })
128 call extend(s:cterm_color, {
129 \ 65: "#5f875f", 66: "#5f8787", 67: "#5f87af", 68: "#5f87d7",
130 \ 69: "#5f87ff", 70: "#5faf00", 71: "#5faf5f", 72: "#5faf87",
131 \ 73: "#5fafaf", 74: "#5fafd7", 75: "#5fafff", 76: "#5fd700",
132 \ 77: "#5fd75f", 78: "#5fd787", 79: "#5fd7af", 80: "#5fd7d7",
133 \ 81: "#5fd7ff", 82: "#5fff00", 83: "#5fff5f", 84: "#5fff87",
134 \ 85: "#5fffaf", 86: "#5fffd7", 87: "#5fffff", 88: "#870000",
135 \ 89: "#87005f", 90: "#870087", 91: "#8700af", 92: "#8700d7",
136 \ 93: "#8700ff", 94: "#875f00", 95: "#875f5f", 96: "#875f87",
137 \ 97: "#875faf", 98: "#875fd7", 99: "#875fff", 100: "#878700",
138 \ 101: "#87875f", 102: "#878787", 103: "#8787af", 104: "#8787d7",
139 \ 105: "#8787ff", 106: "#87af00", 107: "#87af5f", 108: "#87af87",
140 \ 109: "#87afaf", 110: "#87afd7", 111: "#87afff", 112: "#87d700"
141 \ })
142 call extend(s:cterm_color, {
143 \ 113: "#87d75f", 114: "#87d787", 115: "#87d7af", 116: "#87d7d7",
144 \ 117: "#87d7ff", 118: "#87ff00", 119: "#87ff5f", 120: "#87ff87",
145 \ 121: "#87ffaf", 122: "#87ffd7", 123: "#87ffff", 124: "#af0000",
146 \ 125: "#af005f", 126: "#af0087", 127: "#af00af", 128: "#af00d7",
147 \ 129: "#af00ff", 130: "#af5f00", 131: "#af5f5f", 132: "#af5f87",
148 \ 133: "#af5faf", 134: "#af5fd7", 135: "#af5fff", 136: "#af8700",
149 \ 137: "#af875f", 138: "#af8787", 139: "#af87af", 140: "#af87d7",
150 \ 141: "#af87ff", 142: "#afaf00", 143: "#afaf5f", 144: "#afaf87",
151 \ 145: "#afafaf", 146: "#afafd7", 147: "#afafff", 148: "#afd700",
152 \ 149: "#afd75f", 150: "#afd787", 151: "#afd7af", 152: "#afd7d7",
153 \ 153: "#afd7ff", 154: "#afff00", 155: "#afff5f", 156: "#afff87",
154 \ 157: "#afffaf", 158: "#afffd7"
155 \ })
156 call extend(s:cterm_color, {
157 \ 159: "#afffff", 160: "#d70000", 161: "#d7005f", 162: "#d70087",
158 \ 163: "#d700af", 164: "#d700d7", 165: "#d700ff", 166: "#d75f00",
159 \ 167: "#d75f5f", 168: "#d75f87", 169: "#d75faf", 170: "#d75fd7",
160 \ 171: "#d75fff", 172: "#d78700", 173: "#d7875f", 174: "#d78787",
161 \ 175: "#d787af", 176: "#d787d7", 177: "#d787ff", 178: "#d7af00",
162 \ 179: "#d7af5f", 180: "#d7af87", 181: "#d7afaf", 182: "#d7afd7",
163 \ 183: "#d7afff", 184: "#d7d700", 185: "#d7d75f", 186: "#d7d787",
164 \ 187: "#d7d7af", 188: "#d7d7d7", 189: "#d7d7ff", 190: "#d7ff00",
165 \ 191: "#d7ff5f", 192: "#d7ff87", 193: "#d7ffaf", 194: "#d7ffd7",
166 \ 195: "#d7ffff", 196: "#ff0000", 197: "#ff005f", 198: "#ff0087",
167 \ 199: "#ff00af", 200: "#ff00d7", 201: "#ff00ff", 202: "#ff5f00",
168 \ 203: "#ff5f5f", 204: "#ff5f87"
169 \ })
170 call extend(s:cterm_color, {
171 \ 205: "#ff5faf", 206: "#ff5fd7", 207: "#ff5fff", 208: "#ff8700",
172 \ 209: "#ff875f", 210: "#ff8787", 211: "#ff87af", 212: "#ff87d7",
173 \ 213: "#ff87ff", 214: "#ffaf00", 215: "#ffaf5f", 216: "#ffaf87",
174 \ 217: "#ffafaf", 218: "#ffafd7", 219: "#ffafff", 220: "#ffd700",
175 \ 221: "#ffd75f", 222: "#ffd787", 223: "#ffd7af", 224: "#ffd7d7",
176 \ 225: "#ffd7ff", 226: "#ffff00", 227: "#ffff5f", 228: "#ffff87",
177 \ 229: "#ffffaf", 230: "#ffffd7", 231: "#ffffff", 232: "#080808",
178 \ 233: "#121212", 234: "#1c1c1c", 235: "#262626", 236: "#303030",
179 \ 237: "#3a3a3a", 238: "#444444", 239: "#4e4e4e", 240: "#585858",
180 \ 241: "#626262", 242: "#6c6c6c", 243: "#767676", 244: "#808080",
181 \ 245: "#8a8a8a", 246: "#949494", 247: "#9e9e9e", 248: "#a8a8a8",
182 \ 249: "#b2b2b2", 250: "#bcbcbc", 251: "#c6c6c6", 252: "#d0d0d0",
183 \ 253: "#dadada", 254: "#e4e4e4", 255: "#eeeeee"
184 \ })
Bram Moolenaar313b7232007-05-05 17:56:55 +0000185 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000186 endif
187endif
188
189" Return good color specification: in GUI no transformation is done, in
Bram Moolenaar313b7232007-05-05 17:56:55 +0000190" terminal return RGB values of known colors and empty string for unknown
Bram Moolenaar071d4272004-06-13 20:20:40 +0000191if s:whatterm == "gui"
192 function! s:HtmlColor(color)
193 return a:color
194 endfun
195else
196 function! s:HtmlColor(color)
Bram Moolenaar313b7232007-05-05 17:56:55 +0000197 if has_key(s:cterm_color, a:color)
198 return s:cterm_color[a:color]
Bram Moolenaar071d4272004-06-13 20:20:40 +0000199 else
200 return ""
201 endif
202 endfun
203endif
204
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200205" Find out the background and foreground color for use later
206let s:fgc = s:HtmlColor(synIDattr(hlID("Normal"), "fg#", s:whatterm))
207let s:bgc = s:HtmlColor(synIDattr(hlID("Normal"), "bg#", s:whatterm))
208if s:fgc == ""
209 let s:fgc = ( &background == "dark" ? "#ffffff" : "#000000" )
210endif
211if s:bgc == ""
212 let s:bgc = ( &background == "dark" ? "#000000" : "#ffffff" )
213endif
214
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200215if !s:settings.use_css
Bram Moolenaar071d4272004-06-13 20:20:40 +0000216 " Return opening HTML tag for given highlight id
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200217 function! s:HtmlOpening(id, extra_attrs)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000218 let a = ""
219 if synIDattr(a:id, "inverse")
220 " For inverse, we always must set both colors (and exchange them)
221 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200222 let a = a . '<span '.a:extra_attrs.'style="background-color: ' . ( x != "" ? x : s:fgc ) . '">'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000223 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
224 let a = a . '<font color="' . ( x != "" ? x : s:bgc ) . '">'
225 else
226 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200227 if x != ""
228 let a = a . '<span '.a:extra_attrs.'style="background-color: ' . x . '">'
229 elseif !empty(a:extra_attrs)
230 let a = a . '<span '.a:extra_attrs.'>'
231 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000232 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
233 if x != "" | let a = a . '<font color="' . x . '">' | endif
234 endif
235 if synIDattr(a:id, "bold") | let a = a . "<b>" | endif
236 if synIDattr(a:id, "italic") | let a = a . "<i>" | endif
237 if synIDattr(a:id, "underline") | let a = a . "<u>" | endif
238 return a
239 endfun
240
241 " Return closing HTML tag for given highlight id
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200242 function! s:HtmlClosing(id, has_extra_attrs)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000243 let a = ""
244 if synIDattr(a:id, "underline") | let a = a . "</u>" | endif
245 if synIDattr(a:id, "italic") | let a = a . "</i>" | endif
246 if synIDattr(a:id, "bold") | let a = a . "</b>" | endif
247 if synIDattr(a:id, "inverse")
248 let a = a . '</font></span>'
249 else
250 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
251 if x != "" | let a = a . '</font>' | endif
252 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200253 if x != "" || a:has_extra_attrs | let a = a . '</span>' | endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000254 endif
255 return a
256 endfun
257endif
258
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200259" Use a different function for formatting based on user options. This way we
260" can avoid a lot of logic during the actual execution.
261"
262" Build the function line by line containing only what is needed for the options
263" in use for maximum code sharing with minimal branch logic for greater speed.
264"
265" Note, 'exec' commands do not recognize line continuations, so must concatenate
266" lines rather than continue them.
267if s:settings.use_css
268 " save CSS to a list of rules to add to the output at the end of processing
269
270 " first, get the style names we need
271 let wrapperfunc_lines = [
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200272 \ 'function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, make_unselectable, unformatted)',
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200273 \ '',
274 \ ' let l:style_name = synIDattr(a:style_id, "name", s:whatterm)'
275 \ ]
276 if &diff
277 let wrapperfunc_lines += [
278 \ ' let l:diff_style_name = synIDattr(a:diff_style_id, "name", s:whatterm)']
279
280 " Add normal groups and diff groups to separate lists so we can order them to
281 " allow diff highlight to override normal highlight
282
283 " if primary style IS a diff style, grab it from the diff cache instead
284 " (always succeeds because we pre-populate it)
285 let wrapperfunc_lines += [
286 \ '',
287 \ ' if a:style_id == s:DIFF_D_ID || a:style_id == s:DIFF_A_ID ||'.
288 \ ' a:style_id == s:DIFF_C_ID || a:style_id == s:DIFF_T_ID',
289 \ ' let l:saved_style = get(s:diffstylelist,a:style_id)',
290 \ ' else'
291 \ ]
292 endif
293
294 " get primary style info from cache or build it on the fly if not found
295 let wrapperfunc_lines += [
296 \ ' let l:saved_style = get(s:stylelist,a:style_id)',
297 \ ' if type(l:saved_style) == type(0)',
298 \ ' unlet l:saved_style',
299 \ ' let l:saved_style = s:CSS1(a:style_id)',
300 \ ' if l:saved_style != ""',
301 \ ' let l:saved_style = "." . l:style_name . " { " . l:saved_style . "}"',
302 \ ' endif',
303 \ ' let s:stylelist[a:style_id]= l:saved_style',
304 \ ' endif'
305 \ ]
306 if &diff
307 let wrapperfunc_lines += [ ' endif' ]
308 endif
309
310 " Build the wrapper tags around the text. It turns out that caching these
311 " gives pretty much zero performance gain and adds a lot of logic.
312
313 let wrapperfunc_lines += [
314 \ '',
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200315 \ ' if l:saved_style == "" && empty(a:extra_attrs)'
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200316 \ ]
317 if &diff
318 let wrapperfunc_lines += [
319 \ ' if a:diff_style_id <= 0'
320 \ ]
321 endif
322 " no surroundings if neither primary nor diff style has any info
323 let wrapperfunc_lines += [
324 \ ' return a:text'
325 \ ]
326 if &diff
327 " no primary style, but diff style
328 let wrapperfunc_lines += [
329 \ ' else',
330 \ ' return "<span class=\"" .l:diff_style_name . "\">".a:text."</span>"',
331 \ ' endif'
332 \ ]
333 endif
334 " open tag for non-empty primary style
335 let wrapperfunc_lines += [
336 \ ' else']
337 " non-empty primary style. handle either empty or non-empty diff style.
338 "
339 " separate the two classes by a space to apply them both if there is a diff
340 " style name, unless the primary style is empty, then just use the diff style
341 " name
342 let diffstyle =
343 \ (&diff ? '(a:diff_style_id <= 0 ? "" : " ". l:diff_style_name) .'
344 \ : "")
345 if s:settings.prevent_copy == ""
346 let wrapperfunc_lines += [
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200347 \ ' return "<span ".a:extra_attrs."class=\"" . l:style_name .'.diffstyle.'"\">".a:text."</span>"'
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200348 \ ]
349 else
350
351 "
352 " Wrap the <input> in a <span> to allow fixing the stupid bug in some fonts
353 " which cause browsers to display a 1px gap between lines when these
354 " <input>s have a background color (maybe not really a bug, this isn't
355 " well-defined)
356 "
357 " use strwidth, because we care only about how many character boxes are
358 " needed to size the input, we don't care how many characters (including
359 " separately counted composing chars, from strchars()) or bytes (from
360 " len())the string contains. strdisplaywidth() is not needed because none of
361 " the unselectable groups can contain tab characters (fold column, fold
362 " text, line number).
363 "
364 " Note, if maxlength property needs to be added in the future, it will need
365 " to use strchars(), because HTML specifies that the maxlength parameter
366 " uses the number of unique codepoints for its limit.
367 let wrapperfunc_lines += [
368 \ ' if a:make_unselectable',
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200369 \ ' return "<span ".a:extra_attrs."class=\"" . l:style_name .'.diffstyle.'"\">'.
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200370 \ '<input'.s:unselInputType.' class=\"" . l:style_name .'.diffstyle.'"\"'.
371 \ ' value=\"".substitute(a:unformatted,''\s\+$'',"","")."\"'.
372 \ ' onselect=''this.blur(); return false;'''.
373 \ ' onmousedown=''this.blur(); return false;'''.
374 \ ' onclick=''this.blur(); return false;'''.
375 \ ' readonly=''readonly'''.
376 \ ' size=\"".strwidth(a:unformatted)."\"'.
377 \ (s:settings.use_xhtml ? '/' : '').'></span>"',
378 \ ' else',
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200379 \ ' return "<span ".a:extra_attrs."class=\"" . l:style_name .'. diffstyle .'"\">".a:text."</span>"'
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200380 \ ]
381 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200382 let wrapperfunc_lines += [
383 \ ' endif',
384 \ 'endfun'
385 \ ]
386else
387 " Non-CSS method just needs the wrapper.
388 "
389 " Functions used to get opening/closing automatically return null strings if
390 " no styles exist.
391 if &diff
392 let wrapperfunc_lines = [
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200393 \ 'function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, unusedarg, unusedarg2)',
394 \ ' return s:HtmlOpening(a:style_id, a:extra_attrs).(a:diff_style_id <= 0 ? "" :'.
395 \ 's:HtmlOpening(a:diff_style_id, "")).a:text.'.
396 \ '(a:diff_style_id <= 0 ? "" : s:HtmlClosing(a:diff_style_id, 0)).s:HtmlClosing(a:style_id, !empty(a:extra_attrs))',
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200397 \ 'endfun'
398 \ ]
399 else
400 let wrapperfunc_lines = [
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200401 \ 'function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, unusedarg, unusedarg2)',
402 \ ' return s:HtmlOpening(a:style_id, a:extra_attrs).a:text.s:HtmlClosing(a:style_id, !empty(a:extra_attrs))',
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200403 \ 'endfun'
404 \ ]
405 endif
406endif
407
408" create the function we built line by line above
409exec join(wrapperfunc_lines, "\n")
410
411let s:diff_mode = &diff
412
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000413" Return HTML valid characters enclosed in a span of class style_name with
414" unprintable characters expanded and double spaces replaced as necessary.
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200415"
416" TODO: eliminate unneeded logic like done for BuildStyleWrapper
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200417function! s:HtmlFormat(text, style_id, diff_style_id, extra_attrs, make_unselectable)
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000418 " Replace unprintable characters
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200419 let unformatted = strtrans(a:text)
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000420
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200421 let formatted = unformatted
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +0200422
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000423 " Replace the reserved html characters
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +0100424 let formatted = substitute(formatted, '&', '\&amp;', 'g')
425 let formatted = substitute(formatted, '<', '\&lt;', 'g')
426 let formatted = substitute(formatted, '>', '\&gt;', 'g')
427 let formatted = substitute(formatted, '"', '\&quot;', 'g')
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200428 " &apos; is not valid in HTML but it is in XHTML, so just use the numeric
429 " reference for it instead. Needed because it could appear in quotes
430 " especially if unselectable regions is turned on.
431 let formatted = substitute(formatted, '"', '\&#0039;', 'g')
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +0100432
433 " Replace a "form feed" character with HTML to do a page break
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200434 " TODO: need to prevent this in unselectable areas? Probably it should never
435 " BE in an unselectable area...
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +0100436 let formatted = substitute(formatted, "\x0c", '<hr class="PAGE-BREAK">', 'g')
437
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200438 " Replace double spaces, leading spaces, and trailing spaces if needed
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000439 if ' ' != s:HtmlSpace
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000440 let formatted = substitute(formatted, ' ', s:HtmlSpace . s:HtmlSpace, 'g')
Bram Moolenaar8424a622006-04-19 21:23:36 +0000441 let formatted = substitute(formatted, '^ ', s:HtmlSpace, 'g')
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200442 let formatted = substitute(formatted, ' \+$', s:HtmlSpace, 'g')
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000443 endif
444
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200445 " Enclose in the correct format
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200446 return s:BuildStyleWrapper(a:style_id, a:diff_style_id, a:extra_attrs, formatted, a:make_unselectable, unformatted)
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000447endfun
448
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200449" set up functions to call HtmlFormat in certain ways based on whether the
450" element is supposed to be unselectable or not
451if s:settings.prevent_copy =~# 'n'
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200452 if s:settings.number_lines
Bram Moolenaar31c31672013-06-26 13:28:14 +0200453 if s:settings.line_ids
454 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
455 if a:lnr > 0
456 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'.(exists('g:html_diff_win_num') ? 'W'.g:html_diff_win_num : "").'L'.a:lnr.s:settings.id_suffix.'" ', 1)
457 else
458 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
459 endif
460 endfun
461 else
462 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200463 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
Bram Moolenaar31c31672013-06-26 13:28:14 +0200464 endfun
465 endif
466 elseif s:settings.line_ids
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200467 " if lines are not being numbered the only reason this function gets called
468 " is to put the line IDs on each line; "text" will be emtpy but lnr will
469 " always be non-zero, however we don't want to use the <input> because that
470 " won't work as nice for empty text
471 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
Bram Moolenaar31c31672013-06-26 13:28:14 +0200472 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)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200473 endfun
474 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200475else
Bram Moolenaar31c31672013-06-26 13:28:14 +0200476 if s:settings.line_ids
477 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
478 if a:lnr > 0
479 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'.(exists('g:html_diff_win_num') ? 'W'.g:html_diff_win_num : "").'L'.a:lnr.s:settings.id_suffix.'" ', 0)
480 else
481 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
482 endif
483 endfun
484 else
485 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200486 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
Bram Moolenaar31c31672013-06-26 13:28:14 +0200487 endfun
488 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200489endif
490if s:settings.prevent_copy =~# 'd'
491 function! s:HtmlFormat_d(text, style_id, diff_style_id)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200492 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200493 endfun
494else
495 function! s:HtmlFormat_d(text, style_id, diff_style_id)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200496 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200497 endfun
498endif
499if s:settings.prevent_copy =~# 'f'
500 " Note the <input> elements for fill spaces will have a single space for
501 " content, to allow active cursor CSS selection to work.
502 "
503 " Wrap the whole thing in a span for the 1px padding workaround for gaps.
504 function! s:FoldColumn_build(char, len, numfill, char2, class, click)
505 let l:input_open = "<input readonly='readonly'".s:unselInputType.
506 \ " onselect='this.blur(); return false;'".
507 \ " onmousedown='this.blur(); ".a:click." return false;'".
508 \ " onclick='return false;' size='".
509 \ string(a:len + (empty(a:char2) ? 0 : 1) + a:numfill) .
510 \ "' "
511 let l:common_attrs = "class='FoldColumn' value='"
512 let l:input_close = (s:settings.use_xhtml ? "' />" : "'>")
513 return "<span class='".a:class."'>".
514 \ l:input_open.l:common_attrs.repeat(a:char, a:len).
515 \ (!empty(a:char2) ? a:char2 : "").
516 \ l:input_close . "</span>"
517 endfun
518 function! s:FoldColumn_fill()
519 return s:FoldColumn_build('', s:foldcolumn, 0, '', 'FoldColumn', '')
520 endfun
521else
522 " For normal fold columns, simply space-pad to the desired width (note that
523 " the FoldColumn definition includes a whitespace:pre rule)
524 function! s:FoldColumn_build(char, len, numfill, char2, class, click)
525 return "<a href='#' class='".a:class."' onclick='".a:click."'>".
526 \ repeat(a:char, a:len).a:char2.repeat(' ', a:numfill).
527 \ "</a>"
528 endfun
529 function! s:FoldColumn_fill()
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200530 return s:HtmlFormat(repeat(' ', s:foldcolumn), s:FOLD_C_ID, 0, "", 0)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200531 endfun
532endif
533if s:settings.prevent_copy =~# 't'
534 " put an extra empty span at the end for dynamic folds, so the linebreak can
535 " be surrounded. Otherwise do it as normal.
536 "
537 " TODO: isn't there a better way to do this, than placing it here and using a
538 " substitute later?
539 if s:settings.dynamic_folds
540 function! s:HtmlFormat_t(text, style_id, diff_style_id)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200541 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) .
542 \ s:HtmlFormat("", a:style_id, 0, "", 0)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200543 endfun
544 else
545 function! s:HtmlFormat_t(text, style_id, diff_style_id)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200546 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200547 endfun
548 endif
549else
550 function! s:HtmlFormat_t(text, style_id, diff_style_id)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200551 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200552 endfun
553endif
554
Bram Moolenaar071d4272004-06-13 20:20:40 +0000555" Return CSS style describing given highlight id (can be empty)
556function! s:CSS1(id)
557 let a = ""
558 if synIDattr(a:id, "inverse")
559 " For inverse, we always must set both colors (and exchange them)
560 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
561 let a = a . "color: " . ( x != "" ? x : s:bgc ) . "; "
562 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
563 let a = a . "background-color: " . ( x != "" ? x : s:fgc ) . "; "
564 else
565 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
566 if x != "" | let a = a . "color: " . x . "; " | endif
567 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200568 if x != ""
569 let a = a . "background-color: " . x . "; "
570 " stupid hack because almost every browser seems to have at least one font
571 " which shows 1px gaps between lines which have background
572 let a = a . "padding-bottom: 1px; "
573 elseif (a:id == s:FOLDED_ID || a:id == s:LINENR_ID || a:id == s:FOLD_C_ID) && !empty(s:settings.prevent_copy)
574 " input elements default to a different color than the rest of the page
575 let a = a . "background-color: " . s:bgc . "; "
576 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000577 endif
578 if synIDattr(a:id, "bold") | let a = a . "font-weight: bold; " | endif
579 if synIDattr(a:id, "italic") | let a = a . "font-style: italic; " | endif
580 if synIDattr(a:id, "underline") | let a = a . "text-decoration: underline; " | endif
581 return a
582endfun
583
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200584if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +0100585 " compares two folds as stored in our list of folds
586 " A fold is "less" than another if it starts at an earlier line number,
587 " or ends at a later line number, ties broken by fold level
588 function! s:FoldCompare(f1, f2)
589 if a:f1.firstline != a:f2.firstline
590 " put it before if it starts earlier
591 return a:f1.firstline - a:f2.firstline
592 elseif a:f1.lastline != a:f2.lastline
593 " put it before if it ends later
594 return a:f2.lastline - a:f1.lastline
595 else
596 " if folds begin and end on the same lines, put lowest fold level first
597 return a:f1.level - a:f2.level
598 endif
599 endfunction
600
601endif
602
Bram Moolenaar071d4272004-06-13 20:20:40 +0000603
604" Set some options to make it work faster.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000605" Don't report changes for :substitute, there will be many of them.
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200606" Don't change other windows; turn off scroll bind temporarily
Bram Moolenaar071d4272004-06-13 20:20:40 +0000607let s:old_title = &title
608let s:old_icon = &icon
609let s:old_et = &l:et
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200610let s:old_bind = &l:scrollbind
Bram Moolenaar071d4272004-06-13 20:20:40 +0000611let s:old_report = &report
612let s:old_search = @/
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200613let s:old_more = &more
Bram Moolenaar071d4272004-06-13 20:20:40 +0000614set notitle noicon
615setlocal et
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200616set nomore
Bram Moolenaar071d4272004-06-13 20:20:40 +0000617set report=1000000
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200618setlocal noscrollbind
Bram Moolenaar071d4272004-06-13 20:20:40 +0000619
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200620if exists(':ownsyntax') && exists('w:current_syntax')
621 let s:current_syntax = w:current_syntax
622elseif exists('b:current_syntax')
623 let s:current_syntax = b:current_syntax
624else
625 let s:current_syntax = 'none'
626endif
627
628if s:current_syntax == ''
629 let s:current_syntax = 'none'
630endif
631
Bram Moolenaar071d4272004-06-13 20:20:40 +0000632" Split window to create a buffer with the HTML file.
633let s:orgbufnr = winbufnr(0)
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200634let s:origwin_stl = &l:stl
Bram Moolenaar071d4272004-06-13 20:20:40 +0000635if expand("%") == ""
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200636 if exists('g:html_diff_win_num')
637 exec 'new Untitled_win'.g:html_diff_win_num.'.'.(s:settings.use_xhtml ? 'x' : '').'html'
638 else
639 exec 'new Untitled.'.(s:settings.use_xhtml ? 'x' : '').'html'
640 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641else
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200642 exec 'new %.'.(s:settings.use_xhtml ? 'x' : '').'html'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000643endif
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200644
645" Resize the new window to very small in order to make it draw faster
646let s:old_winheight = winheight(0)
647let s:old_winfixheight = &l:winfixheight
648if s:old_winheight > 2
649 resize 1 " leave enough room to view one line at a time
650 norm! G
651 norm! zt
652endif
653setlocal winfixheight
654
655let s:newwin_stl = &l:stl
656
657" on the new window, set the least time-consuming fold method
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200658let s:old_fen = &foldenable
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200659setlocal foldmethod=manual
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200660setlocal nofoldenable
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200661
Bram Moolenaar071d4272004-06-13 20:20:40 +0000662let s:newwin = winnr()
663let s:orgwin = bufwinnr(s:orgbufnr)
664
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200665setlocal modifiable
Bram Moolenaar071d4272004-06-13 20:20:40 +0000666%d
667let s:old_paste = &paste
668set paste
669let s:old_magic = &magic
670set magic
671
Bram Moolenaar166af9b2010-11-16 20:34:40 +0100672" set the fileencoding to match the charset we'll be using
673let &l:fileencoding=s:settings.vim_encoding
674
675" According to http://www.w3.org/TR/html4/charset.html#doc-char-set, the byte
676" order mark is highly recommend on the web when using multibyte encodings. But,
677" it is not a good idea to include it on UTF-8 files. Otherwise, let Vim
678" determine when it is actually inserted.
679if s:settings.vim_encoding == 'utf-8'
680 setlocal nobomb
681else
682 setlocal bomb
683endif
684
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200685let s:lines = []
686
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200687if s:settings.use_xhtml
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200688 if s:settings.encoding != ""
689 call add(s:lines, "<?xml version=\"1.0\" encoding=\"" . s:settings.encoding . "\"?>")
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000690 else
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200691 call add(s:lines, "<?xml version=\"1.0\"?>")
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000692 endif
Bram Moolenaar313b7232007-05-05 17:56:55 +0000693 let s:tag_close = ' />'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694else
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000695 let s:tag_close = '>'
696endif
697
698let s:HtmlSpace = ' '
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000699let s:LeadingSpace = ' '
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000700let s:HtmlEndline = ''
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200701if s:settings.no_pre
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000702 let s:HtmlEndline = '<br' . s:tag_close
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200703 let s:LeadingSpace = s:settings.use_xhtml ? '&#160;' : '&nbsp;'
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000704 let s:HtmlSpace = '\' . s:LeadingSpace
Bram Moolenaar071d4272004-06-13 20:20:40 +0000705endif
706
707" HTML header, with the title and generator ;-). Left free space for the CSS,
708" to be filled at the end.
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200709call extend(s:lines, [
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200710 \ "<html>",
711 \ "<head>"])
712" include encoding as close to the top as possible, but only if not already
713" contained in XML information (to avoid haggling over content type)
714if s:settings.encoding != "" && !s:settings.use_xhtml
715 call add(s:lines, "<meta http-equiv=\"content-type\" content=\"text/html; charset=" . s:settings.encoding . '"' . s:tag_close)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000716endif
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200717call extend(s:lines, [
718 \ ("<title>".expand("%:p:~")."</title>"),
719 \ ("<meta name=\"Generator\" content=\"Vim/".v:version/100.".".v:version%100.'"'.s:tag_close),
720 \ ("<meta name=\"plugin-version\" content=\"".g:loaded_2html_plugin.'"'.s:tag_close)
721 \ ])
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200722call add(s:lines, '<meta name="syntax" content="'.s:current_syntax.'"'.s:tag_close)
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200723call add(s:lines, '<meta name="settings" content="'.
724 \ join(filter(keys(s:settings),'s:settings[v:val]'),',').
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200725 \ ',prevent_copy='.s:settings.prevent_copy.
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200726 \ '"'.s:tag_close)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200727call add(s:lines, '<meta name="colorscheme" content="'.
728 \ (exists('g:colors_name')
729 \ ? g:colors_name
730 \ : 'none'). '"'.s:tag_close)
Bram Moolenaar313b7232007-05-05 17:56:55 +0000731
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200732if s:settings.use_css
733 if s:settings.dynamic_folds
734 if s:settings.hover_unfold
Bram Moolenaar5c736222010-01-06 20:54:52 +0100735 " if we are doing hover_unfold, use css 2 with css 1 fallback for IE6
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200736 call extend(s:lines, [
737 \ "<style type=\"text/css\">",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200738 \ s:settings.use_xhtml ? "" : "<!--",
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200739 \ ".FoldColumn { text-decoration: none; white-space: pre; }",
740 \ "",
741 \ "body * { margin: 0; padding: 0; }", "",
742 \ ".open-fold > .Folded { display: none; }",
743 \ ".open-fold > .fulltext { display: inline; }",
744 \ ".closed-fold > .fulltext { display: none; }",
745 \ ".closed-fold > .Folded { display: inline; }",
746 \ "",
747 \ ".open-fold > .toggle-open { display: none; }",
748 \ ".open-fold > .toggle-closed { display: inline; }",
749 \ ".closed-fold > .toggle-open { display: inline; }",
750 \ ".closed-fold > .toggle-closed { display: none; }",
751 \ "", "",
752 \ '/* opening a fold while hovering won''t be supported by IE6 and other',
753 \ "similar browsers, but it should fail gracefully. */",
754 \ ".closed-fold:hover > .fulltext { display: inline; }",
755 \ ".closed-fold:hover > .toggle-filler { display: none; }",
756 \ ".closed-fold:hover > .Folded { display: none; }",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200757 \ s:settings.use_xhtml ? "" : '-->',
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200758 \ '</style>'])
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200759 " TODO: IE7 doesn't *actually* support XHTML, maybe we should remove this.
760 " But if it's served up as tag soup, maybe the following will work, so
761 " leave it in for now.
762 call extend(s:lines, [
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200763 \ "<!--[if lt IE 7]><style type=\"text/css\">",
764 \ ".open-fold .Folded { display: none; }",
765 \ ".open-fold .fulltext { display: inline; }",
766 \ ".open-fold .toggle-open { display: none; }",
767 \ ".closed-fold .toggle-closed { display: inline; }",
768 \ "",
769 \ ".closed-fold .fulltext { display: none; }",
770 \ ".closed-fold .Folded { display: inline; }",
771 \ ".closed-fold .toggle-open { display: inline; }",
772 \ ".closed-fold .toggle-closed { display: none; }",
773 \ "</style>",
774 \ "<![endif]-->",
775 \])
Bram Moolenaar5c736222010-01-06 20:54:52 +0100776 else
777 " if we aren't doing hover_unfold, use CSS 1 only
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200778 call extend(s:lines, [
779 \ "<style type=\"text/css\">",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200780 \ s:settings.use_xhtml ? "" :"<!--",
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200781 \ ".FoldColumn { text-decoration: none; white-space: pre; }",
782 \ ".open-fold .Folded { display: none; }",
783 \ ".open-fold .fulltext { display: inline; }",
784 \ ".open-fold .toggle-open { display: none; }",
785 \ ".closed-fold .toggle-closed { display: inline; }",
786 \ "",
787 \ ".closed-fold .fulltext { display: none; }",
788 \ ".closed-fold .Folded { display: inline; }",
789 \ ".closed-fold .toggle-open { display: inline; }",
790 \ ".closed-fold .toggle-closed { display: none; }",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200791 \ s:settings.use_xhtml ? "" : '-->',
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200792 \ '</style>'
793 \])
Bram Moolenaar5c736222010-01-06 20:54:52 +0100794 endif
795 else
796 " if we aren't doing any dynamic folding, no need for any special rules
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200797 call extend(s:lines, [
798 \ "<style type=\"text/css\">",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200799 \ s:settings.use_xhtml ? "" : "<!--",
800 \ s:settings.use_xhtml ? "" : '-->',
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200801 \ "</style>",
802 \])
Bram Moolenaar5c736222010-01-06 20:54:52 +0100803 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000804endif
Bram Moolenaar5c736222010-01-06 20:54:52 +0100805
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200806" insert script tag; javascript is always needed for the line number
807" normalization for URL hashes
808call extend(s:lines, [
809 \ "",
810 \ "<script type='text/javascript'>",
811 \ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"])
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200812
Bram Moolenaar5c736222010-01-06 20:54:52 +0100813" insert javascript to toggle folds open and closed
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200814if s:settings.dynamic_folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200815 call extend(s:lines, [
816 \ "",
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200817 \ "function toggleFold(objID)",
818 \ "{",
819 \ " var fold;",
820 \ " fold = document.getElementById(objID);",
821 \ " if(fold.className == 'closed-fold')",
822 \ " {",
823 \ " fold.className = 'open-fold';",
824 \ " }",
825 \ " else if (fold.className == 'open-fold')",
826 \ " {",
827 \ " fold.className = 'closed-fold';",
828 \ " }",
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200829 \ "}"
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200830 \ ])
Bram Moolenaar5c736222010-01-06 20:54:52 +0100831endif
832
Bram Moolenaar31c31672013-06-26 13:28:14 +0200833if s:settings.line_ids
834 " insert javascript to get IDs from line numbers, and to open a fold before
835 " jumping to any lines contained therein
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200836 call extend(s:lines, [
837 \ "",
Bram Moolenaar31c31672013-06-26 13:28:14 +0200838 \ "/* function to open any folds containing a jumped-to line before jumping to it */",
839 \ "function JumpToLine()",
840 \ "{",
841 \ " var lineNum;",
842 \ " lineNum = window.location.hash;",
843 \ " lineNum = lineNum.substr(1); /* strip off '#' */",
844 \ "",
845 \ " if (lineNum.indexOf('L') == -1) {",
846 \ " lineNum = 'L'+lineNum;",
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200847 \ " }",
Bram Moolenaar31c31672013-06-26 13:28:14 +0200848 \ " lineElem = document.getElementById(lineNum);"
849 \ ])
850 if s:settings.dynamic_folds
851 call extend(s:lines, [
852 \ "",
853 \ " /* navigate upwards in the DOM tree to open all folds containing the line */",
854 \ " var node = lineElem;",
855 \ " while (node && node.id != 'vimCodeElement".s:settings.id_suffix."')",
856 \ " {",
857 \ " if (node.className == 'closed-fold')",
858 \ " {",
859 \ " node.className = 'open-fold';",
860 \ " }",
861 \ " node = node.parentNode;",
862 \ " }",
863 \ ])
864 endif
865 call extend(s:lines, [
866 \ " /* Always jump to new location even if the line was hidden inside a fold, or",
867 \ " * we corrected the raw number to a line ID.",
868 \ " */",
869 \ " if (lineElem) {",
870 \ " lineElem.scrollIntoView(true);",
871 \ " }",
872 \ " return true;",
873 \ "}",
874 \ "if ('onhashchange' in window) {",
875 \ " window.onhashchange = JumpToLine;",
876 \ "}"
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200877 \ ])
878endif
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200879
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200880" Small text columns like the foldcolumn and line number column need a weird
881" hack to work around Webkit's and (in versions prior to 9) IE's lack of support
882" for the 'ch' unit without messing up Opera, which also doesn't support it but
883" works anyway.
884"
885" The problem is that without the 'ch' unit, it is not possible to specify a
886" size of an <input> in terms of character widths. Only Opera seems to do the
887" "sensible" thing and make the <input> sized to fit exactly as many characters
888" as specified by its "size" attribute, but the spec actually says "at least
889" wide enough to fit 'size' characters", so the other browsers are technically
890" correct as well.
891"
892" Anyway, this leads to two diffculties:
893" 1. The foldcolumn is made up of multiple elements side-by-side with
894" different sizes, each of which has their own extra padding added. Thus, a
895" column made up of one item of size 1 and another of size 2 would not
896" necessarily be equal in size to another line's foldcolumn with a single
897" item of size 3.
898" 2. The extra padding added to the <input> elements adds up to make the
899" foldcolumn and line number column too wide, especially in Webkit
900" browsers.
901"
902" So, the full workaround is:
903" 1. Define a default size in em, equal to the number of characters in the
904" input element, in case javascript is disabled and the browser does not
905" support the 'ch' unit. Unfortunately this makes Opera no longer work
906" properly without javascript. 1em per character is much too wide but it
907" looks better in webkit browsers than unaligned columns.
908" 2. Insert the following javascript to run at page load, which checks for the
909" width of a single character (in an extraneous page element inserted
910" before the page title, and set to hidden) and compares it to the width of
911" another extra <input> element with only one character. If the width
912" matches, the script does nothing more, but if not, it will figure out the
913" fraction of an em unit which would correspond with a ch unit if there
914" were one, and set the containing element (<pre> or <div>) to a class with
915" pre-defined rules which is closest to that fraction of an em. Rules are
916" defined from 0.05 em to 1em per ch.
917if !empty(s:settings.prevent_copy)
918 call extend(s:lines, [
919 \ '',
920 \ '/* simulate a "ch" unit by asking the browser how big a zero character is */',
921 \ 'function FixCharWidth() {',
922 \ ' /* get the hidden element which gives the width of a single character */',
923 \ ' var goodWidth = document.getElementById("oneCharWidth").clientWidth;',
924 \ ' /* get all input elements, we''ll filter on class later */',
925 \ ' var inputTags = document.getElementsByTagName("input");',
926 \ ' var ratio = 5;',
927 \ ' var inputWidth = document.getElementById("oneInputWidth").clientWidth;',
928 \ ' var emWidth = document.getElementById("oneEmWidth").clientWidth;',
929 \ ' if (inputWidth > goodWidth) {',
930 \ ' while (ratio < 100*goodWidth/emWidth && ratio < 100) {',
Bram Moolenaar31c31672013-06-26 13:28:14 +0200931 \ ' ratio += 5;',
932 \ ' }',
933 \ ' document.getElementById("vimCodeElement'.s:settings.id_suffix.'").className = "em"+ratio;',
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200934 \ ' }',
935 \ '}'
936 \ ])
937endif
938
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200939" insert script closing tag
940call extend(s:lines, [
941 \ '',
942 \ s:settings.use_xhtml ? '//]]>' : '-->',
943 \ "</script>"
944 \ ])
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200945
946call extend(s:lines, ["</head>"])
947if !empty(s:settings.prevent_copy)
948 call extend(s:lines,
Bram Moolenaar31c31672013-06-26 13:28:14 +0200949 \ ["<body onload='FixCharWidth();".(s:settings.line_ids ? " JumpToLine();" : "")."'>",
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200950 \ "<!-- hidden divs used by javascript to get the width of a char -->",
951 \ "<div id='oneCharWidth'>0</div>",
952 \ "<div id='oneInputWidth'><input size='1' value='0'".s:tag_close."</div>",
953 \ "<div id='oneEmWidth' style='width: 1em;'></div>"
954 \ ])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000955else
Bram Moolenaar31c31672013-06-26 13:28:14 +0200956 call extend(s:lines, ["<body".(s:settings.line_ids ? " onload='JumpToLine();'" : "").">"])
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200957endif
958if s:settings.no_pre
959 " if we're not using CSS we use a font tag which can't have a div inside
960 if s:settings.use_css
Bram Moolenaar31c31672013-06-26 13:28:14 +0200961 call extend(s:lines, ["<div id='vimCodeElement".s:settings.id_suffix."'>"])
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200962 endif
963else
Bram Moolenaar31c31672013-06-26 13:28:14 +0200964 call extend(s:lines, ["<pre id='vimCodeElement".s:settings.id_suffix."'>"])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000965endif
966
967exe s:orgwin . "wincmd w"
968
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200969" caches of style data
970" initialize to include line numbers if using them
971if s:settings.number_lines
972 let s:stylelist = { s:LINENR_ID : ".LineNr { " . s:CSS1( s:LINENR_ID ) . "}" }
973else
974 let s:stylelist = {}
975endif
976let s:diffstylelist = {
977 \ s:DIFF_A_ID : ".DiffAdd { " . s:CSS1( s:DIFF_A_ID ) . "}",
978 \ s:DIFF_C_ID : ".DiffChange { " . s:CSS1( s:DIFF_C_ID ) . "}",
979 \ s:DIFF_D_ID : ".DiffDelete { " . s:CSS1( s:DIFF_D_ID ) . "}",
980 \ s:DIFF_T_ID : ".DiffText { " . s:CSS1( s:DIFF_T_ID ) . "}"
981 \ }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000982
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200983" set up progress bar in the status line
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200984if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200985 " ProgressBar Indicator
986 let s:progressbar={}
987
988 " Progessbar specific functions
989 func! s:ProgressBar(title, max_value, winnr)
990 let pgb=copy(s:progressbar)
991 let pgb.title = a:title.' '
992 let pgb.max_value = a:max_value
993 let pgb.winnr = a:winnr
994 let pgb.cur_value = 0
995 let pgb.items = { 'title' : { 'color' : 'Statusline' },
996 \'bar' : { 'color' : 'Statusline' , 'fillcolor' : 'DiffDelete' , 'bg' : 'Statusline' } ,
997 \'counter' : { 'color' : 'Statusline' } }
998 let pgb.last_value = 0
999 let pgb.needs_redraw = 0
1000 " Note that you must use len(split) instead of len() if you want to use
1001 " unicode in title.
1002 "
1003 " Subtract 3 for spacing around the title.
1004 " Subtract 4 for the percentage display.
1005 " Subtract 2 for spacing before this.
1006 " Subtract 2 more for the '|' on either side of the progress bar
1007 let pgb.subtractedlen=len(split(pgb.title, '\zs'))+3+4+2+2
1008 let pgb.max_len = 0
1009 set laststatus=2
1010 return pgb
1011 endfun
1012
1013 " Function: progressbar.calculate_ticks() {{{1
1014 func! s:progressbar.calculate_ticks(pb_len)
1015 if a:pb_len<=0
1016 let pb_len = 100
1017 else
1018 let pb_len = a:pb_len
1019 endif
1020 let self.progress_ticks = map(range(pb_len+1), "v:val * self.max_value / pb_len")
1021 endfun
1022
1023 "Function: progressbar.paint()
1024 func! s:progressbar.paint()
1025 " Recalculate widths.
1026 let max_len = winwidth(self.winnr)
1027 let pb_len = 0
1028 " always true on first call because of initial value of self.max_len
1029 if max_len != self.max_len
1030 let self.max_len = max_len
1031
1032 " Progressbar length
1033 let pb_len = max_len - self.subtractedlen
1034
1035 call self.calculate_ticks(pb_len)
1036
1037 let self.needs_redraw = 1
1038 let cur_value = 0
1039 let self.pb_len = pb_len
1040 else
1041 " start searching at the last found index to make the search for the
1042 " appropriate tick value normally take 0 or 1 comparisons
1043 let cur_value = self.last_value
1044 let pb_len = self.pb_len
1045 endif
1046
1047 let cur_val_max = pb_len > 0 ? pb_len : 100
1048
1049 " find the current progress bar position based on precalculated thresholds
1050 while cur_value < cur_val_max && self.cur_value > self.progress_ticks[cur_value]
1051 let cur_value += 1
1052 endwhile
1053
1054 " update progress bar
1055 if self.last_value != cur_value || self.needs_redraw || self.cur_value == self.max_value
1056 let self.needs_redraw = 1
1057 let self.last_value = cur_value
1058
1059 let t_color = self.items.title.color
1060 let b_fcolor = self.items.bar.fillcolor
1061 let b_color = self.items.bar.color
1062 let c_color = self.items.counter.color
1063
1064 let stl = "%#".t_color."#%-( ".self.title." %)".
1065 \"%#".b_color."#".
1066 \(pb_len>0 ?
1067 \ ('|%#'.b_fcolor."#%-(".repeat(" ",cur_value)."%)".
1068 \ '%#'.b_color."#".repeat(" ",pb_len-cur_value)."|"):
1069 \ ('')).
1070 \"%=%#".c_color."#%( ".printf("%3.d ",100*self.cur_value/self.max_value)."%% %)"
1071 call setwinvar(self.winnr, '&stl', stl)
1072 endif
1073 endfun
1074
1075 func! s:progressbar.incr( ... )
1076 let self.cur_value += (a:0 ? a:1 : 1)
1077 " if we were making a general-purpose progress bar, we'd need to limit to a
1078 " lower limit as well, but since we always increment with a positive value
1079 " in this script, we only need limit the upper value
1080 let self.cur_value = (self.cur_value > self.max_value ? self.max_value : self.cur_value)
1081 call self.paint()
1082 endfun
1083 " }}}
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001084 if s:settings.dynamic_folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001085 " to process folds we make two passes through each line
1086 let s:pgb = s:ProgressBar("Processing folds:", line('$')*2, s:orgwin)
1087 endif
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001088endif
1089
Bram Moolenaar5c736222010-01-06 20:54:52 +01001090" First do some preprocessing for dynamic folding. Do this for the entire file
1091" so we don't accidentally start within a closed fold or something.
1092let s:allfolds = []
1093
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001094if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +01001095 let s:lnum = 1
1096 let s:end = line('$')
1097 " save the fold text and set it to the default so we can find fold levels
1098 let s:foldtext_save = &foldtext
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001099 setlocal foldtext&
Bram Moolenaar5c736222010-01-06 20:54:52 +01001100
1101 " we will set the foldcolumn in the html to the greater of the maximum fold
1102 " level and the current foldcolumn setting
1103 let s:foldcolumn = &foldcolumn
1104
1105 " get all info needed to describe currently closed folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001106 while s:lnum <= s:end
Bram Moolenaar5c736222010-01-06 20:54:52 +01001107 if foldclosed(s:lnum) == s:lnum
1108 " default fold text has '+-' and then a number of dashes equal to fold
1109 " level, so subtract 2 from index of first non-dash after the dashes
1110 " in order to get the fold level of the current fold
1111 let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2
Bram Moolenaar5c736222010-01-06 20:54:52 +01001112 " store fold info for later use
1113 let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"}
1114 call add(s:allfolds, s:newfold)
1115 " open the fold so we can find any contained folds
1116 execute s:lnum."foldopen"
1117 else
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001118 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001119 call s:pgb.incr()
1120 if s:pgb.needs_redraw
1121 redrawstatus
1122 let s:pgb.needs_redraw = 0
1123 endif
1124 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001125 let s:lnum = s:lnum + 1
1126 endif
1127 endwhile
1128
1129 " close all folds to get info for originally open folds
1130 silent! %foldclose!
1131 let s:lnum = 1
1132
1133 " the originally open folds will be all folds we encounter that aren't
1134 " already in the list of closed folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001135 while s:lnum <= s:end
Bram Moolenaar5c736222010-01-06 20:54:52 +01001136 if foldclosed(s:lnum) == s:lnum
1137 " default fold text has '+-' and then a number of dashes equal to fold
1138 " level, so subtract 2 from index of first non-dash after the dashes
1139 " in order to get the fold level of the current fold
1140 let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2
Bram Moolenaar5c736222010-01-06 20:54:52 +01001141 let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"}
1142 " only add the fold if we don't already have it
1143 if empty(s:allfolds) || index(s:allfolds, s:newfold) == -1
1144 let s:newfold.type = "open-fold"
1145 call add(s:allfolds, s:newfold)
1146 endif
1147 " open the fold so we can find any contained folds
1148 execute s:lnum."foldopen"
1149 else
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001150 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001151 call s:pgb.incr()
1152 if s:pgb.needs_redraw
1153 redrawstatus
1154 let s:pgb.needs_redraw = 0
1155 endif
1156 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001157 let s:lnum = s:lnum + 1
1158 endif
1159 endwhile
1160
1161 " sort the folds so that we only ever need to look at the first item in the
1162 " list of folds
1163 call sort(s:allfolds, "s:FoldCompare")
1164
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001165 let &l:foldtext = s:foldtext_save
Bram Moolenaar5c736222010-01-06 20:54:52 +01001166 unlet s:foldtext_save
1167
1168 " close all folds again so we can get the fold text as we go
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001169 silent! %foldclose!
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001170
Bram Moolenaar251e1912011-06-19 05:09:16 +02001171 " Go through and remove folds we don't need to (or cannot) process in the
1172 " current conversion range
1173 "
1174 " If a fold is removed which contains other folds, which are included, we need
1175 " to adjust the level of the included folds as used by the conversion logic
1176 " (avoiding special cases is good)
1177 "
1178 " Note any time we remove a fold, either all of the included folds are in it,
1179 " or none of them, because we only remove a fold if neither its start nor its
1180 " end are within the conversion range.
1181 let leveladjust = 0
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001182 for afold in s:allfolds
1183 let removed = 0
1184 if exists("g:html_start_line") && exists("g:html_end_line")
1185 if afold.firstline < g:html_start_line
Bram Moolenaar251e1912011-06-19 05:09:16 +02001186 if afold.lastline <= g:html_end_line && afold.lastline >= g:html_start_line
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001187 " if a fold starts before the range to convert but stops within the
1188 " range, we need to include it. Make it start on the first converted
1189 " line.
1190 let afold.firstline = g:html_start_line
1191 else
1192 " if the fold lies outside the range or the start and stop enclose
1193 " the entire range, don't bother parsing it
1194 call remove(s:allfolds, index(s:allfolds, afold))
1195 let removed = 1
Bram Moolenaar251e1912011-06-19 05:09:16 +02001196 if afold.lastline > g:html_end_line
1197 let leveladjust += 1
1198 endif
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001199 endif
1200 elseif afold.firstline > g:html_end_line
1201 " If the entire fold lies outside the range we need to remove it.
1202 call remove(s:allfolds, index(s:allfolds, afold))
1203 let removed = 1
1204 endif
1205 elseif exists("g:html_start_line")
1206 if afold.firstline < g:html_start_line
1207 " if there is no last line, but there is a first line, the end of the
1208 " fold will always lie within the region of interest, so keep it
1209 let afold.firstline = g:html_start_line
1210 endif
1211 elseif exists("g:html_end_line")
1212 " if there is no first line we default to the first line in the buffer so
1213 " the fold start will always be included if the fold itself is included.
1214 " If however the entire fold lies outside the range we need to remove it.
1215 if afold.firstline > g:html_end_line
1216 call remove(s:allfolds, index(s:allfolds, afold))
1217 let removed = 1
1218 endif
1219 endif
1220 if !removed
Bram Moolenaar251e1912011-06-19 05:09:16 +02001221 let afold.level -= leveladjust
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001222 if afold.level+1 > s:foldcolumn
1223 let s:foldcolumn = afold.level+1
1224 endif
1225 endif
1226 endfor
Bram Moolenaar251e1912011-06-19 05:09:16 +02001227
1228 " if we've removed folds containing the conversion range from processing,
1229 " getting foldtext as we go won't know to open the removed folds, so the
1230 " foldtext would be wrong; open them now.
1231 "
1232 " Note that only when a start and an end line is specified will a fold
1233 " containing the current range ever be removed.
1234 while leveladjust > 0
1235 exe g:html_start_line."foldopen"
1236 let leveladjust -= 1
1237 endwhile
Bram Moolenaar5c736222010-01-06 20:54:52 +01001238endif
1239
1240" Now loop over all lines in the original text to convert to html.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241" Use html_start_line and html_end_line if they are set.
Bram Moolenaarb02cbe32010-07-11 22:38:52 +02001242if exists("g:html_start_line")
Bram Moolenaar071d4272004-06-13 20:20:40 +00001243 let s:lnum = html_start_line
1244 if s:lnum < 1 || s:lnum > line("$")
1245 let s:lnum = 1
1246 endif
1247else
1248 let s:lnum = 1
1249endif
Bram Moolenaarb02cbe32010-07-11 22:38:52 +02001250if exists("g:html_end_line")
Bram Moolenaar071d4272004-06-13 20:20:40 +00001251 let s:end = html_end_line
1252 if s:end < s:lnum || s:end > line("$")
1253 let s:end = line("$")
1254 endif
1255else
1256 let s:end = line("$")
1257endif
1258
Bram Moolenaar5c736222010-01-06 20:54:52 +01001259" stack to keep track of all the folds containing the current line
1260let s:foldstack = []
1261
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001262if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001263 let s:pgb = s:ProgressBar("Processing lines:", s:end - s:lnum + 1, s:orgwin)
1264endif
1265
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001266if s:settings.number_lines
Bram Moolenaar5c736222010-01-06 20:54:52 +01001267 let s:margin = strlen(s:end) + 1
1268else
1269 let s:margin = 0
1270endif
1271
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001272if has('folding') && !s:settings.ignore_folding
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001273 let s:foldfillchar = &fillchars[matchend(&fillchars, 'fold:')]
1274 if s:foldfillchar == ''
1275 let s:foldfillchar = '-'
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001276 endif
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001277endif
1278let s:difffillchar = &fillchars[matchend(&fillchars, 'diff:')]
1279if s:difffillchar == ''
1280 let s:difffillchar = '-'
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001281endif
1282
Bram Moolenaar5c736222010-01-06 20:54:52 +01001283let s:foldId = 0
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001284
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001285if !s:settings.expand_tabs
1286 " If keeping tabs, add them to printable characters so we keep them when
1287 " formatting text (strtrans() doesn't replace printable chars)
1288 let s:old_isprint = &isprint
1289 setlocal isprint+=9
1290endif
1291
Bram Moolenaar071d4272004-06-13 20:20:40 +00001292while s:lnum <= s:end
1293
Bram Moolenaar47136d72004-10-12 20:02:24 +00001294 " If there are filler lines for diff mode, show these above the line.
1295 let s:filler = diff_filler(s:lnum)
1296 if s:filler > 0
1297 let s:n = s:filler
1298 while s:n > 0
Bram Moolenaar5c736222010-01-06 20:54:52 +01001299 let s:new = repeat(s:difffillchar, 3)
Bram Moolenaar47136d72004-10-12 20:02:24 +00001300
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001301 if s:n > 2 && s:n < s:filler && !s:settings.whole_filler
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001302 let s:new = s:new . " " . s:filler . " inserted lines "
1303 let s:n = 2
1304 endif
1305
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001306 if !s:settings.no_pre
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001307 " HTML line wrapping is off--go ahead and fill to the margin
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001308 " TODO: what about when CSS wrapping is turned on?
Bram Moolenaar5c736222010-01-06 20:54:52 +01001309 let s:new = s:new . repeat(s:difffillchar, &columns - strlen(s:new) - s:margin)
1310 else
1311 let s:new = s:new . repeat(s:difffillchar, 3)
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001312 endif
1313
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001314 let s:new = s:HtmlFormat_d(s:new, s:DIFF_D_ID, 0)
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001315 if s:settings.number_lines
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001316 " Indent if line numbering is on. Indent gets style of line number
1317 " column.
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001318 let s:new = s:HtmlFormat_n(repeat(' ', s:margin), s:LINENR_ID, 0, 0) . s:new
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001319 endif
1320 if s:settings.dynamic_folds && !s:settings.no_foldcolumn && s:foldcolumn > 0
1321 " Indent for foldcolumn if there is one. Assume it's empty, there should
1322 " not be a fold for deleted lines in diff mode.
1323 let s:new = s:FoldColumn_fill() . s:new
Bram Moolenaar5c736222010-01-06 20:54:52 +01001324 endif
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001325 call add(s:lines, s:new.s:HtmlEndline)
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001326
Bram Moolenaar47136d72004-10-12 20:02:24 +00001327 let s:n = s:n - 1
1328 endwhile
1329 unlet s:n
1330 endif
1331 unlet s:filler
1332
1333 " Start the line with the line number.
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001334 if s:settings.number_lines
Bram Moolenaar5c736222010-01-06 20:54:52 +01001335 let s:numcol = repeat(' ', s:margin - 1 - strlen(s:lnum)) . s:lnum . ' '
Bram Moolenaar47136d72004-10-12 20:02:24 +00001336 endif
1337
Bram Moolenaar5c736222010-01-06 20:54:52 +01001338 let s:new = ""
1339
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001340 if has('folding') && !s:settings.ignore_folding && foldclosed(s:lnum) > -1 && !s:settings.dynamic_folds
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001341 "
Bram Moolenaar5c736222010-01-06 20:54:52 +01001342 " This is the beginning of a folded block (with no dynamic folding)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001343 let s:new = foldtextresult(s:lnum)
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001344 if !s:settings.no_pre
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001345 " HTML line wrapping is off--go ahead and fill to the margin
1346 let s:new = s:new . repeat(s:foldfillchar, &columns - strlen(s:new))
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001347 endif
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001348
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001349 " put numcol in a separate group for sake of unselectable text
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001350 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)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001351
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001352 " Skip to the end of the fold
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001353 let s:new_lnum = foldclosedend(s:lnum)
1354
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001355 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001356 call s:pgb.incr(s:new_lnum - s:lnum)
1357 endif
1358
1359 let s:lnum = s:new_lnum
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001360
1361 else
1362 "
Bram Moolenaar5c736222010-01-06 20:54:52 +01001363 " A line that is not folded, or doing dynamic folding.
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001364 "
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001365 let s:line = getline(s:lnum)
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001366 let s:len = strlen(s:line)
1367
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001368 if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +01001369 " First insert a closing for any open folds that end on this line
1370 while !empty(s:foldstack) && get(s:foldstack,0).lastline == s:lnum-1
1371 let s:new = s:new."</span></span>"
1372 call remove(s:foldstack, 0)
1373 endwhile
1374
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001375 " Now insert an opening for any new folds that start on this line
Bram Moolenaar5c736222010-01-06 20:54:52 +01001376 let s:firstfold = 1
1377 while !empty(s:allfolds) && get(s:allfolds,0).firstline == s:lnum
1378 let s:foldId = s:foldId + 1
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001379 let s:new .= "<span id='"
1380 let s:new .= (exists('g:html_diff_win_num') ? "win".g:html_diff_win_num : "")
Bram Moolenaar31c31672013-06-26 13:28:14 +02001381 let s:new .= "fold".s:foldId.s:settings.id_suffix."' class='".s:allfolds[0].type."'>"
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001382
Bram Moolenaar5c736222010-01-06 20:54:52 +01001383
1384 " Unless disabled, add a fold column for the opening line of a fold.
1385 "
1386 " Note that dynamic folds require using css so we just use css to take
1387 " care of the leading spaces rather than using &nbsp; in the case of
1388 " html_no_pre to make it easier
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001389 if !s:settings.no_foldcolumn
Bram Moolenaar5c736222010-01-06 20:54:52 +01001390 " add fold column that can open the new fold
1391 if s:allfolds[0].level > 1 && s:firstfold
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001392 let s:new = s:new . s:FoldColumn_build('|', s:allfolds[0].level - 1, 0, "",
Bram Moolenaar31c31672013-06-26 13:28:14 +02001393 \ 'toggle-open FoldColumn','javascript:toggleFold("fold'.s:foldstack[0].id.s:settings.id_suffix.'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001394 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001395 " add the filler spaces separately from the '+' char so that it can be
1396 " shown/hidden separately during a hover unfold
1397 let s:new = s:new . s:FoldColumn_build("+", 1, 0, "",
Bram Moolenaar31c31672013-06-26 13:28:14 +02001398 \ 'toggle-open FoldColumn', 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001399 " If this is not the last fold we're opening on this line, we need
1400 " to keep the filler spaces hidden if the fold is opened by mouse
1401 " hover. If it is the last fold to open in the line, we shouldn't hide
1402 " them, so don't apply the toggle-filler class.
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001403 let s:new = s:new . s:FoldColumn_build(" ", 1, s:foldcolumn - s:allfolds[0].level - 1, "",
1404 \ 'toggle-open FoldColumn'. (get(s:allfolds, 1, {'firstline': 0}).firstline == s:lnum ?" toggle-filler" :""),
Bram Moolenaar31c31672013-06-26 13:28:14 +02001405 \ 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001406
1407 " add fold column that can close the new fold
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001408 " only add extra blank space if we aren't opening another fold on the
1409 " same line
Bram Moolenaar5c736222010-01-06 20:54:52 +01001410 if get(s:allfolds, 1, {'firstline': 0}).firstline != s:lnum
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001411 let s:extra_space = s:foldcolumn - s:allfolds[0].level
1412 else
1413 let s:extra_space = 0
Bram Moolenaar5c736222010-01-06 20:54:52 +01001414 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001415 if s:firstfold
1416 " the first fold in a line has '|' characters from folds opened in
1417 " previous lines, before the '-' for this fold
1418 let s:new .= s:FoldColumn_build('|', s:allfolds[0].level - 1, s:extra_space, '-',
Bram Moolenaar31c31672013-06-26 13:28:14 +02001419 \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001420 else
1421 " any subsequent folds in the line only add a single '-'
1422 let s:new = s:new . s:FoldColumn_build("-", 1, s:extra_space, "",
Bram Moolenaar31c31672013-06-26 13:28:14 +02001423 \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001424 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001425 let s:firstfold = 0
1426 endif
1427
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001428 " Add fold text, moving the span ending to the next line so collapsing
1429 " of folds works correctly.
1430 " Put numcol in a separate group for sake of unselectable text.
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001431 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', '')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001432 let s:new = s:new . "<span class='fulltext'>"
1433
1434 " open the fold now that we have the fold text to allow retrieval of
1435 " fold text for subsequent folds
1436 execute s:lnum."foldopen"
1437 call insert(s:foldstack, remove(s:allfolds,0))
1438 let s:foldstack[0].id = s:foldId
1439 endwhile
1440
1441 " Unless disabled, add a fold column for other lines.
1442 "
1443 " Note that dynamic folds require using css so we just use css to take
1444 " care of the leading spaces rather than using &nbsp; in the case of
1445 " html_no_pre to make it easier
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001446 if !s:settings.no_foldcolumn
Bram Moolenaar5c736222010-01-06 20:54:52 +01001447 if empty(s:foldstack)
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001448 " add the empty foldcolumn for unfolded lines if there is a fold
1449 " column at all
1450 if s:foldcolumn > 0
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001451 let s:new = s:new . s:FoldColumn_fill()
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001452 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001453 else
1454 " add the fold column for folds not on the opening line
1455 if get(s:foldstack, 0).firstline < s:lnum
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001456 let s:new = s:new . s:FoldColumn_build('|', s:foldstack[0].level, s:foldcolumn - s:foldstack[0].level, "",
Bram Moolenaar31c31672013-06-26 13:28:14 +02001457 \ 'FoldColumn', 'javascript:toggleFold("fold'.s:foldstack[0].id.s:settings.id_suffix.'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001458 endif
1459 endif
1460 endif
1461 endif
1462
1463 " Now continue with the unfolded line text
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001464 if s:settings.number_lines
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001465 let s:new = s:new . s:HtmlFormat_n(s:numcol, s:LINENR_ID, 0, s:lnum)
Bram Moolenaar31c31672013-06-26 13:28:14 +02001466 elseif s:settings.line_ids
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001467 let s:new = s:new . s:HtmlFormat_n("", s:LINENR_ID, 0, s:lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001468 endif
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001469
Bram Moolenaar47136d72004-10-12 20:02:24 +00001470 " Get the diff attribute, if any.
1471 let s:diffattr = diff_hlID(s:lnum, 1)
1472
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001473 " initialize conceal info to act like not concealed, just in case
1474 let s:concealinfo = [0, '']
1475
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001476 " Loop over each character in the line
1477 let s:col = 1
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001478
1479 " most of the time we won't use the diff_id, initialize to zero
1480 let s:diff_id = 0
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001481
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001482 while s:col <= s:len || (s:col == 1 && s:diffattr)
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001483 let s:startcol = s:col " The start column for processing text
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001484 if !s:settings.ignore_conceal && has('conceal')
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001485 let s:concealinfo = synconcealed(s:lnum, s:col)
1486 endif
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001487 if !s:settings.ignore_conceal && s:concealinfo[0]
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001488 let s:col = s:col + 1
1489 " Speed loop (it's small - that's the trick)
1490 " Go along till we find a change in the match sequence number (ending
1491 " the specific concealed region) or until there are no more concealed
1492 " characters.
1493 while s:col <= s:len && s:concealinfo == synconcealed(s:lnum, s:col) | let s:col = s:col + 1 | endwhile
1494 elseif s:diffattr
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001495 let s:diff_id = diff_hlID(s:lnum, s:col)
1496 let s:id = synID(s:lnum, s:col, 1)
Bram Moolenaar47136d72004-10-12 20:02:24 +00001497 let s:col = s:col + 1
1498 " Speed loop (it's small - that's the trick)
1499 " Go along till we find a change in hlID
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001500 while s:col <= s:len && s:id == synID(s:lnum, s:col, 1)
1501 \ && s:diff_id == diff_hlID(s:lnum, s:col) |
1502 \ let s:col = s:col + 1 |
1503 \ endwhile
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001504 if s:len < &columns && !s:settings.no_pre
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001505 " Add spaces at the end of the raw text line to extend the changed
1506 " line to the full width.
Bram Moolenaar5c736222010-01-06 20:54:52 +01001507 let s:line = s:line . repeat(' ', &columns - virtcol([s:lnum, s:len]) - s:margin)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001508 let s:len = &columns
1509 endif
Bram Moolenaar47136d72004-10-12 20:02:24 +00001510 else
1511 let s:id = synID(s:lnum, s:col, 1)
1512 let s:col = s:col + 1
1513 " Speed loop (it's small - that's the trick)
1514 " Go along till we find a change in synID
1515 while s:col <= s:len && s:id == synID(s:lnum, s:col, 1) | let s:col = s:col + 1 | endwhile
1516 endif
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001517
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001518 if s:settings.ignore_conceal || !s:concealinfo[0]
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001519 " Expand tabs if needed
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001520 let s:expandedtab = strpart(s:line, s:startcol - 1, s:col - s:startcol)
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001521 if s:settings.expand_tabs
1522 let s:offset = 0
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001523 let s:idx = stridx(s:expandedtab, "\t")
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001524 while s:idx >= 0
1525 if has("multi_byte_encoding")
1526 if s:startcol + s:idx == 1
1527 let s:i = &ts
1528 else
1529 if s:idx == 0
1530 let s:prevc = matchstr(s:line, '.\%' . (s:startcol + s:idx + s:offset) . 'c')
1531 else
1532 let s:prevc = matchstr(s:expandedtab, '.\%' . (s:idx + 1) . 'c')
1533 endif
1534 let s:vcol = virtcol([s:lnum, s:startcol + s:idx + s:offset - len(s:prevc)])
1535 let s:i = &ts - (s:vcol % &ts)
1536 endif
1537 let s:offset -= s:i - 1
1538 else
1539 let s:i = &ts - ((s:idx + s:startcol - 1) % &ts)
1540 endif
1541 let s:expandedtab = substitute(s:expandedtab, '\t', repeat(' ', s:i), '')
1542 let s:idx = stridx(s:expandedtab, "\t")
1543 endwhile
1544 end
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001545
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001546 " get the highlight group name to use
1547 let s:id = synIDtrans(s:id)
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001548 else
1549 " use Conceal highlighting for concealed text
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001550 let s:id = s:CONCEAL_ID
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001551 let s:expandedtab = s:concealinfo[1]
1552 endif
1553
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001554 " Output the text with the same synID, with class set to the highlight ID
1555 " name, unless it has been concealed completely.
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001556 if strlen(s:expandedtab) > 0
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001557 let s:new = s:new . s:HtmlFormat(s:expandedtab, s:id, s:diff_id, "", 0)
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001558 endif
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001559 endwhile
1560 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001561
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001562 call extend(s:lines, split(s:new.s:HtmlEndline, '\n', 1))
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001563 if !s:settings.no_progress && s:pgb.needs_redraw
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001564 redrawstatus
1565 let s:pgb.needs_redraw = 0
1566 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001567 let s:lnum = s:lnum + 1
Bram Moolenaar313b7232007-05-05 17:56:55 +00001568
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001569 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001570 call s:pgb.incr()
1571 endif
1572endwhile
1573
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001574if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +01001575 " finish off any open folds
1576 while !empty(s:foldstack)
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001577 let s:lines[-1].="</span></span>"
Bram Moolenaar5c736222010-01-06 20:54:52 +01001578 call remove(s:foldstack, 0)
1579 endwhile
1580
1581 " add fold column to the style list if not already there
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001582 let s:id = s:FOLD_C_ID
1583 if !has_key(s:stylelist, s:id)
1584 let s:stylelist[s:id] = '.FoldColumn { ' . s:CSS1(s:id) . '}'
Bram Moolenaar5c736222010-01-06 20:54:52 +01001585 endif
1586endif
1587
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001588if s:settings.no_pre
1589 if !s:settings.use_css
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001590 " Close off the font tag that encapsulates the whole <body>
Bram Moolenaarbebca9d2010-08-07 15:47:30 +02001591 call extend(s:lines, ["</font>", "</body>", "</html>"])
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001592 else
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001593 call extend(s:lines, ["</div>", "</body>", "</html>"])
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001594 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001595else
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001596 call extend(s:lines, ["</pre>", "</body>", "</html>"])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001597endif
1598
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001599exe s:newwin . "wincmd w"
1600call setline(1, s:lines)
1601unlet s:lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00001602
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001603" Mangle modelines so Vim doesn't try to use HTML text as a modeline if editing
1604" this file in the future; need to do this after generating all the text in case
1605" the modeline text has different highlight groups which all turn out to be
1606" stripped from the final output.
Bram Moolenaardd007ed2013-07-09 15:44:17 +02001607%s!\v(%(^|\s+)%([Vv]i%(m%([<=>]?\d+)?)?|ex)):!\1\&#0058;!ge
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001608
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001609" The generated HTML is admittedly ugly and takes a LONG time to fold.
1610" Make sure the user doesn't do syntax folding when loading a generated file,
1611" using a modeline.
1612call append(line('$'), "<!-- vim: set foldmethod=manual : -->")
1613
Bram Moolenaar071d4272004-06-13 20:20:40 +00001614" Now, when we finally know which, we define the colors and styles
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001615if s:settings.use_css
Bram Moolenaar071d4272004-06-13 20:20:40 +00001616 1;/<style type="text/+1
1617endif
1618
Bram Moolenaar071d4272004-06-13 20:20:40 +00001619" Normal/global attributes
1620" For Netscape 4, set <body> attributes too, though, strictly speaking, it's
1621" incorrect.
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001622if s:settings.use_css
1623 if s:settings.no_pre
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001624 call append('.', "body { color: " . s:fgc . "; background-color: " . s:bgc . "; font-family: ". s:htmlfont ."; }")
1625 +
Bram Moolenaar071d4272004-06-13 20:20:40 +00001626 else
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001627 call append('.', "pre { " . s:whitespace . "font-family: ". s:htmlfont ."; color: " . s:fgc . "; background-color: " . s:bgc . "; }")
1628 +
Bram Moolenaar071d4272004-06-13 20:20:40 +00001629 yank
1630 put
1631 execute "normal! ^cwbody\e"
Bram Moolenaar8e5af3e2011-04-28 19:02:44 +02001632 " body should not have the wrap formatting, only the pre section
1633 if s:whitespace != ''
1634 exec 's#'.s:whitespace
1635 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001636 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001637 " fix browser inconsistencies (sometimes within the same browser) of different
1638 " default font size for different elements
1639 call append('.', '* { font-size: 1em; }')
1640 +
1641 " if we use any input elements for unselectable content, make sure they look
1642 " like normal text
1643 if !empty(s:settings.prevent_copy)
1644 call append('.', 'input { border: none; margin: 0; padding: 0; font-family: '.s:htmlfont.'; }')
1645 +
1646 " ch units for browsers which support them, em units for a somewhat
1647 " reasonable fallback. Also make sure the special elements for size
1648 " calculations aren't seen.
1649 call append('.', [
1650 \ "input[size='1'] { width: 1em; width: 1ch; }",
1651 \ "input[size='2'] { width: 2em; width: 2ch; }",
1652 \ "input[size='3'] { width: 3em; width: 3ch; }",
1653 \ "input[size='4'] { width: 4em; width: 4ch; }",
1654 \ "input[size='5'] { width: 5em; width: 5ch; }",
1655 \ "input[size='6'] { width: 6em; width: 6ch; }",
1656 \ "input[size='7'] { width: 7em; width: 7ch; }",
1657 \ "input[size='8'] { width: 8em; width: 8ch; }",
1658 \ "input[size='9'] { width: 9em; width: 9ch; }",
1659 \ "input[size='10'] { width: 10em; width: 10ch; }",
1660 \ "input[size='11'] { width: 11em; width: 11ch; }",
1661 \ "input[size='12'] { width: 12em; width: 12ch; }",
1662 \ "input[size='13'] { width: 13em; width: 13ch; }",
1663 \ "input[size='14'] { width: 14em; width: 14ch; }",
1664 \ "input[size='15'] { width: 15em; width: 15ch; }",
1665 \ "input[size='16'] { width: 16em; width: 16ch; }",
1666 \ "input[size='17'] { width: 17em; width: 17ch; }",
1667 \ "input[size='18'] { width: 18em; width: 18ch; }",
1668 \ "input[size='19'] { width: 19em; width: 19ch; }",
1669 \ "input[size='20'] { width: 20em; width: 20ch; }",
1670 \ "#oneCharWidth, #oneEmWidth, #oneInputWidth { padding: 0; margin: 0; position: absolute; left: -999999px; visibility: hidden; }"
1671 \ ])
1672 +21
1673 for w in range(5, 100, 5)
1674 let base = 0.01 * w
1675 call append('.', join(map(range(1,20), "'.em'.w.' input[size='''.v:val.'''] { width: '.string(v:val*base).'em; }'")))
1676 +
1677 endfor
1678 if s:settings.prevent_copy =~# 'f'
1679 " Make the cursor show active fold columns as active areas, and empty fold
1680 " columns as not interactive.
1681 call append('.', ['input.FoldColumn { cursor: pointer; }',
1682 \ 'input.FoldColumn[value=""] { cursor: default; }'
1683 \ ])
1684 +2
1685 endif
1686 " make line number column show as non-interactive if not selectable
1687 if s:settings.prevent_copy =~# 'n'
1688 call append('.', 'input.LineNr { cursor: default; }')
1689 +
1690 endif
1691 " make fold text and line number column within fold text show as
1692 " non-interactive if not selectable
1693 if (s:settings.prevent_copy =~# 'n' || s:settings.prevent_copy =~# 't') && !s:settings.ignore_folding
1694 call append('.', 'input.Folded { cursor: default; }')
1695 +
1696 endif
1697 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001698else
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001699 execute '%s:<body\([^>]*\):<body bgcolor="' . s:bgc . '" text="' . s:fgc . '"\1>\r<font face="'. s:htmlfont .'"'
Bram Moolenaar071d4272004-06-13 20:20:40 +00001700endif
1701
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001702" Gather attributes for all other classes. Do diff first so that normal
1703" highlight groups are inserted before it.
1704if s:settings.use_css
1705 if s:diff_mode
1706 call append('.', filter(map(keys(s:diffstylelist), "s:diffstylelist[v:val]"), 'v:val != ""'))
1707 endif
1708 if !empty(s:stylelist)
1709 call append('.', filter(map(keys(s:stylelist), "s:stylelist[v:val]"), 'v:val != ""'))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001710 endif
1711endif
1712
Bram Moolenaar071d4272004-06-13 20:20:40 +00001713" Add hyperlinks
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001714" TODO: add option to not do this? Maybe just make the color the same as the
1715" text highlight group normally is?
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001716%s+\(https\=://\S\{-}\)\(\([.,;:}]\=\(\s\|$\)\)\|[\\"'<>]\|&gt;\|&lt;\|&quot;\)+<a href="\1">\1</a>\2+ge
Bram Moolenaar071d4272004-06-13 20:20:40 +00001717
1718" The DTD
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001719if s:settings.use_xhtml
1720 exe "normal! gg$a\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
1721elseif s:settings.use_css && !s:settings.no_pre
1722 exe "normal! gg0i<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
Bram Moolenaar313b7232007-05-05 17:56:55 +00001723else
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001724 exe "normal! gg0i<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001725endif
1726
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001727if s:settings.use_xhtml
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001728 exe "normal! gg/<html/e\na xmlns=\"http://www.w3.org/1999/xhtml\"\e"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001729endif
1730
1731" Cleanup
1732%s:\s\+$::e
1733
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001734" Restore old settings (new window first)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001735"
1736" Don't bother restoring foldmethod in case it was syntax because the markup is
1737" so weirdly formatted it can take a LONG time.
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001738let &l:foldenable = s:old_fen
Bram Moolenaar071d4272004-06-13 20:20:40 +00001739let &report = s:old_report
1740let &title = s:old_title
1741let &icon = s:old_icon
1742let &paste = s:old_paste
1743let &magic = s:old_magic
1744let @/ = s:old_search
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001745let &more = s:old_more
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001746
1747" switch to original window to restore those settings
Bram Moolenaar071d4272004-06-13 20:20:40 +00001748exe s:orgwin . "wincmd w"
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001749
1750if !s:settings.expand_tabs
1751 let &l:isprint = s:old_isprint
1752endif
Bram Moolenaar166af9b2010-11-16 20:34:40 +01001753let &l:stl = s:origwin_stl
Bram Moolenaar071d4272004-06-13 20:20:40 +00001754let &l:et = s:old_et
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001755let &l:scrollbind = s:old_bind
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001756
1757" and back to the new window again to end there
Bram Moolenaar071d4272004-06-13 20:20:40 +00001758exe s:newwin . "wincmd w"
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001759
Bram Moolenaar166af9b2010-11-16 20:34:40 +01001760let &l:stl = s:newwin_stl
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001761exec 'resize' s:old_winheight
1762let &l:winfixheight = s:old_winfixheight
Bram Moolenaar071d4272004-06-13 20:20:40 +00001763
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001764let &ls=s:ls
1765
Bram Moolenaar071d4272004-06-13 20:20:40 +00001766" Save a little bit of memory (worth doing?)
Bram Moolenaar8e5af3e2011-04-28 19:02:44 +02001767unlet s:htmlfont s:whitespace
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001768unlet s:old_et s:old_paste s:old_icon s:old_report s:old_title s:old_search
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001769unlet s:old_magic s:old_more s:old_fen s:old_winheight
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001770unlet! s:old_isprint
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001771unlet s:whatterm s:stylelist s:diffstylelist s:lnum s:end s:margin s:fgc s:bgc s:old_winfixheight
1772unlet! s:col s:id s:attr s:len s:line s:new s:expandedtab s:concealinfo s:diff_mode
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001773unlet! s:orgwin s:newwin s:orgbufnr s:idx s:i s:offset s:ls s:origwin_stl
1774unlet! s:newwin_stl s:current_syntax
Bram Moolenaar05159a02005-02-26 23:04:13 +00001775if !v:profiling
1776 delfunc s:HtmlColor
1777 delfunc s:HtmlFormat
1778 delfunc s:CSS1
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001779 delfunc s:BuildStyleWrapper
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001780 if !s:settings.use_css
Bram Moolenaar05159a02005-02-26 23:04:13 +00001781 delfunc s:HtmlOpening
1782 delfunc s:HtmlClosing
1783 endif
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001784 if s:settings.dynamic_folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001785 delfunc s:FoldCompare
1786 endif
1787
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001788 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001789 delfunc s:ProgressBar
1790 delfunc s:progressbar.paint
1791 delfunc s:progressbar.incr
1792 unlet s:pgb s:progressbar
1793 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001794endif
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001795
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001796unlet! s:new_lnum s:diffattr s:difffillchar s:foldfillchar s:HtmlSpace
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001797unlet! s:LeadingSpace s:HtmlEndline s:firstfold s:numcol s:foldcolumn
1798unlet s:foldstack s:allfolds s:foldId s:settings
Bram Moolenaar5c736222010-01-06 20:54:52 +01001799
1800let &cpo = s:cpo_sav
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001801unlet! s:cpo_sav
Bram Moolenaar5c736222010-01-06 20:54:52 +01001802
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001803" Make sure any patches will probably use consistent indent
Bram Moolenaar7c86f4c2010-07-18 14:07:22 +02001804" vim: ts=8 sw=2 sts=2 noet