From 13cd758929cc6a2c0df5e493f6439784fb4ceb25 Mon Sep 17 00:00:00 2001 From: lambdalisue Date: Sun, 23 Jul 2017 12:10:52 +0900 Subject: [PATCH 1/7] Follow linter (vim-vint) --- autoload/openbrowser.vim | 80 ++++++++++++++++++++-------------------- plugin/openbrowser.vim | 3 +- 2 files changed, 43 insertions(+), 40 deletions(-) diff --git a/autoload/openbrowser.vim b/autoload/openbrowser.vim index 0fd99de..837d56e 100644 --- a/autoload/openbrowser.vim +++ b/autoload/openbrowser.vim @@ -16,6 +16,8 @@ let s:Buffer = s:V.import('Vim.Buffer') let s:Msg = s:V.import('Vim.Message') unlet s:V +let s:t_string = type('') + " Save/Determine global variable values. let s:vimproc_is_installed = globpath(&rtp, 'autoload/vimproc.vim') !=# '' @@ -23,7 +25,7 @@ let s:vimproc_is_installed = globpath(&rtp, 'autoload/vimproc.vim') !=# '' " Interfaces {{{ -function! openbrowser#load() "{{{ +function! openbrowser#load() abort "{{{ " dummy function to load this file. endfunction "}}} @@ -31,14 +33,14 @@ endfunction "}}} " :OpenBrowser " @param uri URI object or String -function! openbrowser#open(uri) "{{{ +function! openbrowser#open(uri) abort "{{{ if type(a:uri) is type({}) \ && has_key(a:uri, '__pattern_set') " URI object " Trust URI object value because " it must be validated by parser. let uriobj = a:uri let uristr = a:uri.to_string() - elseif type(a:uri) is type("") + elseif type(a:uri) is s:t_string let uristr = a:uri if uristr =~# '^\s*$' return @@ -108,7 +110,7 @@ function! openbrowser#open(uri) "{{{ endfunction "}}} " :OpenBrowserSearch -function! openbrowser#search(query, ...) "{{{ +function! openbrowser#search(query, ...) abort "{{{ if a:query =~# '^\s*$' return endif @@ -128,7 +130,7 @@ function! openbrowser#search(query, ...) "{{{ endfunction "}}} " :OpenBrowserSmartSearch -function! openbrowser#smart_search(query, ...) "{{{ +function! openbrowser#smart_search(query, ...) abort "{{{ let type = s:detect_query_type(a:query) if type.uri || type.filepath return openbrowser#open(a:query) @@ -141,7 +143,7 @@ function! openbrowser#smart_search(query, ...) "{{{ endfunction "}}} " Escape one argument. -function! openbrowser#shellescape(...) "{{{ +function! openbrowser#shellescape(...) abort "{{{ return call(s:Process.shellescape, a:000, s:Process) endfunction "}}} @@ -154,7 +156,7 @@ lockvar s:NONE -function! s:parse_and_delegate(excmd, parse, delegate, cmdline) "{{{ +function! s:parse_and_delegate(excmd, parse, delegate, cmdline) abort "{{{ let cmdline = substitute(a:cmdline, '^\s\+', '', '') try @@ -171,13 +173,13 @@ function! s:parse_and_delegate(excmd, parse, delegate, cmdline) "{{{ let args = [cmdline] + (engine is s:NONE ? [] : [engine]) return call(a:delegate, args) endfunction "}}} -function! s:parse_cmdline(cmdline) "{{{ +function! s:parse_cmdline(cmdline) abort "{{{ let m = matchlist(a:cmdline, '^-\(\S\+\)\s\+\(.*\)') return !empty(m) ? m[1:2] : [s:NONE, a:cmdline] endfunction "}}} " :OpenBrowserSearch -function! openbrowser#_cmd_open_browser_search(cmdline) "{{{ +function! openbrowser#_cmd_open_browser_search(cmdline) abort "{{{ return s:parse_and_delegate( \ ':OpenBrowserSearch', \ 's:parse_cmdline', @@ -187,7 +189,7 @@ function! openbrowser#_cmd_open_browser_search(cmdline) "{{{ endfunction "}}} " @vimlint(EVL103, 1, a:arglead) " @vimlint(EVL103, 1, a:cursorpos) -function! openbrowser#_cmd_complete(arglead, cmdline, cursorpos) "{{{ +function! openbrowser#_cmd_complete(arglead, cmdline, cursorpos) abort "{{{ let excmd = '^\s*OpenBrowser\w\+\s\+' if a:cmdline !~# excmd return @@ -196,7 +198,7 @@ function! openbrowser#_cmd_complete(arglead, cmdline, cursorpos) "{{{ let engine_options = map( \ sort(keys(s:get_var('openbrowser_search_engines'))), - \ '"-" . v:val' + \ '''-'' . v:val' \) if cmdline ==# '' || cmdline ==# '-' " Return all search engines. @@ -211,7 +213,7 @@ endfunction "}}} " @vimlint(EVL103, 0, a:cursorpos) " :OpenBrowserSmartSearch -function! openbrowser#_cmd_open_browser_smart_search(cmdline) "{{{ +function! openbrowser#_cmd_open_browser_smart_search(cmdline) abort "{{{ return s:parse_and_delegate( \ ':OpenBrowserSmartSearch', \ 's:parse_cmdline', @@ -221,7 +223,7 @@ function! openbrowser#_cmd_open_browser_smart_search(cmdline) "{{{ endfunction "}}} " (openbrowser-open) -function! openbrowser#_keymapping_open(mode, ...) "{{{ +function! openbrowser#_keymapping_open(mode, ...) abort "{{{ let silent = get(a:000, 0, s:get_var('openbrowser_message_verbosity') ==# 0) if a:mode ==# 'n' " URL @@ -239,7 +241,7 @@ function! openbrowser#_keymapping_open(mode, ...) "{{{ " Fail! if !silent call s:Msg.error( - \ "URL or file path is not found under cursor!") + \ 'URL or file path is not found under cursor!') endif return 0 else @@ -253,7 +255,7 @@ function! openbrowser#_keymapping_open(mode, ...) "{{{ endfunction "}}} " (openbrowser-search) -function! openbrowser#_keymapping_search(mode) "{{{ +function! openbrowser#_keymapping_search(mode) abort "{{{ if a:mode ==# 'n' return openbrowser#search(expand('')) else @@ -262,7 +264,7 @@ function! openbrowser#_keymapping_search(mode) "{{{ endfunction "}}} " (openbrowser-smart-search) -function! openbrowser#_keymapping_smart_search(mode) "{{{ +function! openbrowser#_keymapping_smart_search(mode) abort "{{{ if openbrowser#_keymapping_open(a:mode, 1) " Suceeded to open! return @@ -281,7 +283,7 @@ function! openbrowser#_keymapping_smart_search(mode) "{{{ endif endfunction "}}} -function! s:get_selected_text() "{{{ +function! s:get_selected_text() abort "{{{ let selected_text = s:Buffer.get_last_selected() let text = substitute(selected_text, '[\n\r]\+', ' ', 'g') return substitute(text, '^\s*\|\s*$', '', 'g') @@ -363,7 +365,7 @@ function! s:extract_urls(text) abort "{{{ return urls endfunction "}}} -function! s:seems_path(uri) "{{{ +function! s:seems_path(uri) abort "{{{ " - Has no invalid filename character (seeing &isfname) " and, either " - file:// prefixed string and existed file path @@ -376,12 +378,12 @@ function! s:seems_path(uri) "{{{ return getftype(path) !=# '' endfunction "}}} -function! s:seems_uri(uriobj) "{{{ +function! s:seems_uri(uriobj) abort "{{{ return !empty(a:uriobj) \ && a:uriobj.scheme() !=# '' endfunction "}}} -function! s:detect_query_type(query, ...) "{{{ +function! s:detect_query_type(query, ...) abort "{{{ let uriobj = a:0 ? a:1 : {} if empty(uriobj) let uriobj = s:URI.new(a:query, {}) @@ -393,7 +395,7 @@ function! s:detect_query_type(query, ...) "{{{ endfunction "}}} " @vimlint(EVL104, 1, l:save_shellslash) -function! s:convert_to_fullpath(path) "{{{ +function! s:convert_to_fullpath(path) abort "{{{ if exists('+shellslash') let save_shellslash = &l:shellslash let &l:shellslash = 1 @@ -408,7 +410,7 @@ function! s:convert_to_fullpath(path) "{{{ endfunction "}}} " @vimlint(EVL104, 0, l:save_shellslash) -function! s:expand_format_message(format_message, keywords) "{{{ +function! s:expand_format_message(format_message, keywords) abort "{{{ let maxlen = s:Msg.get_hit_enter_max_length() let expanded_msg = s:expand_keywords(a:format_message.msg, a:keywords) if a:format_message.truncate && strlen(expanded_msg) > maxlen @@ -447,7 +449,7 @@ endfunction "}}} " @param uristr String " This function is public for testing. -function! openbrowser#__open_browser__(uristr) "{{{ +function! openbrowser#__open_browser__(uristr) abort "{{{ let uri = a:uristr " Clear previous message @@ -481,11 +483,11 @@ function! openbrowser#__open_browser__(uristr) "{{{ \ 's:expand_keywords( \ v:val, \ { - \ "browser" : quote . cmd.name . quote, - \ "browser_noesc": cmd.name, - \ "uri" : quote . uri . quote, - \ "uri_noesc" : uri, - \ "use_vimproc" : use_vimproc, + \ ''browser'' : quote . cmd.name . quote, + \ ''browser_noesc'': cmd.name, + \ ''uri'' : quote . uri . quote, + \ ''uri_noesc'' : uri, + \ ''use_vimproc'' : use_vimproc, \ } \ )' \) @@ -523,21 +525,21 @@ function! openbrowser#__open_browser__(uristr) "{{{ return 0 endfunction "}}} -function! openbrowser#__system__(...) +function! openbrowser#__system__(...) abort return call(s:Process.system, a:000, s:Process) endfunction " @return Dictionary: the URL on cursor, or the first URL after cursor " Empty Dictionary means no URLs found. " :help openbrowser-url-detection -function! s:get_url_on_cursor() "{{{ +function! s:get_url_on_cursor() abort "{{{ let url = s:get_thing_on_cursor('s:detect_url_cb') return url isnot s:NONE ? url : '' endfunction "}}} " @return the filepath on cursor, or the first filepath after cursor " :help openbrowser-filepath-detection -function! s:get_filepath_on_cursor() "{{{ +function! s:get_filepath_on_cursor() abort "{{{ let filepath = s:get_thing_on_cursor('s:detect_filepath_cb') return filepath isnot s:NONE ? filepath : '' endfunction "}}} @@ -600,7 +602,7 @@ endfunction " Escape by \ if you does not want to expand. " - "\{keyword}" => "{keyword}", not expression `keyword`. " it does not expand vim variable `keyword`. -function! s:expand_keywords(str, options) " {{{ +function! s:expand_keywords(str, options) abort " {{{ if type(a:str) != type('') || type(a:options) != type({}) echoerr 's:expand_keywords(): invalid arguments. (a:str = '.string(a:str).', a:options = '.string(a:options).')' return '' @@ -631,10 +633,10 @@ function! s:expand_keywords(str, options) " {{{ endif " Process special string. - if rest[0] == '\' + if rest[0] ==# '\' let result .= rest[1] let rest = rest[2 :] - elseif rest[0] == '{' + elseif rest[0] ==# '{' " NOTE: braindex + 1 == 1, it skips first bracket (rest[0]) let braindex = 0 let braindex_stack = [braindex] @@ -654,7 +656,7 @@ function! s:expand_keywords(str, options) " {{{ let right = rest[braindex+1 :] " Remove(unescape) backslashes. let expr = substitute(expr, '\\\([{}]\)', '\1', 'g') - let value = eval(expr) . "" + let value = eval(expr) . '' let rest = left . value . right let braindex -= len(expr) - len(value) endif @@ -668,7 +670,7 @@ function! s:expand_keywords(str, options) " {{{ return result endfunction "}}} -function! s:get_var(varname) "{{{ +function! s:get_var(varname) abort "{{{ for ns in [b:, w:, t:, g:] if has_key(ns, a:varname) return ns[a:varname] @@ -679,7 +681,7 @@ function! s:get_var(varname) "{{{ endfunction "}}} " From https://github.com/chikatoike/concealedyank.vim -function! s:getconcealedline(lnum, ...) "{{{ +function! s:getconcealedline(lnum, ...) abort "{{{ if !has('conceal') return getline(a:lnum) endif @@ -710,7 +712,7 @@ function! s:getconcealedline(lnum, ...) "{{{ return ret endfunction "}}} -function! s:getconcealedcol(expr) "{{{ +function! s:getconcealedcol(expr) abort "{{{ if !has('conceal') return col(a:expr) endif @@ -741,7 +743,7 @@ function! s:getconcealedcol(expr) "{{{ return ret endfunction "}}} -function! s:shellslash() +function! s:shellslash() abort return exists('+shellslash') && &l:shellslash endfunction "}}} diff --git a/plugin/openbrowser.vim b/plugin/openbrowser.vim index eb2eaec..e0e22c1 100644 --- a/plugin/openbrowser.vim +++ b/plugin/openbrowser.vim @@ -13,6 +13,7 @@ set cpo&vim " }}} +let s:t_string = type('') let s:is_unix = has('unix') let s:is_mswin = has('win16') || has('win32') || has('win64') let s:is_cygwin = has('win32unix') @@ -183,7 +184,7 @@ let s:FORMAT_MESSAGE_DEFAULT = { \} if !exists('g:openbrowser_format_message') let g:openbrowser_format_message = s:FORMAT_MESSAGE_DEFAULT -elseif type(g:openbrowser_format_message) is type("") +elseif type(g:openbrowser_format_message) is s:t_string " Backward-compatibility let s:msg = g:openbrowser_format_message unlet g:openbrowser_format_message From e4d82cbf6d6ea18bd223999d1bce56dd8be919f8 Mon Sep 17 00:00:00 2001 From: lambdalisue Date: Sun, 23 Jul 2017 12:14:08 +0900 Subject: [PATCH 2/7] Use a new syntax for vital imports --- autoload/openbrowser.vim | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/autoload/openbrowser.vim b/autoload/openbrowser.vim index 837d56e..1ff6380 100644 --- a/autoload/openbrowser.vim +++ b/autoload/openbrowser.vim @@ -6,15 +6,13 @@ let s:save_cpo = &cpo set cpo&vim " }}} -let s:V = vital#openbrowser#new() -let s:Prelude = s:V.import('Prelude') -let s:String = s:V.import('Data.String') -let s:Process = s:V.import('Process') -let s:URI = s:V.import('Web.URI') -let s:HTTP = s:V.import('Web.HTTP') -let s:Buffer = s:V.import('Vim.Buffer') -let s:Msg = s:V.import('Vim.Message') -unlet s:V +let s:Prelude = vital#openbrowser#import('Prelude') +let s:String = vital#openbrowser#import('Data.String') +let s:Process = vital#openbrowser#import('Process') +let s:URI = vital#openbrowser#import('Web.URI') +let s:HTTP = vital#openbrowser#import('Web.HTTP') +let s:Buffer = vital#openbrowser#import('Vim.Buffer') +let s:Msg = vital#openbrowser#import('Vim.Message') let s:t_string = type('') From 9e431c4737c07fea7e11ed1d8daa5cdd31c48671 Mon Sep 17 00:00:00 2001 From: lambdalisue Date: Sun, 23 Jul 2017 12:20:58 +0900 Subject: [PATCH 3/7] Use s:t_xxx instead --- autoload/openbrowser.vim | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/autoload/openbrowser.vim b/autoload/openbrowser.vim index 1ff6380..27775a5 100644 --- a/autoload/openbrowser.vim +++ b/autoload/openbrowser.vim @@ -15,6 +15,8 @@ let s:Buffer = vital#openbrowser#import('Vim.Buffer') let s:Msg = vital#openbrowser#import('Vim.Message') let s:t_string = type('') +let s:t_dict = type({}) +let s:t_list = type([]) " Save/Determine global variable values. @@ -32,7 +34,7 @@ endfunction "}}} " :OpenBrowser " @param uri URI object or String function! openbrowser#open(uri) abort "{{{ - if type(a:uri) is type({}) + if type(a:uri) is s:t_list \ && has_key(a:uri, '__pattern_set') " URI object " Trust URI object value because " it must be validated by parser. @@ -473,11 +475,11 @@ function! openbrowser#__open_browser__(uristr) abort "{{{ " If args is not List, need to escape by open-browser, " not s:Process.system(). let args = deepcopy(cmd.args) - let need_escape = type(args) isnot type([]) + let need_escape = type(args) isnot s:t_list let quote = need_escape ? "'" : '' let use_vimproc = (g:openbrowser_use_vimproc && s:vimproc_is_installed) let system_args = map( - \ (type(args) is type([]) ? copy(args) : [args]), + \ (type(args) is s:t_list ? copy(args) : [args]), \ 's:expand_keywords( \ v:val, \ { @@ -491,7 +493,7 @@ function! openbrowser#__open_browser__(uristr) abort "{{{ \) try call openbrowser#__system__( - \ (type(args) is type([]) ? system_args : system_args[0]), + \ (type(args) is s:t_list ? system_args : system_args[0]), \ {'use_vimproc': use_vimproc, \ 'background': get(cmd, 'background')} \) @@ -601,7 +603,7 @@ endfunction " - "\{keyword}" => "{keyword}", not expression `keyword`. " it does not expand vim variable `keyword`. function! s:expand_keywords(str, options) abort " {{{ - if type(a:str) != type('') || type(a:options) != type({}) + if type(a:str) isnot s:t_string || type(a:options) isnot s:t_dict echoerr 's:expand_keywords(): invalid arguments. (a:str = '.string(a:str).', a:options = '.string(a:options).')' return '' endif From 01fb905089fb409f4df9822325da745fbb7d1f4a Mon Sep 17 00:00:00 2001 From: lambdalisue Date: Sun, 23 Jul 2017 13:19:51 +0900 Subject: [PATCH 4/7] Use System.Process instead of Process It fixes "neovim's system does not support background..." issue --- autoload/openbrowser.vim | 8 +- autoload/vital/_openbrowser/Process.vim | 184 ------------------ .../vital/_openbrowser/System/Process.vim | 102 ++++++++++ .../_openbrowser/System/Process/System.vim | 134 +++++++++++++ .../_openbrowser/System/Process/Vimproc.vim | 88 +++++++++ autoload/vital/openbrowser.vital | 2 +- 6 files changed, 327 insertions(+), 191 deletions(-) delete mode 100644 autoload/vital/_openbrowser/Process.vim create mode 100644 autoload/vital/_openbrowser/System/Process.vim create mode 100644 autoload/vital/_openbrowser/System/Process/System.vim create mode 100644 autoload/vital/_openbrowser/System/Process/Vimproc.vim diff --git a/autoload/openbrowser.vim b/autoload/openbrowser.vim index 27775a5..98eaf9f 100644 --- a/autoload/openbrowser.vim +++ b/autoload/openbrowser.vim @@ -8,7 +8,7 @@ set cpo&vim let s:Prelude = vital#openbrowser#import('Prelude') let s:String = vital#openbrowser#import('Data.String') -let s:Process = vital#openbrowser#import('Process') +let s:Process = vital#openbrowser#import('System.Process') let s:URI = vital#openbrowser#import('Web.URI') let s:HTTP = vital#openbrowser#import('Web.HTTP') let s:Buffer = vital#openbrowser#import('Vim.Buffer') @@ -492,7 +492,7 @@ function! openbrowser#__open_browser__(uristr) abort "{{{ \ )' \) try - call openbrowser#__system__( + call s:Process.execute( \ (type(args) is s:t_list ? system_args : system_args[0]), \ {'use_vimproc': use_vimproc, \ 'background': get(cmd, 'background')} @@ -525,10 +525,6 @@ function! openbrowser#__open_browser__(uristr) abort "{{{ return 0 endfunction "}}} -function! openbrowser#__system__(...) abort - return call(s:Process.system, a:000, s:Process) -endfunction - " @return Dictionary: the URL on cursor, or the first URL after cursor " Empty Dictionary means no URLs found. " :help openbrowser-url-detection diff --git a/autoload/vital/_openbrowser/Process.vim b/autoload/vital/_openbrowser/Process.vim deleted file mode 100644 index 30c4144..0000000 --- a/autoload/vital/_openbrowser/Process.vim +++ /dev/null @@ -1,184 +0,0 @@ -" ___vital___ -" NOTE: lines between '" ___vital___' is generated by :Vitalize. -" Do not mofidify the code nor insert new lines before '" ___vital___' -if v:version > 703 || v:version == 703 && has('patch1170') - function! vital#_openbrowser#Process#import() abort - return map({'shellescape': '', 'has_vimproc': '', 'system': '', 'iconv': '', 'spawn': '', 'get_last_status': ''}, 'function("s:" . v:key)') - endfunction -else - function! s:_SID() abort - return matchstr(expand(''), '\zs\d\+\ze__SID$') - endfunction - execute join(['function! vital#_openbrowser#Process#import() abort', printf("return map({'shellescape': '', 'has_vimproc': '', 'system': '', 'iconv': '', 'spawn': '', 'get_last_status': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") - delfunction s:_SID -endif -" ___vital___ -" TODO: move all comments to doc file. -" -" -" FIXME: This module name should be Vital.System ? -" But the name has been already taken. - -let s:save_cpo = &cpo -set cpo&vim - - -" FIXME: Unfortunately, can't use s:_vital_loaded() for this purpose. -" Because these variables are used when this script file is loaded. -let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95') -let s:is_unix = has('unix') -" As of 7.4.122, the system()'s 1st argument is converted internally by Vim. -" Note that Patch 7.4.122 does not convert system()'s 2nd argument and -" return-value. We must convert them manually. -let s:need_trans = v:version < 704 || (v:version == 704 && !has('patch122')) - -let s:TYPE_DICT = type({}) -let s:TYPE_LIST = type([]) -let s:TYPE_STRING = type('') - -function! s:spawn(expr, ...) abort - let shellslash = 0 - if s:is_windows - let shellslash = &l:shellslash - setlocal noshellslash - endif - try - if type(a:expr) is s:TYPE_LIST - let special = 1 - let cmdline = join(map(a:expr, 'shellescape(v:val, special)'), ' ') - elseif type(a:expr) is s:TYPE_STRING - let cmdline = a:expr - if a:0 && a:1 - " for :! command - let cmdline = substitute(cmdline, '\([!%#]\|<[^<>]\+>\)', '\\\1', 'g') - endif - else - throw 'vital: Process: invalid argument (value type:' . type(a:expr) . ')' - endif - if s:is_windows - silent execute '!start' cmdline - else - silent execute '!' cmdline '&' - endif - finally - if s:is_windows - let &l:shellslash = shellslash - endif - endtry - return '' -endfunction - -" iconv() wrapper for safety. -function! s:iconv(expr, from, to) abort - if a:from ==# '' || a:to ==# '' || a:from ==? a:to - return a:expr - endif - let result = iconv(a:expr, a:from, a:to) - return result !=# '' ? result : a:expr -endfunction - -" Check vimproc. -function! s:has_vimproc() abort - if !exists('s:exists_vimproc') - try - call vimproc#version() - let s:exists_vimproc = 1 - catch - let s:exists_vimproc = 0 - endtry - endif - return s:exists_vimproc -endfunction - -" * {command} [, {input} [, {timeout}]] -" * {command} [, {dict}] -" {dict} = { -" use_vimproc: bool, -" input: string, -" timeout: bool, -" background: bool, -" } -function! s:system(str, ...) abort - " Process optional arguments at first - " because use_vimproc is required later - " for a:str argument. - let input = '' - let use_vimproc = s:has_vimproc() - let background = 0 - let args = [] - if a:0 ==# 1 - " {command} [, {dict}] - " a:1 = {dict} - if type(a:1) is s:TYPE_DICT - if has_key(a:1, 'use_vimproc') - let use_vimproc = a:1.use_vimproc - endif - if has_key(a:1, 'input') - let args += [s:iconv(a:1.input, &encoding, 'char')] - endif - if use_vimproc && has_key(a:1, 'timeout') - " ignores timeout unless you have vimproc. - let args += [a:1.timeout] - endif - if has_key(a:1, 'background') - let background = a:1.background - endif - elseif type(a:1) is s:TYPE_STRING - let args += [s:iconv(a:1, &encoding, 'char')] - else - throw 'vital: Process: invalid argument (value type:' . type(a:1) . ')' - endif - elseif a:0 >= 2 - " {command} [, {input} [, {timeout}]] - " a:000 = [{input} [, {timeout}]] - let [input; rest] = a:000 - let input = s:iconv(input, &encoding, 'char') - let args += [input] + rest - endif - - " Process a:str argument. - if type(a:str) is s:TYPE_LIST - let expr = use_vimproc ? '"''" . v:val . "''"' : 's:shellescape(v:val)' - let command = join(map(copy(a:str), expr), ' ') - elseif type(a:str) is s:TYPE_STRING - let command = a:str - else - throw 'vital: Process: invalid argument (value type:' . type(a:str) . ')' - endif - if s:need_trans - let command = s:iconv(command, &encoding, 'char') - endif - let args = [command] + args - if background && (use_vimproc || !s:is_windows) - if has('nvim') - throw "vital: Process: neovim's system() doesn't support background(&) process (cmdline:" . string(a:str) . ')' - endif - let args[0] = args[0] . ' &' - endif - - let funcname = use_vimproc ? 'vimproc#system' : 'system' - let output = call(funcname, args) - let output = s:iconv(output, 'char', &encoding) - return output -endfunction - -function! s:get_last_status() abort - return s:has_vimproc() ? - \ vimproc#get_last_status() : v:shell_error -endfunction - -if s:is_windows - function! s:shellescape(command) abort - return substitute(a:command, '[&()[\]{}^=;!''+,`~]', '^\0', 'g') - endfunction -else - function! s:shellescape(...) abort - return call('shellescape', a:000) - endfunction -endif - - -let &cpo = s:save_cpo -unlet s:save_cpo - -" vim:set et ts=2 sts=2 sw=2 tw=0: diff --git a/autoload/vital/_openbrowser/System/Process.vim b/autoload/vital/_openbrowser/System/Process.vim new file mode 100644 index 0000000..016c052 --- /dev/null +++ b/autoload/vital/_openbrowser/System/Process.vim @@ -0,0 +1,102 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_openbrowser#System#Process#import() abort + return map({'_vital_depends': '', 'execute': '', 'register': '', '_vital_loaded': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_openbrowser#System#Process#import() abort', printf("return map({'_vital_depends': '', 'execute': '', 'register': '', '_vital_loaded': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ +let s:save_cpoptions = &cpoptions +set cpoptions&vim + +let s:registry = {} +let s:priority = [] + +function! s:_vital_loaded(V) abort + let s:V = a:V + let s:Prelude = a:V.import('Prelude') + let s:String = a:V.import('Data.String') + call s:register('System.Process.Vimproc') + call s:register('System.Process.System') +endfunction + +function! s:_vital_depends() abort + return [ + \ 'Prelude', + \ 'Data.String', + \ 'System.Process.System', + \ 'System.Process.Vimproc', + \] +endfunction + +function! s:_throw(msg) abort + throw printf('vital: System.Process: %s', a:msg) +endfunction + +function! s:register(name) abort + let client = s:V.import(a:name) + if client.is_available() + let s:registry[a:name] = client + call add(s:priority, a:name) + endif +endfunction + +function! s:_execute(args, options) abort + for name_or_client in a:options.clients + let client = s:Prelude.is_string(name_or_client) + \ ? s:registry[name_or_client] + \ : name_or_client + if !client.is_supported(a:options) + continue + endif + return client.execute(a:args, a:options) + endfor + call s:_throw(printf( + \ 'None of client support options : %s', + \ string(a:options), + \)) +endfunction + +" execute({args}[, {options}]) +function! s:execute(args, ...) abort + let options = extend({ + \ 'clients': s:priority, + \ 'input': 0, + \ 'timeout': 0, + \ 'background': 0, + \ 'encode_input': 1, + \ 'encode_output': 1, + \ 'split_output': 1, + \ 'debug': &verbose, + \}, get(a:000, 0, {})) + if s:Prelude.is_string(options.input) && !empty(options.encode_input) + let encoding = s:Prelude.is_string(options.encode_input) + \ ? options.encode_input + \ : &encoding + let options.input = s:String.iconv(options.input, encoding, 'char') + endif + let result = s:_execute(a:args, options) + if s:Prelude.is_string(result.output) && !empty(options.encode_output) + let encoding = s:Prelude.is_string(options.encode_output) + \ ? options.encode_output + \ : &encoding + let result.output = s:String.iconv(result.output, 'char', encoding) + endif + if options.split_output + let result.content = s:String.split_posix_text(result.output) + endif + let result.success = result.status == 0 + let result.args = a:args + let result.options = options + return result +endfunction + +let &cpoptions = s:save_cpoptions +unlet s:save_cpoptions diff --git a/autoload/vital/_openbrowser/System/Process/System.vim b/autoload/vital/_openbrowser/System/Process/System.vim new file mode 100644 index 0000000..f41f01e --- /dev/null +++ b/autoload/vital/_openbrowser/System/Process/System.vim @@ -0,0 +1,134 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_openbrowser#System#Process#System#import() abort + return map({'_vital_depends': '', 'shellescape': '', 'execute': '', 'is_supported': '', 'is_available': '', '_vital_loaded': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_openbrowser#System#Process#System#import() abort', printf("return map({'_vital_depends': '', 'shellescape': '', 'execute': '', 'is_supported': '', 'is_available': '', '_vital_loaded': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ +let s:save_cpoptions = &cpoptions +set cpoptions&vim + +function! s:_vital_loaded(V) abort + let s:Prelude = a:V.import('Prelude') + let s:String = a:V.import('Data.String') + let s:Guard = a:V.import('Vim.Guard') +endfunction + +function! s:_vital_depends() abort + return [ + \ 'Prelude', + \ 'Data.String', + \ 'Vim.Guard', + \] +endfunction + +function! s:is_available() abort + return 1 +endfunction + +function! s:is_supported(options) abort + if get(a:options, 'timeout') + return 0 + elseif get(a:options, 'background') && s:Prelude.is_windows() + return 0 + endif + return 1 +endfunction + +function! s:shellescape(string) abort + if s:Prelude.is_windows() + " NOTE: + " In windows, a string which does not contain space SHOULD NOT be escaped + return a:string =~# '\s' ? shellescape(a:string) : a:string + else + return shellescape(a:string) + endif +endfunction + +function! s:execute(args, options) abort + " NOTE: + " execute() is a command for executing program WITHOUT using shell. + " so mimic that behaviour with shell + let guard = s:Guard.store(filter([ + \ '&shell', + \ '&shellcmdflag', + \ '&shellquote', + \ '&shellredir', + \ '&shelltemp', + \ (exists('+shelltype') ? '&shelltype' : ''), + \ (exists('+shellxescape') ? '&shellxescape' : ''), + \ (exists('+shellxquote') ? '&shellxquote' : ''), + \ (exists('+shellslash') ? '&shellslash' : ''), + \], '!empty(v:val)') + \) + try + " Reset shell related options + if s:Prelude.is_windows() + set shell& + if exists('+shellslash') + set shellslash& + endif + else + set shell=sh + endif + set shellcmdflag& shellquote& shellredir& shelltemp& + if exists('+shelltype') + set shelltype& + endif + if exists('+shellxescape') + set shellxescape& + endif + if exists('+shellxquote') + set shellxquote& + endif + let cmdline = join(map( + \ copy(a:args), + \ 's:shellescape(v:val)', + \)) + if a:options.background && !s:Prelude.is_windows() + let cmdline = cmdline . ' &' + endif + if a:options.debug > 0 + echomsg printf( + \ 'vital: System.Process.System: %s', + \ cmdline + \) + endif + if v:version < 704 || (v:version == 704 && !has('patch122')) + " {cmdline} of system() before Vim 7.4.122 is not converted so convert + " it manually from &encoding to 'char' + let cmdline = s:String.iconv(cmdline, &encoding, 'char') + endif + let args = [cmdline] + (s:Prelude.is_string(a:options.input) ? [a:options.input] : []) + let output = call('system', args) + if s:Prelude.is_windows() + " A builtin system() add a trailing space in Windows. + " It is probably an issue of pipe in Windows so remove it. + let output = substitute(output, '\s\n$', '\n', '') + endif + " NOTE: + " Vim 7.4 always return exit_status:0 for background process so mimic + let status = a:options.background ? 0 : v:shell_error + " NOTE: + " status, output are COMMON information + " cmdline is an EXTRA information + return { + \ 'status': status, + \ 'output': output, + \ 'cmdline': cmdline, + \} + finally + call guard.restore() + endtry +endfunction + +let &cpoptions = s:save_cpoptions +unlet s:save_cpoptions diff --git a/autoload/vital/_openbrowser/System/Process/Vimproc.vim b/autoload/vital/_openbrowser/System/Process/Vimproc.vim new file mode 100644 index 0000000..08f19fc --- /dev/null +++ b/autoload/vital/_openbrowser/System/Process/Vimproc.vim @@ -0,0 +1,88 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_openbrowser#System#Process#Vimproc#import() abort + return map({'_vital_depends': '', 'execute': '', 'is_supported': '', 'is_available': '', '_vital_loaded': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_openbrowser#System#Process#Vimproc#import() abort', printf("return map({'_vital_depends': '', 'execute': '', 'is_supported': '', 'is_available': '', '_vital_loaded': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ +let s:save_cpoptions = &cpoptions +set cpoptions&vim + +function! s:_vital_loaded(V) abort + let s:Prelude = a:V.import('Prelude') +endfunction + +function! s:_vital_depends() abort + return [ + \ 'Prelude', + \] +endfunction + +function! s:is_available() abort + if exists('s:vimproc_available') + return s:vimproc_available + endif + try + call vimproc#version() + let s:vimproc_available = 1 + catch + let s:vimproc_available = 0 + endtry + return s:vimproc_available +endfunction + +function! s:is_supported(options) abort + if get(a:options, 'background') && ( + \ s:Prelude.is_string(get(a:options, 'input')) || + \ get(a:options, 'timeout') + \) + return 0 + endif + return 1 +endfunction + +function! s:execute(args, options) abort + let cmdline = join(map( + \ copy(a:args), + \ 'vimproc#shellescape(v:val)', + \)) + if a:options.debug > 0 + echomsg printf( + \ 'vital: System.Process.Vimproc: %s', + \ cmdline + \) + endif + if a:options.background + let output = vimproc#system_bg(cmdline) + " NOTE: + " background process via Builtin always return exit_code:0 so mimic + let status = 0 + else + let output = vimproc#system( + \ cmdline, + \ s:Prelude.is_string(a:options.input) ? a:options.input : '', + \ a:options.timeout, + \) + let status = vimproc#get_last_status() + endif + " NOTE: + " status, output are COMMON information + " errormsg, cmdline are EXTRA information + return { + \ 'status': status, + \ 'output': output, + \ 'errormsg': vimproc#get_last_errmsg(), + \ 'cmdline': cmdline, + \} +endfunction + +let &cpoptions = s:save_cpoptions +unlet s:save_cpoptions diff --git a/autoload/vital/openbrowser.vital b/autoload/vital/openbrowser.vital index ba17fb7..8f7f930 100644 --- a/autoload/vital/openbrowser.vital +++ b/autoload/vital/openbrowser.vital @@ -1,9 +1,9 @@ openbrowser 8b5b32ae61c863bf8d930921a9213687774327d1 -Process Web.URI Web.HTTP Vim.Buffer Prelude Vim.Message +System.Process From 7f3644bb97923123e774403920acd1607b8f3789 Mon Sep 17 00:00:00 2001 From: lambdalisue Date: Sun, 23 Jul 2017 14:00:29 +0900 Subject: [PATCH 5/7] Add Process again Web.HTTP still require Process module --- autoload/vital/_openbrowser/Process.vim | 184 ++++++++++++++++++++++++ autoload/vital/openbrowser.vital | 1 + 2 files changed, 185 insertions(+) create mode 100644 autoload/vital/_openbrowser/Process.vim diff --git a/autoload/vital/_openbrowser/Process.vim b/autoload/vital/_openbrowser/Process.vim new file mode 100644 index 0000000..30c4144 --- /dev/null +++ b/autoload/vital/_openbrowser/Process.vim @@ -0,0 +1,184 @@ +" ___vital___ +" NOTE: lines between '" ___vital___' is generated by :Vitalize. +" Do not mofidify the code nor insert new lines before '" ___vital___' +if v:version > 703 || v:version == 703 && has('patch1170') + function! vital#_openbrowser#Process#import() abort + return map({'shellescape': '', 'has_vimproc': '', 'system': '', 'iconv': '', 'spawn': '', 'get_last_status': ''}, 'function("s:" . v:key)') + endfunction +else + function! s:_SID() abort + return matchstr(expand(''), '\zs\d\+\ze__SID$') + endfunction + execute join(['function! vital#_openbrowser#Process#import() abort', printf("return map({'shellescape': '', 'has_vimproc': '', 'system': '', 'iconv': '', 'spawn': '', 'get_last_status': ''}, \"function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") + delfunction s:_SID +endif +" ___vital___ +" TODO: move all comments to doc file. +" +" +" FIXME: This module name should be Vital.System ? +" But the name has been already taken. + +let s:save_cpo = &cpo +set cpo&vim + + +" FIXME: Unfortunately, can't use s:_vital_loaded() for this purpose. +" Because these variables are used when this script file is loaded. +let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95') +let s:is_unix = has('unix') +" As of 7.4.122, the system()'s 1st argument is converted internally by Vim. +" Note that Patch 7.4.122 does not convert system()'s 2nd argument and +" return-value. We must convert them manually. +let s:need_trans = v:version < 704 || (v:version == 704 && !has('patch122')) + +let s:TYPE_DICT = type({}) +let s:TYPE_LIST = type([]) +let s:TYPE_STRING = type('') + +function! s:spawn(expr, ...) abort + let shellslash = 0 + if s:is_windows + let shellslash = &l:shellslash + setlocal noshellslash + endif + try + if type(a:expr) is s:TYPE_LIST + let special = 1 + let cmdline = join(map(a:expr, 'shellescape(v:val, special)'), ' ') + elseif type(a:expr) is s:TYPE_STRING + let cmdline = a:expr + if a:0 && a:1 + " for :! command + let cmdline = substitute(cmdline, '\([!%#]\|<[^<>]\+>\)', '\\\1', 'g') + endif + else + throw 'vital: Process: invalid argument (value type:' . type(a:expr) . ')' + endif + if s:is_windows + silent execute '!start' cmdline + else + silent execute '!' cmdline '&' + endif + finally + if s:is_windows + let &l:shellslash = shellslash + endif + endtry + return '' +endfunction + +" iconv() wrapper for safety. +function! s:iconv(expr, from, to) abort + if a:from ==# '' || a:to ==# '' || a:from ==? a:to + return a:expr + endif + let result = iconv(a:expr, a:from, a:to) + return result !=# '' ? result : a:expr +endfunction + +" Check vimproc. +function! s:has_vimproc() abort + if !exists('s:exists_vimproc') + try + call vimproc#version() + let s:exists_vimproc = 1 + catch + let s:exists_vimproc = 0 + endtry + endif + return s:exists_vimproc +endfunction + +" * {command} [, {input} [, {timeout}]] +" * {command} [, {dict}] +" {dict} = { +" use_vimproc: bool, +" input: string, +" timeout: bool, +" background: bool, +" } +function! s:system(str, ...) abort + " Process optional arguments at first + " because use_vimproc is required later + " for a:str argument. + let input = '' + let use_vimproc = s:has_vimproc() + let background = 0 + let args = [] + if a:0 ==# 1 + " {command} [, {dict}] + " a:1 = {dict} + if type(a:1) is s:TYPE_DICT + if has_key(a:1, 'use_vimproc') + let use_vimproc = a:1.use_vimproc + endif + if has_key(a:1, 'input') + let args += [s:iconv(a:1.input, &encoding, 'char')] + endif + if use_vimproc && has_key(a:1, 'timeout') + " ignores timeout unless you have vimproc. + let args += [a:1.timeout] + endif + if has_key(a:1, 'background') + let background = a:1.background + endif + elseif type(a:1) is s:TYPE_STRING + let args += [s:iconv(a:1, &encoding, 'char')] + else + throw 'vital: Process: invalid argument (value type:' . type(a:1) . ')' + endif + elseif a:0 >= 2 + " {command} [, {input} [, {timeout}]] + " a:000 = [{input} [, {timeout}]] + let [input; rest] = a:000 + let input = s:iconv(input, &encoding, 'char') + let args += [input] + rest + endif + + " Process a:str argument. + if type(a:str) is s:TYPE_LIST + let expr = use_vimproc ? '"''" . v:val . "''"' : 's:shellescape(v:val)' + let command = join(map(copy(a:str), expr), ' ') + elseif type(a:str) is s:TYPE_STRING + let command = a:str + else + throw 'vital: Process: invalid argument (value type:' . type(a:str) . ')' + endif + if s:need_trans + let command = s:iconv(command, &encoding, 'char') + endif + let args = [command] + args + if background && (use_vimproc || !s:is_windows) + if has('nvim') + throw "vital: Process: neovim's system() doesn't support background(&) process (cmdline:" . string(a:str) . ')' + endif + let args[0] = args[0] . ' &' + endif + + let funcname = use_vimproc ? 'vimproc#system' : 'system' + let output = call(funcname, args) + let output = s:iconv(output, 'char', &encoding) + return output +endfunction + +function! s:get_last_status() abort + return s:has_vimproc() ? + \ vimproc#get_last_status() : v:shell_error +endfunction + +if s:is_windows + function! s:shellescape(command) abort + return substitute(a:command, '[&()[\]{}^=;!''+,`~]', '^\0', 'g') + endfunction +else + function! s:shellescape(...) abort + return call('shellescape', a:000) + endfunction +endif + + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim:set et ts=2 sts=2 sw=2 tw=0: diff --git a/autoload/vital/openbrowser.vital b/autoload/vital/openbrowser.vital index 8f7f930..b13e438 100644 --- a/autoload/vital/openbrowser.vital +++ b/autoload/vital/openbrowser.vital @@ -7,3 +7,4 @@ Vim.Buffer Prelude Vim.Message System.Process +Process From bee6e0cc850c462b9af0787e02fcff26d990a74f Mon Sep 17 00:00:00 2001 From: lambdalisue Date: Sun, 23 Jul 2017 14:00:43 +0900 Subject: [PATCH 6/7] Remove unnecessary function --- autoload/openbrowser.vim | 5 ----- 1 file changed, 5 deletions(-) diff --git a/autoload/openbrowser.vim b/autoload/openbrowser.vim index 98eaf9f..3d2f4b0 100644 --- a/autoload/openbrowser.vim +++ b/autoload/openbrowser.vim @@ -142,11 +142,6 @@ function! openbrowser#smart_search(query, ...) abort "{{{ endif endfunction "}}} -" Escape one argument. -function! openbrowser#shellescape(...) abort "{{{ - return call(s:Process.shellescape, a:000, s:Process) -endfunction "}}} - " }}} " Implementations {{{ From b0dccb3be0f708d189420463ebff1838dea01a58 Mon Sep 17 00:00:00 2001 From: lambdalisue Date: Fri, 28 Jul 2017 19:57:50 +0900 Subject: [PATCH 7/7] Use __system__ instead --- autoload/openbrowser.vim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/autoload/openbrowser.vim b/autoload/openbrowser.vim index 3d2f4b0..2487b1a 100644 --- a/autoload/openbrowser.vim +++ b/autoload/openbrowser.vim @@ -487,7 +487,7 @@ function! openbrowser#__open_browser__(uristr) abort "{{{ \ )' \) try - call s:Process.execute( + call openbrowser#__system__( \ (type(args) is s:t_list ? system_args : system_args[0]), \ {'use_vimproc': use_vimproc, \ 'background': get(cmd, 'background')} @@ -520,6 +520,10 @@ function! openbrowser#__open_browser__(uristr) abort "{{{ return 0 endfunction "}}} +function! openbrowser#__system__(...) abort + return call(s:Process.execute, a:000, s:Process) +endfunction + " @return Dictionary: the URL on cursor, or the first URL after cursor " Empty Dictionary means no URLs found. " :help openbrowser-url-detection