Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(414)

Unified Diff: pkg/analyzer_plugin/doc/tutorial/creating_edits.md

Issue 2973753003: Initial documentation for the plugin package (Closed)
Patch Set: Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analyzer_plugin/doc/tutorial/creating_edits.md
diff --git a/pkg/analyzer_plugin/doc/tutorial/creating_edits.md b/pkg/analyzer_plugin/doc/tutorial/creating_edits.md
new file mode 100644
index 0000000000000000000000000000000000000000..9fc69088817130338ab157e8175e59d0f4b670bc
--- /dev/null
+++ b/pkg/analyzer_plugin/doc/tutorial/creating_edits.md
@@ -0,0 +1,205 @@
+# 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.
+
+## `DartChangeBuilder`
+
+The class used to create a `SourceChange` is `DartChangeBuilder`, defined in
+`package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart`.
+You can create a `DartChangeBuilder` with the following:
+
+```dart
+DartChangeBuilder changeBuilder = new DartChangeBuilder(session);
+```
+
+The constructor required 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 `addFileEdit`, as
+illustrated by the following:
+
+```dart
+changeBuilder.addFileEdit(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 of 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.writeOverrideOfInheritedMember(
+ typeProvider.objectType.getMethod('toString'));
+ });
+});
+```
+
+## 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 `DartChangeBuilder`.
+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 of the edits for that 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 of 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
+
+All of 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`.

Powered by Google App Engine
This is Rietveld 408576698