# Creating `SourceChange`s

Several of the response objects take a `SourceChange` (specifically, assists,
fixes, and refactorings). Because `SourceChange` is a structured object that
can be difficult to create correctly, this package provides a set of utility
classes to help you build those structures.

Using these classes will not only simplify the work you need to do to implement
your plugin, but will ensure a consistent user experience in terms of the code
being generated by the analysis server.

## `ChangeBuilder`

The class used to create a `SourceChange` is `ChangeBuilder`, defined in
`package:analyzer_plugin/utilities/change_builder/change_builder_core.dart`.
You can create a `ChangeBuilder` with the following:

```dart
ChangeBuilder changeBuilder = new ChangeBuilder(session: session);
```

The constructor requires an instance of the class `AnalysisSession`. How you get
the correct instance depends on where the constructor is being invoked.

A `SourceChange` can contain edits that are to be applied to multiple files. The
edits for a single file are created by invoking the method `addDartFileEdit`, as
illustrated by the following:

```dart
await changeBuilder.addDartFileEdit(path,
    (DartFileEditBuilder fileEditBuilder) {
  // ...
});
```

where the `path` is the path to the file to which the edits will be applied.

## `DartFileEditBuilder`

The class `DartFileEditBuilder` defines methods for creating three kinds of
edits: deletions, insertions, and replacements.

For deletions, you pass in the range of code to be deleted as a `SourceRange`.
In addition to the constructor for `SourceRange`, there are a set of functions
defined in `package:analyzer_plugin/utilities/range_factory.dart` that can be
used to build a `SourceRange` from tokens, AST nodes, and elements.

For example, if you need to remove the text in a given `range`, you could write:

```dart
fileEditBuilder.addDeletion(range);
```

In the case of insertions and replacements, there are two styles of method. The
first takes the string that is to be inserted; the second takes a closure in
which the string can be composed. Insertions take the offset of the insertion,
while replacements take a `SourceRange` indicating the location of the text to
be replaced.

For example, if you need to insert `text` at offset `offset`, you could write

```dart
fileEditBuilder.addSimpleInsertion(offset, text);
```

The forms that take a closure are useful primarily because they give you access
to a `DartEditBuilder`, which is described below.

For example, to replace a given `range` of text with some yet to be constructed
text, you could write:

```dart
fileEditBuilder.addReplacement(range, (DartEditBuilder editBuilder) {
  // ...
}
```

In addition, `DartFileEditBuilder` has some methods that allow you to build some
common sets of edits more easily. For example, `importLibraries` allows you to
pass in the `Source`s for one or more libraries and will create one or more
edits to insert `import` directives in the correct locations.

## `DartEditBuilder`

A `DartEditBuilder` allows you to compose source code by writing the individual
pieces, much like a `StringSink`. It also provides additional methods to compose
more complex code. For example, if you need to write a type annotation, the
method `writeType` will handle writing all the type arguments and will add
import directives as needed. There are also methods to write class declarations
and to write various members within a class.

For example, if you're implementing a quick assist to insert a template for a
class declaration, the code to create the insertion edit could look like the
following:

```dart
String className = 'NewClass';
fileEditBuilder.addReplacement(range, (DartEditBuilder editBuilder) {
  editBuilder.writeClassDeclaration(className, memberWriter: () {
    editBuilder.writeConstructorDeclaration(className);
    editBuilder.writeOverride(
        typeProvider.objectType.getMethod('toString').type);
  });
});
```

## Utility methods

As was mentioned briefly above, many of these classes provide utility methods.
For the best UX, it's important to use these utility method when possible. They
often do more than just write some simple text; they can take care of other
details, such as adding required imports, and adherence to style preferences
that the user has expressed (typically by enabling lints).

## Linked edits

Many clients support a style of editing in which multiple regions of text can be
edited simultaneously. Server refers to these as "linked" edit groups. Many
clients also support having multiple groups associated with the edits in a file
and allow users to tab from one group to the next. Essentially, these edit
groups mark placeholders for text that users might want to change after the
edits are applied.

The class `DartEditBuilder` provides support for creating linked edits through
the method `addLinkedEdit`. As with the insertion and replacement methods
provided by `DartFileEditBuilder` (see above), there are both a "simple" and a
closure-based version of this method.

For example, if you're implementing a quick assist to insert a for loop, you
should add the places where the loop variable name appears to a linked edit
group. You should also add the name of the list being iterated over to a
different group. The code to create the insertion edit could look like the
following:

```dart
fileEditBuilder.addReplacement(range, (DartEditBuilder editBuilder) {
  String listName = 'list';
  String listGroup = 'list_variable';
  String variableName = 'i';
  String variableGroup = 'loop_variable';

  editBuilder.write('for (int ');
  editBuilder.addSimpleLinkedEdit(variableGroup, variableName);
  editBuilder.write(' = 0; ');
  editBuilder.addSimpleLinkedEdit(variableGroup, variableName);
  editBuilder.write(' < ');
  editBuilder.addSimpleLinkedEdit(listGroup, listName);
  editBuilder.write('.length; ');
  editBuilder.addSimpleLinkedEdit(variableGroup, variableName);
  editBuilder.write('++) {}');
}
```

One of the advantages of the closure-based form of `addLinkedEdit` is that you
can specify suggested replacements for the values of each group. You do that by
invoking either `addSuggestion` or  `addSuggestions`. In the example above, you
might choose to suggest `j` and `k` as other likely loop variable names. You
could do that by replacing one of the places where the variable name is written
with code like the following:

```dart
editBuilder.addLinkedEdit(variableGroup, (LinkedEditBuilder linkedEditBuilder) {
  linkedEditBuilder.write(variableName);
  linkedEditBuilder.addSuggestions(['j', 'k']);
});
```

A more interesting use of this feature would be to find the names of all of the
list-valued variables within scope and suggest those names as alternatives for
the name of the list.

That said, most of the methods on `DartEditBuilder` that help you generate Dart
code take one or more optional arguments that allow you to create linked edit
groups for appropriate pieces of text and even to specify the suggestions for
those groups.

## Post-edit selection

A `SourceChange` also allows you to specify where the cursor should be placed
after the edits are applied. There are two ways to specify this.

The first is by invoking the method `setSelection` on a `ChangeBuilder`.
The method takes a `Position`, which encapsulates an offset in a particular
file. This can be difficult to get right because the offset is required to be
the offset *after* all the edits for the file have been applied.

The second, and easier, way is by invoking the method `selectHere` on a
`DartEditBuilder`. This method does not require any arguments; it computes the
offset for the position based on the edits that have previously been created.
It does require that all the edits that apply to text before the desired
cursor location have been created before the method is invoked.

For example, if you're implementing a quick assist to insert a to-do comment at
the cursor location, the code to create the insertion edit could look like the
following:

```dart
fileEditBuilder.addReplacement(range, (DartEditBuilder editBuilder) {
  editBuilder.write('/* TODO ');
  editBuilder.selectHere();
  editBuilder.write(' */');
}
```

This will cause the cursor to be placed between the two spaces inside the
comment.

## Non-dart files

The classes above are subclasses of more general classes (just drop the
prefix "Dart" from the subclass names). If you are editing files that do not
contain Dart code, the more general classes might be a better choice. These
classes are defined in
`package:analyzer_plugin/utilities/change_builder/change_builder_core.dart`.
