Update runtime files.

This commit is contained in:
Bram Moolenaar
2023-02-02 13:59:48 +00:00
parent 685bf83b73
commit be4e01637e
63 changed files with 1245 additions and 693 deletions

View File

@ -2,12 +2,12 @@ vim9script
# Language: Vim script
# Maintainer: github user lacygoill
# Last Change: 2023 Jan 03
# Last Change: 2023 Feb 01
# NOTE: Whenever you change the code, make sure the tests are still passing:
#
# $ cd runtime/indent/
# $ make clean; make test || vimdiff testdir/vim.{fail,ok}
# $ make clean; make test || vimdiff testdir/vim.{ok,fail}
# Config {{{1
@ -112,6 +112,10 @@ const DICT_KEY: string = '^\s*\%('
.. '\)'
.. ':\%(\s\|$\)'
# NOT_A_DICT_KEY {{{3
const NOT_A_DICT_KEY: string = ':\@!'
# END_OF_COMMAND {{{3
const END_OF_COMMAND: string = $'\s*\%($\|||\@!\|{INLINE_COMMENT}\)'
@ -144,19 +148,43 @@ const HEREDOC_OPERATOR: string = '\s=<<\s\@=\%(\s\+\%(trim\|eval\)\)\{,2}'
#
# But sometimes, it can be too costly and cause `E363` to be given.
const PATTERN_DELIMITER: string = '[-+*/%]\%(=\s\)\@!'
# QUOTE {{{3
const QUOTE: string = '["'']'
# }}}2
# Syntaxes {{{2
# ASSIGNS_HEREDOC {{{3
# BLOCKS {{{3
const ASSIGNS_HEREDOC: string = $'^\%({COMMENT}\)\@!.*\%({HEREDOC_OPERATOR}\)\s\+\zs[A-Z]\+{END_OF_LINE}'
const BLOCKS: list<list<string>> = [
['if', 'el\%[se]', 'elseif\=', 'en\%[dif]'],
['for', 'endfor\='],
['wh\%[ile]', 'endw\%[hile]'],
['try', 'cat\%[ch]', 'fina\|finally\=', 'endt\%[ry]'],
['def', 'enddef'],
['fu\%[nction](\@!', 'endf\%[unction]'],
['class', 'endclass'],
['interface', 'endinterface'],
['enum', 'endenum'],
['aug\%[roup]\%(\s\+[eE][nN][dD]\)\@!\s\+\S\+', 'aug\%[roup]\s\+[eE][nN][dD]'],
]
# CD_COMMAND {{{3
# MODIFIERS {{{3
const CD_COMMAND: string = $'\<[lt]\=cd!\=\s\+-{END_OF_COMMAND}'
# some keywords can be prefixed by modifiers (e.g. `def` can be prefixed by `export`)
const MODIFIERS: dict<string> = {
def: ['export', 'static'],
class: ['export', 'abstract', 'export abstract'],
interface: ['export'],
}
# ...
# class: ['export', 'abstract', 'export abstract'],
# ...
# →
# ...
# class: '\%(export\|abstract\|export\s\+abstract\)\s\+',
# ...
->map((_, mods: list<string>): string =>
'\%(' .. mods
->join('\|')
->substitute('\s\+', '\\s\\+', 'g')
.. '\)' .. '\s\+')
# HIGHER_ORDER_COMMAND {{{3
@ -174,58 +202,102 @@ patterns =<< trim eval END
g\%[lobal]!\={PATTERN_DELIMITER}.*
v\%[global]!\={PATTERN_DELIMITER}.*
END
const HIGHER_ORDER_COMMAND: string = $'\%(^\|{BAR_SEPARATION}\)\s*\<\%(' .. patterns->join('\|') .. '\):\@!'
# MAPPING_COMMAND {{{3
const HIGHER_ORDER_COMMAND: string = $'\%(^\|{BAR_SEPARATION}\)\s*\<\%({patterns->join('\|')}\){NOT_A_DICT_KEY}'
const MAPPING_COMMAND: string = '\%(\<sil\%[ent]!\=\s\+\)\=\<[nvxsoilct]\=\%(nore\|un\)map!\=\s'
# START_MIDDLE_END {{{3
# NORMAL_COMMAND {{{3
# Let's derive this constant from `BLOCKS`:
#
# [['if', 'el\%[se]', 'elseif\=', 'en\%[dif]'],
# ['for', 'endfor\='],
# ...,
# [...]]
# →
# {
# 'for': ['for', '', 'endfor\='],
# 'endfor': ['for', '', 'endfor\='],
# 'if': ['if', 'el\%[se]\|elseif\=', 'en\%[dif]'],
# 'else': ['if', 'el\%[se]\|elseif\=', 'en\%[dif]'],
# 'elseif': ['if', 'el\%[se]\|elseif\=', 'en\%[dif]'],
# 'endif': ['if', 'el\%[se]\|elseif\=', 'en\%[dif]'],
# ...
# }
var START_MIDDLE_END: dict<list<string>>
const NORMAL_COMMAND: string = '\<norm\%[al]!\=\s*\S\+$'
def Unshorten(kwd: string): string
return BlockStartKeyword(kwd)
enddef
# PLUS_MINUS_COMMAND {{{3
def BlockStartKeyword(line: string): string
var kwd: string = line->matchstr('\l\+')
return fullcommand(kwd, false)
enddef
# In legacy, the `:+` and `:-` commands are not required to be preceded by a colon.
# As a result, when `+` or `-` is alone on a line, there is ambiguity.
# It might be an operator or a command.
# To not break the indentation in legacy scripts, we might need to consider such
# lines as commands.
const PLUS_MINUS_COMMAND: string = '^\s*[+-]\s*$'
{
for kwds: list<string> in BLOCKS
var [start: string, middle: string, end: string] = [kwds[0], '', kwds[-1]]
if MODIFIERS->has_key(start->Unshorten())
start = $'\%({MODIFIERS[start]}\)\={start}'
endif
if kwds->len() > 2
middle = kwds[1 : -2]->join('\|')
endif
for kwd: string in kwds
START_MIDDLE_END->extend({[kwd->Unshorten()]: [start, middle, end]})
endfor
endfor
}
START_MIDDLE_END = START_MIDDLE_END
->map((_, kwds: list<string>) =>
kwds->map((_, kwd: string) => kwd == ''
? ''
: $'\%(^\|{BAR_SEPARATION}\|\<sil\%[ent]\|{HIGHER_ORDER_COMMAND}\)\s*'
.. $'\<\%({kwd}\)\>\%(\s*{OPERATOR}\)\@!'))
lockvar! START_MIDDLE_END
# ENDS_BLOCK {{{3
const ENDS_BLOCK: string = '^\s*\%('
.. 'en\%[dif]'
.. '\|' .. 'endfor\='
.. '\|' .. 'endw\%[hile]'
.. '\|' .. 'endt\%[ry]'
.. '\|' .. 'enddef'
.. '\|' .. 'endclass'
.. '\|' .. 'endf\%[unction]'
.. '\|' .. 'aug\%[roup]\s\+[eE][nN][dD]'
.. BLOCKS
->copy()
->map((_, kwds: list<string>): string => kwds[-1])
->join('\|')
.. '\|' .. CLOSING_BRACKET
.. $'\){END_OF_COMMAND}'
# ENDS_BLOCK_OR_CLAUSE {{{3
patterns =<< trim END
en\%[dif]
el\%[se]
endfor\=
endclass
endw\%[hile]
endt\%[ry]
fina\|finally\=
enddef
endf\%[unction]
aug\%[roup]\s\+[eE][nN][dD]
END
patterns = BLOCKS
->copy()
->map((_, kwds: list<string>) => kwds[1 :])
->flattennew()
# `catch` and `elseif` need to be handled as special cases
->filter((_, pat: string): bool => pat->Unshorten() !~ '^\%(catch\|elseif\)\>')
const ENDS_BLOCK_OR_CLAUSE: string = '^\s*\%(' .. patterns->join('\|') .. $'\){END_OF_COMMAND}'
.. $'\|^\s*cat\%[ch]\%(\s\+\({PATTERN_DELIMITER}\).*\1\)\={END_OF_COMMAND}'
.. $'\|^\s*elseif\=\>\%({OPERATOR}\)\@!'
# STARTS_NAMED_BLOCK {{{3
patterns = []
{
for kwds: list<string> in BLOCKS
for kwd: string in kwds[0 : -2]
if MODIFIERS->has_key(kwd->Unshorten())
patterns += [$'\%({MODIFIERS[kwd]}\)\={kwd}']
else
patterns += [kwd]
endif
endfor
endfor
}
const STARTS_NAMED_BLOCK: string = $'^\s*\%(sil\%[ent]\s\+\)\=\%({patterns->join('\|')}\)\>{NOT_A_DICT_KEY}'
# STARTS_CURLY_BLOCK {{{3
# TODO: `{` alone on a line is not necessarily the start of a block.
@ -238,73 +310,57 @@ const STARTS_CURLY_BLOCK: string = '\%('
.. '\|' .. $'^\%(\s*\|.*{BAR_SEPARATION}\s*\)\%(com\%[mand]\|au\%[tocmd]\).*\zs\s{{'
.. '\)' .. END_OF_COMMAND
# STARTS_NAMED_BLOCK {{{3
# All of these will be used at the start of a line (or after a bar).
# NOTE: Don't replace `\%x28` with `(`.{{{
#
# Otherwise, the paren would be unbalanced which might cause syntax highlighting
# issues much later in the code of the current script (sometimes, the syntax
# highlighting plugin fails to correctly recognize a heredoc which is far away
# and/or not displayed because inside a fold).
# }}}
patterns =<< trim END
if
el\%[se]
elseif\=
for
class
wh\%[ile]
try
cat\%[ch]
fina\|finally\=
fu\%[nction]\%x28\@!
\%(export\s\+\)\=def
aug\%[roup]\%(\s\+[eE][nN][dD]\)\@!\s\+\S\+
END
const STARTS_NAMED_BLOCK: string = '^\s*\%(sil\%[ent]\s\+\)\=\%(' .. patterns->join('\|') .. '\)\>:\@!'
# STARTS_FUNCTION {{{3
const STARTS_FUNCTION: string = '^\s*\%(export\s\+\)\=def\>:\@!'
const STARTS_FUNCTION: string = $'^\s*\%({MODIFIERS.def}\)\=def\>{NOT_A_DICT_KEY}'
# ENDS_FUNCTION {{{3
const ENDS_FUNCTION: string = $'^\s*enddef\>:\@!{END_OF_COMMAND}'
const ENDS_FUNCTION: string = $'^\s*enddef\>{END_OF_COMMAND}'
# START_MIDDLE_END {{{3
# ASSIGNS_HEREDOC {{{3
const START_MIDDLE_END: dict<list<string>> = {
if: ['if', 'el\%[se]\|elseif\=', 'en\%[dif]'],
else: ['if', 'el\%[se]\|elseif\=', 'en\%[dif]'],
elseif: ['if', 'el\%[se]\|elseif\=', 'en\%[dif]'],
endif: ['if', 'el\%[se]\|elseif\=', 'en\%[dif]'],
for: ['for', '', 'endfor\='],
endfor: ['for', '', 'endfor\='],
class: ['class', '', 'endclass'],
endclass: ['class', '', 'endclass'],
while: ['wh\%[ile]', '', 'endw\%[hile]'],
endwhile: ['wh\%[ile]', '', 'endw\%[hile]'],
try: ['try', 'cat\%[ch]\|fina\|finally\=', 'endt\%[ry]'],
catch: ['try', 'cat\%[ch]\|fina\|finally\=', 'endt\%[ry]'],
finally: ['try', 'cat\%[ch]\|fina\|finally\=', 'endt\%[ry]'],
endtry: ['try', 'cat\%[ch]\|fina\|finally\=', 'endt\%[ry]'],
def: ['\%(export\s\+\)\=def', '', 'enddef'],
enddef: ['\%(export\s\+\)\=def', '', 'enddef'],
function: ['fu\%[nction]', '', 'endf\%[unction]'],
endfunction: ['fu\%[nction]', '', 'endf\%[unction]'],
augroup: ['aug\%[roup]\%(\s\+[eE][nN][dD]\)\@!\s\+\S\+', '', 'aug\%[roup]\s\+[eE][nN][dD]'],
}->map((_, kwds: list<string>) =>
kwds->map((_, kwd: string) => kwd == ''
? ''
: $'\%(^\|{BAR_SEPARATION}\|\<sil\%[ent]\|{HIGHER_ORDER_COMMAND}\)\s*'
.. $'\%({printf('\C\<\%%(%s\)\>:\@!\%%(\s*%s\)\@!', kwd, OPERATOR)}\)'))
const ASSIGNS_HEREDOC: string = $'^\%({COMMENT}\)\@!.*\%({HEREDOC_OPERATOR}\)\s\+\zs[A-Z]\+{END_OF_LINE}'
# PLUS_MINUS_COMMAND {{{3
# In legacy, the `:+` and `:-` commands are not required to be preceded by a colon.
# As a result, when `+` or `-` is alone on a line, there is ambiguity.
# It might be an operator or a command.
# To not break the indentation in legacy scripts, we might need to consider such
# lines as commands.
const PLUS_MINUS_COMMAND: string = '^\s*[+-]\s*$'
# TRICKY_COMMANDS {{{3
# Some commands are tricky because they accept an argument which can be
# conflated with an operator. Examples:
#
# argdelete *
# cd -
# normal! ==
# nunmap <buffer> (
#
# TODO: Other commands might accept operators as argument. Handle them too.
patterns =<< trim eval END
{'\'}<argd\%[elete]\s\+\*\s*$
\<[lt]\=cd!\=\s\+-\s*$
\<norm\%[al]!\=\s*\S\+$
\%(\<sil\%[ent]!\=\s\+\)\=\<[nvxsoilct]\=\%(nore\|un\)map!\=\s
{PLUS_MINUS_COMMAND}
END
const TRICKY_COMMANDS: string = patterns->join('\|')
# }}}2
# EOL {{{2
# OPENING_BRACKET_AT_EOL {{{3
const OPENING_BRACKET_AT_EOL: string = OPENING_BRACKET .. END_OF_VIM9_LINE
# CLOSING_BRACKET_AT_EOL {{{3
const CLOSING_BRACKET_AT_EOL: string = CLOSING_BRACKET .. END_OF_VIM9_LINE
# COMMA_AT_EOL {{{3
const COMMA_AT_EOL: string = $',{END_OF_VIM9_LINE}'
@ -392,6 +448,7 @@ export def Expr(lnum = v:lnum): number # {{{2
endif
if line_A->AtStartOf('FuncHeader')
&& !IsInInterface()
line_A.lnum->CacheFuncHeader()
elseif line_A.lnum->IsInside('FuncHeader')
return b:vimindent.startindent + 2 * shiftwidth()
@ -430,6 +487,7 @@ export def Expr(lnum = v:lnum): number # {{{2
if line_A.text->ContinuesBelowBracketBlock(line_B, past_bracket_block)
&& line_A.text !~ CLOSING_BRACKET_AT_SOL
return past_bracket_block.startindent
+ (past_bracket_block.startline =~ STARTS_NAMED_BLOCK ? 2 * shiftwidth() : 0)
endif
# Problem: If we press `==` on the line right below the start of a multiline
@ -438,6 +496,18 @@ export def Expr(lnum = v:lnum): number # {{{2
if line_B->EndsWithLambdaArrow()
return Indent(line_B.lnum) + shiftwidth() + IndentMoreInBracketBlock()
endif
# FIXME: Similar issue here:
#
# var x = []
# ->filter((_, _) =>
# true)
# ->items()
#
# Press `==` on last line.
# Expected: The `->items()` line is indented like `->filter(...)`.
# Actual: It's indented like `true)`.
# Is it worth fixing? `=ip` gives the correct indentation, because then the
# cache is used.
# Don't move this block before the heredoc one.{{{
#
@ -536,8 +606,13 @@ def Offset( # {{{2
line_B: dict<any>,
): number
if line_B->AtStartOf('FuncHeader')
&& IsInInterface()
return 0
# increase indentation inside a block
if line_B.text =~ STARTS_NAMED_BLOCK || line_B->EndsWithCurlyBlock()
elseif line_B.text =~ STARTS_NAMED_BLOCK
|| line_B->EndsWithCurlyBlock()
# But don't indent if the line starting the block also closes it.
if line_B->AlsoClosesBlock()
return 0
@ -807,11 +882,6 @@ def Indent(lnum: number): number # {{{3
return indent(lnum)
enddef
def BlockStartKeyword(line: string): string # {{{3
var kwd: string = line->matchstr('\l\+')
return fullcommand(kwd, false)
enddef
def MatchingOpenBracket(line: dict<any>): number # {{{3
var end: string = line.text->matchstr(CLOSING_BRACKET)
var start: string = {']': '[', '}': '{', ')': '('}[end]
@ -908,7 +978,8 @@ def SearchPair( # {{{3
if end == '[' || end == ']'
e = e->escape('[]')
endif
return searchpair(s, middle, e, flags, (): bool => InCommentOrString(), stopline, TIMEOUT)
return searchpair('\C' .. s, (middle == '' ? '' : '\C' .. middle), '\C' .. e,
flags, (): bool => InCommentOrString(), stopline, TIMEOUT)
enddef
def SearchPairStart( # {{{3
@ -1016,6 +1087,10 @@ def IsInThisBlock(line_A: dict<any>, lnum: number): bool # {{{3
return line_A.lnum <= end
enddef
def IsInInterface(): bool # {{{3
return SearchPair('interface', '', 'endinterface', 'nW') > 0
enddef
def IsFirstLineOfCommand(line_1: dict<any>, line_2: dict<any>): bool # {{{3
if line_1.text->Is_IN_KeywordForLoop(line_2.text)
return false
@ -1096,6 +1171,10 @@ def EndsWithOpeningBracket(line: dict<any>): bool # {{{3
return NonCommentedMatch(line, OPENING_BRACKET_AT_EOL)
enddef
def EndsWithClosingBracket(line: dict<any>): bool # {{{3
return NonCommentedMatch(line, CLOSING_BRACKET_AT_EOL)
enddef
def NonCommentedMatch(line: dict<any>, pat: string): bool # {{{3
# Could happen if there is no code above us, and we're not on the 1st line.
# In that case, `PrevCodeLine()` returns `{lnum: 0, line: ''}`.
@ -1103,16 +1182,6 @@ def NonCommentedMatch(line: dict<any>, pat: string): bool # {{{3
return false
endif
if line.text =~ PLUS_MINUS_COMMAND
return false
endif
# In `argdelete *`, `*` is not a multiplication operator.
# TODO: Other commands can accept `*` as an argument. Handle them too.
if line.text =~ '\<argd\%[elete]\s\+\*\s*$'
return false
endif
# Technically, that's wrong. A line might start with a range and end with a
# line continuation symbol. But it's unlikely. And it's useful to assume the
# opposite because it prevents us from conflating a mark with an operator or
@ -1179,24 +1248,7 @@ def NonCommentedMatch(line: dict<any>, pat: string): bool # {{{3
return false
endif
# `:help cd-`
if line.text =~ CD_COMMAND
return false
endif
# At the end of a mapping, any character might appear; e.g. a paren:
#
# nunmap <buffer> (
#
# Don't conflate this with a line continuation symbol.
if line.text =~ MAPPING_COMMAND
return false
endif
# not a comparison operator
# vv
# normal! ==
if line.text =~ NORMAL_COMMAND
if line.text =~ TRICKY_COMMANDS
return false
endif

View File

@ -1,6 +1,6 @@
" Vim autoload file for the tohtml plugin.
" Maintainer: Ben Fritz <fritzophrenic@gmail.com>
" Last Change: 2019 Aug 16
" Last Change: 2023 Jan 01
"
" Additional contributors:
"
@ -351,63 +351,65 @@ func! tohtml#Diff2HTML(win_list, buf_list) "{{{
let s:old_magic = &magic
set magic
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 html = []
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)
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, '<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();"' : '').'>')
call add(html, "<table ".(s:settings.use_css? "" : "border='1' width='100%' ")."id='vimCodeElement".s:settings.id_suffix."'>")
call add(html, '<tr>')
@ -430,47 +432,53 @@ func! tohtml#Diff2HTML(win_list, buf_list) "{{{
" 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 body_line == ''
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
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
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
call insert(style, a_style, insert_index)
let insert_index += 1
endif
endfor
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
" 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
" 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_
let temp = getline(1,'$')
" clean out id on the main content container because we already set it on
" the table
@ -478,7 +486,11 @@ func! tohtml#Diff2HTML(win_list, buf_list) "{{{
" 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?
normal! 2u
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
@ -495,17 +507,23 @@ func! tohtml#Diff2HTML(win_list, buf_list) "{{{
quit!
endfor
let html[body_line_num] = body_line
if !s:settings.no_doc
let html[body_line_num] = body_line
endif
call add(html, '</tr>')
call add(html, '</table>')
call add(html, s:body_end_line)
call add(html, '</html>')
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.
call add(html, '<!-- vim: set foldmethod=manual : -->')
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")
@ -542,129 +560,131 @@ func! tohtml#Diff2HTML(win_list, buf_list) "{{{
call append(0, html)
if len(style) > 0
1
let style_start = search('^</head>')-1
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 {{{
" 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
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
" insert script closing tag if needed
if s:uses_script
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;",
\ " }",
\ '',
\ s:settings.use_xhtml ? '//]]>' : '-->',
\ "</script>"
\ ])
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 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;",
\ "}"
\ ])
" 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: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:uses_script
" insert script tag if needed
call append(style_start, [
\ "<script" . (s:html5 ? "" : " type='text/javascript'") . ">",
\ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"])
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 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 "}}}
" 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