diff --git a/tests/rules/test_line_length.py b/tests/rules/test_line_length.py index e2c6bcd..2212c3f 100644 --- a/tests/rules/test_line_length.py +++ b/tests/rules/test_line_length.py @@ -84,6 +84,9 @@ class LineLengthTestCase(RuleTestCase): 'another:\n' ' - https://localhost/very/very/long/url\n' '...\n', conf) + self.check('---\n' + 'long_line: http://localhost/very/very/long/url\n', conf, + problem=(2, 21)) conf = 'line-length: {max: 20, allow-non-breakable-words: no}' self.check('---\n' + 30 * 'A' + '\n', conf, problem=(2, 21)) @@ -106,3 +109,39 @@ class LineLengthTestCase(RuleTestCase): 'another:\n' ' - https://localhost/very/very/long/url\n' '...\n', conf, problem=(5, 21)) + self.check('---\n' + 'long_line: http://localhost/very/very/long/url\n' + '...\n', conf, problem=(2, 21)) + + conf = ('line-length: {max: 20, allow-non-breakable-words: yes}\n' + 'trailing-spaces: disable') + self.check('---\n' + 'loooooooooong+word+and+some+space+at+the+end \n', + conf, problem=(2, 21)) + + def test_non_breakable_inline_mappings(self): + conf = 'line-length: {max: 20, ' \ + 'allow-non-breakable-inline-mappings: yes}' + self.check('---\n' + 'long_line: http://localhost/very/very/long/url\n' + 'long line: http://localhost/very/very/long/url\n', conf) + self.check('---\n' + '- long line: http://localhost/very/very/long/url\n', conf) + + self.check('---\n' + 'long_line: http://localhost/short/url + word\n' + 'long line: http://localhost/short/url + word\n', + conf, problem1=(2, 21), problem2=(3, 21)) + + conf = ('line-length: {max: 20,' + ' allow-non-breakable-inline-mappings: yes}\n' + 'trailing-spaces: disable') + self.check('---\n' + 'long_line: and+some+space+at+the+end \n', + conf, problem=(2, 21)) + self.check('---\n' + 'long line: and+some+space+at+the+end \n', + conf, problem=(2, 21)) + self.check('---\n' + '- long line: and+some+space+at+the+end \n', + conf, problem=(2, 21)) diff --git a/yamllint/conf/default.yaml b/yamllint/conf/default.yaml index e2476ce..9eec756 100644 --- a/yamllint/conf/default.yaml +++ b/yamllint/conf/default.yaml @@ -38,6 +38,7 @@ rules: line-length: max: 80 allow-non-breakable-words: yes + allow-non-breakable-inline-mappings: no new-line-at-end-of-file: enable new-lines: type: unix diff --git a/yamllint/conf/relaxed.yaml b/yamllint/conf/relaxed.yaml index 343f784..f653757 100644 --- a/yamllint/conf/relaxed.yaml +++ b/yamllint/conf/relaxed.yaml @@ -25,3 +25,4 @@ rules: indent-sequences: consistent line-length: level: warning + allow-non-breakable-inline-mappings: yes diff --git a/yamllint/rules/line_length.py b/yamllint/rules/line_length.py index 8980267..09e8b23 100644 --- a/yamllint/rules/line_length.py +++ b/yamllint/rules/line_length.py @@ -23,6 +23,8 @@ Use this rule to set a limit to lines length. * ``allow-non-breakable-words`` is used to allow non breakable words (without spaces inside) to overflow the limit. This is useful for long URLs, for instance. Use ``yes`` to allow, ``no`` to forbid. +* ``allow-non-breakable-inline-mappings`` implies ``allow-non-breakable-words`` + and extends it to also allow non-breakable words in inline mappings. .. rubric:: Examples @@ -61,6 +63,19 @@ Use this rule to set a limit to lines length. - this line is waaaaaaaaaaaaaay too long but could be easily split... + and the following code snippet would also **FAIL**: + :: + + - foobar: http://localhost/very/very/very/very/very/very/very/very/long/url + +#. With ``line-length: {max: 60, allow-non-breakable-words: yes, + allow-non-breakable-inline-mappings: yes}`` + + the following code snippet would **PASS**: + :: + + - foobar: http://localhost/very/very/very/very/very/very/very/very/long/url + #. With ``line-length: {max: 60, allow-non-breakable-words: no}`` the following code snippet would **FAIL**: @@ -73,17 +88,34 @@ Use this rule to set a limit to lines length. """ +import yaml + from yamllint.linter import LintProblem ID = 'line-length' TYPE = 'line' CONF = {'max': int, - 'allow-non-breakable-words': bool} + 'allow-non-breakable-words': bool, + 'allow-non-breakable-inline-mappings': bool} + + +def check_inline_mapping(line): + loader = yaml.SafeLoader(line.content) + while loader.peek_token(): + if isinstance(loader.get_token(), yaml.BlockMappingStartToken): + while loader.peek_token(): + if isinstance(loader.get_token(), yaml.ValueToken): + t = loader.get_token() + if isinstance(t, yaml.ScalarToken): + return ' ' not in line.content[t.start_mark.column:] + return False def check(conf, line): if line.end - line.start > conf['max']: + conf['allow-non-breakable-words'] |= \ + conf['allow-non-breakable-inline-mappings'] if conf['allow-non-breakable-words']: start = line.start while start < line.end and line.buffer[start] == ' ': @@ -96,6 +128,10 @@ def check(conf, line): if line.buffer.find(' ', start, line.end) == -1: return + if (conf['allow-non-breakable-inline-mappings'] and + check_inline_mapping(line)): + return + yield LintProblem(line.line_no, conf['max'] + 1, 'line too long (%d > %d characters)' % (line.end - line.start, conf['max']))