X-Git-Url: https://git.madduck.net/etc/vim.git/blobdiff_plain/da3c2f358208dc9838c63d57b66c89e164eb241a..c26daa4fd50e2db13f38279ce27b51b0ae4479fe:/black.py?ds=inline diff --git a/black.py b/black.py index 75fbdca..774d91d 100644 --- a/black.py +++ b/black.py @@ -55,6 +55,15 @@ class CannotSplit(Exception): help='How many character per line to allow.', show_default=True, ) +@click.option( + '--check', + is_flag=True, + help=( + "Don't write back the files, just return the status. Return code 0 " + "means nothing changed. Return code 1 means some files were " + "reformatted. Return code 123 means there was an internal error." + ), +) @click.option( '--fast/--safe', is_flag=True, @@ -67,7 +76,9 @@ class CannotSplit(Exception): type=click.Path(exists=True, file_okay=True, dir_okay=True, readable=True), ) @click.pass_context -def main(ctx: click.Context, line_length: int, fast: bool, src: List[str]) -> None: +def main( + ctx: click.Context, line_length: int, check: bool, fast: bool, src: List[str] +) -> None: """The uncompromising code formatter.""" sources: List[Path] = [] for s in src: @@ -85,7 +96,9 @@ def main(ctx: click.Context, line_length: int, fast: bool, src: List[str]) -> No p = sources[0] report = Report() try: - changed = format_file_in_place(p, line_length=line_length, fast=fast) + changed = format_file_in_place( + p, line_length=line_length, fast=fast, write_back=not check + ) report.done(p, changed) except Exception as exc: report.failed(p, str(exc)) @@ -96,7 +109,9 @@ def main(ctx: click.Context, line_length: int, fast: bool, src: List[str]) -> No return_code = 1 try: return_code = loop.run_until_complete( - schedule_formatting(sources, line_length, fast, loop, executor) + schedule_formatting( + sources, line_length, not check, fast, loop, executor + ) ) finally: loop.close() @@ -106,13 +121,14 @@ def main(ctx: click.Context, line_length: int, fast: bool, src: List[str]) -> No async def schedule_formatting( sources: List[Path], line_length: int, + write_back: bool, fast: bool, loop: BaseEventLoop, executor: Executor, ) -> int: tasks = { src: loop.run_in_executor( - executor, format_file_in_place, src, line_length, fast + executor, format_file_in_place, src, line_length, fast, write_back ) for src in sources } @@ -135,15 +151,18 @@ async def schedule_formatting( return report.return_code -def format_file_in_place(src: Path, line_length: int, fast: bool) -> bool: +def format_file_in_place( + src: Path, line_length: int, fast: bool, write_back: bool = False +) -> bool: """Format the file and rewrite if changed. Return True if changed.""" try: contents, encoding = format_file(src, line_length=line_length, fast=fast) except NothingChanged: return False - with open(src, "w", encoding=encoding) as f: - f.write(contents) + if write_back: + with open(src, "w", encoding=encoding) as f: + f.write(contents) return True @@ -373,6 +392,8 @@ class Line: comments: Dict[LeafID, Leaf] = attrib(default=Factory(dict)) bracket_tracker: BracketTracker = attrib(default=Factory(BracketTracker)) inside_brackets: bool = attrib(default=False) + has_for: bool = attrib(default=False) + _for_loop_variable: bool = attrib(default=False, init=False) def append(self, leaf: Leaf, preformatted: bool = False) -> None: has_value = leaf.value.strip() @@ -384,8 +405,10 @@ class Line: # imports, for which we only preserve newlines. leaf.prefix += whitespace(leaf) if self.inside_brackets or not preformatted: + self.maybe_decrement_after_for_loop_variable(leaf) self.bracket_tracker.mark(leaf) self.maybe_remove_trailing_comma(leaf) + self.maybe_increment_for_loop_variable(leaf) if self.maybe_adapt_standalone_comment(leaf): return @@ -489,6 +512,29 @@ class Line: return False + def maybe_increment_for_loop_variable(self, leaf: Leaf) -> bool: + """In a for loop, or comprehension, the variables are often unpacks. + + To avoid splitting on the comma in this situation, we will increase + the depth of tokens between `for` and `in`. + """ + if leaf.type == token.NAME and leaf.value == 'for': + self.has_for = True + self.bracket_tracker.depth += 1 + self._for_loop_variable = True + return True + + return False + + def maybe_decrement_after_for_loop_variable(self, leaf: Leaf) -> bool: + # See `maybe_increment_for_loop_variable` above for explanation. + if self._for_loop_variable and leaf.type == token.NAME and leaf.value == 'in': + self.bracket_tracker.depth -= 1 + self._for_loop_variable = False + return True + + return False + def maybe_adapt_standalone_comment(self, comment: Leaf) -> bool: """Hack a standalone comment to act as a trailing comment for line splitting. @@ -815,6 +861,9 @@ def whitespace(leaf: Leaf) -> str: if prevp.parent and prevp.parent.type == syms.subscript: return NO + elif prevp.parent and prevp.parent.type == syms.factor: + return NO + elif prev.type in OPENING_BRACKETS: return NO @@ -1318,7 +1367,15 @@ class Report: @property def return_code(self) -> int: """Which return code should the app use considering the current state.""" - return 1 if self.failure_count else 0 + # According to http://tldp.org/LDP/abs/html/exitcodes.html starting with + # 126 we have special returncodes reserved by the shell. + if self.failure_count: + return 123 + + elif self.change_count: + return 1 + + return 0 def __str__(self) -> str: """A color report of the current state.