ek9_extract_variable

Inserts `name <- expression` before the first occurrence and replaces every matching occurrence in the enclosing block with the name; answers status, edits[] and summary, a side-effecting multi-occurrence expression as conflicts[].

Category: Editor · Compare: Refactor → Extract → Variable (IntelliJ) / LSP Extract Variable

When to use it

'Name the repeated items.sum() * taxRate as subtotal' is one call with file, expression:'items.sum() * taxRate' and name:'subtotal' - no line or column.

When not to use it

Details

Anchored by the outermost expression parse-node that EXACTLY spans the located text (its first occurrence outside a comment, on 'line' when given, or the IDE's 1-based startColumn..endColumn); a misaligned span returns status:'conflicts' (kind 'expression-spans-statement-boundaries'), a line outside any body is an error. Replacement is whitespace-insensitive and confined to the anchor's nearest enclosing instruction block (nested sub-blocks included). A call-bearing expression with multiple occurrences under replaceAll returns status:'conflicts' (kind 'expression-has-side-effects-and-multiple-occurrences' - the call would run once instead of N times); set replaceAll:false to take just the anchor; a name colliding in scope conflicts as 'name-collision'.

Arguments

ArgumentTypeRequiredWhat it is
endColumninteger1-based EXCLUSIVE column of the END of the span (an IDE selection). Omit it - pass the text instead.
explicitTypestringOptional. Emits `name as <Type>: expression` instead of the inferred `name <- expression` form.
expressionstringyesThe expression exactly as written in the source ('base * base'); its first occurrence is the anchor.
filestringyesThe .ek9 source file (workspace-relative; an absolute path inside the workspace is accepted).
lineintegerOptional 1-based line of the occurrence to anchor on. Omit it - the expression text is enough.
namestringyesIdentifier for the new local. camelCase by EK9 convention; placeholder names (temp/data/value/…) are rejected.
replaceAllbooleanWhen true (default) replaces EVERY matching occurrence in the enclosing block; when false replaces only the located occurrence.
startColumninteger1-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 4 recorded are shown.

Expression in two functions no line refused

Request

{
  "expression" : "price * rate",
  "file" : "task.ek9",
  "name" : "charge"
}

Response

{
  "conflicts" : [ ],
  "edits" : [ ],
  "error" : {
    "message" : "expression 'price * rate' is on lines [11, 18], in different bodies - pass line (one of [11, 18]) to say which one to extract."
  },
  "meta" : {
    "serverMs" : "<timing>"
  },
  "status" : "error",
  "summary" : "expression 'price * rate' is on lines [11, 18], in different bodies - pass line (one of [11, 18]) to say which one to extract.",
  "wouldBeDiagnostics" : [ ],
  "written" : false
}

Occurrence after a write not replaced

Request

{
  "expression" : "total * 2",
  "file" : "task.ek9",
  "name" : "doubled"
}

Response

{
  "conflicts" : [ ],
  "edits" : [ {
    "after" : "      doubled <- total * 2",
    "endLine" : 9,
    "file" : "task.ek9",
    "startLine" : 9,
    "type" : "insert"
  }, {
    "after" : "      stdout.println($ doubled)",
    "before" : "      stdout.println($ (total * 2))",
    "endLine" : 9,
    "file" : "task.ek9",
    "startLine" : 9,
    "type" : "replace"
  } ],
  "meta" : {
    "serverMs" : "<timing>"
  },
  "risk" : {
    "crossesModuleBoundary" : false,
    "estimatedSeverity" : "trivial",
    "fileCount" : 1,
    "lineCount" : 2,
...

Cut short here: 9 more lines. The whole recorded call.

Return initialiser extracted

Request

{
  "expression" : "price * (1.0 + rate)",
  "file" : "task.ek9",
  "name" : "gross"
}

Response

{
  "conflicts" : [ ],
  "edits" : [ {
    "after" : "      <- rtn as String?",
    "before" : "      <- rtn as String: `net ${price} gross ${price * (1.0 + rate)} tax ${price * (1.0 + rate) - price}`",
    "endLine" : 10,
    "file" : "task.ek9",
    "startLine" : 10,
    "type" : "replace"
  }, {
    "after" : "      gross <- price * (1.0 + rate)\n      rtn: `net ${price} gross ${gross} tax ${gross - price}`",
    "endLine" : 11,
    "file" : "task.ek9",
    "startLine" : 11,
    "type" : "insert"
  } ],
  "meta" : {
    "serverMs" : "<timing>"
  },
  "risk" : {
    "crossesModuleBoundary" : false,
    "estimatedSeverity" : "trivial",
    "fileCount" : 1,
    "lineCount" : 2,
...

Cut short here: 9 more lines. The whole recorded call.

See also