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).
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
- The selection is NOT a single literal - a variable, call, or compound expression - use
ek9_extract_variable. - The selection is an interpolated backtick string (`x is ${y}`) - a runtime expression, not a compile-time literal - extract the embedded expression with
ek9_extract_variable. - You want to hoist an already-named local/expression to a constant rather than a raw literal - use
ek9_promote_to_constant. - You just want existing scattered constants consolidated (E11066/E11067), not a new one extracted - use
ek9_organize_constants.
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
| 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 | The .ek9 source file holding the literal (workspace-relative; an absolute path inside the workspace is accepted). |
line | integer | Optional 1-based line of the occurrence to anchor on. Omit it - the literal text is enough. | |
literal | string | yes | The 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. |
name | string | yes | Identifier for the new constant. Must not clash with an existing constant; placeholder names (temp/data/value/…) are rejected. |
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.
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
- Show me in the IDE: Extract Constant
- Fixes E11064
- Fixes E11065