diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py index 200406fa85c33..7b6db24c03314 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py @@ -467,3 +467,37 @@ def foo(): # e ): pass + + +match pattern_match_or: + case ( # leading 1 + a # trailing 1 + # own line 1 + | # trailing 2 + # own line 2 + b # trailing 3 + # own line 3 + | # trailing 4 + # own line 4 + c # trailing 5 + ): + ... + + case ( + (a) + | # trailing + ( b ) + ): + ... + + case (a|b|c): + ... + + case foo | bar | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh: + ... + + case ( # end of line + a | b + # own line + ): + ... diff --git a/crates/ruff_python_formatter/src/lib.rs b/crates/ruff_python_formatter/src/lib.rs index b75b38eaf63ac..17193b8444b2c 100644 --- a/crates/ruff_python_formatter/src/lib.rs +++ b/crates/ruff_python_formatter/src/lib.rs @@ -1,15 +1,13 @@ use thiserror::Error; -use ruff_formatter::format_element::tag; use ruff_formatter::prelude::*; use ruff_formatter::{format, FormatError, Formatted, PrintError, Printed, SourceCode}; -use ruff_python_ast::node::{AnyNodeRef, AstNode}; +use ruff_python_ast::node::AstNode; use ruff_python_ast::Mod; use ruff_python_index::{CommentRanges, CommentRangesBuilder}; use ruff_python_parser::lexer::{lex, LexicalError}; use ruff_python_parser::{parse_tokens, Mode, ParseError}; use ruff_source_file::Locator; -use ruff_text_size::TextLen; use crate::comments::{ dangling_comments, leading_comments, trailing_comments, Comments, SourceComment, @@ -178,45 +176,6 @@ pub fn pretty_comments(formatted: &Formatted, source: &str) -> ) } -pub(crate) struct NotYetImplementedCustomText<'a> { - text: &'static str, - node: AnyNodeRef<'a>, -} - -/// Formats a placeholder for nodes that have not yet been implemented -pub(crate) fn not_yet_implemented_custom_text<'a, T>( - text: &'static str, - node: T, -) -> NotYetImplementedCustomText<'a> -where - T: Into>, -{ - NotYetImplementedCustomText { - text, - node: node.into(), - } -} - -impl Format> for NotYetImplementedCustomText<'_> { - fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> { - f.write_element(FormatElement::Tag(Tag::StartVerbatim( - tag::VerbatimKind::Verbatim { - length: self.text.text_len(), - }, - ))); - - text(self.text).fmt(f)?; - - f.write_element(FormatElement::Tag(Tag::EndVerbatim)); - - f.context() - .comments() - .mark_verbatim_node_comments_formatted(self.node); - - Ok(()) - } -} - #[cfg(test)] mod tests { use std::path::Path; diff --git a/crates/ruff_python_formatter/src/pattern/pattern_match_or.rs b/crates/ruff_python_formatter/src/pattern/pattern_match_or.rs index 847c7597d7ff9..8d84a240e9337 100644 --- a/crates/ruff_python_formatter/src/pattern/pattern_match_or.rs +++ b/crates/ruff_python_formatter/src/pattern/pattern_match_or.rs @@ -2,8 +2,10 @@ use ruff_formatter::write; use ruff_python_ast::node::AnyNodeRef; use ruff_python_ast::PatternMatchOr; -use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; -use crate::not_yet_implemented_custom_text; +use crate::comments::leading_comments; +use crate::expression::parentheses::{ + in_parentheses_only_soft_line_break_or_space, NeedsParentheses, OptionalParentheses, +}; use crate::prelude::*; #[derive(Default)] @@ -11,13 +13,35 @@ pub struct FormatPatternMatchOr; impl FormatNodeRule for FormatPatternMatchOr { fn fmt_fields(&self, item: &PatternMatchOr, f: &mut PyFormatter) -> FormatResult<()> { - write!( - f, - [not_yet_implemented_custom_text( - "NOT_YET_IMPLEMENTED_PatternMatchOf | (y)", - item - )] - ) + let PatternMatchOr { range: _, patterns } = item; + let inner = format_with(|f: &mut PyFormatter| { + let mut patterns = patterns.iter(); + let comments = f.context().comments().clone(); + + let Some(first) = patterns.next() else { + return Ok(()); + }; + + first.format().fmt(f)?; + + for pattern in patterns { + let leading_value_comments = comments.leading(pattern); + // Format the expressions leading comments **before** the operator + if leading_value_comments.is_empty() { + write!(f, [in_parentheses_only_soft_line_break_or_space()])?; + } else { + write!( + f, + [hard_line_break(), leading_comments(leading_value_comments)] + )?; + } + write!(f, [text("|"), space(), pattern.format()])?; + } + + Ok(()) + }); + + inner.fmt(f) } } diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_complex.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_complex.py.snap deleted file mode 100644 index 3a98fded6c9a5..0000000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_complex.py.snap +++ /dev/null @@ -1,507 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_complex.py ---- -## Input - -```py -# Cases sampled from Lib/test/test_patma.py - -# case black_test_patma_098 -match x: - case -0j: - y = 0 -# case black_test_patma_142 -match x: - case bytes(z): - y = 0 -# case black_test_patma_073 -match x: - case 0 if 0: - y = 0 - case 0 if 1: - y = 1 -# case black_test_patma_006 -match 3: - case 0 | 1 | 2 | 3: - x = True -# case black_test_patma_049 -match x: - case [0, 1] | [1, 0]: - y = 0 -# case black_check_sequence_then_mapping -match x: - case [*_]: - return "seq" - case {}: - return "map" -# case black_test_patma_035 -match x: - case {0: [1, 2, {}]}: - y = 0 - case {0: [1, 2, {}] | True} | {1: [[]]} | {0: [1, 2, {}]} | [] | "X" | {}: - y = 1 - case []: - y = 2 -# case black_test_patma_107 -match x: - case 0.25 + 1.75j: - y = 0 -# case black_test_patma_097 -match x: - case -0j: - y = 0 -# case black_test_patma_007 -match 4: - case 0 | 1 | 2 | 3: - x = True -# case black_test_patma_154 -match x: - case 0 if x: - y = 0 -# case black_test_patma_134 -match x: - case {1: 0}: - y = 0 - case {0: 0}: - y = 1 - case {**z}: - y = 2 -# case black_test_patma_185 -match Seq(): - case [*_]: - y = 0 -# case black_test_patma_063 -match x: - case 1: - y = 0 - case 1: - y = 1 -# case black_test_patma_248 -match x: - case {"foo": bar}: - y = bar -# case black_test_patma_019 -match (0, 1, 2): - case [0, 1, *x, 2]: - y = 0 -# case black_test_patma_052 -match x: - case [0]: - y = 0 - case [1, 0] if (x := x[:0]): - y = 1 - case [1, 0]: - y = 2 -# case black_test_patma_191 -match w: - case [x, y, *_]: - z = 0 -# case black_test_patma_110 -match x: - case -0.25 - 1.75j: - y = 0 -# case black_test_patma_151 -match (x,): - case [y]: - z = 0 -# case black_test_patma_114 -match x: - case A.B.C.D: - y = 0 -# case black_test_patma_232 -match x: - case None: - y = 0 -# case black_test_patma_058 -match x: - case 0: - y = 0 -# case black_test_patma_233 -match x: - case False: - y = 0 -# case black_test_patma_078 -match x: - case []: - y = 0 - case [""]: - y = 1 - case "": - y = 2 -# case black_test_patma_156 -match x: - case z: - y = 0 -# case black_test_patma_189 -match w: - case [x, y, *rest]: - z = 0 -# case black_test_patma_042 -match x: - case (0 as z) | (1 as z) | (2 as z) if z == x % 2: - y = 0 -# case black_test_patma_034 -match x: - case {0: [1, 2, {}]}: - y = 0 - case {0: [1, 2, {}] | False} | {1: [[]]} | {0: [1, 2, {}]} | [] | "X" | {}: - y = 1 - case []: - y = 2 -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -16,11 +16,11 @@ - y = 1 - # case black_test_patma_006 - match 3: -- case 0 | 1 | 2 | 3: -+ case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - x = True - # case black_test_patma_049 - match x: -- case [0, 1] | [1, 0]: -+ case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - y = 0 - # case black_check_sequence_then_mapping - match x: -@@ -32,7 +32,7 @@ - match x: - case {0: [1, 2, {}]}: - y = 0 -- case {0: [1, 2, {}] | True} | {1: [[]]} | {0: [1, 2, {}]} | [] | "X" | {}: -+ case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - y = 1 - case []: - y = 2 -@@ -46,7 +46,7 @@ - y = 0 - # case black_test_patma_007 - match 4: -- case 0 | 1 | 2 | 3: -+ case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - x = True - # case black_test_patma_154 - match x: -@@ -132,13 +132,13 @@ - z = 0 - # case black_test_patma_042 - match x: -- case (0 as z) | (1 as z) | (2 as z) if z == x % 2: -+ case NOT_YET_IMPLEMENTED_PatternMatchOf | (y) if z == x % 2: - y = 0 - # case black_test_patma_034 - match x: - case {0: [1, 2, {}]}: - y = 0 -- case {0: [1, 2, {}] | False} | {1: [[]]} | {0: [1, 2, {}]} | [] | "X" | {}: -+ case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - y = 1 - case []: - y = 2 -``` - -## Ruff Output - -```py -# Cases sampled from Lib/test/test_patma.py - -# case black_test_patma_098 -match x: - case -0j: - y = 0 -# case black_test_patma_142 -match x: - case bytes(z): - y = 0 -# case black_test_patma_073 -match x: - case 0 if 0: - y = 0 - case 0 if 1: - y = 1 -# case black_test_patma_006 -match 3: - case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - x = True -# case black_test_patma_049 -match x: - case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - y = 0 -# case black_check_sequence_then_mapping -match x: - case [*_]: - return "seq" - case {}: - return "map" -# case black_test_patma_035 -match x: - case {0: [1, 2, {}]}: - y = 0 - case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - y = 1 - case []: - y = 2 -# case black_test_patma_107 -match x: - case 0.25 + 1.75j: - y = 0 -# case black_test_patma_097 -match x: - case -0j: - y = 0 -# case black_test_patma_007 -match 4: - case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - x = True -# case black_test_patma_154 -match x: - case 0 if x: - y = 0 -# case black_test_patma_134 -match x: - case {1: 0}: - y = 0 - case {0: 0}: - y = 1 - case {**z}: - y = 2 -# case black_test_patma_185 -match Seq(): - case [*_]: - y = 0 -# case black_test_patma_063 -match x: - case 1: - y = 0 - case 1: - y = 1 -# case black_test_patma_248 -match x: - case {"foo": bar}: - y = bar -# case black_test_patma_019 -match (0, 1, 2): - case [0, 1, *x, 2]: - y = 0 -# case black_test_patma_052 -match x: - case [0]: - y = 0 - case [1, 0] if (x := x[:0]): - y = 1 - case [1, 0]: - y = 2 -# case black_test_patma_191 -match w: - case [x, y, *_]: - z = 0 -# case black_test_patma_110 -match x: - case -0.25 - 1.75j: - y = 0 -# case black_test_patma_151 -match (x,): - case [y]: - z = 0 -# case black_test_patma_114 -match x: - case A.B.C.D: - y = 0 -# case black_test_patma_232 -match x: - case None: - y = 0 -# case black_test_patma_058 -match x: - case 0: - y = 0 -# case black_test_patma_233 -match x: - case False: - y = 0 -# case black_test_patma_078 -match x: - case []: - y = 0 - case [""]: - y = 1 - case "": - y = 2 -# case black_test_patma_156 -match x: - case z: - y = 0 -# case black_test_patma_189 -match w: - case [x, y, *rest]: - z = 0 -# case black_test_patma_042 -match x: - case NOT_YET_IMPLEMENTED_PatternMatchOf | (y) if z == x % 2: - y = 0 -# case black_test_patma_034 -match x: - case {0: [1, 2, {}]}: - y = 0 - case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - y = 1 - case []: - y = 2 -``` - -## Black Output - -```py -# Cases sampled from Lib/test/test_patma.py - -# case black_test_patma_098 -match x: - case -0j: - y = 0 -# case black_test_patma_142 -match x: - case bytes(z): - y = 0 -# case black_test_patma_073 -match x: - case 0 if 0: - y = 0 - case 0 if 1: - y = 1 -# case black_test_patma_006 -match 3: - case 0 | 1 | 2 | 3: - x = True -# case black_test_patma_049 -match x: - case [0, 1] | [1, 0]: - y = 0 -# case black_check_sequence_then_mapping -match x: - case [*_]: - return "seq" - case {}: - return "map" -# case black_test_patma_035 -match x: - case {0: [1, 2, {}]}: - y = 0 - case {0: [1, 2, {}] | True} | {1: [[]]} | {0: [1, 2, {}]} | [] | "X" | {}: - y = 1 - case []: - y = 2 -# case black_test_patma_107 -match x: - case 0.25 + 1.75j: - y = 0 -# case black_test_patma_097 -match x: - case -0j: - y = 0 -# case black_test_patma_007 -match 4: - case 0 | 1 | 2 | 3: - x = True -# case black_test_patma_154 -match x: - case 0 if x: - y = 0 -# case black_test_patma_134 -match x: - case {1: 0}: - y = 0 - case {0: 0}: - y = 1 - case {**z}: - y = 2 -# case black_test_patma_185 -match Seq(): - case [*_]: - y = 0 -# case black_test_patma_063 -match x: - case 1: - y = 0 - case 1: - y = 1 -# case black_test_patma_248 -match x: - case {"foo": bar}: - y = bar -# case black_test_patma_019 -match (0, 1, 2): - case [0, 1, *x, 2]: - y = 0 -# case black_test_patma_052 -match x: - case [0]: - y = 0 - case [1, 0] if (x := x[:0]): - y = 1 - case [1, 0]: - y = 2 -# case black_test_patma_191 -match w: - case [x, y, *_]: - z = 0 -# case black_test_patma_110 -match x: - case -0.25 - 1.75j: - y = 0 -# case black_test_patma_151 -match (x,): - case [y]: - z = 0 -# case black_test_patma_114 -match x: - case A.B.C.D: - y = 0 -# case black_test_patma_232 -match x: - case None: - y = 0 -# case black_test_patma_058 -match x: - case 0: - y = 0 -# case black_test_patma_233 -match x: - case False: - y = 0 -# case black_test_patma_078 -match x: - case []: - y = 0 - case [""]: - y = 1 - case "": - y = 2 -# case black_test_patma_156 -match x: - case z: - y = 0 -# case black_test_patma_189 -match w: - case [x, y, *rest]: - z = 0 -# case black_test_patma_042 -match x: - case (0 as z) | (1 as z) | (2 as z) if z == x % 2: - y = 0 -# case black_test_patma_034 -match x: - case {0: [1, 2, {}]}: - y = 0 - case {0: [1, 2, {}] | False} | {1: [[]]} | {0: [1, 2, {}]} | [] | "X" | {}: - y = 1 - case []: - y = 2 -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_extras.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_extras.py.snap index 8d5c61422e436..3f7ef28e907cb 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_extras.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_extras.py.snap @@ -158,24 +158,6 @@ match bar1: pass case _: pass -@@ -87,7 +96,7 @@ - match something: - case { - "key": key as key_1, -- "password": PASS.ONE | PASS.TWO | PASS.THREE as password, -+ "password": NOT_YET_IMPLEMENTED_PatternMatchOf | (y) as password, - }: - pass - case {"maybe": something(complicated as this) as that}: -@@ -101,7 +110,7 @@ - case 2 as b, 3 as c: - pass - -- case 4 as d, (5 as e), (6 | 7 as g), *h: -+ case 4 as d, (5 as e), (NOT_YET_IMPLEMENTED_PatternMatchOf | (y) as g), *h: - pass - - ``` ## Ruff Output @@ -279,7 +261,7 @@ match a, *b(), c: match something: case { "key": key as key_1, - "password": NOT_YET_IMPLEMENTED_PatternMatchOf | (y) as password, + "password": PASS.ONE | PASS.TWO | PASS.THREE as password, }: pass case {"maybe": something(complicated as this) as that}: @@ -293,7 +275,7 @@ match something: case 2 as b, 3 as c: pass - case 4 as d, (5 as e), (NOT_YET_IMPLEMENTED_PatternMatchOf | (y) as g), *h: + case 4 as d, (5 as e), (6 | 7 as g), *h: pass diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_simple.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_simple.py.snap deleted file mode 100644 index a3525af0b89bd..0000000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_simple.py.snap +++ /dev/null @@ -1,335 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_simple.py ---- -## Input - -```py -# Cases sampled from PEP 636 examples - -match command.split(): - case [action, obj]: - ... # interpret action, obj - -match command.split(): - case [action]: - ... # interpret single-verb action - case [action, obj]: - ... # interpret action, obj - -match command.split(): - case ["quit"]: - print("Goodbye!") - quit_game() - case ["look"]: - current_room.describe() - case ["get", obj]: - character.get(obj, current_room) - case ["go", direction]: - current_room = current_room.neighbor(direction) - # The rest of your commands go here - -match command.split(): - case ["drop", *objects]: - for obj in objects: - character.drop(obj, current_room) - # The rest of your commands go here - -match command.split(): - case ["quit"]: - pass - case ["go", direction]: - print("Going:", direction) - case ["drop", *objects]: - print("Dropping: ", *objects) - case _: - print(f"Sorry, I couldn't understand {command!r}") - -match command.split(): - case ["north"] | ["go", "north"]: - current_room = current_room.neighbor("north") - case ["get", obj] | ["pick", "up", obj] | ["pick", obj, "up"]: - ... # Code for picking up the given object - -match command.split(): - case ["go", ("north" | "south" | "east" | "west")]: - current_room = current_room.neighbor(...) - # how do I know which direction to go? - -match command.split(): - case ["go", ("north" | "south" | "east" | "west") as direction]: - current_room = current_room.neighbor(direction) - -match command.split(): - case ["go", direction] if direction in current_room.exits: - current_room = current_room.neighbor(direction) - case ["go", _]: - print("Sorry, you can't go that way") - -match event.get(): - case Click(position=(x, y)): - handle_click_at(x, y) - case KeyPress(key_name="Q") | Quit(): - game.quit() - case KeyPress(key_name="up arrow"): - game.go_north() - case KeyPress(): - pass # Ignore other keystrokes - case other_event: - raise ValueError(f"Unrecognized event: {other_event}") - -match event.get(): - case Click((x, y), button=Button.LEFT): # This is a left click - handle_click_at(x, y) - case Click(): - pass # ignore other clicks - - -def where_is(point): - match point: - case Point(x=0, y=0): - print("Origin") - case Point(x=0, y=y): - print(f"Y={y}") - case Point(x=x, y=0): - print(f"X={x}") - case Point(): - print("Somewhere else") - case _: - print("Not a point") -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -39,18 +39,18 @@ - print(f"Sorry, I couldn't understand {command!r}") - - match command.split(): -- case ["north"] | ["go", "north"]: -+ case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - current_room = current_room.neighbor("north") -- case ["get", obj] | ["pick", "up", obj] | ["pick", obj, "up"]: -+ case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - ... # Code for picking up the given object - - match command.split(): -- case ["go", ("north" | "south" | "east" | "west")]: -+ case ["go", (NOT_YET_IMPLEMENTED_PatternMatchOf | (y))]: - current_room = current_room.neighbor(...) - # how do I know which direction to go? - - match command.split(): -- case ["go", ("north" | "south" | "east" | "west") as direction]: -+ case ["go", (NOT_YET_IMPLEMENTED_PatternMatchOf | (y)) as direction]: - current_room = current_room.neighbor(direction) - - match command.split(): -@@ -62,7 +62,7 @@ - match event.get(): - case Click(position=(x, y)): - handle_click_at(x, y) -- case KeyPress(key_name="Q") | Quit(): -+ case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - game.quit() - case KeyPress(key_name="up arrow"): - game.go_north() -``` - -## Ruff Output - -```py -# Cases sampled from PEP 636 examples - -match command.split(): - case [action, obj]: - ... # interpret action, obj - -match command.split(): - case [action]: - ... # interpret single-verb action - case [action, obj]: - ... # interpret action, obj - -match command.split(): - case ["quit"]: - print("Goodbye!") - quit_game() - case ["look"]: - current_room.describe() - case ["get", obj]: - character.get(obj, current_room) - case ["go", direction]: - current_room = current_room.neighbor(direction) - # The rest of your commands go here - -match command.split(): - case ["drop", *objects]: - for obj in objects: - character.drop(obj, current_room) - # The rest of your commands go here - -match command.split(): - case ["quit"]: - pass - case ["go", direction]: - print("Going:", direction) - case ["drop", *objects]: - print("Dropping: ", *objects) - case _: - print(f"Sorry, I couldn't understand {command!r}") - -match command.split(): - case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - current_room = current_room.neighbor("north") - case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - ... # Code for picking up the given object - -match command.split(): - case ["go", (NOT_YET_IMPLEMENTED_PatternMatchOf | (y))]: - current_room = current_room.neighbor(...) - # how do I know which direction to go? - -match command.split(): - case ["go", (NOT_YET_IMPLEMENTED_PatternMatchOf | (y)) as direction]: - current_room = current_room.neighbor(direction) - -match command.split(): - case ["go", direction] if direction in current_room.exits: - current_room = current_room.neighbor(direction) - case ["go", _]: - print("Sorry, you can't go that way") - -match event.get(): - case Click(position=(x, y)): - handle_click_at(x, y) - case NOT_YET_IMPLEMENTED_PatternMatchOf | (y): - game.quit() - case KeyPress(key_name="up arrow"): - game.go_north() - case KeyPress(): - pass # Ignore other keystrokes - case other_event: - raise ValueError(f"Unrecognized event: {other_event}") - -match event.get(): - case Click((x, y), button=Button.LEFT): # This is a left click - handle_click_at(x, y) - case Click(): - pass # ignore other clicks - - -def where_is(point): - match point: - case Point(x=0, y=0): - print("Origin") - case Point(x=0, y=y): - print(f"Y={y}") - case Point(x=x, y=0): - print(f"X={x}") - case Point(): - print("Somewhere else") - case _: - print("Not a point") -``` - -## Black Output - -```py -# Cases sampled from PEP 636 examples - -match command.split(): - case [action, obj]: - ... # interpret action, obj - -match command.split(): - case [action]: - ... # interpret single-verb action - case [action, obj]: - ... # interpret action, obj - -match command.split(): - case ["quit"]: - print("Goodbye!") - quit_game() - case ["look"]: - current_room.describe() - case ["get", obj]: - character.get(obj, current_room) - case ["go", direction]: - current_room = current_room.neighbor(direction) - # The rest of your commands go here - -match command.split(): - case ["drop", *objects]: - for obj in objects: - character.drop(obj, current_room) - # The rest of your commands go here - -match command.split(): - case ["quit"]: - pass - case ["go", direction]: - print("Going:", direction) - case ["drop", *objects]: - print("Dropping: ", *objects) - case _: - print(f"Sorry, I couldn't understand {command!r}") - -match command.split(): - case ["north"] | ["go", "north"]: - current_room = current_room.neighbor("north") - case ["get", obj] | ["pick", "up", obj] | ["pick", obj, "up"]: - ... # Code for picking up the given object - -match command.split(): - case ["go", ("north" | "south" | "east" | "west")]: - current_room = current_room.neighbor(...) - # how do I know which direction to go? - -match command.split(): - case ["go", ("north" | "south" | "east" | "west") as direction]: - current_room = current_room.neighbor(direction) - -match command.split(): - case ["go", direction] if direction in current_room.exits: - current_room = current_room.neighbor(direction) - case ["go", _]: - print("Sorry, you can't go that way") - -match event.get(): - case Click(position=(x, y)): - handle_click_at(x, y) - case KeyPress(key_name="Q") | Quit(): - game.quit() - case KeyPress(key_name="up arrow"): - game.go_north() - case KeyPress(): - pass # Ignore other keystrokes - case other_event: - raise ValueError(f"Unrecognized event: {other_event}") - -match event.get(): - case Click((x, y), button=Button.LEFT): # This is a left click - handle_click_at(x, y) - case Click(): - pass # ignore other clicks - - -def where_is(point): - match point: - case Point(x=0, y=0): - print("Origin") - case Point(x=0, y=y): - print(f"Y={y}") - case Point(x=x, y=0): - print(f"X={x}") - case Point(): - print("Somewhere else") - case _: - print("Not a point") -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap index bd9f9131a112c..8a23c2f6d7be8 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap @@ -473,6 +473,40 @@ match pattern_match_class: # e ): pass + + +match pattern_match_or: + case ( # leading 1 + a # trailing 1 + # own line 1 + | # trailing 2 + # own line 2 + b # trailing 3 + # own line 3 + | # trailing 4 + # own line 4 + c # trailing 5 + ): + ... + + case ( + (a) + | # trailing + ( b ) + ): + ... + + case (a|b|c): + ... + + case foo | bar | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh: + ... + + case ( # end of line + a | b + # own line + ): + ... ``` ## Output @@ -966,6 +1000,44 @@ match pattern_match_class: # e ): pass + + +match pattern_match_or: + case ( # leading 1 + a # trailing 1 # trailing 2 + # own line 1 + # own line 2 + | b # trailing 3 # trailing 4 + # own line 3 + # own line 4 + | c # trailing 5 + ): + ... + + case ( + ( + a # trailing + ) + | (b) + ): + ... + + case a | b | c: + ... + + case ( + foo + | bar + | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh + ): + ... + + case ( # end of line + a + | b + # own line + ): + ... ```