Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/emacs-for-python/rope-dist/rope/contrib/fixsyntax.py
1415 views
1
import rope.base.codeanalyze
2
import rope.base.evaluate
3
from rope.base import worder, exceptions, utils
4
from rope.base.codeanalyze import ArrayLinesAdapter, LogicalLineFinder
5
6
7
class FixSyntax(object):
8
9
def __init__(self, pycore, code, resource, maxfixes=1):
10
self.pycore = pycore
11
self.code = code
12
self.resource = resource
13
self.maxfixes = maxfixes
14
15
@utils.saveit
16
def get_pymodule(self):
17
"""Get a `PyModule`"""
18
errors = []
19
code = self.code
20
tries = 0
21
while True:
22
try:
23
if tries == 0 and self.resource is not None and \
24
self.resource.read() == code:
25
return self.pycore.resource_to_pyobject(self.resource,
26
force_errors=True)
27
return self.pycore.get_string_module(
28
code, resource=self.resource, force_errors=True)
29
except exceptions.ModuleSyntaxError, e:
30
if tries < self.maxfixes:
31
tries += 1
32
self.commenter.comment(e.lineno)
33
code = '\n'.join(self.commenter.lines)
34
errors.append(' * line %s: %s ... fixed' % (e.lineno,
35
e.message_))
36
else:
37
errors.append(' * line %s: %s ... raised!' % (e.lineno,
38
e.message_))
39
new_message = ('\nSyntax errors in file %s:\n' % e.filename) \
40
+ '\n'.join(errors)
41
raise exceptions.ModuleSyntaxError(e.filename, e.lineno,
42
new_message)
43
44
@property
45
@utils.saveit
46
def commenter(self):
47
return _Commenter(self.code)
48
49
def pyname_at(self, offset):
50
pymodule = self.get_pymodule()
51
def old_pyname():
52
word_finder = worder.Worder(self.code, True)
53
expression = word_finder.get_primary_at(offset)
54
expression = expression.replace('\\\n', ' ').replace('\n', ' ')
55
lineno = self.code.count('\n', 0, offset)
56
scope = pymodule.get_scope().get_inner_scope_for_line(lineno)
57
return rope.base.evaluate.eval_str(scope, expression)
58
new_code = pymodule.source_code
59
def new_pyname():
60
newoffset = self.commenter.transfered_offset(offset)
61
return rope.base.evaluate.eval_location(pymodule, newoffset)
62
if new_code.startswith(self.code[:offset + 1]):
63
return new_pyname()
64
result = old_pyname()
65
if result is None:
66
return new_pyname()
67
return result
68
69
70
class _Commenter(object):
71
72
def __init__(self, code):
73
self.code = code
74
self.lines = self.code.split('\n')
75
self.lines.append('\n')
76
self.origs = range(len(self.lines) + 1)
77
self.diffs = [0] * (len(self.lines) + 1)
78
79
def comment(self, lineno):
80
start = _logical_start(self.lines, lineno, check_prev=True) - 1
81
# using self._get_stmt_end() instead of self._get_block_end()
82
# to lower commented lines
83
end = self._get_stmt_end(start)
84
indents = _get_line_indents(self.lines[start])
85
if 0 < start:
86
last_lineno = self._last_non_blank(start - 1)
87
last_line = self.lines[last_lineno]
88
if last_line.rstrip().endswith(':'):
89
indents = _get_line_indents(last_line) + 4
90
self._set(start, ' ' * indents + 'pass')
91
for line in range(start + 1, end + 1):
92
self._set(line, self.lines[start])
93
self._fix_incomplete_try_blocks(lineno, indents)
94
95
def transfered_offset(self, offset):
96
lineno = self.code.count('\n', 0, offset)
97
diff = sum(self.diffs[:lineno])
98
return offset + diff
99
100
def _last_non_blank(self, start):
101
while start > 0 and self.lines[start].strip() == '':
102
start -= 1
103
return start
104
105
def _get_block_end(self, lineno):
106
end_line = lineno
107
base_indents = _get_line_indents(self.lines[lineno])
108
for i in range(lineno + 1, len(self.lines)):
109
if _get_line_indents(self.lines[i]) >= base_indents:
110
end_line = i
111
else:
112
break
113
return end_line
114
115
def _get_stmt_end(self, lineno):
116
end_line = lineno
117
base_indents = _get_line_indents(self.lines[lineno])
118
for i in range(lineno + 1, len(self.lines)):
119
if _get_line_indents(self.lines[i]) <= base_indents:
120
return i - 1
121
return lineno
122
123
def _fix_incomplete_try_blocks(self, lineno, indents):
124
block_start = lineno
125
last_indents = current_indents = indents
126
while block_start > 0:
127
block_start = rope.base.codeanalyze.get_block_start(
128
ArrayLinesAdapter(self.lines), block_start) - 1
129
if self.lines[block_start].strip().startswith('try:'):
130
indents = _get_line_indents(self.lines[block_start])
131
if indents > last_indents:
132
continue
133
last_indents = indents
134
block_end = self._find_matching_deindent(block_start)
135
line = self.lines[block_end].strip()
136
if not (line.startswith('finally:') or
137
line.startswith('except ') or
138
line.startswith('except:')):
139
self._insert(block_end, ' ' * indents + 'finally:')
140
self._insert(block_end + 1, ' ' * indents + ' pass')
141
142
def _find_matching_deindent(self, line_number):
143
indents = _get_line_indents(self.lines[line_number])
144
current_line = line_number + 1
145
while current_line < len(self.lines):
146
line = self.lines[current_line]
147
if not line.strip().startswith('#') and not line.strip() == '':
148
# HACK: We should have used logical lines here
149
if _get_line_indents(self.lines[current_line]) <= indents:
150
return current_line
151
current_line += 1
152
return len(self.lines) - 1
153
154
def _set(self, lineno, line):
155
self.diffs[self.origs[lineno]] += len(line) - len(self.lines[lineno])
156
self.lines[lineno] = line
157
158
def _insert(self, lineno, line):
159
self.diffs[self.origs[lineno]] += len(line) + 1
160
self.origs.insert(lineno, self.origs[lineno])
161
self.lines.insert(lineno, line)
162
163
def _logical_start(lines, lineno, check_prev=False):
164
logical_finder = LogicalLineFinder(ArrayLinesAdapter(lines))
165
if check_prev:
166
prev = lineno - 1
167
while prev > 0:
168
start, end = logical_finder.logical_line_in(prev)
169
if end is None or start <= lineno < end:
170
return start
171
if start <= prev:
172
break
173
prev -= 1
174
return logical_finder.logical_line_in(lineno)[0]
175
176
177
def _get_line_indents(line):
178
return rope.base.codeanalyze.count_line_indents(line)
179
180