spacevim/autoload/SpaceVim/api/web/xml.vim
JIe 2bb7059579
Some checks failed
Detach Plugins / check (FlyGrep.vim) (push) Has been cancelled
Detach Plugins / check (GitHub.vim) (push) Has been cancelled
Detach Plugins / check (JavaUnit.vim) (push) Has been cancelled
Detach Plugins / check (SourceCounter.vim) (push) Has been cancelled
Detach Plugins / check (cpicker.nvim) (push) Has been cancelled
Detach Plugins / check (dein-ui.vim) (push) Has been cancelled
Detach Plugins / check (git.vim) (push) Has been cancelled
Detach Plugins / check (iedit.vim) (push) Has been cancelled
Detach Plugins / check (scrollbar.vim) (push) Has been cancelled
Detach Plugins / check (vim-chat) (push) Has been cancelled
Detach Plugins / check (vim-cheat) (push) Has been cancelled
Detach Plugins / check (vim-todo) (push) Has been cancelled
Detach Plugins / check (xmake.vim) (push) Has been cancelled
test / Linux (nvim, nightly) (push) Has been cancelled
test / Linux (nvim, v0.3.8) (push) Has been cancelled
test / Linux (nvim, v0.4.0) (push) Has been cancelled
test / Linux (nvim, v0.4.2) (push) Has been cancelled
test / Linux (nvim, v0.4.3) (push) Has been cancelled
test / Linux (nvim, v0.4.4) (push) Has been cancelled
test / Linux (nvim, v0.5.0) (push) Has been cancelled
test / Linux (nvim, v0.5.1) (push) Has been cancelled
test / Linux (nvim, v0.6.0) (push) Has been cancelled
test / Linux (nvim, v0.6.1) (push) Has been cancelled
test / Linux (nvim, v0.7.0) (push) Has been cancelled
test / Linux (nvim, v0.7.2) (push) Has been cancelled
test / Linux (nvim, v0.8.0) (push) Has been cancelled
test / Linux (nvim, v0.8.1) (push) Has been cancelled
test / Linux (nvim, v0.8.2) (push) Has been cancelled
test / Linux (nvim, v0.8.3) (push) Has been cancelled
test / Linux (nvim, v0.9.0) (push) Has been cancelled
test / Linux (nvim, v0.9.1) (push) Has been cancelled
test / Linux (true, vim, v7.4.052) (push) Has been cancelled
test / Linux (true, vim, v7.4.1689) (push) Has been cancelled
test / Linux (true, vim, v7.4.629) (push) Has been cancelled
test / Linux (true, vim, v8.0.0027) (push) Has been cancelled
test / Linux (true, vim, v8.0.0183) (push) Has been cancelled
test / Linux (vim, nightly) (push) Has been cancelled
test / Linux (vim, v8.0.0184) (push) Has been cancelled
test / Linux (vim, v8.0.1453) (push) Has been cancelled
test / Linux (vim, v8.1.2269) (push) Has been cancelled
test / Linux (vim, v8.2.2434) (push) Has been cancelled
test / Linux (vim, v8.2.3995) (push) Has been cancelled
test / Windows (nvim, nightly) (push) Has been cancelled
test / Windows (nvim, v0.3.8) (push) Has been cancelled
test / Windows (nvim, v0.4.2) (push) Has been cancelled
test / Windows (nvim, v0.4.3) (push) Has been cancelled
test / Windows (nvim, v0.4.4) (push) Has been cancelled
test / Windows (nvim, v0.5.0) (push) Has been cancelled
test / Windows (nvim, v0.5.1) (push) Has been cancelled
test / Windows (nvim, v0.6.0) (push) Has been cancelled
test / Windows (nvim, v0.6.1) (push) Has been cancelled
test / Windows (nvim, v0.7.0) (push) Has been cancelled
test / Windows (nvim, v0.7.2) (push) Has been cancelled
test / Windows (nvim, v0.8.0) (push) Has been cancelled
test / Windows (nvim, v0.8.1) (push) Has been cancelled
test / Windows (nvim, v0.8.2) (push) Has been cancelled
test / Windows (nvim, v0.8.3) (push) Has been cancelled
test / Windows (nvim, v0.9.0) (push) Has been cancelled
test / Windows (nvim, v0.9.1) (push) Has been cancelled
test / Windows (vim, nightly) (push) Has been cancelled
test / Windows (vim, v7.4.1185) (push) Has been cancelled
test / Windows (vim, v7.4.1689) (push) Has been cancelled
test / Windows (vim, v8.0.0027) (push) Has been cancelled
test / Windows (vim, v8.0.1453) (push) Has been cancelled
test / Windows (vim, v8.1.2269) (push) Has been cancelled
test / Windows (vim, v8.2.2434) (push) Has been cancelled
test / Windows (vim, v8.2.3995) (push) Has been cancelled
docker / docker (push) Has been cancelled
mirror / check (coding) (push) Has been cancelled
mirror / check (gitee) (push) Has been cancelled
mirror / check (gitlab) (push) Has been cancelled
init
2024-08-21 14:17:26 +08:00

