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
|
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)
import sys
import re
from powerline.lib.encoding import get_preferred_output_encoding
NON_PRINTABLE_STR = (
'[^'
# ASCII control characters: 0x00-0x19
+ '\t\n' # Tab, newline: allowed ASCII control characters
+ '\x20-\x7E' # ASCII printable characters
# Unicode control characters: 0x7F-0x9F
+ '\u0085' # Allowed unicode control character: next line character
+ '\u00A0-\uD7FF'
# Surrogate escapes: 0xD800-0xDFFF
+ '\uE000-\uFFFD'
+ ((
'\uD800-\uDFFF'
) if sys.maxunicode < 0x10FFFF else (
'\U00010000-\U0010FFFF'
))
+ ']'
+ ((
# Paired surrogate escapes: allowed in UCS-2 builds as the only way to
# represent characters above 0xFFFF. Only paired variant is allowed.
'|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]'
+ '|[\uD800-\uDBFF](?![\uDC00-\uDFFF])'
) if sys.maxunicode < 0x10FFFF else (
''
))
)
NON_PRINTABLE_RE = re.compile(NON_PRINTABLE_STR)
def repl(s):
return '<x%04x>' % ord(s.group())
def strtrans(s):
return NON_PRINTABLE_RE.sub(repl, s.replace('\t', '>---'))
class Mark:
def __init__(self, name, line, column, buffer, pointer, old_mark=None, merged_marks=None):
self.name = name
self.line = line
self.column = column
self.buffer = buffer
self.pointer = pointer
self.old_mark = old_mark
self.merged_marks = merged_marks or []
def copy(self):
return Mark(self.name, self.line, self.column, self.buffer, self.pointer, self.old_mark, self.merged_marks[:])
def get_snippet(self, indent=4, max_length=75):
if self.buffer is None:
return None
head = ''
start = self.pointer
while start > 0 and self.buffer[start - 1] not in '\0\n':
start -= 1
if self.pointer - start > max_length / 2 - 1:
head = ' ... '
start += 5
break
tail = ''
end = self.pointer
while end < len(self.buffer) and self.buffer[end] not in '\0\n':
end += 1
if end - self.pointer > max_length / 2 - 1:
tail = ' ... '
end -= 5
break
snippet = [self.buffer[start:self.pointer], self.buffer[self.pointer], self.buffer[self.pointer + 1:end]]
snippet = [strtrans(s) for s in snippet]
return (
' ' * indent + head + ''.join(snippet) + tail + '\n'
+ ' ' * (indent + len(head) + len(snippet[0])) + '^'
)
def advance_string(self, diff):
ret = self.copy()
# FIXME Currently does not work properly with escaped strings.
ret.column += diff
ret.pointer += diff
return ret
def set_old_mark(self, old_mark):
if self is old_mark:
return
checked_marks = set([id(self)])
older_mark = old_mark
while True:
if id(older_mark) in checked_marks:
raise ValueError('Trying to set recursive marks')
checked_marks.add(id(older_mark))
older_mark = older_mark.old_mark
if not older_mark:
break
self.old_mark = old_mark
def set_merged_mark(self, merged_mark):
self.merged_marks.append(merged_mark)
def to_string(self, indent=0, head_text='in ', add_snippet=True):
mark = self
where = ''
processed_marks = set()
while mark:
indentstr = ' ' * indent
where += ('%s %s"%s", line %d, column %d' % (
indentstr, head_text, mark.name, mark.line + 1, mark.column + 1))
if add_snippet:
snippet = mark.get_snippet(indent=(indent + 4))
if snippet:
where += ':\n' + snippet
if mark.merged_marks:
where += '\n' + indentstr + ' with additionally merged\n'
where += mark.merged_marks[0].to_string(indent + 4, head_text='', add_snippet=False)
for mmark in mark.merged_marks[1:]:
where += '\n' + indentstr + ' and\n'
where += mmark.to_string(indent + 4, head_text='', add_snippet=False)
if add_snippet:
processed_marks.add(id(mark))
if mark.old_mark:
where += '\n' + indentstr + ' which replaced value\n'
indent += 4
mark = mark.old_mark
if id(mark) in processed_marks:
raise ValueError('Trying to dump recursive mark')
return where
if sys.version_info < (3,):
def __str__(self):
return self.to_string().encode('utf-8')
def __unicode__(self):
return self.to_string()
else:
def __str__(self):
return self.to_string()
def __eq__(self, other):
return self is other or (
self.name == other.name
and self.line == other.line
and self.column == other.column
)
if sys.version_info < (3,):
def echoerr(**kwargs):
stream = kwargs.pop('stream', sys.stderr)
stream.write('\n')
stream.write((format_error(**kwargs) + '\n').encode(get_preferred_output_encoding()))
else:
def echoerr(**kwargs):
stream = kwargs.pop('stream', sys.stderr)
stream.write('\n')
stream.write(format_error(**kwargs) + '\n')
def format_error(context=None, context_mark=None, problem=None, problem_mark=None, note=None, indent=0):
lines = []
indentstr = ' ' * indent
if context is not None:
lines.append(indentstr + context)
if (
context_mark is not None
and (
problem is None or problem_mark is None
or context_mark != problem_mark
)
):
lines.append(context_mark.to_string(indent=indent))
if problem is not None:
lines.append(indentstr + problem)
if problem_mark is not None:
lines.append(problem_mark.to_string(indent=indent))
if note is not None:
lines.append(indentstr + note)
return '\n'.join(lines)
class MarkedError(Exception):
def __init__(self, context=None, context_mark=None, problem=None, problem_mark=None, note=None):
Exception.__init__(self, format_error(context, context_mark, problem, problem_mark, note))
class EchoErr(object):
__slots__ = ('echoerr', 'logger', 'indent')
def __init__(self, echoerr, logger, indent=0):
self.echoerr = echoerr
self.logger = logger
self.indent = indent
def __call__(self, **kwargs):
kwargs = kwargs.copy()
kwargs.setdefault('indent', self.indent)
self.echoerr(**kwargs)
class DelayedEchoErr(EchoErr):
__slots__ = ('echoerr', 'logger', 'errs', 'message', 'separator_message', 'indent', 'indent_shift')
def __init__(self, echoerr, message='', separator_message=''):
super(DelayedEchoErr, self).__init__(echoerr, echoerr.logger)
self.errs = [[]]
self.message = message
self.separator_message = separator_message
self.indent_shift = (4 if message or separator_message else 0)
self.indent = echoerr.indent + self.indent_shift
def __call__(self, **kwargs):
kwargs = kwargs.copy()
kwargs['indent'] = kwargs.get('indent', 0) + self.indent
self.errs[-1].append(kwargs)
def next_variant(self):
self.errs.append([])
def echo_all(self):
if self.message:
self.echoerr(problem=self.message, indent=(self.indent - self.indent_shift))
for variant in self.errs:
if not variant:
continue
if self.separator_message and variant is not self.errs[0]:
self.echoerr(problem=self.separator_message, indent=(self.indent - self.indent_shift))
for kwargs in variant:
self.echoerr(**kwargs)
def __nonzero__(self):
return not not self.errs
__bool__ = __nonzero__
|