blob: 4a2d1d3959b54f13f5fc9ab2855887d69a44f567 [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 Moolenaarf0d58ef2018-11-16 16:13:44 +01003" Last Change: 2018 Nov 11
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 Moolenaarf0d58ef2018-11-16 16:13:44 +0100636" If the user is sourcing this script directly then the plugin version isn't
637" known because the main plugin script didn't load. In the usual case where the
638" user still has the full Vim runtime installed, or has this full plugin
639" installed in a package or something, then we can extract the version from the
640" main plugin file at it's usual spot relative to this file. Otherwise the user
641" is assembling their runtime piecemeal and we have no idea what versions of
642" other files may be present so don't even try to make a guess or assume the
643" presence of other specific files with specific meaning.
644"
645" We don't want to actually source the main plugin file here because the user
646" may have a good reason not to (e.g. they define their own TOhtml command or
647" something).
648"
649" If this seems way too complicated and convoluted, it is. Probably I should
650" have put the version information in the autoload file from the start. But the
651" version has been in the global variable for so long that changing it could
652" break a lot of user scripts.
653if exists("g:loaded_2html_plugin")
654 let s:pluginversion = g:loaded_2html_plugin
655else
656 if !exists("g:unloaded_tohtml_plugin")
657 let s:main_plugin_path = expand("<sfile>:p:h:h")."/plugin/tohtml.vim"
658 if filereadable(s:main_plugin_path)
659 let s:lines = readfile(s:main_plugin_path, "", 20)
660 call filter(s:lines, 'v:val =~ "loaded_2html_plugin = "')
661 if empty(s:lines)
662 let g:unloaded_tohtml_plugin = "unknown"
663 else
664 let g:unloaded_tohtml_plugin = substitute(s:lines[0], '.*loaded_2html_plugin = \([''"]\)\(\%(\1\@!.\)\+\)\1', '\2', '')
665 endif
666 unlet s:lines
667 else
668 let g:unloaded_tohtml_plugin = "unknown"
669 endif
670 unlet s:main_plugin_path
671 endif
672 let s:pluginversion = g:unloaded_tohtml_plugin
673endif
674
Bram Moolenaar071d4272004-06-13 20:20:40 +0000675" Split window to create a buffer with the HTML file.
676let s:orgbufnr = winbufnr(0)
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200677let s:origwin_stl = &l:stl
Bram Moolenaar071d4272004-06-13 20:20:40 +0000678if expand("%") == ""
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200679 if exists('g:html_diff_win_num')
680 exec 'new Untitled_win'.g:html_diff_win_num.'.'.(s:settings.use_xhtml ? 'x' : '').'html'
681 else
682 exec 'new Untitled.'.(s:settings.use_xhtml ? 'x' : '').'html'
683 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000684else
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200685 exec 'new %.'.(s:settings.use_xhtml ? 'x' : '').'html'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000686endif
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200687
688" Resize the new window to very small in order to make it draw faster
689let s:old_winheight = winheight(0)
690let s:old_winfixheight = &l:winfixheight
691if s:old_winheight > 2
692 resize 1 " leave enough room to view one line at a time
693 norm! G
694 norm! zt
695endif
696setlocal winfixheight
697
698let s:newwin_stl = &l:stl
699
700" on the new window, set the least time-consuming fold method
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200701let s:old_fen = &foldenable
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200702setlocal foldmethod=manual
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200703setlocal nofoldenable
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200704
Bram Moolenaar071d4272004-06-13 20:20:40 +0000705let s:newwin = winnr()
706let s:orgwin = bufwinnr(s:orgbufnr)
707
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200708setlocal modifiable
Bram Moolenaar071d4272004-06-13 20:20:40 +0000709%d
710let s:old_paste = &paste
711set paste
712let s:old_magic = &magic
713set magic
714
Bram Moolenaar166af9b2010-11-16 20:34:40 +0100715" set the fileencoding to match the charset we'll be using
716let &l:fileencoding=s:settings.vim_encoding
717
718" According to http://www.w3.org/TR/html4/charset.html#doc-char-set, the byte
719" order mark is highly recommend on the web when using multibyte encodings. But,
720" it is not a good idea to include it on UTF-8 files. Otherwise, let Vim
721" determine when it is actually inserted.
722if s:settings.vim_encoding == 'utf-8'
723 setlocal nobomb
724else
725 setlocal bomb
726endif
727
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200728let s:lines = []
729
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200730if s:settings.use_xhtml
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200731 if s:settings.encoding != ""
732 call add(s:lines, "<?xml version=\"1.0\" encoding=\"" . s:settings.encoding . "\"?>")
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000733 else
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200734 call add(s:lines, "<?xml version=\"1.0\"?>")
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000735 endif
Bram Moolenaar313b7232007-05-05 17:56:55 +0000736 let s:tag_close = ' />'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000737else
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000738 let s:tag_close = '>'
739endif
740
741let s:HtmlSpace = ' '
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000742let s:LeadingSpace = ' '
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000743let s:HtmlEndline = ''
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200744if s:settings.no_pre
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000745 let s:HtmlEndline = '<br' . s:tag_close
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200746 let s:LeadingSpace = s:settings.use_xhtml ? '&#160;' : '&nbsp;'
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000747 let s:HtmlSpace = '\' . s:LeadingSpace
Bram Moolenaar071d4272004-06-13 20:20:40 +0000748endif
749
750" HTML header, with the title and generator ;-). Left free space for the CSS,
751" to be filled at the end.
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200752call extend(s:lines, [
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200753 \ "<html>",
754 \ "<head>"])
755" include encoding as close to the top as possible, but only if not already
756" contained in XML information (to avoid haggling over content type)
757if s:settings.encoding != "" && !s:settings.use_xhtml
758 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 +0000759endif
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200760call extend(s:lines, [
761 \ ("<title>".expand("%:p:~")."</title>"),
762 \ ("<meta name=\"Generator\" content=\"Vim/".v:version/100.".".v:version%100.'"'.s:tag_close),
Bram Moolenaarf0d58ef2018-11-16 16:13:44 +0100763 \ ("<meta name=\"plugin-version\" content=\"".s:pluginversion.'"'.s:tag_close)
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200764 \ ])
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200765call add(s:lines, '<meta name="syntax" content="'.s:current_syntax.'"'.s:tag_close)
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200766call add(s:lines, '<meta name="settings" content="'.
767 \ join(filter(keys(s:settings),'s:settings[v:val]'),',').
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200768 \ ',prevent_copy='.s:settings.prevent_copy.
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200769 \ '"'.s:tag_close)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200770call add(s:lines, '<meta name="colorscheme" content="'.
771 \ (exists('g:colors_name')
772 \ ? g:colors_name
773 \ : 'none'). '"'.s:tag_close)
Bram Moolenaar313b7232007-05-05 17:56:55 +0000774
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200775if s:settings.use_css
776 if s:settings.dynamic_folds
777 if s:settings.hover_unfold
Bram Moolenaar5c736222010-01-06 20:54:52 +0100778 " if we are doing hover_unfold, use css 2 with css 1 fallback for IE6
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200779 call extend(s:lines, [
780 \ "<style type=\"text/css\">",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200781 \ s:settings.use_xhtml ? "" : "<!--",
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200782 \ ".FoldColumn { text-decoration: none; white-space: pre; }",
783 \ "",
784 \ "body * { margin: 0; padding: 0; }", "",
785 \ ".open-fold > .Folded { display: none; }",
786 \ ".open-fold > .fulltext { display: inline; }",
787 \ ".closed-fold > .fulltext { display: none; }",
788 \ ".closed-fold > .Folded { display: inline; }",
789 \ "",
790 \ ".open-fold > .toggle-open { display: none; }",
791 \ ".open-fold > .toggle-closed { display: inline; }",
792 \ ".closed-fold > .toggle-open { display: inline; }",
793 \ ".closed-fold > .toggle-closed { display: none; }",
794 \ "", "",
795 \ '/* opening a fold while hovering won''t be supported by IE6 and other',
796 \ "similar browsers, but it should fail gracefully. */",
797 \ ".closed-fold:hover > .fulltext { display: inline; }",
798 \ ".closed-fold:hover > .toggle-filler { display: none; }",
799 \ ".closed-fold:hover > .Folded { display: none; }",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200800 \ s:settings.use_xhtml ? "" : '-->',
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200801 \ '</style>'])
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200802 " TODO: IE7 doesn't *actually* support XHTML, maybe we should remove this.
803 " But if it's served up as tag soup, maybe the following will work, so
804 " leave it in for now.
805 call extend(s:lines, [
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200806 \ "<!--[if lt IE 7]><style type=\"text/css\">",
807 \ ".open-fold .Folded { display: none; }",
808 \ ".open-fold .fulltext { display: inline; }",
809 \ ".open-fold .toggle-open { display: none; }",
810 \ ".closed-fold .toggle-closed { display: inline; }",
811 \ "",
812 \ ".closed-fold .fulltext { display: none; }",
813 \ ".closed-fold .Folded { display: inline; }",
814 \ ".closed-fold .toggle-open { display: inline; }",
815 \ ".closed-fold .toggle-closed { display: none; }",
816 \ "</style>",
817 \ "<![endif]-->",
818 \])
Bram Moolenaar5c736222010-01-06 20:54:52 +0100819 else
820 " if we aren't doing hover_unfold, use CSS 1 only
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200821 call extend(s:lines, [
822 \ "<style type=\"text/css\">",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200823 \ s:settings.use_xhtml ? "" :"<!--",
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200824 \ ".FoldColumn { text-decoration: none; white-space: pre; }",
825 \ ".open-fold .Folded { display: none; }",
826 \ ".open-fold .fulltext { display: inline; }",
827 \ ".open-fold .toggle-open { display: none; }",
828 \ ".closed-fold .toggle-closed { display: inline; }",
829 \ "",
830 \ ".closed-fold .fulltext { display: none; }",
831 \ ".closed-fold .Folded { display: inline; }",
832 \ ".closed-fold .toggle-open { display: inline; }",
833 \ ".closed-fold .toggle-closed { display: none; }",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200834 \ s:settings.use_xhtml ? "" : '-->',
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200835 \ '</style>'
836 \])
Bram Moolenaar5c736222010-01-06 20:54:52 +0100837 endif
838 else
839 " if we aren't doing any dynamic folding, no need for any special rules
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200840 call extend(s:lines, [
841 \ "<style type=\"text/css\">",
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200842 \ s:settings.use_xhtml ? "" : "<!--",
843 \ s:settings.use_xhtml ? "" : '-->',
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200844 \ "</style>",
845 \])
Bram Moolenaar5c736222010-01-06 20:54:52 +0100846 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000847endif
Bram Moolenaar5c736222010-01-06 20:54:52 +0100848
Bram Moolenaarf0d58ef2018-11-16 16:13:44 +0100849let s:uses_script = s:settings.dynamic_folds || s:settings.line_ids || !empty(s:settings.prevent_copy)
850
851" insert script tag if needed
852if s:uses_script
853 call extend(s:lines, [
854 \ "",
855 \ "<script type='text/javascript'>",
856 \ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"])
857endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200858
Bram Moolenaar5c736222010-01-06 20:54:52 +0100859" insert javascript to toggle folds open and closed
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200860if s:settings.dynamic_folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200861 call extend(s:lines, [
862 \ "",
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200863 \ "function toggleFold(objID)",
864 \ "{",
865 \ " var fold;",
866 \ " fold = document.getElementById(objID);",
867 \ " if(fold.className == 'closed-fold')",
868 \ " {",
869 \ " fold.className = 'open-fold';",
870 \ " }",
871 \ " else if (fold.className == 'open-fold')",
872 \ " {",
873 \ " fold.className = 'closed-fold';",
874 \ " }",
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200875 \ "}"
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200876 \ ])
Bram Moolenaar5c736222010-01-06 20:54:52 +0100877endif
878
Bram Moolenaar31c31672013-06-26 13:28:14 +0200879if s:settings.line_ids
880 " insert javascript to get IDs from line numbers, and to open a fold before
881 " jumping to any lines contained therein
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200882 call extend(s:lines, [
883 \ "",
Bram Moolenaar31c31672013-06-26 13:28:14 +0200884 \ "/* function to open any folds containing a jumped-to line before jumping to it */",
885 \ "function JumpToLine()",
886 \ "{",
887 \ " var lineNum;",
888 \ " lineNum = window.location.hash;",
889 \ " lineNum = lineNum.substr(1); /* strip off '#' */",
890 \ "",
891 \ " if (lineNum.indexOf('L') == -1) {",
892 \ " lineNum = 'L'+lineNum;",
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200893 \ " }",
Bram Moolenaarf0d58ef2018-11-16 16:13:44 +0100894 \ " var lineElem = document.getElementById(lineNum);"
Bram Moolenaar31c31672013-06-26 13:28:14 +0200895 \ ])
Bram Moolenaarf0d58ef2018-11-16 16:13:44 +0100896
Bram Moolenaar31c31672013-06-26 13:28:14 +0200897 if s:settings.dynamic_folds
898 call extend(s:lines, [
899 \ "",
900 \ " /* navigate upwards in the DOM tree to open all folds containing the line */",
901 \ " var node = lineElem;",
902 \ " while (node && node.id != 'vimCodeElement".s:settings.id_suffix."')",
903 \ " {",
904 \ " if (node.className == 'closed-fold')",
905 \ " {",
906 \ " node.className = 'open-fold';",
907 \ " }",
908 \ " node = node.parentNode;",
909 \ " }",
910 \ ])
911 endif
912 call extend(s:lines, [
913 \ " /* Always jump to new location even if the line was hidden inside a fold, or",
914 \ " * we corrected the raw number to a line ID.",
915 \ " */",
916 \ " if (lineElem) {",
917 \ " lineElem.scrollIntoView(true);",
918 \ " }",
919 \ " return true;",
920 \ "}",
921 \ "if ('onhashchange' in window) {",
922 \ " window.onhashchange = JumpToLine;",
923 \ "}"
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200924 \ ])
925endif
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200926
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200927" Small text columns like the foldcolumn and line number column need a weird
928" hack to work around Webkit's and (in versions prior to 9) IE's lack of support
929" for the 'ch' unit without messing up Opera, which also doesn't support it but
930" works anyway.
931"
932" The problem is that without the 'ch' unit, it is not possible to specify a
933" size of an <input> in terms of character widths. Only Opera seems to do the
934" "sensible" thing and make the <input> sized to fit exactly as many characters
935" as specified by its "size" attribute, but the spec actually says "at least
936" wide enough to fit 'size' characters", so the other browsers are technically
937" correct as well.
938"
939" Anyway, this leads to two diffculties:
940" 1. The foldcolumn is made up of multiple elements side-by-side with
941" different sizes, each of which has their own extra padding added. Thus, a
942" column made up of one item of size 1 and another of size 2 would not
943" necessarily be equal in size to another line's foldcolumn with a single
944" item of size 3.
945" 2. The extra padding added to the <input> elements adds up to make the
946" foldcolumn and line number column too wide, especially in Webkit
947" browsers.
948"
949" So, the full workaround is:
950" 1. Define a default size in em, equal to the number of characters in the
951" input element, in case javascript is disabled and the browser does not
952" support the 'ch' unit. Unfortunately this makes Opera no longer work
953" properly without javascript. 1em per character is much too wide but it
954" looks better in webkit browsers than unaligned columns.
955" 2. Insert the following javascript to run at page load, which checks for the
956" width of a single character (in an extraneous page element inserted
957" before the page title, and set to hidden) and compares it to the width of
958" another extra <input> element with only one character. If the width
959" matches, the script does nothing more, but if not, it will figure out the
960" fraction of an em unit which would correspond with a ch unit if there
961" were one, and set the containing element (<pre> or <div>) to a class with
962" pre-defined rules which is closest to that fraction of an em. Rules are
963" defined from 0.05 em to 1em per ch.
964if !empty(s:settings.prevent_copy)
965 call extend(s:lines, [
966 \ '',
967 \ '/* simulate a "ch" unit by asking the browser how big a zero character is */',
968 \ 'function FixCharWidth() {',
969 \ ' /* get the hidden element which gives the width of a single character */',
970 \ ' var goodWidth = document.getElementById("oneCharWidth").clientWidth;',
971 \ ' /* get all input elements, we''ll filter on class later */',
972 \ ' var inputTags = document.getElementsByTagName("input");',
973 \ ' var ratio = 5;',
974 \ ' var inputWidth = document.getElementById("oneInputWidth").clientWidth;',
975 \ ' var emWidth = document.getElementById("oneEmWidth").clientWidth;',
976 \ ' if (inputWidth > goodWidth) {',
977 \ ' while (ratio < 100*goodWidth/emWidth && ratio < 100) {',
Bram Moolenaar31c31672013-06-26 13:28:14 +0200978 \ ' ratio += 5;',
979 \ ' }',
980 \ ' document.getElementById("vimCodeElement'.s:settings.id_suffix.'").className = "em"+ratio;',
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200981 \ ' }',
982 \ '}'
983 \ ])
984endif
985
Bram Moolenaarf0d58ef2018-11-16 16:13:44 +0100986" insert script closing tag if needed
987if s:uses_script
988 call extend(s:lines, [
989 \ '',
990 \ s:settings.use_xhtml ? '//]]>' : '-->',
991 \ "</script>"
992 \ ])
993endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200994
995call extend(s:lines, ["</head>"])
996if !empty(s:settings.prevent_copy)
997 call extend(s:lines,
Bram Moolenaar31c31672013-06-26 13:28:14 +0200998 \ ["<body onload='FixCharWidth();".(s:settings.line_ids ? " JumpToLine();" : "")."'>",
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200999 \ "<!-- hidden divs used by javascript to get the width of a char -->",
1000 \ "<div id='oneCharWidth'>0</div>",
1001 \ "<div id='oneInputWidth'><input size='1' value='0'".s:tag_close."</div>",
1002 \ "<div id='oneEmWidth' style='width: 1em;'></div>"
1003 \ ])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001004else
Bram Moolenaar31c31672013-06-26 13:28:14 +02001005 call extend(s:lines, ["<body".(s:settings.line_ids ? " onload='JumpToLine();'" : "").">"])
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001006endif
1007if s:settings.no_pre
1008 " if we're not using CSS we use a font tag which can't have a div inside
1009 if s:settings.use_css
Bram Moolenaar31c31672013-06-26 13:28:14 +02001010 call extend(s:lines, ["<div id='vimCodeElement".s:settings.id_suffix."'>"])
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001011 endif
1012else
Bram Moolenaar31c31672013-06-26 13:28:14 +02001013 call extend(s:lines, ["<pre id='vimCodeElement".s:settings.id_suffix."'>"])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001014endif
1015
1016exe s:orgwin . "wincmd w"
1017
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001018" caches of style data
1019" initialize to include line numbers if using them
1020if s:settings.number_lines
1021 let s:stylelist = { s:LINENR_ID : ".LineNr { " . s:CSS1( s:LINENR_ID ) . "}" }
1022else
1023 let s:stylelist = {}
1024endif
1025let s:diffstylelist = {
1026 \ s:DIFF_A_ID : ".DiffAdd { " . s:CSS1( s:DIFF_A_ID ) . "}",
1027 \ s:DIFF_C_ID : ".DiffChange { " . s:CSS1( s:DIFF_C_ID ) . "}",
1028 \ s:DIFF_D_ID : ".DiffDelete { " . s:CSS1( s:DIFF_D_ID ) . "}",
1029 \ s:DIFF_T_ID : ".DiffText { " . s:CSS1( s:DIFF_T_ID ) . "}"
1030 \ }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001031
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001032" set up progress bar in the status line
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001033if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001034 " ProgressBar Indicator
1035 let s:progressbar={}
1036
1037 " Progessbar specific functions
1038 func! s:ProgressBar(title, max_value, winnr)
1039 let pgb=copy(s:progressbar)
1040 let pgb.title = a:title.' '
1041 let pgb.max_value = a:max_value
1042 let pgb.winnr = a:winnr
1043 let pgb.cur_value = 0
1044 let pgb.items = { 'title' : { 'color' : 'Statusline' },
1045 \'bar' : { 'color' : 'Statusline' , 'fillcolor' : 'DiffDelete' , 'bg' : 'Statusline' } ,
1046 \'counter' : { 'color' : 'Statusline' } }
1047 let pgb.last_value = 0
1048 let pgb.needs_redraw = 0
1049 " Note that you must use len(split) instead of len() if you want to use
1050 " unicode in title.
1051 "
1052 " Subtract 3 for spacing around the title.
1053 " Subtract 4 for the percentage display.
1054 " Subtract 2 for spacing before this.
1055 " Subtract 2 more for the '|' on either side of the progress bar
1056 let pgb.subtractedlen=len(split(pgb.title, '\zs'))+3+4+2+2
1057 let pgb.max_len = 0
1058 set laststatus=2
1059 return pgb
1060 endfun
1061
1062 " Function: progressbar.calculate_ticks() {{{1
1063 func! s:progressbar.calculate_ticks(pb_len)
1064 if a:pb_len<=0
1065 let pb_len = 100
1066 else
1067 let pb_len = a:pb_len
1068 endif
1069 let self.progress_ticks = map(range(pb_len+1), "v:val * self.max_value / pb_len")
1070 endfun
1071
1072 "Function: progressbar.paint()
1073 func! s:progressbar.paint()
1074 " Recalculate widths.
1075 let max_len = winwidth(self.winnr)
1076 let pb_len = 0
1077 " always true on first call because of initial value of self.max_len
1078 if max_len != self.max_len
1079 let self.max_len = max_len
1080
1081 " Progressbar length
1082 let pb_len = max_len - self.subtractedlen
1083
1084 call self.calculate_ticks(pb_len)
1085
1086 let self.needs_redraw = 1
1087 let cur_value = 0
1088 let self.pb_len = pb_len
1089 else
1090 " start searching at the last found index to make the search for the
1091 " appropriate tick value normally take 0 or 1 comparisons
1092 let cur_value = self.last_value
1093 let pb_len = self.pb_len
1094 endif
1095
1096 let cur_val_max = pb_len > 0 ? pb_len : 100
1097
1098 " find the current progress bar position based on precalculated thresholds
1099 while cur_value < cur_val_max && self.cur_value > self.progress_ticks[cur_value]
1100 let cur_value += 1
1101 endwhile
1102
1103 " update progress bar
1104 if self.last_value != cur_value || self.needs_redraw || self.cur_value == self.max_value
1105 let self.needs_redraw = 1
1106 let self.last_value = cur_value
1107
1108 let t_color = self.items.title.color
1109 let b_fcolor = self.items.bar.fillcolor
1110 let b_color = self.items.bar.color
1111 let c_color = self.items.counter.color
1112
1113 let stl = "%#".t_color."#%-( ".self.title." %)".
1114 \"%#".b_color."#".
1115 \(pb_len>0 ?
1116 \ ('|%#'.b_fcolor."#%-(".repeat(" ",cur_value)."%)".
1117 \ '%#'.b_color."#".repeat(" ",pb_len-cur_value)."|"):
1118 \ ('')).
1119 \"%=%#".c_color."#%( ".printf("%3.d ",100*self.cur_value/self.max_value)."%% %)"
1120 call setwinvar(self.winnr, '&stl', stl)
1121 endif
1122 endfun
1123
1124 func! s:progressbar.incr( ... )
1125 let self.cur_value += (a:0 ? a:1 : 1)
1126 " if we were making a general-purpose progress bar, we'd need to limit to a
1127 " lower limit as well, but since we always increment with a positive value
1128 " in this script, we only need limit the upper value
1129 let self.cur_value = (self.cur_value > self.max_value ? self.max_value : self.cur_value)
1130 call self.paint()
1131 endfun
1132 " }}}
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001133 if s:settings.dynamic_folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001134 " to process folds we make two passes through each line
1135 let s:pgb = s:ProgressBar("Processing folds:", line('$')*2, s:orgwin)
1136 endif
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001137endif
1138
Bram Moolenaar5c736222010-01-06 20:54:52 +01001139" First do some preprocessing for dynamic folding. Do this for the entire file
1140" so we don't accidentally start within a closed fold or something.
1141let s:allfolds = []
1142
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001143if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +01001144 let s:lnum = 1
1145 let s:end = line('$')
1146 " save the fold text and set it to the default so we can find fold levels
1147 let s:foldtext_save = &foldtext
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001148 setlocal foldtext&
Bram Moolenaar5c736222010-01-06 20:54:52 +01001149
1150 " we will set the foldcolumn in the html to the greater of the maximum fold
1151 " level and the current foldcolumn setting
1152 let s:foldcolumn = &foldcolumn
1153
1154 " get all info needed to describe currently closed folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001155 while s:lnum <= s:end
Bram Moolenaar5c736222010-01-06 20:54:52 +01001156 if foldclosed(s:lnum) == s:lnum
1157 " default fold text has '+-' and then a number of dashes equal to fold
1158 " level, so subtract 2 from index of first non-dash after the dashes
1159 " in order to get the fold level of the current fold
1160 let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2
Bram Moolenaar5c736222010-01-06 20:54:52 +01001161 " store fold info for later use
1162 let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"}
1163 call add(s:allfolds, s:newfold)
1164 " open the fold so we can find any contained folds
1165 execute s:lnum."foldopen"
1166 else
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001167 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001168 call s:pgb.incr()
1169 if s:pgb.needs_redraw
1170 redrawstatus
1171 let s:pgb.needs_redraw = 0
1172 endif
1173 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001174 let s:lnum = s:lnum + 1
1175 endif
1176 endwhile
1177
1178 " close all folds to get info for originally open folds
1179 silent! %foldclose!
1180 let s:lnum = 1
1181
1182 " the originally open folds will be all folds we encounter that aren't
1183 " already in the list of closed folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001184 while s:lnum <= s:end
Bram Moolenaar5c736222010-01-06 20:54:52 +01001185 if foldclosed(s:lnum) == s:lnum
1186 " default fold text has '+-' and then a number of dashes equal to fold
1187 " level, so subtract 2 from index of first non-dash after the dashes
1188 " in order to get the fold level of the current fold
1189 let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2
Bram Moolenaar5c736222010-01-06 20:54:52 +01001190 let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"}
1191 " only add the fold if we don't already have it
1192 if empty(s:allfolds) || index(s:allfolds, s:newfold) == -1
1193 let s:newfold.type = "open-fold"
1194 call add(s:allfolds, s:newfold)
1195 endif
1196 " open the fold so we can find any contained folds
1197 execute s:lnum."foldopen"
1198 else
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001199 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001200 call s:pgb.incr()
1201 if s:pgb.needs_redraw
1202 redrawstatus
1203 let s:pgb.needs_redraw = 0
1204 endif
1205 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001206 let s:lnum = s:lnum + 1
1207 endif
1208 endwhile
1209
1210 " sort the folds so that we only ever need to look at the first item in the
1211 " list of folds
1212 call sort(s:allfolds, "s:FoldCompare")
1213
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001214 let &l:foldtext = s:foldtext_save
Bram Moolenaar5c736222010-01-06 20:54:52 +01001215 unlet s:foldtext_save
1216
1217 " close all folds again so we can get the fold text as we go
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001218 silent! %foldclose!
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001219
Bram Moolenaar251e1912011-06-19 05:09:16 +02001220 " Go through and remove folds we don't need to (or cannot) process in the
1221 " current conversion range
1222 "
1223 " If a fold is removed which contains other folds, which are included, we need
1224 " to adjust the level of the included folds as used by the conversion logic
1225 " (avoiding special cases is good)
1226 "
1227 " Note any time we remove a fold, either all of the included folds are in it,
1228 " or none of them, because we only remove a fold if neither its start nor its
1229 " end are within the conversion range.
1230 let leveladjust = 0
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001231 for afold in s:allfolds
1232 let removed = 0
1233 if exists("g:html_start_line") && exists("g:html_end_line")
1234 if afold.firstline < g:html_start_line
Bram Moolenaar251e1912011-06-19 05:09:16 +02001235 if afold.lastline <= g:html_end_line && afold.lastline >= g:html_start_line
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001236 " if a fold starts before the range to convert but stops within the
1237 " range, we need to include it. Make it start on the first converted
1238 " line.
1239 let afold.firstline = g:html_start_line
1240 else
1241 " if the fold lies outside the range or the start and stop enclose
1242 " the entire range, don't bother parsing it
1243 call remove(s:allfolds, index(s:allfolds, afold))
1244 let removed = 1
Bram Moolenaar251e1912011-06-19 05:09:16 +02001245 if afold.lastline > g:html_end_line
1246 let leveladjust += 1
1247 endif
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001248 endif
1249 elseif afold.firstline > g:html_end_line
1250 " If the entire fold lies outside the range we need to remove it.
1251 call remove(s:allfolds, index(s:allfolds, afold))
1252 let removed = 1
1253 endif
1254 elseif exists("g:html_start_line")
1255 if afold.firstline < g:html_start_line
1256 " if there is no last line, but there is a first line, the end of the
1257 " fold will always lie within the region of interest, so keep it
1258 let afold.firstline = g:html_start_line
1259 endif
1260 elseif exists("g:html_end_line")
1261 " if there is no first line we default to the first line in the buffer so
1262 " the fold start will always be included if the fold itself is included.
1263 " If however the entire fold lies outside the range we need to remove it.
1264 if afold.firstline > g:html_end_line
1265 call remove(s:allfolds, index(s:allfolds, afold))
1266 let removed = 1
1267 endif
1268 endif
1269 if !removed
Bram Moolenaar251e1912011-06-19 05:09:16 +02001270 let afold.level -= leveladjust
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001271 if afold.level+1 > s:foldcolumn
1272 let s:foldcolumn = afold.level+1
1273 endif
1274 endif
1275 endfor
Bram Moolenaar251e1912011-06-19 05:09:16 +02001276
1277 " if we've removed folds containing the conversion range from processing,
1278 " getting foldtext as we go won't know to open the removed folds, so the
1279 " foldtext would be wrong; open them now.
1280 "
1281 " Note that only when a start and an end line is specified will a fold
1282 " containing the current range ever be removed.
1283 while leveladjust > 0
1284 exe g:html_start_line."foldopen"
1285 let leveladjust -= 1
1286 endwhile
Bram Moolenaar5c736222010-01-06 20:54:52 +01001287endif
1288
1289" Now loop over all lines in the original text to convert to html.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001290" Use html_start_line and html_end_line if they are set.
Bram Moolenaarb02cbe32010-07-11 22:38:52 +02001291if exists("g:html_start_line")
Bram Moolenaar071d4272004-06-13 20:20:40 +00001292 let s:lnum = html_start_line
1293 if s:lnum < 1 || s:lnum > line("$")
1294 let s:lnum = 1
1295 endif
1296else
1297 let s:lnum = 1
1298endif
Bram Moolenaarb02cbe32010-07-11 22:38:52 +02001299if exists("g:html_end_line")
Bram Moolenaar071d4272004-06-13 20:20:40 +00001300 let s:end = html_end_line
1301 if s:end < s:lnum || s:end > line("$")
1302 let s:end = line("$")
1303 endif
1304else
1305 let s:end = line("$")
1306endif
1307
Bram Moolenaar5c736222010-01-06 20:54:52 +01001308" stack to keep track of all the folds containing the current line
1309let s:foldstack = []
1310
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001311if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001312 let s:pgb = s:ProgressBar("Processing lines:", s:end - s:lnum + 1, s:orgwin)
1313endif
1314
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001315if s:settings.number_lines
Bram Moolenaar5c736222010-01-06 20:54:52 +01001316 let s:margin = strlen(s:end) + 1
1317else
1318 let s:margin = 0
1319endif
1320
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001321if has('folding') && !s:settings.ignore_folding
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001322 let s:foldfillchar = &fillchars[matchend(&fillchars, 'fold:')]
1323 if s:foldfillchar == ''
1324 let s:foldfillchar = '-'
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001325 endif
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001326endif
1327let s:difffillchar = &fillchars[matchend(&fillchars, 'diff:')]
1328if s:difffillchar == ''
1329 let s:difffillchar = '-'
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001330endif
1331
Bram Moolenaar5c736222010-01-06 20:54:52 +01001332let s:foldId = 0
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001333
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001334if !s:settings.expand_tabs
1335 " If keeping tabs, add them to printable characters so we keep them when
1336 " formatting text (strtrans() doesn't replace printable chars)
1337 let s:old_isprint = &isprint
1338 setlocal isprint+=9
1339endif
1340
Bram Moolenaar071d4272004-06-13 20:20:40 +00001341while s:lnum <= s:end
1342
Bram Moolenaar47136d72004-10-12 20:02:24 +00001343 " If there are filler lines for diff mode, show these above the line.
1344 let s:filler = diff_filler(s:lnum)
1345 if s:filler > 0
1346 let s:n = s:filler
1347 while s:n > 0
Bram Moolenaar5c736222010-01-06 20:54:52 +01001348 let s:new = repeat(s:difffillchar, 3)
Bram Moolenaar47136d72004-10-12 20:02:24 +00001349
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001350 if s:n > 2 && s:n < s:filler && !s:settings.whole_filler
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001351 let s:new = s:new . " " . s:filler . " inserted lines "
1352 let s:n = 2
1353 endif
1354
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001355 if !s:settings.no_pre
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001356 " HTML line wrapping is off--go ahead and fill to the margin
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001357 " TODO: what about when CSS wrapping is turned on?
Bram Moolenaar5c736222010-01-06 20:54:52 +01001358 let s:new = s:new . repeat(s:difffillchar, &columns - strlen(s:new) - s:margin)
1359 else
1360 let s:new = s:new . repeat(s:difffillchar, 3)
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001361 endif
1362
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001363 let s:new = s:HtmlFormat_d(s:new, s:DIFF_D_ID, 0)
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001364 if s:settings.number_lines
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001365 " Indent if line numbering is on. Indent gets style of line number
1366 " column.
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001367 let s:new = s:HtmlFormat_n(repeat(' ', s:margin), s:LINENR_ID, 0, 0) . s:new
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001368 endif
1369 if s:settings.dynamic_folds && !s:settings.no_foldcolumn && s:foldcolumn > 0
1370 " Indent for foldcolumn if there is one. Assume it's empty, there should
1371 " not be a fold for deleted lines in diff mode.
1372 let s:new = s:FoldColumn_fill() . s:new
Bram Moolenaar5c736222010-01-06 20:54:52 +01001373 endif
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001374 call add(s:lines, s:new.s:HtmlEndline)
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001375
Bram Moolenaar47136d72004-10-12 20:02:24 +00001376 let s:n = s:n - 1
1377 endwhile
1378 unlet s:n
1379 endif
1380 unlet s:filler
1381
1382 " Start the line with the line number.
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001383 if s:settings.number_lines
Bram Moolenaar5c736222010-01-06 20:54:52 +01001384 let s:numcol = repeat(' ', s:margin - 1 - strlen(s:lnum)) . s:lnum . ' '
Bram Moolenaar47136d72004-10-12 20:02:24 +00001385 endif
1386
Bram Moolenaar5c736222010-01-06 20:54:52 +01001387 let s:new = ""
1388
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001389 if has('folding') && !s:settings.ignore_folding && foldclosed(s:lnum) > -1 && !s:settings.dynamic_folds
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001390 "
Bram Moolenaar5c736222010-01-06 20:54:52 +01001391 " This is the beginning of a folded block (with no dynamic folding)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001392 let s:new = foldtextresult(s:lnum)
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001393 if !s:settings.no_pre
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001394 " HTML line wrapping is off--go ahead and fill to the margin
1395 let s:new = s:new . repeat(s:foldfillchar, &columns - strlen(s:new))
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001396 endif
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001397
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001398 " put numcol in a separate group for sake of unselectable text
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001399 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 +00001400
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001401 " Skip to the end of the fold
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001402 let s:new_lnum = foldclosedend(s:lnum)
1403
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001404 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001405 call s:pgb.incr(s:new_lnum - s:lnum)
1406 endif
1407
1408 let s:lnum = s:new_lnum
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001409
1410 else
1411 "
Bram Moolenaar5c736222010-01-06 20:54:52 +01001412 " A line that is not folded, or doing dynamic folding.
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001413 "
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001414 let s:line = getline(s:lnum)
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001415 let s:len = strlen(s:line)
1416
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001417 if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +01001418 " First insert a closing for any open folds that end on this line
1419 while !empty(s:foldstack) && get(s:foldstack,0).lastline == s:lnum-1
1420 let s:new = s:new."</span></span>"
1421 call remove(s:foldstack, 0)
1422 endwhile
1423
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001424 " Now insert an opening for any new folds that start on this line
Bram Moolenaar5c736222010-01-06 20:54:52 +01001425 let s:firstfold = 1
1426 while !empty(s:allfolds) && get(s:allfolds,0).firstline == s:lnum
1427 let s:foldId = s:foldId + 1
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001428 let s:new .= "<span id='"
1429 let s:new .= (exists('g:html_diff_win_num') ? "win".g:html_diff_win_num : "")
Bram Moolenaar31c31672013-06-26 13:28:14 +02001430 let s:new .= "fold".s:foldId.s:settings.id_suffix."' class='".s:allfolds[0].type."'>"
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001431
Bram Moolenaar5c736222010-01-06 20:54:52 +01001432
1433 " Unless disabled, add a fold column for the opening line of a fold.
1434 "
1435 " Note that dynamic folds require using css so we just use css to take
1436 " care of the leading spaces rather than using &nbsp; in the case of
1437 " html_no_pre to make it easier
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001438 if !s:settings.no_foldcolumn
Bram Moolenaar5c736222010-01-06 20:54:52 +01001439 " add fold column that can open the new fold
1440 if s:allfolds[0].level > 1 && s:firstfold
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001441 let s:new = s:new . s:FoldColumn_build('|', s:allfolds[0].level - 1, 0, "",
Bram Moolenaar31c31672013-06-26 13:28:14 +02001442 \ 'toggle-open FoldColumn','javascript:toggleFold("fold'.s:foldstack[0].id.s:settings.id_suffix.'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001443 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001444 " add the filler spaces separately from the '+' char so that it can be
1445 " shown/hidden separately during a hover unfold
1446 let s:new = s:new . s:FoldColumn_build("+", 1, 0, "",
Bram Moolenaar31c31672013-06-26 13:28:14 +02001447 \ 'toggle-open FoldColumn', 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001448 " If this is not the last fold we're opening on this line, we need
1449 " to keep the filler spaces hidden if the fold is opened by mouse
1450 " hover. If it is the last fold to open in the line, we shouldn't hide
1451 " them, so don't apply the toggle-filler class.
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001452 let s:new = s:new . s:FoldColumn_build(" ", 1, s:foldcolumn - s:allfolds[0].level - 1, "",
1453 \ 'toggle-open FoldColumn'. (get(s:allfolds, 1, {'firstline': 0}).firstline == s:lnum ?" toggle-filler" :""),
Bram Moolenaar31c31672013-06-26 13:28:14 +02001454 \ 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001455
1456 " add fold column that can close the new fold
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001457 " only add extra blank space if we aren't opening another fold on the
1458 " same line
Bram Moolenaar5c736222010-01-06 20:54:52 +01001459 if get(s:allfolds, 1, {'firstline': 0}).firstline != s:lnum
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001460 let s:extra_space = s:foldcolumn - s:allfolds[0].level
1461 else
1462 let s:extra_space = 0
Bram Moolenaar5c736222010-01-06 20:54:52 +01001463 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001464 if s:firstfold
1465 " the first fold in a line has '|' characters from folds opened in
1466 " previous lines, before the '-' for this fold
1467 let s:new .= s:FoldColumn_build('|', s:allfolds[0].level - 1, s:extra_space, '-',
Bram Moolenaar31c31672013-06-26 13:28:14 +02001468 \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001469 else
1470 " any subsequent folds in the line only add a single '-'
1471 let s:new = s:new . s:FoldColumn_build("-", 1, s:extra_space, "",
Bram Moolenaar31c31672013-06-26 13:28:14 +02001472 \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'.s:foldId.s:settings.id_suffix.'");')
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001473 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001474 let s:firstfold = 0
1475 endif
1476
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001477 " Add fold text, moving the span ending to the next line so collapsing
1478 " of folds works correctly.
1479 " Put numcol in a separate group for sake of unselectable text.
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001480 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 +01001481 let s:new = s:new . "<span class='fulltext'>"
1482
1483 " open the fold now that we have the fold text to allow retrieval of
1484 " fold text for subsequent folds
1485 execute s:lnum."foldopen"
1486 call insert(s:foldstack, remove(s:allfolds,0))
1487 let s:foldstack[0].id = s:foldId
1488 endwhile
1489
1490 " Unless disabled, add a fold column for other lines.
1491 "
1492 " Note that dynamic folds require using css so we just use css to take
1493 " care of the leading spaces rather than using &nbsp; in the case of
1494 " html_no_pre to make it easier
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001495 if !s:settings.no_foldcolumn
Bram Moolenaar5c736222010-01-06 20:54:52 +01001496 if empty(s:foldstack)
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001497 " add the empty foldcolumn for unfolded lines if there is a fold
1498 " column at all
1499 if s:foldcolumn > 0
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001500 let s:new = s:new . s:FoldColumn_fill()
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001501 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001502 else
1503 " add the fold column for folds not on the opening line
1504 if get(s:foldstack, 0).firstline < s:lnum
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001505 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 +02001506 \ 'FoldColumn', 'javascript:toggleFold("fold'.s:foldstack[0].id.s:settings.id_suffix.'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001507 endif
1508 endif
1509 endif
1510 endif
1511
1512 " Now continue with the unfolded line text
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001513 if s:settings.number_lines
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001514 let s:new = s:new . s:HtmlFormat_n(s:numcol, s:LINENR_ID, 0, s:lnum)
Bram Moolenaar31c31672013-06-26 13:28:14 +02001515 elseif s:settings.line_ids
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001516 let s:new = s:new . s:HtmlFormat_n("", s:LINENR_ID, 0, s:lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001517 endif
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001518
Bram Moolenaar47136d72004-10-12 20:02:24 +00001519 " Get the diff attribute, if any.
1520 let s:diffattr = diff_hlID(s:lnum, 1)
1521
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001522 " initialize conceal info to act like not concealed, just in case
1523 let s:concealinfo = [0, '']
1524
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001525 " Loop over each character in the line
1526 let s:col = 1
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001527
1528 " most of the time we won't use the diff_id, initialize to zero
1529 let s:diff_id = 0
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001530
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001531 while s:col <= s:len || (s:col == 1 && s:diffattr)
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001532 let s:startcol = s:col " The start column for processing text
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001533 if !s:settings.ignore_conceal && has('conceal')
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001534 let s:concealinfo = synconcealed(s:lnum, s:col)
1535 endif
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001536 if !s:settings.ignore_conceal && s:concealinfo[0]
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001537 let s:col = s:col + 1
1538 " Speed loop (it's small - that's the trick)
1539 " Go along till we find a change in the match sequence number (ending
1540 " the specific concealed region) or until there are no more concealed
1541 " characters.
1542 while s:col <= s:len && s:concealinfo == synconcealed(s:lnum, s:col) | let s:col = s:col + 1 | endwhile
1543 elseif s:diffattr
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001544 let s:diff_id = diff_hlID(s:lnum, s:col)
1545 let s:id = synID(s:lnum, s:col, 1)
Bram Moolenaar47136d72004-10-12 20:02:24 +00001546 let s:col = s:col + 1
1547 " Speed loop (it's small - that's the trick)
1548 " Go along till we find a change in hlID
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001549 while s:col <= s:len && s:id == synID(s:lnum, s:col, 1)
1550 \ && s:diff_id == diff_hlID(s:lnum, s:col) |
1551 \ let s:col = s:col + 1 |
1552 \ endwhile
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001553 if s:len < &columns && !s:settings.no_pre
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001554 " Add spaces at the end of the raw text line to extend the changed
1555 " line to the full width.
Bram Moolenaar5c736222010-01-06 20:54:52 +01001556 let s:line = s:line . repeat(' ', &columns - virtcol([s:lnum, s:len]) - s:margin)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001557 let s:len = &columns
1558 endif
Bram Moolenaar47136d72004-10-12 20:02:24 +00001559 else
1560 let s:id = synID(s:lnum, s:col, 1)
1561 let s:col = s:col + 1
1562 " Speed loop (it's small - that's the trick)
1563 " Go along till we find a change in synID
1564 while s:col <= s:len && s:id == synID(s:lnum, s:col, 1) | let s:col = s:col + 1 | endwhile
1565 endif
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001566
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001567 if s:settings.ignore_conceal || !s:concealinfo[0]
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001568 " Expand tabs if needed
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001569 let s:expandedtab = strpart(s:line, s:startcol - 1, s:col - s:startcol)
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001570 if s:settings.expand_tabs
1571 let s:offset = 0
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001572 let s:idx = stridx(s:expandedtab, "\t")
Bram Moolenaarf0d58ef2018-11-16 16:13:44 +01001573 let s:tablist = split(&vts,',')
1574 if empty(s:tablist)
1575 let s:tablist = [ &ts ]
1576 endif
1577 let s:tabidx = 0
1578 let s:tabwidth = 0
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001579 while s:idx >= 0
Bram Moolenaarf0d58ef2018-11-16 16:13:44 +01001580 while s:startcol+s:idx > s:tabwidth + s:tablist[s:tabidx]
1581 let s:tabwidth += s:tablist[s:tabidx]
1582 if s:tabidx < len(s:tablist)-1
1583 let s:tabidx = s:tabidx+1
1584 endif
1585 endwhile
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001586 if has("multi_byte_encoding")
1587 if s:startcol + s:idx == 1
Bram Moolenaarf0d58ef2018-11-16 16:13:44 +01001588 let s:i = s:tablist[s:tabidx]
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001589 else
1590 if s:idx == 0
1591 let s:prevc = matchstr(s:line, '.\%' . (s:startcol + s:idx + s:offset) . 'c')
1592 else
1593 let s:prevc = matchstr(s:expandedtab, '.\%' . (s:idx + 1) . 'c')
1594 endif
1595 let s:vcol = virtcol([s:lnum, s:startcol + s:idx + s:offset - len(s:prevc)])
Bram Moolenaarf0d58ef2018-11-16 16:13:44 +01001596 let s:i = s:tablist[s:tabidx] - (s:vcol - s:tabwidth)
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001597 endif
1598 let s:offset -= s:i - 1
1599 else
Bram Moolenaarf0d58ef2018-11-16 16:13:44 +01001600 let s:i = s:tablist[s:tabidx] - ((s:idx + s:startcol - 1) - s:tabwidth)
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001601 endif
1602 let s:expandedtab = substitute(s:expandedtab, '\t', repeat(' ', s:i), '')
1603 let s:idx = stridx(s:expandedtab, "\t")
1604 endwhile
1605 end
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001606
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001607 " get the highlight group name to use
1608 let s:id = synIDtrans(s:id)
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001609 else
1610 " use Conceal highlighting for concealed text
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001611 let s:id = s:CONCEAL_ID
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001612 let s:expandedtab = s:concealinfo[1]
1613 endif
1614
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001615 " Output the text with the same synID, with class set to the highlight ID
1616 " name, unless it has been concealed completely.
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001617 if strlen(s:expandedtab) > 0
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001618 let s:new = s:new . s:HtmlFormat(s:expandedtab, s:id, s:diff_id, "", 0)
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001619 endif
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001620 endwhile
1621 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001622
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001623 call extend(s:lines, split(s:new.s:HtmlEndline, '\n', 1))
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001624 if !s:settings.no_progress && s:pgb.needs_redraw
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001625 redrawstatus
1626 let s:pgb.needs_redraw = 0
1627 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001628 let s:lnum = s:lnum + 1
Bram Moolenaar313b7232007-05-05 17:56:55 +00001629
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001630 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001631 call s:pgb.incr()
1632 endif
1633endwhile
1634
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001635if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +01001636 " finish off any open folds
1637 while !empty(s:foldstack)
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001638 let s:lines[-1].="</span></span>"
Bram Moolenaar5c736222010-01-06 20:54:52 +01001639 call remove(s:foldstack, 0)
1640 endwhile
1641
1642 " add fold column to the style list if not already there
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001643 let s:id = s:FOLD_C_ID
1644 if !has_key(s:stylelist, s:id)
1645 let s:stylelist[s:id] = '.FoldColumn { ' . s:CSS1(s:id) . '}'
Bram Moolenaar5c736222010-01-06 20:54:52 +01001646 endif
1647endif
1648
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001649if s:settings.no_pre
1650 if !s:settings.use_css
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001651 " Close off the font tag that encapsulates the whole <body>
Bram Moolenaarbebca9d2010-08-07 15:47:30 +02001652 call extend(s:lines, ["</font>", "</body>", "</html>"])
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001653 else
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001654 call extend(s:lines, ["</div>", "</body>", "</html>"])
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001655 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001656else
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001657 call extend(s:lines, ["</pre>", "</body>", "</html>"])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001658endif
1659
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001660exe s:newwin . "wincmd w"
1661call setline(1, s:lines)
1662unlet s:lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00001663
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001664" Mangle modelines so Vim doesn't try to use HTML text as a modeline if editing
1665" this file in the future; need to do this after generating all the text in case
1666" the modeline text has different highlight groups which all turn out to be
1667" stripped from the final output.
Bram Moolenaardd007ed2013-07-09 15:44:17 +02001668%s!\v(%(^|\s+)%([Vv]i%(m%([<=>]?\d+)?)?|ex)):!\1\&#0058;!ge
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001669
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001670" The generated HTML is admittedly ugly and takes a LONG time to fold.
1671" Make sure the user doesn't do syntax folding when loading a generated file,
1672" using a modeline.
1673call append(line('$'), "<!-- vim: set foldmethod=manual : -->")
1674
Bram Moolenaar071d4272004-06-13 20:20:40 +00001675" Now, when we finally know which, we define the colors and styles
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001676if s:settings.use_css
Bram Moolenaar071d4272004-06-13 20:20:40 +00001677 1;/<style type="text/+1
1678endif
1679
Bram Moolenaar071d4272004-06-13 20:20:40 +00001680" Normal/global attributes
1681" For Netscape 4, set <body> attributes too, though, strictly speaking, it's
1682" incorrect.
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001683if s:settings.use_css
1684 if s:settings.no_pre
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001685 call append('.', "body { color: " . s:fgc . "; background-color: " . s:bgc . "; font-family: ". s:htmlfont ."; }")
1686 +
Bram Moolenaar071d4272004-06-13 20:20:40 +00001687 else
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001688 call append('.', "pre { " . s:whitespace . "font-family: ". s:htmlfont ."; color: " . s:fgc . "; background-color: " . s:bgc . "; }")
1689 +
Bram Moolenaar071d4272004-06-13 20:20:40 +00001690 yank
1691 put
1692 execute "normal! ^cwbody\e"
Bram Moolenaar8e5af3e2011-04-28 19:02:44 +02001693 " body should not have the wrap formatting, only the pre section
1694 if s:whitespace != ''
1695 exec 's#'.s:whitespace
1696 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001697 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001698 " fix browser inconsistencies (sometimes within the same browser) of different
1699 " default font size for different elements
1700 call append('.', '* { font-size: 1em; }')
1701 +
1702 " if we use any input elements for unselectable content, make sure they look
1703 " like normal text
1704 if !empty(s:settings.prevent_copy)
1705 call append('.', 'input { border: none; margin: 0; padding: 0; font-family: '.s:htmlfont.'; }')
1706 +
1707 " ch units for browsers which support them, em units for a somewhat
1708 " reasonable fallback. Also make sure the special elements for size
1709 " calculations aren't seen.
1710 call append('.', [
1711 \ "input[size='1'] { width: 1em; width: 1ch; }",
1712 \ "input[size='2'] { width: 2em; width: 2ch; }",
1713 \ "input[size='3'] { width: 3em; width: 3ch; }",
1714 \ "input[size='4'] { width: 4em; width: 4ch; }",
1715 \ "input[size='5'] { width: 5em; width: 5ch; }",
1716 \ "input[size='6'] { width: 6em; width: 6ch; }",
1717 \ "input[size='7'] { width: 7em; width: 7ch; }",
1718 \ "input[size='8'] { width: 8em; width: 8ch; }",
1719 \ "input[size='9'] { width: 9em; width: 9ch; }",
1720 \ "input[size='10'] { width: 10em; width: 10ch; }",
1721 \ "input[size='11'] { width: 11em; width: 11ch; }",
1722 \ "input[size='12'] { width: 12em; width: 12ch; }",
1723 \ "input[size='13'] { width: 13em; width: 13ch; }",
1724 \ "input[size='14'] { width: 14em; width: 14ch; }",
1725 \ "input[size='15'] { width: 15em; width: 15ch; }",
1726 \ "input[size='16'] { width: 16em; width: 16ch; }",
1727 \ "input[size='17'] { width: 17em; width: 17ch; }",
1728 \ "input[size='18'] { width: 18em; width: 18ch; }",
1729 \ "input[size='19'] { width: 19em; width: 19ch; }",
1730 \ "input[size='20'] { width: 20em; width: 20ch; }",
1731 \ "#oneCharWidth, #oneEmWidth, #oneInputWidth { padding: 0; margin: 0; position: absolute; left: -999999px; visibility: hidden; }"
1732 \ ])
1733 +21
1734 for w in range(5, 100, 5)
1735 let base = 0.01 * w
1736 call append('.', join(map(range(1,20), "'.em'.w.' input[size='''.v:val.'''] { width: '.string(v:val*base).'em; }'")))
1737 +
1738 endfor
1739 if s:settings.prevent_copy =~# 'f'
1740 " Make the cursor show active fold columns as active areas, and empty fold
1741 " columns as not interactive.
1742 call append('.', ['input.FoldColumn { cursor: pointer; }',
1743 \ 'input.FoldColumn[value=""] { cursor: default; }'
1744 \ ])
1745 +2
1746 endif
1747 " make line number column show as non-interactive if not selectable
1748 if s:settings.prevent_copy =~# 'n'
1749 call append('.', 'input.LineNr { cursor: default; }')
1750 +
1751 endif
1752 " make fold text and line number column within fold text show as
1753 " non-interactive if not selectable
1754 if (s:settings.prevent_copy =~# 'n' || s:settings.prevent_copy =~# 't') && !s:settings.ignore_folding
1755 call append('.', 'input.Folded { cursor: default; }')
1756 +
1757 endif
1758 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001759else
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001760 execute '%s:<body\([^>]*\):<body bgcolor="' . s:bgc . '" text="' . s:fgc . '"\1>\r<font face="'. s:htmlfont .'"'
Bram Moolenaar071d4272004-06-13 20:20:40 +00001761endif
1762
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001763" Gather attributes for all other classes. Do diff first so that normal
1764" highlight groups are inserted before it.
1765if s:settings.use_css
1766 if s:diff_mode
1767 call append('.', filter(map(keys(s:diffstylelist), "s:diffstylelist[v:val]"), 'v:val != ""'))
1768 endif
1769 if !empty(s:stylelist)
1770 call append('.', filter(map(keys(s:stylelist), "s:stylelist[v:val]"), 'v:val != ""'))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001771 endif
1772endif
1773
Bram Moolenaar071d4272004-06-13 20:20:40 +00001774" Add hyperlinks
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001775" TODO: add option to not do this? Maybe just make the color the same as the
1776" text highlight group normally is?
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001777%s+\(https\=://\S\{-}\)\(\([.,;:}]\=\(\s\|$\)\)\|[\\"'<>]\|&gt;\|&lt;\|&quot;\)+<a href="\1">\1</a>\2+ge
Bram Moolenaar071d4272004-06-13 20:20:40 +00001778
1779" The DTD
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001780if s:settings.use_xhtml
1781 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\">"
1782elseif s:settings.use_css && !s:settings.no_pre
1783 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 +00001784else
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001785 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 +00001786endif
1787
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001788if s:settings.use_xhtml
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001789 exe "normal! gg/<html/e\na xmlns=\"http://www.w3.org/1999/xhtml\"\e"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001790endif
1791
1792" Cleanup
1793%s:\s\+$::e
1794
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001795" Restore old settings (new window first)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001796"
1797" Don't bother restoring foldmethod in case it was syntax because the markup is
1798" so weirdly formatted it can take a LONG time.
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001799let &l:foldenable = s:old_fen
Bram Moolenaar071d4272004-06-13 20:20:40 +00001800let &report = s:old_report
1801let &title = s:old_title
1802let &icon = s:old_icon
1803let &paste = s:old_paste
1804let &magic = s:old_magic
1805let @/ = s:old_search
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001806let &more = s:old_more
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001807
1808" switch to original window to restore those settings
Bram Moolenaar071d4272004-06-13 20:20:40 +00001809exe s:orgwin . "wincmd w"
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001810
1811if !s:settings.expand_tabs
1812 let &l:isprint = s:old_isprint
1813endif
Bram Moolenaar166af9b2010-11-16 20:34:40 +01001814let &l:stl = s:origwin_stl
Bram Moolenaar071d4272004-06-13 20:20:40 +00001815let &l:et = s:old_et
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001816let &l:scrollbind = s:old_bind
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001817
1818" and back to the new window again to end there
Bram Moolenaar071d4272004-06-13 20:20:40 +00001819exe s:newwin . "wincmd w"
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001820
Bram Moolenaar166af9b2010-11-16 20:34:40 +01001821let &l:stl = s:newwin_stl
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001822exec 'resize' s:old_winheight
1823let &l:winfixheight = s:old_winfixheight
Bram Moolenaar071d4272004-06-13 20:20:40 +00001824
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001825let &ls=s:ls
1826
Bram Moolenaar071d4272004-06-13 20:20:40 +00001827" Save a little bit of memory (worth doing?)
Bram Moolenaar8e5af3e2011-04-28 19:02:44 +02001828unlet s:htmlfont s:whitespace
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001829unlet 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 +02001830unlet s:old_magic s:old_more s:old_fen s:old_winheight
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001831unlet! s:old_isprint
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001832unlet s:whatterm s:stylelist s:diffstylelist s:lnum s:end s:margin s:fgc s:bgc s:old_winfixheight
1833unlet! 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 +02001834unlet! s:orgwin s:newwin s:orgbufnr s:idx s:i s:offset s:ls s:origwin_stl
1835unlet! s:newwin_stl s:current_syntax
Bram Moolenaar05159a02005-02-26 23:04:13 +00001836if !v:profiling
1837 delfunc s:HtmlColor
1838 delfunc s:HtmlFormat
1839 delfunc s:CSS1
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001840 delfunc s:BuildStyleWrapper
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001841 if !s:settings.use_css
Bram Moolenaar05159a02005-02-26 23:04:13 +00001842 delfunc s:HtmlOpening
1843 delfunc s:HtmlClosing
1844 endif
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001845 if s:settings.dynamic_folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001846 delfunc s:FoldCompare
1847 endif
1848
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001849 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001850 delfunc s:ProgressBar
1851 delfunc s:progressbar.paint
1852 delfunc s:progressbar.incr
1853 unlet s:pgb s:progressbar
1854 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001855endif
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001856
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001857unlet! s:new_lnum s:diffattr s:difffillchar s:foldfillchar s:HtmlSpace
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001858unlet! s:LeadingSpace s:HtmlEndline s:firstfold s:numcol s:foldcolumn
1859unlet s:foldstack s:allfolds s:foldId s:settings
Bram Moolenaar5c736222010-01-06 20:54:52 +01001860
1861let &cpo = s:cpo_sav
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001862unlet! s:cpo_sav
Bram Moolenaar5c736222010-01-06 20:54:52 +01001863
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001864" Make sure any patches will probably use consistent indent
Bram Moolenaar7c86f4c2010-07-18 14:07:22 +02001865" vim: ts=8 sw=2 sts=2 noet