You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

77 lines
2.2 KiB
Python

#!/usr/bin/env python
# encoding: utf-8
"""Implements `echo hi` shell code interpolation."""
import os
import platform
from subprocess import Popen, PIPE
import stat
import tempfile
from UltiSnips.compatibility import as_unicode
from UltiSnips.text_objects._base import NoneditableTextObject
def _chomp(string):
"""Rather than rstrip(), remove only the last newline and preserve
purposeful whitespace."""
if len(string) and string[-1] == '\n':
string = string[:-1]
if len(string) and string[-1] == '\r':
string = string[:-1]
return string
def _run_shell_command(cmd, tmpdir):
"""Write the code to a temporary file."""
cmdsuf = ''
if platform.system() == 'Windows':
# suffix required to run command on windows
cmdsuf = '.bat'
# turn echo off
cmd = '@echo off\r\n' + cmd
handle, path = tempfile.mkstemp(text=True, dir=tmpdir, suffix=cmdsuf)
os.write(handle, cmd.encode('utf-8'))
os.close(handle)
os.chmod(path, stat.S_IRWXU)
# Execute the file and read stdout
proc = Popen(path, shell=True, stdout=PIPE, stderr=PIPE)
proc.wait()
stdout, _ = proc.communicate()
os.unlink(path)
return _chomp(as_unicode(stdout))
def _get_tmp():
"""Find an executable tmp directory."""
userdir = os.path.expanduser('~')
for testdir in [tempfile.gettempdir(), os.path.join(userdir, '.cache'),
os.path.join(userdir, '.tmp'), userdir]:
if (not os.path.exists(testdir) or
not _run_shell_command('echo success', testdir) == 'success'):
continue
return testdir
return ''
class ShellCode(NoneditableTextObject):
"""See module docstring."""
def __init__(self, parent, token):
NoneditableTextObject.__init__(self, parent, token)
self._code = token.code.replace('\\`', '`')
self._tmpdir = _get_tmp()
def _update(self, done):
if not self._tmpdir:
output = \
'Unable to find executable tmp directory, check noexec on /tmp'
else:
output = _run_shell_command(self._code, self._tmpdir)
self.overwrite(output)
self._parent._del_child(self) # pylint:disable=protected-access
return True