diff --git a/tools/generation/lib/template.py b/tools/generation/lib/template.py index 609724b6f3..53bd03ba19 100644 --- a/tools/generation/lib/template.py +++ b/tools/generation/lib/template.py @@ -11,18 +11,40 @@ from test import Test indentPattern = re.compile(r'^(\s*)') interpolatePattern = re.compile(r'\{\s*(\S+)\s*\}') -def indent(text, prefix = ' '): +def indent(text, prefix = ' ', js_value = False): '''Prefix a block of text (as defined by the "line break" control - character) with some character sequence.''' + character) with some character sequence. + + :param prefix: String value to insert before each line + :param js_value: If True, the text will be interpreted as a JavaScript + value, meaning that indentation will not occur for lines that would + effect the runtime value; defaults to False + ''' if isinstance(text, list): lines = text else: lines = text.split('\n') - indented = map( - lambda line: line if len(line) == 0 else prefix + line, - lines) + indented = [prefix + lines[0]] + str_char = None + + for line in lines[1:]: + # Determine if the beginning of the current line is part of some + # previously-opened literal value. + if js_value: + for char in indented[-1]: + if char == str_char: + str_char = None + elif str_char is None and char in '\'"`': + str_char = char + + # Do not indent the current line if it is a continuation of a literal + # value or if it is empty. + if str_char or len(line) == 0: + indented.append(line) + else: + indented.append(prefix + line) return '\n'.join(indented) @@ -90,7 +112,7 @@ class Template: value = value.replace('\n', '\\\n') source = source[:region['firstchar']] + \ - indent(value, whitespace).lstrip() + \ + indent(value, whitespace, True).lstrip() + \ source[region['lastchar']:] setup = context['regions'].get('setup') diff --git a/tools/generation/test/expected/indentation/spaces-indent-code.js b/tools/generation/test/expected/indentation/spaces-indent-code.js new file mode 100644 index 0000000000..41fa3c22d7 --- /dev/null +++ b/tools/generation/test/expected/indentation/spaces-indent-code.js @@ -0,0 +1,19 @@ +// This file was procedurally generated from the following sources: +// - tools/generation/test/fixtures/indent-code.case +// - tools/generation/test/fixtures/indentation/spaces.template +/*--- +description: Multiple lines of code (Preserving "soft" indentation across newlines) +flags: [generated] +---*/ + +(function() { + 'These literals are each contained on a single line...'; + "...which means they may be indented..."; + `...without effecting the semantics of the generated source code.`; + + if (true) { + 'These literals are each contained on a single line...'; + "...which means they may be indented..."; + `...without effecting the semantics of the generated source code.`; + } +}()); diff --git a/tools/generation/test/expected/indentation/spaces-indent-string-continuation.js b/tools/generation/test/expected/indentation/spaces-indent-string-continuation.js new file mode 100644 index 0000000000..da98f99a3f --- /dev/null +++ b/tools/generation/test/expected/indentation/spaces-indent-string-continuation.js @@ -0,0 +1,19 @@ +// This file was procedurally generated from the following sources: +// - tools/generation/test/fixtures/indent-string-continuation.case +// - tools/generation/test/fixtures/indentation/spaces.template +/*--- +description: Multiline string via a line continuation character (Preserving "soft" indentation across newlines) +flags: [generated] +---*/ + +(function() { + 'this string is declared across multiple lines\ +\ +which disqualifies it as a candidate for indentation'; + + if (true) { + 'this string is declared across multiple lines\ +\ +which disqualifies it as a candidate for indentation'; + } +}()); diff --git a/tools/generation/test/expected/indentation/spaces-indent-string-template.js b/tools/generation/test/expected/indentation/spaces-indent-string-template.js new file mode 100644 index 0000000000..504b9a6e64 --- /dev/null +++ b/tools/generation/test/expected/indentation/spaces-indent-string-template.js @@ -0,0 +1,21 @@ +// This file was procedurally generated from the following sources: +// - tools/generation/test/fixtures/indent-string-template.case +// - tools/generation/test/fixtures/indentation/spaces.template +/*--- +description: String template spanning multiple lines (Preserving "soft" indentation across newlines) +flags: [generated] +---*/ + +(function() { + `this string template is declared across multiple lines + +which disqualifies it as a candidate for indentation +it also happens to contain ' and ".`; + + if (true) { + `this string template is declared across multiple lines + +which disqualifies it as a candidate for indentation +it also happens to contain ' and ".`; + } +}()); diff --git a/tools/generation/test/expected/indentation/tabs-indent-code.js b/tools/generation/test/expected/indentation/tabs-indent-code.js new file mode 100644 index 0000000000..ccfc7c71d1 --- /dev/null +++ b/tools/generation/test/expected/indentation/tabs-indent-code.js @@ -0,0 +1,19 @@ +// This file was procedurally generated from the following sources: +// - tools/generation/test/fixtures/indent-code.case +// - tools/generation/test/fixtures/indentation/tabs.template +/*--- +description: Multiple lines of code (Preserving "hard" indentation across newlines) +flags: [generated] +---*/ + +(function() { + 'These literals are each contained on a single line...'; + "...which means they may be indented..."; + `...without effecting the semantics of the generated source code.`; + + if (true) { + 'These literals are each contained on a single line...'; + "...which means they may be indented..."; + `...without effecting the semantics of the generated source code.`; + } +}()); diff --git a/tools/generation/test/expected/indentation/tabs-indent-string-continuation.js b/tools/generation/test/expected/indentation/tabs-indent-string-continuation.js new file mode 100644 index 0000000000..a4719748e3 --- /dev/null +++ b/tools/generation/test/expected/indentation/tabs-indent-string-continuation.js @@ -0,0 +1,19 @@ +// This file was procedurally generated from the following sources: +// - tools/generation/test/fixtures/indent-string-continuation.case +// - tools/generation/test/fixtures/indentation/tabs.template +/*--- +description: Multiline string via a line continuation character (Preserving "hard" indentation across newlines) +flags: [generated] +---*/ + +(function() { + 'this string is declared across multiple lines\ +\ +which disqualifies it as a candidate for indentation'; + + if (true) { + 'this string is declared across multiple lines\ +\ +which disqualifies it as a candidate for indentation'; + } +}()); diff --git a/tools/generation/test/expected/indentation/tabs-indent-string-template.js b/tools/generation/test/expected/indentation/tabs-indent-string-template.js new file mode 100644 index 0000000000..b40bd7578d --- /dev/null +++ b/tools/generation/test/expected/indentation/tabs-indent-string-template.js @@ -0,0 +1,21 @@ +// This file was procedurally generated from the following sources: +// - tools/generation/test/fixtures/indent-string-template.case +// - tools/generation/test/fixtures/indentation/tabs.template +/*--- +description: String template spanning multiple lines (Preserving "hard" indentation across newlines) +flags: [generated] +---*/ + +(function() { + `this string template is declared across multiple lines + +which disqualifies it as a candidate for indentation +it also happens to contain ' and ".`; + + if (true) { + `this string template is declared across multiple lines + +which disqualifies it as a candidate for indentation +it also happens to contain ' and ".`; + } +}()); diff --git a/tools/generation/test/fixtures/indent-code.case b/tools/generation/test/fixtures/indent-code.case new file mode 100644 index 0000000000..e10f49a8df --- /dev/null +++ b/tools/generation/test/fixtures/indent-code.case @@ -0,0 +1,11 @@ +// Copyright (C) 2017 Mike Pennisi. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +template: indentation +desc: Multiple lines of code +---*/ + +//- value +'These literals are each contained on a single line...'; +"...which means they may be indented..."; +`...without effecting the semantics of the generated source code.`; diff --git a/tools/generation/test/fixtures/indent-string-continuation.case b/tools/generation/test/fixtures/indent-string-continuation.case new file mode 100644 index 0000000000..e22d9690c9 --- /dev/null +++ b/tools/generation/test/fixtures/indent-string-continuation.case @@ -0,0 +1,11 @@ +// Copyright (C) 2017 Mike Pennisi. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +template: indentation +desc: Multiline string via a line continuation character +---*/ + +//- value +'this string is declared across multiple lines\ +\ +which disqualifies it as a candidate for indentation'; diff --git a/tools/generation/test/fixtures/indent-string-template.case b/tools/generation/test/fixtures/indent-string-template.case new file mode 100644 index 0000000000..56daee9a14 --- /dev/null +++ b/tools/generation/test/fixtures/indent-string-template.case @@ -0,0 +1,12 @@ +// Copyright (C) 2017 Mike Pennisi. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +template: indentation +desc: String template spanning multiple lines +---*/ + +//- value +`this string template is declared across multiple lines + +which disqualifies it as a candidate for indentation +it also happens to contain ' and ".`; diff --git a/tools/generation/test/fixtures/indentation/spaces.template b/tools/generation/test/fixtures/indentation/spaces.template new file mode 100644 index 0000000000..0796dac5a0 --- /dev/null +++ b/tools/generation/test/fixtures/indentation/spaces.template @@ -0,0 +1,14 @@ +// Copyright (C) 2017 Mike Pennisi. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +name: Preserving "soft" indentation across newlines +path: indentation/spaces- +---*/ + +(function() { + /*{ value }*/ + + if (true) { + /*{ value }*/ + } +}()); diff --git a/tools/generation/test/fixtures/indentation/tabs.template b/tools/generation/test/fixtures/indentation/tabs.template new file mode 100644 index 0000000000..87130571f5 --- /dev/null +++ b/tools/generation/test/fixtures/indentation/tabs.template @@ -0,0 +1,14 @@ +// Copyright (C) 2017 Mike Pennisi. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +name: Preserving "hard" indentation across newlines +path: indentation/tabs- +---*/ + +(function() { + /*{ value }*/ + + if (true) { + /*{ value }*/ + } +}()); diff --git a/tools/generation/test/run.py b/tools/generation/test/run.py index 9fc9ee3a1b..c21efa1b83 100755 --- a/tools/generation/test/run.py +++ b/tools/generation/test/run.py @@ -59,5 +59,14 @@ class TestGeneration(unittest.TestCase): self.assertEqual(result['returncode'], 0) self.compareTrees('negative') + def test_indentation(self): + result = self.fixture('indent-code.case') + self.assertEqual(result['returncode'], 0) + result = self.fixture('indent-string-continuation.case') + self.assertEqual(result['returncode'], 0) + result = self.fixture('indent-string-template.case') + self.assertEqual(result['returncode'], 0) + self.compareTrees('indentation') + if __name__ == '__main__': unittest.main()