1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-03 06:40:05 +08:00
SpaceVim/bundle/calendar.vim/autoload/calendar/webapi.vim
2022-05-28 15:29:51 +08:00

385 lines
12 KiB
VimL
Vendored

" =============================================================================
" Filename: autoload/calendar/webapi.vim
" Author: itchyny
" License: MIT License
" Last Change: 2019/12/03 12:48:49.
" =============================================================================
" Web interface.
" Most part of this file was copied from webapi-vim and vital.vim.
" Thank you Yasuhiro Matsumoto, for distributing useful scripts under public
" domain.
" Maintainer and License of the original script {{{
" Last Change: 2010-09-10
" Maintainer: Yasuhiro Matsumoto <mattn.jp@gmail.com>
" License: This file is placed in the public domain.
" }}}
let s:save_cpo = &cpo
set cpo&vim
let s:cache = calendar#cache#new('download')
call s:cache.check_dir(1)
if !calendar#setting#get('debug')
call s:cache.rmdir_on_exit()
endif
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! calendar#webapi#get(url, ...) abort
return s:request(1, {}, a:url, get(a:000, 0, {}), get(a:000, 1, {}), get(a:000, 2, 'GET'))
endfunction
function! calendar#webapi#post(url, ...) abort
return s:request(1, {}, a:url, get(a:000, 0, {}), get(a:000, 1, {}), get(a:000, 2, 'POST'))
endfunction
function! calendar#webapi#delete(url, ...) abort
return s:request(1, {}, a:url, get(a:000, 0, {}), get(a:000, 1, {}), get(a:000, 2, 'DELETE'))
endfunction
function! calendar#webapi#patch(url, ...) abort
return s:request(1, {}, a:url, get(a:000, 0, {}), get(a:000, 1, {}), get(a:000, 2, 'PATCH'))
endfunction
function! calendar#webapi#put(url, ...) abort
return s:request(1, {}, a:url, get(a:000, 0, {}), get(a:000, 1, {}), get(a:000, 2, 'PUT'))
endfunction
function! calendar#webapi#post_nojson(url, ...) abort
return s:request(0, {}, a:url, get(a:000, 0, {}), get(a:000, 1, {}), get(a:000, 2, 'POST'))
endfunction
function! calendar#webapi#get_async(id, cb, url, ...) abort
return s:request(1, { 'id': a:id, 'cb': a:cb } , a:url, get(a:000, 0, {}), get(a:000, 1, {}), get(a:000, 2, 'GET'))
endfunction
function! calendar#webapi#post_async(id, cb, url, ...) abort
return s:request(1, { 'id': a:id, 'cb': a:cb }, a:url, get(a:000, 0, {}), get(a:000, 1, {}), get(a:000, 2, 'POST'))
endfunction
function! calendar#webapi#delete_async(id, cb, url, ...) abort
return s:request(1, { 'id': a:id, 'cb': a:cb }, a:url, get(a:000, 0, {}), get(a:000, 1, {}), get(a:000, 2, 'DELETE'))
endfunction
function! calendar#webapi#patch_async(id, cb, url, ...) abort
return s:request(1, { 'id': a:id, 'cb': a:cb }, a:url, get(a:000, 0, {}), get(a:000, 1, {}), get(a:000, 2, 'PATCH'))
endfunction
function! calendar#webapi#put_async(id, cb, url, ...) abort
return s:request(1, { 'id': a:id, 'cb': a:cb }, a:url, get(a:000, 0, {}), get(a:000, 1, {}), get(a:000, 2, 'PUT'))
endfunction
function! calendar#webapi#post_nojson_async(id, cb, url, ...) abort
return s:request(0, { 'id': a:id, 'cb': a:cb }, a:url, get(a:000, 0, {}), get(a:000, 1, {}), get(a:000, 2, 'POST'))
endfunction
function! s:request(json, async, url, param, postdata, method) abort
let url = a:url
let paramstr = calendar#webapi#encodeURI(a:param)
let withbody = a:method !=# 'GET' && a:method !=# 'DELETE'
let header = {}
if paramstr !=# ''
let url .= '?' . paramstr
endif
let postfile = ''
if withbody
let postdatastr = a:json ? calendar#webapi#encode(a:postdata) : join(s:postdata(a:postdata), "\n")
let postfile = tempname()
call writefile(split(postdatastr, "\n"), postfile, 'b')
let header['Content-Length'] = len(postdatastr)
if a:json
let header['Content-Type'] = 'application/json'
endif
endif
let command = s:command(url, a:method, header, postfile, a:async == {} ? '' : s:cache.path(a:async.id))
if type(command) != type('')
return { 'status': '0', 'message': '', 'header': '', 'content': '' }
endif
call s:cache.check_dir(1)
if a:async == {}
let data = calendar#util#system(command)
let response = calendar#webapi#parse(split(data, "\n"))
if withbody
call delete(postfile)
endif
return response
else
if !calendar#setting#get('debug')
call s:cache.delete(a:async.id)
endif
call calendar#async#new('calendar#webapi#callback(' . string(a:async.id) . ',' . string(a:async.cb) . ')')
if has('win32')
call calendar#util#system('cmd /c start /min ' . command)
else
let command .= ' &'
call calendar#util#system(command)
endif
endif
endfunction
function! s:command(url, method, header, postfile, output) abort
let quote = s:_quote()
if executable('curl')
let command = 'curl --http1.1 --suppress-connect-headers -s -k -i -N -X ' . a:method
let command .= s:make_header_args(a:header, '-H ', quote)
if a:postfile !=# ''
let command .= ' --data-binary @' . quote . a:postfile . quote
endif
if a:output !=# ''
let command .= ' -o ' . quote . a:output . quote
endif
let command .= ' ' . quote . a:url . quote
return command
elseif executable('wget')
let command = 'wget -O- --server-response -q'
let a:header['X-HTTP-Method-Override'] = a:method
let command .= s:make_header_args(a:header, '--header=', quote)
if a:postfile !=# ''
let command .= ' --post-file=' . quote . a:postfile . quote
else
let command .= ' --method=' . a:method
endif
let command .= ' ' . quote . a:url . quote
if a:output !=# ''
let command .= ' > ' . quote . a:output . quote . ' 2>&1'
endif
return command
else
call calendar#echo#error_message('curl_wget_not_found')
return 1
endif
endfunction
let s:callback_datalen = {}
function! calendar#webapi#callback(id, cb) abort
let data = s:cache.get_raw(a:id)
if type(data) != type([])
return 1
endif
let prevdatalen = get(s:callback_datalen, a:id)
let s:callback_datalen[a:id] = len(data)
if len(data) == 0 || len(data) != prevdatalen
return 1
endif
let response = calendar#webapi#parse(data)
if empty(response)
return 1
elseif a:cb !=# ''
call call(a:cb, [a:id, response])
endif
if !calendar#setting#get('debug')
call s:cache.delete(a:id)
endif
unlet s:callback_datalen[a:id]
return 0
endfunction
function! calendar#webapi#parse(data) abort
if len(a:data) == 0
return { 'status': '0', 'message': '', 'header': '', 'content': '' }
endif
let i = 0
while i < len(a:data) && a:data[i] =~# '^ ' " for wget
let a:data[i] = a:data[i][2:]
let i += 1
endwhile
if i > 0
call insert(a:data, '', i)
let i = 0
endif
while i < len(a:data) && (a:data[i] =~# '\v^HTTP/[12]%(\.\d)? 3' ||
\ (i + 2 < len(a:data) && a:data[i] =~# '\v^HTTP/1\.\d \d{3}' &&
\ a:data[i + 1] =~# '\v^\r?$' && a:data[i + 2] =~# '\v^HTTP/1\.\d \d{3}'))
while i < len(a:data) && a:data[i] !~# '\v^\r?$'
let i += 1
endwhile
let i += 1
endwhile
while i < len(a:data) && a:data[i] !~# '\v^\r?$'
let i += 1
endwhile
let header = a:data[:i]
let content = join(a:data[(i):], "\n")
let matched = matchlist(get(header, 0, ''), '\v^HTTP/[12]%(\.\d)?\s+(\d+)\s*(.*)')
if !empty(matched)
let [status, message] = matched[1 : 2]
call remove(header, 0)
else
let [status, message] = ['200', 'OK']
endif
return { 'status': status, 'message': message, 'header': header, 'content': content }
endfunction
function! calendar#webapi#null() abort
return 0
endfunction
function! calendar#webapi#true() abort
return 1
endfunction
function! calendar#webapi#false() abort
return 0
endfunction
function! calendar#webapi#encode(val) abort
if type(a:val) == 0
return a:val
elseif type(a:val) == 1
let json = '"' . escape(a:val, '\"') . '"'
let json = substitute(json, "\r", '\\r', 'g')
let json = substitute(json, "\n", '\\n', 'g')
let json = substitute(json, "\t", '\\t', 'g')
let json = substitute(json, '\([[:cntrl:]]\)', '\=printf("\x%02d", char2nr(submatch(1)))', 'g')
return iconv(json, &encoding, 'utf-8')
elseif type(a:val) == 2
let s = string(a:val)
if s == "function('calendar#webapi#null')"
return 'null'
elseif s == "function('calendar#webapi#true')"
return 'true'
elseif s == "function('calendar#webapi#false')"
return 'false'
endif
elseif type(a:val) == 3
return '[' . join(map(copy(a:val), 'calendar#webapi#encode(v:val)'), ',') . ']'
elseif type(a:val) == 4
return '{' . join(map(keys(a:val), 'calendar#webapi#encode(v:val).":".calendar#webapi#encode(a:val[v:val])'), ',') . '}'
else
return string(a:val)
endif
endfunction
function! calendar#webapi#decode(json) abort
let json = iconv(a:json, 'utf-8', &encoding)
let json = substitute(json, '[\r\n]', '', 'g')
let json = substitute(json, '\\x22\|\\u0022', '\\"', 'g')
if v:version > 703 || v:version == 703 && has('patch780')
let json = substitute(json, '\\u\(\x\x\x\x\)', '\=iconv(nr2char(str2nr(submatch(1), 16), 1), "utf-8", &encoding)', 'g')
else
let json = substitute(json, '\\u\(\x\x\x\x\)', '\=s:nr2enc_char("0x".submatch(1))', 'g')
endif
let [null,true,false] = [0,1,0]
try
sandbox let ret = eval(json)
catch
let ret = {}
endtry
return ret
endfunction
function! calendar#webapi#open_url(url) abort
if has('win32')
silent! call calendar#util#system('cmd /c start "" "' . a:url . '"')
elseif executable('xdg-open')
silent! call calendar#util#system('xdg-open "' . a:url . '" &')
elseif executable('open')
silent! call calendar#util#system('open "' . a:url . '" &')
endif
endfunction
function! calendar#webapi#echo_error(response) abort
let message = get(a:response, 'message', '')
if has_key(a:response, 'content')
let cnt = calendar#webapi#decode(a:response.content)
if type(cnt) == type({}) && len(get(get(cnt, 'error', {}), 'message', ''))
let message = get(get(cnt, 'error', {}), 'message', '')
endif
endif
if message !=# ''
call calendar#echo#error(message)
endif
endfunction
function! s:make_header_args(headdata, option, quote) abort
let args = ''
for key in keys(a:headdata)
unlet! value
let value = type(a:headdata[key]) == type('') || type(a:headdata[key]) == type(0) ? a:headdata[key] :
\ type(a:headdata[key]) == type({}) ? '' :
\ type(a:headdata[key]) == type([]) ? '[' . join(map(a:headdata[key], 's:make_header_args(v:val, a:option, a:quote)'), ',') . ']' : ''
if has('win32')
let value = substitute(value, '"', '"""', 'g')
endif
let args .= ' ' . a:option . a:quote . key . ': ' . value . a:quote
endfor
return args
endfunction
function! s: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:escape(str) abort
return substitute(a:str, '[^a-zA-Z0-9_.-]', '\=printf("%%%02X", char2nr(submatch(0)))', 'g')
endfunction
function! calendar#webapi#encodeURI(items) abort
let ret = ''
if type(a:items) == type({})
for key in sort(keys(a:items))
if ret !=# ''
let ret .= '&'
endif
let ret .= key . '=' . calendar#webapi#encodeURI(a:items[key])
endfor
elseif type(a:items) == type([])
for item in sort(a:items)
if ret !=# ''
let ret .= '&'
endif
let ret .= item
endfor
else
let ret = s:escape(a:items)
endif
return ret
endfunction
function! s:postdata(data) abort
if type(a:data) == type({})
return [calendar#webapi#encodeURI(a:data)]
elseif type(a:data) == type([])
return a:data
else
return split(a:data, "\n")
endif
endfunction
function! s:_quote() abort
return &shellxquote == '"' ? "'" : '"'
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo