]> git.madduck.net Git - etc/vim.git/commitdiff

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Add "# fmt: skip" directive to black (#1800)
authorSagi Shadur <saroad2@gmail.com>
Mon, 15 Feb 2021 16:02:48 +0000 (18:02 +0200)
committerGitHub <noreply@github.com>
Mon, 15 Feb 2021 16:02:48 +0000 (08:02 -0800)
Fixes #1162

README.md
src/black/__init__.py
tests/data/fmtskip.py [new file with mode: 0644]
tests/data/fmtskip2.py [new file with mode: 0644]
tests/data/fmtskip3.py [new file with mode: 0644]
tests/data/fmtskip4.py [new file with mode: 0644]
tests/data/fmtskip5.py [new file with mode: 0644]
tests/test_format.py

index 97df938eab25489bb3f78e993f295df4880dba84..89ef4271d41db013d35ae3344ff0d60b8ecf0bc0 100644 (file)
--- a/README.md
+++ b/README.md
@@ -239,8 +239,9 @@ feeling confident, use `--fast`.
 _Black_ is a PEP 8 compliant opinionated formatter. _Black_ reformats entire files in
 place. It is not configurable. It doesn't take previous formatting into account. Your
 main option of configuring _Black_ is that it doesn't reformat blocks that start with
-`# fmt: off` and end with `# fmt: on`. `# fmt: on/off` have to be on the same level of
-indentation. To learn more about _Black_'s opinions, to go
+`# fmt: off` and end with `# fmt: on`, or lines that ends with `# fmt: skip`. Pay
+attention that `# fmt: on/off` have to be on the same level of indentation. To learn
+more about _Black_'s opinions, to go
 [the_black_code_style](https://github.com/psf/black/blob/master/docs/the_black_code_style.md).
 
 Please refer to this document before submitting an issue. What seems like a bug might be
index 6dbb765cf7a488afcf0a1289e1fc681caaa3a9e5..08930d11ceac497a429510e1710219b424eeb028 100644 (file)
@@ -2581,6 +2581,8 @@ def is_split_before_delimiter(leaf: Leaf, previous: Optional[Leaf] = None) -> Pr
 
 
 FMT_OFF = {"# fmt: off", "# fmt:off", "# yapf: disable"}
+FMT_SKIP = {"# fmt: skip", "# fmt:skip"}
+FMT_PASS = {*FMT_OFF, *FMT_SKIP}
 FMT_ON = {"# fmt: on", "# fmt:on", "# yapf: enable"}
 
 
@@ -5404,58 +5406,80 @@ def convert_one_fmt_off_pair(node: Node) -> bool:
     for leaf in node.leaves():
         previous_consumed = 0
         for comment in list_comments(leaf.prefix, is_endmarker=False):
-            if comment.value in FMT_OFF:
-                # We only want standalone comments. If there's no previous leaf or
-                # the previous leaf is indentation, it's a standalone comment in
-                # disguise.
-                if comment.type != STANDALONE_COMMENT:
-                    prev = preceding_leaf(leaf)
-                    if prev and prev.type not in WHITESPACE:
+            if comment.value not in FMT_PASS:
+                previous_consumed = comment.consumed
+                continue
+            # We only want standalone comments. If there's no previous leaf or
+            # the previous leaf is indentation, it's a standalone comment in
+            # disguise.
+            if comment.value in FMT_PASS and comment.type != STANDALONE_COMMENT:
+                prev = preceding_leaf(leaf)
+                if prev:
+                    if comment.value in FMT_OFF and prev.type not in WHITESPACE:
+                        continue
+                    if comment.value in FMT_SKIP and prev.type in WHITESPACE:
                         continue
 
-                ignored_nodes = list(generate_ignored_nodes(leaf))
-                if not ignored_nodes:
-                    continue
-
-                first = ignored_nodes[0]  # Can be a container node with the `leaf`.
-                parent = first.parent
-                prefix = first.prefix
-                first.prefix = prefix[comment.consumed :]
-                hidden_value = (
-                    comment.value + "\n" + "".join(str(n) for n in ignored_nodes)
-                )
-                if hidden_value.endswith("\n"):
-                    # That happens when one of the `ignored_nodes` ended with a NEWLINE
-                    # leaf (possibly followed by a DEDENT).
-                    hidden_value = hidden_value[:-1]
-                first_idx: Optional[int] = None
-                for ignored in ignored_nodes:
-                    index = ignored.remove()
-                    if first_idx is None:
-                        first_idx = index
-                assert parent is not None, "INTERNAL ERROR: fmt: on/off handling (1)"
-                assert first_idx is not None, "INTERNAL ERROR: fmt: on/off handling (2)"
-                parent.insert_child(
-                    first_idx,
-                    Leaf(
-                        STANDALONE_COMMENT,
-                        hidden_value,
-                        prefix=prefix[:previous_consumed] + "\n" * comment.newlines,
-                    ),
-                )
-                return True
+            ignored_nodes = list(generate_ignored_nodes(leaf, comment))
+            if not ignored_nodes:
+                continue
 
-            previous_consumed = comment.consumed
+            first = ignored_nodes[0]  # Can be a container node with the `leaf`.
+            parent = first.parent
+            prefix = first.prefix
+            first.prefix = prefix[comment.consumed :]
+            hidden_value = "".join(str(n) for n in ignored_nodes)
+            if comment.value in FMT_OFF:
+                hidden_value = comment.value + "\n" + hidden_value
+            if comment.value in FMT_SKIP:
+                hidden_value += "  " + comment.value
+            if hidden_value.endswith("\n"):
+                # That happens when one of the `ignored_nodes` ended with a NEWLINE
+                # leaf (possibly followed by a DEDENT).
+                hidden_value = hidden_value[:-1]
+            first_idx: Optional[int] = None
+            for ignored in ignored_nodes:
+                index = ignored.remove()
+                if first_idx is None:
+                    first_idx = index
+            assert parent is not None, "INTERNAL ERROR: fmt: on/off handling (1)"
+            assert first_idx is not None, "INTERNAL ERROR: fmt: on/off handling (2)"
+            parent.insert_child(
+                first_idx,
+                Leaf(
+                    STANDALONE_COMMENT,
+                    hidden_value,
+                    prefix=prefix[:previous_consumed] + "\n" * comment.newlines,
+                ),
+            )
+            return True
 
     return False
 
 
-def generate_ignored_nodes(leaf: Leaf) -> Iterator[LN]:
+def generate_ignored_nodes(leaf: Leaf, comment: ProtoComment) -> Iterator[LN]:
     """Starting from the container of `leaf`, generate all leaves until `# fmt: on`.
 
+    If comment is skip, returns leaf only.
     Stops at the end of the block.
     """
     container: Optional[LN] = container_of(leaf)
+    if comment.value in FMT_SKIP:
+        prev_sibling = leaf.prev_sibling
+        if comment.value in leaf.prefix and prev_sibling is not None:
+            leaf.prefix = leaf.prefix.replace(comment.value, "")
+            siblings = [prev_sibling]
+            while (
+                "\n" not in prev_sibling.prefix
+                and prev_sibling.prev_sibling is not None
+            ):
+                prev_sibling = prev_sibling.prev_sibling
+                siblings.insert(0, prev_sibling)
+            for sibling in siblings:
+                yield sibling
+        elif leaf.parent is not None:
+            yield leaf.parent
+        return
     while container is not None and container.type != token.ENDMARKER:
         if is_fmt_on(container):
             return
diff --git a/tests/data/fmtskip.py b/tests/data/fmtskip.py
new file mode 100644 (file)
index 0000000..1d5836f
--- /dev/null
@@ -0,0 +1,3 @@
+a, b = 1, 2
+c =    6  # fmt: skip
+d = 5
diff --git a/tests/data/fmtskip2.py b/tests/data/fmtskip2.py
new file mode 100644 (file)
index 0000000..e624811
--- /dev/null
@@ -0,0 +1,17 @@
+l1 = ["This list should be broken up", "into multiple lines", "because it is way too long"]
+l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"]  # fmt: skip
+l3 = ["I have", "trailing comma", "so I should be braked",]
+
+# output
+
+l1 = [
+    "This list should be broken up",
+    "into multiple lines",
+    "because it is way too long",
+]
+l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"]  # fmt: skip
+l3 = [
+    "I have",
+    "trailing comma",
+    "so I should be braked",
+]
\ No newline at end of file
diff --git a/tests/data/fmtskip3.py b/tests/data/fmtskip3.py
new file mode 100644 (file)
index 0000000..6e16688
--- /dev/null
@@ -0,0 +1,20 @@
+a  =    3
+# fmt: off
+b,    c = 1, 2
+d =    6  # fmt: skip
+e = 5
+# fmt: on
+f = ["This is a very long line that should be formatted into a clearer line ", "by rearranging."]
+
+# output
+
+a = 3
+# fmt: off
+b,    c = 1, 2
+d =    6  # fmt: skip
+e = 5
+# fmt: on
+f = [
+    "This is a very long line that should be formatted into a clearer line ",
+    "by rearranging.",
+]
diff --git a/tests/data/fmtskip4.py b/tests/data/fmtskip4.py
new file mode 100644 (file)
index 0000000..aadd77d
--- /dev/null
@@ -0,0 +1,13 @@
+a =   2
+# fmt: skip
+l = [1, 2, 3,]
+
+# output
+
+a = 2
+# fmt: skip
+l = [
+    1,
+    2,
+    3,
+]
\ No newline at end of file
diff --git a/tests/data/fmtskip5.py b/tests/data/fmtskip5.py
new file mode 100644 (file)
index 0000000..d7b15e0
--- /dev/null
@@ -0,0 +1,22 @@
+a, b, c = 3, 4,       5
+if (
+    a ==    3
+    and b    != 9  # fmt: skip
+    and c is not None
+):
+    print("I'm good!")
+else:
+    print("I'm bad")
+
+
+# output
+
+a, b, c = 3, 4, 5
+if (
+    a == 3
+    and b    != 9  # fmt: skip
+    and c is not None
+):
+    print("I'm good!")
+else:
+    print("I'm bad")
index e4677707e3ca5091d6f8d111f6cb33559ecc6cdb..e0cb0b74195792a30f06b3ac63646bcb80ab64a1 100644 (file)
@@ -37,6 +37,11 @@ SIMPLE_CASES = [
     "fmtonoff2",
     "fmtonoff3",
     "fmtonoff4",
+    "fmtskip",
+    "fmtskip2",
+    "fmtskip3",
+    "fmtskip4",
+    "fmtskip5",
     "fstring",
     "function",
     "function2",