ek9_insert_coalescing
Splices `LEFT <op> RIGHT` in place of a selected single-line left operand using one of the EK9 coalescing / elvis operators; answers status, edits[] (one REPLACE) and conflicts[] (kind 'selection-not-an-expression'), with a type-incompatible pairing surfacing as would-fail.
When to use it
'Default name to "Anonymous" when unset' is one call with file, the LEFT operand as written (operand:'name'), operator:'?:' and rightOperand:'"Anonymous"'. Use it to wrap an existing single-line expression in one of EK9's null-safe operators (?: <? >?) without hand-writing tokens.
When not to use it
- Inserting a whole new statement/expression rather than wrapping an existing operand - use
ek9_insert_statements. - Adding a set-check guard to an if/while/for/switch/try head (not an inline expression) — use
ek9_apply_guard_pattern. - Lifting an expression into a local rather than coalescing it — use
ek9_extract_variable.
Details
The splice is syntactic but gated by a semantic scratch-compile: `?:` works on any tri-state type, while `<?`/`>?` need the operand's type to define `<=>` (`_cmp`), so a bad pairing or type-incompatible rightOperand surfaces as status:'would-fail' with the compiler diagnostic, not a broken edit. A compound left operand (e.g. `a + b`) is parenthesised for unambiguous binding, as is a compound rightOperand (`count ?: (base + 1)`) and the whole new expression when the operand sits inside another operator (`x + (a <? c)`), so the value never silently changes. The operand text names its occurrence; when it is an operand on several lines and no line is given, the call is refused naming them. A declaration's LHS is an identifier, not an expression (status:'conflicts', kind 'selection-not-an-expression') — and V1 supports a single-line left operand only.
Arguments
| Argument | Type | Required | What it is |
|---|---|---|---|
endColumn | integer | 1-based EXCLUSIVE column of the END of the span (an IDE selection). Omit it - pass the text instead. | |
file | string | yes | Workspace-relative path to the .ek9 source file containing the expression. |
line | integer | Optional 1-based line of the operand. Needed only when the operand text is an operand on more than one line. | |
operand | string | The LEFT operand exactly as written in the source ('name', 'a + b'). | |
operator | string: ?: | <? | >? | yes | The coalescing operator to insert: `?:` first SET - the one that defaults an unset value ('name ?: "Anonymous"'); `<?` min, `>?` max (the LEFT operand on a tie). |
rightOperand | string | yes | The RIGHT operand expression as source text — e.g. a fallback literal (`"Anonymous"`), another variable (`y`), or a further chained expression (`b ?: c`). Spliced verbatim. |
startColumn | integer | 1-based column of the START of the span on 'line' (an IDE selection). Omit it - pass the text instead. |
It also takes the arguments every tool takes.
Recorded calls
Real calls, recorded against small projects and checked on every build - a changed answer fails it. 3 of the 10 recorded are shown.
Operand text ambiguous without line
Request
{
"file" : "Bounds.ek9",
"operand" : "alpha",
"operator" : "<?",
"rightOperand" : "beta"
}
Response
{
"conflicts" : [ ],
"edits" : [ ],
"error" : {
"message" : "operand 'alpha' is on lines [9, 10] - pass line (one of [9, 10]) to say which one to coalesce."
},
"meta" : {
"serverMs" : "<timing>"
},
"status" : "error",
"summary" : "operand 'alpha' is on lines [9, 10] - pass line (one of [9, 10]) to say which one to coalesce.",
"wouldBeDiagnostics" : [ ],
"written" : false
}
Min dry run
Request
{
"dryRun" : true,
"endColumn" : 23,
"file" : "Bounds.ek9",
"line" : 9,
"operator" : "<?",
"rightOperand" : "beta",
"startColumn" : 18
}
Response
{
"conflicts" : [ ],
"edits" : [ {
"after" : " smaller <- alpha <? beta",
"before" : " smaller <- alpha",
"endLine" : 9,
"file" : "Bounds.ek9",
"startLine" : 9,
"type" : "replace"
} ],
"meta" : {
"serverMs" : "<timing>"
},
"risk" : {
"crossesModuleBoundary" : false,
"estimatedSeverity" : "trivial",
"fileCount" : 1,
"lineCount" : 1,
"touchesPublicApi" : false,
"touchesTests" : false
},
"status" : "ready",
"summary" : "Would wrap `alpha` as `alpha <? beta`",
"workspaceHash" : "<workspace-hash>",
...
Cut short here: 3 more lines. The whole recorded call.
Right operand parenthesised
Request
{
"dryRun" : true,
"endColumn" : 23,
"file" : "Bounds.ek9",
"line" : 9,
"operator" : "?:",
"rightOperand" : "beta + 1",
"startColumn" : 18
}
Response
{
"conflicts" : [ ],
"edits" : [ {
"after" : " smaller <- alpha ?: (beta + 1)",
"before" : " smaller <- alpha",
"endLine" : 9,
"file" : "Bounds.ek9",
"startLine" : 9,
"type" : "replace"
} ],
"meta" : {
"serverMs" : "<timing>"
},
"risk" : {
"crossesModuleBoundary" : false,
"estimatedSeverity" : "trivial",
"fileCount" : 1,
"lineCount" : 1,
"touchesPublicApi" : false,
"touchesTests" : false
},
"status" : "ready",
"summary" : "Would wrap `alpha` as `alpha ?: (beta + 1)`",
"workspaceHash" : "<workspace-hash>",
...
Cut short here: 3 more lines. The whole recorded call.
See also
- Show me in the IDE: Wrap with coalescing