code_builder
code_builder copied to clipboard
Add ability to generate if-else blocks
A previous version had .asIf()
for building statements like
if (something) {
// ...
}
As far as I know, the only way to do these with current API is to write this manually via Code(...)
.
The problem with builders for statements is there are so many, and it is hard to cover everything.
Happy to accept PRs though for statements you feel add value!
Hmm, good point.
Maybe there a way to just let folks built their own statements (like the one above) in a safer way than right now?
Right now, I do something like this:
final conditionString = condition.accept(_dartEmitter).toString();
final returnStatement = literal(returnValue).returned.statement;
final returnString = returnStatement.accept(_dartEmitter).toString();
return Code('if ($conditionString) { $returnString }');
(_dartEmitter
is a private member of a class that does not end up building the final code, so it seems a bit out of place here.)
Would it be better to have something like:
final buf = CodeBuffer()
..addString('if (')
..addExpression(condition)
..addString(') {')
..addStatement(literal(returnValue).returned)
..addString('}');
return buf.build();
Eh. Not sure if this is any better. What I was driving at is a way not to use DartEmitter
until the last step. To keep the expressions and statements in their semantic form (as opposed to a string) as long as possible, and yet have the flexibility to just write stuff directly.
Just came across this issue and I was thinking about something like this:
CompountStatementBuilder()
..addStatement(refer('myBool').eq(literal('1')).asIf())
..addBlock(BlockBuilder()
..addStatement(literal(returnValue).returned.statement)
)
..followedBy(CompountStatementBuilder()
..addStatement(literalElse)
..addBlock(/* .. */));
CompountStatementBuilder()
..addStatement(refer('myArray').asFor(refer('item')))
..addBlock(/* .. */);
CompountStatementBuilder()
..addStatement(literalTry)
..addBlock(/* .. */)
// Maybe use builder callback directly for ease of use?
..followedBy((b) => b
..addStatement(literalCatch)
..addBlock(/* .. */));
Happy to setup a PR.
I created a helper method like so:
Block ifStatement(
Expression conditional,
Code body, [
bool singleLine = false,
]) =>
Block.of([
Code('if ('),
conditional.code,
Code(') {'),
body,
Code('}'),
]);
I even have one for try catch thats a bit more complicated
Block tryStatement({
required Code try$,
List<CatchClause> clauses = const [],
Catch? catch$,
Code? finally$,
}) {
if (clauses.isEmpty && catch$ == null && finally$ == null) {
throw ArgumentError(
r'Must provide at least one clause such as in `clauses`, `catch$`, or `finally$`');
}
return Block((b) {
b.statements.add(Code('try {'));
b.statements.add(try$);
b.statements.add(Code('}'));
// on X {}
// on X catch (e) {}
// on X catch (e, s) {}
// catch (e) {}
// catch (e, s) {}
for (final clause in clauses) {
if (clause.on case final on) {
b.statements.add(Code('on '));
b.statements.add(on.code);
}
if (clause.catch$) {
b.statements.add(Code('catch (e, s)'));
}
b.statements.add(Code('{'));
b.statements.add(clause.body(refer('e'), refer('s')));
b.statements.add(Code('}'));
}
if (catch$ != null) {
b.statements.add(Code('catch (e, s) {'));
b.statements.add(catch$.body(refer('e'), refer('s')));
b.statements.add(Code('}'));
}
if (finally$ != null) {
b.statements.add(Code('finally {'));
b.statements.add(finally$);
b.statements.add(Code('}'));
}
});
}
class CatchClause {
final CatchBodyBuilder body;
final Reference on;
final bool catch$;
CatchClause(
Code body, {
required this.on,
}) : catch$ = false,
body = ((_, __) => body);
const CatchClause.catch$(
this.body, {
required this.on,
}) : catch$ = true;
}
class Catch {
final CatchBodyBuilder body;
const Catch(this.body);
static const rethrow$ = Catch(_rethrowBody);
static Code _rethrowBody(_, __) => refer('rethrow').statement;
}
typedef CatchBodyBuilder = Code Function(Reference error, Reference stackTrace);
(note, i did not implement an option that excludes the stacktrace option)
I believe that both statements need to exist in code_builder
in some form, not necessarily like this.