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).

Category: Editor · Compare: Refactor → Introduce Field (from local variable)

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

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

ArgumentTypeRequiredWhat it is
filestringyesWorkspace-relative path to the .ek9 source file holding the local declaration.
lineinteger1-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).
symbolstringThe 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