在外部shell中运行可视选择,将输出发送到新窗口

4

目前我主要使用Vim来编写Python代码,最近我发现了两种非常好的策略,可以在外部运行代码并将其带回到Vim中。第一种方法是使用一个名为Shell的函数,这个函数可以在Vim页面找到:

function! s:ExecuteInShell(command)
  let command = join(map(split(a:command), 'expand(v:val)'))
  let winnr = bufwinnr('^' . command . '$')
  silent! execute  winnr < 0 ? 'botright new ' . fnameescape(command) : winnr . 'wincmd w'
  setlocal buftype=nowrite bufhidden=wipe nobuflisted noswapfile nowrap number
  echo 'Execute ' . command . '...'
  silent! execute 'silent %!'. command
  silent! execute 'resize ' . line('$')
  silent! redraw
  silent! execute 'au BufUnload <buffer> execute bufwinnr(' . bufnr('#') . ') . ''wincmd w'''
  silent! execute 'nnoremap <silent> <buffer> <LocalLeader>r :call <SID>ExecuteInShell(''' . command . ''')<CR>'
  echo 'Shell command ' . command . ' executed.'
endfunction
command! -complete=shellcmd -nargs=+ Shell call s:ExecuteInShell(<q-args>)

这个功能可以让你像这样运行:Shell nosetests,并在一个新窗口中查看结果:

  1. 持久化(不需要“按回车键”使其消失)
  2. 使用临时缓冲区(而不是临时文件)
  3. 最重要的是,再次运行命令只需刷新当前窗口,而不是每次都打开新窗口。

然后我还使用这个小工具

:'<,'>:w !python

我可以使用当前缓冲区的选择,但按下回车后就会消失。

我无法想出如何将两者结合起来。我想要的是:

  1. Shell命令的所有窗口属性,但是
  2. 能够从屏幕上的选择运行它。
  3. 编辑:对于选定的Python代码执行此操作,而不是Bash代码。该函数已经执行常规shell命令。我想让它直接运行代码,就像:'<,'>:w !python一样。

我不了解足够的Vimscript来修改Shell以包括选择,并且我无法弄清楚如何将:'<,'>:w !python至少放入自己的窗口中,而不使用临时文件,这对我来说似乎是不必要的。有什么想法?提示?


我对 VimScript 的了解还不够深入,无法修复这个问题。但是由于此代码已经在临时缓冲区中使用了“%!”,如果您能先将所选内容复制到临时缓冲区中,它可能会起作用。也许用户定义的命令可以接受范围?祝你好运 :) - Eevee
2个回答

2
你可以让函数接受范围并测试是否传递了范围,方法如下:
function! s:ExecuteInShell(command) range
  let lines = []
  if (a:firstline != a:lastline)
    let lines=getline(a:firstline, a:lastline)
  endif
  let command = join(map(split(a:command), 'expand(v:val)'))
  let winnr = bufwinnr('^' . command . '$')
  silent! execute  winnr < 0 ? 'botright new ' . fnameescape(command) : winnr . 'wincmd w'
  setlocal buftype=nowrite bufhidden=wipe nobuflisted noswapfile nowrap number
  echo 'Execute ' . command . '...'
  if (len(lines))
    silent! call append(line('.'), lines)
    silent! 1d
    silent! redir => results
    silent! execute '1,$w !' . command
    silent! redir end
    silent! %d
    let lines = split(results, '\r')
    for line in lines[:1]
        call append(line('$'), line[1:])
    endfor
    silent! 1d
  else
    silent! execute 'silent %!'. command
  endif
  silent! execute 'resize ' . line('$')
  silent! redraw
  silent! execute 'au BufUnload <buffer> execute bufwinnr(' . bufnr('#') . ') . ''wincmd w'''
  silent! execute 'nnoremap <silent> <buffer> <LocalLeader>r :call <SID>ExecuteInShell(''' . command . ''')<CR>'
  echo 'Shell command ' . command . ' executed.'
endfunction
command! -range -complete=shellcmd -nargs=+ Shell <line1>,<line2>call s:ExecuteInShell(<q-args>)

用于命令行:

:Shell echo 'no range supplied, but a command (echo) is.'

在选择行的情况下使用(不需要键入“'<,'>”部分,因为按下“:”键命令会自动添加该部分,命令将解释所选择的行):

:'<,'>Shell python

使用原封不动的代码,新窗口弹出但没有运行任何内容。我在一个空白缓冲区中输入了 ls -hal,然后我将这一行进行可视化选择并像这样运行它::Shell '<,'>。我使用的方式有问题吗? - brianclements
两件事情。首先,这个脚本需要传入多于一行的参数,因为我发现唯一确定是否选择了范围的方法是a:firstline与a:lastline不同。其次,这个脚本需要传入一个要使用的命令(例如python)。你可以尝试编辑它,以使用缺少传递命令的方式来知道这些行是要运行的东西。 - cforbish

0
这是一种利用缺少命令来处理一系列行的版本:
function! s:ExecuteInShell(command) range
  let lines = []
  if (!len(a:command))
    let lines=getline(a:firstline, a:lastline)
  endif
  let command = join(map(split(a:command), 'expand(v:val)'))
  let winnr = bufwinnr('^' . command . '$')
  silent! execute  winnr < 0 ? 'botright new ' . fnameescape(command) : winnr . 'wincmd w'
  setlocal buftype=nowrite bufhidden=wipe nobuflisted noswapfile nowrap number
  echo 'Execute ' . command . '...'
  if (len(lines))
    for line in lines
        silent! execute 'silent r! '. line
    endfor
    silent! 1d
  else
    silent! execute 'silent %!'. command
  endif
  silent! execute 'resize ' . line('$')
  silent! redraw
  silent! execute 'au BufUnload <buffer> execute bufwinnr(' . bufnr('#') . ') . ''wincmd w'''
  silent! execute 'nnoremap <silent> <buffer> <LocalLeader>r :call <SID>ExecuteInShell(''' . command . ''')<CR>'
  echo 'Shell command ' . command . ' executed.'
endfunction
command! -range -complete=shellcmd -nargs=* Shell <line1>,<line2>call s:ExecuteInShell(<q-args>)

使用命令行:

:Shell echo 'no range supplied, but a command (echo) is.'

若选中了多行,则可使用 while(不必输入“'<,'>”部分,因为按下“:”时将会自动填入该部分,且无需提供命令,因为所选行已经提供了命令):

:'<,'>Shell

太好了,谢谢!但是问题的另一半是如何运行Python代码。我在最初的问题中没有表述清楚,所以现在我来更新一下。我自己尝试了一些东西,认为这很简单,但是只是在 ! 前/后添加 python 并不像我想象的那样有效。您认为您能做出这个补充吗,@cforbish? - brianclements
这是我在原回答中理解的内容。我已经更新了原回答,并详细说明如何使用。 - cforbish
很遗憾,尝试在此答案中使用脚本:'<,'>Shell python会返回一个空白窗口。 - brianclements

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接