view runtime/autoload/tohtml.vim @ 34790:1104d85c7dcc v9.1.0269

patch 9.1.0269: Test for TextChanged is still flaky with ASAN Commit: https://github.com/vim/vim/commit/e9ff79a7c9affea970f50de2aa65f62080b55323 Author: zeertzjq <zeertzjq@outlook.com> Date: Fri Apr 5 20:07:39 2024 +0200 patch 9.1.0269: Test for TextChanged is still flaky with ASAN Problem: Test for TextChanged is still flaky with ASAN. Solution: Don't index the result of readfile(). (zeertzjq) It turns out that with ASAN the file may become empty during a write even if it's non-empty both before and after the write, in which case indexing the result of readfile() will error, so use join() instead. Also don't delete the file halfway the test, just in case it may cause errors on the next read. closes: #14421 Signed-off-by: zeertzjq <zeertzjq@outlook.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Fri, 05 Apr 2024 20:15:06 +0200
parents d77a9aab91ad
children
line wrap: on
line source

" Vim autoload file for the tohtml plugin.
" Maintainer: Ben Fritz <fritzophrenic@gmail.com>
" Last Change: 2023 Sep 03
"
" Additional contributors:
"
"	      Original by Bram Moolenaar <Bram@vim.org>
"	      Diff2HTML() added by Christian Brabandt <cb@256bit.org>
"
"	      See Mercurial change logs for more!

" this file uses line continuations
let s:cpo_sav = &cpo
set cpo&vim

" Automatically find charsets from all encodings supported natively by Vim. With
" the 8bit- and 2byte- prefixes, Vim can actually support more encodings than
" this. Let the user specify these however since they won't be supported on
" every system.
"
" Note, not all of Vim's supported encodings have a charset to use.
"
" Names in this list are from:
"   http://www.iana.org/assignments/character-sets
" g:tohtml#encoding_to_charset: {{{
let g:tohtml#encoding_to_charset = {
      \ 'latin1' : 'ISO-8859-1',
      \ 'iso-8859-2' : 'ISO-8859-2',
      \ 'iso-8859-3' : 'ISO-8859-3',
      \ 'iso-8859-4' : 'ISO-8859-4',
      \ 'iso-8859-5' : 'ISO-8859-5',
      \ 'iso-8859-6' : 'ISO-8859-6',
      \ 'iso-8859-7' : 'ISO-8859-7',
      \ 'iso-8859-8' : 'ISO-8859-8',
      \ 'iso-8859-9' : 'ISO-8859-9',
      \ 'iso-8859-10' : '',
      \ 'iso-8859-13' : 'ISO-8859-13',
      \ 'iso-8859-14' : '',
      \ 'iso-8859-15' : 'ISO-8859-15',
      \ 'koi8-r' : 'KOI8-R',
      \ 'koi8-u' : 'KOI8-U',
      \ 'macroman' : 'macintosh',
      \ 'cp437' : '',
      \ 'cp775' : '',
      \ 'cp850' : '',
      \ 'cp852' : '',
      \ 'cp855' : '',
      \ 'cp857' : '',
      \ 'cp860' : '',
      \ 'cp861' : '',
      \ 'cp862' : '',
      \ 'cp863' : '',
      \ 'cp865' : '',
      \ 'cp866' : 'IBM866',
      \ 'cp869' : '',
      \ 'cp874' : '',
      \ 'cp1250' : 'windows-1250',
      \ 'cp1251' : 'windows-1251',
      \ 'cp1253' : 'windows-1253',
      \ 'cp1254' : 'windows-1254',
      \ 'cp1255' : 'windows-1255',
      \ 'cp1256' : 'windows-1256',
      \ 'cp1257' : 'windows-1257',
      \ 'cp1258' : 'windows-1258',
      \ 'euc-jp' : 'EUC-JP',
      \ 'sjis' : 'Shift_JIS',
      \ 'cp932' : 'Shift_JIS',
      \ 'cp949' : '',
      \ 'euc-kr' : 'EUC-KR',
      \ 'cp936' : 'GBK',
      \ 'euc-cn' : 'GB2312',
      \ 'big5' : 'Big5',
      \ 'cp950' : 'Big5',
      \ 'utf-8' : 'UTF-8',
      \ 'ucs-2' : 'UTF-8',
      \ 'ucs-2le' : 'UTF-8',
      \ 'utf-16' : 'UTF-8',
      \ 'utf-16le' : 'UTF-8',
      \ 'ucs-4' : 'UTF-8',
      \ 'ucs-4le' : 'UTF-8',
      \ }
lockvar g:tohtml#encoding_to_charset
" Notes:
"   1. All UCS/UTF are converted to UTF-8 because it is much better supported
"   2. Any blank spaces are there because Vim supports it but at least one major
"      web browser does not according to http://wiki.whatwg.org/wiki/Web_Encodings.
" }}}

" Only automatically find encodings supported natively by Vim, let the user
" specify the encoding if it's not natively supported. This function is only
" used when the user specifies the charset, they better know what they are
" doing!
"
" Names in this list are from:
"   http://www.iana.org/assignments/character-sets
" g:tohtml#charset_to_encoding: {{{
let g:tohtml#charset_to_encoding = {
      \ 'iso_8859-1:1987' : 'latin1',
      \ 'iso-ir-100' : 'latin1',
      \ 'iso_8859-1' : 'latin1',
      \ 'iso-8859-1' : 'latin1',
      \ 'latin1' : 'latin1',
      \ 'l1' : 'latin1',
      \ 'ibm819' : 'latin1',
      \ 'cp819' : 'latin1',
      \ 'csisolatin1' : 'latin1',
      \ 'iso_8859-2:1987' : 'iso-8859-2',
      \ 'iso-ir-101' : 'iso-8859-2',
      \ 'iso_8859-2' : 'iso-8859-2',
      \ 'iso-8859-2' : 'iso-8859-2',
      \ 'latin2' : 'iso-8859-2',
      \ 'l2' : 'iso-8859-2',
      \ 'csisolatin2' : 'iso-8859-2',
      \ 'iso_8859-3:1988' : 'iso-8859-3',
      \ 'iso-ir-109' : 'iso-8859-3',
      \ 'iso_8859-3' : 'iso-8859-3',
      \ 'iso-8859-3' : 'iso-8859-3',
      \ 'latin3' : 'iso-8859-3',
      \ 'l3' : 'iso-8859-3',
      \ 'csisolatin3' : 'iso-8859-3',
      \ 'iso_8859-4:1988' : 'iso-8859-4',
      \ 'iso-ir-110' : 'iso-8859-4',
      \ 'iso_8859-4' : 'iso-8859-4',
      \ 'iso-8859-4' : 'iso-8859-4',
      \ 'latin4' : 'iso-8859-4',
      \ 'l4' : 'iso-8859-4',
      \ 'csisolatin4' : 'iso-8859-4',
      \ 'iso_8859-5:1988' : 'iso-8859-5',
      \ 'iso-ir-144' : 'iso-8859-5',
      \ 'iso_8859-5' : 'iso-8859-5',
      \ 'iso-8859-5' : 'iso-8859-5',
      \ 'cyrillic' : 'iso-8859-5',
      \ 'csisolatincyrillic' : 'iso-8859-5',
      \ 'iso_8859-6:1987' : 'iso-8859-6',
      \ 'iso-ir-127' : 'iso-8859-6',
      \ 'iso_8859-6' : 'iso-8859-6',
      \ 'iso-8859-6' : 'iso-8859-6',
      \ 'ecma-114' : 'iso-8859-6',
      \ 'asmo-708' : 'iso-8859-6',
      \ 'arabic' : 'iso-8859-6',
      \ 'csisolatinarabic' : 'iso-8859-6',
      \ 'iso_8859-7:1987' : 'iso-8859-7',
      \ 'iso-ir-126' : 'iso-8859-7',
      \ 'iso_8859-7' : 'iso-8859-7',
      \ 'iso-8859-7' : 'iso-8859-7',
      \ 'elot_928' : 'iso-8859-7',
      \ 'ecma-118' : 'iso-8859-7',
      \ 'greek' : 'iso-8859-7',
      \ 'greek8' : 'iso-8859-7',
      \ 'csisolatingreek' : 'iso-8859-7',
      \ 'iso_8859-8:1988' : 'iso-8859-8',
      \ 'iso-ir-138' : 'iso-8859-8',
      \ 'iso_8859-8' : 'iso-8859-8',
      \ 'iso-8859-8' : 'iso-8859-8',
      \ 'hebrew' : 'iso-8859-8',
      \ 'csisolatinhebrew' : 'iso-8859-8',
      \ 'iso_8859-9:1989' : 'iso-8859-9',
      \ 'iso-ir-148' : 'iso-8859-9',
      \ 'iso_8859-9' : 'iso-8859-9',
      \ 'iso-8859-9' : 'iso-8859-9',
      \ 'latin5' : 'iso-8859-9',
      \ 'l5' : 'iso-8859-9',
      \ 'csisolatin5' : 'iso-8859-9',
      \ 'iso-8859-10' : 'iso-8859-10',
      \ 'iso-ir-157' : 'iso-8859-10',
      \ 'l6' : 'iso-8859-10',
      \ 'iso_8859-10:1992' : 'iso-8859-10',
      \ 'csisolatin6' : 'iso-8859-10',
      \ 'latin6' : 'iso-8859-10',
      \ 'iso-8859-13' : 'iso-8859-13',
      \ 'iso-8859-14' : 'iso-8859-14',
      \ 'iso-ir-199' : 'iso-8859-14',
      \ 'iso_8859-14:1998' : 'iso-8859-14',
      \ 'iso_8859-14' : 'iso-8859-14',
      \ 'latin8' : 'iso-8859-14',
      \ 'iso-celtic' : 'iso-8859-14',
      \ 'l8' : 'iso-8859-14',
      \ 'iso-8859-15' : 'iso-8859-15',
      \ 'iso_8859-15' : 'iso-8859-15',
      \ 'latin-9' : 'iso-8859-15',
      \ 'koi8-r' : 'koi8-r',
      \ 'cskoi8r' : 'koi8-r',
      \ 'koi8-u' : 'koi8-u',
      \ 'macintosh' : 'macroman',
      \ 'mac' : 'macroman',
      \ 'csmacintosh' : 'macroman',
      \ 'ibm437' : 'cp437',
      \ 'cp437' : 'cp437',
      \ '437' : 'cp437',
      \ 'cspc8codepage437' : 'cp437',
      \ 'ibm775' : 'cp775',
      \ 'cp775' : 'cp775',
      \ 'cspc775baltic' : 'cp775',
      \ 'ibm850' : 'cp850',
      \ 'cp850' : 'cp850',
      \ '850' : 'cp850',
      \ 'cspc850multilingual' : 'cp850',
      \ 'ibm852' : 'cp852',
      \ 'cp852' : 'cp852',
      \ '852' : 'cp852',
      \ 'cspcp852' : 'cp852',
      \ 'ibm855' : 'cp855',
      \ 'cp855' : 'cp855',
      \ '855' : 'cp855',
      \ 'csibm855' : 'cp855',
      \ 'ibm857' : 'cp857',
      \ 'cp857' : 'cp857',
      \ '857' : 'cp857',
      \ 'csibm857' : 'cp857',
      \ 'ibm860' : 'cp860',
      \ 'cp860' : 'cp860',
      \ '860' : 'cp860',
      \ 'csibm860' : 'cp860',
      \ 'ibm861' : 'cp861',
      \ 'cp861' : 'cp861',
      \ '861' : 'cp861',
      \ 'cp-is' : 'cp861',
      \ 'csibm861' : 'cp861',
      \ 'ibm862' : 'cp862',
      \ 'cp862' : 'cp862',
      \ '862' : 'cp862',
      \ 'cspc862latinhebrew' : 'cp862',
      \ 'ibm863' : 'cp863',
      \ 'cp863' : 'cp863',
      \ '863' : 'cp863',
      \ 'csibm863' : 'cp863',
      \ 'ibm865' : 'cp865',
      \ 'cp865' : 'cp865',
      \ '865' : 'cp865',
      \ 'csibm865' : 'cp865',
      \ 'ibm866' : 'cp866',
      \ 'cp866' : 'cp866',
      \ '866' : 'cp866',
      \ 'csibm866' : 'cp866',
      \ 'ibm869' : 'cp869',
      \ 'cp869' : 'cp869',
      \ '869' : 'cp869',
      \ 'cp-gr' : 'cp869',
      \ 'csibm869' : 'cp869',
      \ 'windows-1250' : 'cp1250',
      \ 'windows-1251' : 'cp1251',
      \ 'windows-1253' : 'cp1253',
      \ 'windows-1254' : 'cp1254',
      \ 'windows-1255' : 'cp1255',
      \ 'windows-1256' : 'cp1256',
      \ 'windows-1257' : 'cp1257',
      \ 'windows-1258' : 'cp1258',
      \ 'extended_unix_code_packed_format_for_japanese' : 'euc-jp',
      \ 'cseucpkdfmtjapanese' : 'euc-jp',
      \ 'euc-jp' : 'euc-jp',
      \ 'shift_jis' : 'sjis',
      \ 'ms_kanji' : 'sjis',
      \ 'sjis' : 'sjis',
      \ 'csshiftjis' : 'sjis',
      \ 'ibm-thai' : 'cp874',
      \ 'csibmthai' : 'cp874',
      \ 'ks_c_5601-1987' : 'cp949',
      \ 'iso-ir-149' : 'cp949',
      \ 'ks_c_5601-1989' : 'cp949',
      \ 'ksc_5601' : 'cp949',
      \ 'korean' : 'cp949',
      \ 'csksc56011987' : 'cp949',
      \ 'euc-kr' : 'euc-kr',
      \ 'cseuckr' : 'euc-kr',
      \ 'gbk' : 'cp936',
      \ 'cp936' : 'cp936',
      \ 'ms936' : 'cp936',
      \ 'windows-936' : 'cp936',
      \ 'gb_2312-80' : 'euc-cn',
      \ 'iso-ir-58' : 'euc-cn',
      \ 'chinese' : 'euc-cn',
      \ 'csiso58gb231280' : 'euc-cn',
      \ 'big5' : 'big5',
      \ 'csbig5' : 'big5',
      \ 'utf-8' : 'utf-8',
      \ 'iso-10646-ucs-2' : 'ucs-2',
      \ 'csunicode' : 'ucs-2',
      \ 'utf-16' : 'utf-16',
      \ 'utf-16be' : 'utf-16',
      \ 'utf-16le' : 'utf-16le',
      \ 'utf-32' : 'ucs-4',
      \ 'utf-32be' : 'ucs-4',
      \ 'utf-32le' : 'ucs-4le',
      \ 'iso-10646-ucs-4' : 'ucs-4',
      \ 'csucs4' : 'ucs-4'
      \ }
