* generalized star expression handling, including double stars; this
fixes multiplication making expressions "unsafe" for trailing commas (#132)
+* fixed comment indentation when a standalone comment closes a block (#16, #32)
+
* fixed `--diff` not showing entire path (#130)
* fixed parsing of complex expressions after star and double stars in
def visit_DEDENT(self, node: Node) -> Iterator[Line]:
"""Decrease indentation level, maybe yield a line."""
- # DEDENT has no value. Additionally, in blib2to3 it never holds comments.
+ # The current line might still wait for trailing comments. At DEDENT time
+ # there won't be any (they would be prefixes on the preceding NEWLINE).
+ # Emit the line then.
+ yield from self.line()
+
+ # While DEDENT has no value, its prefix may contain standalone comments
+ # that belong to the current indentation level. Get 'em.
+ yield from self.visit_default(node)
+
+ # Finally, emit the dedent.
yield from self.line(-1)
def visit_stmt(
p.setup()
lineno = 1
column = 0
+ indent_columns = []
type = value = start = end = line_text = None
prefix = ""
for quintuple in tokens:
if type in {token.INDENT, token.DEDENT}:
_prefix = prefix
prefix = ""
+ if type == token.DEDENT:
+ _indent_col = indent_columns.pop()
+ prefix, _prefix = self._partially_consume_prefix(_prefix, _indent_col)
if p.addtoken(type, value, (prefix, start)):
if debug:
self.logger.debug("Stop.")
break
prefix = ""
if type == token.INDENT:
+ indent_columns.append(len(value))
if _prefix.startswith(value):
# Don't double-indent. Since we're delaying the prefix that
# would normally belong to INDENT, we need to put the value
tokens = tokenize.generate_tokens(io.StringIO(text).readline)
return self.parse_tokens(tokens, debug)
+ def _partially_consume_prefix(self, prefix, column):
+ lines = []
+ current_line = ""
+ current_column = 0
+ wait_for_nl = False
+ for char in prefix:
+ current_line += char
+ if wait_for_nl:
+ if char == '\n':
+ if current_line.strip() and current_column < column:
+ res = ''.join(lines)
+ return res, prefix[len(res):]
+
+ lines.append(current_line)
+ current_line = ""
+ current_column = 0
+ wait_for_nl = False
+ elif char == ' ':
+ current_column += 1
+ elif char == '\t':
+ current_column += 4
+ elif char == '\n':
+ # enexpected empty line
+ current_column = 0
+ else:
+ # indent is finished
+ wait_for_nl = True
+ return ''.join(lines), current_line
+
def _generate_pickle_name(gt):
head, tail = os.path.splitext(gt)
# for compiler in compilers.values():
# add_compiler(compiler)
add_compiler(compilers[(7.0, 32)])
-# add_compiler(compilers[(7.1, 64)])
+ # add_compiler(compilers[(7.1, 64)])
# Comment before function.
if False:
continue
- # and round and round we go
- # and round and round we go
+ # and round and round we go
+ # and round and round we go
# let's return
return Node(
--- /dev/null
+while True:
+ if something.changed:
+ do.stuff() # trailing comment
+ # Comment belongs to the `if` block.
+ # This one belongs to the `while` block.
+
+ # Should this one, too? I guess so.
+
+# This one is properly standalone now.
+
+for i in range(100):
+ # first we do this
+ if i % 33 == 0:
+ break
+
+ # then we do this
+ print(i)
+ # and finally we loop around
+
+with open(some_temp_file) as f:
+ data = f.read()
+
+try:
+ with open(some_other_file) as w:
+ w.write(data)
+
+except OSError:
+ print("problems")
+
+if __name__ == "__main__":
+ main()
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, line_length=ll)
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_comments5(self) -> None:
+ source, expected = read_data("comments5")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, line_length=ll)
+
@patch("black.dump_to_file", dump_to_stderr)
def test_cantfit(self) -> None:
source, expected = read_data("cantfit")