mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-02-03 20:40:04 +08:00
932 lines
29 KiB
VimL
932 lines
29 KiB
VimL
" ___vital___
|
|
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
|
|
" Do not modify the code nor insert new lines before '" ___vital___'
|
|
function! s:_SID() abort
|
|
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
|
endfunction
|
|
execute join(['function! vital#_openbrowser#Web#URI#import() abort', printf("return map({'decode': '', '_vital_depends': '', 'new_default_pattern_set': '', 'is_uri': '', 'new': '', 'clone_pattern_set': '', 'new_from_uri_like_string': '', 'new_from_seq_string': '', 'like_uri': '', 'encode': '', '_vital_loaded': ''}, \"vital#_openbrowser#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
|
delfunction s:_SID
|
|
" ___vital___
|
|
let s:save_cpo = &cpo
|
|
set cpo&vim
|
|
|
|
function! s:_vital_loaded(V) abort
|
|
let s:V = a:V
|
|
endfunction
|
|
|
|
function! s:_vital_depends() abort
|
|
return ['Web.URI.HTTP', 'Web.URI.HTTPS']
|
|
endfunction
|
|
|
|
" NOTE: See s:DefaultPatternSet about the reason
|
|
" why s:DefaultPatternSet is not deepcopy()ed here.
|
|
function! s:new(uri, ...) abort
|
|
let NothrowValue = get(a:000, 0, s:NONE)
|
|
let pattern_set = get(a:000, 1, s:DefaultPatternSet)
|
|
return s:_uri_new_sandbox(
|
|
\ a:uri, 0, pattern_set, 0, NothrowValue)
|
|
endfunction
|
|
|
|
" NOTE: See s:DefaultPatternSet about the reason
|
|
" why s:DefaultPatternSet is not deepcopy()ed here.
|
|
function! s:new_from_uri_like_string(str, ...) abort
|
|
let NothrowValue = get(a:000, 0, s:NONE)
|
|
let pattern_set = get(a:000, 1, s:DefaultPatternSet)
|
|
" Prepend http if no scheme.
|
|
if a:str !~# '^' . pattern_set.get('scheme') . '://'
|
|
let str = 'http://' . a:str
|
|
else
|
|
let str = a:str
|
|
endif
|
|
|
|
return s:_uri_new_sandbox(
|
|
\ str, 0, pattern_set, 0, NothrowValue)
|
|
endfunction
|
|
|
|
" NOTE: See s:DefaultPatternSet about the reason
|
|
" why s:DefaultPatternSet is not deepcopy()ed here.
|
|
function! s:new_from_seq_string(uri, ...) abort
|
|
let NothrowValue = get(a:000, 0, s:NONE)
|
|
let pattern_set = get(a:000, 1, s:DefaultPatternSet)
|
|
return s:_uri_new_sandbox(
|
|
\ a:uri, 1, pattern_set, 1, NothrowValue)
|
|
endfunction
|
|
|
|
function! s:is_uri(str) abort
|
|
let ERROR = []
|
|
return s:new(a:str, ERROR) isnot ERROR
|
|
endfunction
|
|
|
|
function! s:like_uri(str) abort
|
|
let ERROR = []
|
|
return s:new_from_uri_like_string(a:str, ERROR) isnot ERROR
|
|
endfunction
|
|
|
|
function! s:encode(str, ...) abort
|
|
let encoding = a:0 ? a:1 : 'utf-8'
|
|
if encoding ==# ''
|
|
let str = a:str
|
|
else
|
|
let str = iconv(a:str, &encoding, encoding)
|
|
endif
|
|
|
|
let result = ''
|
|
for i in range(len(str))
|
|
if str[i] =~# '^[a-zA-Z0-9_.~-]$'
|
|
let result .= str[i]
|
|
else
|
|
let result .= printf('%%%02X', char2nr(str[i]))
|
|
endif
|
|
endfor
|
|
return result
|
|
endfunction
|
|
|
|
function! s:decode(str, ...) abort
|
|
let result = substitute(a:str, '%\(\x\x\)',
|
|
\ '\=printf("%c", str2nr(submatch(1), 16))', 'g')
|
|
|
|
let encoding = a:0 ? a:1 : 'utf-8'
|
|
if encoding ==# ''
|
|
return result
|
|
endif
|
|
return iconv(result, encoding, &encoding)
|
|
endfunction
|
|
|
|
|
|
let s:NONE = []
|
|
|
|
function! s:_uri_new_sandbox(uri, ignore_rest, pattern_set, retall, NothrowValue) abort
|
|
try
|
|
let results = call('s:_uri_new', [a:uri, a:ignore_rest, a:pattern_set])
|
|
return a:retall ? results : results[0]
|
|
catch
|
|
if a:NothrowValue isnot s:NONE && s:_is_own_exception(v:exception)
|
|
return a:NothrowValue
|
|
else
|
|
let ex = substitute(v:exception, '^Vim([^()]\+):', '', '')
|
|
throw 'vital: Web.URI: ' . ex . ' @ ' . v:throwpoint
|
|
\ . ' (original URI: ' . a:uri . ')'
|
|
endif
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:_is_own_exception(str) abort
|
|
return a:str =~# '^vital: Web.URI: uri parse error\%(([^)]\+)\)\?:'
|
|
endfunction
|
|
|
|
|
|
" ================ Parsing Functions ================
|
|
|
|
" @return instance of s:URI .
|
|
"
|
|
" TODO: Support punycode
|
|
"
|
|
" Quoted the outline of RFC3986 here.
|
|
" RFC3986: http://tools.ietf.org/html/rfc3986
|
|
"
|
|
" URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
|
|
" authority = [ userinfo "@" ] host [ ":" port ]
|
|
function! s:_parse_uri(str, ignore_rest, pattern_set) abort
|
|
let rest = a:str
|
|
|
|
" Ignore leading/trailing whitespaces.
|
|
let rest = substitute(rest, '^\s\+', '', '')
|
|
let rest = substitute(rest, '\s\+$', '', '')
|
|
|
|
" scheme
|
|
let [scheme, rest] = s:_eat_scheme(rest, a:pattern_set)
|
|
|
|
" hier-part
|
|
let [hier_part, rest] = s:_eat_hier_part(rest, a:pattern_set)
|
|
|
|
" query
|
|
if rest[0] ==# '?'
|
|
let [query, rest] = s:_eat_query(rest[1:], a:pattern_set)
|
|
else
|
|
let query = ''
|
|
endif
|
|
|
|
" fragment
|
|
if rest[0] ==# '#'
|
|
let [fragment, rest] = s:_eat_fragment(rest[1:], a:pattern_set)
|
|
else
|
|
let fragment = ''
|
|
endif
|
|
|
|
if !a:ignore_rest && rest !=# ''
|
|
throw 'vital: Web.URI: uri parse error: unnecessary string at the end.'
|
|
endif
|
|
|
|
let obj = deepcopy(s:URI)
|
|
let obj.__scheme = scheme
|
|
let obj.__userinfo = hier_part.userinfo
|
|
let obj.__host = hier_part.host
|
|
let obj.__port = hier_part.port
|
|
let obj.__path = hier_part.path
|
|
" NOTE: obj.__query must not have "?" as prefix.
|
|
let obj.__query = substitute(query, '^?', '', '')
|
|
" NOTE: obj.__fragment must not have "#" as prefix.
|
|
let obj.__fragment = substitute(fragment, '^#', '', '')
|
|
let obj.__pattern_set = a:pattern_set
|
|
let obj.__handler = s:_get_handler_module(scheme, obj)
|
|
return [obj, rest]
|
|
endfunction
|
|
|
|
function! s:_get_handler_module(scheme, uriobj) abort
|
|
if a:scheme ==# ''
|
|
return {}
|
|
endif
|
|
let name = 'Web.URI.' . toupper(a:scheme)
|
|
if !s:V.exists(name)
|
|
return {}
|
|
endif
|
|
return s:V.import(name)
|
|
endfunction
|
|
|
|
function! s:_eat_em(str, pat, ...) abort
|
|
let pat = a:pat.'\C'
|
|
let m = matchlist(a:str, pat)
|
|
if empty(m)
|
|
let prefix = printf('uri parse error%s: ', (a:0 ? '('.a:1.')' : ''))
|
|
let msg = printf("can't parse '%s' with '%s'.", a:str, pat)
|
|
throw 'vital: Web.URI: ' . prefix . msg
|
|
endif
|
|
let rest = strpart(a:str, strlen(m[0]))
|
|
return [m[0], rest]
|
|
endfunction
|
|
|
|
" hier-part = "//" authority path-abempty
|
|
" / path-absolute
|
|
" / path-noscheme
|
|
" / path-rootless
|
|
" / path-empty
|
|
function! s:_eat_hier_part(rest, pattern_set) abort
|
|
let rest = a:rest
|
|
if rest =~# '^://'
|
|
" authority
|
|
let rest = rest[3:]
|
|
let [authority, rest] = s:_eat_authority(rest, a:pattern_set)
|
|
let userinfo = authority.userinfo
|
|
let host = authority.host
|
|
let port = authority.port
|
|
" path
|
|
let [path, rest] = s:_eat_path_abempty(rest, a:pattern_set)
|
|
elseif rest =~# '^:'
|
|
let rest = rest[1:]
|
|
let userinfo = ''
|
|
let host = ''
|
|
let port = ''
|
|
" path
|
|
if rest =~# '^/[^/]' " begins with '/' but not '//'
|
|
let [path, rest] = s:_eat_path_absolute(rest, a:pattern_set)
|
|
elseif rest =~# '^[^:]' " begins with a non-colon segment
|
|
let [path, rest] = s:_eat_path_noscheme(rest, a:pattern_set)
|
|
elseif rest =~# a:pattern_set.segment_nz() " begins with a segment
|
|
let [path, rest] = s:_eat_path_rootless(rest, a:pattern_set)
|
|
elseif rest ==# '' || rest =~# '^[?#]' " zero characters
|
|
let path = ''
|
|
else
|
|
throw printf("vital: Web.URI: uri parse error(hier-part): can't parse '%s'.", rest)
|
|
endif
|
|
else
|
|
throw printf("vital: Web.URI: uri parse error(hier-part): can't parse '%s'.", rest)
|
|
endif
|
|
return [{
|
|
\ 'userinfo': userinfo,
|
|
\ 'host': host,
|
|
\ 'port': port,
|
|
\ 'path': path,
|
|
\}, rest]
|
|
endfunction
|
|
|
|
function! s:_eat_authority(str, pattern_set) abort
|
|
let rest = a:str
|
|
" authority(userinfo)
|
|
try
|
|
let oldrest = rest
|
|
let [userinfo, rest] = s:_eat_userinfo(rest, a:pattern_set)
|
|
let rest = s:_eat_em(rest, '^@')[1]
|
|
catch
|
|
let rest = oldrest
|
|
let userinfo = ''
|
|
endtry
|
|
" authority(host)
|
|
let [host, rest] = s:_eat_host(rest, a:pattern_set)
|
|
" authority(port)
|
|
if rest[0] ==# ':'
|
|
let [port, rest] = s:_eat_port(rest[1:], a:pattern_set)
|
|
else
|
|
let port = ''
|
|
endif
|
|
return [{
|
|
\ 'userinfo': userinfo,
|
|
\ 'host': host,
|
|
\ 'port': port,
|
|
\}, rest]
|
|
endfunction
|
|
|
|
" NOTE: More s:_eat_*() functions are defined by s:_create_eat_functions().
|
|
" =============== Parsing Functions ===============
|
|
|
|
|
|
" ===================== s:URI =====================
|
|
|
|
function! s:_uri_new(str, ignore_rest, pattern_set) abort
|
|
let [obj, rest] = s:_parse_uri(a:str, a:ignore_rest, a:pattern_set)
|
|
if a:ignore_rest
|
|
let original_url = a:str[: len(a:str)-len(rest)-1]
|
|
return [obj, original_url, rest]
|
|
else
|
|
return [obj, a:str, '']
|
|
endif
|
|
endfunction
|
|
|
|
function! s:_uri_scheme(...) dict abort
|
|
if a:0
|
|
if self.is_scheme(a:1)
|
|
let self.__scheme = a:1
|
|
return self
|
|
else
|
|
throw 'vital: Web.URI: scheme(): '
|
|
\ . 'invalid argument (' . string(a:1) . ')'
|
|
endif
|
|
endif
|
|
return self.__scheme
|
|
endfunction
|
|
|
|
function! s:_uri_userinfo(...) dict abort
|
|
if a:0
|
|
if self.is_userinfo(a:1)
|
|
let self.__userinfo = a:1
|
|
return self
|
|
else
|
|
throw 'vital: Web.URI: userinfo(): '
|
|
\ . 'invalid argument (' . string(a:1) . ')'
|
|
endif
|
|
endif
|
|
return self.__userinfo
|
|
endfunction
|
|
|
|
function! s:_uri_host(...) dict abort
|
|
if a:0
|
|
if self.is_host(a:1)
|
|
let self.__host = a:1
|
|
return self
|
|
else
|
|
throw 'vital: Web.URI: host(): '
|
|
\ . 'invalid argument (' . string(a:1) . ')'
|
|
endif
|
|
endif
|
|
return self.__host
|
|
endfunction
|
|
|
|
function! s:_uri_port(...) dict abort
|
|
if a:0
|
|
if type(a:1) ==# type(0)
|
|
let self.__port = '' . a:1
|
|
return self
|
|
elseif type(a:1) ==# type('') && self.is_port(a:1)
|
|
let self.__port = a:1
|
|
return self
|
|
else
|
|
throw 'vital: Web.URI: port(): '
|
|
\ . 'invalid argument (' . string(a:1) . ')'
|
|
endif
|
|
endif
|
|
return self.__port
|
|
endfunction
|
|
|
|
function! s:_uri_path(...) dict abort
|
|
if a:0
|
|
if self.is_path(a:1)
|
|
let self.__path = a:1
|
|
return self
|
|
else
|
|
throw 'vital: Web.URI: path(): '
|
|
\ . 'invalid argument (' . string(a:1) . ')'
|
|
endif
|
|
endif
|
|
return self.__path
|
|
endfunction
|
|
|
|
function! s:_uri_authority(...) dict abort
|
|
if a:0
|
|
" TODO
|
|
throw 'vital: Web.URI: uri.authority(value) does not support yet.'
|
|
endif
|
|
return
|
|
\ (self.__userinfo !=# '' ? self.__userinfo . '@' : '')
|
|
\ . self.__host
|
|
\ . (self.__port !=# '' ? ':' . self.__port : '')
|
|
endfunction
|
|
|
|
function! s:_uri_opaque(...) dict abort
|
|
if a:0
|
|
" TODO
|
|
throw 'vital: Web.URI: uri.opaque(value) does not support yet.'
|
|
endif
|
|
return printf('//%s%s',
|
|
\ self.authority(),
|
|
\ self.__path)
|
|
endfunction
|
|
|
|
function! s:_uri_query(...) dict abort
|
|
if a:0
|
|
" NOTE: self.__query must not have "?" as prefix.
|
|
let query = substitute(a:1, '^?', '', '')
|
|
if self.is_query(query)
|
|
let self.__query = query
|
|
return self
|
|
else
|
|
throw 'vital: Web.URI: query(): '
|
|
\ . 'invalid argument (' . string(a:1) . ')'
|
|
endif
|
|
endif
|
|
return self.__query
|
|
endfunction
|
|
|
|
function! s:_uri_fragment(...) dict abort
|
|
if a:0
|
|
" NOTE: self.__fragment must not have "#" as prefix.
|
|
let fragment = substitute(a:1, '^#', '', '')
|
|
if self.is_fragment(fragment)
|
|
let self.__fragment = fragment
|
|
return self
|
|
else
|
|
throw 'vital: Web.URI: fragment(): '
|
|
\ . 'invalid argument (' . string(a:1) . ')'
|
|
endif
|
|
endif
|
|
return self.__fragment
|
|
endfunction
|
|
|
|
function! s:_uri_canonicalize() dict abort
|
|
call s:_call_handler_method(self, 'canonicalize', [])
|
|
return self
|
|
endfunction
|
|
|
|
function! s:_uri_default_port() dict abort
|
|
return s:_call_handler_method(self, 'default_port', [])
|
|
endfunction
|
|
|
|
function! s:_call_handler_method(this, name, args) abort
|
|
if empty(a:this.__handler)
|
|
throw 'vital: Web.URI: ' . a:name . '(): '
|
|
\ . "Handler was not found for scheme '" . a:this.__scheme . "'."
|
|
endif
|
|
return call(a:this.__handler[a:name], [a:this] + a:args)
|
|
endfunction
|
|
|
|
function! s:_uri_clone() dict abort
|
|
return deepcopy(self)
|
|
endfunction
|
|
|
|
function! s:_uri_relative(relstr) dict abort
|
|
call self.canonicalize()
|
|
let relobj = s:_parse_relative_ref(a:relstr, self.__pattern_set)
|
|
call s:_resolve_relative(self, relobj)
|
|
return self
|
|
endfunction
|
|
|
|
" @seealso s:_parse_uri()
|
|
"
|
|
" URI-reference = URI / relative-ref
|
|
" relative-ref = relative-part [ "?" query ] [ "#" fragment ]
|
|
function! s:_parse_relative_ref(relstr, pattern_set) abort
|
|
" relative-part
|
|
let [relpart, rest] = s:_parse_relative_part(a:relstr, a:pattern_set)
|
|
" query
|
|
if rest[0] ==# '?'
|
|
let [query, rest] = s:_eat_query(rest[1:], a:pattern_set)
|
|
else
|
|
let query = ''
|
|
endif
|
|
" fragment
|
|
if rest[0] ==# '#'
|
|
let [fragment, rest] = s:_eat_fragment(rest[1:], a:pattern_set)
|
|
else
|
|
let fragment = ''
|
|
endif
|
|
" no trailing string allowed.
|
|
if rest !=# ''
|
|
throw 'vital: Web.URI: uri parse error(relative-ref): unnecessary string at the end.'
|
|
endif
|
|
|
|
let obj = deepcopy(s:URI)
|
|
let obj.__pattern_set = s:clone_pattern_set(a:pattern_set)
|
|
let obj.__scheme = ''
|
|
let obj.__userinfo = relpart.userinfo
|
|
let obj.__host = relpart.host
|
|
let obj.__port = relpart.port
|
|
let obj.__path = relpart.path
|
|
" NOTE: obj.__query must not have "?" as prefix.
|
|
let obj.__query = substitute(query, '^?', '', '')
|
|
" NOTE: obj.__fragment must not have "#" as prefix.
|
|
let obj.__fragment = substitute(fragment, '^#', '', '')
|
|
return obj
|
|
endfunction
|
|
|
|
" @seealso s:_eat_hier_part()
|
|
"
|
|
" relative-part = "//" authority path-abempty
|
|
" / path-absolute
|
|
" / path-noscheme
|
|
" / path-empty
|
|
function! s:_parse_relative_part(rel_uri, pattern_set) abort
|
|
let rest = a:rel_uri
|
|
if rest =~# '^//'
|
|
" authority
|
|
let rest = rest[2:]
|
|
let [authority, rest] = s:_eat_authority(rest, a:pattern_set)
|
|
let userinfo = authority.userinfo
|
|
let host = authority.host
|
|
let port = authority.port
|
|
" path
|
|
let [path, rest] = s:_eat_path_abempty(rest, a:pattern_set)
|
|
else
|
|
let userinfo = ''
|
|
let host = ''
|
|
let port = ''
|
|
" path
|
|
if rest =~# '^/[^/]' " begins with '/' but not '//'
|
|
let [path, rest] = s:_eat_path_absolute(rest, a:pattern_set)
|
|
elseif rest =~# '^[^:]' " begins with a non-colon segment
|
|
let [path, rest] = s:_eat_path_noscheme(rest, a:pattern_set)
|
|
elseif rest ==# '' || rest =~# '^[?#]' " zero characters
|
|
let path = ''
|
|
else
|
|
throw printf("vital: Web.URI: uri parse error(relative-part): can't parse '%s'.", rest)
|
|
endif
|
|
endif
|
|
return [{
|
|
\ 'userinfo': userinfo,
|
|
\ 'host': host,
|
|
\ 'port': port,
|
|
\ 'path': path,
|
|
\}, rest]
|
|
endfunction
|
|
|
|
" https://tools.ietf.org/html/rfc3986#section-5.2.2
|
|
function! s:_resolve_relative(obj, relobj) abort
|
|
if a:relobj.__scheme !=# ''
|
|
let a:obj.__scheme = a:relobj.__scheme
|
|
let a:obj.__userinfo = a:relobj.__userinfo
|
|
let a:obj.__host = a:relobj.__host
|
|
let a:obj.__port = a:relobj.__port
|
|
let a:obj.__path = s:_remove_dot_segments(a:relobj.__path)
|
|
let a:obj.__query = a:relobj.__query
|
|
else
|
|
if a:relobj.authority() !=# ''
|
|
let a:obj.__userinfo = a:relobj.__userinfo
|
|
let a:obj.__host = a:relobj.__host
|
|
let a:obj.__port = a:relobj.__port
|
|
let a:obj.__path = a:relobj.__path
|
|
let a:obj.__query = a:relobj.__query
|
|
else
|
|
if a:relobj.__path ==# ''
|
|
if a:relobj.__query !=# ''
|
|
let a:obj.__query = a:relobj.__query
|
|
endif
|
|
else
|
|
if a:relobj.__path[0] ==# '/'
|
|
let a:obj.__path = s:_remove_dot_segments(a:relobj.__path)
|
|
else
|
|
let a:obj.__path = s:_merge_paths(a:obj, a:relobj)
|
|
let a:obj.__path = s:_remove_dot_segments(a:obj.__path)
|
|
endif
|
|
let a:obj.__query = a:relobj.__query
|
|
endif
|
|
endif
|
|
endif
|
|
let a:obj.__fragment = a:relobj.__fragment
|
|
endfunction
|
|
|
|
" Merge base URI and relative URI.
|
|
"
|
|
" 5.2.3. Merge Paths
|
|
" https://tools.ietf.org/html/rfc3986#section-5.2.3
|
|
function! s:_merge_paths(baseobj, relobj) abort
|
|
if a:baseobj.authority() !=# '' && a:baseobj.__path ==# ''
|
|
return a:relobj.__path
|
|
else
|
|
return substitute(a:baseobj.__path, '/\zs[^/]\+$', '', '')
|
|
\ . a:relobj.__path
|
|
endif
|
|
endfunction
|
|
|
|
" Remove '.' or '..' in a:path.
|
|
" Trailing '.' or '..' leaves '/' at the end.
|
|
" e.g.:
|
|
" base: http://example.com/a/b/c
|
|
" rel: d/.
|
|
" result: http://example.com/a/b/d/
|
|
"
|
|
" 5.2.4. Remove Dot Segments
|
|
" https://tools.ietf.org/html/rfc3986#section-5.2.4
|
|
function! s:_remove_dot_segments(path) abort
|
|
" Get rid of continuous '/'.
|
|
" May exist empty string because of starting/trailing '/'.
|
|
let paths = split(a:path, '/\+', 1)
|
|
let i = 0
|
|
while i < len(paths)
|
|
if paths[i] ==# '.'
|
|
call remove(paths, i)
|
|
if i >=# len(paths)
|
|
call add(paths, '')
|
|
endif
|
|
elseif paths[i] ==# '..'
|
|
call remove(paths, i)
|
|
" except starting '/..' or '..'
|
|
if !empty(paths) && i > 0 && paths[i-1] !=# ''
|
|
call remove(paths, i-1)
|
|
let i -= 1
|
|
endif
|
|
if i >=# len(paths)
|
|
call add(paths, '')
|
|
endif
|
|
else
|
|
let i += 1
|
|
endif
|
|
endwhile
|
|
return join(paths, '/')
|
|
endfunction
|
|
|
|
function! s:_uri_to_iri(...) dict abort
|
|
" Same as uri.to_string(), but do unescape for self.__path.
|
|
return printf(
|
|
\ '%s://%s%s%s%s',
|
|
\ self.__scheme,
|
|
\ self.authority(),
|
|
\ call('s:decode', [self.__path] + (a:0 ? [a:1] : [])),
|
|
\ (self.__query !=# '' ? '?' . self.__query : ''),
|
|
\ (self.__fragment !=# '' ? '#' . self.__fragment : ''),
|
|
\)
|
|
endfunction
|
|
|
|
function! s:_uri_to_string() dict abort
|
|
return printf(
|
|
\ '%s://%s%s%s%s',
|
|
\ self.__scheme,
|
|
\ self.authority(),
|
|
\ self.__path,
|
|
\ (self.__query !=# '' ? '?' . self.__query : ''),
|
|
\ (self.__fragment !=# '' ? '#' . self.__fragment : ''),
|
|
\)
|
|
endfunction
|
|
|
|
|
|
function! s:_eat_scheme(str, pattern_set) abort
|
|
return s:_eat_em(a:str, '^' . a:pattern_set.get('scheme'), 'scheme')
|
|
endfunction
|
|
function! s:_eat_userinfo(str, pattern_set) abort
|
|
return s:_eat_em(a:str, '^' . a:pattern_set.get('userinfo'), 'userinfo')
|
|
endfunction
|
|
function! s:_eat_host(str, pattern_set) abort
|
|
return s:_eat_em(a:str, '^' . a:pattern_set.get('host'), 'host')
|
|
endfunction
|
|
function! s:_eat_port(str, pattern_set) abort
|
|
return s:_eat_em(a:str, '^' . a:pattern_set.get('port'), 'port')
|
|
endfunction
|
|
function! s:_eat_path(str, pattern_set) abort
|
|
return s:_eat_em(a:str, '^' . a:pattern_set.get('path'), 'path')
|
|
endfunction
|
|
function! s:_eat_path_abempty(str, pattern_set) abort
|
|
return s:_eat_em(a:str, '^' . a:pattern_set.get('path_abempty'), 'path_abempty')
|
|
endfunction
|
|
function! s:_eat_path_absolute(str, pattern_set) abort
|
|
return s:_eat_em(a:str, '^' . a:pattern_set.get('path_absolute'), 'path_absolute')
|
|
endfunction
|
|
function! s:_eat_path_noscheme(str, pattern_set) abort
|
|
return s:_eat_em(a:str, '^' . a:pattern_set.get('path_noscheme'), 'path_noscheme')
|
|
endfunction
|
|
function! s:_eat_path_rootless(str, pattern_set) abort
|
|
return s:_eat_em(a:str, '^' . a:pattern_set.get('path_rootless'), 'path_rootless')
|
|
endfunction
|
|
function! s:_eat_query(str, pattern_set) abort
|
|
return s:_eat_em(a:str, '^' . a:pattern_set.get('query'), 'query')
|
|
endfunction
|
|
function! s:_eat_fragment(str, pattern_set) abort
|
|
return s:_eat_em(a:str, '^' . a:pattern_set.get('fragment'), 'fragment')
|
|
endfunction
|
|
|
|
function! s:_has_error(func, args) abort
|
|
try
|
|
call call(a:func, a:args)
|
|
return 0
|
|
catch
|
|
return 1
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:_uri_is_scheme(str) dict abort
|
|
return !s:_has_error('s:_eat_scheme', [a:str, self.__pattern_set])
|
|
endfunction
|
|
function! s:_uri_is_userinfo(str) dict abort
|
|
return !s:_has_error('s:_eat_userinfo', [a:str, self.__pattern_set])
|
|
endfunction
|
|
function! s:_uri_is_host(str) dict abort
|
|
return !s:_has_error('s:_eat_host', [a:str, self.__pattern_set])
|
|
endfunction
|
|
function! s:_uri_is_port(str) dict abort
|
|
return !s:_has_error('s:_eat_port', [a:str, self.__pattern_set])
|
|
endfunction
|
|
function! s:_uri_is_path(str) dict abort
|
|
return !s:_has_error('s:_eat_path', [a:str, self.__pattern_set])
|
|
endfunction
|
|
function! s:_uri_is_path_abempty(str) dict abort
|
|
return !s:_has_error('s:_eat_path_abempty', [a:str, self.__pattern_set])
|
|
endfunction
|
|
function! s:_uri_is_path_absolute(str) dict abort
|
|
return !s:_has_error('s:_eat_path_absolute', [a:str, self.__pattern_set])
|
|
endfunction
|
|
function! s:_uri_is_path_noscheme(str) dict abort
|
|
return !s:_has_error('s:_eat_path_noscheme', [a:str, self.__pattern_set])
|
|
endfunction
|
|
function! s:_uri_is_path_rootless(str) dict abort
|
|
return !s:_has_error('s:_eat_path_rootless', [a:str, self.__pattern_set])
|
|
endfunction
|
|
function! s:_uri_is_query(str) dict abort
|
|
return !s:_has_error('s:_eat_query', [a:str, self.__pattern_set])
|
|
endfunction
|
|
function! s:_uri_is_fragment(str) dict abort
|
|
return !s:_has_error('s:_eat_fragment', [a:str, self.__pattern_set])
|
|
endfunction
|
|
|
|
function! s:_local_func(name) abort
|
|
let sid = matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__local_func$')
|
|
return function('<SNR>' . sid . '_' . a:name)
|
|
endfunction
|
|
|
|
let s:URI = {
|
|
\ '__scheme': '',
|
|
\ '__userinfo': '',
|
|
\ '__host': '',
|
|
\ '__port': '',
|
|
\ '__path': '',
|
|
\ '__query': '',
|
|
\ '__fragment': '',
|
|
\
|
|
\ '__pattern_set': {},
|
|
\
|
|
\ 'scheme': s:_local_func('_uri_scheme'),
|
|
\ 'userinfo': s:_local_func('_uri_userinfo'),
|
|
\ 'host': s:_local_func('_uri_host'),
|
|
\ 'port': s:_local_func('_uri_port'),
|
|
\ 'path': s:_local_func('_uri_path'),
|
|
\ 'authority': s:_local_func('_uri_authority'),
|
|
\ 'opaque': s:_local_func('_uri_opaque'),
|
|
\ 'query': s:_local_func('_uri_query'),
|
|
\ 'fragment': s:_local_func('_uri_fragment'),
|
|
\
|
|
\ 'clone': s:_local_func('_uri_clone'),
|
|
\ 'relative': s:_local_func('_uri_relative'),
|
|
\ 'canonicalize': s:_local_func('_uri_canonicalize'),
|
|
\ 'default_port': s:_local_func('_uri_default_port'),
|
|
\
|
|
\ 'to_iri': s:_local_func('_uri_to_iri'),
|
|
\ 'to_string': s:_local_func('_uri_to_string'),
|
|
\
|
|
\ 'is_scheme': s:_local_func('_uri_is_scheme'),
|
|
\ 'is_userinfo': s:_local_func('_uri_is_userinfo'),
|
|
\ 'is_host': s:_local_func('_uri_is_host'),
|
|
\ 'is_port': s:_local_func('_uri_is_port'),
|
|
\ 'is_path': s:_local_func('_uri_is_path'),
|
|
\ 'is_query': s:_local_func('_uri_is_query'),
|
|
\ 'is_fragment': s:_local_func('_uri_is_fragment'),
|
|
\}
|
|
|
|
" ===================== s:URI =====================
|
|
|
|
|
|
" ================= s:DefaultPatternSet ==================
|
|
" s:DefaultPatternSet: Default patterns for URI syntax
|
|
"
|
|
" @seealso http://tools.ietf.org/html/rfc3986
|
|
|
|
" s:new*() methods do not create new copy of s:DefaultPatternSet
|
|
" Thus it shares this instance also cache.
|
|
" But it is no problem because of the following reasons.
|
|
" 1. Each component's return value doesn't change
|
|
" unless it is overridden by a user. but...
|
|
" 2. s:DefaultPatternSet can't be accessed by a user.
|
|
let s:DefaultPatternSet = {'_cache': {}}
|
|
|
|
function! s:new_default_pattern_set() abort
|
|
return s:clone_pattern_set(s:DefaultPatternSet)
|
|
endfunction
|
|
|
|
function! s:clone_pattern_set(pattern_set) abort
|
|
let pattern_set = deepcopy(a:pattern_set)
|
|
let pattern_set._cache = {}
|
|
return pattern_set
|
|
endfunction
|
|
|
|
" Memoize
|
|
function! s:DefaultPatternSet.get(component, ...) abort
|
|
if has_key(self._cache, a:component)
|
|
return self._cache[a:component]
|
|
endif
|
|
let ret = call(self[a:component], a:000, self)
|
|
let self._cache[a:component] = ret
|
|
return ret
|
|
endfunction
|
|
|
|
" unreserved = ALPHA / DIGIT / "." / "_" / "~" / "-"
|
|
function! s:DefaultPatternSet.unreserved() abort
|
|
return '[[:alpha:]0-9._~-]'
|
|
endfunction
|
|
" pct-encoded = "%" HEXDIG HEXDIG
|
|
function! s:DefaultPatternSet.pct_encoded() abort
|
|
return '%\x\x'
|
|
endfunction
|
|
" sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
|
" / "*" / "+" / "," / ";" / "="
|
|
function! s:DefaultPatternSet.sub_delims() abort
|
|
return '[!$&''()*+,;=]'
|
|
endfunction
|
|
" dec-octet = DIGIT ; 0-9
|
|
" / %x31-39 DIGIT ; 10-99
|
|
" / "1" 2DIGIT ; 100-199
|
|
" / "2" %x30-34 DIGIT ; 200-249
|
|
" / "25" %x30-35 ; 250-255
|
|
function! s:DefaultPatternSet.dec_octet() abort
|
|
return '\%(1[0-9][0-9]\|2[0-4][0-9]\|25[0-5]\|[1-9][0-9]\|[0-9]\)'
|
|
endfunction
|
|
" IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
|
|
function! s:DefaultPatternSet.ipv4address() abort
|
|
return self.dec_octet() . '\.' . self.dec_octet()
|
|
\ . '\.' . self.dec_octet() . '\.' . self.dec_octet()
|
|
endfunction
|
|
" IPv6address = 6( h16 ":" ) ls32
|
|
" / "::" 5( h16 ":" ) ls32
|
|
" / [ h16 ] "::" 4( h16 ":" ) ls32
|
|
" / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
|
|
" / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
|
|
" / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
|
|
" / [ *4( h16 ":" ) h16 ] "::" ls32
|
|
" / [ *5( h16 ":" ) h16 ] "::" h16
|
|
" / [ *6( h16 ":" ) h16 ] "::"
|
|
"
|
|
" NOTE: Using repeat() in some parts because
|
|
" can't use /\{ at most 10 in whole regexp.
|
|
" https://github.com/vim/vim/blob/cde885473099296c4837de261833f48b24caf87c/src/regexp.c#L1884
|
|
function! s:DefaultPatternSet.ipv6address() abort
|
|
return '\%(' . join([
|
|
\ (repeat('\%(' . self.h16() . ':\)', 6) . self.ls32()),
|
|
\ ('::' . repeat('\%(' . self.h16() . ':\)', 5) . self.ls32()),
|
|
\ ('\%(' . self.h16() . '\)\?::'
|
|
\ . repeat('\%(' . self.h16() . ':\)', 4) . self.ls32()),
|
|
\ ('\%(\%(' . self.h16() . ':\)\?' . self.h16() . '\)\?::'
|
|
\ . repeat('\%(' . self.h16() . ':\)', 3) . self.ls32()),
|
|
\ ('\%(\%(' . self.h16() . ':\)\{,2}' . self.h16() . '\)\?::'
|
|
\ . repeat('\%(' . self.h16() . ':\)', 2) . self.ls32()),
|
|
\ ('\%(\%(' . self.h16() . ':\)\{,3}' . self.h16() . '\)\?::'
|
|
\ . self.h16() . ':' . self.ls32()),
|
|
\ ('\%(\%(' . self.h16() . ':\)\{,4}' . self.h16() . '\)\?::' . self.ls32()),
|
|
\ ('\%(\%(' . self.h16() . ':\)\{,5}' . self.h16() . '\)\?::' . self.h16()),
|
|
\ ('\%(\%(' . self.h16() . ':\)\{,6}' . self.h16() . '\)\?::')
|
|
\], '\|') . '\)'
|
|
endfunction
|
|
" h16 = 1*4HEXDIG
|
|
" ; 16 bits of address represented in hexadecimal
|
|
function! s:DefaultPatternSet.h16() abort
|
|
return '\x\{1,4}'
|
|
endfunction
|
|
" ls32 = ( h16 ":" h16 ) / IPv4address
|
|
" ; least-significant 32 bits of address
|
|
function! s:DefaultPatternSet.ls32() abort
|
|
return '\%(' . self.h16() . ':' . self.h16()
|
|
\ . '\|' . self.ipv4address() . '\)'
|
|
endfunction
|
|
" IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
|
|
function! s:DefaultPatternSet.ipv_future() abort
|
|
return 'v\x\+\.'
|
|
\ . '\%(' . join([self.unreserved(),
|
|
\ self.sub_delims(), ':'], '\|') . '\)\+'
|
|
endfunction
|
|
" IP-Literal = "[" ( IPv6address / IPvFuture ) "]"
|
|
function! s:DefaultPatternSet.ip_literal() abort
|
|
return '\[\%(' . self.ipv6address() . '\|' . self.ipv_future() . '\)\]'
|
|
endfunction
|
|
" reg-name = *( unreserved / pct-encoded / sub-delims )
|
|
function! s:DefaultPatternSet.reg_name() abort
|
|
return '\%(' . join([self.unreserved(), self.pct_encoded(),
|
|
\ self.sub_delims()], '\|') . '\)*'
|
|
endfunction
|
|
" pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
|
function! s:DefaultPatternSet.pchar() abort
|
|
return '\%(' . join([self.unreserved(), self.pct_encoded(),
|
|
\ self.sub_delims(), ':', '@'], '\|') . '\)'
|
|
endfunction
|
|
" segment = *pchar
|
|
function! s:DefaultPatternSet.segment() abort
|
|
return self.pchar() . '*'
|
|
endfunction
|
|
" segment-nz = 1*pchar
|
|
function! s:DefaultPatternSet.segment_nz() abort
|
|
return self.pchar() . '\+'
|
|
endfunction
|
|
" segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
|
|
" ; non-zero-length segment without any colon ":"
|
|
function! s:DefaultPatternSet.segment_nz_nc() abort
|
|
return '\%(' . join([self.unreserved(), self.pct_encoded(),
|
|
\ self.sub_delims(), '@'], '\|') . '\)\+'
|
|
endfunction
|
|
" path-abempty = *( "/" segment )
|
|
function! s:DefaultPatternSet.path_abempty() abort
|
|
return '\%(/' . self.segment() . '\)*'
|
|
endfunction
|
|
" path-absolute = "/" [ segment-nz *( "/" segment ) ]
|
|
function! s:DefaultPatternSet.path_absolute() abort
|
|
return '/\%(' . self.segment_nz() . '\%(/' . self.segment() . '\)*\)\?'
|
|
endfunction
|
|
" path-noscheme = segment-nz-nc *( "/" segment )
|
|
function! s:DefaultPatternSet.path_noscheme() abort
|
|
return self.segment_nz_nc() . '\%(/' . self.segment() . '\)*'
|
|
endfunction
|
|
" path-rootless = segment-nz *( "/" segment )
|
|
function! s:DefaultPatternSet.path_rootless() abort
|
|
return self.segment_nz() . '\%(/' . self.segment() . '\)*'
|
|
endfunction
|
|
|
|
" scheme = ALPHA *( ALPHA / DIGIT / "+" / "." / "-" )
|
|
function! s:DefaultPatternSet.scheme() abort
|
|
return '[[:alpha:]][[:alpha:]0-9+.-]*'
|
|
endfunction
|
|
" userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
|
|
function! s:DefaultPatternSet.userinfo() abort
|
|
return '\%(' . join([self.unreserved(), self.pct_encoded(),
|
|
\ self.sub_delims(), ':'], '\|') . '\)*'
|
|
endfunction
|
|
" host = IP-literal / IPv4address / reg-name
|
|
function! s:DefaultPatternSet.host() abort
|
|
return '\%(' . join([self.ip_literal(), self.ipv4address(),
|
|
\ self.reg_name()], '\|') . '\)'
|
|
endfunction
|
|
" port = *DIGIT
|
|
function! s:DefaultPatternSet.port() abort
|
|
return '[0-9]*'
|
|
endfunction
|
|
" path = path-abempty ; begins with "/" or is empty
|
|
" / path-absolute ; begins with "/" but not "//"
|
|
" / path-noscheme ; begins with a non-colon segment
|
|
" / path-rootless ; begins with a segment
|
|
" / path-empty ; zero characters
|
|
function! s:DefaultPatternSet.path() abort
|
|
return '\%(' . join([self.path_abempty(), self.path_absolute(),
|
|
\ self.path_noscheme(), self.path_rootless(),
|
|
\ ''], '\|') . '\)'
|
|
endfunction
|
|
" query = *( pchar / "/" / "?" )
|
|
function! s:DefaultPatternSet.query() abort
|
|
return '\%(' . join([self.pchar(), '/', '?'], '\|') . '\)*'
|
|
endfunction
|
|
" fragment = *( pchar / "/" / "?" )
|
|
function! s:DefaultPatternSet.fragment() abort
|
|
return '\%(' . join([self.pchar(), '/', '?'], '\|') . '\)*'
|
|
endfunction
|
|
|
|
" ================= s:DefaultPatternSet ==================
|
|
|
|
" vim:set et ts=2 sts=2 sw=2 tw=0:fen:
|