lockvar g:tohtml#charset_to_encoding
"}}}

func! tohtml#Convert2HTML(line1, line2) "{{{
  let s:settings = tohtml#GetUserSettings()

  if !&diff || s:settings.diff_one_file "{{{
    if a:line2 >= a:line1
      let g:html_start_line = a:line1
      let g:html_end_line = a:line2
    else
      let g:html_start_line = a:line2
      let g:html_end_line = a:line1
    endif
    runtime syntax/2html.vim "}}}
  else "{{{
    let win_list = []
    let buf_list = []
    windo if &diff | call add(win_list, winbufnr(0)) | endif
    let s:settings.whole_filler = 1
    let g:html_diff_win_num = 0
    for window in win_list
      " switch to the next buffer to convert
      exe ":" .. bufwinnr(window) .. "wincmd w"

      " figure out whether current charset and encoding will work, if not
      " default to UTF-8
      if !exists('g:html_use_encoding') &&
	    \ (((&l:fileencoding=='' || (&l:buftype!='' && &l:buftype!=?'help'))
	    \      && &encoding!=?s:settings.vim_encoding)
	    \ || &l:fileencoding!='' && &l:fileencoding!=?s:settings.vim_encoding)
	echohl WarningMsg
	echomsg "TOhtml: mismatched file encodings in Diff buffers, using UTF-8"
	echohl None
	let s:settings.vim_encoding = 'utf-8'
	let s:settings.encoding = 'UTF-8'
      endif

      " set up for diff-mode conversion
      let g:html_start_line = 1
      let g:html_end_line = line('$')
      let g:html_diff_win_num += 1

      " convert this file
      runtime syntax/2html.vim

      " remember the HTML buffer for later combination
      call add(buf_list, bufnr('%'))
    endfor
    unlet g:html_diff_win_num
    call tohtml#Diff2HTML(win_list, buf_list)
  endif "}}}

  unlet g:html_start_line
  unlet g:html_end_line
  unlet s:settings
