Skip to content

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 idconfig-service
Display nameConfig
DescriptionLanguage support for configuration files (.config)
Default URL/plugin/config
Sourceapp/packages/service-config, app/packages/language-config
Depends onNothing directly, but it is useless without plugins contributing sections

Languages contributed

Language idNameExtensionTextual editorGraphical editorGenerated
configConfig.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:

mdeo-config
// 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:

SectionContributed by
problem, goalConfig Optimization
search, solver, runtimeConfig MDEO

Qualified section names

Every section can also be written as <section>.<plugin>:

mdeo-config
// 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:

FieldMeaning
idUnique id, used for dependency resolution
nameShort name used in qualified section names
languageKeyThe language id whose service handles requests for this plugin
grammarA serialised grammar containing the rules for all its sections
sectionsThe sections it contributes: name, rule, AST interface, and whether it is executable
dependenciesPlugin ids whose exported types this grammar needs
exportedTypesTypes this plugin makes available to plugins that depend on it
sectionDependenciesSections 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 keyContents
configThe 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.

Released under the terms of the repository licence.