diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 51c25860..d1797d99 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -41,7 +41,7 @@ def generate_json_config_loader(lhadproblem): function_name_re = '^(\w+\.)*[a-zA-Z_]\w*$' -divider_spec = Spec().type(unicode).len( +divider_spec = Spec().printable().len( 'le', 3, (lambda value: 'Divider {0!r} is too large!'.format(value))).copy ext_theme_spec = Spec().type(unicode).func(lambda *args: check_config('themes', *args)).copy top_theme_spec = Spec().type(unicode).func(check_top_theme).copy @@ -211,12 +211,12 @@ segment_spec = Spec( display=Spec().type(bool).optional(), module=segment_module_spec(), priority=Spec().type(int, float, type(None)).optional(), - after=Spec().type(unicode).optional(), - before=Spec().type(unicode).optional(), + after=Spec().printable().optional(), + before=Spec().printable().optional(), width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(), align=Spec().oneof(set('lr')).optional(), args=args_spec().func(lambda *args, **kwargs: check_args(get_one_segment_function, *args, **kwargs)), - contents=Spec().type(unicode).optional(), + contents=Spec().printable().optional(), highlight_group=Spec().list( highlight_group_spec().re( '^(?:(?!:divider$).)+$', @@ -243,11 +243,11 @@ divside_spec = Spec( soft=divider_spec(), ).copy segment_data_value_spec = Spec( - after=Spec().type(unicode).optional(), - before=Spec().type(unicode).optional(), + after=Spec().printable().optional(), + before=Spec().printable().optional(), display=Spec().type(bool).optional(), args=args_spec().func(lambda *args, **kwargs: check_args(get_all_possible_functions, *args, **kwargs)), - contents=Spec().type(unicode).optional(), + contents=Spec().printable().optional(), ).copy dividers_spec = Spec( left=divside_spec(), diff --git a/powerline/lint/markedjson/error.py b/powerline/lint/markedjson/error.py index ad3fd875..dfde4086 100644 --- a/powerline/lint/markedjson/error.py +++ b/powerline/lint/markedjson/error.py @@ -7,7 +7,7 @@ import re from powerline.lib.unicode import unichr -NON_PRINTABLE = re.compile( +NON_PRINTABLE_STR = ( '[^' # ASCII control characters: 0x00-0x19 + '\t\n' # Tab, newline: allowed ASCII control characters @@ -26,6 +26,7 @@ NON_PRINTABLE = re.compile( '' )) ) +NON_PRINTABLE_RE = re.compile(NON_PRINTABLE_STR) def repl(s): @@ -33,7 +34,7 @@ def repl(s): def strtrans(s): - return NON_PRINTABLE.sub(repl, s.replace('\t', '>---')) + return NON_PRINTABLE_RE.sub(repl, s.replace('\t', '>---')) class Mark: @@ -73,6 +74,13 @@ class Mark: + ' ' * (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 __str__(self): snippet = self.get_snippet() where = (' in "%s", line %d, column %d' % ( diff --git a/powerline/lint/markedjson/markedvalue.py b/powerline/lint/markedjson/markedvalue.py index 74a62b64..c17a8e35 100644 --- a/powerline/lint/markedjson/markedvalue.py +++ b/powerline/lint/markedjson/markedvalue.py @@ -33,12 +33,7 @@ class MarkedUnicode(unicode): pointdiff = 1 r = [] for s in part_result: - mark = self.mark.copy() - # XXX Does not work properly with escaped strings, but this requires - # saving much more information in mark. - mark.column += pointdiff - mark.pointer += pointdiff - r.append(MarkedUnicode(s, mark)) + r.append(MarkedUnicode(s, self.mark.advance_string(pointdiff))) pointdiff += len(s) return tuple(r) diff --git a/powerline/lint/markedjson/reader.py b/powerline/lint/markedjson/reader.py index a17a736b..0ca45160 100644 --- a/powerline/lint/markedjson/reader.py +++ b/powerline/lint/markedjson/reader.py @@ -3,7 +3,7 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct import codecs -from powerline.lint.markedjson.error import MarkedError, Mark, NON_PRINTABLE +from powerline.lint.markedjson.error import MarkedError, Mark, NON_PRINTABLE_RE from powerline.lib.unicode import unicode @@ -84,7 +84,7 @@ class Reader(object): return Mark(self.name, self.line, self.column, self.full_buffer, self.full_pointer) def check_printable(self, data): - match = NON_PRINTABLE.search(data) + match = NON_PRINTABLE_RE.search(data) if match: self.update_pointer(match.start()) raise ReaderError( diff --git a/powerline/lint/spec.py b/powerline/lint/spec.py index 1d095721..6de14fea 100644 --- a/powerline/lint/spec.py +++ b/powerline/lint/spec.py @@ -7,10 +7,19 @@ import re from copy import copy from powerline.lib.unicode import unicode -from powerline.lint.markedjson.error import echoerr, DelayedEchoErr +from powerline.lint.markedjson.error import echoerr, DelayedEchoErr, NON_PRINTABLE_STR from powerline.lint.selfcheck import havemarks +NON_PRINTABLE_RE = re.compile( + NON_PRINTABLE_STR.translate({ + ord('\t'): None, + ord('\n'): None, + 0x0085: None, + }) +) + + class Spec(object): '''Class that describes some JSON value @@ -342,6 +351,26 @@ class Spec(object): return False, hadproblem return True, hadproblem + def check_printable(self, value, context_mark, data, context, echoerr, _): + '''Check that given unicode string contains only printable characters + ''' + hadproblem = False + for match in NON_PRINTABLE_RE.finditer(value): + hadproblem = True + echoerr( + context=self.cmsg.format(key=context.key), + context_mark=value.mark, + problem='found not printable character U+{0:04x} in a configuration string'.format( + ord(match.group(0))), + problem_mark=value.mark.advance_string(match.start() + 1) + ) + return True, hadproblem + + def printable(self, *args): + self.type(unicode) + self.checks.append(('check_printable', args)) + return self + def type(self, *args): '''Describe value that has one of the types given in arguments