-
Notifications
You must be signed in to change notification settings - Fork 51
/
editor.py
executable file
·148 lines (107 loc) · 3.53 KB
/
editor.py
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
#!/usr/bin/env python
"""Tools for invoking editors programmatically."""
from __future__ import print_function
import sys
import locale
import os.path
import subprocess
import tempfile
__all__ = [
'edit',
'get_editor',
'EditorError',
]
__version__ = '1.0.5'
class EditorError(RuntimeError):
pass
def get_default_editors():
# TODO: Make platform-specific
return [
'editor',
'vim',
'emacs',
'nano',
]
def get_editor_args(editor):
if editor in ['vim', 'gvim', 'vim.basic', 'vim.tiny']:
return ['-f', '-o']
elif editor == 'emacs':
return ['-nw']
elif editor == 'gedit':
return ['-w', '--new-window']
elif editor == 'nano':
return ['-R']
elif editor == 'code':
return ["-w", "-n"]
else:
return []
def get_editor():
# The import from distutils needs to be here, at this low level to
# prevent import of 'editor' itself from breaking inquirer. This
# has to do with ubuntu (debian) python packages artificially
# separated from distutils.
#
# If this import is at top level inquirer breaks on ubuntu until
# the user explicitly apt-get install python3-distutils. With the
# import here it will only break if the code is utilizing the
# inquirer editor prompt.
try:
from distutils.spawn import find_executable
except ImportError:
from shutil import which as find_executable
# Get the editor from the environment. Prefer VISUAL to EDITOR
editor = os.environ.get('VISUAL') or os.environ.get('EDITOR')
if editor:
return editor
# None found in the environment. Fallback to platform-specific defaults.
for ed in get_default_editors():
path = find_executable(ed)
if path is not None:
return path
raise EditorError("Unable to find a viable editor on this system."
"Please consider setting your $EDITOR variable")
def get_tty_filename():
if sys.platform == 'win32':
return 'CON:'
return '/dev/tty'
def edit(filename=None, contents=None, use_tty=None, suffix=''):
editor = get_editor()
args = [editor] + get_editor_args(os.path.basename(os.path.realpath(editor)))
if use_tty is None:
use_tty = sys.stdin.isatty() and not sys.stdout.isatty()
if filename is None:
tmp = tempfile.NamedTemporaryFile(suffix=suffix)
filename = tmp.name
if contents is not None:
# For python3 only. If str is passed instead of bytes, encode default
if hasattr(contents, 'encode'):
contents = contents.encode()
with open(filename, mode='wb') as f:
f.write(contents)
args += [filename]
stdout = None
if use_tty:
stdout = open(get_tty_filename(), 'wb')
proc = subprocess.Popen(args, close_fds=True, stdout=stdout)
proc.communicate()
with open(filename, mode='rb') as f:
return f.read()
def _get_editor(ns):
print(get_editor())
def _edit(ns):
contents = ns.contents
if contents is not None:
contents = contents.encode(locale.getpreferredencoding())
print(edit(filename=ns.path, contents=contents))
if __name__ == '__main__':
import argparse
ap = argparse.ArgumentParser()
sp = ap.add_subparsers()
cmd = sp.add_parser('get-editor')
cmd.set_defaults(cmd=_get_editor)
cmd = sp.add_parser('edit')
cmd.set_defaults(cmd=_edit)
cmd.add_argument('path', type=str, nargs='?')
cmd.add_argument('--contents', type=str)
ns = ap.parse_args()
ns.cmd(ns)