Differences from Prettier
Prettier with its Monkey C plugin is the other
formatter in use for Monkey C, so it’s a natural point of comparison. rafiki
doesn’t aim to reproduce it line for line, and it has no flags to emulate it.
This page lists the differences currently known and why they exist. It isn’t
complete, and entries may change as either formatter evolves.
The examples compare the output of both at a line width of 80 with alignment disabled.
Line width
rafiki’s default line width is 111 to match modern formatters and monitors,
while Prettier defaults to 80. Both can be reconfigured, with --line-width or
line-width in rafiki.toml.
Alignment
rafiki aligns dictionaries and trailing comments into
columns for readability, which Prettier doesn’t. It can be disabled with
--no-alignment, or with alignment = false in rafiki.toml.
Short arrays
Both formatters fill the lines of a wrapped array
instead of giving each item its own line, but differ in when and how. Prettier
only fills arrays of numbers and puts as many on each line as fit. rafiki
fills any array of short items and lines them up
in columns, with numbers aligned by their last digit.
// rafiki
var keys = [
:alpha, :beta, :gamma, :delta, :epsilon, :zeta, :eta, :theta, :iota,
:kappa, :lambda, :mu
];
// Prettier
var keys = [
:alpha,
:beta,
// ...
:lambda,
:mu,
];
Trailing commas
Prettier adds a trailing comma to every list it breaks over multiple lines.
rafiki only keeps a trailing comma that’s already in the source, where it
works as a magic trailing comma and keeps the list
broken. Leaving it out means a list folds back onto one line by itself if it
shrinks, e.g. after renaming a variable.
// rafiki
var settings = {
:background => Graphics.COLOR_BLACK,
:foreground => Graphics.COLOR_WHITE
};
// Prettier
var settings = {
:background => Graphics.COLOR_BLACK,
:foreground => Graphics.COLOR_WHITE,
};
Parentheses
Prettier rewrites parentheses: it drops the ones precedence makes redundant and
adds some for clarity. rafiki drops the ones that can’t change how the code
reads, but never adds any.
// rafiki
var width = (percent / 100f * total).toLong();
var scaled = 10 / 360.toFloat();
// Prettier
var width = ((percent / 100f) * total).toLong();
var scaled = 10 / (360).toFloat();
Number literals
Both rewrite number literals with lowercase exponents and a leading zero before
the decimal point. Prettier also lowercases hex digits and drops trailing zeros
after the decimal point, where rafiki uppercases hex digits and keeps trailing
zeros. Setting hex-digit-case = "lower" and
trailing-zeros = "trim" matches Prettier.
// rafiki
var color = 0xFFAA00;
var half = 0.5;
var ratio = 0.80;
var tiny = 3.5e-7f;
// Prettier
var color = 0xffaa00;
var half = 0.5;
var ratio = 0.8;
var tiny = 3.5e-7f;
Annotations
Prettier separates annotations with a comma and no space. rafiki separates
them with a space, the only form Garmin documents.
// rafiki
(:excludeFromLowMemoryDevices :excludeFromDataField)
// Prettier
(:excludeFromLowMemoryDevices,:excludeFromDataField)
Nested generic types
Prettier puts a space between the closing > of nested generic types. rafiki
writes them together.
// rafiki
var rows = [] as Array<Array<Number>>;
// Prettier
var rows = [] as Array<Array<Number> >;
Binary operators
When an expression has to break, rafiki breaks before an operator and starts
the continuation line with it. Prettier breaks after the operator and leaves it
at the end of the line. This is one of the most common differences between the
two, and applies to every binary operator, most often + when building strings
and && or || in conditions.
// rafiki
string += posnTime.hour.format("%2d")
+ ":"
+ posnTime.min.format("%02d")
+ ":"
+ posnTime.sec.format("%02d");
// Prettier
string +=
posnTime.hour.format("%2d") +
":" +
posnTime.min.format("%02d") +
":" +
posnTime.sec.format("%02d");
A parenthesised expression keeps its ( next to the first operand and its )
after the last one. Prettier moves the expression inside the parentheses, as it
does for conditions. The example is formatted with
parens = "preserve", since rafiki otherwise drops parentheses
around a returned value.
// rafiki
return (coordinates[0] > buttonCoordinates[0][0]
&& coordinates[0] < buttonCoordinates[0][1]
&& coordinates[1] > buttonCoordinates[1][0]);
// Prettier
return (
coordinates[0] > buttonCoordinates[0][0] &&
coordinates[0] < buttonCoordinates[0][1] &&
coordinates[1] > buttonCoordinates[1][0]
);
Breaking after =
Prettier first tries breaking after = and keeping the whole right-hand side
together on the next line. rafiki keeps the start of the expression next to
the = and breaks at its operators instead.
// rafiki
isAvailable = SomeModule.SOME_LONG_CONSTANT_NAME
== otherObject.someLongPropertyName;
// Prettier
isAvailable =
SomeModule.SOME_LONG_CONSTANT_NAME == otherObject.someLongPropertyName;
Conditions
A condition that doesn’t fit breaks at its operators, with the closing ) right
after the last operand and { on its own line. Prettier moves the whole
condition inside the parentheses instead, which leaves if ( and ) { on lines
of their own.
// rafiki
if (Toybox has :SomeModule
&& Toybox.SomeModule has :someVeryLongFunctionName)
{
// Prettier
if (
Toybox has :SomeModule &&
Toybox.SomeModule has :someVeryLongFunctionName
) {
Call arguments
When a call doesn’t fit, rafiki puts every argument on its own line. Prettier
first tries to break only the last argument when it’s a non-empty array or
dictionary and the argument before it isn’t of the same kind.
// rafiki
var text = Lang.format(
"$1$ of $2$",
[currentValue.format("%d"), maximumValue.format("%d")]
);
// Prettier
var text = Lang.format("$1$ of $2$", [
currentValue.format("%d"),
maximumValue.format("%d"),
]);
Prettier tries three layouts in order: everything on one line, only the last
argument broken, and every argument on its own line. rafiki only keeps
brackets next to the parentheses when the array or dictionary is the
only argument.
Note
Whether to expand the last argument like Prettier is still open. It saves a level of indentation for calls like
Lang.format, but the other arguments end up easy to miss at the end of a long first line.
Multiple bindings
A var or const declaring several names stays on one line in rafiki when
it fits. Prettier puts every name on its own line as soon as one of them has a
value, even when the declaration would fit.
// rafiki
var x = 1, y = 2;
// Prettier
var x = 1,
y = 2;
Comments
rafiki keeps comments where they are. Prettier moves some of them,
most visibly a comment after an opening {, which it puts on the next line
above the first statement.
// rafiki
if (isReady) { // wait for the first reading
update();
}
// Prettier
if (isReady) {
// wait for the first reading
update();
}
Multi-line block comments
When a /* … */ comment is re-indented, rafiki moves every line of it by the
same amount, so the layout inside the comment stays intact. Prettier only does
that when every line starts with *, like a /** … */ doc comment. For any
other comment it moves just the first line and leaves the rest where they were,
which separates the comment from its opener. This matches how rustfmt and
gofmt handle block comments.
// Source
function f() {
if (x) {
/* disabled
foo();
bar();
*/
/*
bar
*/
}
}
// rafiki
function f() {
if (x) {
/* disabled
foo();
bar();
*/
/*
bar
*/
}
}
// Prettier
function f() {
if (x) {
/* disabled
foo();
bar();
*/
/*
bar
*/
}
}
Comments that are already indented correctly come out unchanged in both.
Dictionary spacing
rafiki writes one-line dictionaries without spaces inside the braces, the same
way as arrays and as most of the Connect IQ SDK samples. Prettier pads them.
// rafiki
var params = {"key" => "value"};
// Prettier
var params = { "key" => "value" };
Blank lines between declarations
rafiki always puts a blank line between functions and classes. Prettier keeps
whatever blank lines the source has.
// rafiki
class C {
function a() {}
function b() {}
}
// Prettier
class C {
function a() {}
function b() {}
}
Empty blocks
rafiki writes every empty block as {}. Prettier splits some of them over two
lines, such as the body of an if or a class, while keeping others like
function f() {} on one.
// rafiki
if (x) {}
class Foo {}
// Prettier
if (x) {
}
class Foo {
}
Long class headers
Prettier breaks a class header that doesn’t fit before extends, with { on
its own line. rafiki keeps the header on one line even when it overflows.
// rafiki
class ExampleBackgroundServiceDelegate extends Toybox.Background.ServiceDelegate {
// Prettier
class ExampleBackgroundServiceDelegate
extends Toybox.Background.ServiceDelegate
{
Index expressions
Prettier breaks inside [...] when an index expression doesn’t fit. rafiki
keeps index expressions on one line, even when that overflows.
// rafiki
var entries = {
:first => lookupTable[indexTable[SomeModule.SOME_VERY_LONG_CONSTANT_NAME_HERE]],
:second => 2
};
// Prettier
var entries = {
:first => lookupTable[
indexTable[SomeModule.SOME_VERY_LONG_CONSTANT_NAME_HERE]
],
:second => 2,
};
Missing semicolons
Prettier adds a semicolon the source is missing. rafiki reports a parse
error instead, since the Monkey C compiler rejects the code as well.
// Source
var a = 1
var b = 2;
// Prettier
var a = 1;
var b = 2;