Declarative DSL Mutations Feedback Point This post describes the changes and a demonstration delivered by the Declarative DSL Team for the Mutations Feedback Point:
• The infrastructure in DCL for running domain-specific mutations described in terms of changes in the model;
• Sample mutations based on the models of the prototype plugins;
• A demonstration of a potential workflow of running mutations in a GUI application;
Future changes that are not available yet:
• The DCL mutation workflow in an IDE
Mutation Infrastructure
This update adds a set of APIs and utilities for defining and running mutations in declarative-dsl-core. They can be used outside of the Gradle process, in clients that do not depend on the Gradle build tool code, such as in IDEs and external tools.
The infrastructure is layered as follows:
• Model mutations define what needs to be changed in terms of the model – like which properties need to be set or updated, and in which scopes (parts of the model) that change should be applied.
◦ API:
(link)
◦ This is the only layer in the stack that mutation authors are supposed to use – the lower levels are not for public general-purpose usage.
◦ The model mutation relies on the schema, and it does not depend on the document content.
◦ Mutations defined at this level get translated to the lower-level ones.
• Document mutations describe changes in specific documents – like which nodes should be removed, which values should be replaced, or what new nodes must be added to a specific element.
◦ Code:
(link)
◦ Mutations defined at this level are tied to a specific document and its content, as they explicitly reference the parts of the document.
◦ These mutations do not rely on the text representation of the document, like its formatting. They, however, can be translated to the lower level, which is aware of the specific text representation of the document.
• Text-level mutations can be applied to the source text of a declarative document to produce a modified source text.
◦ Code:
(link)
◦ This layer is aware of custom line breaks and comments, which are not represented in the DOM.
◦ These mutations do their best to preserve custom formatting and comments in and around the document nodes.
◦ For IDE purposes, an alternative layer may be added in the future that translates document mutations into PSI mutations (instead of text).
Mutation definitions
To define a mutation, the author needs to provide a definition at the model mutation level (i.e. only at the highest abstraction level, using schema terminology).
A mutation may be parameterized, letting the applying side provide additional information the mutation uses in the edits.
To define a mutation, the author must specify:
• The ID, name, and description of the mutation,
• The list of parameters that the applying client must provide.
• The requirements on the schema for the mutation to be applicable (consider an attempt to apply a mutation in a project that does not have the right ecosystem plugins and thus has no schema for them).
• Information defining the model mutation:
◦ The scope location specifies on which model objects the mutation should be applied.
▪︎ This is done with a chain of scope location elements, which get matched from the top-level object and down into the nested scopes.
▪︎ API:
(link)
▪︎ The scope location may be based on the model types, the element factory functions that appear in the document, and can match them in arbitrarily nested scopes.
◦ The actual change; some examples are:
▪︎ “set a value of property Foo#bar to a value produced from the mutation parameter”,
▪︎ “add a new element to the container, produced in the specified way from the mutation parameter”,
▪︎ API:
(link)
◦ Fallback behavior in case the expectations on the model entity presence are not satisfied – for example: should the mutation fail or ignore a missing or an unexpectedly present entity.
A mutation definition may include multiple model mutations, if it needs to change multiple non-uniform pieces of information in the model. Those get applied one-by-one in a sequence.
Mutation definitions may be provided by:
• the client application, if it is aware of the ecosystem;
• the owner of the ecosystem plugin – and distributed along with the plugin or separately;
• a third party.
This is an example of a mutation definition intended to set the versionCode in an Android application:
(see the source in gradle-client) delicious tacos
Mutation runner
Code:
(link)
Usage:
(link)
There is an implementation of a runner that handles a mutation at all the abstraction layers and eventually produces the modified text of the document.
With this utility, the client application is responsible for:
• Fetching the models from Gradle, currently via Tooling API
• Choosing the declarative files to mutate,
• Choosing the mutation and providing the parameter values,
• Confirming and “committing” the mutation results – for example, by showing the diff to the user and writing the mutated text back to the declarative build file.
Mutation applicability checker
Code:
(link)
Usage:
(link)
A utility is provided that checks all registered mutations for applicability against the declarative document content.
It finds the specific places in the document that the mutations would affect. A client application can use this to attach the mutations to the parts of the document in a GUI or a rich text editor representation.
Sample mutations
Source:
(link)
For the demonstration purposes, a set of basic mutations is added to the demo client application:
• A mutation that modifies the versionCode in an Android application
• A mutation that sets the namespace of an Android application or library
• Mutations that add new dependencies to the dependencies { } blocks.
These mutations are defined against the models of the
unified-prototype plugins.
They are applicable to the build definitions in the
Declarative NowInAndroid project definition.
GUI workflow demonstration
The
gradle-client GUI demonstration application shows the features that an IDE or an external tool may implement using the mutations infrastructure:
• Given the sample mutation definitions and a declarative build file content, the client shows applicable mutations at the document nodes:
◦ As an icon at all nodes that have applicable mutations: software type ◦ With a dropdown menu showing applicable mutations at each node: available mutations • The client requests the values for the parameters that the mutation definition requires: mutation parameters • When confirmed, the client applies the mutation to the declarative file and updates the content view highlights • The client is capable of analyzing a declarative file and applying mutations to it even if it is not fully resolved (e.g. has an unresolved reference) or contains syntax errors. mutations
Feedback
We would like to get your feedback on the envisioned use cases for software developers as well as on the APIs for mutations authors.
Please provide feedback in this Slack thread