
Description
Use when authoring or debugging MPS TextGen — concept-to-plain-text serialisation for languages whose final output is text (source files, config, scripts, DDL, XML, markdown). Covers `ConceptTextGenDeclaration` roots, `append` / `indent buffer` / `with indent` statements, text layout areas, context objects (e.g. imports sets), attributes, the base text gen component (`LanguageTextGenDeclaration`), binary `write`, `found error`, and the indentation-buffer model. Reach for this skill whenever the task involves editing `<lang>/languageModels/textGen.mps`.
SKILL.md
MPS TextGen Aspect
TextGen turns a model (usually the output of generation) into plain text files. It is how BaseLanguage becomes .java on disk, and how any text-targeting language serialises its models. Lives in <lang>/languageModels/textGen.mps, language jetbrains.mps.lang.textGen. Rule bodies are BaseLanguage + smodel + textgen-specific statements (append, indent buffer, with indent).
Prerequisite for any insert: the textGen model must exist (mps_mcp_create_model with modelName: "<lang>.textGen" — aspect ID textGen, case-sensitive, no @ suffix; see aspect-model-stereotypes.md) and must import jetbrains.mps.lang.textGen, jetbrains.mps.baseLanguage, and jetbrains.mps.lang.smodel as used languages before the first mps_mcp_insert_root_node_from_json. Missing any of these three causes node inserts to fail with unresolved-concept errors. See step 1 of the Common-Path Workflow.
Critical Directives
- One
ConceptTextGenDeclarationroot per concept you want to serialise. Only the file-generating root concept needsextension/filename/encoding; structural concepts inside the file need onlytextGenBlock. extensionis a function body returning a string, not a literal property. It mustreturna string.- TextGen dispatch is concept-exact, not polymorphic. An extending concept that wants the parent's serialisation must declare its own
ConceptTextGenDeclaration(even an empty one) — or its output will be silently missing. Seereferences/dispatch-and-base-component.md. - The indentation buffer is a per-output-file depth counter.
with indent/increase depth/decrease depthand thewithIndentflag onNodeAppendPartonly mutate the counter — whitespace is emitted only whenindent bufferis called. Always pairappend \n ;withindent buffer ;on the next line that should be indented. Seereferences/indentation-model.md. - Prefer
with indent { ... }over pairedincrease depth ; … ; decrease depth ;— the block form cannot leak depth on an earlyreturnor exception. Use the paired form only when the scope is not a block. - Use TextGen only when the final artifact is text. If your pipeline ends in a model-to-model transformation targeting another MPS language (e.g. BaseLanguage), you don't need TextGen — that language's own TextGen handles the final step.
BinaryWriteOperation(write) cannot be mixed with textappends in the sameConceptTextGenDeclaration. A rule emits either text or bytes, not both.- The older
$ref{node.reference<target>}syntax is deprecated. Use aNodeAppendPartover a resolved node (or over.name). - Edit textGen models through MPS MCP tools (
mps_mcp_insert_root_node_from_json,mps_mcp_update_node). Do not hand-edit.mpsfiles. - After edits run
mps_mcp_check_root_node_problemsand rebuild the language; regenerate consumers.
Common-Path Workflow
- Create a
textGenmodel (mps_mcp_create_modelwithmodelName: "<lang>.textGen"; aspect ID istextGen— case-sensitive, no@suffix) if absent, then before any insert add the three required used languages:jetbrains.mps.lang.textGen(forConceptTextGenDeclaration,AppendOperation, parts,NodeParameter, etc.),jetbrains.mps.baseLanguage(forStatementList,DotExpression, control flow, returns), andjetbrains.mps.lang.smodel(forSPropertyAccess,SLinkAccess,SLinkListAccessused to read node data). Also add the structure language whose concepts you serialise (referenced byconceptDeclarationand inside smodel accesses), andjetbrains.mps.baseLanguage.collectionsif you use maps/lists. Skipping any of the three core languages causesmps_mcp_insert_root_node_from_jsonto fail with unresolved-concept errors — don't add them piecemeal after a failure. - For each concept to serialise, insert a
ConceptTextGenDeclarationroot viamps_mcp_insert_root_node_from_json. The file-generating concept also needsextension, optionallyencoding/filename/filePath/layout/contextObjects. - Fill the
textGenBlockbody — aStatementListofappends,indent buffer,with indent { ... }, and standard BaseLanguage control flow. Seereferences/statements-and-appends.mdfor the part vocabulary andreferences/json-blueprints.mdfor AST shapes. - Push formatting heuristics ("should this go on a new line?") into the behavior aspect and call them back from textgen (
node.hasNewLineAfter()pattern). Seereferences/delegating-to-behavior.md. - For large-file languages (imports area, headers, shared state), declare a
TextUnitLayout, aUnitContextDeclarationin the baseLanguageTextGenDeclaration, and per-conceptUnitContextObjects. Seereferences/layout-and-context-objects.md. - Validate with
mps_mcp_check_root_node_problems, rebuild the language module, regenerate any consumer, and inspect the produced text.
TextGen vs. Generator
| Aspect | Role |
|---|---|
| Generator | Model → Model (typed AST transformation) |
| TextGen | Model → Text (final serialisation) |
A custom language usually has both: a generator rewrites into BaseLanguage + runtime API, and BaseLanguage's TextGen emits .java. Write your own TextGen only if you target text directly (as jetbrains.mps.core.xml does).
Related Skills
mps-aspect-generator— model-to-model step that usually precedes TextGen; emits the AST you serialise.mps-aspect-behavior— host for layout-heuristic helper methods called from textgen bodies.mps-model-manipulation— BaseLanguage + smodel + collections used inside textgen bodies (StatementList,DotExpression,SLinkAccess,SLinkListAccess,StaticMethodCall).mps-quotations— anti-quotations may appear insideQuotations used as${...}expression values.mps-aspect-structure-concepts— when introducing the concepts a textgen will serialise.
Reference Index
- Open
references/concept-textgen-root.mdwhen adding or editing aConceptTextGenDeclaration— slot purposes (extension,encoding,filename,filePath,layout,contextObjects,textGenBlock), the editor shorthand, and what each child holds. - Open
references/statements-and-appends.mdwhen writing the body —AppendOperationand its part vocabulary (ConstantStringAppendPart,NodeAppendPart,CollectionAppendPart,NewLineAppendPart), the$list{... with sep}form,BinaryWriteOperation(write),FoundErrorOperation(found error), control flow, and the smodel/behavior accessors usable inside a body. - Open
references/indentation-model.mdwhen indentation is wrong — the depth-counter mental model, rules of thumb (always pair\nwithindent buffer, preferwith indent { ... }, usewithIndentflag for one-shot dispatch), and worked examples fromIfStatement_TextGen,BlockStatement_TextGen, andClassConcept_TextGen. - Open
references/layout-and-context-objects.mdwhen emitting multi-region files (headers, imports, body) or threading per-generation state —TextUnitLayoutwithappend to AREA { ... },LanguageTextGenDeclaration,UnitContextDeclarationtypes, and per-conceptUnitContextObjectbindings (the imports-set pattern). Also covers attribute dispatch order (reverse containment) and${attributed node}. - Open
references/dispatch-and-base-component.mdfor concept-exact dispatch — why an extending concept needs its own (possibly empty)ConceptTextGenDeclaration, and how the baseLanguageTextGenDeclarationexposes reusableOperationDeclarations /UtilityMethodDeclarations /UnitContextDeclarations; inheriting between language base components viaextends. - Open
references/delegating-to-behavior.mdwhen textgen bodies grow tangled with formatting heuristics — the xml pattern of movinghasNewLineAfter/isMultiline/onNewLineinto behavior and calling them from textgen, plus utility classes (XmlCharEscape) for escaping/formatting. - Open
references/json-blueprints.mdwhen inserting nodes via MCP — full validated AST shapes for each statement/part (AppendOperationwith literal/expression/list parts,WithIndentOperation,IncreaseDepthOperation/DecreaseDepthOperation,NodeAppendPartwithwithIndent), the minimal file-generating-concept skeleton, layout/context skeletons, and the full validated concept reference (FQNs, concept refs, child roles). - Open
references/common-failures.mdwhen output is missing, indentation is broken, file extension is wrong, special characters appear unescaped, subclasses emit nothing, imports leak inline, attributes are ignored, orwriteandappendcollide.
More skills from the MPS repository
View all 31 skillsmps-aspect-accessories
configure JetBrains MPS module dependencies
Jul 17ArchitectureConfigurationEngineeringmps-aspect-actions
define and edit MPS node factories
Jul 17ArchitectureEngineeringmps-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 Components
More from JetBrains
View publishermps-aspect-editor-menus-and-keymaps
author MPS editor menus and keymaps
MPS
Jul 23EngineeringUI Componentsmps-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