|
|
|
# Copyright (c) 2013 - present Facebook, Inc.
|
|
|
|
# All rights reserved.
|
|
|
|
#
|
|
|
|
# This source code is licensed under the BSD style license found in the
|
|
|
|
# LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
# of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
|
|
|
|
from __future__ import absolute_import
|
|
|
|
from __future__ import division
|
|
|
|
from __future__ import print_function
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
|
|
import codecs
|
|
|
|
import os
|
|
|
|
|
|
|
|
from . import colorize, config, utils
|
|
|
|
|
|
|
|
BASE_INDENT = 2
|
|
|
|
# how many lines of context around each report
|
|
|
|
SOURCE_CONTEXT = 2
|
|
|
|
|
|
|
|
|
|
|
|
class Indenter(unicode):
|
|
|
|
def __init__(self):
|
|
|
|
super(Indenter, self).__init__()
|
|
|
|
self.text = ''
|
|
|
|
self.indent = []
|
|
|
|
|
|
|
|
def indent_get(self):
|
|
|
|
indent = ''
|
|
|
|
for i in self.indent:
|
|
|
|
indent += i
|
|
|
|
return indent
|
|
|
|
|
|
|
|
def indent_push(self, n=1):
|
|
|
|
self.indent.append(n * BASE_INDENT * ' ')
|
|
|
|
return self
|
|
|
|
|
|
|
|
def indent_pop(self):
|
|
|
|
return self.indent.pop()
|
|
|
|
return self
|
|
|
|
|
|
|
|
def newline(self):
|
|
|
|
self.text += '\n'
|
|
|
|
return self
|
|
|
|
|
|
|
|
def add(self, x):
|
|
|
|
if type(x) != unicode:
|
|
|
|
x = utils.decode(x)
|
|
|
|
lines = x.splitlines()
|
|
|
|
indent = self.indent_get()
|
|
|
|
lines = [indent + l for l in lines]
|
|
|
|
self.text += '\n'.join(lines)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def __unicode__(self):
|
|
|
|
return self.text
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return utils.encode(unicode(self))
|
|
|
|
|
|
|
|
|
|
|
|
def build_source_context(source_name, mode, report_line,
|
|
|
|
report_col, empty_desc):
|
|
|
|
start_line = max(1, report_line - SOURCE_CONTEXT)
|
|
|
|
start_col = max(0, report_col)
|
|
|
|
# could go beyond last line, checked in the loop
|
|
|
|
end_line = report_line + SOURCE_CONTEXT
|
|
|
|
|
|
|
|
# get source excerpt
|
|
|
|
line_number = 1
|
|
|
|
excerpt = ''
|
|
|
|
if not os.path.isfile(source_name):
|
|
|
|
return ''
|
|
|
|
with codecs.open(source_name, 'r',
|
|
|
|
encoding=config.CODESET, errors="replace") as source_file:
|
|
|
|
# avoid going past the end of the file
|
|
|
|
for line in source_file:
|
|
|
|
last_line = line_number
|
|
|
|
if start_line <= line_number <= end_line:
|
|
|
|
excerpt += line
|
|
|
|
elif line_number > end_line:
|
|
|
|
# OPTIM: no need to read past the last line of the excerpt
|
|
|
|
break
|
|
|
|
line_number += 1
|
|
|
|
excerpt = colorize.syntax_highlighting(source_name, mode, excerpt)
|
|
|
|
|
|
|
|
# number lines and add caret at the right position
|
|
|
|
n_length = len(str(last_line))
|
|
|
|
s = ''
|
|
|
|
line_number = start_line
|
|
|
|
for line in excerpt.split('\n')[:-1]:
|
|
|
|
num = colorize.color((str(line_number) + '.').zfill(n_length),
|
|
|
|
colorize.DIM, mode)
|
|
|
|
caret = ' '
|
|
|
|
do_mark_column = (line_number == report_line and
|
|
|
|
start_col > 1 and not empty_desc)
|
|
|
|
|
|
|
|
# mark the line if we are not also marking the column
|
|
|
|
if line_number == report_line and not do_mark_column:
|
|
|
|
caret = colorize.color('> ',
|
|
|
|
colorize.BLUE + colorize.BRIGHT, mode)
|
|
|
|
s += '%s %s%s\n' % (num, caret, line)
|
|
|
|
# mark the column position
|
|
|
|
if do_mark_column:
|
|
|
|
pad = ' ' * (3 + n_length + start_col)
|
|
|
|
s += pad + colorize.color('^',
|
|
|
|
colorize.BLUE + colorize.BRIGHT,
|
|
|
|
mode) + '\n'
|
|
|
|
line_number += 1
|
|
|
|
|
|
|
|
return s
|