1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-24 02:30:05 +08:00

Add web api

This commit is contained in:
wsdjeg 2017-07-15 05:17:20 +08:00
parent 7401904c51
commit 1538b4a41d
3 changed files with 858 additions and 0 deletions

View File

@ -0,0 +1,53 @@
let s:save_cpo = &cpo
set cpo&vim
let s:self = {}
let s:XML = SpaceVim#api#import('web#xml')
let s:HTTP = SpaceVim#api#import('web#http')
function! s:self.decodeEntityReference(str) abort
let str = a:str
let str = substitute(str, '>', '>', 'g')
let str = substitute(str, '&lt;', '<', 'g')
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')
let str = substitute(str, '&#\(\d\+\);', '\=s:nr2enc_char(submatch(1))', 'g')
let str = substitute(str, '&amp;', '\&', 'g')
let str = substitute(str, '&raquo;', '>', 'g')
let str = substitute(str, '&laquo;', '<', 'g')
return str
endfunction
function! s:self.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, "\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:self.parse(html) abort
let html = substitute(a:html, '<\(area\|base\|basefont\|br\|nobr\|col\|frame\|hr\|img\|input\|isindex\|link\|meta\|param\|embed\|keygen\|command\)\([^>]*[^/]\|\)>', '<\1\2/>', 'g')
return s:XML.parse(html)
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#html#get() abort
return deepcopy(s:self)
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo

View File

