Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

lots of input related bugs fixed

  • Loading branch information...
commit dac28796a9df9acd30542c4a18a6393497bacbaa 1 parent b8e3870
@troydm authored
View
3  README.md
@@ -8,6 +8,7 @@ Introduction
------------
shellasync.vim plugin allows you to asynchronously execute shell commands inside vim
and see output inside a seperate window buffer without waiting for a command to finish.
+It also includes shell emulator so you can interactivly execute commands inside vim buffer.
It uses python's subprocess and threading capabilities to execute shell commands in seperate
thread and non-blockingly get the output as the command executes
@@ -17,7 +18,7 @@ Platform:
only unix based operating systems are supported
Requirements:
- vim compiled with python support
+ vim 7.3 with atleast 569 patchset included and compiled with python support
Screenshot
----------
View
188 autoload/shellasync.py
@@ -25,6 +25,7 @@ def __init__(self):
self.output = []
self.outputrem = False
self.outputremc = False
+ self.newdata = False
self.input = []
self.waitingForInput = False
self.remainder = ''
@@ -70,11 +71,14 @@ def run(self):
outread = ''
else:
plr = plr[0][1]
- if plr & select.POLLIN:
- outread = p.stdout.read()
- else:
+ try:
+ if plr & select.POLLIN or plr & select.POLLPRI:
+ outread = p.stdout.read()
+ else:
+ outread = ''
+ except IOError:
outread = ''
- canWrite = (type(plr) == list or (pl != None and plr & select.POLLOUT))
+ canWrite = type(plr) == list or (pl != None and (plr & select.POLLOUT) > 0)
else:
try:
outread = p.stdout.read()
@@ -84,12 +88,12 @@ def run(self):
if len(outread) == 0 and canWrite:
wr = self.getWrite()
if wr != None:
- p.stdin.flush()
- for w in wr:
- w = w+"\n"
- p.stdin.write(w)
- outread += w
- p.stdin.flush()
+ if wr == "\x04":
+ p.stdin.close()
+ else:
+ p.stdin.write(wr)
+ p.stdin.flush()
+ outread += wr
time.sleep(0.001)
if len(outread) == 0:
retval = p.poll()
@@ -117,8 +121,6 @@ def run(self):
out = out[-1]
self.extend(lines)
if len(out) > 0:
- while len(self.output) > 0:
- time.sleep(0.01)
self.extendrem(out)
self.lock.acquire()
self.waitingForInput = True
@@ -155,13 +157,14 @@ def getRemainder(self):
if self.waitingForInput:
ret = self.remainder
else:
- ret = None
+ ret = ''
self.lock.release()
return ret
def get(self):
r = None
self.lock.acquire()
+ self.newdata = False
if len(self.output) > 0:
r = (self.outputrem, self.outputremc, self.output)
self.outputremc = False
@@ -175,8 +178,15 @@ def hasdata(self):
self.lock.release()
return r
+ def hasnewdata(self):
+ self.lock.acquire()
+ r = self.newdata
+ self.lock.release()
+ return r
+
def extend(self,data):
self.lock.acquire()
+ self.newdata = True
if self.outputrem:
self.output = []
self.outputremc = self.outputrem or self.outputremc
@@ -219,6 +229,16 @@ def processCommand(self):
def write(self,data):
if data == None:
return
+ if type(data) == list:
+ for i in xrange(len(data)):
+ data[i] += "\n"
+ else:
+ data += "\n"
+ self.writenonl(data)
+
+ def writenonl(self,data):
+ if data == None:
+ return
self.lock.acquire()
if type(data) == list:
self.input.extend(data)
@@ -230,8 +250,7 @@ def getWrite(self):
out = None
self.lock.acquire()
if len(self.input) > 0:
- out = [self.input[0]]
- self.input = self.input[1:]
+ out = self.input.pop(0)
self.lock.release()
return out
@@ -297,7 +316,7 @@ def sendhistorydown(self):
self.sendhistoryind = -1
return ""
- def send(self):
+ def send(self,nl):
if self.pid != None:
command = vim.eval('send_input')
if len(command) > 0:
@@ -307,7 +326,7 @@ def send(self):
else:
self.sendhistory.append(command)
self.sendhistoryind = -1
- ShellAsyncSendCmd(self.pid)
+ ShellAsyncSendCmd(self.pid,nl)
def execute(self):
self.sendhistory = []
@@ -414,41 +433,83 @@ def commandFinished(self):
vim.command("call shellasync#RefreshShellTerminal()")
vim.command("startinsert")
+def ShellAsyncRefreshOutputFetch(output):
+ switchbackint = 0
+ while True:
+ out = output.get()
+ if out != None:
+ rem = out[0]
+ remc = out[1]
+ out = out[2]
+ if rem:
+ remc = remc and len(vim.current.buffer) != 1
+ if remc:
+ vim.current.buffer.append(out)
+ vim.command("normal! G0")
+ switchbackwnr = vim.eval("getbufvar('%','switchbackwnr')")
+ if switchbackwnr != None:
+ if switchbackint > 9:
+ if output.isWaitingForInput():
+ vim.command("unlet b:switchbackwnr")
+ vim.command(str(switchbackwnr)+"wincmd w")
+ break
+ else:
+ time.sleep(0.001)
+ switchbackint += 1
+ continue
+ if remc and int(vim.eval("&filetype == 'shellasyncterm'")) == 1:
+ vim.command("startinsert")
+ break
+ else:
+ if remc and len(vim.current.buffer) > 1:
+ if out[0].rstrip().find(vim.current.buffer[-1].rstrip()) == 0:
+ vim.current.buffer[-1] = None
+ if len(vim.current.buffer) == 1 and len(vim.current.buffer[0]) == 0:
+ vim.current.buffer[0] = out[0]
+ out = out[1:]
+ if len(out) > 0:
+ vim.current.buffer.append(out)
+ else:
+ vim.current.buffer.append(out)
+ vim.command("normal! G0")
+ time.sleep(0.001)
+ switchbackint = 0
+ else:
+ break
+
def ShellAsyncRefreshOutput():
global shellasync_cmds
pid = vim.eval("getbufvar('%','pid')")
if pid == None:
- return
+ return
pid = int(pid)
if pid in shellasync_cmds:
output = shellasync_cmds[pid]
if output != None:
- out = output.get()
- if out != None:
- rem = out[0]
- remc = out[1]
- out = out[2]
- if rem:
- if remc and len(vim.current.buffer) != 1:
- vim.current.buffer.append(out)
- vim.command("call setpos('.',["+str(vim.current.buffer.number)+","+str(len(vim.current.buffer))+",0,0])")
- switchbackwnr = vim.eval("getbufvar('%','switchbackwnr')")
- if switchbackwnr != None:
- if output.isWaitingForInput():
- vim.command("unlet b:switchbackwnr")
- vim.command(str(switchbackwnr)+"wincmd w")
- else:
- if remc and len(vim.current.buffer) > 1:
- vim.current.buffer[len(vim.current.buffer)-1] = None
- if len(vim.current.buffer) == 1 and len(vim.current.buffer[0]) == 0:
- vim.current.buffer[0] = out[0]
- out = out[1:]
- if len(out) > 0:
- vim.current.buffer.append(out)
- else:
- vim.current.buffer.append(out)
- vim.command("call setpos('.',["+str(vim.current.buffer.number)+","+str(len(vim.current.buffer))+",0,0])")
- vim.command("call feedkeys(\"f\e\")")
+ ShellAsyncRefreshOutputFetch(output)
+ vim.command("call feedkeys(\"f\e\",'n')")
+
+def ShellAsyncRefreshOutputI():
+ global shellasync_cmds
+ pid = vim.eval("getbufvar('%','pid')")
+ if pid == None:
+ return
+ pid = int(pid)
+ if pid in shellasync_cmds:
+ output = shellasync_cmds[pid]
+ if output != None:
+ if output.hasnewdata():
+ vim.command('stopinsert')
+ return
+ linelen = int(vim.eval("col('$')-1"))
+ if linelen > 0:
+ if int(vim.eval("col('.')")) == 1:
+ vim.command("call feedkeys(\"\<Right>\<Left>\",'n')")
+ else:
+ vim.command("call feedkeys(\"\<Left>\<Right>\",'n')")
+ else:
+ vim.command("call feedkeys(\"\ei\",'n')")
+
def ShellAsyncTerminalRefreshOutput():
global shellasync_cmds
@@ -477,6 +538,16 @@ def ShellAsyncTerminalRefreshOutput():
vim.command("call shellasync#RefreshShellTerminal()")
vim.command("call feedkeys(\"i\")")
+def ShellAsyncTerminalRefreshOutputI():
+ global shellasync_cmds
+ cfinished = vim.eval("getbufvar('%','cfinished')")
+ if cfinished == None:
+ return
+ cfinished = int(cfinished)
+ if cfinished == 1:
+ return
+ ShellAsyncRefreshOutputI()
+
def ShellAsyncExecuteCmd(clear,enviroment):
global shellasync_cmds
print_retval = vim.eval("g:shellasync_print_return_value") == '1'
@@ -498,12 +569,16 @@ def ShellAsyncExecuteCmd(clear,enviroment):
shellasync_cmds[pid] = out
return pid
+def ShellAsyncEchoMessage(message):
+ message = message.replace('"','\\"')
+ vim.command("echomsg \""+message+"\"")
+
def ShellAsyncTermCmd(pid):
global shellasync_cmds
if pid in shellasync_cmds:
out = shellasync_cmds[pid]
if not out.isAlive():
- vim.command("echomsg 'shell command "+out.processCommand()+" pid: "+str(pid)+" is finished'")
+ ShellAsyncEchoMessage("shell command "+out.processCommand()+" pid: "+str(pid)+" is finished")
return True
try:
os.killpg(pid,signal.SIGTERM)
@@ -511,10 +586,10 @@ def ShellAsyncTermCmd(pid):
os.kill(pid,signal.SIGTERM)
out.join(15.0)
if out.isAlive():
- vim.command("echomsg 'shell command "+out.processCommand()+" pid: "+str(pid)+" is still running'")
+ ShellAsyncEchoMessage("shell command "+out.processCommand()+" pid: "+str(pid)+" is still running")
return False
else:
- vim.command("echomsg 'shell command "+out.processCommand()+" pid: "+str(pid)+" terminated'")
+ ShellAsyncEchoMessage("shell command "+out.processCommand()+" pid: "+str(pid)+" terminated")
return True
def ShellAsyncKillCmd(pid):
@@ -522,7 +597,7 @@ def ShellAsyncKillCmd(pid):
if pid in shellasync_cmds:
out = shellasync_cmds[pid]
if not out.isAlive():
- vim.command("echomsg 'shell command "+out.processCommand()+" pid: "+str(pid)+" is finished'")
+ ShellAsyncEchoMessage("shell command "+out.processCommand()+" pid: "+str(pid)+" is finished")
return True
try:
os.killpg(pid,signal.SIGKILL)
@@ -530,10 +605,10 @@ def ShellAsyncKillCmd(pid):
os.kill(pid,signal.SIGKILL)
out.join(15.0)
if out.isAlive():
- vim.command("echomsg 'shell command "+out.processCommand()+" pid: "+str(pid)+" is still running'")
+ ShellAsyncEchoMessage("shell command "+out.processCommand()+" pid: "+str(pid)+" is still running")
return False
else:
- vim.command("echomsg 'shell command "+out.processCommand()+" pid: "+str(pid)+" killed'")
+ ShellAsyncEchoMessage("shell command "+out.processCommand()+" pid: "+str(pid)+" killed")
return True
def ShellAsyncDeleteCmd(pid):
@@ -542,10 +617,10 @@ def ShellAsyncDeleteCmd(pid):
out = shellasync_cmds[pid]
if not out.isAlive():
shellasync_cmds.pop(pid)
- vim.command("echomsg 'shell command "+out.processCommand()+" pid: "+str(pid)+" deleted'")
+ ShellAsyncEchoMessage("shell command "+out.processCommand()+" pid: "+str(pid)+" deleted")
return True
else:
- vim.command("echomsg 'shell command "+out.processCommand()+" pid: "+str(pid)+" is still running'")
+ ShellAsyncEchoMessage("shell command "+out.processCommand()+" pid: "+str(pid)+" is still running")
return False
return True
@@ -555,16 +630,19 @@ def ShellAsyncShellRunning(pid):
return shellasync_cmds[pid].isRunning()
return False
-def ShellAsyncSendCmd(pid):
+def ShellAsyncSendCmd(pid,nl):
global shellasync_cmds
input = vim.eval('send_input')
if pid in shellasync_cmds:
out = shellasync_cmds[pid]
if not out.isAlive():
- vim.command("echomsg 'shell command "+out.processCommand()+" pid: "+str(pid)+" is finished'")
+ ShellAsyncEchoMessage("shell command "+out.processCommand()+" pid: "+str(pid)+" is finished")
return False
else:
- out.write(input)
+ if nl:
+ out.write(input)
+ else:
+ out.writenonl(input)
return True
return False
View
73 autoload/shellasync.vim
@@ -13,9 +13,7 @@ let s:save_cpo = &cpo
set cpo&vim
" check if already loaded {{{
-if !exists('g:shellasync_loaded')
- let g:shellasync_loaded = 1
-elseif g:shellasync_loaded
+if exists('g:shellasync_loaded') && g:shellasync_loaded
let &cpo = s:save_cpo
unlet s:save_cpo
finish
@@ -35,6 +33,8 @@ import shellasync
EOF
" }}}
+" functions {{{
+" utility functions {{{
function! s:ShellWindow(pid)
for wnr in range(1,winnr('$'))
let bnr = winbufnr(wnr)
@@ -47,9 +47,18 @@ function! s:ShellWindow(pid)
endfor
return -1
endfunction
+
function! s:ExpandCommand(command)
return join(map(split(a:command), 'expand(v:val)'))
endfunction
+
+function! s:ShellPidCompletion(ArgLead, CmdLine, CursorPos)
+ let pidlist = pyeval("filter(shellasync.ShellAsyncShellRunning,shellasync.shellasync_cmds.keys())")
+ return map(filter(pidlist,"v:val =~ \'^".a:ArgLead."\'"),"\'\'.v:val")
+endfunction
+" }}}
+
+" shell functions {{{
function! s:TermShellInBuf(del)
let pid = getbufvar('%','pid')
if pid != ''
@@ -59,6 +68,7 @@ function! s:TermShellInBuf(del)
endif
endif
endfunction
+
function! s:SelectShell(pidlist)
if len(a:pidlist) > 0
let pid = a:pidlist[0]
@@ -83,14 +93,15 @@ function! s:SelectShell(pidlist)
echo 'please select shell'
endif
endfunction
-function! s:SendShellInput(pid,input_data)
+
+function! s:SendShellInput(pid,input_data,nl)
let send_input = a:input_data
if len(send_input) == 0
let send_input = ''
endif
redraw!
if pyeval('shellasync.ShellAsyncShellRunning('.a:pid.')')
- exe 'python shellasync.ShellAsyncSendCmd('.a:pid.')'
+ exe 'python shellasync.ShellAsyncSendCmd('.a:pid.','.a:nl.')'
let wnr = s:ShellWindow(a:pid)
if wnr != -1 && winnr() != wnr
let bnr = winbufnr(wnr)
@@ -102,6 +113,7 @@ function! s:SendShellInput(pid,input_data)
echo 'shell '.a:pid.' is not running'
endif
endfunction
+
function! s:CmdShell(cmd, pidlist)
if len(a:pidlist) == 0
echo 'please specify a pid or associate a pid with current buffer using ShellSelect'
@@ -111,15 +123,25 @@ function! s:CmdShell(cmd, pidlist)
endfor
endif
endfunction
+" }}}
+
+" terminal functions {{{
function! s:GetTerminalPrompt()
let cfinished = getbufvar('%','cfinished')
if cfinished == 0
let pid = getbufvar('%','pid')
- return pyeval('shellasync.shellasync_cmds['.pid.'].getRemainder()').' '
+ let prompt = pyeval('shellasync.shellasync_cmds['.pid.'].getRemainder()').' '
+ if len(prompt) > 0
+ let b:prompt = prompt
+ else
+ let prompt = b:prompt
+ endif
+ return prompt
else
return eval(g:shellasync_terminal_prompt).' '
endif
endfunction
+
function! s:TerminalStartInsert()
let cfinished = getbufvar('%','cfinished')
let pos = getpos('.')
@@ -137,6 +159,7 @@ function! s:TerminalStartInsert()
endif
call setpos('.',pos)
endfunction
+
function! s:TerminalGetCommand()
let cfinished = getbufvar('%','cfinished')
if cfinished == 0
@@ -149,6 +172,7 @@ function! s:TerminalGetCommand()
let ln = ln[len(prompt)-1: len(ln)-2]
return ln
endfunction
+
function! s:TerminalSetCommand(command)
let cfinished = getbufvar('%','cfinished')
if cfinished == 0
@@ -160,6 +184,16 @@ function! s:TerminalSetCommand(command)
call setline(pl,prompt.a:command.' ')
normal! $
endfunction
+
+function! s:TerminalCtrlDPressed()
+ let cfinished = getbufvar('%','cfinished')
+ if cfinished == 0
+ call s:SendShellInput(b:pid,"\x04",0)
+ else
+ bd!
+ endif
+endfunction
+
function! s:TerminalBackspacePressed()
let prompt = s:GetTerminalPrompt()
if col('.') >= len(prompt)
@@ -169,6 +203,7 @@ function! s:TerminalBackspacePressed()
endif
startinsert
endfunction
+
function! s:TerminalLeftPressed()
let prompt = s:GetTerminalPrompt()
if col('.') < len(prompt)
@@ -176,6 +211,7 @@ function! s:TerminalLeftPressed()
endif
startinsert
endfunction
+
function! s:TerminalRightPressed()
normal! l
if col('.') != (col('$')-1)
@@ -183,6 +219,7 @@ function! s:TerminalRightPressed()
endif
startinsert
endfunction
+
function! s:TerminalUpPressed()
let cfinished = getbufvar('%','cfinished')
let termnr = getbufvar('%','termnr')
@@ -194,6 +231,7 @@ function! s:TerminalUpPressed()
call s:TerminalSetCommand(command)
startinsert
endfunction
+
function! s:TerminalDownPressed()
let cfinished = getbufvar('%','cfinished')
let termnr = getbufvar('%','termnr')
@@ -205,6 +243,7 @@ function! s:TerminalDownPressed()
call s:TerminalSetCommand(command)
startinsert
endfunction
+
function! s:TerminalEnterPressed()
let cfinished = getbufvar('%','cfinished')
let command = s:TerminalGetCommand()
@@ -214,7 +253,7 @@ function! s:TerminalEnterPressed()
if len(send_input) == 0
let send_input = ''
endif
- exe 'python shellasync.shellasync_terms['.termnr.'].send()'
+ exe 'python shellasync.shellasync_terms['.termnr.'].send(1)'
return
endif
if empty(command)
@@ -236,22 +275,18 @@ function! s:TerminalEnterPressed()
python shellasync.ShellAsyncTerminalRefreshOutput()
endif
endfunction
+
function! s:TerminalBufEnter()
if g:shellasync_terminal_insert_on_enter
- if b:cfinished
- startinsert
- else
- normal! G
- endif
+ normal! G0
+ startinsert
endif
endfunction
+
function! s:CloseShellTerminal()
call s:TermShellInBuf(1)
endfunction
-function! s:ShellPidCompletion(ArgLead, CmdLine, CursorPos)
- let pidlist = pyeval("filter(shellasync.ShellAsyncShellRunning,shellasync.shellasync_cmds.keys())")
- return map(filter(pidlist,"v:val =~ \'^".a:ArgLead."\'"),"\'\'.v:val")
-endfunction
+" }}}
" global functions {{{
function! shellasync#ExecuteInShell(bang, samewin, command)
@@ -286,6 +321,7 @@ function! shellasync#ExecuteInShell(bang, samewin, command)
exe 'au BufEnter <buffer> set updatetime='.g:shellasync_update_interval
au BufLeave <buffer> let &updatetime=getbufvar('%','prevupdatetime')
au CursorHold <buffer> python shellasync.ShellAsyncRefreshOutput()
+ au CursorHoldI <buffer> python shellasync.ShellAsyncRefreshOutputI()
nnoremap <buffer> <C-c> :echo '' \| call <SID>TermShellInBuf(0)<CR>
python shellasync.ShellAsyncExecuteCmd(True,os.environ)
else
@@ -340,7 +376,7 @@ function! shellasync#SendShell(c,l1,l2,pidlist)
let send_input = input('send input: ')
echo ''
endif
- call s:SendShellInput(a:pidlist[0],send_input)
+ call s:SendShellInput(a:pidlist[0],send_input,1)
endfunction
function! shellasync#ShellSelect(...)
@@ -427,12 +463,14 @@ function! shellasync#OpenShellTerminal()
au BufLeave <buffer> let &updatetime=getbufvar('%','prevupdatetime') | stopinsert
au InsertEnter <buffer> call <SID>TerminalStartInsert()
au CursorHold <buffer> python shellasync.ShellAsyncTerminalRefreshOutput()
+ au CursorHoldI <buffer> python shellasync.ShellAsyncTerminalRefreshOutputI()
inoremap <silent> <buffer> <Enter> <ESC>:call <SID>TerminalEnterPressed() \| normal! 0<CR>
inoremap <silent> <buffer> <Left> <ESC>:call <SID>TerminalLeftPressed()<CR>
inoremap <silent> <buffer> <Right> <ESC>:call <SID>TerminalRightPressed()<CR>
inoremap <silent> <buffer> <Up> <ESC>:call <SID>TerminalUpPressed()<CR>
inoremap <silent> <buffer> <Down> <ESC>:call <SID>TerminalDownPressed()<CR>
inoremap <silent> <buffer> <BS> <ESC>:call <SID>TerminalBackspacePressed()<CR>
+ inoremap <silent> <buffer> <C-d> <ESC>:call <SID>TerminalCtrlDPressed()<CR>
nnoremap <silent> <buffer> <C-c> :call shellasync#TermShell([getbufvar('%','pid')])<CR>
nnoremap <silent> <buffer> t :call shellasync#TermShell([getbufvar('%','pid')])<CR>
nnoremap <silent> <buffer> K :call shellasync#KillShell([getbufvar('%','pid')])<CR>
@@ -449,6 +487,7 @@ function! shellasync#RefreshShellTerminal()
redraw!
endfunction
" }}}
+" }}}
let &cpo = s:save_cpo
unlet s:save_cpo
View
7 doc/shellasync.txt
@@ -19,6 +19,7 @@ Help on using shellasync.vim *shellasync.vi
shellasync.vim plugin allows you to asynchronously execute shell commands inside vim
and see output inside a seperate window buffer without waiting for a command to finish.
+It also includes shell emulator so you can interactivly execute commands inside vim buffer.
It uses python's subprocess and threading capabilities to execute shell commands in seperate
thread and non-blockingly get the output as the command executes
@@ -28,11 +29,15 @@ Platform:
only unix based operating systems are supported
Requirements:
- vim compiled with python support
+ vim 7.3 with atleast 569 patchset included and compiled with python support
============================================================================================
2. CONFIGURATION *shellasync.vim-configuration*
+ *g:shellasync_suppress_load_warning*
+g:shellasync_suppress_load_warning (Default: '0')
+ Suppress loading warnings messages
+
*g:shellasync_print_return_value*
g:shellasync_print_return_value (Default: '0')
Print command's return value when command finishes it's execution
View
29 plugin/shellasync.vim
@@ -12,9 +12,36 @@
let s:save_cpo = &cpo
set cpo&vim
+" check if running on windows {{{
+if has("win32") || has("win64")
+ let &cpo = s:save_cpo
+ unlet s:save_cpo
+ if exists("g:shellasync_suppress_load_warning") && g:shellasync_suppress_load_warning
+ finish
+ endif
+ echo "shellasync won't work windows operating system"
+ finish
+endif
+" }}}
+
" check python support {{{
if !has("python")
- echo "ShellAsync needs vim compiled with +python option"
+ let &cpo = s:save_cpo
+ unlet s:save_cpo
+ if exists("g:shellasync_suppress_load_warning") && g:shellasync_suppress_load_warning
+ finish
+ endif
+ echo "shellasync needs vim compiled with +python option"
+ finish
+endif
+
+if !exists('*pyeval')
+ let &cpo = s:save_cpo
+ unlet s:save_cpo
+ if exists("g:shellasync_suppress_load_warning") && g:shellasync_suppress_load_warning
+ finish
+ endif
+ echo "shellasync needs vim 7.3 with atleast 569 patchset included"
finish
endif
" }}}
Please sign in to comment.
Something went wrong with that request. Please try again.