diff options
Diffstat (limited to 'autoload/vimwiki/html.vim')
-rw-r--r-- | autoload/vimwiki/html.vim | 1587 |
1 files changed, 1587 insertions, 0 deletions
diff --git a/autoload/vimwiki/html.vim b/autoload/vimwiki/html.vim new file mode 100644 index 0000000..b4a2d36 --- /dev/null +++ b/autoload/vimwiki/html.vim @@ -0,0 +1,1587 @@ +" vim:tabstop=2:shiftwidth=2:expandtab:foldmethod=marker:textwidth=79 +" Vimwiki autoload plugin file +" Export to HTML +" Author: Maxim Kim <habamax@gmail.com> +" Home: http://code.google.com/p/vimwiki/ + +" TODO: We need vimwiki abstract syntax tree. If properly designed it wourld +" greatly symplify different syntax to HTML generation. +" +" vimwiki -- --> PDF +" \ / +" markdown -----> AST -----> HTML +" / \ +" mediawiki -- --> Latex +" + +" Load only once {{{ +if exists("g:loaded_vimwiki_html_auto") || &cp + finish +endif +let g:loaded_vimwiki_html_auto = 1 +"}}} + +" UTILITY "{{{ +function s:get_completion_index(sym) "{{{ + for idx in range(1, 5) + if match(g:vimwiki_listsyms, '\C\%'.idx.'v'.a:sym) != -1 + return (idx-1) + endif + endfor + return 0 +endfunction "}}} + +function! s:root_path(subdir) "{{{ + return repeat('../', len(split(a:subdir, '[/\\]'))) +endfunction "}}} + +function! s:syntax_supported() " {{{ + return VimwikiGet('syntax') == "default" +endfunction " }}} + +function! s:remove_blank_lines(lines) " {{{ + while !empty(a:lines) && a:lines[-1] =~ '^\s*$' + call remove(a:lines, -1) + endwhile +endfunction "}}} + +function! s:is_web_link(lnk) "{{{ + if a:lnk =~ '^\%(https://\|http://\|www.\|ftp://\|file://\|mailto:\)' + return 1 + endif + return 0 +endfunction "}}} + +function! s:is_img_link(lnk) "{{{ + if tolower(a:lnk) =~ '\.\%(png\|jpg\|gif\|jpeg\)$' + return 1 + endif + return 0 +endfunction "}}} + +function! s:has_abs_path(fname) "{{{ + if a:fname =~ '\(^.:\)\|\(^/\)' + return 1 + endif + return 0 +endfunction "}}} + +function! s:find_autoload_file(name) " {{{ + for path in split(&runtimepath, ',') + let fname = path.'/autoload/vimwiki/'.a:name + if glob(fname) != '' + return fname + endif + endfor + return '' +endfunction " }}} + +function! s:default_CSS_full_name(path) " {{{ + let path = expand(a:path) + let css_full_name = path.VimwikiGet('css_name') + return css_full_name +endfunction "}}} + +function! s:create_default_CSS(path) " {{{ + let css_full_name = s:default_CSS_full_name(a:path) + if glob(css_full_name) == "" + call vimwiki#base#mkdir(fnamemodify(css_full_name, ':p:h')) + let default_css = s:find_autoload_file('style.css') + if default_css != '' + let lines = readfile(default_css) + call writefile(lines, css_full_name) + echomsg "Default style.css has been created." + endif + endif +endfunction "}}} + +function! s:template_full_name(name) "{{{ + if a:name == '' + let name = VimwikiGet('template_default') + else + let name = a:name + endif + + let fname = expand(VimwikiGet('template_path'). + \ name.VimwikiGet('template_ext')) + + if filereadable(fname) + return fname + else + return '' + endif +endfunction "}}} + +function! s:get_html_template(wikifile, template) "{{{ + " TODO: refactor it!!! + let lines=[] + + if a:template != '' + let template_name = s:template_full_name(a:template) + try + let lines = readfile(template_name) + return lines + catch /E484/ + echomsg 'vimwiki: html template '.template_name. + \ ' does not exist!' + endtry + endif + + let default_tpl = s:template_full_name('') + + if default_tpl == '' + let default_tpl = s:find_autoload_file('default.tpl') + endif + + let lines = readfile(default_tpl) + return lines +endfunction "}}} + +function! s:safe_html_tags(line) "{{{ + let line = substitute(a:line,'<','\<', 'g') + let line = substitute(line,'>','\>', 'g') + return line +endfunction "}}} + +function! s:safe_html(line) "{{{ + " escape & < > when producing HTML text + " s:lt_pattern, s:gt_pattern depend on g:vimwiki_valid_html_tags + " and are set in vimwiki#html#Wiki2HTML() + let line = substitute(a:line, '&', '\&', 'g') + let line = substitute(line,s:lt_pattern,'\<', 'g') + let line = substitute(line,s:gt_pattern,'\>', 'g') + + return line +endfunction "}}} + +function! s:delete_html_files(path) "{{{ + let htmlfiles = split(glob(a:path.'**/*.html'), '\n') + for fname in htmlfiles + " ignore user html files, e.g. search.html,404.html + if stridx(g:vimwiki_user_htmls, fnamemodify(fname, ":t")) >= 0 + continue + endif + + " delete if there is no corresponding wiki file + let subdir = vimwiki#base#subdir(VimwikiGet('path_html'), fname) + let wikifile = VimwikiGet('path').subdir. + \fnamemodify(fname, ":t:r").VimwikiGet('ext') + if filereadable(wikifile) + continue + endif + + try + call delete(fname) + catch + echomsg 'vimwiki: Cannot delete '.fname + endtry + endfor +endfunction "}}} + +function! s:mid(value, cnt) "{{{ + return strpart(a:value, a:cnt, len(a:value) - 2 * a:cnt) +endfunction "}}} + +function! s:subst_func(line, regexp, func) " {{{ + " Substitute text found by regexp with result of + " func(matched) function. + + let pos = 0 + let lines = split(a:line, a:regexp, 1) + let res_line = "" + for line in lines + let res_line = res_line.line + let matched = matchstr(a:line, a:regexp, pos) + if matched != "" + let res_line = res_line.{a:func}(matched) + endif + let pos = matchend(a:line, a:regexp, pos) + endfor + return res_line +endfunction " }}} + +function! s:save_vimwiki_buffer() "{{{ + if &filetype == 'vimwiki' + silent update + endif +endfunction "}}} + +function! s:get_html_toc(toc_list) "{{{ + " toc_list is list of [level, header_text, header_id] + " ex: [[1, "Header", "toc1"], [2, "Header2", "toc2"], ...] + function! s:close_list(toc, plevel, level) "{{{ + let plevel = a:plevel + while plevel > a:level + call add(a:toc, '</ul>') + let plevel -= 1 + endwhile + return plevel + endfunction "}}} + + if empty(a:toc_list) + return [] + endif + + let toc = ['<div class="toc">'] + let level = 0 + let plevel = 0 + for [level, text, id] in a:toc_list + if level > plevel + call add(toc, '<ul>') + elseif level < plevel + let plevel = s:close_list(toc, plevel, level) + endif + + let toc_text = s:process_tags_remove_links(text) + let toc_text = s:process_tags_typefaces(toc_text) + call add(toc, '<li><a href="#'.id.'">'.toc_text.'</a>') + let plevel = level + endfor + call s:close_list(toc, level, 0) + call add(toc, '</div>') + return toc +endfunction "}}} + +" insert toc into dest. +function! s:process_toc(dest, placeholders, toc) "{{{ + let toc_idx = 0 + if !empty(a:placeholders) + for [placeholder, row, idx] in a:placeholders + let [type, param] = placeholder + if type == 'toc' + let toc = a:toc[:] + if !empty(param) + call insert(toc, '<h1>'.param.'</h1>') + endif + let shift = toc_idx * len(toc) + call extend(a:dest, toc, row + shift) + let toc_idx += 1 + endif + endfor + endif +endfunction "}}} + +" get title. +function! s:process_title(placeholders, default_title) "{{{ + if !empty(a:placeholders) + for [placeholder, row, idx] in a:placeholders + let [type, param] = placeholder + if type == 'title' && !empty(param) + return param + endif + endfor + endif + return a:default_title +endfunction "}}} + +function! s:is_html_uptodate(wikifile) "{{{ + let tpl_time = -1 + + let tpl_file = s:template_full_name('') + if tpl_file != '' + let tpl_time = getftime(tpl_file) + endif + + let wikifile = fnamemodify(a:wikifile, ":p") + let htmlfile = expand(VimwikiGet('path_html').VimwikiGet('subdir'). + \fnamemodify(wikifile, ":t:r").".html") + + if getftime(wikifile) <= getftime(htmlfile) && tpl_time <= getftime(htmlfile) + return 1 + endif + return 0 +endfunction "}}} + +function! s:html_insert_contents(html_lines, content) "{{{ + let lines = [] + for line in a:html_lines + if line =~ '%content%' + let parts = split(line, '%content%', 1) + if empty(parts) + call extend(lines, a:content) + else + for idx in range(len(parts)) + call add(lines, parts[idx]) + if idx < len(parts) - 1 + call extend(lines, a:content) + endif + endfor + endif + else + call add(lines, line) + endif + endfor + return lines +endfunction "}}} +"}}} + +" INLINE TAGS "{{{ +function! s:tag_eqin(value) "{{{ + " mathJAX wants \( \) for inline maths + return '\('.s:mid(a:value, 1).'\)' +endfunction "}}} + +function! s:tag_em(value) "{{{ + return '<em>'.s:mid(a:value, 1).'</em>' +endfunction "}}} + +function! s:tag_strong(value) "{{{ + return '<strong>'.s:mid(a:value, 1).'</strong>' +endfunction "}}} + +function! s:tag_todo(value) "{{{ + return '<span class="todo">'.a:value.'</span>' +endfunction "}}} + +function! s:tag_strike(value) "{{{ + return '<del>'.s:mid(a:value, 2).'</del>' +endfunction "}}} + +function! s:tag_super(value) "{{{ + return '<sup><small>'.s:mid(a:value, 1).'</small></sup>' +endfunction "}}} + +function! s:tag_sub(value) "{{{ + return '<sub><small>'.s:mid(a:value, 2).'</small></sub>' +endfunction "}}} + +function! s:tag_code(value) "{{{ + return '<code>'.s:safe_html_tags(s:mid(a:value, 1)).'</code>' +endfunction "}}} + +"function! s:tag_pre(value) "{{{ +" return '<code>'.s:mid(a:value, 3).'</code>' +"endfunction "}}} + +"FIXME dead code? +"function! s:tag_math(value) "{{{ +" return '\['.s:mid(a:value, 3).'\]' +"endfunction "}}} + + +"{{{ v2.0 links +" match n-th ARG within {{URL[|ARG1|ARG2|...]}} " {{{ +" *c,d,e),... +function! vimwiki#html#incl_match_arg(nn_index) + let rx = g:vimwiki_rxWikiInclPrefix. g:vimwiki_rxWikiInclUrl + let rx = rx. repeat(g:vimwiki_rxWikiInclSeparator. g:vimwiki_rxWikiInclArg, a:nn_index-1) + if a:nn_index > 0 + let rx = rx. g:vimwiki_rxWikiInclSeparator. '\zs'. g:vimwiki_rxWikiInclArg. '\ze' + endif + let rx = rx. g:vimwiki_rxWikiInclArgs. g:vimwiki_rxWikiInclSuffix + return rx +endfunction +"}}} + +function! vimwiki#html#linkify_link(src, descr) "{{{ + let src_str = ' href="'.a:src.'"' + let descr = substitute(a:descr,'^\s*\(.*\)\s*$','\1','') + let descr = (descr == "" ? a:src : descr) + let descr_str = (descr =~ g:vimwiki_rxWikiIncl + \ ? s:tag_wikiincl(descr) + \ : descr) + return '<a'.src_str.'>'.descr_str.'</a>' +endfunction "}}} + +function! vimwiki#html#linkify_image(src, descr, verbatim_str) "{{{ + let src_str = ' src="'.a:src.'"' + let descr_str = (a:descr != '' ? ' alt="'.a:descr.'"' : '') + let verbatim_str = (a:verbatim_str != '' ? ' '.a:verbatim_str : '') + return '<img'.src_str.descr_str.verbatim_str.' />' +endfunction "}}} + +function! s:tag_weblink(value) "{{{ + " Weblink Template -> <a href="url">descr</a> + let str = a:value + let url = matchstr(str, g:vimwiki_rxWeblinkMatchUrl) + let descr = matchstr(str, g:vimwiki_rxWeblinkMatchDescr) + let line = vimwiki#html#linkify_link(url, descr) + return line +endfunction "}}} + +function! s:tag_wikiincl(value) "{{{ + " {{imgurl|arg1|arg2}} -> ??? + " {{imgurl}} -> <img src="imgurl"/> + " {{imgurl|descr|style="A"}} -> <img src="imgurl" alt="descr" style="A" /> + " {{imgurl|descr|class="B"}} -> <img src="imgurl" alt="descr" class="B" /> + let str = a:value + " custom transclusions + let line = VimwikiWikiIncludeHandler(str) + " otherwise, assume image transclusion + if line == '' + let url_0 = matchstr(str, g:vimwiki_rxWikiInclMatchUrl) + let descr = matchstr(str, vimwiki#html#incl_match_arg(1)) + let verbatim_str = matchstr(str, vimwiki#html#incl_match_arg(2)) + " resolve url + let [idx, scheme, path, subdir, lnk, ext, url] = + \ vimwiki#base#resolve_scheme(url_0, 1) + " generate html output + " TODO: migrate non-essential debugging messages into g:VimwikiLog + if g:vimwiki_debug > 1 + echom '{{idx='.idx.', scheme='.scheme.', path='.path.', subdir='.subdir.', lnk='.lnk.', ext='.ext.'}}' + endif + + " Issue 343: Image transclusions: schemeless links have .html appended. + " If link is schemeless pass it as it is + if scheme == '' + let url = lnk + endif + + let url = escape(url, '#') + let line = vimwiki#html#linkify_image(url, descr, verbatim_str) + return line + endif + return line +endfunction "}}} + +function! s:tag_wikilink(value) "{{{ + " [[url]] -> <a href="url.html">url</a> + " [[url|descr]] -> <a href="url.html">descr</a> + " [[url|{{...}}]] -> <a href="url.html"> ... </a> + " [[fileurl.ext|descr]] -> <a href="fileurl.ext">descr</a> + " [[dirurl/|descr]] -> <a href="dirurl/index.html">descr</a> + let str = a:value + let url = matchstr(str, g:vimwiki_rxWikiLinkMatchUrl) + let descr = matchstr(str, g:vimwiki_rxWikiLinkMatchDescr) + let descr = (substitute(descr,'^\s*\(.*\)\s*$','\1','') != '' ? descr : url) + + " resolve url + let [idx, scheme, path, subdir, lnk, ext, url] = + \ vimwiki#base#resolve_scheme(url, 1) + + " generate html output + " TODO: migrate non-essential debugging messages into g:VimwikiLog + if g:vimwiki_debug > 1 + echom '[[idx='.idx.', scheme='.scheme.', path='.path.', subdir='.subdir.', lnk='.lnk.', ext='.ext.']]' + endif + let line = vimwiki#html#linkify_link(url, descr) + return line +endfunction "}}} +"}}} + + +function! s:tag_remove_internal_link(value) "{{{ + let value = s:mid(a:value, 2) + + let line = '' + if value =~ '|' + let link_parts = split(value, "|", 1) + else + let link_parts = split(value, "][", 1) + endif + + if len(link_parts) > 1 + if len(link_parts) < 3 + let style = "" + else + let style = link_parts[2] + endif + let line = link_parts[1] + else + let line = value + endif + return line +endfunction "}}} + +function! s:tag_remove_external_link(value) "{{{ + let value = s:mid(a:value, 1) + + let line = '' + if s:is_web_link(value) + let lnkElements = split(value) + let head = lnkElements[0] + let rest = join(lnkElements[1:]) + if rest=="" + let rest=head + endif + let line = rest + elseif s:is_img_link(value) + let line = '<img src="'.value.'" />' + else + " [alskfj sfsf] shouldn't be a link. So return it as it was -- + " enclosed in [...] + let line = '['.value.']' + endif + return line +endfunction "}}} + +function! s:make_tag(line, regexp, func) "{{{ + " Make tags for a given matched regexp. + " Exclude preformatted text and href links. + " FIXME + let patt_splitter = '\(`[^`]\+`\)\|'. + \ '\('.g:vimwiki_rxPreStart.'.\+'.g:vimwiki_rxPreEnd.'\)\|'. + \ '\(<a href.\{-}</a>\)\|'. + \ '\(<img src.\{-}/>\)\|'. + \ '\('.g:vimwiki_rxEqIn.'\)' + + "FIXME FIXME !!! these can easily occur on the same line! + "XXX {{{ }}} ??? obsolete + if '`[^`]\+`' == a:regexp || '{{{.\+}}}' == a:regexp || g:vimwiki_rxEqIn == a:regexp + let res_line = s:subst_func(a:line, a:regexp, a:func) + else + let pos = 0 + " split line with patt_splitter to have parts of line before and after + " href links, preformatted text + " ie: + " hello world `is just a` simple <a href="link.html">type of</a> prg. + " result: + " ['hello world ', ' simple ', 'type of', ' prg'] + let lines = split(a:line, patt_splitter, 1) + let res_line = "" + for line in lines + let res_line = res_line.s:subst_func(line, a:regexp, a:func) + let res_line = res_line.matchstr(a:line, patt_splitter, pos) + let pos = matchend(a:line, patt_splitter, pos) + endfor + endif + return res_line +endfunction "}}} + +function! s:process_tags_remove_links(line) " {{{ + let line = a:line + let line = s:make_tag(line, '\[\[.\{-}\]\]', 's:tag_remove_internal_link') + let line = s:make_tag(line, '\[.\{-}\]', 's:tag_remove_external_link') + return line +endfunction " }}} + +function! s:process_tags_typefaces(line) "{{{ + let line = a:line + let line = s:make_tag(line, g:vimwiki_rxItalic, 's:tag_em') + let line = s:make_tag(line, g:vimwiki_rxBold, 's:tag_strong') + let line = s:make_tag(line, g:vimwiki_rxTodo, 's:tag_todo') + let line = s:make_tag(line, g:vimwiki_rxDelText, 's:tag_strike') + let line = s:make_tag(line, g:vimwiki_rxSuperScript, 's:tag_super') + let line = s:make_tag(line, g:vimwiki_rxSubScript, 's:tag_sub') + let line = s:make_tag(line, g:vimwiki_rxCode, 's:tag_code') + let line = s:make_tag(line, g:vimwiki_rxEqIn, 's:tag_eqin') + return line +endfunction " }}} + +function! s:process_tags_links(line) " {{{ + let line = a:line + let line = s:make_tag(line, g:vimwiki_rxWikiLink, 's:tag_wikilink') + let line = s:make_tag(line, g:vimwiki_rxWikiIncl, 's:tag_wikiincl') + let line = s:make_tag(line, g:vimwiki_rxWeblink, 's:tag_weblink') + return line +endfunction " }}} + +function! s:process_inline_tags(line) "{{{ + let line = s:process_tags_links(a:line) + let line = s:process_tags_typefaces(line) + return line +endfunction " }}} +"}}} + +" BLOCK TAGS {{{ +function! s:close_tag_pre(pre, ldest) "{{{ + if a:pre[0] + call insert(a:ldest, "</pre>") + return 0 + endif + return a:pre +endfunction "}}} + +function! s:close_tag_math(math, ldest) "{{{ + if a:math[0] + call insert(a:ldest, "\\\]") + return 0 + endif + return a:math +endfunction "}}} + +function! s:close_tag_quote(quote, ldest) "{{{ + if a:quote + call insert(a:ldest, "</blockquote>") + return 0 + endif + return a:quote +endfunction "}}} + +function! s:close_tag_para(para, ldest) "{{{ + if a:para + call insert(a:ldest, "</p>") + return 0 + endif + return a:para +endfunction "}}} + +function! s:close_tag_table(table, ldest) "{{{ + " The first element of table list is a string which tells us if table should be centered. + " The rest elements are rows which are lists of columns: + " ['center', + " [ CELL1, CELL2, CELL3 ], + " [ CELL1, CELL2, CELL3 ], + " [ CELL1, CELL2, CELL3 ], + " ] + " And CELLx is: { 'body': 'col_x', 'rowspan': r, 'colspan': c } + + function! s:sum_rowspan(table) "{{{ + let table = a:table + + " Get max cells + let max_cells = 0 + for row in table[1:] + let n_cells = len(row) + if n_cells > max_cells + let max_cells = n_cells + end + endfor + + " Sum rowspan + for cell_idx in range(max_cells) + let rows = 1 + + for row_idx in range(len(table)-1, 1, -1) + if cell_idx >= len(table[row_idx]) + let rows = 1 + continue + endif + + if table[row_idx][cell_idx].rowspan == 0 + let rows += 1 + else " table[row_idx][cell_idx].rowspan == 1 + let table[row_idx][cell_idx].rowspan = rows + let rows = 1 + endif + endfor + endfor + endfunction "}}} + + function! s:sum_colspan(table) "{{{ + for row in a:table[1:] + let cols = 1 + + for cell_idx in range(len(row)-1, 0, -1) + if row[cell_idx].colspan == 0 + let cols += 1 + else "row[cell_idx].colspan == 1 + let row[cell_idx].colspan = cols + let cols = 1 + endif + endfor + endfor + endfunction "}}} + + function! s:close_tag_row(row, header, ldest) "{{{ + call add(a:ldest, '<tr>') + + " Set tag element of columns + if a:header + let tag_name = 'th' + else + let tag_name = 'td' + end + + " Close tag of columns + for cell in a:row + if cell.rowspan == 0 || cell.colspan == 0 + continue + endif + + if cell.rowspan > 1 + let rowspan_attr = ' rowspan="' . cell.rowspan . '"' + else "cell.rowspan == 1 + let rowspan_attr = '' + endif + if cell.colspan > 1 + let colspan_attr = ' colspan="' . cell.colspan . '"' + else "cell.colspan == 1 + let colspan_attr = '' + endif + + call add(a:ldest, '<' . tag_name . rowspan_attr . colspan_attr .'>') + call add(a:ldest, s:process_inline_tags(cell.body)) + call add(a:ldest, '</'. tag_name . '>') + endfor + + call add(a:ldest, '</tr>') + endfunction "}}} + + let table = a:table + let ldest = a:ldest + if len(table) + call s:sum_rowspan(table) + call s:sum_colspan(table) + + if table[0] == 'center' + call add(ldest, "<table class='center'>") + else + call add(ldest, "<table>") + endif + + " Empty lists are table separators. + " Search for the last empty list. All the above rows would be a table header. + " We should exclude the first element of the table list as it is a text tag + " that shows if table should be centered or not. + let head = 0 + for idx in range(len(table)-1, 1, -1) + if empty(table[idx]) + let head = idx + break + endif + endfor + if head > 0 + for row in table[1 : head-1] + if !empty(filter(row, '!empty(v:val)')) + call s:close_tag_row(row, 1, ldest) + endif + endfor + for row in table[head+1 :] + call s:close_tag_row(row, 0, ldest) + endfor + else + for row in table[1 :] + call s:close_tag_row(row, 0, ldest) + endfor + endif + call add(ldest, "</table>") + let table = [] + endif + return table +endfunction "}}} + +function! s:close_tag_list(lists, ldest) "{{{ + while len(a:lists) + let item = remove(a:lists, 0) + call insert(a:ldest, item[0]) + endwhile +endfunction! "}}} + +function! s:close_tag_def_list(deflist, ldest) "{{{ + if a:deflist + call insert(a:ldest, "</dl>") + return 0 + endif + return a:deflist +endfunction! "}}} + +function! s:process_tag_pre(line, pre) "{{{ + " pre is the list of [is_in_pre, indent_of_pre] + "XXX always outputs a single line or empty list! + let lines = [] + let pre = a:pre + let processed = 0 + "XXX huh? + "if !pre[0] && a:line =~ '^\s*{{{[^\(}}}\)]*\s*$' + if !pre[0] && a:line =~ '^\s*{{{' + let class = matchstr(a:line, '{{{\zs.*$') + "FIXME class cannot contain arbitrary strings + let class = substitute(class, '\s\+$', '', 'g') + if class != "" + call add(lines, "<pre ".class.">") + else + call add(lines, "<pre>") + endif + let pre = [1, len(matchstr(a:line, '^\s*\ze{{{'))] + let processed = 1 + elseif pre[0] && a:line =~ '^\s*}}}\s*$' + let pre = [0, 0] + call add(lines, "</pre>") + let processed = 1 + elseif pre[0] + let processed = 1 + "XXX destroys indent in general! + "call add(lines, substitute(a:line, '^\s\{'.pre[1].'}', '', '')) + call add(lines, s:safe_html_tags(a:line)) + endif + return [processed, lines, pre] +endfunction "}}} + +function! s:process_tag_math(line, math) "{{{ + " math is the list of [is_in_math, indent_of_math] + let lines = [] + let math = a:math + let processed = 0 + if !math[0] && a:line =~ '^\s*{{\$[^\(}}$\)]*\s*$' + let class = matchstr(a:line, '{{$\zs.*$') + "FIXME class cannot be any string! + let class = substitute(class, '\s\+$', '', 'g') + " Check the math placeholder (default: displaymath) + let b:vimwiki_mathEnv = matchstr(class, '^%\zs\S\+\ze%') + if b:vimwiki_mathEnv != "" + call add(lines, substitute(class, '^%\(\S\+\)%','\\begin{\1}', '')) + elseif class != "" + call add(lines, "\\\[".class) + else + call add(lines, "\\\[") + endif + let math = [1, len(matchstr(a:line, '^\s*\ze{{\$'))] + let processed = 1 + elseif math[0] && a:line =~ '^\s*}}\$\s*$' + let math = [0, 0] + if b:vimwiki_mathEnv != "" + call add(lines, "\\end{".b:vimwiki_mathEnv."}") + else + call add(lines, "\\\]") + endif + let processed = 1 + elseif math[0] + let processed = 1 + call add(lines, substitute(a:line, '^\s\{'.math[1].'}', '', '')) + endif + return [processed, lines, math] +endfunction "}}} + +function! s:process_tag_quote(line, quote) "{{{ + let lines = [] + let quote = a:quote + let processed = 0 + if a:line =~ '^\s\{4,}\S' + if !quote + call add(lines, "<blockquote>") + let quote = 1 + endif + let processed = 1 + call add(lines, substitute(a:line, '^\s*', '', '')) + elseif quote + call add(lines, "</blockquote>") + let quote = 0 + endif + return [processed, lines, quote] +endfunction "}}} + +function! s:process_tag_list(line, lists) "{{{ + + function! s:add_checkbox(line, rx_list, st_tag, en_tag) "{{{ + let st_tag = a:st_tag + let en_tag = a:en_tag + + let chk = matchlist(a:line, a:rx_list) + if len(chk) > 0 + if len(chk[1])>0 + "wildcard characters are difficult to match correctly + if chk[1] =~ '[.*\\^$~]' + let chk[1] ='\'.chk[1] + endif + " let completion = match(g:vimwiki_listsyms, '\C' . chk[1]) + let completion = s:get_completion_index(chk[1]) + if completion >= 0 && completion <=4 + let st_tag = '<li class="done'.completion.'">' + endif + endif + endif + return [st_tag, en_tag] + endfunction "}}} + + let in_list = (len(a:lists) > 0) + + " If it is not list yet then do not process line that starts from *bold* + " text. + if !in_list + let pos = match(a:line, g:vimwiki_rxBold) + if pos != -1 && strpart(a:line, 0, pos) =~ '^\s*$' + return [0, []] + endif + endif + + let lines = [] + let processed = 0 + + if a:line =~ g:vimwiki_rxListBullet + let lstSym = matchstr(a:line, '[*-]') + let lstTagOpen = '<ul>' + let lstTagClose = '</ul>' + let lstRegExp = g:vimwiki_rxListBullet + elseif a:line =~ g:vimwiki_rxListNumber + let lstSym = '#' + let lstTagOpen = '<ol>' + let lstTagClose = '</ol>' + let lstRegExp = g:vimwiki_rxListNumber + else + let lstSym = '' + let lstTagOpen = '' + let lstTagClose = '' + let lstRegExp = '' + endif + + if lstSym != '' + " To get proper indent level 'retab' the line -- change all tabs + " to spaces*tabstop + let line = substitute(a:line, '\t', repeat(' ', &tabstop), 'g') + let indent = stridx(line, lstSym) + + let checkbox = '\s*\[\(.\?\)\]\s*' + let [st_tag, en_tag] = s:add_checkbox(line, + \ lstRegExp.checkbox, '<li>', '') + + if !in_list + call add(a:lists, [lstTagClose, indent]) + call add(lines, lstTagOpen) + elseif (in_list && indent > a:lists[-1][1]) + let item = remove(a:lists, -1) + call add(lines, item[0]) + + call add(a:lists, [lstTagClose, indent]) + call add(lines, lstTagOpen) + elseif (in_list && indent < a:lists[-1][1]) + while len(a:lists) && indent < a:lists[-1][1] + let item = remove(a:lists, -1) + call add(lines, item[0]) + endwhile + elseif in_list + let item = remove(a:lists, -1) + call add(lines, item[0]) + endif + + call add(a:lists, [en_tag, indent]) + call add(lines, st_tag) + call add(lines, + \ substitute(a:line, lstRegExp.'\%('.checkbox.'\)\?', '', '')) + let processed = 1 + elseif in_list > 0 && a:line =~ '^\s\+\S\+' + if g:vimwiki_list_ignore_newline + call add(lines, a:line) + else + call add(lines, '<br />'.a:line) + endif + let processed = 1 + else + call s:close_tag_list(a:lists, lines) + endif + return [processed, lines] +endfunction "}}} + +function! s:process_tag_def_list(line, deflist) "{{{ + let lines = [] + let deflist = a:deflist + let processed = 0 + let matches = matchlist(a:line, '\(^.*\)::\%(\s\|$\)\(.*\)') + if !deflist && len(matches) > 0 + call add(lines, "<dl>") + let deflist = 1 + endif + if deflist && len(matches) > 0 + if matches[1] != '' + call add(lines, "<dt>".matches[1]."</dt>") + endif + if matches[2] != '' + call add(lines, "<dd>".matches[2]."</dd>") + endif + let processed = 1 + elseif deflist + let deflist = 0 + call add(lines, "</dl>") + endif + return [processed, lines, deflist] +endfunction "}}} + +function! s:process_tag_para(line, para) "{{{ + let lines = [] + let para = a:para + let processed = 0 + if a:line =~ '^\s\{,3}\S' + if !para + call add(lines, "<p>") + let para = 1 + endif + let processed = 1 + call add(lines, a:line) + elseif para && a:line =~ '^\s*$' + call add(lines, "</p>") + let para = 0 + endif + return [processed, lines, para] +endfunction "}}} + +function! s:process_tag_h(line, id) "{{{ + let line = a:line + let processed = 0 + let h_level = 0 + let h_text = '' + let h_id = '' + + if a:line =~ g:vimwiki_rxHeader + let h_level = vimwiki#u#count_first_sym(a:line) + endif + if h_level > 0 + let a:id[h_level] += 1 + " reset higher level ids + for level in range(h_level+1, 6) + let a:id[level] = 0 + endfor + + let centered = 0 + if a:line =~ '^\s\+' + let centered = 1 + endif + + let h_number = '' + for l in range(1, h_level-1) + let h_number .= a:id[l].'.' + endfor + let h_number .= a:id[h_level] + + let h_id = 'toc_'.h_number + + let h_part = '<h'.h_level.' id="'.h_id.'"' + + if centered + let h_part .= ' class="justcenter">' + else + let h_part .= '>' + endif + + let h_text = vimwiki#u#trim(matchstr(line, g:vimwiki_rxHeader)) + + if g:vimwiki_html_header_numbering + let num = matchstr(h_number, + \ '^\(\d.\)\{'.(g:vimwiki_html_header_numbering-1).'}\zs.*') + if !empty(num) + let num .= g:vimwiki_html_header_numbering_sym + endif + let h_text = num.' '.h_text + endif + + let line = h_part.h_text.'</h'.h_level.'>' + let processed = 1 + endif + return [processed, line, h_level, h_text, h_id] +endfunction "}}} + +function! s:process_tag_hr(line) "{{{ + let line = a:line + let processed = 0 + if a:line =~ '^-----*$' + let line = '<hr />' + let processed = 1 + endif + return [processed, line] +endfunction "}}} + +function! s:process_tag_table(line, table) "{{{ + function! s:table_empty_cell(value) "{{{ + let cell = {} + + if a:value =~ '^\s*\\/\s*$' + let cell.body = '' + let cell.rowspan = 0 + let cell.colspan = 1 + elseif a:value =~ '^\s*>\s*$' + let cell.body = '' + let cell.rowspan = 1 + let cell.colspan = 0 + elseif a:value =~ '^\s*$' + let cell.body = ' ' + let cell.rowspan = 1 + let cell.colspan = 1 + else + let cell.body = a:value + let cell.rowspan = 1 + let cell.colspan = 1 + endif + + return cell + endfunction "}}} + + function! s:table_add_row(table, line) "{{{ + if empty(a:table) + if a:line =~ '^\s\+' + let row = ['center', []] + else + let row = ['normal', []] + endif + else + let row = [[]] + endif + return row + endfunction "}}} + + let table = a:table + let lines = [] + let processed = 0 + + if vimwiki#tbl#is_separator(a:line) + call extend(table, s:table_add_row(a:table, a:line)) + let processed = 1 + elseif vimwiki#tbl#is_table(a:line) + call extend(table, s:table_add_row(a:table, a:line)) + + let processed = 1 + " let cells = split(a:line, vimwiki#tbl#cell_splitter(), 1)[1: -2] + let cells = vimwiki#tbl#get_cells(a:line) + call map(cells, 's:table_empty_cell(v:val)') + call extend(table[-1], cells) + else + let table = s:close_tag_table(table, lines) + endif + return [processed, lines, table] +endfunction "}}} + +"}}} + +" }}} + +" WIKI2HTML "{{{ +function! s:parse_line(line, state) " {{{ + let state = {} + let state.para = a:state.para + let state.quote = a:state.quote + let state.pre = a:state.pre[:] + let state.math = a:state.math[:] + let state.table = a:state.table[:] + let state.lists = a:state.lists[:] + let state.deflist = a:state.deflist + let state.placeholder = a:state.placeholder + let state.toc = a:state.toc + let state.toc_id = a:state.toc_id + + let res_lines = [] + + let line = s:safe_html(a:line) + + let processed = 0 + + if !processed + if line =~ g:vimwiki_rxComment + let processed = 1 + endif + endif + + " nohtml -- placeholder + if !processed + if line =~ '^\s*%nohtml' + let processed = 1 + let state.placeholder = ['nohtml'] + endif + endif + + " title -- placeholder + if !processed + if line =~ '^\s*%title' + let processed = 1 + let param = matchstr(line, '^\s*%title\s\zs.*') + let state.placeholder = ['title', param] + endif + endif + + " html template -- placeholder "{{{ + if !processed + if line =~ '^\s*%template' + let processed = 1 + let param = matchstr(line, '^\s*%template\s\zs.*') + let state.placeholder = ['template', param] + endif + endif + "}}} + + " toc -- placeholder "{{{ + if !processed + if line =~ '^\s*%toc' + let processed = 1 + let param = matchstr(line, '^\s*%toc\s\zs.*') + let state.placeholder = ['toc', param] + endif + endif + "}}} + + " pres "{{{ + if !processed + let [processed, lines, state.pre] = s:process_tag_pre(line, state.pre) + " pre is just fine to be in the list -- do not close list item here. + " if processed && len(state.lists) + " call s:close_tag_list(state.lists, lines) + " endif + if !processed + let [processed, lines, state.math] = s:process_tag_math(line, state.math) + endif + if processed && len(state.table) + let state.table = s:close_tag_table(state.table, lines) + endif + if processed && state.deflist + let state.deflist = s:close_tag_def_list(state.deflist, lines) + endif + if processed && state.quote + let state.quote = s:close_tag_quote(state.quote, lines) + endif + if processed && state.para + let state.para = s:close_tag_para(state.para, lines) + endif + call extend(res_lines, lines) + endif + "}}} + + " lists "{{{ + if !processed + let [processed, lines] = s:process_tag_list(line, state.lists) + if processed && state.quote + let state.quote = s:close_tag_quote(state.quote, lines) + endif + if processed && state.pre[0] + let state.pre = s:close_tag_pre(state.pre, lines) + endif + if processed && state.math[0] + let state.math = s:close_tag_math(state.math, lines) + endif + if processed && len(state.table) + let state.table = s:close_tag_table(state.table, lines) + endif + if processed && state.deflist + let state.deflist = s:close_tag_def_list(state.deflist, lines) + endif + if processed && state.para + let state.para = s:close_tag_para(state.para, lines) + endif + + call map(lines, 's:process_inline_tags(v:val)') + + call extend(res_lines, lines) + endif + "}}} + + " headers "{{{ + if !processed + let [processed, line, h_level, h_text, h_id] = s:process_tag_h(line, state.toc_id) + if processed + call s:close_tag_list(state.lists, res_lines) + let state.table = s:close_tag_table(state.table, res_lines) + let state.pre = s:close_tag_pre(state.pre, res_lines) + let state.math = s:close_tag_math(state.math, res_lines) + let state.quote = s:close_tag_quote(state.quote, res_lines) + let state.para = s:close_tag_para(state.para, res_lines) + + let line = s:process_inline_tags(line) + + call add(res_lines, line) + + " gather information for table of contents + call add(state.toc, [h_level, h_text, h_id]) + endif + endif + "}}} + + " tables "{{{ + if !processed + let [processed, lines, state.table] = s:process_tag_table(line, state.table) + call extend(res_lines, lines) + endif + "}}} + + " quotes "{{{ + if !processed + let [processed, lines, state.quote] = s:process_tag_quote(line, state.quote) + if processed && len(state.lists) + call s:close_tag_list(state.lists, lines) + endif + if processed && state.deflist + let state.deflist = s:close_tag_def_list(state.deflist, lines) + endif + if processed && len(state.table) + let state.table = s:close_tag_table(state.table, lines) + endif + if processed && state.pre[0] + let state.pre = s:close_tag_pre(state.pre, lines) + endif + if processed && state.math[0] + let state.math = s:close_tag_math(state.math, lines) + endif + if processed && state.para + let state.para = s:close_tag_para(state.para, lines) + endif + + call map(lines, 's:process_inline_tags(v:val)') + + call extend(res_lines, lines) + endif + "}}} + + " horizontal rules "{{{ + if !processed + let [processed, line] = s:process_tag_hr(line) + if processed + call s:close_tag_list(state.lists, res_lines) + let state.table = s:close_tag_table(state.table, res_lines) + let state.pre = s:close_tag_pre(state.pre, res_lines) + let state.math = s:close_tag_math(state.math, res_lines) + call add(res_lines, line) + endif + endif + "}}} + + " definition lists "{{{ + if !processed + let [processed, lines, state.deflist] = s:process_tag_def_list(line, state.deflist) + + call map(lines, 's:process_inline_tags(v:val)') + + call extend(res_lines, lines) + endif + "}}} + + "" P "{{{ + if !processed + let [processed, lines, state.para] = s:process_tag_para(line, state.para) + if processed && len(state.lists) + call s:close_tag_list(state.lists, lines) + endif + if processed && state.quote + let state.quote = s:close_tag_quote(state.quote, res_lines) + endif + if processed && state.pre[0] + let state.pre = s:close_tag_pre(state.pre, res_lines) + endif + if processed && state.math[0] + let state.math = s:close_tag_math(state.math, res_lines) + endif + if processed && len(state.table) + let state.table = s:close_tag_table(state.table, res_lines) + endif + + call map(lines, 's:process_inline_tags(v:val)') + + call extend(res_lines, lines) + endif + "}}} + + "" add the rest + if !processed + call add(res_lines, line) + endif + + return [res_lines, state] + +endfunction " }}} + +function! s:use_custom_wiki2html() "{{{ + let custom_wiki2html = VimwikiGet('custom_wiki2html') + return !empty(custom_wiki2html) && s:file_exists(custom_wiki2html) +endfunction " }}} + +function! vimwiki#html#CustomWiki2HTML(path, wikifile, force) "{{{ + call vimwiki#base#mkdir(a:path) + echomsg system(VimwikiGet('custom_wiki2html'). ' '. + \ a:force. ' '. + \ VimwikiGet('syntax'). ' '. + \ strpart(VimwikiGet('ext'), 1). ' '. + \ shellescape(a:path, 1). ' '. + \ shellescape(a:wikifile, 1). ' '. + \ shellescape(s:default_CSS_full_name(a:path), 1). ' '. + \ (len(VimwikiGet('template_path')) > 1 ? shellescape(expand(VimwikiGet('template_path')), 1) : '-'). ' '. + \ (len(VimwikiGet('template_default')) > 0 ? VimwikiGet('template_default') : '-'). ' '. + \ (len(VimwikiGet('template_ext')) > 0 ? VimwikiGet('template_ext') : '-'). ' '. + \ (len(VimwikiGet('subdir')) > 0 ? shellescape(s:root_path(VimwikiGet('subdir')), 1) : '-')) +endfunction " }}} + +function! vimwiki#html#Wiki2HTML(path_html, wikifile) "{{{ + + let starttime = reltime() " start the clock + + let done = 0 + + let wikifile = fnamemodify(a:wikifile, ":p") + + let path_html = expand(a:path_html).VimwikiGet('subdir') + let htmlfile = fnamemodify(wikifile, ":t:r").'.html' + + if s:use_custom_wiki2html() + let force = 1 + call vimwiki#html#CustomWiki2HTML(path_html, wikifile, force) + let done = 1 + endif + + if s:syntax_supported() && done == 0 + let lsource = readfile(wikifile) + let ldest = [] + + "if g:vimwiki_debug + " echo 'Generating HTML ... ' + "endif + + call vimwiki#base#mkdir(path_html) + + " nohtml placeholder -- to skip html generation. + let nohtml = 0 + + " template placeholder + let template_name = '' + + " for table of contents placeholders. + let placeholders = [] + + " current state of converter + let state = {} + let state.para = 0 + let state.quote = 0 + let state.pre = [0, 0] " [in_pre, indent_pre] + let state.math = [0, 0] " [in_math, indent_math] + let state.table = [] + let state.deflist = 0 + let state.lists = [] + let state.placeholder = [] + let state.toc = [] + let state.toc_id = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0 } + + " prepare constants for s:safe_html() + let s:lt_pattern = '<' + let s:gt_pattern = '>' + if g:vimwiki_valid_html_tags != '' + let tags = join(split(g:vimwiki_valid_html_tags, '\s*,\s*'), '\|') + let s:lt_pattern = '\c<\%(/\?\%('.tags.'\)\%(\s\{-1}\S\{-}\)\{-}/\?>\)\@!' + let s:gt_pattern = '\c\%(</\?\%('.tags.'\)\%(\s\{-1}\S\{-}\)\{-}/\?\)\@<!>' + endif + + for line in lsource + let oldquote = state.quote + let [lines, state] = s:parse_line(line, state) + + " Hack: There could be a lot of empty strings before s:process_tag_quote + " find out `quote` is over. So we should delete them all. Think of the way + " to refactor it out. + if oldquote != state.quote + call s:remove_blank_lines(ldest) + endif + + if !empty(state.placeholder) + if state.placeholder[0] == 'nohtml' + let nohtml = 1 + break + elseif state.placeholder[0] == 'template' + let template_name = state.placeholder[1] + else + call add(placeholders, [state.placeholder, len(ldest), len(placeholders)]) + endif + let state.placeholder = [] + endif + + call extend(ldest, lines) + endfor + + + if nohtml + echon "\r"."%nohtml placeholder found" + return + endif + + let toc = s:get_html_toc(state.toc) + call s:process_toc(ldest, placeholders, toc) + call s:remove_blank_lines(ldest) + + "" process end of file + "" close opened tags if any + let lines = [] + call s:close_tag_quote(state.quote, lines) + call s:close_tag_para(state.para, lines) + call s:close_tag_pre(state.pre, lines) + call s:close_tag_math(state.math, lines) + call s:close_tag_list(state.lists, lines) + call s:close_tag_def_list(state.deflist, lines) + call s:close_tag_table(state.table, lines) + call extend(ldest, lines) + + let title = s:process_title(placeholders, fnamemodify(a:wikifile, ":t:r")) + + let html_lines = s:get_html_template(a:wikifile, template_name) + + " processing template variables (refactor to a function) + call map(html_lines, 'substitute(v:val, "%title%", "'. title .'", "g")') + call map(html_lines, 'substitute(v:val, "%root_path%", "'. + \ s:root_path(VimwikiGet('subdir')) .'", "g")') + + let css_name = expand(VimwikiGet('css_name')) + let css_name = substitute(css_name, '\', '/', 'g') + call map(html_lines, 'substitute(v:val, "%css%", "'. css_name .'", "g")') + + let enc = &fileencoding + if enc == '' + let enc = &encoding + endif + call map(html_lines, 'substitute(v:val, "%encoding%", "'. enc .'", "g")') + + let html_lines = s:html_insert_contents(html_lines, ldest) " %contents% + + "" make html file. + call writefile(html_lines, path_html.htmlfile) + let done = 1 + + endif + + if done == 0 + echomsg 'vimwiki: conversion to HTML is not supported for this syntax!!!' + return + endif + + " measure the elapsed time + let time1 = vimwiki#u#time(starttime) "XXX + call VimwikiLog_extend('html',[htmlfile,time1]) + "if g:vimwiki_debug + " echon "\r".htmlfile.' written (time: '.time1.'s)' + "endif + + return path_html.htmlfile +endfunction "}}} + + +function! vimwiki#html#WikiAll2HTML(path_html) "{{{ + if !s:syntax_supported() && !s:use_custom_wiki2html() + echomsg 'vimwiki: conversion to HTML is not supported for this syntax!!!' + return + endif + + echomsg 'Saving vimwiki files...' + let save_eventignore = &eventignore + let &eventignore = "all" + let cur_buf = bufname('%') + bufdo call s:save_vimwiki_buffer() + exe 'buffer '.cur_buf + let &eventignore = save_eventignore + + let path_html = expand(a:path_html) + call vimwiki#base#mkdir(path_html) + + echomsg 'Deleting non-wiki html files...' + call s:delete_html_files(path_html) + + echomsg 'Converting wiki to html files...' + let setting_more = &more + setlocal nomore + + " temporarily adjust current_subdir global state variable + let current_subdir = VimwikiGet('subdir') + let current_invsubdir = VimwikiGet('invsubdir') + + let wikifiles = split(glob(VimwikiGet('path').'**/*'.VimwikiGet('ext')), '\n') + for wikifile in wikifiles + let wikifile = fnamemodify(wikifile, ":p") + + " temporarily adjust 'subdir' and 'invsubdir' state variables + let subdir = vimwiki#base#subdir(VimwikiGet('path'), wikifile) + call VimwikiSet('subdir', subdir) + call VimwikiSet('invsubdir', vimwiki#base#invsubdir(subdir)) + + if !s:is_html_uptodate(wikifile) + echomsg 'Processing '.wikifile + + call vimwiki#html#Wiki2HTML(path_html, wikifile) + else + echomsg 'Skipping '.wikifile + endif + endfor + " reset 'subdir' state variable + call VimwikiSet('subdir', current_subdir) + call VimwikiSet('invsubdir', current_invsubdir) + + call s:create_default_CSS(path_html) + echomsg 'Done!' + + let &more = setting_more +endfunction "}}} + +function! s:file_exists(fname) "{{{ + return !empty(getftype(expand(a:fname))) +endfunction "}}} + +" uses VimwikiGet('path') +function! vimwiki#html#get_wikifile_url(wikifile) "{{{ + return VimwikiGet('path_html'). + \ vimwiki#base#subdir(VimwikiGet('path'), a:wikifile). + \ fnamemodify(a:wikifile, ":t:r").'.html' +endfunction "}}} + +function! vimwiki#html#PasteUrl(wikifile) "{{{ + execute 'r !echo file://'.vimwiki#html#get_wikifile_url(a:wikifile) +endfunction "}}} + +function! vimwiki#html#CatUrl(wikifile) "{{{ + execute '!echo file://'.vimwiki#html#get_wikifile_url(a:wikifile) +endfunction "}}} +"}}} |