Quick Fixes

For validations written using the AbstractDeclarativeValidator it is possible to provide corresponding quick fixes in the editor. To be able to implement a quick fix for a given diagnostic (a warning or error) the underlying cause of the diagnostic must be known (i.e. what actual problem does the diagnostic represent?), otherwise the fix doesn’t know what needs to be done. As we don’t want to deduce this from the diagnostic’s error message we associate a problem specific code with the diagnostic.

In the following example taken from the DomainmodelJavaValidator the diagnostic’s code is given by the third argument to the warning() method and it is a reference to the static String field _==INVALID_TYPE_NAME==_ in the validator class.

warning("Name should start with a capital", 
    DomainmodelPackage.TYPE__NAME, INVALID_TYPE_NAME, type.getName());

Now that the validation has a unique code identifying the problem we can register quick fixes for it. We start by adding the QuickfixProviderFragment to our workflow and after regenerating the code we should find an empty class MyDslQuickfixProvider in our DSL’s UI project and new entries in the _==plugin.xml_gen==_ file.

Continuing with the _==INVALID_TYPE_NAME==_ problem from the Domainmodel example we add a method with which the problem can be fixed (have a look at the DomainmodelQuickfixProvider for details):

@Fix(DomainmodelJavaValidator.INVALID_TYPE_NAME)
public void fixName(final Issue issue, IssueResolutionAcceptor acceptor) {
  acceptor.accept(issue, 
    "Capitalize name", // quick fix label
    "Capitalize name  of '" + issue.getData()[0] + "'",  // description 
    "upcase.png",      // quick fix icon
    new IModification() {
      public void apply(IModificationContext context) 
                                             throws BadLocationException {
        IXtextDocument xtextDocument = context.getXtextDocument();
        String firstLetter = xtextDocument.get(issue.getOffset(), 1);
        xtextDocument.replace(issue.getOffset(), 1, 
                              Strings.toFirstUpper(firstLetter));
      }
    }
  );
}

By using the correct signature (see below) and annotating the method with the @Fix annotation referencing the previously specified issue code from the validator, Xtext knows that this method implements a fix for the problem. This also allows us to annotate multiple methods as fixes for the same problem.

The first three parameters given to the IssueResolutionAcceptor define the UI representation of the quick fix. As the document is not necessarily loaded when the quick fix is offered, we need to provide any additional data from the model that we want to refer to in the UI when creating the issue in the validator above. In this case, we provided the existing type name. The additional data is available as Issue.getData(). As it is persisted in markers, only strings are allowed.

The actual model modification is implemented in the IModification. The IModificationContext provides access to the erroneous document. In this case, we’re using Eclipse’s IDocument API to replace a text region.

If you prefer to implement the quick fix in terms of the semantic model use a ISemanticModification instead. Its apply(EObject, IModificationContext) method will be invoked inside a modify-transaction and the first argument will be the erroneous semantic element. This makes it very easy for the fix method to modify the model as necessary. After the method returns the model as well as the Xtext editor’s content will be updated accordingly. If the method fails (throws an exception) the change will not be committed. The following snippet shows a semantic quick fix for a similar problem.

@Fix(DomainmodelJavaValidator.INVALID_FEATURE_NAME)
public void fixFeatureName(final Issue issue, 
                           IssueResolutionAcceptor acceptor) {
  acceptor.accept(issue, 
    "Uncapitalize name",    // label
    "Uncapitalize name of '" + issue.getData()[0] + "'", // description
    "upcase.png",           // icon 
    new ISemanticModification() {
      public void apply(EObject element, IModificationContext context) {
        ((Feature) element).setName(
            Strings.toFirstLower(issue.getData()[0]));
      }
    }
  );
}

Quickfixes for Linking Errors and Syntax Errors

You can even define quick fixes for linking errors. The issue codes are assigned by the ILinkingDiagnosticMessageProvider. Have a look at the domain model example how to add quick fixes for these errors.

Analogously, there is the ISyntaxErrorMessageProvider to assign issue codes to syntactical errors.