@ -0,0 +1,478 @@
let s:save_cpo = &cpo
set cpo&vim
let s:self = {}
let s:system = function(get(g:, 'webapi#system_function', 'system'))
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)
elseif a:nr < 0x10000
return nr2char(a:nr/4096%16+224).nr2char(a:nr/64%64+128).nr2char(a:nr%64+128)
elseif a:nr < 0x200000
return nr2char(a:nr/262144%16+240).nr2char(a:nr/4096/16+128).nr2char(a:nr/64%64+128).nr2char(a:nr%64+128)
elseif a:nr < 0x4000000
return nr2char(a:nr/16777216%16+248).nr2char(a:nr/262144%16+128).nr2char(a:nr/4096/16+128).nr2char(a:nr/64%64+128).nr2char(a:nr%64+128)
else
return nr2char(a:nr/1073741824%16+252).nr2char(a:nr/16777216%16+128).nr2char(a:nr/262144%16+128).nr2char(a:nr/4096/16+128).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:urlencode_char(c, ...) abort
let is_binary = get(a:000, 1)
let c = a:c
if !is_binary
let c = iconv(a:c, &encoding, 'utf-8')
if c ==# ''
let c = a:c
endif
endif
let s = ''
for i in range(strlen(c))
let s .= printf('%%%02X', char2nr(c[i]))
endfor
return s
endfunction
function! s:self.decodeURI(str) abort
let ret = a:str
let ret = substitute(ret, '+', ' ', 'g')
let ret = substitute(ret, '%\(\x\x\)', '\=printf("%c", str2nr(submatch(1), 16))', 'g')
return ret
endfunction
function! s:self.escape(str) abort
return substitute(a:str, '[^a-zA-Z0-9_.~/-]', '\=s:urlencode_char(submatch(0))', 'g')
endfunction
function! s:self.encodeURI(items, ...) abort
let is_binary = get(a:000, 1)
let ret = ''
if type(a:items) == 4
for key in sort(keys(a:items))
if strlen(ret) | let ret .= '&' | endif
let ret .= key . '=' . s:self.encodeURI(a:items[key])
endfor
elseif type(a:items) == 3
for item in sort(a:items)
if strlen(ret) | let ret .= '&' | endif
let ret .= item
endfor
else
let ret = substitute(a:items, '[^a-zA-Z0-9_.~-]', '\=s:urlencode_char(submatch(0), is_binary)', 'g')
endif
return ret
endfunction
function! s:self.encodeURIComponent(items) abort
let ret = ''
if type(a:items) == 4
for key in sort(keys(a:items))
if strlen(ret) | let ret .= '&' | endif
let ret .= key . '=' . s:self.encodeURIComponent(a:items[key])
endfor
elseif type(a:items) == 3
for item in sort(a:items)
if strlen(ret) | let ret .= '&' | endif
let ret .= item
endfor
else
let items = iconv(a:items, &enc, 'utf-8')
let len = strlen(items)
let i = 0
while i < len
let ch = items[i]
if ch =~# '[0-9A-Za-z-._~!''()*]'
let ret .= ch
elseif ch ==# ' '
let ret .= '+'
else
let ret .= '%' . substitute('0' . s:nr2hex(char2nr(ch)), '^.*\(..\)$', '\1', '')
endif
let i = i + 1
endwhile
endif
return ret
endfunction
function! s:self.get(url, ...) abort
let getdata = a:0 > 0 ? a:000[0] : {}
let headdata = a:0 > 1 ? a:000[1] : {}
let follow = a:0 > 2 ? a:000[2] : 1
let url = a:url
let getdatastr = self.encodeURI(getdata)
if strlen(getdatastr)
let url .= '?' . getdatastr
endif
if executable('curl')
let command = printf('curl -q %s -s -k -i', follow ? '-L' : '')
let quote = &shellxquote ==# '"' ? "'" : '"'
for key in keys(headdata)
if has('win32')
let command .= ' -H ' . quote . key . ': ' . substitute(headdata[key], '"', '"""', 'g') . quote
else
let command .= ' -H ' . quote . key . ': ' . headdata[key] . quote
endif
endfor
let command .= ' ' . quote . url . quote
let res = s:system(command)
elseif executable('wget')
let command = printf('wget -O- --save-headers --server-response -q %s', follow ? '-L' : '')
let quote = &shellxquote ==# '"' ? "'" : '"'
for key in keys(headdata)
if has('win32')
let command .= ' --header=' . quote . key . ': ' . substitute(headdata[key], '"', '"""', 'g') . quote
else
let command .= ' --header=' . quote . key . ': ' . headdata[key] . quote
endif
endfor
let command .= ' ' . quote . url . quote
let res = s:system(command)
else
throw 'require `curl` or `wget` command'
endif
if follow != 0
let mx = 'HTTP/\%(1\.[01]\|2\%(\.0\)\?\)'
while res =~# '^' . mx . ' 3' || res =~# '^' . mx . ' [0-9]\{3} .\+\n\r\?\n' . mx . ' .\+'
let pos = stridx(res, "\r\n\r\n")
if pos != -1
let res = strpart(res, pos+4)
else
let pos = stridx(res, "\n\n")
let res = strpart(res, pos+2)
endif
endwhile
endif
let pos = stridx(res, "\r\n\r\n")
if pos != -1
let content = strpart(res, pos+4)
else
let pos = stridx(res, "\n\n")
let content = strpart(res, pos+2)
endif
let header = split(res[:pos-1], '\r\?\n')
let matched = matchlist(get(header, 0), '^HTTP/\%(1\.[01]\|2\%(\.0\)\?\)\s\+\(\d\+\)\s*\(.*\)')
if !empty(matched)
let [status, message] = matched[1 : 2]
call remove(header, 0)
else
if v:shell_error || len(matched)
let [status, message] = ['500', "Couldn't connect to host"]
else
let [status, message] = ['200', 'OK']
endif
endif
return {
\ 'status' : status,
\ 'message' : message,
\ 'header' : header,
\ 'content' : content
\}
endfunction
function! s:self.post(url, ...) abort
let postdata = a:0 > 0 ? a:000[0] : {}
let headdata = a:0 > 1 ? a:000[1] : {}
let method = a:0 > 2 ? a:000[2] : 'POST'
let follow = a:0 > 3 ? a:000[3] : 1
let url = a:url
if type(postdata) == 4
let postdatastr = self.encodeURI(postdata)
else
let postdatastr = postdata
endif
let file = tempname()
if executable('curl')
let command = printf('curl -q %s -s -k -i -X %s', (follow ? '-L' : ''), len(method) ? method : 'POST')
let quote = &shellxquote ==# '"' ? "'" : '"'
for key in keys(headdata)
if has('win32')
let command .= ' -H ' . quote . key . ': ' . substitute(headdata[key], '"', '"""', 'g') . quote
else
let command .= ' -H ' . quote . key . ': ' . headdata[key] . quote
endif
endfor
let command .= ' ' . quote . url . quote
call writefile(split(postdatastr, "\n"), file, 'b')
let res = s:system(command . ' --data-binary @' . quote.file.quote)
elseif executable('wget')
let command = printf('wget -O- --save-headers --server-response -q %s', follow ? '-L' : '')
let headdata['X-HTTP-Method-Override'] = method
let quote = &shellxquote ==# '"' ? "'" : '"'
for key in keys(headdata)
if has('win32')
let command .= ' --header=' . quote . key . ': ' . substitute(headdata[key], '"', '"""', 'g') . quote
else
let command .= ' --header=' . quote . key . ': ' . headdata[key] . quote
endif
endfor
let command .= ' '.quote.url.quote
call writefile(split(postdatastr, "\n"), file, 'b')
let res = s:system(command . ' --post-data @' . quote.file.quote)
else
throw 'require `curl` or `wget` command'
endif
call delete(file)
if follow != 0
let mx = 'HTTP/\%(1\.[01]\|2\%(\.0\)\?\)'
while res =~# '^' . mx . ' 3' || res =~# '^' . mx . ' [0-9]\{3} .\+\n\r\?\n' . mx . ' .\+'
let pos = stridx(res, "\r\n\r\n")
if pos != -1
let res = strpart(res, pos+4)
else
let pos = stridx(res, "\n\n")
let res = strpart(res, pos+2)
endif
endwhile
endif
let pos = stridx(res, "\r\n\r\n")
if pos != -1
let content = strpart(res, pos+4)
else
let pos = stridx(res, "\n\n")
let content = strpart(res, pos+2)
endif
let header = split(res[:pos-1], '\r\?\n')
let matched = matchlist(get(header, 0), '^HTTP/\%(1\.[01]\|2\%(\.0\)\?\)\s\+\(\d\+\)\s*\(.*\)')
if !empty(matched)
let [status, message] = matched[1 : 2]
call remove(header, 0)
else
if v:shell_error || len(matched)
let [status, message] = ['500', "Couldn't connect to host"]
else
let [status, message] = ['200', 'OK']
endif
endif
return {
\ 'status' : status,
\ 'message' : message,
\ 'header' : header,
\ 'content' : content
\}
endfunction
function! s:self.send(req) abort
let postdata = get(a:req, 'data', '')
let method = get(a:req, 'method', postdata ==# '' ? 'GET': 'POST')
let headdata = get(a:req, 'header', {})
let follow = get(a:req, 'follow', 1)
let url = get(a:req, 'url', '')
if type(postdata) == 4
let postdatastr = self.encodeURI(postdata)
else
let postdatastr = postdata
endif
if empty(postdatastr)
let file = ''
else
let file = tempname()
endif
if executable('curl')
let command = printf('curl -q %s -s -k -i -X %s', (follow ? '-L' : ''), len(method) ? method : 'POST')
let quote = &shellxquote ==# '"' ? "'" : '"'
for key in keys(headdata)
if has('win32')
let command .= ' -H ' . quote . key . ': ' . substitute(headdata[key], '"', '"""', 'g') . quote
else
let command .= ' -H ' . quote . key . ': ' . headdata[key] . quote
endif
endfor
let command .= ' ' . quote . url . quote
if file ==# ''
let res = s:system(command)
else
call writefile(split(postdatastr, "\n"), file, 'b')
let res = s:system(command . ' --data-binary @' . quote.file.quote)
call delete(file)
endif
elseif executable('wget')
let command = printf('wget -O- --save-headers --server-response -q %s', follow ? '-L' : '')
let headdata['X-HTTP-Method-Override'] = method
let quote = &shellxquote ==# '"' ? "'" : '"'
for key in keys(headdata)
if has('win32')
let command .= ' --header=' . quote . key . ': ' . substitute(headdata[key], '"', '"""', 'g') . quote
else
let command .= ' --header=' . quote . key . ': ' . headdata[key] . quote
endif
endfor
let command .= ' '.quote.url.quote
if file ==# ''
let res = s:system(command)
else
call writefile(split(postdatastr, "\n"), file, 'b')
let res = s:system(command . ' --post-data @' . quote.file.quote)
call delete(file)
endif
else
throw 'require `curl` or `wget` command'
endif
if follow != 0
let mx = 'HTTP/\%(1\.[01]\|2\%(\.0\)\?\)'
while res =~# '^' . mx . ' 3' || res =~# '^' . mx . ' [0-9]\{3} .\+\n\r\?\n' . mx . ' .\+'
let pos = stridx(res, "\r\n\r\n")
if pos != -1
let res = strpart(res, pos+4)
else
let pos = stridx(res, "\n\n")
let res = strpart(res, pos+2)
endif
endwhile
endif
let pos = stridx(res, "\r\n\r\n")
if pos != -1
let content = strpart(res, pos+4)
else
let pos = stridx(res, "\n\n")
let content = strpart(res, pos+2)
endif
let header = split(res[:pos-1], '\r\?\n')
let matched = matchlist(get(header, 0), '^HTTP/\%(1\.[01]\|2\%(\.0\)\?\)\s\+\(\d\+\)\s*\(.*\)')
if !empty(matched)
let [status, message] = matched[1 : 2]
call remove(header, 0)
else
if v:shell_error || len(matched)
let [status, message] = ['500', "Couldn't connect to host"]
else
let [status, message] = ['200', 'OK']
endif
endif
return {
\ 'status' : status,
\ 'message' : message,
\ 'header' : header,
\ 'content' : content
\}
endfunction
function! s:self.stream(req) abort
let postdata = get(a:req, 'data', '')
let method = get(a:req, 'method', postdata ==# '' ? 'GET': 'POST')
let headdata = get(a:req, 'header', {})
let follow = get(a:req, 'follow', 1)
let url = get(a:req, 'url', '')
let mode = get(a:req, 'mode', 'nl')
if type(postdata) == 4
let postdatastr = self.encodeURI(postdata)
else
let postdatastr = postdata
endif
if empty(postdatastr)
let file = ''
else
let file = tempname()
endif
if executable('curl')
let command = printf('curl -q %s -s -k -X %s', (follow ? '-L' : ''), len(method) ? method : 'POST')
let quote = &shellxquote ==# '"' ? "'" : '"'
for key in keys(headdata)
if has('win32')
let command .= ' -H ' . quote . key . ': ' . substitute(headdata[key], '"', '"""', 'g') . quote
else
let command .= ' -H ' . quote . key . ': ' . headdata[key] . quote
endif
endfor
let command .= ' '.quote . url . quote
if file ==# ''
let job = job_start(command)
else
call writefile(split(postdatastr, "\n"), file, 'b')
let job = job_start(command . ' --data-binary @' . quote.file.quote)
call delete(file)
endif
elseif executable('wget')
let command = printf('wget -O- -q %s', follow ? '-L' : '')
let headdata['X-HTTP-Method-Override'] = method
let quote = &shellxquote ==# '"' ? "'" : '"'
for key in keys(headdata)
if has('win32')
let command .= ' --header=' . quote . key . ': ' . substitute(headdata[key], '"', '"""', 'g') . quote
else
let command .= ' --header=' . quote . key . ': ' . headdata[key] . quote
endif
endfor
let command .= ' '.quote.url.quote
if file ==# ''
let job = job_start(command)
else
call writefile(split(postdatastr, "\n"), file, 'b')
let job = job_start(command . ' --post-data @' . quote.file.quote)
call delete(file)
endif
else
throw 'require `curl` or `wget` command'
endif
call job_setoptions(job,
\{
\ 'exit_cb': function('webapi#http#exit_cb', [a:req]),
\ 'stoponexit': 'kill',
\})
let a:req['job'] = job
let channel = job_getchannel(job)
call ch_setoptions(channel,
\{
\ 'out_cb': function('webapi#http#out_cb', [a:req]),
\ 'mode': mode,
\})
let a:req['channel'] = channel
let a:req['file'] = file
endfunction
" @vimlint(EVL103, 1, a:job)
function! s:self.exit_cb(req, job, code) abort
let file = get(a:req, 'file')
if file !=# ''
call delete(file)
endif
let fexit_cb = get(a:req, 'exit_cb', v:none)
if fexit_cb != v:none
call call(fexit_cb, [a:code])
endif
endfunction
" @vimlint(EVL103, 0, a:job)
" @vimlint(EVL103, 1, a:ch)
function! s:self.out_cb(req, ch, data) abort
let fout_cb = get(a:req, 'out_cb', v:none)
if fout_cb != v:none
call Fout_cb(a:data)
call call(fout_cb, [a:data])
endif
endfunction
" @vimlint(EVL103, 0, a:ch)
function! SpaceVim#api#web#http#get() abort
return deepcopy(s:self)
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set et:

View File

@ -0,0 +1,327 @@
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! webapi#xml#createElement(name) abort
let node = deepcopy(s:template)
let node.name = a:name
return node
endfunction
function! s:parse_tree(ctx, top) abort
let node = a:top
let stack = [a:top]
let pos = 0
" 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
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()
return deepcopy(s:self)
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set et sw=2 cc=80: