ek9_extract_constant

Appends `name <- literal` to the module's `defines constant` block (created if absent) and replaces every occurrence of the literal in the file with the name; answers status, edits[] and summary with the occurrence count, a clashing name as conflicts[] (constant-name-collision).

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

When to use it

'Turn the magic "Hello" into a named constant' (the fix for E11065) is one call with file, literal:'"Hello"' and name:'greeting' - no line or column.

When not to use it

Details

Only a literal may be extracted (EK9 grammar `constantInitialiser : literal`): the selection must be one of the 18 Ek9LiteralTypes (Integer, Float, String, Money, Date, Colour, Duration, RegEx, …) and its exact source syntax is preserved verbatim (12.50#USD, #FF0000, PT2H). The literal is found at its first occurrence outside a comment (on 'line' when given); an IDE may pass the 1-based startColumn/endColumn pair instead. Placement appends to an existing `defines constant` block even when it lives in ANOTHER file (avoiding E11066/E11067 scattering; with 2+ blocks it picks the richest and attaches a 'constants-scattered' advisory), creating a block only when the module has none - so a changeset can span two files, though occurrence replacement covers the literal's OWN file only (V1). Conflict kinds: selection-is-not-a-literal, interpolated-string-not-constant, selection-is-already-a-constant, constant-name-collision; banned/placeholder identifiers are hard errors.

Arguments

ArgumentTypeRequiredWhat it is
endColumninteger1-based EXCLUSIVE column of the END of the span (an IDE selection). Omit it - pass the text instead.
filestringyesThe .ek9 source file holding the literal (workspace-relative; an absolute path inside the workspace is accepted).
lineintegerOptional 1-based line of the occurrence to anchor on. Omit it - the literal text is enough.
literalstringyesThe literal exactly as written in the source, quotes included for a string ('"Hello"', '42', '12.50#USD'); its first occurrence is extracted and every occurrence in the file replaced.
namestringyesIdentifier for the new constant. Must not clash with an existing constant; placeholder names (temp/data/value/…) are rejected.
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.

Extract constant terse dry run ready

Request

{
  "constantName" : "greeting",
  "dryRun" : true,
  "endColumn" : 29,
  "file" : "magic.ek9",
  "line" : 7,
  "startColumn" : 22,
  "terse" : true
}

Response

{
  "conflicts" : [ ],
  "edits" : [ {
    "endLine" : 7,
    "file" : "magic.ek9",
    "startLine" : 7,
    "type" : "replace"
  }, {
    "endLine" : 8,
    "file" : "magic.ek9",
    "startLine" : 8,
    "type" : "replace"
  }, {
    "endLine" : 3,
    "file" : "magic.ek9",
    "startLine" : 3,
    "type" : "insert"
  } ],
  "meta" : {
    "serverMs" : "<timing>"
  },
  "risk" : {
    "crossesModuleBoundary" : false,
    "estimatedSeverity" : "trivial",
...

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

Extract constant terse apply

Request

{
  "constantName" : "greeting",
  "endColumn" : 29,
  "file" : "magic.ek9",
  "line" : 7,
  "startColumn" : 22,
  "terse" : true
}

Response

{
  "conflicts" : [ ],
  "edits" : [ {
    "endLine" : 7,
    "file" : "magic.ek9",
    "startLine" : 7,
    "type" : "replace"
  }, {
    "endLine" : 8,
    "file" : "magic.ek9",
    "startLine" : 8,
    "type" : "replace"
  }, {
    "endLine" : 3,
    "file" : "magic.ek9",
    "startLine" : 3,
    "type" : "insert"
  } ],
  "meta" : {
    "serverMs" : "<timing>"
  },
  "risk" : {
    "crossesModuleBoundary" : false,
    "estimatedSeverity" : "trivial",
...

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

Literal not inside module name

Request

{
  "dryRun" : true,
  "file" : "task.ek9",
  "literal" : "0",
  "name" : "EMPTY_COUNT"
}

Response

{
  "conflicts" : [ ],
  "edits" : [ {
    "after" : "      if count == EMPTY_COUNT",
    "before" : "      if count == 0",
    "endLine" : 25,
    "file" : "task.ek9",
    "startLine" : 25,
    "type" : "replace"
  }, {
    "after" : "\n  defines constant\n    EMPTY_COUNT <- 0\n",
    "endLine" : 3,
    "file" : "task.ek9",
    "startLine" : 3,
    "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