endfunc "}}}

func! tohtml#Diff2HTML(win_list, buf_list) "{{{
  let xml_line = ""
  let tag_close = '>'

  let s:old_paste = &paste
  set paste
  let s:old_magic = &magic
  set magic

  let html = []
  if !s:settings.no_doc
    if s:settings.use_xhtml
      if s:settings.encoding != ""
	let xml_line = "<?xml version=\"1.0\" encoding=\"" .. s:settings.encoding .. "\"?>"
      else
	let xml_line = "<?xml version=\"1.0\"?>"
      endif
      let tag_close = ' />'
    endif

    let style = [s:settings.use_xhtml ? "" : '-->']
    let body_line = ''

    let s:html5 = 0
    if s:settings.use_xhtml
      call add(html, xml_line)
    endif
    if s:settings.use_xhtml
      call add(html, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">")
      call add(html, '<html xmlns="http://www.w3.org/1999/xhtml">')
    elseif s:settings.use_css && !s:settings.no_pre
      call add(html, "<!DOCTYPE html>")
      call add(html, '<html>')
      let s:html5 = 1
    else
      call add(html, '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"')
      call add(html, '  "http://www.w3.org/TR/html4/loose.dtd">')
      call add(html, '<html>')
    endif
    call add(html, '<head>')

    " include encoding as close to the top as possible, but only if not already
    " contained in XML information
    if s:settings.encoding != "" && !s:settings.use_xhtml
      if s:html5
	call add(html, '<meta charset="' .. s:settings.encoding .. '"' .. tag_close)
      else
	call add(html, "<meta http-equiv=\"content-type\" content=\"text/html; charset=" .. s:settings.encoding .. '"' .. tag_close)
      endif
    endif

    call add(html, '<title>diff</title>')
    call add(html, '<meta name="Generator" content="Vim/'..v:version/100..'.'..v:version%100..'"'..tag_close)
    call add(html, '<meta name="plugin-version" content="'..g:loaded_2html_plugin..'"'..tag_close)
    call add(html, '<meta name="settings" content="'.
	  \ join(filter(keys(s:settings),'s:settings[v:val]'),',').
	  \ ',prevent_copy='..s:settings.prevent_copy.
	  \ ',use_input_for_pc='..s:settings.use_input_for_pc.
	  \ '"'..tag_close)
    call add(html, '<meta name="colorscheme" content="'.
	  \ (exists('g:colors_name')
	  \ ? g:colors_name
	  \ : 'none').. '"'..tag_close)

    call add(html, '</head>')
    let body_line_num = len(html)
    call add(html, '<body'..(s:settings.line_ids ? ' onload="JumpToLine();"' : '')..'>')
  endif
  call add(html, "<table "..(s:settings.use_css? "" : "border='1' width='100%' ").."id='vimCodeElement"..s:settings.id_suffix.."'>")

  call add(html, '<tr>')
  for buf in a:win_list
    call add(html, '<th>'..bufname(buf)..'</th>')
  endfor
  call add(html, '</tr><tr>')

  let diff_style_start = 0
  let insert_index = 0

  for buf in a:buf_list
    let temp = []
    exe bufwinnr(buf) .. 'wincmd w'

    " If text is folded because of user foldmethod settings, etc. we don't want
    " to act on everything in a fold by mistake.
    setlocal nofoldenable

    " When not using CSS or when using xhtml, the <body> line can be important.
    " Assume it will be the same for all buffers and grab it from the first
    " buffer. Similarly, need to grab the body end line as well.
    if !s:settings.no_doc
      if body_line == ''
	1
	call search('<body')
	let body_line = getline('.')
	$
	call search('</body>', 'b')
	let s:body_end_line = getline('.')
      endif

      " Grab the style information. Some of this will be duplicated so only insert
      " it if it's not already there. {{{
      1
      let style_start = search('^<style\( type="text/css"\)\?>')
      1
      let style_end = search('^</style>')
      if style_start > 0 && style_end > 0
	let buf_styles = getline(style_start + 1, style_end - 1)
	for a_style in buf_styles
	  if index(style, a_style) == -1
	    if diff_style_start == 0
	      if a_style =~ '\<Diff\(Change\|Text\|Add\|Delete\)'
		let diff_style_start = len(style)-1
	      endif
	    endif
	    call insert(style, a_style, insert_index)
	    let insert_index += 1
	  endif
	endfor
      endif " }}}

      " everything new will get added before the diff styles so diff highlight
      " properly overrides normal highlight
      if diff_style_start != 0
	let insert_index = diff_style_start
      endif

      " Delete those parts that are not needed so we can include the rest into the
      " resulting table.
      1,/^<body.*\%(\n<!--.*-->\_s\+.*id='oneCharWidth'.*\_s\+.*id='oneInputWidth'.*\_s\+.*id='oneEmWidth'\)\?\zs/d_
      $
      ?</body>?,$d_
    elseif !s:settings.no_modeline
      " remove modeline from source files if it is included and we haven't deleted
      " due to removing html footer already
      $d
    endif
    let temp = getline(1,'$')
    " clean out id on the main content container because we already set it on
    " the table
    let temp[0] = substitute(temp[0], " id='vimCodeElement[^']*'", "", "")
    " undo deletion of start and end part
    " so we can later save the file as valid html
    " TODO: restore using grabbed lines if undolevel is 1?
    if !s:settings.no_doc
      normal! 2u
    elseif !s:settings.no_modeline
      normal! u
    endif
    if s:settings.use_css
      call add(html, '<td><div>')
    elseif s:settings.use_xhtml
      call add(html, '<td nowrap="nowrap" valign="top"><div>')
    else
      call add(html, '<td nowrap valign="top"><div>')
    endif
    let html += temp
    call add(html, '</div></td>')

    " Close this buffer
    " TODO: the comment above says we're going to allow saving the file
    " later...but here we discard it?
    quit!
  endfor

  if !s:settings.no_doc
    let html[body_line_num] = body_line
  endif

  call add(html, '</tr>')
  call add(html, '</table>')
  if !s:settings.no_doc
    call add(html, s:body_end_line)
    call add(html, '</html>')
  endif

  " The generated HTML is admittedly ugly and takes a LONG time to fold.
  " Make sure the user doesn't do syntax folding when loading a generated file,
  " using a modeline.
  if !s:settings.no_modeline
    call add(html, '<!-- vim: set foldmethod=manual : -->')
  endif

  let i = 1
  let name = "Diff" .. (s:settings.use_xhtml ? ".xhtml" : ".html")
  " Find an unused file name if current file name is already in use
  while filereadable(name)
    let name = substitute(name, '\d*\.x\?html$', '', '') .. i .. '.' .. fnamemodify(copy(name), ":t:e")
    let i += 1
  endwhile

  let s:ei_sav = &eventignore
  set eventignore+=FileType
  exe "topleft new " .. name
  let &eventignore=s:ei_sav
  unlet s:ei_sav

  setlocal modifiable

  " just in case some user autocmd creates content in the new buffer, make sure
  " it is empty before proceeding
  %d

  " set the fileencoding to match the charset we'll be using
  let &l:fileencoding=s:settings.vim_encoding

  " According to http://www.w3.org/TR/html4/charset.html#doc-char-set, the byte
  " order mark is highly recommend on the web when using multibyte encodings. But,
  " it is not a good idea to include it on UTF-8 files. Otherwise, let Vim
  " determine when it is actually inserted.
  if s:settings.vim_encoding == 'utf-8'
    setlocal nobomb
  else
    setlocal bomb
  endif

  call append(0, html)

  if !s:settings.no_doc
    if len(style) > 0
      1
      let style_start = search('^</head>')-1

      " add required javascript in reverse order so we can just call append again
      " and again without adjusting {{{

      let s:uses_script = s:settings.dynamic_folds || s:settings.line_ids

      " insert script closing tag if needed
      if s:uses_script
	call append(style_start, [
	      \ '',
	      \ s:settings.use_xhtml ? '//]]>' : '-->',
	      \ "</script>"
	      \ ])
      endif

      " insert javascript to get IDs from line numbers, and to open a fold before
      " jumping to any lines contained therein
      if s:settings.line_ids
	call append(style_start, [
	      \ "  /* Always jump to new location even if the line was hidden inside a fold, or",
	      \ "   * we corrected the raw number to a line ID.",
	      \ "   */",
	      \ "  if (lineElem) {",
	      \ "    lineElem.scrollIntoView(true);",
	      \ "  }",
	      \ "  return true;",
	      \ "}",
	      \ "if ('onhashchange' in window) {",
	      \ "  window.onhashchange = JumpToLine;",
	      \ "}"
	      \ ])

	if s:settings.dynamic_folds
	  call append(style_start, [
		\ "",
		\ "  /* navigate upwards in the DOM tree to open all folds containing the line */",
		\ "  var node = lineElem;",
		\ "  while (node && node.id != 'vimCodeElement"..s:settings.id_suffix.."')",
		\ "  {",
		\ "    if (node.className == 'closed-fold')",
		\ "    {",
		\ "      /* toggle open the fold ID (remove window ID) */",
		\ "      toggleFold(node.id.substr(4));",
		\ "    }",
		\ "    node = node.parentNode;",
		\ "  }",
		\ ])
	endif
      endif

      if s:settings.line_ids
	call append(style_start, [
	      \ "",
	      \ "/* function to open any folds containing a jumped-to line before jumping to it */",
	      \ "function JumpToLine()",
	      \ "{",
	      \ "  var lineNum;",
	      \ "  lineNum = window.location.hash;",
	      \ "  lineNum = lineNum.substr(1); /* strip off '#' */",
	      \ "",
	      \ "  if (lineNum.indexOf('L') == -1) {",
	      \ "    lineNum = 'L'+lineNum;",
	      \ "  }",
	      \ "  if (lineNum.indexOf('W') == -1) {",
	      \ "    lineNum = 'W1'+lineNum;",
	      \ "  }",
	      \ "  var lineElem = document.getElementById(lineNum);"
	      \ ])
      endif

      " Insert javascript to toggle matching folds open and closed in all windows,
      " if dynamic folding is active.
      if s:settings.dynamic_folds
	call append(style_start, [
	      \  "  function toggleFold(objID)",
	      \  "  {",
	      \  "    for (win_num = 1; win_num <= "..len(a:buf_list).."; win_num++)",
	      \  "    {",
	      \  "      var fold;",
	      \  '      fold = document.getElementById("win"+win_num+objID);',
	      \  "      if(fold.className == 'closed-fold')",
	      \  "      {",
	      \  "        fold.className = 'open-fold';",
	      \  "      }",
	      \  "      else if (fold.className == 'open-fold')",
	      \  "      {",
	      \  "        fold.className = 'closed-fold';",
	      \  "      }",
	      \  "    }",
	      \  "  }",
	      \ ])
      endif

      if s:uses_script
	" insert script tag if needed
	call append(style_start, [
	      \ "<script" .. (s:html5 ? "" : " type='text/javascript'") .. ">",
	      \ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"])
      endif

      " Insert styles from all the generated html documents and additional styles
      " for the table-based layout of the side-by-side diff. The diff should take
      " up the full browser window (but not more), and be static in size,
      " horizontally scrollable when the lines are too long. Otherwise, the diff
      " is pretty useless for really long lines. {{{
      if s:settings.use_css
	call append(style_start,
	      \ ['<style' .. (s:html5 ? '' : 'type="text/css"') .. '>']+
	      \ style+
	      \ [ s:settings.use_xhtml ? '' : '<!--',
	      \   'table { table-layout: fixed; }',
	      \   'html, body, table, tbody { width: 100%; margin: 0; padding: 0; }',
	      \   'table, td, th { border: 1px solid; }',
	      \   'td { vertical-align: top; }',
	      \   'th, td { width: '..printf("%.1f",100.0/len(a:win_list))..'%; }',
	      \   'td div { overflow: auto; }',
	      \   s:settings.use_xhtml ? '' : '-->',
	      \   '</style>'
	      \])
      endif "}}}
    endif
  endif

  let &paste = s:old_paste
  let &magic = s:old_magic
endfunc "}}}

" Gets a single user option and sets it in the passed-in Dict, or gives it the
" default value if the option doesn't actually exist.
func! tohtml#GetOption(settings, option, default) "{{{
  if exists('g:html_'..a:option)
    let a:settings[a:option] = g:html_{a:option}
  else
    let a:settings[a:option] = a:default
  endif
endfunc "}}}

" returns a Dict containing the values of all user options for 2html, including
" default values for those not given an explicit value by the user. Discards the
" html_ prefix of the option for nicer looking code.
func! tohtml#GetUserSettings() "{{{
  if exists('s:settings')
    " just restore the known options if we've already retrieved them
    return s:settings
  else
    " otherwise figure out which options are set
    let user_settings = {}

    " Define the correct option if the old option name exists and we haven't
    " already defined the correct one.
    if exists('g:use_xhtml') && !exists("g:html_use_xhtml")
      echohl WarningMsg
      echomsg "Warning: g:use_xhtml is deprecated, use g:html_use_xhtml"
      echohl None
      let g:html_use_xhtml = g:use_xhtml
    endif

    " get current option settings with appropriate defaults {{{
    call tohtml#GetOption(user_settings,       'no_progress', !has("statusline") )
    call tohtml#GetOption(user_settings,     'diff_one_file', 0 )
    call tohtml#GetOption(user_settings,      'number_lines', &number )
    call tohtml#GetOption(user_settings,          'pre_wrap', &wrap )
    call tohtml#GetOption(user_settings,           'use_css', 1 )
    call tohtml#GetOption(user_settings,    'ignore_conceal', 0 )
    call tohtml#GetOption(user_settings,    'ignore_folding', 0 )
    call tohtml#GetOption(user_settings,     'dynamic_folds', 0 )
    call tohtml#GetOption(user_settings,     'no_foldcolumn', user_settings.ignore_folding)
    call tohtml#GetOption(user_settings,      'hover_unfold', 0 )
    call tohtml#GetOption(user_settings,            'no_pre', 0 )
    call tohtml#GetOption(user_settings,            'no_doc', 0 )
    call tohtml#GetOption(user_settings,          'no_links', 0 )
    call tohtml#GetOption(user_settings,       'no_modeline', 0 )
    call tohtml#GetOption(user_settings,        'no_invalid', 0 )
    call tohtml#GetOption(user_settings,      'whole_filler', 0 )
    call tohtml#GetOption(user_settings,         'use_xhtml', 0 )
    call tohtml#GetOption(user_settings,          'line_ids', user_settings.number_lines )
    call tohtml#GetOption(user_settings, 'use_input_for_pc', 'none')
    " }}}
    
    " override those settings that need it {{{

    " hover opening implies dynamic folding
    if user_settings.hover_unfold
      let user_settings.dynamic_folds = 1
    endif

    " ignore folding overrides dynamic folding
    if user_settings.ignore_folding && user_settings.dynamic_folds
      let user_settings.dynamic_folds = 0
      let user_settings.hover_unfold = 0
    endif

    " dynamic folding with no foldcolumn implies hover opens
    if user_settings.dynamic_folds && user_settings.no_foldcolumn
      let user_settings.hover_unfold = 1
    endif

    " dynamic folding implies css
    if user_settings.dynamic_folds
      let user_settings.use_css = 1
    else
      let user_settings.no_foldcolumn = 1 " won't do anything but for consistency and for the test suite
    endif

    " if we're not using CSS we cannot use a pre section because <font> tags
    " aren't allowed inside a <pre> block
    if !user_settings.use_css
      let user_settings.no_pre = 1
    endif

    " pre_wrap doesn't do anything if not using pre or not using CSS
    if user_settings.no_pre || !user_settings.use_css
      let user_settings.pre_wrap = 0
    endif
    "}}}

    " set up expand_tabs option after all the overrides so we know the
    " appropriate defaults {{{
    if user_settings.no_pre == 0
      call tohtml#GetOption(user_settings,
	    \ 'expand_tabs',
	    \ &expandtab || &ts != 8 || &vts != '' || user_settings.number_lines ||
	    \   (user_settings.dynamic_folds && !user_settings.no_foldcolumn))
    else
      let user_settings.expand_tabs = 1
    endif
    " }}}

    " textual options
    if exists("g:html_use_encoding") "{{{
      " user specified the desired MIME charset, figure out proper
      " 'fileencoding' from it or warn the user if we cannot
      let user_settings.encoding = g:html_use_encoding
      let user_settings.vim_encoding = tohtml#EncodingFromCharset(g:html_use_encoding)
      if user_settings.vim_encoding == ''
	echohl WarningMsg
	echomsg "TOhtml: file encoding for"
	      \ g:html_use_encoding
	      \ "unknown, please set 'fileencoding'"
	echohl None
      endif
    else
      " Figure out proper MIME charset from 'fileencoding' if possible
      if &l:fileencoding != '' 
	" If the buffer is not a "normal" type, the 'fileencoding' value may not
	" be trusted; since the buffer should not be written the fileencoding is
	" not intended to be used.
	if &l:buftype=='' || &l:buftype==?'help'
	  let user_settings.vim_encoding = &l:fileencoding
	  call tohtml#CharsetFromEncoding(user_settings)
	else
	  let user_settings.encoding = '' " trigger detection using &encoding
	endif
      endif

      " else from 'encoding' if possible
      if &l:fileencoding == '' || user_settings.encoding == ''
	let user_settings.vim_encoding = &encoding
	call tohtml#CharsetFromEncoding(user_settings)
      endif

      " else default to UTF-8 and warn user
      if user_settings.encoding == ''
	let user_settings.vim_encoding = 'utf-8'
	let user_settings.encoding = 'UTF-8'
	echohl WarningMsg
	echomsg "TOhtml: couldn't determine MIME charset, using UTF-8"
	echohl None
      endif
    endif "}}}

    " Default to making nothing uncopyable, because we default to
    " not-standards way of doing things, and also because Microsoft Word and
    " others paste the <input> elements anyway.
    "
    " html_prevent_copy only has an effect when using CSS.
    "
    " All options:
    "	  f - fold column
    "	  n - line numbers (also within fold text)
    "	  t - fold text
    "	  d - diff filler
    "	  c - concealed text (reserved future)
    "	  l - listchars (reserved possible future)
    "	  s - signs (reserved possible future)
    "
    " Normal text is always selectable.
    let user_settings.prevent_copy = ""
    if user_settings.use_css
      if exists("g:html_prevent_copy")
	if user_settings.dynamic_folds && !user_settings.no_foldcolumn && g:html_prevent_copy =~# 'f'
	  let user_settings.prevent_copy ..= 'f'
	endif
	if user_settings.number_lines && g:html_prevent_copy =~# 'n'
	  let user_settings.prevent_copy ..= 'n'
	endif
	if &diff && g:html_prevent_copy =~# 'd'
	  let user_settings.prevent_copy ..= 'd'
	endif
	if !user_settings.ignore_folding && g:html_prevent_copy =~# 't'
	  let user_settings.prevent_copy ..= 't'
	endif
      else
	let user_settings.prevent_copy = ""
      endif
    endif
    if empty(user_settings.prevent_copy)
      let user_settings.no_invalid = 0
    endif

    " enforce valid values for use_input_for_pc
    if user_settings.use_input_for_pc !~# 'fallback\|none\|all'
      let user_settings.use_input_for_pc = 'none'
      echohl WarningMsg
      echomsg '2html: "' .. g:html_use_input_for_pc .. '" is not valid for g:html_use_input_for_pc'
      echomsg '2html: defaulting to "' .. user_settings.use_input_for_pc .. '"'
      echohl None
      sleep 3
    endif

    if exists('g:html_id_expr')
      let user_settings.id_suffix = eval(g:html_id_expr)
      if user_settings.id_suffix !~ '^[-_:.A-Za-z0-9]*$'
	echohl WarningMsg
	echomsg '2html: g:html_id_expr evaluated to invalid string for HTML id attributes'
	echomsg '2html: Omitting user-specified suffix'
	echohl None
	sleep 3
	let user_settings.id_suffix=""
      endif
    else
      let user_settings.id_suffix=""
    endif

    " TODO: font

    return user_settings
  endif
endfunc "}}}

" get the proper HTML charset name from a Vim encoding option.
function! tohtml#CharsetFromEncoding(settings) "{{{
  let l:vim_encoding = a:settings.vim_encoding
  if exists('g:html_charset_override') && has_key(g:html_charset_override, l:vim_encoding)
    let a:settings.encoding = g:html_charset_override[l:vim_encoding]
  else
    if l:vim_encoding =~ '^8bit\|^2byte'
      " 8bit- and 2byte- prefixes are to indicate encodings available on the
      " system that Vim will convert with iconv(), look up just the encoding name,
      " not Vim's prefix.
      let l:vim_encoding = substitute(l:vim_encoding, '^8bit-\|^2byte-', '', '')
    endif
    if has_key(g:tohtml#encoding_to_charset, l:vim_encoding)
      let a:settings.encoding = g:tohtml#encoding_to_charset[l:vim_encoding]
    else
      let a:settings.encoding = ""
    endif
  endif
  if a:settings.encoding != ""
    let l:vim_encoding = tohtml#EncodingFromCharset(a:settings.encoding)
    if l:vim_encoding != ""
      " if the Vim encoding to HTML encoding conversion is set up (by default or
      " by the user) to convert to a different encoding, we need to also change
      " the Vim encoding of the new buffer
      let a:settings.vim_encoding = l:vim_encoding
    endif
  endif
endfun "}}}

" Get the proper Vim encoding option setting from an HTML charset name.
function! tohtml#EncodingFromCharset(encoding) "{{{
  if exists('g:html_encoding_override') && has_key(g:html_encoding_override, a:encoding)
    return g:html_encoding_override[a:encoding]
  elseif has_key(g:tohtml#charset_to_encoding, tolower(a:encoding))
    return g:tohtml#charset_to_encoding[tolower(a:encoding)]
  else
    return ""
  endif
endfun "}}}

let &cpo = s:cpo_sav
unlet s:cpo_sav

" Make sure any patches will probably use consistent indent
"   vim: ts=8 sw=2 sts=2 noet fdm=marker