Skip to content

Commit 3317ac1

Browse files
author
skywind3000
committed
new runner: macos terminal
1 parent 06034f0 commit 3317ac1

File tree

3 files changed

+383
-0
lines changed

3 files changed

+383
-0
lines changed

autoload/asyncrun/macos.vim

+294
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
"======================================================================
2+
"
3+
" macos.vim -
4+
"
5+
" Created by skywind on 2021/12/30
6+
" Last Modified: 2021/12/30 15:52:58
7+
"
8+
"======================================================================
9+
10+
" vim: set ts=4 sw=4 tw=78 noet :
11+
12+
13+
"----------------------------------------------------------------------
14+
" script name
15+
"----------------------------------------------------------------------
16+
function! asyncrun#macos#script_name(name)
17+
let tmpname = fnamemodify(tempname(), ':h') . '/' . a:name
18+
return tmpname
19+
endfunc
20+
21+
22+
"----------------------------------------------------------------------
23+
" write script
24+
"----------------------------------------------------------------------
25+
function! asyncrun#macos#script_write(name, content)
26+
let tmpname = fnamemodify(tempname(), ':h') . '/' . a:name
27+
call writefile(a:content, tmpname)
28+
silent! call setfperm(tmpname, 'rwxrwxrws')
29+
return tmpname
30+
endfunc
31+
32+
33+
"----------------------------------------------------------------------
34+
" return pause script
35+
"----------------------------------------------------------------------
36+
function! asyncrun#macos#pause_script()
37+
let lines = []
38+
if executable('bash')
39+
let pause = 'read -n1 -rsp "press any key to continue ..."'
40+
let lines += ['bash -c ''' . pause . '''']
41+
else
42+
let lines += ['echo "press enter to continue ..."']
43+
let lines += ['sh -c "read _tmp_"']
44+
endif
45+
return lines
46+
endfunc
47+
48+
49+
"----------------------------------------------------------------------
50+
" write a scpt file
51+
"----------------------------------------------------------------------
52+
function! asyncrun#macos#osascript(content, wait)
53+
let content = ['#! /usr/bin/osascript', '']
54+
let content += a:content
55+
let tmpname = asyncrun#macos#script_write('runner1.scpt', content)
56+
let cmd = '/usr/bin/osascript ' . shellescape(tmpname)
57+
call system(cmd . ((a:wait)? '' : ' &'))
58+
endfunc
59+
60+
61+
"----------------------------------------------------------------------
62+
" open system terminal
63+
"----------------------------------------------------------------------
64+
function! asyncrun#macos#open_system(title, script, profile)
65+
let content = ['#! /bin/sh']
66+
let content = ['clear']
67+
let content += [asyncrun#utils#set_title(a:title, 0)]
68+
let content += a:script
69+
let tmpname = asyncrun#macos#script_write('runner1.sh', content)
70+
let cmd = 'open -a Terminal ' . shellescape(tmpname)
71+
call system(cmd . ' &')
72+
endfunc
73+
74+
75+
"----------------------------------------------------------------------
76+
" open terminal
77+
"----------------------------------------------------------------------
78+
function! asyncrun#macos#open_terminal(title, script, profile, active)
79+
let content = ['#! /bin/sh']
80+
let content += ['clear']
81+
let content += [asyncrun#utils#set_title(a:title, 0)]
82+
let content += a:script
83+
let tmpname = asyncrun#macos#script_write('runner2.sh', content)
84+
let osascript = []
85+
let osascript += ['tell application "Terminal"']
86+
let osascript += [' if it is running then']
87+
let osascript += [' do script "' . tmpname . '; exit"']
88+
let osascript += [' else']
89+
let osascript += [' do script "' . tmpname . '; exit" in window 1']
90+
let osascript += [' end if']
91+
let x = ' set current settings of selected tab of '
92+
let x = x . 'window 1 to settings set "' . a:profile . '"'
93+
if a:profile != ''
94+
let osascript += [x]
95+
endif
96+
if a:active
97+
let osascript += [' activate']
98+
endif
99+
let osascript += ['end tell']
100+
call asyncrun#macos#osascript(osascript, 1)
101+
return 1
102+
endfunc
103+
104+
105+
"----------------------------------------------------------------------
106+
" utils
107+
"----------------------------------------------------------------------
108+
function! s:osascript(...) abort
109+
call system('osascript'.join(map(copy(a:000), '" -e ".shellescape(v:val)'), ''))
110+
return !v:shell_error
111+
endfunc
112+
113+
function! s:escape(string) abort
114+
return '"'.escape(a:string, '"\').'"'
115+
endfunc
116+
117+
function! s:iterm_new_version() abort
118+
if !exists('s:iterm_is_new')
119+
let s:iterm_is_new = asyncrun#macos#iterm_new_version()
120+
endif
121+
return s:iterm_is_new
122+
endfunc
123+
124+
125+
"----------------------------------------------------------------------
126+
" check version
127+
"----------------------------------------------------------------------
128+
function! asyncrun#macos#iterm_new_version() abort
129+
return s:osascript(
130+
\ 'on modernversion(version)',
131+
\ 'set olddelimiters to AppleScript''s text item delimiters',
132+
\ 'set AppleScript''s text item delimiters to "."',
133+
\ 'set thearray to every text item of version',
134+
\ 'set AppleScript''s text item delimiters to olddelimiters',
135+
\ 'set major to item 1 of thearray',
136+
\ 'set minor to item 2 of thearray',
137+
\ 'set veryminor to item 3 of thearray',
138+
\ 'if major < 2 then return false',
139+
\ 'if major > 2 then return true',
140+
\ 'if minor < 9 then return false',
141+
\ 'if minor > 9 then return true',
142+
\ 'if veryminor < 20140903 then return false',
143+
\ 'return true',
144+
\ 'end modernversion',
145+
\ 'tell application "iTerm"',
146+
\ 'if not my modernversion(version) then error',
147+
\ 'end tell')
148+
endfunction
149+
150+
151+
"----------------------------------------------------------------------
152+
" spawn2
153+
"----------------------------------------------------------------------
154+
function! asyncrun#macos#iterm_spawn2(script, opts, activate) abort
155+
let script = asyncrun#utils#isolate(a:opts, [],
156+
\ asyncrun#utils#set_title(a:opts.title, a:opts.expanded), a:script)
157+
return s:osascript(
158+
\ 'if application "iTerm" is not running',
159+
\ 'error',
160+
\ 'end if') && s:osascript(
161+
\ 'tell application "iTerm"',
162+
\ 'tell the current terminal',
163+
\ 'set oldsession to the current session',
164+
\ 'tell (make new session)',
165+
\ 'set name to ' . s:escape(a:opts.title),
166+
\ 'set title to ' . s:escape(a:opts.expanded),
167+
\ 'exec command ' . s:escape(script),
168+
\ a:opts.background ? 'select oldsession' : '',
169+
\ 'end tell',
170+
\ 'end tell',
171+
\ a:activate ? 'activate' : '',
172+
\ 'end tell')
173+
endfunc
174+
175+
176+
"----------------------------------------------------------------------
177+
" spawn3
178+
"----------------------------------------------------------------------
179+
function! asyncrun#macos#iterm_spawn3(script, opts, activate) abort
180+
let script = asyncrun#utils#isolate(a:opts, [],
181+
\ asyncrun#utils#set_title(a:opts.title, a:opts.expanded), a:script)
182+
" echom 'name: '. script
183+
return s:osascript(
184+
\ 'if application "iTerm" is not running',
185+
\ 'error',
186+
\ 'end if') && s:osascript(
187+
\ 'tell application "iTerm"',
188+
\ 'tell the current window',
189+
\ 'set oldtab to the current tab',
190+
\ 'set newtab to (create tab with default profile command ' . s:escape(script) . ')',
191+
\ 'tell current session of newtab',
192+
\ 'set name to ' . s:escape(a:opts.title),
193+
\ 'set title to ' . s:escape(a:opts.expanded),
194+
\ 'end tell',
195+
\ a:opts.background ? 'select oldtab' : '',
196+
\ 'end tell',
197+
\ a:activate ? 'activate' : '',
198+
\ 'end tell')
199+
endfunc
200+
201+
202+
"----------------------------------------------------------------------
203+
" spawn new iterm
204+
"----------------------------------------------------------------------
205+
function! asyncrun#macos#open_iterm(script, opts)
206+
let opts = {}
207+
let opts.title = get(a:opts, 'title', 'AsyncRun')
208+
let opts.expanded = get(a:opts, 'expanded', 1)
209+
let opts.background = get(a:opts, 'background', 0)
210+
let opts.file = asyncrun#macos#script_name(expand('%:t'))
211+
let active = get(a:opts, 'active', 1)
212+
let script = deepcopy(a:script)
213+
if s:iterm_new_version()
214+
return asyncrun#macos#iterm_spawn3(script, opts, active)
215+
else
216+
return asyncrun#macos#iterm_spawn2(script, opts, active)
217+
endif
218+
endfunc
219+
220+
function! asyncrun#macos#iterm_activate(pid)
221+
if s:iterm_new_version()
222+
let tty = matchstr(system('ps -p '.a:pid), 'tty\S\+')
223+
if !empty(tty)
224+
return s:osascript(
225+
\ 'if application "iTerm" is not running',
226+
\ 'error',
227+
\ 'end if') && s:osascript(
228+
\ 'tell application "iTerm"',
229+
\ 'activate',
230+
\ 'tell the current window',
231+
\ 'repeat with atab in tabs',
232+
\ 'repeat with asession in sessions',
233+
\ 'if (tty) = ' . tty,
234+
\ 'select atab',
235+
\ 'end repeat',
236+
\ 'end repeat',
237+
\ 'end tell',
238+
\ 'end tell')
239+
endif
240+
else
241+
let tty = matchstr(system('ps -p '.a:pid), 'tty\S\+')
242+
if !empty(tty)
243+
return s:osascript(
244+
\ 'if application "iTerm" is not running',
245+
\ 'error',
246+
\ 'end if') && s:osascript(
247+
\ 'tell application "iTerm"',
248+
\ 'activate',
249+
\ 'tell the current terminal',
250+
\ 'select session id "/dev/'.tty.'"',
251+
\ 'end tell',
252+
\ 'end tell')
253+
endif
254+
endif
255+
endfunction
256+
257+
258+
"----------------------------------------------------------------------
259+
" start_command
260+
"----------------------------------------------------------------------
261+
function! asyncrun#macos#start_command(runner, opts)
262+
let script = []
263+
let script += ['cd ' . shellescape(getcwd())]
264+
let script += [a:opts.cmd]
265+
let op = {}
266+
let op.title = a:opts.cmd
267+
let op.active = get(a:opts, 'focus', 1)
268+
let op.background = get(a:opts, 'focus', 1)? 0 : 1
269+
if get(a:opts, 'close', 0) == 0
270+
let script += ['echo ""']
271+
let script += asyncrun#macos#pause_script()
272+
endif
273+
if a:runner == 'terminal'
274+
let p = get(a:opts, 'option', '')
275+
return asyncrun#macos#open_terminal(op.title, script, p, op.active)
276+
elseif a:runner == 'iterm' || a:runner == 'iterm2'
277+
return asyncrun#macos#open_iterm(script, op)
278+
endif
279+
endfunc
280+
281+
282+
"----------------------------------------------------------------------
283+
" check environ
284+
"----------------------------------------------------------------------
285+
function! asyncrun#macos#check()
286+
if has('mac') || has('macunix') || has('osx') || has('osxdarwin')
287+
return 1
288+
elseif has('gui_macvim') || has('macvim')
289+
return 1
290+
endif
291+
return 0
292+
endfunc
293+
294+

autoload/asyncrun/runner/macos.vim

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"======================================================================
2+
"
3+
" macos.vim -
4+
"
5+
" Created by skywind on 2021/12/30
6+
" Last Modified: 2021/12/30 20:06:52
7+
"
8+
"======================================================================
9+
10+
function! asyncrun#runner#macos#run(opts)
11+
if asyncrun#macos#check() == 0
12+
return asyncrun#utils#errmsg('require macOS !')
13+
endif
14+
call asyncrun#macos#start_command('terminal', a:opts)
15+
redraw!
16+
endfunc
17+
18+

autoload/asyncrun/utils.vim

+71
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,74 @@ function! asyncrun#utils#require(what)
3030
endfunc
3131

3232

33+
"----------------------------------------------------------------------
34+
" shellescape
35+
"----------------------------------------------------------------------
36+
function! asyncrun#utils#shellescape(...) abort
37+
let args = []
38+
for arg in a:000
39+
if arg =~# '^[A-Za-z0-9_/.-]\+$'
40+
let args += [arg]
41+
elseif &shell =~# 'c\@<!sh'
42+
let args += [substitute(shellescape(arg), '\\\n', '\n', 'g')]
43+
else
44+
let args += [shellescape(arg)]
45+
endif
46+
endfor
47+
return join(args, ' ')
48+
endfunction
49+
50+
51+
"----------------------------------------------------------------------
52+
" tempname
53+
"----------------------------------------------------------------------
54+
function! asyncrun#utils#tempname() abort
55+
let temp = tempname()
56+
if has('win32')
57+
return fnamemodify(fnamemodify(temp, ':h'), ':p').fnamemodify(temp, ':t')
58+
endif
59+
return temp
60+
endfunction
61+
62+
63+
"----------------------------------------------------------------------
64+
" isolate environ
65+
"----------------------------------------------------------------------
66+
function! asyncrun#utils#isolate(request, keep, ...) abort
67+
let keep = ['SHELL', 'HOME'] + a:keep
68+
let command = ['cd ' . shellescape(getcwd())]
69+
for line in split(system('env'), "\n")
70+
let var = matchstr(line, '^\w\+\ze=')
71+
if !empty(var) && var !~# '^\%(_\|SHLVL\|PWD\|VIM\|VIMRUNTIME\|MYG\=VIMRC\)$' && index(keep, var) < 0
72+
if &shell =~# 'csh'
73+
let command += split('setenv '.var.' '.shellescape(eval('$'.var)), "\n")
74+
else
75+
let command += split('export '.var.'='.asyncrun#utils#shellescape(eval('$'.var)), "\n")
76+
endif
77+
endif
78+
endfor
79+
for cmd in a:000
80+
if type(cmd) == type('')
81+
let command += [cmd]
82+
elseif type(cmd) == type(0)
83+
let command += [cmd]
84+
elseif type(cmd) == type([])
85+
let command += cmd
86+
endif
87+
endfor
88+
let temp = type(a:request) == type({}) ? a:request.file . '.script' : asyncrun#utils#tempname()
89+
call writefile(command, temp)
90+
return 'env -i ' . join(map(copy(keep), 'v:val."=". asyncrun#utils#shellescape(eval("$".v:val))." "'), '') . &shell . ' ' . temp
91+
endfunction
92+
93+
94+
"----------------------------------------------------------------------
95+
" set title
96+
"----------------------------------------------------------------------
97+
function! asyncrun#utils#set_title(title, expanded)
98+
return asyncrun#utils#shellescape('printf',
99+
\ '\033]1;%s\007\033]2;%s\007',
100+
\ a:title, a:expanded)
101+
endfunction
102+
103+

0 commit comments

Comments
 (0)