ek9_introduce_field
Lifts a local and its initialiser to a same-named field of the enclosing class/record/component and deletes the local (adding `default operator ?` when it is the first field); answers status, edits[] and conflicts[] (no-promotable-declaration, not-inside-fielded-aggregate, field-name-collision).
When to use it
'Make the local counter a field' is one call with file + symbol:'counter' - no line needed. Use it when a local declared inside a method/operator/constructor should become per-instance state shared by the construct's other members; the field keeps the name.
When not to use it
- Lifting a `name <- literal` to a MODULE constant, not a field - use
ek9_promote_to_constant. - Scaffolding a brand-new field rather than lifting an existing local — use
ek9_add_field. - Wanting a DIFFERENT field name — introduce keeps the name; run
ek9_rename_symbolafterwards. - The local is not inside a class/record/component (e.g. a top-level function) — no fielded aggregate, so there is nowhere to lift to.
Details
Name is kept verbatim, so in-construct references still resolve (to the field) and simple cases keep compiling; the two hard cases surface as would-fail via the scratch-compile gate: an initialiser referencing a parameter/local (E04050 for a complex expression, E50001 for a bare reference) and another method's same-named local now duplicating the field (E50050). When the aggregate has no own `?` operator (typically this is its first field), `default operator ?` is also appended (E07235 tri-state requirement). conflicts kinds: no-promotable-declaration (line is not a single-line initialised local), not-inside-fielded-aggregate (not inside a class/record/component), field-name-collision (aggregate already declares that field). Fields take the construct's natural visibility (class/component private, record public) — no visibility parameter.
Arguments
| Argument | Type | Required | What it is |
|---|---|---|---|
file | string | yes | Workspace-relative path to the .ek9 source file holding the local declaration. |
line | integer | 1-based line of the declaration or a reference - OMIT it and pass symbol:'<name>' to let the tool find the line (a model counting lines in a file miscounts); if you do count, use ek9_read_file(lineNumbers:true). | |
symbol | string | The identifier you mean (e.g. 'total'); the tool finds its declaration line in file. Pass line only to anchor one specific occurrence. |
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 4 recorded are shown.
Introduce subsequent field apply
Request
{
"file" : "Account.ek9",
"line" : 9
}
Response
{
"conflicts" : [ ],
"edits" : [ {
"before" : " limit <- 100",
"endLine" : 9,
"file" : "Account.ek9",
"startLine" : 9,
"type" : "delete"
}, {
"after" : " limit <- 100",
"endLine" : 7,
"file" : "Account.ek9",
"startLine" : 7,
"type" : "insert"
} ],
"meta" : {
"serverMs" : "<timing>"
},
"resolvedPath" : "<workspace-root>/Account.ek9",
"risk" : {
"crossesModuleBoundary" : false,
"estimatedSeverity" : "trivial",
"fileCount" : 1,
"lineCount" : 2,
...
Cut short here: 9 more lines. The whole recorded call.
Introduce not inside aggregate conflict
Request
{
"dryRun" : true,
"file" : "Compute.ek9",
"line" : 6
}
Response
{
"conflicts" : [ {
"kind" : "not-inside-fielded-aggregate",
"message" : "Line 6 is not inside a class, record, or component. Introduce Field lifts a local declaration to a field of its enclosing fielded construct."
} ],
"edits" : [ ],
"meta" : {
"serverMs" : "<timing>"
},
"risk" : {
"crossesModuleBoundary" : false,
"estimatedSeverity" : "trivial",
"fileCount" : 0,
"lineCount" : 0,
"touchesPublicApi" : false,
"touchesTests" : false
},
"status" : "conflicts",
"summary" : "Line 6 is not inside a class, record, or component. Introduce Field lifts a local declaration to a field of its enclosing fielded construct.",
"workspaceHash" : "<workspace-hash>",
"wouldBeDiagnostics" : [ ],
"written" : false
}
Introduce first field apply
Request
{
"file" : "Account.ek9",
"line" : 7
}
Response
{
"conflicts" : [ ],
"edits" : [ {
"before" : " limit <- 100",
"endLine" : 7,
"file" : "Account.ek9",
"startLine" : 7,
"type" : "delete"
}, {
"after" : " limit <- 100",
"endLine" : 6,
"file" : "Account.ek9",
"startLine" : 6,
"type" : "insert"
}, {
"after" : " default operator ?",
"endLine" : 9,
"file" : "Account.ek9",
"startLine" : 9,
"type" : "insert"
} ],
"meta" : {
"serverMs" : "<timing>"
},
...
Cut short here: 15 more lines. The whole recorded call.
See also
- Show me in the IDE: Introduce Field