337 lines
9.6 KiB
VimL

"=============================================================================
" xml.vim --- SpaceVim xml API
" Copyright (c) 2016-2023 Wang Shidong & Contributors
" Author: Wang Shidong < wsdjeg@outlook.com >
" URL: https://spacevim.org
" License: GPLv3
"=============================================================================
let s:save_cpo = &cpo
set cpo&vim
let s:self = {}
let s:HTTP = SpaceVim#api#import('web#http')
let s:template = { 'name': '', 'attr': {}, 'child': [] }
function! s:nr2byte(nr) abort
if a:nr < 0x80
return nr2char(a:nr)
elseif a:nr < 0x800
return nr2char(a:nr/64+192).nr2char(a:nr%64+128)
else
return nr2char(a:nr/4096%16+224).nr2char(a:nr/64%64+128).nr2char(a:nr%64+128)
endif
endfunction
function! s:nr2enc_char(charcode) abort
if &encoding ==# 'utf-8'
return nr2char(a:charcode)
endif
let char = s:nr2byte(a:charcode)
if strlen(char) > 1
let char = strtrans(iconv(char, 'utf-8', &encoding))
endif
return char
endfunction
function! s:nr2hex(nr) abort
let n = a:nr
let r = ''
while n
let r = '0123456789ABCDEF'[n % 16] . r
let n = n / 16
endwhile
return r
endfunction
function! s:decodeEntityReference(str, ...) abort
let str = a:str
let str = substitute(str, '&gt;', '>', 'g')
let str = substitute(str, '&lt;', '<', 'g')
if get(g:, 'webapi#xml#decodeAsHTML', 0)
let str = substitute(str, '&quot;', '"', 'g')
let str = substitute(str, '&apos;', "'", 'g')
let str = substitute(str, '&nbsp;', ' ', 'g')
let str = substitute(str, '&yen;', '\&#65509;', 'g')
endif
let str = substitute(str, '&#x\([0-9a-fA-F]\+\);', '\=s:nr2enc_char("0x".submatch(1))', 'g')
let str = substitute(str, '&#\(\d\+\);', '\=s:nr2enc_char(submatch(1))', 'g')
let str = substitute(str, '&amp;', '\&', 'g')
return str
endfunction
function! s:encodeEntityReference(str) abort
let str = a:str
let str = substitute(str, '&', '\&amp;', 'g')
let str = substitute(str, '>', '\&gt;', 'g')
let str = substitute(str, '<', '\&lt;', 'g')
let str = substitute(str, '"', '\&#34;', 'g')
"let str = substitute(str, "\n", '\&#x0d;', 'g')
"let str = substitute(str, '"', '&quot;', 'g')
"let str = substitute(str, "'", '&apos;', 'g')
"let str = substitute(str, ' ', '&nbsp;', 'g')
return str
endfunction
function! s:matchNode(node, cond) abort
if type(a:cond) == 1 && a:node.name == a:cond
return 1
endif
if type(a:cond) == 2
return a:cond(a:node)
endif
if type(a:cond) == 3
let ret = 1
for l:R in a:cond
if !s:matchNode(a:node, l:R) | let ret = 0 | endif
unlet l:R
endfor
return ret
endif
if type(a:cond) == 4
for k in keys(a:cond)
if has_key(a:node.attr, k) && a:node.attr[k] == a:cond[k] | return 1 | endif
endfor
endif
return 0
endfunction
function! s:template.childNode(...) dict abort
for c in self.child
if type(c) == 4 && s:matchNode(c, a:000)
return c
endif
unlet c
endfor
return {}
endfunction
function! s:template.childNodes(...) dict abort
let ret = []
for c in self.child
if type(c) == 4 && s:matchNode(c, a:000)
let ret += [c]
endif
unlet c
endfor
return ret
endfunction
function! s:template.value(...) dict abort
if a:0
let self.child = a:000
return
endif
let ret = ''
for c in self.child
if type(c) <= 1 || type(c) == 5
let ret .= c
elseif type(c) == 4
let ret .= c.value()
endif
unlet c
endfor
return ret
endfunction
function! s:template.find(...) dict abort
for c in self.child
if type(c) == 4
if s:matchNode(c, a:000)
return c
endif
unlet! ret
let ret = c.find(a:000)
if !empty(ret)
return ret
endif
endif
unlet c
endfor
return {}
endfunction
function! s:template.findAll(...) dict abort
let ret = []
for c in self.child
if type(c) == 4
if s:matchNode(c, a:000)
call add(ret, c)
endif
let ret += c.findAll(a:000)
endif
unlet c
endfor
return ret
endfunction
function! s:template.toString() dict abort
let xml = '<' . self.name
for attr in keys(self.attr)
let xml .= ' ' . attr . '="' . s:encodeEntityReference(self.attr[attr]) . '"'
endfor
if len(self.child)
let xml .= '>'
for c in self.child
if type(c) == 4
let xml .= c.toString()
elseif type(c) > 1
let xml .= s:encodeEntityReference(string(c))
else
let xml .= s:encodeEntityReference(c)
endif
unlet c
endfor
let xml .= '</' . self.name . '>'
else
let xml .= ' />'
endif
return xml
endfunction
function! s:self.createElement(name) abort
let node = deepcopy(s:template)
let node.name = a:name
return node
endfunction
" @vimlint(EVL102, 1, l:content)
function! s:parse_tree(ctx, top) abort
let node = a:top
let stack = [a:top]
" content accumulates the text only tags
let content = ''
let append_content_to_parent = 'if len(stack) && content != "" | call add(stack[-1].child, content) | let content ="" | endif'
let mx = '^\s*\(<?xml[^>]\+>\)'
if a:ctx['xml'] =~ mx
let match = matchstr(a:ctx['xml'], mx)
let a:ctx['xml'] = a:ctx['xml'][stridx(a:ctx['xml'], match) + len(match):]
let mx = 'encoding\s*=\s*["'']\{0,1}\([^"'' \t]\+\|[^"'']\+\)["'']\{0,1}'
let matches = matchlist(match, mx)
if len(matches)
let encoding = matches[1]
if len(encoding) && len(a:ctx['encoding']) == 0
let a:ctx['encoding'] = encoding
let a:ctx['xml'] = iconv(a:ctx['xml'], encoding, &encoding)
endif
endif
endif
" this regex matches
" 1) the remaining until the next tag begins
" 2) maybe closing "/" of tag name
" 3) tagname
" 4) the attributes of the text (optional)
" 5) maybe closing "/" (end of tag name)
" or
" 6) CDATA or ''
" 7) text content of CDATA
" 8) the remaining text after the tag (rest)
" (These numbers correspond to the indexes in matched list m)
let tag_mx = '^\(\_.\{-}\)\%(\%(<\(/\?\)\([^!/>[:space:]]\+\)\(\%([[:space:]]*[^/>=[:space:]]\+[[:space:]]*=[[:space:]]*\%([^"'' >\t]\+\|"[^"]*"\|''[^'']*''\)\|[[:space:]]\+[^/>=[:space:]]\+[[:space:]]*\)*\)[[:space:]]*\(/\?\)>\)\|\%(<!\[\(CDATA\)\[\(.\{-}\)\]\]>\)\|\(<!--.\{-}-->\)\)'
while len(a:ctx['xml']) > 0
let m = matchlist(a:ctx.xml, tag_mx)
if empty(m) | break | endif
let a:ctx.xml = a:ctx.xml[len(m[0]) :]
let is_end_tag = m[2] ==# '/' && m[5] ==# ''
let is_start_and_end_tag = m[2] ==# '' && m[5] ==# '/'
let tag_name = m[3]
let attrs = m[4]
if len(m[1])
let content .= s:decodeEntityReference(m[1])
endif
if is_end_tag
" closing tag: pop from stack and continue at upper level
exec append_content_to_parent
if len(stack) " TODO: checking whether opened tag is exist.
call remove(stack, -1)
endif
continue
endif
" comment tag
if m[8] !=# ''
continue
endif
" if element is a CDATA
if m[6] !=# ''
let content .= m[7]
continue
endif
let node = deepcopy(s:template)
let node.name = tag_name
let attr_mx = '\([^=[:space:]]\+\)\s*\%(=\s*''\([^'']*\)''\|=\s*"\([^"]*\)"\|=\s*\(\w\+\)\|\)'
while len(attrs) > 0
let attr_match = matchlist(attrs, attr_mx)
if len(attr_match) == 0
break
endif
let name = attr_match[1]
let value = len(attr_match[2]) ? attr_match[2] : len(attr_match[3]) ? attr_match[3] : len(attr_match[4]) ? attr_match[4] : ''
if value ==# ''
let value = name
endif
let node.attr[name] = s:decodeEntityReference(value)
let attrs = attrs[stridx(attrs, attr_match[0]) + len(attr_match[0]):]
endwhile
exec append_content_to_parent
if len(stack)
call add(stack[-1].child, node)
endif
if !is_start_and_end_tag
" opening tag, continue parsing its contents
call add(stack, node)
endif
endwhile
endfunction
" @vimlint(EVL102, 0, l:content)
function! s:self.parse(xml) abort
let top = deepcopy(s:template)
let oldmaxmempattern=&maxmempattern
let oldmaxfuncdepth=&maxfuncdepth
let &maxmempattern=2000000
let &maxfuncdepth=2000
"try
call s:parse_tree({'xml': a:xml, 'encoding': ''}, top)
for node in top.child
if type(node) == 4
return node
endif
unlet node
endfor
"catch /.*/
"endtry
let &maxmempattern=oldmaxmempattern
let &maxfuncdepth=oldmaxfuncdepth
throw 'Parse Error'
endfunction
function! s:self.parseFile(file) abort
return self.parse(join(readfile(a:file), "\n"))
endfunction
function! s:self.parseURL(url) abort
return self.parse(s:HTTP.get(a:url).content)
endfunction
function! SpaceVim#api#web#xml#get() abort
return deepcopy(s:self)
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set et sw=2 cc=80: