
Description
Use when defining or editing MPS node factories (the "actions" aspect) — `NodeFactories` roots, per-concept `NodeFactory` setup functions that initialize a freshly created node and optionally copy data from a replaced `sampleNode`, plus the actions aspect's `CopyPasteHandlers` and `PasteWrappers` roots. Reach for this skill when a substitution, side transform, completion replacement, or `add new initialized(...)` should preserve fields from the node it is replacing, or when defaults set in a constructor are not enough.
SKILL.md
MPS Actions Aspect
The actions aspect customizes how new nodes of a concept are constructed by the MPS editor — typically during substitution, side transformations, right-transform/<ctrl-space> replacement, or auto-replace. The framework produces an empty instance of the target concept; the setup function then populates it, often copying fields from the node it is replacing (sampleNode). Lives in <lang>/models/<lang>.actions.mps, language jetbrains.mps.lang.actions. The aspect also hosts copy/paste handlers and paste wrappers (see references/copy-paste-and-paste-wrappers.md).
Critical Directives
- One
NodeFactoriesroot per actions model; add per-conceptNodeFactorychildren to it. Do not create one root per concept. - One
NodeFactoryperapplicableConcept. Multiple factories for the same concept are not merged. - The setup function runs before
newNodeis attached to the model.newNode.parent, ancestors, and model-wide queries return null/empty. Use theenclosingNodeimplicit parameter when you need the would-be parent. sampleNodeis typed asnode<BaseConcept>. Always narrow withifInstanceOf (sampleNode is <Concept> original)before accessing fields — direct dereference will NPE or fail to compile.- Use
SPropertyAccess(refproperty→PropertyDeclaration) for properties; useSLinkAccess(reflink→LinkDeclaration) for both containment and reference child roles. - Factories do not fire for quotations
<C()>,model.add root(<C()>),model.add new root(C), or pure rendering. If you need factory-initialised nodes in generator code, usenew node<C>()with explicit property assignments or route creation through an intention that callsadd new initialized. - Edit factories through MPS MCP tools (
mps_mcp_insert_root_node_from_json,mps_mcp_update_node, optionallymps_mcp_parse_java_and_insertwithfeatureKind: "STATEMENTS"for the body). Do not hand-edit.mpsfiles. - After editing, validate with
mps_mcp_check_root_node_problemson theNodeFactoriesroot and rebuild the language.
Common-Path Workflow
- Ensure an actions model exists (
<lang>/models/<lang>.actions.mps; create withmps_mcp_create_modelandmodelName: "<lang>.actions"— aspect IDactions, case-sensitive, no@suffix; see aspect-model-stereotypes.md). Used languages:jetbrains.mps.lang.actions,jetbrains.mps.baseLanguage,jetbrains.mps.lang.smodel. Addjetbrains.mps.lang.coreas the base. Import the structure model of your language. - Create the
NodeFactoriesroot viamps_mcp_insert_root_node_from_json(blueprint inreferences/json-blueprints.md). Setname. - For each concept that needs custom initialization, add a
NodeFactorychild viamps_mcp_update_node. SetapplicableConcept; attach aNodeSetupFunctionwith aStatementListbody. - Fill the body. Typical pattern:
ifInstanceOf (sampleNode is <Concept> original) { newNode.<prop> = original.<prop>; ... }. Cross-type narrowing is allowed — seereferences/setup-function-bodies.md. - Validate with
mps_mcp_check_root_node_problems, rebuild the language, exercise in a sandbox.
Implicit Parameters of NodeSetupFunction
| Alias | FQN | Type | Nullable | Notes |
|---|---|---|---|---|
newNode | jetbrains.mps.lang.actions.structure.NodeSetupFunction_NewNode | node<applicableConcept> | no | The freshly created empty node. Populate it. |
sampleNode | jetbrains.mps.lang.actions.structure.NodeSetupFunction_SampleNode | node<BaseConcept> | yes | Node being replaced, if any. Narrow with ifInstanceOf. |
enclosingNode | jetbrains.mps.lang.actions.structure.NodeSetupFunction_EnclosingNode | node<BaseConcept> | yes | Prospective parent container; null during root-node creation or programmatic creation outside the editor. |
index | jetbrains.mps.lang.actions.structure.NodeSetupFunction_Index | int | — | 0-based position in the parent collection, or 0 if not in a collection. |
model | (plain VariableReference in generated Java) | SModel | yes | The SModel newNode will be inserted into. Most factories don't use this. |
Signature surfaced in the editor: (newNode, sampleNode, enclosingNode, index, model) -> void.
Related Skills
mps-aspect-behavior— concept constructors are the place to set defaults that don't depend on the node being replaced. Use a constructor for intrinsic defaults, aNodeFactoryfor context-sensitive carry-over.mps-aspect-intentions— intentions that calladd new initialized(...)route through factories. TheNF_*family is documented in the intentions skill'sreferences/factory-initialized.md.mps-aspect-generator— generator output bypasses factories (quotations andadd new rootdo not fire factories). Document where the generator must mimic the factory behavior.mps-model-manipulation— full smodel reference:SLinkAccessvsSPropertyAccess,IfInstanceOfStatement,IfInstanceOfVariable, and theNF_*family.mps-aspect-structure-concepts— when adding the concept thatapplicableConcepttargets.mps-aspect-constraints— forcanBe*rules that gate insertion before a factory ever runs.
Reference Index
- Open
references/node-factories-and-triggers.mdwhen you need the validated concept ref forNodeFactories/NodeFactory/NodeSetupFunction, the rule that factories are picked by exactapplicableConcept(not by subtype inheritance through a more-general parent factory), and the precise list of triggers that fire / do not fire a factory. - Open
references/setup-function-bodies.mdwhen writing the function body — copying properties vs. child links (SPropertyAccessvsSLinkAccess), cross-type narrowing inifInstanceOf, the verbatim ChemMastery (same-type property copy) and Kaja (cross-type child-link copy) examples, and the JSON shape for anAssignmentExpressionoverSLinkAccess. - Open
references/json-blueprints.mdwhen inserting theNodeFactoriesroot or aNodeFactorychild via MCP — minimal blueprints, the fullIfInstanceOfStatementbody, and the tip on usingmps_mcp_parse_java_and_insertto skip the blueprint. - Open
references/copy-paste-and-paste-wrappers.mdwhen the language needs custom copy pre-processing, paste post-processing, or paste-time wrapping of nodes — these other roots also live in the actions aspect. - Open
references/concept-reference.mdfor the validated FQN/ref table — thejetbrains.mps.lang.actionsconcepts plus the supporting BaseLanguage and smodel concepts used in setup bodies. - Open
references/common-failures.mdwhen a freshly created node always has defaults,sampleNode.<prop>does not compile,enclosingNodeis unexpectedly null, the factory fires twice, or it does not fire from quotations / generator code.
More skills from the MPS repository
View all 31 skillsmps-aspect-accessories
configure JetBrains MPS module dependencies
Jul 17ArchitectureConfigurationEngineeringmps-aspect-behavior
define and edit MPS concept behavior
Jul 13ArchitectureEngineeringmps-aspect-constraints
define JetBrains MPS language constraints
Jul 23ArchitectureCode Analysismps-aspect-dataflow
define and debug MPS dataflow builders
Jul 13Data Analysismps-aspect-editor
define MPS editor layouts
Jul 23DesignUI Componentsmps-aspect-editor-menus-and-keymaps
author MPS editor menus and keymaps
Jul 23EngineeringUI Components
More from JetBrains
View publishermps-aspect-generation-plan
modify MPS generation plans
MPS
Jul 13ArchitectureEngineeringmps-aspect-generator
define JetBrains MPS generator rules
MPS
Jul 17ArchitectureCode AnalysisEngineeringmps-aspect-intentions
define and edit MPS intentions
MPS
Jul 23ArchitectureEngineeringmps-aspect-migrations
author and debug MPS migration scripts
MPS
Jul 13DebuggingMigrationmps-aspect-structure-concepts
define concepts in MPS structure aspect
MPS
Jul 23Data Modeling