diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/slice.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/slice.py index bbdfe19010..3b30f67cd4 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/slice.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/slice.py @@ -70,12 +70,15 @@ def a(): e00 = "e"[:] e01 = "e"[:1] e02 = "e"[: a()] +e03 = "e"[:-1] e10 = "e"[1:] e11 = "e"[1:1] e12 = "e"[1 : a()] +e13 = "e"[1:-1] e20 = "e"[a() :] e21 = "e"[a() : 1] e22 = "e"[a() : a()] +e23 = "e"[a() : -1] e200 = "e"[a() :: ] e201 = "e"[a() :: 1] e202 = "e"[a() :: a()] diff --git a/crates/ruff_python_formatter/src/expression/expr_slice.rs b/crates/ruff_python_formatter/src/expression/expr_slice.rs index bf06d7e89a..2423e74cfe 100644 --- a/crates/ruff_python_formatter/src/expression/expr_slice.rs +++ b/crates/ruff_python_formatter/src/expression/expr_slice.rs @@ -1,6 +1,5 @@ use ruff_text_size::TextRange; -use rustpython_parser::ast::ExprSlice; -use rustpython_parser::ast::{Expr, Ranged}; +use rustpython_parser::ast::{Expr, ExprSlice, ExprUnaryOp, Ranged, UnaryOp}; use ruff_formatter::prelude::{hard_line_break, line_suffix_boundary, space, text}; use ruff_formatter::{write, Buffer, Format, FormatError, FormatResult}; @@ -87,7 +86,7 @@ impl FormatNodeRule for FormatExprSlice { // e201 = "e"[a() :: 1] // e202 = "e"[a() :: a()] // ``` - if !all_simple { + if !all_simple && lower.is_some() { space().fmt(f)?; } text(":").fmt(f)?; @@ -196,7 +195,17 @@ pub(crate) fn find_colons( /// Determines whether this expression needs a space around the colon /// fn is_simple_expr(expr: &Expr) -> bool { - matches!(expr, Expr::Constant(_) | Expr::Name(_)) + // Unary op expressions except `not` can be simple. + if let Some(ExprUnaryOp { + op: UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert, + operand, + .. + }) = expr.as_unary_op_expr() + { + is_simple_expr(operand) + } else { + matches!(expr, Expr::Constant(_) | Expr::Name(_)) + } } pub(crate) enum ExprSliceCommentSection { diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap index f548f665b3..86b8c8ddbe 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap @@ -323,32 +323,6 @@ last_call() ) # note: no trailing comma pre-3.6 call(*gidgets[:2]) call(a, *gidgets[:2]) -@@ -152,13 +159,13 @@ - slice[0:1] - slice[0:1:2] - slice[:] --slice[:-1] -+slice[ : -1] - slice[1:] --slice[::-1] -+slice[ :: -1] - slice[d :: d + 1] - slice[:c, c - 1] - numpy[:, 0:1] --numpy[:, :-1] -+numpy[:, : -1] - numpy[0, :] - numpy[:, i] - numpy[0, :2] -@@ -172,7 +179,7 @@ - numpy[1 : c + 1, c] - numpy[-(c + 1) :, d] - numpy[:, l[-2]] --numpy[:, ::-1] -+numpy[:, :: -1] - numpy[np.newaxis, :] - (str or None) if (sys.version_info[0] > (3,)) else (str or bytes or None) - {"2.7": dead, "3.7": long_live or die_hard} @@ -207,25 +214,15 @@ ) what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set( @@ -596,13 +570,13 @@ slice[0] slice[0:1] slice[0:1:2] slice[:] -slice[ : -1] +slice[:-1] slice[1:] -slice[ :: -1] +slice[::-1] slice[d :: d + 1] slice[:c, c - 1] numpy[:, 0:1] -numpy[:, : -1] +numpy[:, :-1] numpy[0, :] numpy[:, i] numpy[0, :2] @@ -616,7 +590,7 @@ numpy[:, [i]] numpy[1 : c + 1, c] numpy[-(c + 1) :, d] numpy[:, l[-2]] -numpy[:, :: -1] +numpy[:, ::-1] numpy[np.newaxis, :] (str or None) if (sys.version_info[0] > (3,)) else (str or bytes or None) {"2.7": dead, "3.7": long_live or die_hard} diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap index 2bf9b9c919..c291a6d4e0 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap @@ -211,7 +211,7 @@ d={'a':1, # Comment 1 # Comment 2 -@@ -17,30 +16,44 @@ +@@ -17,26 +16,40 @@ # fmt: off def func_no_args(): @@ -264,17 +264,12 @@ d={'a':1, + debug: bool = False, + **kwargs, +) -> str: -+ return text[number : -1] ++ return text[number:-1] + + # fmt: on def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""): offset = attr.ib(default=attr.Factory(lambda: _r.uniform(1, 2))) -- assert task._cancel_stack[: len(old_stack)] == old_stack -+ assert task._cancel_stack[ : len(old_stack)] == old_stack - - - def spaces_types( @@ -63,15 +76,15 @@ something = { @@ -445,13 +440,13 @@ def function_signature_stress_test( debug: bool = False, **kwargs, ) -> str: - return text[number : -1] + return text[number:-1] # fmt: on def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""): offset = attr.ib(default=attr.Factory(lambda: _r.uniform(1, 2))) - assert task._cancel_stack[ : len(old_stack)] == old_stack + assert task._cancel_stack[: len(old_stack)] == old_stack def spaces_types( diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__function.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__function.py.snap index 4c89da9f9b..b5fdcc6e94 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__function.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__function.py.snap @@ -117,21 +117,6 @@ def __await__(): return (yield) def func_no_args(): -@@ -41,12 +40,12 @@ - debug: bool = False, - **kwargs, - ) -> str: -- return text[number:-1] -+ return text[number : -1] - - - def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""): - offset = attr.ib(default=attr.Factory(lambda: _r.uniform(10000, 200000))) -- assert task._cancel_stack[: len(old_stack)] == old_stack -+ assert task._cancel_stack[ : len(old_stack)] == old_stack - - - def spaces_types( @@ -65,18 +64,14 @@ def spaces2(result=_core.Value(None)): @@ -220,12 +205,12 @@ def function_signature_stress_test( debug: bool = False, **kwargs, ) -> str: - return text[number : -1] + return text[number:-1] def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""): offset = attr.ib(default=attr.Factory(lambda: _r.uniform(10000, 200000))) - assert task._cancel_stack[ : len(old_stack)] == old_stack + assert task._cancel_stack[: len(old_stack)] == old_stack def spaces_types( diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__slices.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__slices.py.snap deleted file mode 100644 index 2aac3024f5..0000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__slices.py.snap +++ /dev/null @@ -1,151 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/slices.py ---- -## Input - -```py -slice[a.b : c.d] -slice[d :: d + 1] -slice[d + 1 :: d] -slice[d::d] -slice[0] -slice[-1] -slice[:-1] -slice[::-1] -slice[:c, c - 1] -slice[c, c + 1, d::] -slice[ham[c::d] :: 1] -slice[ham[cheese**2 : -1] : 1 : 1, ham[1:2]] -slice[:-1:] -slice[lambda: None : lambda: None] -slice[lambda x, y, *args, really=2, **kwargs: None :, None::] -slice[1 or 2 : True and False] -slice[not so_simple : 1 < val <= 10] -slice[(1 for i in range(42)) : x] -slice[:: [i for i in range(42)]] - - -async def f(): - slice[await x : [i async for i in arange(42)] : 42] - - -# These are from PEP-8: -ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] -ham[lower:upper], ham[lower:upper:], ham[lower::step] -# ham[lower+offset : upper+offset] -ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] -ham[lower + offset : upper + offset] -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -4,19 +4,19 @@ - slice[d::d] - slice[0] - slice[-1] --slice[:-1] --slice[::-1] -+slice[ : -1] -+slice[ :: -1] - slice[:c, c - 1] - slice[c, c + 1, d::] - slice[ham[c::d] :: 1] - slice[ham[cheese**2 : -1] : 1 : 1, ham[1:2]] --slice[:-1:] -+slice[ : -1 :] - slice[lambda: None : lambda: None] - slice[lambda x, y, *args, really=2, **kwargs: None :, None::] - slice[1 or 2 : True and False] - slice[not so_simple : 1 < val <= 10] - slice[(1 for i in range(42)) : x] --slice[:: [i for i in range(42)]] -+slice[ :: [i for i in range(42)]] - - - async def f(): -@@ -27,5 +27,5 @@ - ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] - ham[lower:upper], ham[lower:upper:], ham[lower::step] - # ham[lower+offset : upper+offset] --ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] -+ham[ : upper_fn(x) : step_fn(x)], ham[ :: step_fn(x)] - ham[lower + offset : upper + offset] -``` - -## Ruff Output - -```py -slice[a.b : c.d] -slice[d :: d + 1] -slice[d + 1 :: d] -slice[d::d] -slice[0] -slice[-1] -slice[ : -1] -slice[ :: -1] -slice[:c, c - 1] -slice[c, c + 1, d::] -slice[ham[c::d] :: 1] -slice[ham[cheese**2 : -1] : 1 : 1, ham[1:2]] -slice[ : -1 :] -slice[lambda: None : lambda: None] -slice[lambda x, y, *args, really=2, **kwargs: None :, None::] -slice[1 or 2 : True and False] -slice[not so_simple : 1 < val <= 10] -slice[(1 for i in range(42)) : x] -slice[ :: [i for i in range(42)]] - - -async def f(): - slice[await x : [i async for i in arange(42)] : 42] - - -# These are from PEP-8: -ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] -ham[lower:upper], ham[lower:upper:], ham[lower::step] -# ham[lower+offset : upper+offset] -ham[ : upper_fn(x) : step_fn(x)], ham[ :: step_fn(x)] -ham[lower + offset : upper + offset] -``` - -## Black Output - -```py -slice[a.b : c.d] -slice[d :: d + 1] -slice[d + 1 :: d] -slice[d::d] -slice[0] -slice[-1] -slice[:-1] -slice[::-1] -slice[:c, c - 1] -slice[c, c + 1, d::] -slice[ham[c::d] :: 1] -slice[ham[cheese**2 : -1] : 1 : 1, ham[1:2]] -slice[:-1:] -slice[lambda: None : lambda: None] -slice[lambda x, y, *args, really=2, **kwargs: None :, None::] -slice[1 or 2 : True and False] -slice[not so_simple : 1 < val <= 10] -slice[(1 for i in range(42)) : x] -slice[:: [i for i in range(42)]] - - -async def f(): - slice[await x : [i async for i in arange(42)] : 42] - - -# These are from PEP-8: -ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] -ham[lower:upper], ham[lower:upper:], ham[lower::step] -# ham[lower+offset : upper+offset] -ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] -ham[lower + offset : upper + offset] -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__slice.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__slice.py.snap index a50771e6fb..482cd452c1 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__slice.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__slice.py.snap @@ -76,12 +76,15 @@ def a(): e00 = "e"[:] e01 = "e"[:1] e02 = "e"[: a()] +e03 = "e"[:-1] e10 = "e"[1:] e11 = "e"[1:1] e12 = "e"[1 : a()] +e13 = "e"[1:-1] e20 = "e"[a() :] e21 = "e"[a() : 1] e22 = "e"[a() : a()] +e23 = "e"[a() : -1] e200 = "e"[a() :: ] e201 = "e"[a() :: 1] e202 = "e"[a() :: a()] @@ -167,13 +170,16 @@ def a(): e00 = "e"[:] e01 = "e"[:1] -e02 = "e"[ : a()] +e02 = "e"[: a()] +e03 = "e"[:-1] e10 = "e"[1:] e11 = "e"[1:1] e12 = "e"[1 : a()] +e13 = "e"[1:-1] e20 = "e"[a() :] e21 = "e"[a() : 1] e22 = "e"[a() : a()] +e23 = "e"[a() : -1] e200 = "e"[a() : :] e201 = "e"[a() :: 1] e202 = "e"[a() :: a()]