blob: ddc7819be2824a91580dfeba3ab8db3af4a538e9 [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 Moolenaar60cce2f2015-10-13 23:21:27 +02003" Last Change: 2015 Sep 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 Moolenaar60cce2f2015-10-13 23:21:27 +020029 if type(g:html_font) == type([])
30 let s:htmlfont = "'". join(g:html_font,"','") . "', monospace"
31 else
32 let s:htmlfont = "'". g:html_font . "', monospace"
33 endif
Bram Moolenaar313b7232007-05-05 17:56:55 +000034else
35 let s:htmlfont = "monospace"
36endif
37
Bram Moolenaar076e8b22010-08-05 21:54:00 +020038let s:settings = tohtml#GetUserSettings()
Bram Moolenaar5c736222010-01-06 20:54:52 +010039
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020040if !exists('s:FOLDED_ID')
41 let s:FOLDED_ID = hlID("Folded") | lockvar s:FOLDED_ID
42 let s:FOLD_C_ID = hlID("FoldColumn") | lockvar s:FOLD_C_ID
43 let s:LINENR_ID = hlID('LineNr') | lockvar s:LINENR_ID
44 let s:DIFF_D_ID = hlID("DiffDelete") | lockvar s:DIFF_D_ID
45 let s:DIFF_A_ID = hlID("DiffAdd") | lockvar s:DIFF_A_ID
46 let s:DIFF_C_ID = hlID("DiffChange") | lockvar s:DIFF_C_ID
47 let s:DIFF_T_ID = hlID("DiffText") | lockvar s:DIFF_T_ID
48 let s:CONCEAL_ID = hlID('Conceal') | lockvar s:CONCEAL_ID
49endif
50
Bram Moolenaar8e5af3e2011-04-28 19:02:44 +020051" Whitespace
52if s:settings.pre_wrap
53 let s:whitespace = "white-space: pre-wrap; "
54else
55 let s:whitespace = ""
56endif
57
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020058if !empty(s:settings.prevent_copy)
59 if s:settings.no_invalid
60 " User has decided they don't want invalid markup. Still works in
61 " OpenOffice, and for text editors, but when pasting into Microsoft Word the
62 " input elements get pasted too and they cannot be deleted (at least not
63 " easily).
64 let s:unselInputType = ""
65 else
66 " Prevent from copy-pasting the input elements into Microsoft Word where
67 " they cannot be deleted easily by deliberately inserting invalid markup.
68 let s:unselInputType = " type='invalid_input_type'"
69 endif
70endif
71
Bram Moolenaar071d4272004-06-13 20:20:40 +000072" When not in gui we can only guess the colors.
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020073" TODO - is this true anymore?
Bram Moolenaar071d4272004-06-13 20:20:40 +000074if has("gui_running")
75 let s:whatterm = "gui"
76else
77 let s:whatterm = "cterm"
78 if &t_Co == 8
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020079 let s:cterm_color = {
80 \ 0: "#808080", 1: "#ff6060", 2: "#00ff00", 3: "#ffff00",
81 \ 4: "#8080ff", 5: "#ff40ff", 6: "#00ffff", 7: "#ffffff"
82 \ }
Bram Moolenaar071d4272004-06-13 20:20:40 +000083 else
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020084 let s:cterm_color = {
85 \ 0: "#000000", 1: "#c00000", 2: "#008000", 3: "#804000",
86 \ 4: "#0000c0", 5: "#c000c0", 6: "#008080", 7: "#c0c0c0",
87 \ 8: "#808080", 9: "#ff6060", 10: "#00ff00", 11: "#ffff00",
88 \ 12: "#8080ff", 13: "#ff40ff", 14: "#00ffff", 15: "#ffffff"
89 \ }
Bram Moolenaar313b7232007-05-05 17:56:55 +000090
91 " Colors for 88 and 256 come from xterm.
92 if &t_Co == 88
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020093 call extend(s:cterm_color, {
94 \ 16: "#000000", 17: "#00008b", 18: "#0000cd", 19: "#0000ff",
95 \ 20: "#008b00", 21: "#008b8b", 22: "#008bcd", 23: "#008bff",
96 \ 24: "#00cd00", 25: "#00cd8b", 26: "#00cdcd", 27: "#00cdff",
97 \ 28: "#00ff00", 29: "#00ff8b", 30: "#00ffcd", 31: "#00ffff",
98 \ 32: "#8b0000", 33: "#8b008b", 34: "#8b00cd", 35: "#8b00ff",
99 \ 36: "#8b8b00", 37: "#8b8b8b", 38: "#8b8bcd", 39: "#8b8bff",
100 \ 40: "#8bcd00", 41: "#8bcd8b", 42: "#8bcdcd", 43: "#8bcdff",
101 \ 44: "#8bff00", 45: "#8bff8b", 46: "#8bffcd", 47: "#8bffff",
102 \ 48: "#cd0000", 49: "#cd008b", 50: "#cd00cd", 51: "#cd00ff",
103 \ 52: "#cd8b00", 53: "#cd8b8b", 54: "#cd8bcd", 55: "#cd8bff",
104 \ 56: "#cdcd00", 57: "#cdcd8b", 58: "#cdcdcd", 59: "#cdcdff",
105 \ 60: "#cdff00", 61: "#cdff8b", 62: "#cdffcd", 63: "#cdffff",
106 \ 64: "#ff0000"
107 \ })
108 call extend(s:cterm_color, {
109 \ 65: "#ff008b", 66: "#ff00cd", 67: "#ff00ff", 68: "#ff8b00",
110 \ 69: "#ff8b8b", 70: "#ff8bcd", 71: "#ff8bff", 72: "#ffcd00",
111 \ 73: "#ffcd8b", 74: "#ffcdcd", 75: "#ffcdff", 76: "#ffff00",
112 \ 77: "#ffff8b", 78: "#ffffcd", 79: "#ffffff", 80: "#2e2e2e",
113 \ 81: "#5c5c5c", 82: "#737373", 83: "#8b8b8b", 84: "#a2a2a2",
114 \ 85: "#b9b9b9", 86: "#d0d0d0", 87: "#e7e7e7"
115 \ })
Bram Moolenaar313b7232007-05-05 17:56:55 +0000116 elseif &t_Co == 256
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200117 call extend(s:cterm_color, {
118 \ 16: "#000000", 17: "#00005f", 18: "#000087", 19: "#0000af",
119 \ 20: "#0000d7", 21: "#0000ff", 22: "#005f00", 23: "#005f5f",
120 \ 24: "#005f87", 25: "#005faf", 26: "#005fd7", 27: "#005fff",
121 \ 28: "#008700", 29: "#00875f", 30: "#008787", 31: "#0087af",
122 \ 32: "#0087d7", 33: "#0087ff", 34: "#00af00", 35: "#00af5f",
123 \ 36: "#00af87", 37: "#00afaf", 38: "#00afd7", 39: "#00afff",
124 \ 40: "#00d700", 41: "#00d75f", 42: "#00d787", 43: "#00d7af",
125 \ 44: "#00d7d7", 45: "#00d7ff", 46: "#00ff00", 47: "#00ff5f",
126 \ 48: "#00ff87", 49: "#00ffaf", 50: "#00ffd7", 51: "#00ffff",
127 \ 52: "#5f0000", 53: "#5f005f", 54: "#5f0087", 55: "#5f00af",
128 \ 56: "#5f00d7", 57: "#5f00ff", 58: "#5f5f00", 59: "#5f5f5f",
129 \ 60: "#5f5f87", 61: "#5f5faf", 62: "#5f5fd7", 63: "#5f5fff",
130 \ 64: "#5f8700"
131 \ })
132 call extend(s:cterm_color, {
133 \ 65: "#5f875f", 66: "#5f8787", 67: "#5f87af", 68: "#5f87d7",
134 \ 69: "#5f87ff", 70: "#5faf00", 71: "#5faf5f", 72: "#5faf87",
135 \ 73: "#5fafaf", 74: "#5fafd7", 75: "#5fafff", 76: "#5fd700",
136 \ 77: "#5fd75f", 78: "#5fd787", 79: "#5fd7af", 80: "#5fd7d7",
137 \ 81: "#5fd7ff", 82: "#5fff00", 83: "#5fff5f", 84: "#5fff87",
138 \ 85: "#5fffaf", 86: "#5fffd7", 87: "#5fffff", 88: "#870000",
139 \ 89: "#87005f", 90: "#870087", 91: "#8700af", 92: "#8700d7",
140 \ 93: "#8700ff", 94: "#875f00", 95: "#875f5f", 96: "#875f87",
141 \ 97: "#875faf", 98: "#875fd7", 99: "#875fff", 100: "#878700",
142 \ 101: "#87875f", 102: "#878787", 103: "#8787af", 104: "#8787d7",
143 \ 105: "#8787ff", 106: "#87af00", 107: "#87af5f", 108: "#87af87",
144 \ 109: "#87afaf", 110: "#87afd7", 111: "#87afff", 112: "#87d700"
145 \ })
146 call extend(s:cterm_color, {
147 \ 113: "#87d75f", 114: "#87d787", 115: "#87d7af", 116: "#87d7d7",
148 \ 117: "#87d7ff", 118: "#87ff00", 119: "#87ff5f", 120: "#87ff87",
149 \ 121: "#87ffaf", 122: "#87ffd7", 123: "#87ffff", 124: "#af0000",
150 \ 125: "#af005f", 126: "#af0087", 127: "#af00af", 128: "#af00d7",
151 \ 129: "#af00ff", 130: "#af5f00", 131: "#af5f5f", 132: "#af5f87",
152 \ 133: "#af5faf", 134: "#af5fd7", 135: "#af5fff", 136: "#af8700",
153 \ 137: "#af875f", 138: "#af8787", 139: "#af87af", 140: "#af87d7",
154 \ 141: "#af87ff", 142: "#afaf00", 143: "#afaf5f", 144: "#afaf87",
155 \ 145: "#afafaf", 146: "#afafd7", 147: "#afafff", 148: "#afd700",
156 \ 149: "#afd75f", 150: "#afd787", 151: "#afd7af", 152: "#afd7d7",
157 \ 153: "#afd7ff", 154: "#afff00", 155: "#afff5f", 156: "#afff87",
158 \ 157: "#afffaf", 158: "#afffd7"
159 \ })
160 call extend(s:cterm_color, {
161 \ 159: "#afffff", 160: "#d70000", 161: "#d7005f", 162: "#d70087",
162 \ 163: "#d700af", 164: "#d700d7", 165: "#d700ff", 166: "#d75f00",
163 \ 167: "#d75f5f", 168: "#d75f87", 169: "#d75faf", 170: "#d75fd7",
164 \ 171: "#d75fff", 172: "#d78700", 173: "#d7875f", 174: "#d78787",
165 \ 175: "#d787af", 176: "#d787d7", 177: "#d787ff", 178: "#d7af00",
166 \ 179: "#d7af5f", 180: "#d7af87", 181: "#d7afaf", 182: "#d7afd7",
167 \ 183: "#d7afff", 184: "#d7d700", 185: "#d7d75f", 186: "#d7d787",
168 \ 187: "#d7d7af", 188: "#d7d7d7", 189: "#d7d7ff", 190: "#d7ff00",
169 \ 191: "#d7ff5f", 192: "#d7ff87", 193: "#d7ffaf", 194: "#d7ffd7",
170 \ 195: "#d7ffff", 196: "#ff0000", 197: "#ff005f", 198: "#ff0087",
171 \ 199: "#ff00af", 200: "#ff00d7", 201: "#ff00ff", 202: "#ff5f00",
172 \ 203: "#ff5f5f", 204: "#ff5f87"
173 \ })
174 call extend(s:cterm_color, {
175 \ 205: "#ff5faf", 206: "#ff5fd7", 207: "#ff5fff", 208: "#ff8700",
176 \ 209: "#ff875f", 210: "#ff8787", 211: "#ff87af", 212: "#ff87d7",
177 \ 213: "#ff87ff", 214: "#ffaf00", 215: "#ffaf5f", 216: "#ffaf87",
178 \ 217: "#ffafaf", 218: "#ffafd7", 219: "#ffafff", 220: "#ffd700",
179 \ 221: "#ffd75f", 222: "#ffd787", 223: "#ffd7af", 224: "#ffd7d7",
180 \ 225: "#ffd7ff", 226: "#ffff00", 227: "#ffff5f", 228: "#ffff87",
181 \ 229: "#ffffaf", 230: "#ffffd7", 231: "#ffffff", 232: "#080808",
182 \ 233: "#121212", 234: "#1c1c1c", 235: "#262626", 236: "#303030",
183 \ 237: "#3a3a3a", 238: "#444444", 239: "#4e4e4e", 240: "#585858",
184 \ 241: "#626262", 242: "#6c6c6c", 243: "#767676", 244: "#808080",
185 \ 245: "#8a8a8a", 246: "#949494", 247: "#9e9e9e", 248: "#a8a8a8",
186 \ 249: "#b2b2b2", 250: "#bcbcbc", 251: "#c6c6c6", 252: "#d0d0d0",
187 \ 253: "#dadada", 254: "#e4e4e4", 255: "#eeeeee"
188 \ })
Bram Moolenaar313b7232007-05-05 17:56:55 +0000189 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000190 endif
191endif
192
193" Return good color specification: in GUI no transformation is done, in
Bram Moolenaar313b7232007-05-05 17:56:55 +0000194" terminal return RGB values of known colors and empty string for unknown
Bram Moolenaar071d4272004-06-13 20:20:40 +0000195if s:whatterm == "gui"
196 function! s:HtmlColor(color)
197 return a:color
198 endfun
199else
200 function! s:HtmlColor(color)
Bram Moolenaar313b7232007-05-05 17:56:55 +0000201 if has_key(s:cterm_color, a:color)
202 return s:cterm_color[a:color]
Bram Moolenaar071d4272004-06-13 20:20:40 +0000203 else
204 return ""
205 endif
206 endfun
207endif
208
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200209" Find out the background and foreground color for use later
210let s:fgc = s:HtmlColor(synIDattr(hlID("Normal"), "fg#", s:whatterm))
211let s:bgc = s:HtmlColor(synIDattr(hlID("Normal"), "bg#", s:whatterm))
212if s:fgc == ""
213 let s:fgc = ( &background == "dark" ? "#ffffff" : "#000000" )
214endif
215if s:bgc == ""
216 let s:bgc = ( &background == "dark" ? "#000000" : "#ffffff" )
217endif
218
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200219if !s:settings.use_css
Bram Moolenaar071d4272004-06-13 20:20:40 +0000220 " Return opening HTML tag for given highlight id
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200221 function! s:HtmlOpening(id, extra_attrs)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000222 let a = ""
223 if synIDattr(a:id, "inverse")
224 " For inverse, we always must set both colors (and exchange them)
225 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200226 let a = a . '<span '.a:extra_attrs.'style="background-color: ' . ( x != "" ? x : s:fgc ) . '">'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000227 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
228 let a = a . '<font color="' . ( x != "" ? x : s:bgc ) . '">'
229 else
230 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200231 if x != ""
232 let a = a . '<span '.a:extra_attrs.'style="background-color: ' . x . '">'
233 elseif !empty(a:extra_attrs)
234 let a = a . '<span '.a:extra_attrs.'>'
235 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
237 if x != "" | let a = a . '<font color="' . x . '">' | endif
238 endif
239 if synIDattr(a:id, "bold") | let a = a . "<b>" | endif
240 if synIDattr(a:id, "italic") | let a = a . "<i>" | endif
241 if synIDattr(a:id, "underline") | let a = a . "<u>" | endif
242 return a
243 endfun
244
245 " Return closing HTML tag for given highlight id
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200246 function! s:HtmlClosing(id, has_extra_attrs)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000247 let a = ""
248 if synIDattr(a:id, "underline") | let a = a . "</u>" | endif
249 if synIDattr(a:id, "italic") | let a = a . "</i>" | endif
250 if synIDattr(a:id, "bold") | let a = a . "</b>" | endif
251 if synIDattr(a:id, "inverse")
252 let a = a . '</font></span>'
253 else
254 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
255 if x != "" | let a = a . '</font>' | endif
256 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200257 if x != "" || a:has_extra_attrs | let a = a . '</span>' | endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000258 endif
259 return a
260 endfun
261endif
262
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200263" Use a different function for formatting based on user options. This way we
264" can avoid a lot of logic during the actual execution.
265"
266" Build the function line by line containing only what is needed for the options
267" in use for maximum code sharing with minimal branch logic for greater speed.
268"
269" Note, 'exec' commands do not recognize line continuations, so must concatenate
270" lines rather than continue them.
271if s:settings.use_css
272 " save CSS to a list of rules to add to the output at the end of processing
273
274 " first, get the style names we need
275 let wrapperfunc_lines = [
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200276 \ 'function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, make_unselectable, unformatted)',
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200277 \ '',
278 \ ' let l:style_name = synIDattr(a:style_id, "name", s:whatterm)'
279 \ ]
280 if &diff
281 let wrapperfunc_lines += [
282 \ ' let l:diff_style_name = synIDattr(a:diff_style_id, "name", s:whatterm)']
283
284 " Add normal groups and diff groups to separate lists so we can order them to
285 " allow diff highlight to override normal highlight
286
287 " if primary style IS a diff style, grab it from the diff cache instead
288 " (always succeeds because we pre-populate it)
289 let wrapperfunc_lines += [
290 \ '',
291 \ ' if a:style_id == s:DIFF_D_ID || a:style_id == s:DIFF_A_ID ||'.
292 \ ' a:style_id == s:DIFF_C_ID || a:style_id == s:DIFF_T_ID',
293 \ ' let l:saved_style = get(s:diffstylelist,a:style_id)',
294 \ ' else'
295 \ ]
296 endif
297
298 " get primary style info from cache or build it on the fly if not found
299 let wrapperfunc_lines += [
300 \ ' let l:saved_style = get(s:stylelist,a:style_id)',
301 \ ' if type(l:saved_style) == type(0)',
302 \ ' unlet l:saved_style',
303 \ ' let l:saved_style = s:CSS1(a:style_id)',
304 \ ' if l:saved_style != ""',
305 \ ' let l:saved_style = "." . l:style_name . " { " . l:saved_style . "}"',
306 \ ' endif',
307 \ ' let s:stylelist[a:style_id]= l:saved_style',
308 \ ' endif'
309 \ ]
310 if &diff
311 let wrapperfunc_lines += [ ' endif' ]
312 endif
313
314 " Build the wrapper tags around the text. It turns out that caching these
315 " gives pretty much zero performance gain and adds a lot of logic.
316
317 let wrapperfunc_lines += [
318 \ '',
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200319 \ ' if l:saved_style == "" && empty(a:extra_attrs)'
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200320 \ ]
321 if &diff
322 let wrapperfunc_lines += [
323 \ ' if a:diff_style_id <= 0'
324 \ ]
325 endif
326 " no surroundings if neither primary nor diff style has any info
327 let wrapperfunc_lines += [
328 \ ' return a:text'
329 \ ]
330 if &diff
331 " no primary style, but diff style
332 let wrapperfunc_lines += [
333 \ ' else',
334 \ ' return "<span class=\"" .l:diff_style_name . "\">".a:text."</span>"',
335 \ ' endif'
336 \ ]
337 endif
338 " open tag for non-empty primary style
339 let wrapperfunc_lines += [
340 \ ' else']
341 " non-empty primary style. handle either empty or non-empty diff style.
342 "
343 " separate the two classes by a space to apply them both if there is a diff
344 " style name, unless the primary style is empty, then just use the diff style
345 " name
346 let diffstyle =
347 \ (&diff ? '(a:diff_style_id <= 0 ? "" : " ". l:diff_style_name) .'
348 \ : "")
349 if s:settings.prevent_copy == ""
350 let wrapperfunc_lines += [
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200351 \ ' return "<span ".a:extra_attrs."class=\"" . l:style_name .'.diffstyle.'"\">".a:text."</span>"'
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200352 \ ]
353 else
354
355 "
356 " Wrap the <input> in a <span> to allow fixing the stupid bug in some fonts
357 " which cause browsers to display a 1px gap between lines when these
358 " <input>s have a background color (maybe not really a bug, this isn't
359 " well-defined)
360 "
361 " use strwidth, because we care only about how many character boxes are
362 " needed to size the input, we don't care how many characters (including
363 " separately counted composing chars, from strchars()) or bytes (from
364 " len())the string contains. strdisplaywidth() is not needed because none of
365 " the unselectable groups can contain tab characters (fold column, fold
366 " text, line number).
367 "
368 " Note, if maxlength property needs to be added in the future, it will need
369 " to use strchars(), because HTML specifies that the maxlength parameter
370 " uses the number of unique codepoints for its limit.
371 let wrapperfunc_lines += [
372 \ ' if a:make_unselectable',
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200373 \ ' return "<span ".a:extra_attrs."class=\"" . l:style_name .'.diffstyle.'"\">'.
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200374 \ '<input'.s:unselInputType.' class=\"" . l:style_name .'.diffstyle.'"\"'.
375 \ ' value=\"".substitute(a:unformatted,''\s\+$'',"","")."\"'.
376 \ ' onselect=''this.blur(); return false;'''.
377 \ ' onmousedown=''this.blur(); return false;'''.
378 \ ' onclick=''this.blur(); return false;'''.
379 \ ' readonly=''readonly'''.
380 \ ' size=\"".strwidth(a:unformatted)."\"'.
381 \ (s:settings.use_xhtml ? '/' : '').'></span>"',
382 \ ' else',
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200383 \ ' return "<span ".a:extra_attrs."class=\"" . l:style_name .'. diffstyle .'"\">".a:text."</span>"'
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200384 \ ]
385 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200386 let wrapperfunc_lines += [
387 \ ' endif',
388 \ 'endfun'
389 \ ]
390else
391 " Non-CSS method just needs the wrapper.
392 "
393 " Functions used to get opening/closing automatically return null strings if
394 " no styles exist.
395 if &diff
396 let wrapperfunc_lines = [
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200397 \ 'function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, unusedarg, unusedarg2)',
398 \ ' return s:HtmlOpening(a:style_id, a:extra_attrs).(a:diff_style_id <= 0 ? "" :'.
399 \ 's:HtmlOpening(a:diff_style_id, "")).a:text.'.
400 \ '(a:diff_style_id <= 0 ? "" : s:HtmlClosing(a:diff_style_id, 0)).s:HtmlClosing(a:style_id, !empty(a:extra_attrs))',
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200401 \ 'endfun'
402 \ ]
403 else
404 let wrapperfunc_lines = [
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200405 \ 'function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, unusedarg, unusedarg2)',
406 \ ' return s:HtmlOpening(a:style_id, a:extra_attrs).a:text.s:HtmlClosing(a:style_id, !empty(a:extra_attrs))',
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200407 \ 'endfun'
408 \ ]
409 endif
410endif
411
412" create the function we built line by line above
413exec join(wrapperfunc_lines, "\n")
414
415let s:diff_mode = &diff
416
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000417" Return HTML valid characters enclosed in a span of class style_name with
418" unprintable characters expanded and double spaces replaced as necessary.
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200419"
420" TODO: eliminate unneeded logic like done for BuildStyleWrapper
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200421function! s:HtmlFormat(text, style_id, diff_style_id, extra_attrs, make_unselectable)
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000422 " Replace unprintable characters
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200423 let unformatted = strtrans(a:text)
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000424
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200425 let formatted = unformatted
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +0200426
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000427 " Replace the reserved html characters
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +0100428 let formatted = substitute(formatted, '&', '\&amp;', 'g')
429 let formatted = substitute(formatted, '<', '\&lt;', 'g')
430 let formatted = substitute(formatted, '>', '\&gt;', 'g')
431 let formatted = substitute(formatted, '"', '\&quot;', 'g')
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200432 " &apos; is not valid in HTML but it is in XHTML, so just use the numeric
433 " reference for it instead. Needed because it could appear in quotes
434 " especially if unselectable regions is turned on.
435 let formatted = substitute(formatted, '"', '\&#0039;', 'g')
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +0100436
437 " Replace a "form feed" character with HTML to do a page break
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200438 " TODO: need to prevent this in unselectable areas? Probably it should never
439 " BE in an unselectable area...
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +0100440 let formatted = substitute(formatted, "\x0c", '<hr class="PAGE-BREAK">', 'g')
441
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200442 " Replace double spaces, leading spaces, and trailing spaces if needed
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000443 if ' ' != s:HtmlSpace
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000444 let formatted = substitute(formatted, ' ', s:HtmlSpace . s:HtmlSpace, 'g')
Bram Moolenaar8424a622006-04-19 21:23:36 +0000445 let formatted = substitute(formatted, '^ ', s:HtmlSpace, 'g')
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200446 let formatted = substitute(formatted, ' \+$', s:HtmlSpace, 'g')
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000447 endif
448
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200449 " Enclose in the correct format
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200450 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 +0000451endfun
452
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200453" set up functions to call HtmlFormat in certain ways based on whether the
454" element is supposed to be unselectable or not
455if s:settings.prevent_copy =~# 'n'
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200456 if s:settings.number_lines
Bram Moolenaar31c31672013-06-26 13:28:14 +0200457 if s:settings.line_ids
458 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
459 if a:lnr > 0
460 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'.(exists('g:html_diff_win_num') ? 'W'.g:html_diff_win_num : "").'L'.a:lnr.s:settings.id_suffix.'" ', 1)
461 else
462 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
463 endif
464 endfun
465 else
466 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200467 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
Bram Moolenaar31c31672013-06-26 13:28:14 +0200468 endfun
469 endif
470 elseif s:settings.line_ids
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200471 " if lines are not being numbered the only reason this function gets called
472 " is to put the line IDs on each line; "text" will be emtpy but lnr will
473 " always be non-zero, however we don't want to use the <input> because that
474 " won't work as nice for empty text
475 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
Bram Moolenaar31c31672013-06-26 13:28:14 +0200476 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 +0200477 endfun
478 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200479else
Bram Moolenaar31c31672013-06-26 13:28:14 +0200480 if s:settings.line_ids
481 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
482 if a:lnr > 0
483 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'.(exists('g:html_diff_win_num') ? 'W'.g:html_diff_win_num : "").'L'.a:lnr.s:settings.id_suffix.'" ', 0)
484 else
485 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
486 endif
487 endfun
488 else
489 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200490 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
Bram Moolenaar31c31672013-06-26 13:28:14 +0200491 endfun
492 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200493endif
494if s:settings.prevent_copy =~# 'd'
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, "", 1)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200497 endfun
498else
499 function! s:HtmlFormat_d(text, style_id, diff_style_id)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200500 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200501 endfun
502endif
503if s:settings.prevent_copy =~# 'f'
504 " Note the <input> elements for fill spaces will have a single space for
505 " content, to allow active cursor CSS selection to work.
506 "
507 " Wrap the whole thing in a span for the 1px padding workaround for gaps.
508 function! s:FoldColumn_build(char, len, numfill, char2, class, click)
509 let l:input_open = "<input readonly='readonly'".s:unselInputType.
510 \ " onselect='this.blur(); return false;'".
511 \ " onmousedown='this.blur(); ".a:click." return false;'".
512 \ " onclick='return false;' size='".
513 \ string(a:len + (empty(a:char2) ? 0 : 1) + a:numfill) .
514 \ "' "
515 let l:common_attrs = "class='FoldColumn' value='"
516 let l:input_close = (s:settings.use_xhtml ? "' />" : "'>")
517 return "<span class='".a:class."'>".
518 \ l:input_open.l:common_attrs.repeat(a:char, a:len).
519 \ (!empty(a:char2) ? a:char2 : "").
520 \ l:input_close . "</span>"
521 endfun
522 function! s:FoldColumn_fill()
523 return s:FoldColumn_build('', s:foldcolumn, 0, '', 'FoldColumn', '')
524 endfun
525else
526 " For normal fold columns, simply space-pad to the desired width (note that
527 " the FoldColumn definition includes a whitespace:pre rule)
528 function! s:FoldColumn_build(char, len, numfill, char2, class, click)
529 return "<a href='#' class='".a:class."' onclick='".a:click."'>".
530 \ repeat(a:char, a:len).a:char2.repeat(' ', a:numfill).
531 \ "</a>"
532 endfun
533 function! s:FoldColumn_fill()
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200534 return s:HtmlFormat(repeat(' ', s:foldcolumn), s:FOLD_C_ID, 0, "", 0)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200535 endfun
536endif
537if s:settings.prevent_copy =~# 't'
538 " put an extra empty span at the end for dynamic folds, so the linebreak can
539 " be surrounded. Otherwise do it as normal.
540 "
541 " TODO: isn't there a better way to do this, than placing it here and using a
542 " substitute later?
543 if s:settings.dynamic_folds
544 function! s:HtmlFormat_t(text, style_id, diff_style_id)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200545 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) .
546 \ s:HtmlFormat("", a:style_id, 0, "", 0)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200547 endfun
548 else
549 function! s:HtmlFormat_t(text, style_id, diff_style_id)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200550 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200551 endfun
552 endif
553else
554 function! s:HtmlFormat_t(text, style_id, diff_style_id)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200555 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200556 endfun
557endif
558
Bram Moolenaar071d4272004-06-13 20:20:40 +0000559" Return CSS style describing given highlight id (can be empty)
560function! s:CSS1(id)
561 let a = ""
562 if synIDattr(a:id, "inverse")
563 " For inverse, we always must set both colors (and exchange them)
564 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
565 let a = a . "color: " . ( x != "" ? x : s:bgc ) . "; "
566 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
567 let a = a . "background-color: " . ( x != "" ? x : s:fgc ) . "; "
568 else
569 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm))
570 if x != "" | let a = a . "color: " . x . "; " | endif
571 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm))
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200572 if x != ""
573 let a = a . "background-color: " . x . "; "
574 " stupid hack because almost every browser seems to have at least one font
575 " which shows 1px gaps between lines which have background
576 let a = a . "padding-bottom: 1px; "
577 elseif (a:id == s:FOLDED_ID || a:id == s:LINENR_ID || a:id == s:FOLD_C_ID) && !empty(s:settings.prevent_copy)
578 " input elements default to a different color than the rest of the page
579 let a = a . "background-color: " . s:bgc . "; "
580 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000581 endif
582 if synIDattr(a:id, "bold") | let a = a . "font-weight: bold; " | endif
583 if synIDattr(a:id, "italic") | let a = a . "font-style: italic; " | endif
584 if synIDattr(a:id, "underline") | let a = a . "text-decoration: underline; " | endif
585 return a
586endfun
587
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200588if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +0100589 " compares two folds as stored in our list of folds
590 " A fold is "less" than another if it starts at an earlier line number,
591 " or ends at a later line number, ties broken by fold level
592 function! s:FoldCompare(f1, f2)
593 if a:f1.firstline != a:f2.firstline
594 " put it before if it starts earlier
595 return a:f1.firstline - a:f2.firstline
596 elseif a:f1.lastline != a:f2.lastline
597 " put it before if it ends later
598 return a:f2.lastline - a:f1.lastline
599 else
600 " if folds begin and end on the same lines, put lowest fold level first
601 return a:f1.level - a:f2.level
602 endif
603 endfunction
604
605endif
606
Bram Moolenaar071d4272004-06-13 20:20:40 +0000607
608" Set some options to make it work faster.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000609" Don't report changes for :substitute, there will be many of them.
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200610" Don't change other windows; turn off scroll bind temporarily
Bram Moolenaar071d4272004-06-13 20:20:40 +0000611let s:old_title = &title
612let s:old_icon = &icon
613let s:old_et = &l:et
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200614let s:old_bind = &l:scrollbind
Bram Moolenaar071d4272004-06-13 20:20:40 +0000615let s:old_report = &report
616let s:old_search = @/
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200617let s:old_more = &more
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618set notitle noicon
619setlocal et
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200620set nomore
Bram Moolenaar071d4272004-06-13 20:20:40 +0000621set report=1000000
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200622setlocal noscrollbind
Bram Moolenaar071d4272004-06-13 20:20:40 +0000623
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200624if exists(':ownsyntax') && exists('w:current_syntax')
625 let s:current_syntax = w:current_syntax
626elseif exists('b:current_syntax')
627 let s:current_syntax = b:current_syntax
628else
629 let s:current_syntax = 'none'
630endif
631
632if s:current_syntax == ''
633 let s:current_syntax = 'none'
634endif
635
Bram Moolenaar071d4272004-06-13 20:20:40 +0000636" Split window to create a buffer with the HTML file.
637let s:orgbufnr = winbufnr(0)
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200638let s:origwin_stl = &l:stl
Bram Moolenaar071d4272004-06-13 20:20:40 +0000639if expand("%") == ""
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200640 if exists('g:html_diff_win_num')
641 exec 'new Untitled_win'.g:html_diff_win_num.'.'.(s:settings.use_xhtml ? 'x' : '').'html'
642 else
643 exec 'new Untitled.'.(s:settings.use_xhtml ? 'x' : '').'html'
644 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000645else
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200646 exec 'new %.'.(s:settings.use_xhtml ? 'x' : '').'html'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647endif
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200648
649" Resize the new window to very small in order to make it draw faster
650let s:old_winheight = winheight(0)
651let s:old_winfixheight = &l:winfixheight
652if s:old_winheight > 2
653 resize 1 " leave enough room to view one line at a time
654 norm! G
655 norm! zt
656endif
657setlocal winfixheight
658
659let s:newwin_stl = &l:stl
660
661" on the new window, set the least time-consuming fold method
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200662let s:old_fen = &foldenable
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200663setlocal foldmethod=manual
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200664setlocal nofoldenable
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200665
Bram Moolenaar071d4272004-06-13 20:20:40 +0000666let s:newwin = winnr()
667let s:orgwin = bufwinnr(s:orgbufnr)
668
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200669setlocal modifiable
Bram Moolenaar071d4272004-06-13 20:20:40 +0000670%d
671let s:old_paste = &paste
672set paste
673let s:old_magic = &magic
674set magic
675
Bram Moolenaar166af9b2010-11-16 20:34:40 +0100676" set the fileencoding to match the charset we'll be using
677let &l:fileencoding=s:settings.vim_encoding
678
679" According to http://www.w3.org/TR/html4/charset.html#doc-char-set, the byte
680" order mark is highly recommend on the web when using multibyte encodings. But,
681" it is not a good idea to include it on UTF-8 files. Otherwise, let Vim
682" determine when it is actually inserted.
683if s:settings.vim_encoding == 'utf-8'
684 setlocal nobomb
685else
686 setlocal bomb
687endif
688
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200689let s:lines = []
690
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200691if s:settings.use_xhtml
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200692 if s:settings.encoding != ""
693 call add(s:lines, "<?xml version=\"1.0\" encoding=\"" . s:settings.encoding . "\"?>")
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000694 else
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200695 call add(s:lines, "<?xml version=\"1.0\"?>")
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000696 endif
Bram Moolenaar313b7232007-05-05 17:56:55 +0000697 let s:tag_close = ' />'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000698else
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000699 let s:tag_close = '>'
700endif
701
702let s:HtmlSpace = ' '
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000703let s:LeadingSpace = ' '
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000704let s:HtmlEndline = ''
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200705if s:settings.no_pre
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000706 let s:HtmlEndline = '<br' . s:tag_close
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200707 let s:LeadingSpace = s:settings.use_xhtml ? '&#160;' : '&nbsp;'
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000708 let s:HtmlSpace = '\' . s:LeadingSpace
Bram Moolenaar071d4272004-06-13 20:20:40 +0000709endif
710
711" HTML header, with the title and generator ;-). Left free space for the CSS,
712" to be filled at the end.
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200713call extend(s:lines, [
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200714 \ "<html>",
715 \ "<head>"])
716" include encoding as close to the top as possible, but only if not already
717" contained in XML information (to avoid haggling over content type)
718if s:settings.encoding != "" && !s:settings.use_xhtml
719 call add(s:lines, "<meta http-equiv=\"content-type\" content=\"text/html; charset=" . s:settings.encoding . '"' . s:tag_close)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000720endif
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200721call extend(s:lines, [
722 \ ("<title>".expand("%:p:~")."</title>"),
723 \ ("<meta name=\"Generator\" content=\"Vim/".v:version/100.".".v:version%100.'"'.s:tag_close),
724 \ ("<meta name=\"plugin-version\" content=\"".g:loaded_2html_plugin.'"'.s:tag_close)
725 \ ])
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200726call add(s:lines, '<meta name="syntax" content="'.s:current_syntax.'"'.s:tag_close)
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200727call add(s:lines, '<meta name="settings" content="'.
728 \ join(filter(keys(s:settings),'s:settings[v:val]'),',').
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200729 \ ',prevent_copy='.s:settings.prevent_copy.
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200730 \ '"'.s:tag_close)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200731call add(s:lines, '<meta name="colorscheme" content="'.
732 \ (exists('g:colors_name')
733 \ ? g:colors_name
734 \ : 'none'). '"'.s:tag_close)
Bram Moolenaar313b7232007-05-05 17:56:55 +0000735
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200736if s:settings.use_css
737 if s:settings.dynamic_folds
738 if s:settings.hover_unfold
Bram Moolenaar5c736222010-01-06 20:54:52 +0100739 " if we are doing hover_unfold, use css 2 with css 1 fallback for IE6
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200740 call extend(s:lines, [
741 \ "<style type=\"text/css\">",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200742 \ s:settings.use_xhtml ? "" : "<!--",
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200743 \ ".FoldColumn { text-decoration: none; white-space: pre; }",
744 \ "",
745 \ "body * { margin: 0; padding: 0; }", "",
746 \ ".open-fold > .Folded { display: none; }",
747 \ ".open-fold > .fulltext { display: inline; }",
748 \ ".closed-fold > .fulltext { display: none; }",
749 \ ".closed-fold > .Folded { display: inline; }",
750 \ "",
751 \ ".open-fold > .toggle-open { display: none; }",
752 \ ".open-fold > .toggle-closed { display: inline; }",
753 \ ".closed-fold > .toggle-open { display: inline; }",
754 \ ".closed-fold > .toggle-closed { display: none; }",
755 \ "", "",
756 \ '/* opening a fold while hovering won''t be supported by IE6 and other',
757 \ "similar browsers, but it should fail gracefully. */",
758 \ ".closed-fold:hover > .fulltext { display: inline; }",
759 \ ".closed-fold:hover > .toggle-filler { display: none; }",
760 \ ".closed-fold:hover > .Folded { display: none; }",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200761 \ s:settings.use_xhtml ? "" : '-->',
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200762 \ '</style>'])
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200763 " TODO: IE7 doesn't *actually* support XHTML, maybe we should remove this.
764 " But if it's served up as tag soup, maybe the following will work, so
765 " leave it in for now.
766 call extend(s:lines, [
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200767 \ "<!--[if lt IE 7]><style type=\"text/css\">",
768 \ ".open-fold .Folded { display: none; }",
769 \ ".open-fold .fulltext { display: inline; }",
770 \ ".open-fold .toggle-open { display: none; }",
771 \ ".closed-fold .toggle-closed { display: inline; }",
772 \ "",
773 \ ".closed-fold .fulltext { display: none; }",
774 \ ".closed-fold .Folded { display: inline; }",
775 \ ".closed-fold .toggle-open { display: inline; }",
776 \ ".closed-fold .toggle-closed { display: none; }",
777 \ "</style>",
778 \ "<![endif]-->",
779 \])
Bram Moolenaar5c736222010-01-06 20:54:52 +0100780 else
781 " if we aren't doing hover_unfold, use CSS 1 only
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200782 call extend(s:lines, [
783 \ "<style type=\"text/css\">",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200784 \ s:settings.use_xhtml ? "" :"<!--",
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200785 \ ".FoldColumn { text-decoration: none; white-space: pre; }",
786 \ ".open-fold .Folded { display: none; }",
787 \ ".open-fold .fulltext { display: inline; }",
788 \ ".open-fold .toggle-open { display: none; }",
789 \ ".closed-fold .toggle-closed { display: inline; }",
790 \ "",
791 \ ".closed-fold .fulltext { display: none; }",
792 \ ".closed-fold .Folded { display: inline; }",
793 \ ".closed-fold .toggle-open { display: inline; }",
794 \ ".closed-fold .toggle-closed { display: none; }",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200795 \ s:settings.use_xhtml ? "" : '-->',
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200796 \ '</style>'
797 \])
Bram Moolenaar5c736222010-01-06 20:54:52 +0100798 endif
799 else
800 " if we aren't doing any dynamic folding, no need for any special rules
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200801 call extend(s:lines, [
802 \ "<style type=\"text/css\">",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200803 \ s:settings.use_xhtml ? "" : "<!--",
804 \ s:settings.use_xhtml ? "" : '-->',
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200805 \ "</style>",
806 \])
Bram Moolenaar5c736222010-01-06 20:54:52 +0100807 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000808endif
Bram Moolenaar5c736222010-01-06 20:54:52 +0100809
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200810" insert script tag; javascript is always needed for the line number
811" normalization for URL hashes
812call extend(s:lines, [
813 \ "",
814 \ "<script type='text/javascript'>",
815 \ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"])
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200816
Bram Moolenaar5c736222010-01-06 20:54:52 +0100817" insert javascript to toggle folds open and closed
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200818if s:settings.dynamic_folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200819 call extend(s:lines, [
820 \ "",
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200821 \ "function toggleFold(objID)",
822 \ "{",
823 \ " var fold;",
824 \ " fold = document.getElementById(objID);",
825 \ " if(fold.className == 'closed-fold')",
826 \ " {",
827 \ " fold.className = 'open-fold';",
828 \ " }",
829 \ " else if (fold.className == 'open-fold')",
830 \ " {",
831 \ " fold.className = 'closed-fold';",
832 \ " }",
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200833 \ "}"
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200834 \ ])
Bram Moolenaar5c736222010-01-06 20:54:52 +0100835endif
836
Bram Moolenaar31c31672013-06-26 13:28:14 +0200837if s:settings.line_ids
838 " insert javascript to get IDs from line numbers, and to open a fold before
839 " jumping to any lines contained therein
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200840 call extend(s:lines, [
841 \ "",
Bram Moolenaar31c31672013-06-26 13:28:14 +0200842 \ "/* function to open any folds containing a jumped-to line before jumping to it */",
843 \ "function JumpToLine()",
844 \ "{",
845 \ " var lineNum;",
846 \ " lineNum = window.location.hash;",
847 \ " lineNum = lineNum.substr(1); /* strip off '#' */",
848 \ "",
849 \ " if (lineNum.indexOf('L') == -1) {",
850 \ " lineNum = 'L'+lineNum;",
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200851 \ " }",
Bram Moolenaar31c31672013-06-26 13:28:14 +0200852 \ " lineElem = document.getElementById(lineNum);"
853 \ ])
854 if s:settings.dynamic_folds
855 call extend(s:lines, [
856 \ "",
857 \ " /* navigate upwards in the DOM tree to open all folds containing the line */",
858 \ " var node = lineElem;",
859 \ " while (node && node.id != 'vimCodeElement".s:settings.id_suffix."')",
860 \ " {",
861 \ " if (node.className == 'closed-fold')",
862 \ " {",
863 \ " node.className = 'open-fold';",
864 \ " }",
865 \ " node = node.parentNode;",
866 \ " }",
867 \ ])
868 endif
869 call extend(s:lines, [
870 \ " /* Always jump to new location even if the line was hidden inside a fold, or",
871 \ " * we corrected the raw number to a line ID.",
872 \ " */",
873 \ " if (lineElem) {",
874 \ " lineElem.scrollIntoView(true);",
875 \ " }",
876 \ " return true;",
877 \ "}",
878 \ "if ('onhashchange' in window) {",
879 \ " window.onhashchange = JumpToLine;",
880 \ "}"
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200881 \ ])
882endif
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200883
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200884" Small text columns like the foldcolumn and line number column need a weird
885" hack to work around Webkit's and (in versions prior to 9) IE's lack of support
886" for the 'ch' unit without messing up Opera, which also doesn't support it but
887" works anyway.
888"
889" The problem is that without the 'ch' unit, it is not possible to specify a
890" size of an <input> in terms of character widths. Only Opera seems to do the
891" "sensible" thing and make the <input> sized to fit exactly as many characters
892" as specified by its "size" attribute, but the spec actually says "at least
893" wide enough to fit 'size' characters", so the other browsers are technically
894" correct as well.
895"
896" Anyway, this leads to two diffculties:
897" 1. The foldcolumn is made up of multiple elements side-by-side with
898" different sizes, each of which has their own extra padding added. Thus, a
899" column made up of one item of size 1 and another of size 2 would not
900" necessarily be equal in size to another line's foldcolumn with a single
901" item of size 3.
902" 2. The extra padding added to the <input> elements adds up to make the
903" foldcolumn and line number column too wide, especially in Webkit
904" browsers.
905"
906" So, the full workaround is:
907" 1. Define a default size in em, equal to the number of characters in the
908" input element, in case javascript is disabled and the browser does not
909" support the 'ch' unit. Unfortunately this makes Opera no longer work
910" properly without javascript. 1em per character is much too wide but it
911" looks better in webkit browsers than unaligned columns.
912" 2. Insert the following javascript to run at page load, which checks for the
913" width of a single character (in an extraneous page element inserted
914" before the page title, and set to hidden) and compares it to the width of
915" another extra <input> element with only one character. If the width
916" matches, the script does nothing more, but if not, it will figure out the
917" fraction of an em unit which would correspond with a ch unit if there
918" were one, and set the containing element (<pre> or <div>) to a class with
919" pre-defined rules which is closest to that fraction of an em. Rules are
920" defined from 0.05 em to 1em per ch.
921if !empty(s:settings.prevent_copy)
922 call extend(s:lines, [
923 \ '',
924 \ '/* simulate a "ch" unit by asking the browser how big a zero character is */',
925 \ 'function FixCharWidth() {',
926 \ ' /* get the hidden element which gives the width of a single character */',
927 \ ' var goodWidth = document.getElementById("oneCharWidth").clientWidth;',
928 \ ' /* get all input elements, we''ll filter on class later */',
929 \ ' var inputTags = document.getElementsByTagName("input");',
930 \ ' var ratio = 5;',
931 \ ' var inputWidth = document.getElementById("oneInputWidth").clientWidth;',
932 \ ' var emWidth = document.getElementById("oneEmWidth").clientWidth;',
933 \ ' if (inputWidth > goodWidth) {',
934 \ ' while (ratio < 100*goodWidth/emWidth && ratio < 100) {',
Bram Moolenaar31c31672013-06-26 13:28:14 +0200935 \ ' ratio += 5;',
936 \ ' }',
937 \ ' document.getElementById("vimCodeElement'.s:settings.id_suffix.'").className = "em"+ratio;',
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200938 \ ' }',
939 \ '}'
940 \ ])
941endif
942
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200943" insert script closing tag
944call extend(s:lines, [
945 \ '',
946 \ s:settings.use_xhtml ? '//]]>' : '-->',
947 \ "</script>"
948 \ ])
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200949
950call extend(s:lines, ["</head>"])
951if !empty(s:settings.prevent_copy)
952 call extend(s:lines,
Bram Moolenaar31c31672013-06-26 13:28:14 +0200953 \ ["<body onload='FixCharWidth();".(s:settings.line_ids ? " JumpToLine();" : "")."'>",
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200954 \ "<!-- hidden divs used by javascript to get the width of a char -->",
955 \ "<div id='oneCharWidth'>0</div>",
956 \ "<div id='oneInputWidth'><input size='1' value='0'".s:tag_close."</div>",
957 \ "<div id='oneEmWidth' style='width: 1em;'></div>"
958 \ ])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000959else
Bram Moolenaar31c31672013-06-26 13:28:14 +0200960 call extend(s:lines, ["<body".(s:settings.line_ids ? " onload='JumpToLine();'" : "").">"])
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200961endif
962if s:settings.no_pre
963 " if we're not using CSS we use a font tag which can't have a div inside
964 if s:settings.use_css
Bram Moolenaar31c31672013-06-26 13:28:14 +0200965 call extend(s:lines, ["<div id='vimCodeElement".s:settings.id_suffix."'>"])
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200966 endif
967else
Bram Moolenaar31c31672013-06-26 13:28:14 +0200968 call extend(s:lines, ["<pre id='vimCodeElement".s:settings.id_suffix."'>"])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000969endif
970
971exe s:orgwin . "wincmd w"
972
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200973" caches of style data
974" initialize to include line numbers if using them
975if s:settings.number_lines
976 let s:stylelist = { s:LINENR_ID : ".LineNr { " . s:CSS1( s:LINENR_ID ) . "}" }
977else
978 let s:stylelist = {}
979endif
980let s:diffstylelist = {
981 \ s:DIFF_A_ID : ".DiffAdd { " . s:CSS1( s:DIFF_A_ID ) . "}",
982 \ s:DIFF_C_ID : ".DiffChange { " . s:CSS1( s:DIFF_C_ID ) . "}",
983 \ s:DIFF_D_ID : ".DiffDelete { " . s:CSS1( s:DIFF_D_ID ) . "}",
984 \ s:DIFF_T_ID : ".DiffText { " . s:CSS1( s:DIFF_T_ID ) . "}"
985 \ }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000986
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200987" set up progress bar in the status line
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200988if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200989 " ProgressBar Indicator
990 let s:progressbar={}
991
992 " Progessbar specific functions
993 func! s:ProgressBar(title, max_value, winnr)
994 let pgb=copy(s:progressbar)
995 let pgb.title = a:title.' '
996 let pgb.max_value = a:max_value
997 let pgb.winnr = a:winnr
998 let pgb.cur_value = 0
999 let pgb.items = { 'title' : { 'color' : 'Statusline' },
1000 \'bar' : { 'color' : 'Statusline' , 'fillcolor' : 'DiffDelete' , 'bg' : 'Statusline' } ,
1001 \'counter' : { 'color' : 'Statusline' } }
1002 let pgb.last_value = 0
1003 let pgb.needs_redraw = 0
1004 " Note that you must use len(split) instead of len() if you want to use
1005 " unicode in title.
1006 "
1007 " Subtract 3 for spacing around the title.
1008 " Subtract 4 for the percentage display.
1009 " Subtract 2 for spacing before this.
1010 " Subtract 2 more for the '|' on either side of the progress bar
1011 let pgb.subtractedlen=len(split(pgb.title, '\zs'))+3+4+2+2
1012 let pgb.max_len = 0
1013 set laststatus=2
1014 return pgb
1015 endfun
1016
1017 " Function: progressbar.calculate_ticks() {{{1
1018 func! s:progressbar.calculate_ticks(pb_len)
1019 if a:pb_len<=0
1020 let pb_len = 100
1021 else
1022 let pb_len = a:pb_len
1023 endif
1024 let self.progress_ticks = map(range(pb_len+1), "v:val * self.max_value / pb_len")
1025 endfun
1026
1027 "Function: progressbar.paint()
1028 func! s:progressbar.paint()
1029 " Recalculate widths.
1030 let max_len = winwidth(self.winnr)
1031 let pb_len = 0
1032 " always true on first call because of initial value of self.max_len
1033 if max_len != self.max_len
1034 let self.max_len = max_len
1035
1036 " Progressbar length
1037 let pb_len = max_len - self.subtractedlen
1038
1039 call self.calculate_ticks(pb_len)
1040
1041 let self.needs_redraw = 1
1042 let cur_value = 0
1043 let self.pb_len = pb_len
1044 else
1045 " start searching at the last found index to make the search for the
1046 " appropriate tick value normally take 0 or 1 comparisons
1047 let cur_value = self.last_value
1048 let pb_len = self.pb_len
1049 endif
1050
1051 let cur_val_max = pb_len > 0 ? pb_len : 100
1052
1053 " find the current progress bar position based on precalculated thresholds
1054 while cur_value < cur_val_max && self.cur_value > self.progress_ticks[cur_value]
1055 let cur_value += 1
1056 endwhile
1057
1058 " update progress bar
1059 if self.last_value != cur_value || self.needs_redraw || self.cur_value == self.max_value
1060 let self.needs_redraw = 1
1061 let self.last_value = cur_value
1062
1063 let t_color = self.items.title.color
1064 let b_fcolor = self.items.bar.fillcolor
1065 let b_color = self.items.bar.color
1066 let c_color = self.items.counter.color
1067
1068 let stl = "%#".t_color."#%-( ".self.title." %)".
1069 \"%#".b_color."#".
1070 \(pb_len>0 ?
1071 \ ('|%#'.b_fcolor."#%-(".repeat(" ",cur_value)."%)".
1072 \ '%#'.b_color."#".repeat(" ",pb_len-cur_value)."|"):
1073 \ ('')).
1074 \"%=%#".c_color."#%( ".printf("%3.d ",100*self.cur_value/self.max_value)."%% %)"
1075 call setwinvar(self.winnr, '&stl', stl)
1076 endif
1077 endfun
1078
1079 func! s:progressbar.incr( ... )
1080 let self.cur_value += (a:0 ? a:1 : 1)
1081 " if we were making a general-purpose progress bar, we'd need to limit to a
1082 " lower limit as well, but since we always increment with a positive value
1083 " in this script, we only need limit the upper value
1084 let self.cur_value = (self.cur_value > self.max_value ? self.max_value : self.cur_value)
1085 call self.paint()
1086 endfun
1087 " }}}
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001088 if s:settings.dynamic_folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001089 " to process folds we make two passes through each line
1090 let s:pgb = s:ProgressBar("Processing folds:", line('$')*2, s:orgwin)
1091 endif
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001092endif
1093
Bram Moolenaar5c736222010-01-06 20:54:52 +01001094" First do some preprocessing for dynamic folding. Do this for the entire file
1095" so we don't accidentally start within a closed fold or something.
1096let s:allfolds = []
1097
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001098if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +01001099 let s:lnum = 1
1100 let s:end = line('$')
1101 " save the fold text and set it to the default so we can find fold levels
1102 let s:foldtext_save = &foldtext
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001103 setlocal foldtext&
Bram Moolenaar5c736222010-01-06 20:54:52 +01001104
1105 " we will set the foldcolumn in the html to the greater of the maximum fold
1106 " level and the current foldcolumn setting
1107 let s:foldcolumn = &foldcolumn
1108
1109 " get all info needed to describe currently closed folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001110 while s:lnum <= s:end
Bram Moolenaar5c736222010-01-06 20:54:52 +01001111 if foldclosed(s:lnum) == s:lnum
1112 " default fold text has '+-' and then a number of dashes equal to fold
1113 " level, so subtract 2 from index of first non-dash after the dashes
1114 " in order to get the fold level of the current fold
1115 let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2
Bram Moolenaar5c736222010-01-06 20:54:52 +01001116 " store fold info for later use
1117 let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"}
1118 call add(s:allfolds, s:newfold)
1119 " open the fold so we can find any contained folds
1120 execute s:lnum."foldopen"
1121 else
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001122 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001123 call s:pgb.incr()
1124 if s:pgb.needs_redraw
1125 redrawstatus
1126 let s:pgb.needs_redraw = 0
1127 endif
1128 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001129 let s:lnum = s:lnum + 1
1130 endif
1131 endwhile
1132
1133 " close all folds to get info for originally open folds
1134 silent! %foldclose!
1135 let s:lnum = 1
1136
1137 " the originally open folds will be all folds we encounter that aren't
1138 " already in the list of closed folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001139 while s:lnum <= s:end
Bram Moolenaar5c736222010-01-06 20:54:52 +01001140 if foldclosed(s:lnum) == s:lnum
1141 " default fold text has '+-' and then a number of dashes equal to fold
1142 " level, so subtract 2 from index of first non-dash after the dashes
1143 " in order to get the fold level of the current fold
1144 let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2
Bram Moolenaar5c736222010-01-06 20:54:52 +01001145 let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"}
1146 " only add the fold if we don't already have it
1147 if empty(s:allfolds) || index(s:allfolds, s:newfold) == -1
1148 let s:newfold.type = "open-fold"
1149 call add(s:allfolds, s:newfold)
1150 endif
1151 " open the fold so we can find any contained folds
1152 execute s:lnum."foldopen"
1153 else
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001154 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001155 call s:pgb.incr()
1156 if s:pgb.needs_redraw
1157 redrawstatus
1158 let s:pgb.needs_redraw = 0
1159 endif
1160 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001161 let s:lnum = s:lnum + 1
1162 endif
1163 endwhile
1164
1165 " sort the folds so that we only ever need to look at the first item in the
1166 " list of folds
1167 call sort(s:allfolds, "s:FoldCompare")
1168
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001169 let &l:foldtext = s:foldtext_save
Bram Moolenaar5c736222010-01-06 20:54:52 +01001170 unlet s:foldtext_save
1171
1172 " close all folds again so we can get the fold text as we go
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001173 silent! %foldclose!
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001174
Bram Moolenaar251e1912011-06-19 05:09:16 +02001175 " Go through and remove folds we don't need to (or cannot) process in the
1176 " current conversion range
1177 "
1178 " If a fold is removed which contains other folds, which are included, we need
1179 " to adjust the level of the included folds as used by the conversion logic
1180 " (avoiding special cases is good)
1181 "
1182 " Note any time we remove a fold, either all of the included folds are in it,
1183 " or none of them, because we only remove a fold if neither its start nor its
1184 " end are within the conversion range.
1185 let leveladjust = 0
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001186 for afold in s:allfolds
1187 let removed = 0
1188 if exists("g:html_start_line") && exists("g:html_end_line")
1189 if afold.firstline < g:html_start_line
Bram Moolenaar251e1912011-06-19 05:09:16 +02001190 if afold.lastline <= g:html_end_line && afold.lastline >= g:html_start_line
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001191 " if a fold starts before the range to convert but stops within the
1192 " range, we need to include it. Make it start on the first converted
1193 " line.
1194 let afold.firstline = g:html_start_line
1195 else
1196 " if the fold lies outside the range or the start and stop enclose
1197 " the entire range, don't bother parsing it
1198 call remove(s:allfolds, index(s:allfolds, afold))
1199 let removed = 1
Bram Moolenaar251e1912011-06-19 05:09:16 +02001200 if afold.lastline > g:html_end_line
1201 let leveladjust += 1
1202 endif
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001203 endif
1204 elseif afold.firstline > g:html_end_line
1205 " If the entire fold lies outside the range we need to remove it.
1206 call remove(s:allfolds, index(s:allfolds, afold))
1207 let removed = 1
1208 endif
1209 elseif exists("g:html_start_line")
1210 if afold.firstline < g:html_start_line
1211 " if there is no last line, but there is a first line, the end of the
1212 " fold will always lie within the region of interest, so keep it
1213 let afold.firstline = g:html_start_line
1214 endif
1215 elseif exists("g:html_end_line")
1216 " if there is no first line we default to the first line in the buffer so
1217 " the fold start will always be included if the fold itself is included.
1218 " If however the entire fold lies outside the range we need to remove it.
1219 if afold.firstline > g:html_end_line
1220 call remove(s:allfolds, index(s:allfolds, afold))
1221 let removed = 1
1222 endif
1223 endif
1224 if !removed
Bram Moolenaar251e1912011-06-19 05:09:16 +02001225 let afold.level -= leveladjust
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001226 if afold.level+1 > s:foldcolumn
1227 let s:foldcolumn = afold.level+1
1228 endif
1229 endif
1230 endfor
Bram Moolenaar251e1912011-06-19 05:09:16 +02001231
1232 " if we've removed folds containing the conversion range from processing,
1233 " getting foldtext as we go won't know to open the removed folds, so the
1234 " foldtext would be wrong; open them now.
1235 "
1236 " Note that only when a start and an end line is specified will a fold
1237 " containing the current range ever be removed.
1238 while leveladjust > 0
1239 exe g:html_start_line."foldopen"
1240 let leveladjust -= 1
1241 endwhile
Bram Moolenaar5c736222010-01-06 20:54:52 +01001242endif
1243
1244" Now loop over all lines in the original text to convert to html.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001245" Use html_start_line and html_end_line if they are set.
Bram Moolenaarb02cbe32010-07-11 22:38:52 +02001246if exists("g:html_start_line")
Bram Moolenaar071d4272004-06-13 20:20:40 +00001247 let s:lnum = html_start_line
1248 if s:lnum < 1 || s:lnum > line("$")
1249 let s:lnum = 1
1250 endif
1251else
1252 let s:lnum = 1
1253endif
Bram Moolenaarb02cbe32010-07-11 22:38:52 +02001254if exists("g:html_end_line")
Bram Moolenaar071d4272004-06-13 20:20:40 +00001255 let s:end = html_end_line
1256 if s:end < s:lnum || s:end > line("$")
1257 let s:end = line("$")
1258 endif
1259else
1260 let s:end = line("$")
1261endif
1262
Bram Moolenaar5c736222010-01-06 20:54:52 +01001263" stack to keep track of all the folds containing the current line
1264let s:foldstack = []
1265
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001266if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001267 let s:pgb = s:ProgressBar("Processing lines:", s:end - s:lnum + 1, s:orgwin)
1268endif
1269
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001270if s:settings.number_lines
Bram Moolenaar5c736222010-01-06 20:54:52 +01001271 let s:margin = strlen(s:end) + 1
1272else
1273 let s:margin = 0
1274endif
1275
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001276if has('folding') && !s:settings.ignore_folding
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001277 let s:foldfillchar = &fillchars[matchend(&fillchars, 'fold:')]
1278 if s:foldfillchar == ''
1279 let s:foldfillchar = '-'
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001280 endif
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001281endif
1282let s:difffillchar = &fillchars[matchend(&fillchars, 'diff:')]
1283if s:difffillchar == ''
1284 let s:difffillchar = '-'
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001285endif
1286
Bram Moolenaar5c736222010-01-06 20:54:52 +01001287let s:foldId = 0
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001288
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001289if !s:settings.expand_tabs
1290 " If keeping tabs, add them to printable characters so we keep them when
1291 " formatting text (strtrans() doesn't replace printable chars)
1292 let s:old_isprint = &isprint
1293 setlocal isprint+=9
1294endif
1295
Bram Moolenaar071d4272004-06-13 20:20:40 +00001296while s:lnum <= s:end
1297
Bram Moolenaar47136d72004-10-12 20:02:24 +00001298 " If there are filler lines for diff mode, show these above the line.
1299 let s:filler = diff_filler(s:lnum)
1300 if s:filler > 0
1301 let s:n = s:filler
1302 while s:n > 0
Bram Moolenaar5c736222010-01-06 20:54:52 +01001303 let s:new = repeat(s:difffillchar, 3)
Bram Moolenaar47136d72004-10-12 20:02:24 +00001304
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001305 if s:n > 2 && s:n < s:filler && !s:settings.whole_filler
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001306 let s:new = s:new . " " . s:filler . " inserted lines "
1307 let s:n = 2
1308 endif
1309
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001310 if !s:settings.no_pre
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001311 " HTML line wrapping is off--go ahead and fill to the margin
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001312 " TODO: what about when CSS wrapping is turned on?
Bram Moolenaar5c736222010-01-06 20:54:52 +01001313 let s:new = s:new . repeat(s:difffillchar, &columns - strlen(s:new) - s:margin)
1314 else
1315 let s:new = s:new . repeat(s:difffillchar, 3)
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001316 endif
1317
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001318 let s:new = s:HtmlFormat_d(s:new, s:DIFF_D_ID, 0)
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001319 if s:settings.number_lines
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001320 " Indent if line numbering is on. Indent gets style of line number
1321 " column.
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001322 let s:new = s:HtmlFormat_n(repeat(' ', s:margin), s:LINENR_ID, 0, 0) . s:new
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001323 endif
1324 if s:settings.dynamic_folds && !s:settings.no_foldcolumn && s:foldcolumn > 0
1325 " Indent for foldcolumn if there is one. Assume it's empty, there should
1326 " not be a fold for deleted lines in diff mode.
1327 let s:new = s:FoldColumn_fill() . s:new
Bram Moolenaar5c736222010-01-06 20:54:52 +01001328 endif
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001329 call add(s:lines, s:new.s:HtmlEndline)
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001330
Bram Moolenaar47136d72004-10-12 20:02:24 +00001331 let s:n = s:n - 1
1332 endwhile
1333 unlet s:n
1334 endif
1335 unlet s:filler
1336
1337 " Start the line with the line number.
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001338 if s:settings.number_lines
Bram Moolenaar5c736222010-01-06 20:54:52 +01001339 let s:numcol = repeat(' ', s:margin - 1 - strlen(s:lnum)) . s:lnum . ' '
Bram Moolenaar47136d72004-10-12 20:02:24 +00001340 endif
1341
Bram Moolenaar5c736222010-01-06 20:54:52 +01001342 let s:new = ""
1343
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001344 if has('folding') && !s:settings.ignore_folding && foldclosed(s:lnum) > -1 && !s:settings.dynamic_folds
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001345 "
Bram Moolenaar5c736222010-01-06 20:54:52 +01001346 " This is the beginning of a folded block (with no dynamic folding)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001347 let s:new = foldtextresult(s:lnum)
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001348 if !s:settings.no_pre
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001349 " HTML line wrapping is off--go ahead and fill to the margin
1350 let s:new = s:new . repeat(s:foldfillchar, &columns - strlen(s:new))
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001351 endif
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001352
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001353 " put numcol in a separate group for sake of unselectable text
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001354 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 +00001355
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001356 " Skip to the end of the fold
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001357 let s:new_lnum = foldclosedend(s:lnum)
1358
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001359 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001360 call s:pgb.incr(s:new_lnum - s:lnum)
1361 endif
1362
1363 let s:lnum = s:new_lnum
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001364
1365 else
1366 "
Bram Moolenaar5c736222010-01-06 20:54:52 +01001367 " A line that is not folded, or doing dynamic folding.
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001368 "
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001369 let s:line = getline(s:lnum)
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001370 let s:len = strlen(s:line)
1371
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001372 if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +01001373 " First insert a closing for any open folds that end on this line
1374 while !empty(s:foldstack) && get(s:foldstack,0).lastline == s:lnum-1
1375 let s:new = s:new."</span></span>"
1376 call remove(s:foldstack, 0)
1377 endwhile
1378
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001379 " Now insert an opening for any new folds that start on this line
Bram Moolenaar5c736222010-01-06 20:54:52 +01001380 let s:firstfold = 1
1381 while !empty(s:allfolds) && get(s:allfolds,0).firstline == s:lnum
1382 let s:foldId = s:foldId + 1
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001383 let s:new .= "<span id='"
1384 let s:new .= (exists('g:html_diff_win_num') ? "win".g:html_diff_win_num : "")
Bram Moolenaar31c31672013-06-26 13:28:14 +02001385 let s:new .= "fold".s:foldId.s:settings.id_suffix."' class='".s:allfolds[0].type."'>"
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001386
Bram Moolenaar5c736222010-01-06 20:54:52 +01001387
1388 " Unless disabled, add a fold column for the opening line of a fold.
1389 "
1390 " Note that dynamic folds require using css so we just use css to take
1391 " care of the leading spaces rather than using &nbsp; in the case of
1392 " html_no_pre to make it easier
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001393 if !s:settings.no_foldcolumn
Bram Moolenaar5c736222010-01-06 20:54:52 +01001394 " add fold column that can open the new fold
1395 if s:allfolds[0].level > 1 && s:firstfold
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001396 let s:new = s:new . s:FoldColumn_build('|', s:allfolds[0].level - 1, 0, "",
Bram Moolenaar31c31672013-06-26 13:28:14 +02001397 \ 'toggle-open FoldColumn','javascript:toggleFold("fold'.s:foldstack[0].id.s:settings.id_suffix.'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001398 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001399 " add the filler spaces separately from the '+' char so that it can be
1400 " shown/hidden separately during a hover unfold
1401 let s:new = s:new . s:FoldColumn_build("+", 1, 0, "",
Bram Moolenaar31c31672013-06-26 13:28:14 +02001402 \ 'toggle-open FoldColumn', 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001403 " If this is not the last fold we're opening on this line, we need
1404 " to keep the filler spaces hidden if the fold is opened by mouse
1405 " hover. If it is the last fold to open in the line, we shouldn't hide
1406 " them, so don't apply the toggle-filler class.
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001407 let s:new = s:new . s:FoldColumn_build(" ", 1, s:foldcolumn - s:allfolds[0].level - 1, "",
1408 \ 'toggle-open FoldColumn'. (get(s:allfolds, 1, {'firstline': 0}).firstline == s:lnum ?" toggle-filler" :""),
Bram Moolenaar31c31672013-06-26 13:28:14 +02001409 \ 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001410
1411 " add fold column that can close the new fold
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001412 " only add extra blank space if we aren't opening another fold on the
1413 " same line
Bram Moolenaar5c736222010-01-06 20:54:52 +01001414 if get(s:allfolds, 1, {'firstline': 0}).firstline != s:lnum
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001415 let s:extra_space = s:foldcolumn - s:allfolds[0].level
1416 else
1417 let s:extra_space = 0
Bram Moolenaar5c736222010-01-06 20:54:52 +01001418 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001419 if s:firstfold
1420 " the first fold in a line has '|' characters from folds opened in
1421 " previous lines, before the '-' for this fold
1422 let s:new .= s:FoldColumn_build('|', s:allfolds[0].level - 1, s:extra_space, '-',
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 else
1425 " any subsequent folds in the line only add a single '-'
1426 let s:new = s:new . s:FoldColumn_build("-", 1, s:extra_space, "",
Bram Moolenaar31c31672013-06-26 13:28:14 +02001427 \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001428 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001429 let s:firstfold = 0
1430 endif
1431
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001432 " Add fold text, moving the span ending to the next line so collapsing
1433 " of folds works correctly.
1434 " Put numcol in a separate group for sake of unselectable text.
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001435 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 +01001436 let s:new = s:new . "<span class='fulltext'>"
1437
1438 " open the fold now that we have the fold text to allow retrieval of
1439 " fold text for subsequent folds
1440 execute s:lnum."foldopen"
1441 call insert(s:foldstack, remove(s:allfolds,0))
1442 let s:foldstack[0].id = s:foldId
1443 endwhile
1444
1445 " Unless disabled, add a fold column for other lines.
1446 "
1447 " Note that dynamic folds require using css so we just use css to take
1448 " care of the leading spaces rather than using &nbsp; in the case of
1449 " html_no_pre to make it easier
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001450 if !s:settings.no_foldcolumn
Bram Moolenaar5c736222010-01-06 20:54:52 +01001451 if empty(s:foldstack)
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001452 " add the empty foldcolumn for unfolded lines if there is a fold
1453 " column at all
1454 if s:foldcolumn > 0
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001455 let s:new = s:new . s:FoldColumn_fill()
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001456 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001457 else
1458 " add the fold column for folds not on the opening line
1459 if get(s:foldstack, 0).firstline < s:lnum
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001460 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 +02001461 \ 'FoldColumn', 'javascript:toggleFold("fold'.s:foldstack[0].id.s:settings.id_suffix.'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001462 endif
1463 endif
1464 endif
1465 endif
1466
1467 " Now continue with the unfolded line text
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001468 if s:settings.number_lines
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001469 let s:new = s:new . s:HtmlFormat_n(s:numcol, s:LINENR_ID, 0, s:lnum)
Bram Moolenaar31c31672013-06-26 13:28:14 +02001470 elseif s:settings.line_ids
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001471 let s:new = s:new . s:HtmlFormat_n("", s:LINENR_ID, 0, s:lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001472 endif
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001473
Bram Moolenaar47136d72004-10-12 20:02:24 +00001474 " Get the diff attribute, if any.
1475 let s:diffattr = diff_hlID(s:lnum, 1)
1476
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001477 " initialize conceal info to act like not concealed, just in case
1478 let s:concealinfo = [0, '']
1479
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001480 " Loop over each character in the line
1481 let s:col = 1
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001482
1483 " most of the time we won't use the diff_id, initialize to zero
1484 let s:diff_id = 0
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001485
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001486 while s:col <= s:len || (s:col == 1 && s:diffattr)
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001487 let s:startcol = s:col " The start column for processing text
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001488 if !s:settings.ignore_conceal && has('conceal')
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001489 let s:concealinfo = synconcealed(s:lnum, s:col)
1490 endif
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001491 if !s:settings.ignore_conceal && s:concealinfo[0]
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001492 let s:col = s:col + 1
1493 " Speed loop (it's small - that's the trick)
1494 " Go along till we find a change in the match sequence number (ending
1495 " the specific concealed region) or until there are no more concealed
1496 " characters.
1497 while s:col <= s:len && s:concealinfo == synconcealed(s:lnum, s:col) | let s:col = s:col + 1 | endwhile
1498 elseif s:diffattr
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001499 let s:diff_id = diff_hlID(s:lnum, s:col)
1500 let s:id = synID(s:lnum, s:col, 1)
Bram Moolenaar47136d72004-10-12 20:02:24 +00001501 let s:col = s:col + 1
1502 " Speed loop (it's small - that's the trick)
1503 " Go along till we find a change in hlID
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001504 while s:col <= s:len && s:id == synID(s:lnum, s:col, 1)
1505 \ && s:diff_id == diff_hlID(s:lnum, s:col) |
1506 \ let s:col = s:col + 1 |
1507 \ endwhile
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001508 if s:len < &columns && !s:settings.no_pre
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001509 " Add spaces at the end of the raw text line to extend the changed
1510 " line to the full width.
Bram Moolenaar5c736222010-01-06 20:54:52 +01001511 let s:line = s:line . repeat(' ', &columns - virtcol([s:lnum, s:len]) - s:margin)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001512 let s:len = &columns
1513 endif
Bram Moolenaar47136d72004-10-12 20:02:24 +00001514 else
1515 let s:id = synID(s:lnum, s:col, 1)
1516 let s:col = s:col + 1
1517 " Speed loop (it's small - that's the trick)
1518 " Go along till we find a change in synID
1519 while s:col <= s:len && s:id == synID(s:lnum, s:col, 1) | let s:col = s:col + 1 | endwhile
1520 endif
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001521
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001522 if s:settings.ignore_conceal || !s:concealinfo[0]
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001523 " Expand tabs if needed
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001524 let s:expandedtab = strpart(s:line, s:startcol - 1, s:col - s:startcol)
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001525 if s:settings.expand_tabs
1526 let s:offset = 0
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001527 let s:idx = stridx(s:expandedtab, "\t")
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001528 while s:idx >= 0
1529 if has("multi_byte_encoding")
1530 if s:startcol + s:idx == 1
1531 let s:i = &ts
1532 else
1533 if s:idx == 0
1534 let s:prevc = matchstr(s:line, '.\%' . (s:startcol + s:idx + s:offset) . 'c')
1535 else
1536 let s:prevc = matchstr(s:expandedtab, '.\%' . (s:idx + 1) . 'c')
1537 endif
1538 let s:vcol = virtcol([s:lnum, s:startcol + s:idx + s:offset - len(s:prevc)])
1539 let s:i = &ts - (s:vcol % &ts)
1540 endif
1541 let s:offset -= s:i - 1
1542 else
1543 let s:i = &ts - ((s:idx + s:startcol - 1) % &ts)
1544 endif
1545 let s:expandedtab = substitute(s:expandedtab, '\t', repeat(' ', s:i), '')
1546 let s:idx = stridx(s:expandedtab, "\t")
1547 endwhile
1548 end
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001549
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001550 " get the highlight group name to use
1551 let s:id = synIDtrans(s:id)
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001552 else
1553 " use Conceal highlighting for concealed text
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001554 let s:id = s:CONCEAL_ID
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001555 let s:expandedtab = s:concealinfo[1]
1556 endif
1557
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001558 " Output the text with the same synID, with class set to the highlight ID
1559 " name, unless it has been concealed completely.
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001560 if strlen(s:expandedtab) > 0
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001561 let s:new = s:new . s:HtmlFormat(s:expandedtab, s:id, s:diff_id, "", 0)
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001562 endif
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001563 endwhile
1564 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001565
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001566 call extend(s:lines, split(s:new.s:HtmlEndline, '\n', 1))
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001567 if !s:settings.no_progress && s:pgb.needs_redraw
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001568 redrawstatus
1569 let s:pgb.needs_redraw = 0
1570 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001571 let s:lnum = s:lnum + 1
Bram Moolenaar313b7232007-05-05 17:56:55 +00001572
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001573 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001574 call s:pgb.incr()
1575 endif
1576endwhile
1577
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001578if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +01001579 " finish off any open folds
1580 while !empty(s:foldstack)
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001581 let s:lines[-1].="</span></span>"
Bram Moolenaar5c736222010-01-06 20:54:52 +01001582 call remove(s:foldstack, 0)
1583 endwhile
1584
1585 " add fold column to the style list if not already there
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001586 let s:id = s:FOLD_C_ID
1587 if !has_key(s:stylelist, s:id)
1588 let s:stylelist[s:id] = '.FoldColumn { ' . s:CSS1(s:id) . '}'
Bram Moolenaar5c736222010-01-06 20:54:52 +01001589 endif
1590endif
1591
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001592if s:settings.no_pre
1593 if !s:settings.use_css
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001594 " Close off the font tag that encapsulates the whole <body>
Bram Moolenaarbebca9d2010-08-07 15:47:30 +02001595 call extend(s:lines, ["</font>", "</body>", "</html>"])
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001596 else
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001597 call extend(s:lines, ["</div>", "</body>", "</html>"])
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001598 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001599else
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001600 call extend(s:lines, ["</pre>", "</body>", "</html>"])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001601endif
1602
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001603exe s:newwin . "wincmd w"
1604call setline(1, s:lines)
1605unlet s:lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00001606
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001607" Mangle modelines so Vim doesn't try to use HTML text as a modeline if editing
1608" this file in the future; need to do this after generating all the text in case
1609" the modeline text has different highlight groups which all turn out to be
1610" stripped from the final output.
Bram Moolenaardd007ed2013-07-09 15:44:17 +02001611%s!\v(%(^|\s+)%([Vv]i%(m%([<=>]?\d+)?)?|ex)):!\1\&#0058;!ge
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001612
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001613" The generated HTML is admittedly ugly and takes a LONG time to fold.
1614" Make sure the user doesn't do syntax folding when loading a generated file,
1615" using a modeline.
1616call append(line('$'), "<!-- vim: set foldmethod=manual : -->")
1617
Bram Moolenaar071d4272004-06-13 20:20:40 +00001618" Now, when we finally know which, we define the colors and styles
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001619if s:settings.use_css
Bram Moolenaar071d4272004-06-13 20:20:40 +00001620 1;/<style type="text/+1
1621endif
1622
Bram Moolenaar071d4272004-06-13 20:20:40 +00001623" Normal/global attributes
1624" For Netscape 4, set <body> attributes too, though, strictly speaking, it's
1625" incorrect.
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001626if s:settings.use_css
1627 if s:settings.no_pre
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001628 call append('.', "body { color: " . s:fgc . "; background-color: " . s:bgc . "; font-family: ". s:htmlfont ."; }")
1629 +
Bram Moolenaar071d4272004-06-13 20:20:40 +00001630 else
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001631 call append('.', "pre { " . s:whitespace . "font-family: ". s:htmlfont ."; color: " . s:fgc . "; background-color: " . s:bgc . "; }")
1632 +
Bram Moolenaar071d4272004-06-13 20:20:40 +00001633 yank
1634 put
1635 execute "normal! ^cwbody\e"
Bram Moolenaar8e5af3e2011-04-28 19:02:44 +02001636 " body should not have the wrap formatting, only the pre section
1637 if s:whitespace != ''
1638 exec 's#'.s:whitespace
1639 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001640 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001641 " fix browser inconsistencies (sometimes within the same browser) of different
1642 " default font size for different elements
1643 call append('.', '* { font-size: 1em; }')
1644 +
1645 " if we use any input elements for unselectable content, make sure they look
1646 " like normal text
1647 if !empty(s:settings.prevent_copy)
1648 call append('.', 'input { border: none; margin: 0; padding: 0; font-family: '.s:htmlfont.'; }')
1649 +
1650 " ch units for browsers which support them, em units for a somewhat
1651 " reasonable fallback. Also make sure the special elements for size
1652 " calculations aren't seen.
1653 call append('.', [
1654 \ "input[size='1'] { width: 1em; width: 1ch; }",
1655 \ "input[size='2'] { width: 2em; width: 2ch; }",
1656 \ "input[size='3'] { width: 3em; width: 3ch; }",
1657 \ "input[size='4'] { width: 4em; width: 4ch; }",
1658 \ "input[size='5'] { width: 5em; width: 5ch; }",
1659 \ "input[size='6'] { width: 6em; width: 6ch; }",
1660 \ "input[size='7'] { width: 7em; width: 7ch; }",
1661 \ "input[size='8'] { width: 8em; width: 8ch; }",
1662 \ "input[size='9'] { width: 9em; width: 9ch; }",
1663 \ "input[size='10'] { width: 10em; width: 10ch; }",
1664 \ "input[size='11'] { width: 11em; width: 11ch; }",
1665 \ "input[size='12'] { width: 12em; width: 12ch; }",
1666 \ "input[size='13'] { width: 13em; width: 13ch; }",
1667 \ "input[size='14'] { width: 14em; width: 14ch; }",
1668 \ "input[size='15'] { width: 15em; width: 15ch; }",
1669 \ "input[size='16'] { width: 16em; width: 16ch; }",
1670 \ "input[size='17'] { width: 17em; width: 17ch; }",
1671 \ "input[size='18'] { width: 18em; width: 18ch; }",
1672 \ "input[size='19'] { width: 19em; width: 19ch; }",
1673 \ "input[size='20'] { width: 20em; width: 20ch; }",
1674 \ "#oneCharWidth, #oneEmWidth, #oneInputWidth { padding: 0; margin: 0; position: absolute; left: -999999px; visibility: hidden; }"
1675 \ ])
1676 +21
1677 for w in range(5, 100, 5)
1678 let base = 0.01 * w
1679 call append('.', join(map(range(1,20), "'.em'.w.' input[size='''.v:val.'''] { width: '.string(v:val*base).'em; }'")))
1680 +
1681 endfor
1682 if s:settings.prevent_copy =~# 'f'
1683 " Make the cursor show active fold columns as active areas, and empty fold
1684 " columns as not interactive.
1685 call append('.', ['input.FoldColumn { cursor: pointer; }',
1686 \ 'input.FoldColumn[value=""] { cursor: default; }'
1687 \ ])
1688 +2
1689 endif
1690 " make line number column show as non-interactive if not selectable
1691 if s:settings.prevent_copy =~# 'n'
1692 call append('.', 'input.LineNr { cursor: default; }')
1693 +
1694 endif
1695 " make fold text and line number column within fold text show as
1696 " non-interactive if not selectable
1697 if (s:settings.prevent_copy =~# 'n' || s:settings.prevent_copy =~# 't') && !s:settings.ignore_folding
1698 call append('.', 'input.Folded { cursor: default; }')
1699 +
1700 endif
1701 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001702else
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001703 execute '%s:<body\([^>]*\):<body bgcolor="' . s:bgc . '" text="' . s:fgc . '"\1>\r<font face="'. s:htmlfont .'"'
Bram Moolenaar071d4272004-06-13 20:20:40 +00001704endif
1705
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001706" Gather attributes for all other classes. Do diff first so that normal
1707" highlight groups are inserted before it.
1708if s:settings.use_css
1709 if s:diff_mode
1710 call append('.', filter(map(keys(s:diffstylelist), "s:diffstylelist[v:val]"), 'v:val != ""'))
1711 endif
1712 if !empty(s:stylelist)
1713 call append('.', filter(map(keys(s:stylelist), "s:stylelist[v:val]"), 'v:val != ""'))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001714 endif
1715endif
1716
Bram Moolenaar071d4272004-06-13 20:20:40 +00001717" Add hyperlinks
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001718" TODO: add option to not do this? Maybe just make the color the same as the
1719" text highlight group normally is?
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001720%s+\(https\=://\S\{-}\)\(\([.,;:}]\=\(\s\|$\)\)\|[\\"'<>]\|&gt;\|&lt;\|&quot;\)+<a href="\1">\1</a>\2+ge
Bram Moolenaar071d4272004-06-13 20:20:40 +00001721
1722" The DTD
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001723if s:settings.use_xhtml
1724 exe "normal! gg$a\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
1725elseif s:settings.use_css && !s:settings.no_pre
1726 exe "normal! gg0i<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
Bram Moolenaar313b7232007-05-05 17:56:55 +00001727else
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001728 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 +00001729endif
1730
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001731if s:settings.use_xhtml
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001732 exe "normal! gg/<html/e\na xmlns=\"http://www.w3.org/1999/xhtml\"\e"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001733endif
1734
1735" Cleanup
1736%s:\s\+$::e
1737
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001738" Restore old settings (new window first)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001739"
1740" Don't bother restoring foldmethod in case it was syntax because the markup is
1741" so weirdly formatted it can take a LONG time.
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001742let &l:foldenable = s:old_fen
Bram Moolenaar071d4272004-06-13 20:20:40 +00001743let &report = s:old_report
1744let &title = s:old_title
1745let &icon = s:old_icon
1746let &paste = s:old_paste
1747let &magic = s:old_magic
1748let @/ = s:old_search
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001749let &more = s:old_more
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001750
1751" switch to original window to restore those settings
Bram Moolenaar071d4272004-06-13 20:20:40 +00001752exe s:orgwin . "wincmd w"
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001753
1754if !s:settings.expand_tabs
1755 let &l:isprint = s:old_isprint
1756endif
Bram Moolenaar166af9b2010-11-16 20:34:40 +01001757let &l:stl = s:origwin_stl
Bram Moolenaar071d4272004-06-13 20:20:40 +00001758let &l:et = s:old_et
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001759let &l:scrollbind = s:old_bind
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001760
1761" and back to the new window again to end there
Bram Moolenaar071d4272004-06-13 20:20:40 +00001762exe s:newwin . "wincmd w"
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001763
Bram Moolenaar166af9b2010-11-16 20:34:40 +01001764let &l:stl = s:newwin_stl
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001765exec 'resize' s:old_winheight
1766let &l:winfixheight = s:old_winfixheight
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001768let &ls=s:ls
1769
Bram Moolenaar071d4272004-06-13 20:20:40 +00001770" Save a little bit of memory (worth doing?)
Bram Moolenaar8e5af3e2011-04-28 19:02:44 +02001771unlet s:htmlfont s:whitespace
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001772unlet 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 +02001773unlet s:old_magic s:old_more s:old_fen s:old_winheight
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001774unlet! s:old_isprint
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001775unlet s:whatterm s:stylelist s:diffstylelist s:lnum s:end s:margin s:fgc s:bgc s:old_winfixheight
1776unlet! s:col s:id s:attr s:len s:line s:new s:expandedtab s:concealinfo s:diff_mode
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001777unlet! s:orgwin s:newwin s:orgbufnr s:idx s:i s:offset s:ls s:origwin_stl
1778unlet! s:newwin_stl s:current_syntax
Bram Moolenaar05159a02005-02-26 23:04:13 +00001779if !v:profiling
1780 delfunc s:HtmlColor
1781 delfunc s:HtmlFormat
1782 delfunc s:CSS1
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001783 delfunc s:BuildStyleWrapper
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001784 if !s:settings.use_css
Bram Moolenaar05159a02005-02-26 23:04:13 +00001785 delfunc s:HtmlOpening
1786 delfunc s:HtmlClosing
1787 endif
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001788 if s:settings.dynamic_folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001789 delfunc s:FoldCompare
1790 endif
1791
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001792 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001793 delfunc s:ProgressBar
1794 delfunc s:progressbar.paint
1795 delfunc s:progressbar.incr
1796 unlet s:pgb s:progressbar
1797 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001798endif
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001799
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001800unlet! s:new_lnum s:diffattr s:difffillchar s:foldfillchar s:HtmlSpace
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001801unlet! s:LeadingSpace s:HtmlEndline s:firstfold s:numcol s:foldcolumn
1802unlet s:foldstack s:allfolds s:foldId s:settings
Bram Moolenaar5c736222010-01-06 20:54:52 +01001803
1804let &cpo = s:cpo_sav
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001805unlet! s:cpo_sav
Bram Moolenaar5c736222010-01-06 20:54:52 +01001806
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001807" Make sure any patches will probably use consistent indent
Bram Moolenaar7c86f4c2010-07-18 14:07:22 +02001808" vim: ts=8 sw=2 sts=2 noet