This repository has been archived by the owner on Feb 1, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 23
/
sublautopep8.py
297 lines (235 loc) · 9.24 KB
/
sublautopep8.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# coding=utf-8
"""SublimeAutoPEP8 plugin."""
import glob
import logging
import os
import sys
import sublime
import sublime_plugin
from AutoPEP8.sublimeautopep8lib import autopep8
from AutoPEP8.sublimeautopep8lib import common
VERSION = '2.3.0'
logger = logging.getLogger('SublimeAutoPEP8')
AUTOPEP8_OPTIONS = (
'global-config',
'ignore-local-config',
'ignore',
'select',
'max-line-length',
'indent-size',
'exclude',
'hang-closing',
)
def get_user_settings():
"""Return user settings related to the plugin."""
return sublime.load_settings(common.USER_CONFIG_NAME)
def _setup_logger():
"""Setup logging for the plugin."""
user_settings = get_user_settings()
if not user_settings.get('debug', False):
return
logger = logging.getLogger('SublimeAutoPEP8')
logger.handlers = []
# Set level.
logger.setLevel(logging.DEBUG)
# Init handler.
if user_settings.get('logfile', ''):
handler = logging.FileHandler(user_settings.get('logfile', ''),
encoding='utf8')
logger.propagate = False
else:
logger.propagate = True
handler = logging.StreamHandler(sys.stdout)
# Set formatter.
handler.setFormatter(
logging.Formatter(
'%(asctime)s - %(name)s:%(lineno)d - %(levelname)s - %(message)s'))
logger.addHandler(handler)
def _print_debug_info():
"""Print debug info into the sublime console."""
user_settings = get_user_settings()
message = (
'\n'
'AutoPEP8:'
'\n\tsublime:'
'\n\t version=%(subl_version)s'
'\n\t platform=%(subl_platform)s'
'\n\t arch=%(subl_arch)s'
'\n\t packages_path=%(subl_packages)s'
'\n\t installed_packages_path=%(subl_installed_packages)s'
'\n\tplugin:'
'\n\t version=%(plugin_version)s'
'\n\t config: %(config)s'
'\n'
)
plugin_keys = (
'format_on_save',
'syntax_list',
'file_menu_search_depth',
'avoid_new_line_in_select_mode',
'debug',
'logfile',
)
config = {
key: user_settings.get(key, None)
for key in AUTOPEP8_OPTIONS + plugin_keys
}
message_values = {
'plugin_version': VERSION,
'subl_version': sublime.version(),
'subl_platform': sublime.platform(),
'subl_arch': sublime.arch(),
'subl_packages': sublime.packages_path(),
'subl_installed_packages': sublime.installed_packages_path(),
'config': config
}
logger.info(message, message_values)
print_message = any([
user_settings.get('logfile', ''),
not user_settings.get('debug', False)
])
if print_message:
# Print config information to the console even if there is a logfile.
print(message % message_values)
def pep8_params():
"""Return params for the autopep8 module."""
user_settings = get_user_settings()
env_vars = sublime.active_window().extract_variables()
params = ['-d'] # args for preview
# read settings
for opt in AUTOPEP8_OPTIONS:
opt_value = user_settings.get(opt, '')
if opt_value == '' or opt_value is None:
continue
if opt_value and opt in ('exclude', 'global-config'):
opt_value = sublime.expand_variables(opt_value, env_vars)
if opt in ('exclude', 'global-config'):
if opt_value:
opt_value = sublime.expand_variables(opt_value, env_vars)
params.append('--{0}={1}'.format(opt, opt_value))
elif opt in ('ignore', 'select'):
# remove white spaces as autopep8 does not trim them
opt_value = ','.join(param.strip()
for param in opt_value.split(','))
params.append('--{0}={1}'.format(opt, opt_value))
elif opt in ('ignore-local-config', 'hang-closing'):
if opt_value:
params.append('--{0}'.format(opt))
else:
params.append('--{0}={1}'.format(opt, opt_value))
# use verbose==2 to catch non-fixed issues
params.extend(['--verbose'] * 2)
# autopep8.parse_args required at least one positional argument,
# fake-file parent folder is used as location for local configs.
params.append(sublime.expand_variables('${folder}/fake-file', env_vars))
logger.info('pep8_params: %s', params)
args = autopep8.parse_args(params, apply_config=True)
return args
class AutoPep8Command(sublime_plugin.TextCommand):
def get_selection(self, skip_selected):
region = self.view.sel()[0]
# select all view if there is no selected region.
if region.a == region.b or skip_selected:
region = sublime.Region(0, self.view.size())
return region, self.view.substr(region), self.view.encoding()
def run(self, edit, preview=True, skip_selected=False):
queue = common.Queue()
region, source, encoding = self.get_selection(skip_selected)
if not isinstance(source, str) and hasattr('decode'):
source = source.decode(encoding)
queue.put((source, self.view.file_name(), self.view, region, encoding))
sublime.set_timeout_async(
lambda: common.worker(queue, preview, pep8_params()),
common.WORKER_START_TIMEOUT)
def is_enabled(self, *args):
view_syntax = self.view.settings().get('syntax')
syntax_list = get_user_settings().get('syntax_list', ["Python"])
filename = os.path.basename(view_syntax)
return os.path.splitext(filename)[0] in syntax_list
def is_visible(self, *args):
return True
class AutoPep8OutputCommand(sublime_plugin.TextCommand):
def run(self, edit, text):
self.view.insert(edit, 0, text)
self.view.end_edit(edit)
def is_visible(self, *args):
return False
class AutoPep8ReplaceCommand(sublime_plugin.TextCommand):
def run(self, edit, text, a, b):
user_settings = get_user_settings()
region = sublime.Region(int(a), int(b))
remove_last_line = user_settings.get('avoid_new_line_in_select_mode',
False)
if region.b - region.a < self.view.size() and remove_last_line:
lines = text.split('\n')
if not lines[-1]:
text = '\n'.join(lines[:-1])
self.view.replace(edit, region, text)
def is_visible(self, *args):
return False
class AutoPep8FileCommand(sublime_plugin.WindowCommand):
def run(self, paths=None, preview=True):
if not paths:
return
queue = common.Queue()
for path in self.files(paths, pep8_params().exclude):
with open(path, 'r') as fd:
source = fd.read()
encoding = common.get_pyencoding(source)
if not isinstance(source, str) and hasattr(source, 'decode'):
source = source.decode(encoding)
queue.put((source, path, None, None, encoding))
sublime.set_timeout_async(
lambda: common.worker(queue, preview, pep8_params()),
common.WORKER_START_TIMEOUT)
def files(self, paths, exclude=None):
for path in autopep8.find_files(paths, recursive=True, exclude=exclude):
if path.endswith('.py'):
yield path
def has_pyfiles(self, path, depth):
for step in range(depth):
depth_path = '*/' * step + '*.py'
search_path = os.path.join(path, depth_path)
try:
next(glob.iglob(search_path))
return True
except StopIteration:
pass
return False
def check_paths(self, paths):
if not paths:
return False
depth = get_user_settings().get('file_menu_search_depth',
common.DEFAULT_SEARCH_DEPTH)
for path in paths:
if os.path.isdir(path) and self.has_pyfiles(path, depth):
return True
elif os.path.isfile(path) and path.endswith('.py'):
return True
return False
def is_enabled(self, *args, **kwd):
return self.check_paths(kwd.get('paths'))
def is_visible(self, *args, **kwd):
return True
class AutoPep8Listener(sublime_plugin.EventListener):
def on_pre_save_async(self, view):
user_settings = get_user_settings()
skip_format = view.settings().get(common.VIEW_SKIP_FORMAT, False)
if not user_settings.get('format_on_save', False) or skip_format:
view.settings().erase(common.VIEW_SKIP_FORMAT)
view.settings().erase(common.VIEW_AUTOSAVE)
return
view_syntax = view.settings().get('syntax')
syntax_list = user_settings.get('syntax_list', ['Python'])
if os.path.splitext(os.path.basename(view_syntax))[0] in syntax_list:
view.settings().set(common.VIEW_AUTOSAVE, True)
view.run_command('auto_pep8',
{'preview': False, 'skip_selected': True})
def on_ready():
"""Run code once plugin is loaded."""
_setup_logger()
_print_debug_info()
# Timeout is required for ST3
# as plugin_host loading asynchronously
# and it is not possible to use sublime API at import time.
sublime.set_timeout_async(on_ready, 0)