xref: /vim-8.2.3635/runtime/autoload/tohtml.vim (revision fcfe1a9b)
1" Vim autoload file for the tohtml plugin.
2" Maintainer: Ben Fritz <[email protected]>
3" Last Change: 2018 Nov 11
4"
5" Additional contributors:
6"
7"	      Original by Bram Moolenaar <[email protected]>
8"	      Diff2HTML() added by Christian Brabandt <[email protected]>
9"
10"	      See Mercurial change logs for more!
11
12" this file uses line continuations
13let s:cpo_sav = &cpo
14set cpo&vim
15
16" Automatically find charsets from all encodings supported natively by Vim. With
17" the 8bit- and 2byte- prefixes, Vim can actually support more encodings than
18" this. Let the user specify these however since they won't be supported on
19" every system.
20"
21" Note, not all of Vim's supported encodings have a charset to use.
22"
23" Names in this list are from:
24"   http://www.iana.org/assignments/character-sets
25" g:tohtml#encoding_to_charset: {{{
26let g:tohtml#encoding_to_charset = {
27      \ 'latin1' : 'ISO-8859-1',
28      \ 'iso-8859-2' : 'ISO-8859-2',
29      \ 'iso-8859-3' : 'ISO-8859-3',
30      \ 'iso-8859-4' : 'ISO-8859-4',
31      \ 'iso-8859-5' : 'ISO-8859-5',
32      \ 'iso-8859-6' : 'ISO-8859-6',
33      \ 'iso-8859-7' : 'ISO-8859-7',
34      \ 'iso-8859-8' : 'ISO-8859-8',
35      \ 'iso-8859-9' : 'ISO-8859-9',
36      \ 'iso-8859-10' : '',
37      \ 'iso-8859-13' : 'ISO-8859-13',
38      \ 'iso-8859-14' : '',
39      \ 'iso-8859-15' : 'ISO-8859-15',
40      \ 'koi8-r' : 'KOI8-R',
41      \ 'koi8-u' : 'KOI8-U',
42      \ 'macroman' : 'macintosh',
43      \ 'cp437' : '',
44      \ 'cp775' : '',
45      \ 'cp850' : '',
46      \ 'cp852' : '',
47      \ 'cp855' : '',
48      \ 'cp857' : '',
49      \ 'cp860' : '',
50      \ 'cp861' : '',
51      \ 'cp862' : '',
52      \ 'cp863' : '',
53      \ 'cp865' : '',
54      \ 'cp866' : 'IBM866',
55      \ 'cp869' : '',
56      \ 'cp874' : '',
57      \ 'cp1250' : 'windows-1250',
58      \ 'cp1251' : 'windows-1251',
59      \ 'cp1253' : 'windows-1253',
60      \ 'cp1254' : 'windows-1254',
61      \ 'cp1255' : 'windows-1255',
62      \ 'cp1256' : 'windows-1256',
63      \ 'cp1257' : 'windows-1257',
64      \ 'cp1258' : 'windows-1258',
65      \ 'euc-jp' : 'EUC-JP',
66      \ 'sjis' : 'Shift_JIS',
67      \ 'cp932' : 'Shift_JIS',
68      \ 'cp949' : '',
69      \ 'euc-kr' : 'EUC-KR',
70      \ 'cp936' : 'GBK',
71      \ 'euc-cn' : 'GB2312',
72      \ 'big5' : 'Big5',
73      \ 'cp950' : 'Big5',
74      \ 'utf-8' : 'UTF-8',
75      \ 'ucs-2' : 'UTF-8',
76      \ 'ucs-2le' : 'UTF-8',
77      \ 'utf-16' : 'UTF-8',
78      \ 'utf-16le' : 'UTF-8',
79      \ 'ucs-4' : 'UTF-8',
80      \ 'ucs-4le' : 'UTF-8',
81      \ }
82lockvar g:tohtml#encoding_to_charset
83" Notes:
84"   1. All UCS/UTF are converted to UTF-8 because it is much better supported
85"   2. Any blank spaces are there because Vim supports it but at least one major
86"      web browser does not according to http://wiki.whatwg.org/wiki/Web_Encodings.
87" }}}
88
89" Only automatically find encodings supported natively by Vim, let the user
90" specify the encoding if it's not natively supported. This function is only
91" used when the user specifies the charset, they better know what they are
92" doing!
93"
94" Names in this list are from:
95"   http://www.iana.org/assignments/character-sets
96" g:tohtml#charset_to_encoding: {{{
97let g:tohtml#charset_to_encoding = {
98      \ 'iso_8859-1:1987' : 'latin1',
99      \ 'iso-ir-100' : 'latin1',
100      \ 'iso_8859-1' : 'latin1',
101      \ 'iso-8859-1' : 'latin1',
102      \ 'latin1' : 'latin1',
103      \ 'l1' : 'latin1',
104      \ 'ibm819' : 'latin1',
105      \ 'cp819' : 'latin1',
106      \ 'csisolatin1' : 'latin1',
107      \ 'iso_8859-2:1987' : 'iso-8859-2',
108      \ 'iso-ir-101' : 'iso-8859-2',
109      \ 'iso_8859-2' : 'iso-8859-2',
110      \ 'iso-8859-2' : 'iso-8859-2',
111      \ 'latin2' : 'iso-8859-2',
112      \ 'l2' : 'iso-8859-2',
113      \ 'csisolatin2' : 'iso-8859-2',
114      \ 'iso_8859-3:1988' : 'iso-8859-3',
115      \ 'iso-ir-109' : 'iso-8859-3',
116      \ 'iso_8859-3' : 'iso-8859-3',
117      \ 'iso-8859-3' : 'iso-8859-3',
118      \ 'latin3' : 'iso-8859-3',
119      \ 'l3' : 'iso-8859-3',
120      \ 'csisolatin3' : 'iso-8859-3',
121      \ 'iso_8859-4:1988' : 'iso-8859-4',
122      \ 'iso-ir-110' : 'iso-8859-4',
123      \ 'iso_8859-4' : 'iso-8859-4',
124      \ 'iso-8859-4' : 'iso-8859-4',
125      \ 'latin4' : 'iso-8859-4',
126      \ 'l4' : 'iso-8859-4',
127      \ 'csisolatin4' : 'iso-8859-4',
128      \ 'iso_8859-5:1988' : 'iso-8859-5',
129      \ 'iso-ir-144' : 'iso-8859-5',
130      \ 'iso_8859-5' : 'iso-8859-5',
131      \ 'iso-8859-5' : 'iso-8859-5',
132      \ 'cyrillic' : 'iso-8859-5',
133      \ 'csisolatincyrillic' : 'iso-8859-5',
134      \ 'iso_8859-6:1987' : 'iso-8859-6',
135      \ 'iso-ir-127' : 'iso-8859-6',
136      \ 'iso_8859-6' : 'iso-8859-6',
137      \ 'iso-8859-6' : 'iso-8859-6',
138      \ 'ecma-114' : 'iso-8859-6',
139      \ 'asmo-708' : 'iso-8859-6',
140      \ 'arabic' : 'iso-8859-6',
141      \ 'csisolatinarabic' : 'iso-8859-6',
142      \ 'iso_8859-7:1987' : 'iso-8859-7',
143      \ 'iso-ir-126' : 'iso-8859-7',
144      \ 'iso_8859-7' : 'iso-8859-7',
145      \ 'iso-8859-7' : 'iso-8859-7',
146      \ 'elot_928' : 'iso-8859-7',
147      \ 'ecma-118' : 'iso-8859-7',
148      \ 'greek' : 'iso-8859-7',
149      \ 'greek8' : 'iso-8859-7',
150      \ 'csisolatingreek' : 'iso-8859-7',
151      \ 'iso_8859-8:1988' : 'iso-8859-8',
152      \ 'iso-ir-138' : 'iso-8859-8',
153      \ 'iso_8859-8' : 'iso-8859-8',
154      \ 'iso-8859-8' : 'iso-8859-8',
155      \ 'hebrew' : 'iso-8859-8',
156      \ 'csisolatinhebrew' : 'iso-8859-8',
157      \ 'iso_8859-9:1989' : 'iso-8859-9',
158      \ 'iso-ir-148' : 'iso-8859-9',
159      \ 'iso_8859-9' : 'iso-8859-9',
160      \ 'iso-8859-9' : 'iso-8859-9',
161      \ 'latin5' : 'iso-8859-9',
162      \ 'l5' : 'iso-8859-9',
163      \ 'csisolatin5' : 'iso-8859-9',
164      \ 'iso-8859-10' : 'iso-8859-10',
165      \ 'iso-ir-157' : 'iso-8859-10',
166      \ 'l6' : 'iso-8859-10',
167      \ 'iso_8859-10:1992' : 'iso-8859-10',
168      \ 'csisolatin6' : 'iso-8859-10',
169      \ 'latin6' : 'iso-8859-10',
170      \ 'iso-8859-13' : 'iso-8859-13',
171      \ 'iso-8859-14' : 'iso-8859-14',
172      \ 'iso-ir-199' : 'iso-8859-14',
173      \ 'iso_8859-14:1998' : 'iso-8859-14',
174      \ 'iso_8859-14' : 'iso-8859-14',
175      \ 'latin8' : 'iso-8859-14',
176      \ 'iso-celtic' : 'iso-8859-14',
177      \ 'l8' : 'iso-8859-14',
178      \ 'iso-8859-15' : 'iso-8859-15',
179      \ 'iso_8859-15' : 'iso-8859-15',
180      \ 'latin-9' : 'iso-8859-15',
181      \ 'koi8-r' : 'koi8-r',
182      \ 'cskoi8r' : 'koi8-r',
183      \ 'koi8-u' : 'koi8-u',
184      \ 'macintosh' : 'macroman',
185      \ 'mac' : 'macroman',
186      \ 'csmacintosh' : 'macroman',
187      \ 'ibm437' : 'cp437',
188      \ 'cp437' : 'cp437',
189      \ '437' : 'cp437',
190      \ 'cspc8codepage437' : 'cp437',
191      \ 'ibm775' : 'cp775',
192      \ 'cp775' : 'cp775',
193      \ 'cspc775baltic' : 'cp775',
194      \ 'ibm850' : 'cp850',
195      \ 'cp850' : 'cp850',
196      \ '850' : 'cp850',
197      \ 'cspc850multilingual' : 'cp850',
198      \ 'ibm852' : 'cp852',
199      \ 'cp852' : 'cp852',
200      \ '852' : 'cp852',
201      \ 'cspcp852' : 'cp852',
202      \ 'ibm855' : 'cp855',
203      \ 'cp855' : 'cp855',
204      \ '855' : 'cp855',
205      \ 'csibm855' : 'cp855',
206      \ 'ibm857' : 'cp857',
207      \ 'cp857' : 'cp857',
208      \ '857' : 'cp857',
209      \ 'csibm857' : 'cp857',
210      \ 'ibm860' : 'cp860',
211      \ 'cp860' : 'cp860',
212      \ '860' : 'cp860',
213      \ 'csibm860' : 'cp860',
214      \ 'ibm861' : 'cp861',
215      \ 'cp861' : 'cp861',
216      \ '861' : 'cp861',
217      \ 'cp-is' : 'cp861',
218      \ 'csibm861' : 'cp861',
219      \ 'ibm862' : 'cp862',
220      \ 'cp862' : 'cp862',
221      \ '862' : 'cp862',
222      \ 'cspc862latinhebrew' : 'cp862',
223      \ 'ibm863' : 'cp863',
224      \ 'cp863' : 'cp863',
225      \ '863' : 'cp863',
226      \ 'csibm863' : 'cp863',
227      \ 'ibm865' : 'cp865',
228      \ 'cp865' : 'cp865',
229      \ '865' : 'cp865',
230      \ 'csibm865' : 'cp865',
231      \ 'ibm866' : 'cp866',
232      \ 'cp866' : 'cp866',
233      \ '866' : 'cp866',
234      \ 'csibm866' : 'cp866',
235      \ 'ibm869' : 'cp869',
236      \ 'cp869' : 'cp869',
237      \ '869' : 'cp869',
238      \ 'cp-gr' : 'cp869',
239      \ 'csibm869' : 'cp869',
240      \ 'windows-1250' : 'cp1250',
241      \ 'windows-1251' : 'cp1251',
242      \ 'windows-1253' : 'cp1253',
243      \ 'windows-1254' : 'cp1254',
244      \ 'windows-1255' : 'cp1255',
245      \ 'windows-1256' : 'cp1256',
246      \ 'windows-1257' : 'cp1257',
247      \ 'windows-1258' : 'cp1258',
248      \ 'extended_unix_code_packed_format_for_japanese' : 'euc-jp',
249      \ 'cseucpkdfmtjapanese' : 'euc-jp',
250      \ 'euc-jp' : 'euc-jp',
251      \ 'shift_jis' : 'sjis',
252      \ 'ms_kanji' : 'sjis',
253      \ 'sjis' : 'sjis',
254      \ 'csshiftjis' : 'sjis',
255      \ 'ibm-thai' : 'cp874',
256      \ 'csibmthai' : 'cp874',
257      \ 'ks_c_5601-1987' : 'cp949',
258      \ 'iso-ir-149' : 'cp949',
259      \ 'ks_c_5601-1989' : 'cp949',
260      \ 'ksc_5601' : 'cp949',
261      \ 'korean' : 'cp949',
262      \ 'csksc56011987' : 'cp949',
263      \ 'euc-kr' : 'euc-kr',
264      \ 'cseuckr' : 'euc-kr',
265      \ 'gbk' : 'cp936',
266      \ 'cp936' : 'cp936',
267      \ 'ms936' : 'cp936',
268      \ 'windows-936' : 'cp936',
269      \ 'gb_2312-80' : 'euc-cn',
270      \ 'iso-ir-58' : 'euc-cn',
271      \ 'chinese' : 'euc-cn',
272      \ 'csiso58gb231280' : 'euc-cn',
273      \ 'big5' : 'big5',
274      \ 'csbig5' : 'big5',
275      \ 'utf-8' : 'utf-8',
276      \ 'iso-10646-ucs-2' : 'ucs-2',
277      \ 'csunicode' : 'ucs-2',
278      \ 'utf-16' : 'utf-16',
279      \ 'utf-16be' : 'utf-16',
280      \ 'utf-16le' : 'utf-16le',
281      \ 'utf-32' : 'ucs-4',
282      \ 'utf-32be' : 'ucs-4',
283      \ 'utf-32le' : 'ucs-4le',
284      \ 'iso-10646-ucs-4' : 'ucs-4',
285      \ 'csucs4' : 'ucs-4'
286      \ }
287lockvar g:tohtml#charset_to_encoding
288"}}}
289
290func! tohtml#Convert2HTML(line1, line2) "{{{
291  let s:settings = tohtml#GetUserSettings()
292
293  if !&diff || s:settings.diff_one_file "{{{
294    if a:line2 >= a:line1
295      let g:html_start_line = a:line1
296      let g:html_end_line = a:line2
297    else
298      let g:html_start_line = a:line2
299      let g:html_end_line = a:line1
300    endif
301    runtime syntax/2html.vim "}}}
302  else "{{{
303    let win_list = []
304    let buf_list = []
305    windo if &diff | call add(win_list, winbufnr(0)) | endif
306    let s:settings.whole_filler = 1
307    let g:html_diff_win_num = 0
308    for window in win_list
309      " switch to the next buffer to convert
310      exe ":" . bufwinnr(window) . "wincmd w"
311
312      " figure out whether current charset and encoding will work, if not
313      " default to UTF-8
314      if !exists('g:html_use_encoding') &&
315	    \ (((&l:fileencoding=='' || (&l:buftype!='' && &l:buftype!=?'help'))
316	    \      && &encoding!=?s:settings.vim_encoding)
317	    \ || &l:fileencoding!='' && &l:fileencoding!=?s:settings.vim_encoding)
318	echohl WarningMsg
319	echomsg "TOhtml: mismatched file encodings in Diff buffers, using UTF-8"
320	echohl None
321	let s:settings.vim_encoding = 'utf-8'
322	let s:settings.encoding = 'UTF-8'
323      endif
324
325      " set up for diff-mode conversion
326      let g:html_start_line = 1
327      let g:html_end_line = line('$')
328      let g:html_diff_win_num += 1
329
330      " convert this file
331      runtime syntax/2html.vim
332
333      " remember the HTML buffer for later combination
334      call add(buf_list, bufnr('%'))
335    endfor
336    unlet g:html_diff_win_num
337    call tohtml#Diff2HTML(win_list, buf_list)
338  endif "}}}
339
340  unlet g:html_start_line
341  unlet g:html_end_line
342  unlet s:settings
343endfunc "}}}
344
345func! tohtml#Diff2HTML(win_list, buf_list) "{{{
346  let xml_line = ""
347  let tag_close = '>'
348
349  let s:old_paste = &paste
350  set paste
351  let s:old_magic = &magic
352  set magic
353
354  if s:settings.use_xhtml
355    if s:settings.encoding != ""
356      let xml_line = "<?xml version=\"1.0\" encoding=\"" . s:settings.encoding . "\"?>"
357    else
358      let xml_line = "<?xml version=\"1.0\"?>"
359    endif
360    let tag_close = ' />'
361  endif
362
363  let style = [s:settings.use_xhtml ? "" : '-->']
364  let body_line = ''
365
366  let html = []
367  if s:settings.use_xhtml
368    call add(html, xml_line)
369  endif
370  if s:settings.use_xhtml
371    call add(html, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">")
372    call add(html, '<html xmlns="http://www.w3.org/1999/xhtml">')
373  elseif s:settings.use_css && !s:settings.no_pre
374    call add(html, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">")
375    call add(html, '<html>')
376  else
377    call add(html, '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"')
378    call add(html, '  "http://www.w3.org/TR/html4/loose.dtd">')
379    call add(html, '<html>')
380  endif
381  call add(html, '<head>')
382
383  " include encoding as close to the top as possible, but only if not already
384  " contained in XML information
385  if s:settings.encoding != "" && !s:settings.use_xhtml
386    call add(html, "<meta http-equiv=\"content-type\" content=\"text/html; charset=" . s:settings.encoding . '"' . tag_close)
387  endif
388
389  call add(html, '<title>diff</title>')
390  call add(html, '<meta name="Generator" content="Vim/'.v:version/100.'.'.v:version%100.'"'.tag_close)
391  call add(html, '<meta name="plugin-version" content="'.g:loaded_2html_plugin.'"'.tag_close)
392  call add(html, '<meta name="settings" content="'.
393	\ join(filter(keys(s:settings),'s:settings[v:val]'),',').
394	\ ',prevent_copy='.s:settings.prevent_copy.
395	\ '"'.tag_close)
396  call add(html, '<meta name="colorscheme" content="'.
397	\ (exists('g:colors_name')
398	\ ? g:colors_name
399	\ : 'none'). '"'.tag_close)
400
401  call add(html, '</head>')
402  let body_line_num = len(html)
403  if !empty(s:settings.prevent_copy)
404    call add(html, "<body onload='FixCharWidth();".(s:settings.line_ids ? " JumpToLine();" : "")."'>")
405    call add(html, "<!-- hidden divs used by javascript to get the width of a char -->")
406    call add(html, "<div id='oneCharWidth'>0</div>")
407    call add(html, "<div id='oneInputWidth'><input size='1' value='0'".tag_close."</div>")
408    call add(html, "<div id='oneEmWidth' style='width: 1em;'></div>")
409  else
410    call add(html, '<body'.(s:settings.line_ids ? ' onload="JumpToLine();"' : '').'>')
411  endif
412  call add(html, "<table border='1' width='100%' id='vimCodeElement".s:settings.id_suffix."'>")
413
414  call add(html, '<tr>')
415  for buf in a:win_list
416    call add(html, '<th>'.bufname(buf).'</th>')
417  endfor
418  call add(html, '</tr><tr>')
419
420  let diff_style_start = 0
421  let insert_index = 0
422
423  for buf in a:buf_list
424    let temp = []
425    exe bufwinnr(buf) . 'wincmd w'
426
427    " If text is folded because of user foldmethod settings, etc. we don't want
428    " to act on everything in a fold by mistake.
429    setlocal nofoldenable
430
431    " When not using CSS or when using xhtml, the <body> line can be important.
432    " Assume it will be the same for all buffers and grab it from the first
433    " buffer. Similarly, need to grab the body end line as well.
434    if body_line == ''
435      1
436      call search('<body')
437      let body_line = getline('.')
438      $
439      call search('</body>', 'b')
440      let s:body_end_line = getline('.')
441    endif
442
443    " Grab the style information. Some of this will be duplicated so only insert
444    " it if it's not already there. {{{
445    1
446    let style_start = search('^<style type="text/css">')
447    1
448    let style_end = search('^</style>')
449    if style_start > 0 && style_end > 0
450      let buf_styles = getline(style_start + 1, style_end - 1)
451      for a_style in buf_styles
452	if index(style, a_style) == -1
453	  if diff_style_start == 0
454	    if a_style =~ '\<Diff\(Change\|Text\|Add\|Delete\)'
455	      let diff_style_start = len(style)-1
456	    endif
457	  endif
458	  call insert(style, a_style, insert_index)
459	  let insert_index += 1
460	endif
461      endfor
462    endif " }}}
463
464    " everything new will get added before the diff styles so diff highlight
465    " properly overrides normal highlight
466    if diff_style_start != 0
467      let insert_index = diff_style_start
468    endif
469
470    " Delete those parts that are not needed so we can include the rest into the
471    " resulting table.
472    1,/^<body.*\%(\n<!--.*-->\_s\+.*id='oneCharWidth'.*\_s\+.*id='oneInputWidth'.*\_s\+.*id='oneEmWidth'\)\?\zs/d_
473    $
474    ?</body>?,$d_
475    let temp = getline(1,'$')
476    " clean out id on the main content container because we already set it on
477    " the table
478    let temp[0] = substitute(temp[0], " id='vimCodeElement[^']*'", "", "")
479    " undo deletion of start and end part
480    " so we can later save the file as valid html
481    " TODO: restore using grabbed lines if undolevel is 1?
482    normal! 2u
483    if s:settings.use_css
484      call add(html, '<td valign="top"><div>')
485    elseif s:settings.use_xhtml
486      call add(html, '<td nowrap="nowrap" valign="top"><div>')
487    else
488      call add(html, '<td nowrap valign="top"><div>')
489    endif
490    let html += temp
491    call add(html, '</div></td>')
492
493    " Close this buffer
494    " TODO: the comment above says we're going to allow saving the file
495    " later...but here we discard it?
496    quit!
497  endfor
498
499  let html[body_line_num] = body_line
500
501  call add(html, '</tr>')
502  call add(html, '</table>')
503  call add(html, s:body_end_line)
504  call add(html, '</html>')
505
506  " The generated HTML is admittedly ugly and takes a LONG time to fold.
507  " Make sure the user doesn't do syntax folding when loading a generated file,
508  " using a modeline.
509  call add(html, '<!-- vim: set foldmethod=manual : -->')
510
511  let i = 1
512  let name = "Diff" . (s:settings.use_xhtml ? ".xhtml" : ".html")
513  " Find an unused file name if current file name is already in use
514  while filereadable(name)
515    let name = substitute(name, '\d*\.x\?html$', '', '') . i . '.' . fnamemodify(copy(name), ":t:e")
516    let i += 1
517  endwhile
518  exe "topleft new " . name
519  setlocal modifiable
520
521  " just in case some user autocmd creates content in the new buffer, make sure
522  " it is empty before proceeding
523  %d
524
525  " set the fileencoding to match the charset we'll be using
526  let &l:fileencoding=s:settings.vim_encoding
527
528  " According to http://www.w3.org/TR/html4/charset.html#doc-char-set, the byte
529  " order mark is highly recommend on the web when using multibyte encodings. But,
530  " it is not a good idea to include it on UTF-8 files. Otherwise, let Vim
531  " determine when it is actually inserted.
532  if s:settings.vim_encoding == 'utf-8'
533    setlocal nobomb
534  else
535    setlocal bomb
536  endif
537
538  call append(0, html)
539
540  if len(style) > 0
541    1
542    let style_start = search('^</head>')-1
543
544    " add required javascript in reverse order so we can just call append again
545    " and again without adjusting {{{
546
547    let s:uses_script = s:settings.dynamic_folds || s:settings.line_ids || !empty(s:settings.prevent_copy)
548
549    " insert script closing tag if needed
550    if s:uses_script
551      call append(style_start, [
552	    \ '',
553	    \ s:settings.use_xhtml ? '//]]>' : '-->',
554	    \ "</script>"
555	    \ ])
556    endif
557
558    " insert script which corrects the size of small input elements in
559    " prevent_copy mode. See 2html.vim for details on why this is needed and how
560    " it works.
561    if !empty(s:settings.prevent_copy)
562      call append(style_start, [
563	    \ '',
564	    \ '/* simulate a "ch" unit by asking the browser how big a zero character is */',
565	    \ 'function FixCharWidth() {',
566	    \ '  /* get the hidden element which gives the width of a single character */',
567	    \ '  var goodWidth = document.getElementById("oneCharWidth").clientWidth;',
568	    \ '  /* get all input elements, we''ll filter on class later */',
569	    \ '  var inputTags = document.getElementsByTagName("input");',
570	    \ '  var ratio = 5;',
571	    \ '  var inputWidth = document.getElementById("oneInputWidth").clientWidth;',
572	    \ '  var emWidth = document.getElementById("oneEmWidth").clientWidth;',
573	    \ '  if (inputWidth > goodWidth) {',
574	    \ '    while (ratio < 100*goodWidth/emWidth && ratio < 100) {',
575	    \ '      ratio += 5;',
576	    \ '    }',
577	    \ '    document.getElementById("vimCodeElement'.s:settings.id_suffix.'").className = "em"+ratio;',
578	    \ '  }',
579	    \ '}'
580	    \ ])
581    endif
582
583    " insert javascript to get IDs from line numbers, and to open a fold before
584    " jumping to any lines contained therein
585    if s:settings.line_ids
586      call append(style_start, [
587	    \ "  /* Always jump to new location even if the line was hidden inside a fold, or",
588	    \ "   * we corrected the raw number to a line ID.",
589	    \ "   */",
590	    \ "  if (lineElem) {",
591	    \ "    lineElem.scrollIntoView(true);",
592	    \ "  }",
593	    \ "  return true;",
594	    \ "}",
595	    \ "if ('onhashchange' in window) {",
596	    \ "  window.onhashchange = JumpToLine;",
597	    \ "}"
598	    \ ])
599
600      if s:settings.dynamic_folds
601	call append(style_start, [
602	      \ "",
603	      \ "  /* navigate upwards in the DOM tree to open all folds containing the line */",
604	      \ "  var node = lineElem;",
605	      \ "  while (node && node.id != 'vimCodeElement".s:settings.id_suffix."')",
606	      \ "  {",
607	      \ "    if (node.className == 'closed-fold')",
608	      \ "    {",
609	      \ "      /* toggle open the fold ID (remove window ID) */",
610	      \ "      toggleFold(node.id.substr(4));",
611	      \ "    }",
612	      \ "    node = node.parentNode;",
613	      \ "  }",
614	      \ ])
615      endif
616    endif
617
618    if s:settings.line_ids
619      call append(style_start, [
620	    \ "",
621	    \ "/* function to open any folds containing a jumped-to line before jumping to it */",
622	    \ "function JumpToLine()",
623	    \ "{",
624	    \ "  var lineNum;",
625	    \ "  lineNum = window.location.hash;",
626	    \ "  lineNum = lineNum.substr(1); /* strip off '#' */",
627	    \ "",
628	    \ "  if (lineNum.indexOf('L') == -1) {",
629	    \ "    lineNum = 'L'+lineNum;",
630	    \ "  }",
631	    \ "  if (lineNum.indexOf('W') == -1) {",
632	    \ "    lineNum = 'W1'+lineNum;",
633	    \ "  }",
634	    \ "  var lineElem = document.getElementById(lineNum);"
635	    \ ])
636    endif
637
638    " Insert javascript to toggle matching folds open and closed in all windows,
639    " if dynamic folding is active.
640    if s:settings.dynamic_folds
641      call append(style_start, [
642	    \  "  function toggleFold(objID)",
643	    \  "  {",
644	    \  "    for (win_num = 1; win_num <= ".len(a:buf_list)."; win_num++)",
645	    \  "    {",
646	    \  "      var fold;",
647	    \  '      fold = document.getElementById("win"+win_num+objID);',
648	    \  "      if(fold.className == 'closed-fold')",
649	    \  "      {",
650	    \  "        fold.className = 'open-fold';",
651	    \  "      }",
652	    \  "      else if (fold.className == 'open-fold')",
653	    \  "      {",
654	    \  "        fold.className = 'closed-fold';",
655	    \  "      }",
656	    \  "    }",
657	    \  "  }",
658	    \ ])
659    endif
660
661    if s:uses_script
662      " insert script tag; javascript is always needed for the line number
663      " normalization for URL hashes
664      call append(style_start, [
665	    \ "<script type='text/javascript'>",
666	    \ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"])
667    endif
668
669    " Insert styles from all the generated html documents and additional styles
670    " for the table-based layout of the side-by-side diff. The diff should take
671    " up the full browser window (but not more), and be static in size,
672    " horizontally scrollable when the lines are too long. Otherwise, the diff
673    " is pretty useless for really long lines. {{{
674    if s:settings.use_css
675      call append(style_start,
676	    \ ['<style type="text/css">']+
677	    \ style+
678	    \ [ s:settings.use_xhtml ? '' : '<!--',
679	    \   'table { table-layout: fixed; }',
680	    \   'html, body, table, tbody { width: 100%; margin: 0; padding: 0; }',
681	    \   'th, td { width: '.printf("%.1f",100.0/len(a:win_list)).'%; }',
682	    \   'td div { overflow: auto; }',
683	    \   s:settings.use_xhtml ? '' : '-->',
684	    \   '</style>'
685	    \])
686    endif "}}}
687  endif
688
689  let &paste = s:old_paste
690  let &magic = s:old_magic
691endfunc "}}}
692
693" Gets a single user option and sets it in the passed-in Dict, or gives it the
694" default value if the option doesn't actually exist.
695func! tohtml#GetOption(settings, option, default) "{{{
696  if exists('g:html_'.a:option)
697    let a:settings[a:option] = g:html_{a:option}
698  else
699    let a:settings[a:option] = a:default
700  endif
701endfunc "}}}
702
703" returns a Dict containing the values of all user options for 2html, including
704" default values for those not given an explicit value by the user. Discards the
705" html_ prefix of the option for nicer looking code.
706func! tohtml#GetUserSettings() "{{{
707  if exists('s:settings')
708    " just restore the known options if we've already retrieved them
709    return s:settings
710  else
711    " otherwise figure out which options are set
712    let user_settings = {}
713
714    " Define the correct option if the old option name exists and we haven't
715    " already defined the correct one. Maybe I'll put out a warnig message about
716    " this sometime and remove the old option entirely at some even later time,
717    " but for now just silently accept the old option.
718    if exists('g:use_xhtml') && !exists("g:html_use_xhtml")
719      let g:html_use_xhtml = g:use_xhtml
720    endif
721
722    " get current option settings with appropriate defaults {{{
723    call tohtml#GetOption(user_settings,    'no_progress', !has("statusline") )
724    call tohtml#GetOption(user_settings,  'diff_one_file', 0 )
725    call tohtml#GetOption(user_settings,   'number_lines', &number )
726    call tohtml#GetOption(user_settings,       'pre_wrap', &wrap )
727    call tohtml#GetOption(user_settings,        'use_css', 1 )
728    call tohtml#GetOption(user_settings, 'ignore_conceal', 0 )
729    call tohtml#GetOption(user_settings, 'ignore_folding', 0 )
730    call tohtml#GetOption(user_settings,  'dynamic_folds', 0 )
731    call tohtml#GetOption(user_settings,  'no_foldcolumn', user_settings.ignore_folding)
732    call tohtml#GetOption(user_settings,   'hover_unfold', 0 )
733    call tohtml#GetOption(user_settings,         'no_pre', 0 )
734    call tohtml#GetOption(user_settings,     'no_invalid', 0 )
735    call tohtml#GetOption(user_settings,   'whole_filler', 0 )
736    call tohtml#GetOption(user_settings,      'use_xhtml', 0 )
737    call tohtml#GetOption(user_settings,       'line_ids', user_settings.number_lines )
738    " }}}
739
740    " override those settings that need it {{{
741
742    " hover opening implies dynamic folding
743    if user_settings.hover_unfold
744      let user_settings.dynamic_folds = 1
745    endif
746
747    " ignore folding overrides dynamic folding
748    if user_settings.ignore_folding && user_settings.dynamic_folds
749      let user_settings.dynamic_folds = 0
750      let user_settings.hover_unfold = 0
751    endif
752
753    " dynamic folding with no foldcolumn implies hover opens
754    if user_settings.dynamic_folds && user_settings.no_foldcolumn
755      let user_settings.hover_unfold = 1
756    endif
757
758    " dynamic folding implies css
759    if user_settings.dynamic_folds
760      let user_settings.use_css = 1
761    else
762      let user_settings.no_foldcolumn = 1 " won't do anything but for consistency and for the test suite
763    endif
764
765    " if we're not using CSS we cannot use a pre section because <font> tags
766    " aren't allowed inside a <pre> block
767    if !user_settings.use_css
768      let user_settings.no_pre = 1
769    endif
770
771    " pre_wrap doesn't do anything if not using pre or not using CSS
772    if user_settings.no_pre || !user_settings.use_css
773      let user_settings.pre_wrap=0
774    endif
775    "}}}
776
777    " set up expand_tabs option after all the overrides so we know the
778    " appropriate defaults {{{
779    if user_settings.no_pre == 0
780      call tohtml#GetOption(user_settings,
781	    \ 'expand_tabs',
782	    \ &expandtab || &ts != 8 || &vts != '' || user_settings.number_lines ||
783	    \   (user_settings.dynamic_folds && !user_settings.no_foldcolumn))
784    else
785      let user_settings.expand_tabs = 1
786    endif
787    " }}}
788
789    " textual options
790    if exists("g:html_use_encoding") "{{{
791      " user specified the desired MIME charset, figure out proper
792      " 'fileencoding' from it or warn the user if we cannot
793      let user_settings.encoding = g:html_use_encoding
794      let user_settings.vim_encoding = tohtml#EncodingFromCharset(g:html_use_encoding)
795      if user_settings.vim_encoding == ''
796	echohl WarningMsg
797	echomsg "TOhtml: file encoding for"
798	      \ g:html_use_encoding
799	      \ "unknown, please set 'fileencoding'"
800	echohl None
801      endif
802    else
803      " Figure out proper MIME charset from 'fileencoding' if possible
804      if &l:fileencoding != ''
805	" If the buffer is not a "normal" type, the 'fileencoding' value may not
806	" be trusted; since the buffer should not be written the fileencoding is
807	" not intended to be used.
808	if &l:buftype=='' || &l:buftype==?'help'
809	  let user_settings.vim_encoding = &l:fileencoding
810	  call tohtml#CharsetFromEncoding(user_settings)
811	else
812	  let user_settings.encoding = '' " trigger detection using &encoding
813	endif
814      endif
815
816      " else from 'encoding' if possible
817      if &l:fileencoding == '' || user_settings.encoding == ''
818	let user_settings.vim_encoding = &encoding
819	call tohtml#CharsetFromEncoding(user_settings)
820      endif
821
822      " else default to UTF-8 and warn user
823      if user_settings.encoding == ''
824	let user_settings.vim_encoding = 'utf-8'
825	let user_settings.encoding = 'UTF-8'
826	echohl WarningMsg
827	echomsg "TOhtml: couldn't determine MIME charset, using UTF-8"
828	echohl None
829      endif
830    endif "}}}
831
832    " Default to making nothing uncopyable, because we default to
833    " not-standards way of doing things, and also because Microsoft Word and
834    " others paste the <input> elements anyway.
835    "
836    " html_prevent_copy only has an effect when using CSS.
837    "
838    " All options:
839    "	  f - fold column
840    "	  n - line numbers (also within fold text)
841    "	  t - fold text
842    "	  d - diff filler
843    "	  c - concealed text (reserved future)
844    "	  l - listchars (reserved possible future)
845    "	  s - signs (reserved possible future)
846    "
847    " Normal text is always selectable.
848    let user_settings.prevent_copy = ""
849    if user_settings.use_css
850      if exists("g:html_prevent_copy")
851	if user_settings.dynamic_folds && !user_settings.no_foldcolumn && g:html_prevent_copy =~# 'f'
852	  let user_settings.prevent_copy .= 'f'
853	endif
854	if user_settings.number_lines && g:html_prevent_copy =~# 'n'
855	  let user_settings.prevent_copy .= 'n'
856	endif
857	if &diff && g:html_prevent_copy =~# 'd'
858	  let user_settings.prevent_copy .= 'd'
859	endif
860	if !user_settings.ignore_folding && g:html_prevent_copy =~# 't'
861	  let user_settings.prevent_copy .= 't'
862	endif
863      else
864	let user_settings.prevent_copy = ""
865      endif
866    endif
867    if empty(user_settings.prevent_copy)
868      let user_settings.no_invalid = 0
869    endif
870
871    if exists('g:html_id_expr')
872      let user_settings.id_suffix = eval(g:html_id_expr)
873      if user_settings.id_suffix !~ '^[-_:.A-Za-z0-9]*$'
874	echohl WarningMsg
875	echomsg '2html: g:html_id_expr evaluated to invalid string for HTML id attributes'
876	echomsg '2html: Omitting user-specified suffix'
877	echohl None
878	sleep 3
879	let user_settings.id_suffix=""
880      endif
881    else
882      let user_settings.id_suffix=""
883    endif
884
885    " TODO: font
886
887    return user_settings
888  endif
889endfunc "}}}
890
891" get the proper HTML charset name from a Vim encoding option.
892function! tohtml#CharsetFromEncoding(settings) "{{{
893  let l:vim_encoding = a:settings.vim_encoding
894  if exists('g:html_charset_override') && has_key(g:html_charset_override, l:vim_encoding)
895    let a:settings.encoding = g:html_charset_override[l:vim_encoding]
896  else
897    if l:vim_encoding =~ '^8bit\|^2byte'
898      " 8bit- and 2byte- prefixes are to indicate encodings available on the
899      " system that Vim will convert with iconv(), look up just the encoding name,
900      " not Vim's prefix.
901      let l:vim_encoding = substitute(l:vim_encoding, '^8bit-\|^2byte-', '', '')
902    endif
903    if has_key(g:tohtml#encoding_to_charset, l:vim_encoding)
904      let a:settings.encoding = g:tohtml#encoding_to_charset[l:vim_encoding]
905    else
906      let a:settings.encoding = ""
907    endif
908  endif
909  if a:settings.encoding != ""
910    let l:vim_encoding = tohtml#EncodingFromCharset(a:settings.encoding)
911    if l:vim_encoding != ""
912      " if the Vim encoding to HTML encoding conversion is set up (by default or
913      " by the user) to convert to a different encoding, we need to also change
914      " the Vim encoding of the new buffer
915      let a:settings.vim_encoding = l:vim_encoding
916    endif
917  endif
918endfun "}}}
919
920" Get the proper Vim encoding option setting from an HTML charset name.
921function! tohtml#EncodingFromCharset(encoding) "{{{
922  if exists('g:html_encoding_override') && has_key(g:html_encoding_override, a:encoding)
923    return g:html_encoding_override[a:encoding]
924  elseif has_key(g:tohtml#charset_to_encoding, tolower(a:encoding))
925    return g:tohtml#charset_to_encoding[tolower(a:encoding)]
926  else
927    return ""
928  endif
929endfun "}}}
930
931let &cpo = s:cpo_sav
932unlet s:cpo_sav
933
934" Make sure any patches will probably use consistent indent
935"   vim: ts=8 sw=2 sts=2 noet fdm=marker
936