feat(formatter): implement outdent formatting for multiline lists and records

This commit is contained in:
Sean Hagstrom 2022-04-24 16:35:09 +01:00
parent 3d9daed836
commit b147890b08
9 changed files with 647 additions and 87 deletions

View File

@ -191,12 +191,26 @@ pub fn fmt_body<'a, 'buf>(
buf.push_str(" ="); buf.push_str(" =");
if body.is_multiline() { if body.is_multiline() {
match body { match body {
Expr::SpaceBefore(_, _) => { Expr::SpaceBefore(sub_def, spaces) => {
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT); let should_outdent = match sub_def {
} Expr::Record { .. } | Expr::List { .. } => {
Expr::Record { .. } | Expr::List { .. } => { let is_only_newlines = spaces.iter().all(|s| s.is_newline());
buf.newline(); is_only_newlines && sub_def.is_multiline()
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT); }
_ => 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); buf.spaces(1);

View File

@ -186,14 +186,75 @@ impl<'a> Formattable for Expr<'a> {
loc_expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent); 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 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; let arg_indent = indent + INDENT;
for loc_arg in loc_args.iter() { for loc_arg in loc_args.iter() {
buf.newline(); buf.newline();
loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent); 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 { } else {
for loc_arg in loc_args.iter() { for loc_arg in loc_args.iter() {
buf.spaces(1); 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>( fn fmt_backpassing<'a, 'buf>(

View File

@ -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] #[test]
fn record_updating() { fn record_updating() {
expr_formats_same(indoc!( expr_formats_same(indoc!(
@ -1301,6 +1687,27 @@ mod test_fmt {
fn multi_line_list_def() { fn multi_line_list_def() {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
l = [
1,
2,
]
l
"#
));
expr_formats_same(indoc!(
r#"
l =
[ 1, 2 ]
l
"#
));
expr_formats_to(
indoc!(
r#"
l = l =
[ [
1, 1,
@ -1308,8 +1715,19 @@ mod test_fmt {
] ]
l l
"# "#
)); ),
indoc!(
r#"
l = [
1,
2,
]
l
"#
),
);
expr_formats_to( expr_formats_to(
indoc!( indoc!(
@ -1324,11 +1742,10 @@ mod test_fmt {
), ),
indoc!( indoc!(
r#" r#"
results = results = [
[ Ok 4,
Ok 4, Ok 5,
Ok 5, ]
]
allOks results allOks results
"# "#
@ -1417,18 +1834,69 @@ mod test_fmt {
#[test] #[test]
fn multi_line_record_def() { fn multi_line_record_def() {
expr_formats_same(indoc!(
r#"
pos = {
x: 4,
y: 11,
z: 16,
}
pos
"#
));
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
pos = 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, x: 4,
y: 11, y: 11,
z: 16, z: 16,
} }
pos pos
"# "#
)); ),
);
expr_formats_to( expr_formats_to(
indoc!( indoc!(
@ -1443,11 +1911,10 @@ mod test_fmt {
), ),
indoc!( indoc!(
r#" r#"
pos = pos = {
{ x: 5,
x: 5, y: 10,
y: 10, }
}
pos pos
"# "#

View File

@ -12,13 +12,12 @@ Model position :
} }
initialModel : position -> Model position initialModel : position -> Model position
initialModel = \start -> initialModel = \start -> {
{ evaluated: Set.empty,
evaluated: Set.empty, openSet: Set.single start,
openSet: Set.single start, costs: Dict.single start 0,
costs: Dict.single start 0, cameFrom: Dict.empty,
cameFrom: Dict.empty, }
}
cheapestOpen : (position -> F64), Model position -> Result position {} cheapestOpen : (position -> F64), Model position -> Result position {}
cheapestOpen = \costFn, model -> cheapestOpen = \costFn, model ->

View File

@ -20,11 +20,10 @@ loopHelp = \{ remaining, string } ->
c = Num.intCast z c = Num.intCast z
combined = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy 16 a) (Num.shiftLeftBy 8 b)) c combined = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy 16 a) (Num.shiftLeftBy 8 b)) c
Loop Loop {
{ remaining: remaining - 3,
remaining: remaining - 3, string: Str.concat string (bitsToChars combined 0),
string: Str.concat string (bitsToChars combined 0), }
}
else if remaining == 0 then else if remaining == 0 then
Bytes.Decode.succeed (Done string) Bytes.Decode.succeed (Done string)
else if remaining == 2 then else if remaining == 2 then

View File

@ -132,11 +132,10 @@ encodeHelp = \encoder, offset, output ->
List.walk List.walk
bs bs
{ output, offset } { output, offset }
\accum, byte -> \accum, byte -> {
{ offset: accum.offset + 1,
offset: accum.offset + 1, output: List.set accum.output offset byte,
output: List.set accum.output offset byte, }
}
Sequence _ encoders -> Sequence _ encoders ->
List.walk List.walk

View File

@ -29,20 +29,19 @@ Model : {
} }
init : Bounds -> Model init : Bounds -> Model
init = \{ width, height } -> init = \{ width, height } -> {
{ # Screen height and width
# Screen height and width width,
width, height,
height, # Paddle X-coordinate
# Paddle X-coordinate paddleX: (width * 0.5) - (paddleWidth * width * 0.5),
paddleX: (width * 0.5) - (paddleWidth * width * 0.5), # Ball coordinates
# Ball coordinates ballX: width * 0.5,
ballX: width * 0.5, ballY: height * 0.4,
ballY: height * 0.4, # Delta - how much ball moves in each tick
# Delta - how much ball moves in each tick dBallX: 4,
dBallX: 4, dBallY: 4,
dBallY: 4, }
}
update : Model, Event -> Model update : Model, Event -> Model
update = \model, event -> update = \model, event ->
@ -125,23 +124,21 @@ render = \model ->
top = Num.toF32 (row * blockHeight) top = Num.toF32 (row * blockHeight)
border = blockBorder * blockWidth border = blockBorder * blockWidth
outer = Rect outer = Rect {
{ left,
left, top,
top, width: blockWidth,
width: blockWidth, height: blockHeight,
height: blockHeight, color: { r: color.r * 0.8, g: color.g * 0.8, b: color.b * 0.8, a: 1 },
color: { r: color.r * 0.8, g: color.g * 0.8, b: color.b * 0.8, a: 1 }, }
}
inner = Rect inner = Rect {
{ left: left + border,
left: left + border, top: top + border,
top: top + border, width: blockWidth - (border * 2),
width: blockWidth - (border * 2), height: blockHeight - (border * 2),
height: blockHeight - (border * 2), color,
color, }
}
[ outer, inner ] [ outer, inner ]

View File

@ -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 } styles = { bgColor: rgba 100 50 50 1, borderColor: rgba 10 20 30 1, borderWidth: 10, textColor: rgba 220 220 250 1 }
Col Col [
[ Row [
Row Button (Text "Corner ") styles,
[ Button (Text "Top Mid ") { styles & bgColor: rgba 100 100 50 1 },
Button (Text "Corner ") styles, Button (Text "Top Right ") { styles & bgColor: rgba 50 50 150 1 },
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 },
Button (Text "Mid Left ") { styles & bgColor: rgba 150 100 100 1 }, ]
Button (Text "Bottom Left") { styles & bgColor: rgba 150 50 50 1 },
]

View File

@ -6,9 +6,8 @@ app "tui"
Model : Str Model : Str
main : Program Model main : Program Model
main = main = {
{ init: \{ } -> "Hello World",
init: \{ } -> "Hello World", update: \model, new -> Str.concat model new,
update: \model, new -> Str.concat model new, view: \model -> Str.concat model "!",
view: \model -> Str.concat model "!", }
}