summaryrefslogtreecommitdiffstats
path: root/autoload/vital/_crystal/Process.vim
blob: 9a15993202a55727d4cc79115b5cef8772754ab0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'crystal') == -1

" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify 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#_crystal#Process#import() abort', printf("return map({'shellescape': '', 'has_vimproc': '', 'system': '', 'iconv': '', 'spawn': '', 'get_last_status': ''}, \"vital#_crystal#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___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
  if type(a:expr) is s:TYPE_LIST
    let special = 1
    let cmdline = join(map(a:expr, 's: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
  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(...) abort
    try
      let shellslash = &shellslash
      set noshellslash
      return call('shellescape', a:000)
    finally
      let &shellslash = shellslash
    endtry
  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:

endif