colorise issue messages

Summary:public
Add colors to errors and warnings reported by infer

Reviewed By: cristianoc

Differential Revision: D3023927

fb-gh-sync-id: 555bb8f
shipit-source-id: 555bb8f
master
Jules Villard 9 years ago committed by Facebook Github Bot 8
parent b03304e0d8
commit 527d26dd91

@ -24,6 +24,75 @@ PLAIN_FORMATTER = 0
TERMINAL_FORMATTER = 1 TERMINAL_FORMATTER = 1
def terminal_only(s):
if not sys.stdout.isatty():
return ''
return s
BLUE = terminal_only('\033[34m')
BLUE_BG = terminal_only('\033[44m')
MAGENTA = terminal_only('\033[35m')
MAGENTA_BG = terminal_only('\033[45m')
BRIGHT = terminal_only('\033[1m')
DIM = terminal_only('\033[2m')
RED = terminal_only('\033[31m')
RESET = terminal_only('\033[0m')
WHITE = terminal_only('\033[37m')
WHITE_BG = terminal_only('\033[47m')
YELLOW = terminal_only('\033[35m')
ERROR = RED
HEADER = BRIGHT
SUCCESS = MAGENTA_BG + WHITE + BRIGHT
WARNING = ''
class Invalid_mode(Exception):
pass
INFER_LOGO = """\
_.........
_.....................
_...........................
_.................................
_.....................................
_...............I _....................
_.................I _......................
_..................I _.......................
_..................I _.......................
_...................I _........................
_...................I _........................
_....................I No issues _...............
_....................I found _...............
_....................I _.........................
_...................I _........................
_...................I _........................
_..................I _.......................
_.................I _......................
_................I _.....................
_.....................................
_...................................
_.............................
_........................
_...............\
"""
def logo(mode):
if mode == PLAIN_FORMATTER:
return ''
if mode != TERMINAL_FORMATTER:
raise Invalid_mode()
disc_color = MAGENTA_BG
entailment_color = WHITE_BG + MAGENTA
logo = INFER_LOGO.replace('_', disc_color + ' ') \
.replace('I', entailment_color + ' ') \
.replace('.', ' ') \
.replace('\n', RESET + '\n')
return logo + RESET
def syntax_highlighting(source_name, mode, s): def syntax_highlighting(source_name, mode, s):
if pygments is None or mode == PLAIN_FORMATTER: if pygments is None or mode == PLAIN_FORMATTER:
return s return s
@ -35,3 +104,11 @@ def syntax_highlighting(source_name, mode, s):
return s return s
formatter = pygments.formatters.TerminalFormatter() formatter = pygments.formatters.TerminalFormatter()
return pygments.highlight(s, lexer, formatter) return pygments.highlight(s, lexer, formatter)
def color(s, color, mode):
if mode == TERMINAL_FORMATTER:
return color + s + RESET
if mode == PLAIN_FORMATTER:
return s
raise Invalid_mode()

