mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 23:37:56 +03:00
feat(formatter): implement outdent formatting for multiline lists and records
This commit is contained in:
parent
3d9daed836
commit
b147890b08
@ -191,12 +191,26 @@ pub fn fmt_body<'a, 'buf>(
|
||||
buf.push_str(" =");
|
||||
if body.is_multiline() {
|
||||
match body {
|
||||
Expr::SpaceBefore(_, _) => {
|
||||
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
|
||||
}
|
||||
Expr::Record { .. } | Expr::List { .. } => {
|
||||
buf.newline();
|
||||
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
|
||||
Expr::SpaceBefore(sub_def, spaces) => {
|
||||
let should_outdent = match sub_def {
|
||||
Expr::Record { .. } | Expr::List { .. } => {
|
||||
let is_only_newlines = spaces.iter().all(|s| s.is_newline());
|
||||
is_only_newlines && sub_def.is_multiline()
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if should_outdent {
|
||||
buf.spaces(1);
|
||||
sub_def.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||
} else {
|
||||
body.format_with_options(
|
||||
buf,
|
||||
Parens::NotNeeded,
|
||||
Newlines::Yes,
|
||||
indent + INDENT,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
buf.spaces(1);
|
||||
|
@ -186,14 +186,75 @@ impl<'a> Formattable for Expr<'a> {
|
||||
loc_expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
|
||||
|
||||
let multiline_args = loc_args.iter().any(|loc_arg| loc_arg.is_multiline());
|
||||
let last_arg_index = loc_args.len() - 1;
|
||||
|
||||
if multiline_args {
|
||||
let should_outdent_last_arg =
|
||||
loc_args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold(false, |found_multiline_expr, val| {
|
||||
let (index, loc_arg) = val;
|
||||
if index == last_arg_index {
|
||||
match loc_arg.value {
|
||||
SpaceBefore(sub_expr, spaces) => match sub_expr {
|
||||
Record { .. } | List { .. } => {
|
||||
let is_only_newlines =
|
||||
spaces.iter().all(|s| s.is_newline());
|
||||
is_only_newlines
|
||||
&& !found_multiline_expr
|
||||
&& sub_expr.is_multiline()
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
Record { .. } | List { .. } => {
|
||||
!found_multiline_expr && loc_arg.is_multiline()
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
loc_arg.is_multiline()
|
||||
}
|
||||
});
|
||||
|
||||
if multiline_args && !should_outdent_last_arg {
|
||||
let arg_indent = indent + INDENT;
|
||||
|
||||
for loc_arg in loc_args.iter() {
|
||||
buf.newline();
|
||||
loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent);
|
||||
}
|
||||
} else if multiline_args && should_outdent_last_arg {
|
||||
for (index, loc_arg) in loc_args.iter().enumerate() {
|
||||
buf.spaces(1);
|
||||
|
||||
if index == last_arg_index {
|
||||
match loc_arg.value {
|
||||
SpaceBefore(sub_expr, _) => {
|
||||
sub_expr.format_with_options(
|
||||
buf,
|
||||
Parens::InApply,
|
||||
Newlines::Yes,
|
||||
indent,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
loc_arg.format_with_options(
|
||||
buf,
|
||||
Parens::InApply,
|
||||
Newlines::Yes,
|
||||
indent,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loc_arg.format_with_options(
|
||||
buf,
|
||||
Parens::InApply,
|
||||
Newlines::Yes,
|
||||
indent,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for loc_arg in loc_args.iter() {
|
||||
buf.spaces(1);
|
||||
@ -846,7 +907,34 @@ fn fmt_closure<'a, 'buf>(
|
||||
}
|
||||
};
|
||||
|
||||
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
|
||||
if is_multiline {
|
||||
match &loc_ret.value {
|
||||
SpaceBefore(sub_expr, spaces) => {
|
||||
let should_outdent = match sub_expr {
|
||||
Record { .. } | List { .. } => {
|
||||
let is_only_newlines = spaces.iter().all(|s| s.is_newline());
|
||||
is_only_newlines && sub_expr.is_multiline()
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if should_outdent {
|
||||
buf.spaces(1);
|
||||
sub_expr.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||
} else {
|
||||
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
|
||||
}
|
||||
}
|
||||
Record { .. } | List { .. } => {
|
||||
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||
}
|
||||
_ => {
|
||||
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent);
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_backpassing<'a, 'buf>(
|
||||
|
@ -626,6 +626,392 @@ mod test_fmt {
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lambda_returns_record() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
toRecord = \_ -> {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
}
|
||||
|
||||
toRecord
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
func = \_ ->
|
||||
{ x: 1, y: 2, z: 3 }
|
||||
|
||||
func
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
toRecord = \_ ->
|
||||
val = 0
|
||||
|
||||
{
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
}
|
||||
|
||||
toRecord
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
toRecord = \_ ->
|
||||
{
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
}
|
||||
|
||||
toRecord
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
toRecord = \_ -> {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
}
|
||||
|
||||
toRecord
|
||||
"#
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lambda_returns_list() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
toList = \_ -> [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
|
||||
toList
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
func = \_ ->
|
||||
[ 1, 2, 3 ]
|
||||
|
||||
func
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
toList = \_ ->
|
||||
val = 0
|
||||
|
||||
[
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
|
||||
toList
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
toList = \_ ->
|
||||
[
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
|
||||
toList
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
toList = \_ -> [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
|
||||
toList
|
||||
"#
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_list_func_arg() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
result = func arg [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
|
||||
result
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
result = func arg
|
||||
[ 1, 2, 3 ]
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
result = func
|
||||
arg
|
||||
[ 1, 2, 3 ]
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
result = func arg [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
result = func arg [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
result = func [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
arg
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
result = func
|
||||
[
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
arg
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
result = func arg
|
||||
[
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
result = func arg [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
result = func
|
||||
arg
|
||||
[
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
|
||||
result
|
||||
"#
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_record_func_arg() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
result = func arg {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
}
|
||||
|
||||
result
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
result = func arg
|
||||
{ x: 1, y: 2, z: 3 }
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
result = func
|
||||
arg
|
||||
{ x: 1, y: 2, z: 3 }
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
result = func arg {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
}
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
result = func arg {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
}
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
result = func {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
}
|
||||
arg
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
result = func
|
||||
{
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
}
|
||||
arg
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
result = func arg
|
||||
{
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
}
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
result = func arg {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
}
|
||||
|
||||
result
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
result = func
|
||||
arg
|
||||
{
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
}
|
||||
|
||||
result
|
||||
"#
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_updating() {
|
||||
expr_formats_same(indoc!(
|
||||
@ -1301,6 +1687,27 @@ mod test_fmt {
|
||||
fn multi_line_list_def() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
l = [
|
||||
1,
|
||||
2,
|
||||
]
|
||||
|
||||
l
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
l =
|
||||
[ 1, 2 ]
|
||||
|
||||
l
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
l =
|
||||
[
|
||||
1,
|
||||
@ -1308,8 +1715,19 @@ mod test_fmt {
|
||||
]
|
||||
|
||||
l
|
||||
"#
|
||||
));
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
l = [
|
||||
1,
|
||||
2,
|
||||
]
|
||||
|
||||
l
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
@ -1324,11 +1742,10 @@ mod test_fmt {
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
results =
|
||||
[
|
||||
Ok 4,
|
||||
Ok 5,
|
||||
]
|
||||
results = [
|
||||
Ok 4,
|
||||
Ok 5,
|
||||
]
|
||||
|
||||
allOks results
|
||||
"#
|
||||
@ -1417,18 +1834,69 @@ mod test_fmt {
|
||||
|
||||
#[test]
|
||||
fn multi_line_record_def() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
pos = {
|
||||
x: 4,
|
||||
y: 11,
|
||||
z: 16,
|
||||
}
|
||||
|
||||
pos
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
pos =
|
||||
{ x: 4, y: 11, z: 16 }
|
||||
|
||||
pos
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
myDef =
|
||||
list = [
|
||||
a,
|
||||
b,
|
||||
]
|
||||
|
||||
{
|
||||
c,
|
||||
d,
|
||||
}
|
||||
|
||||
myDef
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
pos =
|
||||
{
|
||||
x: 4,
|
||||
y: 11,
|
||||
z: 16,
|
||||
}
|
||||
|
||||
pos
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
pos = {
|
||||
x: 4,
|
||||
y: 11,
|
||||
z: 16,
|
||||
}
|
||||
|
||||
pos
|
||||
"#
|
||||
));
|
||||
pos
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
@ -1443,11 +1911,10 @@ mod test_fmt {
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
pos =
|
||||
{
|
||||
x: 5,
|
||||
y: 10,
|
||||
}
|
||||
pos = {
|
||||
x: 5,
|
||||
y: 10,
|
||||
}
|
||||
|
||||
pos
|
||||
"#
|
||||
|
@ -12,13 +12,12 @@ Model position :
|
||||
}
|
||||
|
||||
initialModel : position -> Model position
|
||||
initialModel = \start ->
|
||||
{
|
||||
evaluated: Set.empty,
|
||||
openSet: Set.single start,
|
||||
costs: Dict.single start 0,
|
||||
cameFrom: Dict.empty,
|
||||
}
|
||||
initialModel = \start -> {
|
||||
evaluated: Set.empty,
|
||||
openSet: Set.single start,
|
||||
costs: Dict.single start 0,
|
||||
cameFrom: Dict.empty,
|
||||
}
|
||||
|
||||
cheapestOpen : (position -> F64), Model position -> Result position {}
|
||||
cheapestOpen = \costFn, model ->
|
||||
|
@ -20,11 +20,10 @@ loopHelp = \{ remaining, string } ->
|
||||
c = Num.intCast z
|
||||
combined = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy 16 a) (Num.shiftLeftBy 8 b)) c
|
||||
|
||||
Loop
|
||||
{
|
||||
remaining: remaining - 3,
|
||||
string: Str.concat string (bitsToChars combined 0),
|
||||
}
|
||||
Loop {
|
||||
remaining: remaining - 3,
|
||||
string: Str.concat string (bitsToChars combined 0),
|
||||
}
|
||||
else if remaining == 0 then
|
||||
Bytes.Decode.succeed (Done string)
|
||||
else if remaining == 2 then
|
||||
|
@ -132,11 +132,10 @@ encodeHelp = \encoder, offset, output ->
|
||||
List.walk
|
||||
bs
|
||||
{ output, offset }
|
||||
\accum, byte ->
|
||||
{
|
||||
offset: accum.offset + 1,
|
||||
output: List.set accum.output offset byte,
|
||||
}
|
||||
\accum, byte -> {
|
||||
offset: accum.offset + 1,
|
||||
output: List.set accum.output offset byte,
|
||||
}
|
||||
|
||||
Sequence _ encoders ->
|
||||
List.walk
|
||||
|
@ -29,20 +29,19 @@ Model : {
|
||||
}
|
||||
|
||||
init : Bounds -> Model
|
||||
init = \{ width, height } ->
|
||||
{
|
||||
# Screen height and width
|
||||
width,
|
||||
height,
|
||||
# Paddle X-coordinate
|
||||
paddleX: (width * 0.5) - (paddleWidth * width * 0.5),
|
||||
# Ball coordinates
|
||||
ballX: width * 0.5,
|
||||
ballY: height * 0.4,
|
||||
# Delta - how much ball moves in each tick
|
||||
dBallX: 4,
|
||||
dBallY: 4,
|
||||
}
|
||||
init = \{ width, height } -> {
|
||||
# Screen height and width
|
||||
width,
|
||||
height,
|
||||
# Paddle X-coordinate
|
||||
paddleX: (width * 0.5) - (paddleWidth * width * 0.5),
|
||||
# Ball coordinates
|
||||
ballX: width * 0.5,
|
||||
ballY: height * 0.4,
|
||||
# Delta - how much ball moves in each tick
|
||||
dBallX: 4,
|
||||
dBallY: 4,
|
||||
}
|
||||
|
||||
update : Model, Event -> Model
|
||||
update = \model, event ->
|
||||
@ -125,23 +124,21 @@ render = \model ->
|
||||
top = Num.toF32 (row * blockHeight)
|
||||
border = blockBorder * blockWidth
|
||||
|
||||
outer = Rect
|
||||
{
|
||||
left,
|
||||
top,
|
||||
width: blockWidth,
|
||||
height: blockHeight,
|
||||
color: { r: color.r * 0.8, g: color.g * 0.8, b: color.b * 0.8, a: 1 },
|
||||
}
|
||||
outer = Rect {
|
||||
left,
|
||||
top,
|
||||
width: blockWidth,
|
||||
height: blockHeight,
|
||||
color: { r: color.r * 0.8, g: color.g * 0.8, b: color.b * 0.8, a: 1 },
|
||||
}
|
||||
|
||||
inner = Rect
|
||||
{
|
||||
left: left + border,
|
||||
top: top + border,
|
||||
width: blockWidth - (border * 2),
|
||||
height: blockHeight - (border * 2),
|
||||
color,
|
||||
}
|
||||
inner = Rect {
|
||||
left: left + border,
|
||||
top: top + border,
|
||||
width: blockWidth - (border * 2),
|
||||
height: blockHeight - (border * 2),
|
||||
color,
|
||||
}
|
||||
|
||||
[ outer, inner ]
|
||||
|
||||
|
@ -8,14 +8,12 @@ render =
|
||||
|
||||
styles = { bgColor: rgba 100 50 50 1, borderColor: rgba 10 20 30 1, borderWidth: 10, textColor: rgba 220 220 250 1 }
|
||||
|
||||
Col
|
||||
[
|
||||
Row
|
||||
[
|
||||
Button (Text "Corner ") styles,
|
||||
Button (Text "Top Mid ") { styles & bgColor: rgba 100 100 50 1 },
|
||||
Button (Text "Top Right ") { styles & bgColor: rgba 50 50 150 1 },
|
||||
],
|
||||
Button (Text "Mid Left ") { styles & bgColor: rgba 150 100 100 1 },
|
||||
Button (Text "Bottom Left") { styles & bgColor: rgba 150 50 50 1 },
|
||||
]
|
||||
Col [
|
||||
Row [
|
||||
Button (Text "Corner ") styles,
|
||||
Button (Text "Top Mid ") { styles & bgColor: rgba 100 100 50 1 },
|
||||
Button (Text "Top Right ") { styles & bgColor: rgba 50 50 150 1 },
|
||||
],
|
||||
Button (Text "Mid Left ") { styles & bgColor: rgba 150 100 100 1 },
|
||||
Button (Text "Bottom Left") { styles & bgColor: rgba 150 50 50 1 },
|
||||
]
|
||||
|
@ -6,9 +6,8 @@ app "tui"
|
||||
Model : Str
|
||||
|
||||
main : Program Model
|
||||
main =
|
||||
{
|
||||
init: \{ } -> "Hello World",
|
||||
update: \model, new -> Str.concat model new,
|
||||
view: \model -> Str.concat model "!",
|
||||
}
|
||||
main = {
|
||||
init: \{ } -> "Hello World",
|
||||
update: \model, new -> Str.concat model new,
|
||||
view: \model -> Str.concat model "!",
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user