Config plugin
The host language for configuration files. On its own it has no syntax at all — a .config file is a sequence of sections, and every section comes from a contribution plugin.
That makes this plugin the clearest demonstration of the extension model: it defines the shape of an extension point and nothing else.
At a glance
| Plugin id | config-service |
| Display name | Config |
| Description | Language support for configuration files (.config) |
| Default URL | /plugin/config |
| Source | app/packages/service-config, app/packages/language-config |
| Depends on | Nothing directly, but it is useless without plugins contributing sections |
Languages contributed
| Language id | Name | Extension | Textual editor | Graphical editor | Generated |
|---|---|---|---|---|---|
config | Config | .config | ✅ | ❌ | ❌ |
The config language
The complete grammar of the config language without contributions is: zero or more sections. With the two bundled contribution plugins enabled, a config file looks like this:
// Which model is optimised, and against which metamodel.
// Contributed by the Config Optimization plugin.
problem {
metamodel = "./shapes.mm"
model = "./shapes.m"
}
// What "better" means. Contributed by the Config Optimization plugin.
goal {
import { shapeCount, invisibleShapes, emptyLayers } from "./metrics.fn"
minimize invisibleShapes
maximize shapeCount
constraint emptyLayers
refine Canvas.layers[1..4]
}
// How new candidate models are derived. Contributed by the Config MDEO plugin.
search {
mutations {
using "./match.mt"
using "./control-flow.mt"
create Annotation
delete Annotation
mutate Circle
add Layer.shapes
remove Layer.shapes
mutate Canvas.layers
}
}
// Which search algorithm runs, and for how long. Contributed by the Config MDEO plugin.
solver {
algorithm = NSGAII
parameters {
population = 40
variation = mutation
mutation {
step = interval(1, 5)
strategy = random
selection = random
application = random
credit = random
repair = default
}
}
termination {
evolutions = 500
time = 600
delta = 5
iterations = 3
}
batches = 3
}
// Execution limits for the run. Contributed by the Config MDEO plugin.
runtime {
timeout {
script = 1000
transformation = 1000
}
backend = MDEO
resources {
threads = 10
nodes = 4
threadsPerNode = 3
}
}Each block is provided by a plugin:
| Section | Contributed by |
|---|---|
problem, goal | Config Optimization |
search, solver, runtime | Config MDEO |
Qualified section names
Every section can also be written as <section>.<plugin>:
// Every section can also be written with its fully qualified name, `<section>.<plugin>`.
// This is only required when two enabled plugins contribute a section with the same name,
// but it is always allowed and makes the origin of a section explicit.
problem.optimization {
metamodel = "./shapes.mm"
model = "./shapes.m"
}
goal.optimization {
import { shapeCount } from "./metrics.fn"
maximize shapeCount
}
search.mdeo {
mutations {
using "./match.mt"
}
}
solver.mdeo {
algorithm = SPEA2
parameters {
population = 20
variation = genetic
}
termination {
evolutions = 100
}
}The qualified form is always accepted. It becomes required when two enabled plugins contribute a section with the same name — in that case the plain name is ambiguous and is not part of the grammar at all.
Running a config file
A contribution plugin can mark a section as executable. When a config file contains such a section, the file gets a run action. The solver section of Config MDEO is the one executable section in the bundled set, which is why running a .config file starts an optimisation.
The config plugin does not know what running means. It looks up which contribution plugin owns the executable section, forwards the execution to that plugin's language service, and afterwards relays follow-up requests — summary, file tree, file contents, cancel, delete — to the same place.
Contribution plugins contributed
None. Config is the language that receives contributions.
The extension point it defines
A config contribution plugin hands the config language:
| Field | Meaning |
|---|---|
id | Unique id, used for dependency resolution |
name | Short name used in qualified section names |
languageKey | The language id whose service handles requests for this plugin |
grammar | A serialised grammar containing the rules for all its sections |
sections | The sections it contributes: name, rule, AST interface, and whether it is executable |
dependencies | Plugin ids whose exported types this grammar needs |
exportedTypes | Types this plugin makes available to plugins that depend on it |
sectionDependencies | Sections whose computed data must be available before this plugin's handler runs |
At startup the config language sorts the active contribution plugins topologically by dependencies, deserialises each grammar in a context containing the types exported by its dependencies, wraps every section in a rule that prefixes it with its keyword, and assembles them into a single root rule.
The result is a grammar that exists only for the plugin set of one project. Change the project's plugins and the .config language changes with it.
See Config contributions for how to write one, and Contribution plugins for the general mechanism.
Server-side capabilities
| File data key | Contents |
|---|---|
config | The merged section data, assembled from every contribution plugin's own computation |
Computing that data is a two-level process. The config service splits the file into the sections of each plugin, sends each fragment to the plugin that owns it as a config request, and merges the answers — respecting sectionDependencies, so a plugin that needs the problem section receives its computed data alongside its own text.