@ -21,7 +21,7 @@ import sys
import tempfile import tempfile
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from . import config, colorize, source, utils from . import colorize, config, source, utils
# Increase the limit of the CSV parser to sys.maxlimit # Increase the limit of the CSV parser to sys.maxlimit
@ -182,10 +182,10 @@ def _text_of_report_list(reports, formatter=colorize.TERMINAL_FORMATTER):
line = report[JSON_INDEX_LINE] line = report[JSON_INDEX_LINE]
source_context = '' source_context = ''
if formatter is not None: if formatter == colorize.TERMINAL_FORMATTER:
source_context = source.build_source_context( source_context = source.build_source_context(
filename, filename,
colorize.TERMINAL_FORMATTER, formatter,
line, line,
) )
indenter = source.Indenter() \ indenter = source.Indenter() \
@ -193,7 +193,12 @@ def _text_of_report_list(reports, formatter=colorize.TERMINAL_FORMATTER):
.add(source_context) .add(source_context)
source_context = '\n' + unicode(indenter) source_context = '\n' + unicode(indenter)
text = '%s%s' % (text_of_report(report), source_context) msg = text_of_report(report)
if report[JSON_INDEX_KIND] == ISSUE_KIND_ERROR:
msg = colorize.color(msg, colorize.ERROR, formatter)
elif report[JSON_INDEX_KIND] == ISSUE_KIND_WARNING:
msg = colorize.color(msg, colorize.WARNING, formatter)
text = '%s%s' % (msg, source_context)
text_errors_list.append(text) text_errors_list.append(text)
t = report[JSON_INDEX_TYPE] t = report[JSON_INDEX_TYPE]
@ -208,6 +213,9 @@ def _text_of_report_list(reports, formatter=colorize.TERMINAL_FORMATTER):
n_issues = len(text_errors_list) n_issues = len(text_errors_list)
if n_issues == 0: if n_issues == 0:
if formatter == colorize.TERMINAL_FORMATTER:
return colorize.logo(formatter)
else:
return 'No issues found' return 'No issues found'
max_type_length = max(map(len, error_types_count.keys())) + 2 max_type_length = max(map(len, error_types_count.keys())) + 2
@ -220,10 +228,17 @@ def _text_of_report_list(reports, formatter=colorize.TERMINAL_FORMATTER):
text_errors = '\n\n'.join(text_errors_list) text_errors = '\n\n'.join(text_errors_list)
msg = 'Found %s\n\n%s\n\nSummary of the reports:\n\n%s' % ( issues_found = 'Found {n_issues}'.format(
utils.get_plural('issue', n_issues), n_issues=utils.get_plural('issue', n_issues),
text_errors, )
'\n'.join(types_text_list), msg = '{issues_found}\n\n{issues}\n\n{header}\n\n{summary}'.format(
issues_found=colorize.color(issues_found,
colorize.HEADER,
formatter),
issues=text_errors,
header=colorize.color('Summary of the reports',
colorize.HEADER, formatter),
summary='\n'.join(types_text_list),
) )
return msg return msg
@ -241,7 +256,9 @@ def print_and_save_errors(json_report, bugs_out):
utils.stdout('\n' + _text_of_report_list(errors)) utils.stdout('\n' + _text_of_report_list(errors))
with codecs.open(bugs_out, 'w', with codecs.open(bugs_out, 'w',
encoding=config.LOCALE, errors='replace') as file_out: encoding=config.LOCALE, errors='replace') as file_out:
file_out.write(_text_of_report_list(errors, formatter=None)) plain_out = _text_of_report_list(errors,
formatter=colorize.PLAIN_FORMATTER)
file_out.write(plain_out)
def merge_reports_from_paths(report_paths): def merge_reports_from_paths(report_paths):

@ -12,7 +12,7 @@ from __future__ import unicode_literals
import codecs import codecs
from . import config, colorize, utils from . import colorize, config, utils
BASE_INDENT = 2 BASE_INDENT = 2
# how many lines of context around each report # how many lines of context around each report
@ -63,17 +63,30 @@ def build_source_context(source_name, mode, report_line):
# could go beyond last line, checked in the loop # could go beyond last line, checked in the loop
end_line = report_line + SOURCE_CONTEXT end_line = report_line + SOURCE_CONTEXT
n_length = len(str(end_line)) # get source excerpt
line_number = 1 line_number = 1
s = '' excerpt = ''
with codecs.open(source_name, 'r', with codecs.open(source_name, 'r',
encoding=config.LOCALE, errors="replace") as source_file: encoding=config.LOCALE, errors="replace") as source_file:
# avoid going past the end of the file
for line in source_file: for line in source_file:
if start_line <= line_number <= end_line: if start_line <= line_number <= end_line:
num = str(line_number).zfill(n_length) excerpt += line
line_number += 1
excerpt = colorize.syntax_highlighting(source_name, mode, excerpt)
# number lines and add caret at the right position
n_length = len(str(end_line))
s = ''
line_number = start_line
for line in excerpt.split('\n'):
num = colorize.color((str(line_number) + '.').zfill(n_length),
colorize.DIM, mode)
caret = ' ' caret = ' '
if line_number == report_line: if line_number == report_line:
caret = '> ' caret = colorize.color('> ',
s += '%s. %s%s' % (num, caret, line) colorize.HEADER, mode)
s += '%s %s%s\n' % (num, caret, line)
line_number += 1 line_number += 1
return colorize.syntax_highlighting(source_name, mode, s)
return s

Loading…
Cancel
Save