xref: /vim-8.2.3635/runtime/autoload/tohtml.vim (revision cf2d8dee)
1" Vim autoload file for the tohtml plugin.
2" Maintainer: Ben Fritz <[email protected]>
3" Last Change: 2013 Sep 03
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    " insert script closing tag
548    call append(style_start, [
549	  \ '',
550	  \ s:settings.use_xhtml ? '//]]>' : '-->',
551	  \ "</script>"
552	  \ ])
553
554    " insert script which corrects the size of small input elements in
555    " prevent_copy mode. See 2html.vim for details on why this is needed and how
556    " it works.
557    if !empty(s:settings.prevent_copy)
558      call append(style_start, [
559	    \ '',
560	    \ '/* simulate a "ch" unit by asking the browser how big a zero character is */',
561	    \ 'function FixCharWidth() {',
562	    \ '  /* get the hidden element which gives the width of a single character */',
563	    \ '  var goodWidth = document.getElementById("oneCharWidth").clientWidth;',
564	    \ '  /* get all input elements, we''ll filter on class later */',
565	    \ '  var inputTags = document.getElementsByTagName("input");',
566	    \ '  var ratio = 5;',
567	    \ '  var inputWidth = document.getElementById("oneInputWidth").clientWidth;',
568	    \ '  var emWidth = document.getElementById("oneEmWidth").clientWidth;',
569	    \ '  if (inputWidth > goodWidth) {',
570	    \ '    while (ratio < 100*goodWidth/emWidth && ratio < 100) {',
571	    \ '      ratio += 5;',
572	    \ '    }',
573	    \ '    document.getElementById("vimCodeElement'.s:settings.id_suffix.'").className = "em"+ratio;',
574	    \ '  }',
575	    \ '}'
576	    \ ])
577    endif
578    "
579    " insert javascript to get IDs from line numbers, and to open a fold before
580    " jumping to any lines contained therein
581    call append(style_start, [
582	  \ "  /* Always jump to new location even if the line was hidden inside a fold, or",
583	  \ "   * we corrected the raw number to a line ID.",
584	  \ "   */",
585	  \ "  if (lineElem) {",
586	  \ "    lineElem.scrollIntoView(true);",
587	  \ "  }",
588	  \ "  return true;",
589	  \ "}",
590	  \ "if ('onhashchange' in window) {",
591	  \ "  window.onhashchange = JumpToLine;",
592	  \ "}"
593	  \ ])
594    if s:settings.dynamic_folds
595      call append(style_start, [
596	    \ "",
597	    \ "  /* navigate upwards in the DOM tree to open all folds containing the line */",
598	    \ "  var node = lineElem;",
599	    \ "  while (node && node.id != 'vimCodeElement".s:settings.id_suffix."')",
600	    \ "  {",
601	    \ "    if (node.className == 'closed-fold')",
602	    \ "    {",
603	    \ "      /* toggle open the fold ID (remove window ID) */",
604	    \ "      toggleFold(node.id.substr(4));",
605	    \ "    }",
606	    \ "    node = node.parentNode;",
607	    \ "  }",
608	    \ ])
609    endif
610    call append(style_start, [
611	  \ "",
612	  \ "/* function to open any folds containing a jumped-to line before jumping to it */",
613	  \ "function JumpToLine()",
614	  \ "{",
615	  \ "  var lineNum;",
616	  \ "  lineNum = window.location.hash;",
617	  \ "  lineNum = lineNum.substr(1); /* strip off '#' */",
618	  \ "",
619	  \ "  if (lineNum.indexOf('L') == -1) {",
620	  \ "    lineNum = 'L'+lineNum;",
621	  \ "  }",
622	  \ "  if (lineNum.indexOf('W') == -1) {",
623	  \ "    lineNum = 'W1'+lineNum;",
624	  \ "  }",
625	  \ "  lineElem = document.getElementById(lineNum);"
626	  \ ])
627
628    " Insert javascript to toggle matching folds open and closed in all windows,
629    " if dynamic folding is active.
630    if s:settings.dynamic_folds
631      call append(style_start, [
632	    \  "  function toggleFold(objID)",
633	    \  "  {",
634	    \  "    for (win_num = 1; win_num <= ".len(a:buf_list)."; win_num++)",
635	    \  "    {",
636	    \  "      var fold;",
637	    \  '      fold = document.getElementById("win"+win_num+objID);',
638	    \  "      if(fold.className == 'closed-fold')",
639	    \  "      {",
640	    \  "        fold.className = 'open-fold';",
641	    \  "      }",
642	    \  "      else if (fold.className == 'open-fold')",
643	    \  "      {",
644	    \  "        fold.className = 'closed-fold';",
645	    \  "      }",
646	    \  "    }",
647	    \  "  }",
648	    \ ])
649    endif
650
651    " insert script tag; javascript is always needed for the line number
652    " normalization for URL hashes
653    call append(style_start, [
654	  \ "<script type='text/javascript'>",
655	  \ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"])
656
657    " Insert styles from all the generated html documents and additional styles
658    " for the table-based layout of the side-by-side diff. The diff should take
659    " up the full browser window (but not more), and be static in size,
660    " horizontally scrollable when the lines are too long. Otherwise, the diff
661    " is pretty useless for really long lines. {{{
662    if s:settings.use_css
663      call append(style_start,
664	    \ ['<style type="text/css">']+
665	    \ style+
666	    \ [ s:settings.use_xhtml ? '' : '<!--',
667	    \   'table { table-layout: fixed; }',
668	    \   'html, body, table, tbody { width: 100%; margin: 0; padding: 0; }',
669	    \   'th, td { width: '.printf("%.1f",100.0/len(a:win_list)).'%; }',
670	    \   'td div { overflow: auto; }',
671	    \   s:settings.use_xhtml ? '' : '-->',
672	    \   '</style>'
673	    \])
674    endif "}}}
675  endif
676
677  let &paste = s:old_paste
678  let &magic = s:old_magic
679endfunc "}}}
680
681" Gets a single user option and sets it in the passed-in Dict, or gives it the
682" default value if the option doesn't actually exist.
683func! tohtml#GetOption(settings, option, default) "{{{
684  if exists('g:html_'.a:option)
685    let a:settings[a:option] = g:html_{a:option}
686  else
687    let a:settings[a:option] = a:default
688  endif
689endfunc "}}}
690
691" returns a Dict containing the values of all user options for 2html, including
692" default values for those not given an explicit value by the user. Discards the
693" html_ prefix of the option for nicer looking code.
694func! tohtml#GetUserSettings() "{{{
695  if exists('s:settings')
696    " just restore the known options if we've already retrieved them
697    return s:settings
698  else
699    " otherwise figure out which options are set
700    let user_settings = {}
701
702    " Define the correct option if the old option name exists and we haven't
703    " already defined the correct one. Maybe I'll put out a warnig message about
704    " this sometime and remove the old option entirely at some even later time,
705    " but for now just silently accept the old option.
706    if exists('g:use_xhtml') && !exists("g:html_use_xhtml")
707      let g:html_use_xhtml = g:use_xhtml
708    endif
709
710    " get current option settings with appropriate defaults {{{
711    call tohtml#GetOption(user_settings,    'no_progress', !has("statusline") )
712    call tohtml#GetOption(user_settings,  'diff_one_file', 0 )
713    call tohtml#GetOption(user_settings,   'number_lines', &number )
714    call tohtml#GetOption(user_settings,       'pre_wrap', &wrap )
715    call tohtml#GetOption(user_settings,        'use_css', 1 )
716    call tohtml#GetOption(user_settings, 'ignore_conceal', 0 )
717    call tohtml#GetOption(user_settings, 'ignore_folding', 0 )
718    call tohtml#GetOption(user_settings,  'dynamic_folds', 0 )
719    call tohtml#GetOption(user_settings,  'no_foldcolumn', user_settings.ignore_folding)
720    call tohtml#GetOption(user_settings,   'hover_unfold', 0 )
721    call tohtml#GetOption(user_settings,         'no_pre', 0 )
722    call tohtml#GetOption(user_settings,     'no_invalid', 0 )
723    call tohtml#GetOption(user_settings,   'whole_filler', 0 )
724    call tohtml#GetOption(user_settings,      'use_xhtml', 0 )
725    call tohtml#GetOption(user_settings,       'line_ids', user_settings.number_lines )
726    " }}}
727
728    " override those settings that need it {{{
729
730    " hover opening implies dynamic folding
731    if user_settings.hover_unfold
732      let user_settings.dynamic_folds = 1
733    endif
734
735    " ignore folding overrides dynamic folding
736    if user_settings.ignore_folding && user_settings.dynamic_folds
737      let user_settings.dynamic_folds = 0
738      let user_settings.hover_unfold = 0
739    endif
740
741    " dynamic folding with no foldcolumn implies hover opens
742    if user_settings.dynamic_folds && user_settings.no_foldcolumn
743      let user_settings.hover_unfold = 1
744    endif
745
746    " dynamic folding implies css
747    if user_settings.dynamic_folds
748      let user_settings.use_css = 1
749    else
750      let user_settings.no_foldcolumn = 1 " won't do anything but for consistency and for the test suite
751    endif
752
753    " if we're not using CSS we cannot use a pre section because <font> tags
754    " aren't allowed inside a <pre> block
755    if !user_settings.use_css
756      let user_settings.no_pre = 1
757    endif
758
759    " pre_wrap doesn't do anything if not using pre or not using CSS
760    if user_settings.no_pre || !user_settings.use_css
761      let user_settings.pre_wrap=0
762    endif
763    "}}}
764
765    " set up expand_tabs option after all the overrides so we know the
766    " appropriate defaults {{{
767    if user_settings.no_pre == 0
768      call tohtml#GetOption(user_settings,
769	    \ 'expand_tabs',
770	    \ &expandtab || &ts != 8 || user_settings.number_lines ||
771	    \   (user_settings.dynamic_folds && !user_settings.no_foldcolumn))
772    else
773      let user_settings.expand_tabs = 1
774    endif
775    " }}}
776
777    " textual options
778    if exists("g:html_use_encoding") "{{{
779      " user specified the desired MIME charset, figure out proper
780      " 'fileencoding' from it or warn the user if we cannot
781      let user_settings.encoding = g:html_use_encoding
782      let user_settings.vim_encoding = tohtml#EncodingFromCharset(g:html_use_encoding)
783      if user_settings.vim_encoding == ''
784	echohl WarningMsg
785	echomsg "TOhtml: file encoding for"
786	      \ g:html_use_encoding
787	      \ "unknown, please set 'fileencoding'"
788	echohl None
789      endif
790    else
791      " Figure out proper MIME charset from 'fileencoding' if possible
792      if &l:fileencoding != ''
793	" If the buffer is not a "normal" type, the 'fileencoding' value may not
794	" be trusted; since the buffer should not be written the fileencoding is
795	" not intended to be used.
796	if &l:buftype=='' || &l:buftype==?'help'
797	  let user_settings.vim_encoding = &l:fileencoding
798	  call tohtml#CharsetFromEncoding(user_settings)
799	else
800	  let user_settings.encoding = '' " trigger detection using &encoding
801	endif
802      endif
803
804      " else from 'encoding' if possible
805      if &l:fileencoding == '' || user_settings.encoding == ''
806	let user_settings.vim_encoding = &encoding
807	call tohtml#CharsetFromEncoding(user_settings)
808      endif
809
810      " else default to UTF-8 and warn user
811      if user_settings.encoding == ''
812	let user_settings.vim_encoding = 'utf-8'
813	let user_settings.encoding = 'UTF-8'
814	echohl WarningMsg
815	echomsg "TOhtml: couldn't determine MIME charset, using UTF-8"
816	echohl None
817      endif
818    endif "}}}
819
820    " Default to making nothing uncopyable, because we default to
821    " not-standards way of doing things, and also because Microsoft Word and
822    " others paste the <input> elements anyway.
823    "
824    " html_prevent_copy only has an effect when using CSS.
825    "
826    " All options:
827    "	  f - fold column
828    "	  n - line numbers (also within fold text)
829    "	  t - fold text
830    "	  d - diff filler
831    "	  c - concealed text (reserved future)
832    "	  l - listchars (reserved possible future)
833    "	  s - signs (reserved possible future)
834    "
835    " Normal text is always selectable.
836    let user_settings.prevent_copy = ""
837    if user_settings.use_css
838      if exists("g:html_prevent_copy")
839	if user_settings.dynamic_folds && !user_settings.no_foldcolumn && g:html_prevent_copy =~# 'f'
840	  let user_settings.prevent_copy .= 'f'
841	endif
842	if user_settings.number_lines && g:html_prevent_copy =~# 'n'
843	  let user_settings.prevent_copy .= 'n'
844	endif
845	if &diff && g:html_prevent_copy =~# 'd'
846	  let user_settings.prevent_copy .= 'd'
847	endif
848	if !user_settings.ignore_folding && g:html_prevent_copy =~# 't'
849	  let user_settings.prevent_copy .= 't'
850	endif
851      else
852	let user_settings.prevent_copy = ""
853      endif
854    endif
855    if empty(user_settings.prevent_copy)
856      let user_settings.no_invalid = 0
857    endif
858
859    if exists('g:html_id_expr')
860      let user_settings.id_suffix = eval(g:html_id_expr)
861      if user_settings.id_suffix !~ '^[-_:.A-Za-z0-9]*$'
862	echohl WarningMsg
863	echomsg '2html: g:html_id_expr evaluated to invalid string for HTML id attributes'
864	echomsg '2html: Omitting user-specified suffix'
865	echohl None
866	sleep 3
867	let user_settings.id_suffix=""
868      endif
869    else
870      let user_settings.id_suffix=""
871    endif
872
873    " TODO: font
874
875    return user_settings
876  endif
877endfunc "}}}
878
879" get the proper HTML charset name from a Vim encoding option.
880function! tohtml#CharsetFromEncoding(settings) "{{{
881  let l:vim_encoding = a:settings.vim_encoding
882  if exists('g:html_charset_override') && has_key(g:html_charset_override, l:vim_encoding)
883    let a:settings.encoding = g:html_charset_override[l:vim_encoding]
884  else
885    if l:vim_encoding =~ '^8bit\|^2byte'
886      " 8bit- and 2byte- prefixes are to indicate encodings available on the
887      " system that Vim will convert with iconv(), look up just the encoding name,
888      " not Vim's prefix.
889      let l:vim_encoding = substitute(l:vim_encoding, '^8bit-\|^2byte-', '', '')
890    endif
891    if has_key(g:tohtml#encoding_to_charset, l:vim_encoding)
892      let a:settings.encoding = g:tohtml#encoding_to_charset[l:vim_encoding]
893    else
894      let a:settings.encoding = ""
895    endif
896  endif
897  if a:settings.encoding != ""
898    let l:vim_encoding = tohtml#EncodingFromCharset(a:settings.encoding)
899    if l:vim_encoding != ""
900      " if the Vim encoding to HTML encoding conversion is set up (by default or
901      " by the user) to convert to a different encoding, we need to also change
902      " the Vim encoding of the new buffer
903      let a:settings.vim_encoding = l:vim_encoding
904    endif
905  endif
906endfun "}}}
907
908" Get the proper Vim encoding option setting from an HTML charset name.
909function! tohtml#EncodingFromCharset(encoding) "{{{
910  if exists('g:html_encoding_override') && has_key(g:html_encoding_override, a:encoding)
911    return g:html_encoding_override[a:encoding]
912  elseif has_key(g:tohtml#charset_to_encoding, tolower(a:encoding))
913    return g:tohtml#charset_to_encoding[tolower(a:encoding)]
914  else
915    return ""
916  endif
917endfun "}}}
918
919let &cpo = s:cpo_sav
920unlet s:cpo_sav
921
922" Make sure any patches will probably use consistent indent
923"   vim: ts=8 sw=2 sts=2 noet fdm=marker
924