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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 # Creating `SourceChange`s
2
3 Several of the response objects take a `SourceChange` (specifically, assists,
4 fixes, and refactorings). Because `SourceChange` is a structured object that
5 can be difficult to create correctly, this package provides a set of utility
6 classes to help you build those structures.
7
8 Using these classes will not only simplify the work you need to do to implement
9 your plugin, but will ensure a consistent user experience in terms of the code
10 being generated by the analysis server.
11
12 ## `DartChangeBuilder`
13
14 The class used to create a `SourceChange` is `DartChangeBuilder`, defined in
15 `package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart`.
16 You can create a `DartChangeBuilder` with the following:
17
18 ```dart
19 DartChangeBuilder changeBuilder = new DartChangeBuilder(session);
20 ```
21
22 The constructor required an instance of the class `AnalysisSession`. How you get
23 the correct instance depends on where the constructor is being invoked.
24
25 A `SourceChange` can contain edits that are to be applied to multiple files. The
26 edits for a single file are created by invoking the method `addFileEdit`, as
27 illustrated by the following:
28
29 ```dart
30 changeBuilder.addFileEdit(path, (DartFileEditBuilder fileEditBuilder) {
31 // ...
32 }
33 ```
34
35 where the `path` is the path to the file to which the edits will be applied.
36
37 ## `DartFileEditBuilder`
38
39 The class `DartFileEditBuilder` defines methods for creating three kinds of
40 edits: deletions, insertions, and replacements.
41
42 For deletions, you pass in the range of code to be deleted as a `SourceRange`.
43 In addition to the constructor for `SourceRange`, there are a set of functions
44 defined in `package:analyzer_plugin/utilities/range_factory.dart` that can be
45 used to build a `SourceRange` from tokens, AST nodes, and elements.
46
47 For example, if you need to remove the text in a given `range`, you could write:
48
49 ```dart
50 fileEditBuilder.addDeletion(range);
51 ```
52
53 In the case of insertions and replacements, there are two styles of method. The
54 first takes the string that is to be inserted; the second takes a closure in
55 which the string can be composed. Insertions take the offset of the insertion,
56 while replacements take a `SourceRange` indicating the location of the text to
57 be replaced.
58
59 For example, if you need to insert `text` at offset `offset`, you could write
60
61 ```dart
62 fileEditBuilder.addSimpleInsertion(offset, text);
63 ```
64
65 The forms that take a closure are useful primarily because they give you access
66 to a `DartEditBuilder`, which is described below.
67
68 For example, to replace a given `range` of text with some yet to be constructed
69 text, you could write:
70
71 ```dart
72 fileEditBuilder.addReplacement(range, (DartEditBuilder editBuilder) {
73 // ...
74 }
75 ```
76
77 In addition, `DartFileEditBuilder` has some methods that allow you to build some
78 common sets of edits more easily. For example, `importLibraries` allows you to
79 pass in the `Source`s for one or more libraries and will create one or more
80 edits to insert `import` directives in the correct locations.
81
82 ## `DartEditBuilder`
83
84 A `DartEditBuilder` allows you to compose source code by writing the individual
85 pieces, much like a `StringSink`. It also provides additional methods to compose
86 more complex code. For example, if you need to write a type annotation, the
87 method `writeType` will handle writing all of the type arguments and will add
88 import directives as needed. There are also methods to write class declarations
89 and to write various members within a class.
90
91 For example, if you're implementing a quick assist to insert a template for a
92 class declaration, the code to create the insertion edit could look like the
93 following:
94
95 ```dart
96 String className = 'NewClass';
97 fileEditBuilder.addReplacement(range, (DartEditBuilder editBuilder) {
98 editBuilder.writeClassDeclaration(className, memberWriter: () {
99 editBuilder.writeConstructorDeclaration(className);
100 editBuilder.writeOverrideOfInheritedMember(
101 typeProvider.objectType.getMethod('toString'));
102 });
103 });
104 ```
105
106 ## Linked Edits
107
108 Many clients support a style of editing in which multiple regions of text can be
109 edited simultaneously. Server refers to these as "linked" edit groups. Many
110 clients also support having multiple groups associated with the edits in a file
111 and allow users to tab from one group to the next. Essentially, these edit
112 groups mark placeholders for text that users might want to change after the
113 edits are applied.
114
115 The class `DartEditBuilder` provides support for creating linked edits through
116 the method `addLinkedEdit`. As with the insertion and replacement methods
117 provided by `DartFileEditBuilder` (see above), there are both a "simple" and a
118 closure-based version of this method.
119
120 For example, if you're implementing a quick assist to insert a for loop, you
121 should add the places where the loop variable name appears to a linked edit
122 group. You should also add the name of the list being iterated over to a
123 different group. The code to create the insertion edit could look like the
124 following:
125
126 ```dart
127 fileEditBuilder.addReplacement(range, (DartEditBuilder editBuilder) {
128 String listName = 'list';
129 String listGroup = 'list_variable';
130 String variableName = 'i';
131 String variableGroup = 'loop_variable';
132
133 editBuilder.write('for (int ');
134 editBuilder.addSimpleLinkedEdit(variableGroup, variableName);
135 editBuilder.write(' = 0; ');
136 editBuilder.addSimpleLinkedEdit(variableGroup, variableName);
137 editBuilder.write(' < ');
138 editBuilder.addSimpleLinkedEdit(listGroup, listName);
139 editBuilder.write('.length; ');
140 editBuilder.addSimpleLinkedEdit(variableGroup, variableName);
141 editBuilder.write('++) {}');
142 }
143 ```
144
145 One of the advantages of the closure-based form of `addLinkedEdit` is that you
146 can specify suggested replacements for the values of each group. You do that by
147 invoking either `addSuggestion` or `addSuggestions`. In the example above, you
148 might choose to suggest `j` and `k` as other likely loop variable names. You
149 could do that by replacing one of the places where the variable name is written
150 with code like the following:
151
152 ```dart
153 editBuilder.addLinkedEdit(variableGroup, (LinkedEditBuilder linkedEditBuilder) {
154 linkedEditBuilder.write(variableName);
155 linkedEditBuilder.addSuggestions(['j', 'k']);
156 });
157 ```
158
159 A more interesting use of this feature would be to find the names of all of the
160 list-valued variables within scope and suggest those names as alternatives for
161 the name of the list.
162
163 That said, most of the methods on `DartEditBuilder` that help you generate Dart
164 code take one or more optional arguments that allow you to create linked edit
165 groups for appropriate pieces of text and even to specify the suggestions for
166 those groups.
167
168 ## Post-edit Selection
169
170 A `SourceChange` also allows you to specify where the cursor should be placed
171 after the edits are applied. There are two ways to specify this.
172
173 The first is by invoking the method `setSelection` on a `DartChangeBuilder`.
174 The method takes a `Position`, which encapsulates an offset in a particular
175 file. This can be difficult to get right because the offset is required to be
176 the offset *after* all of the edits for that file have been applied.
177
178 The second, and easier, way is by invoking the method `selectHere` on a
179 `DartEditBuilder`. This method does not require any arguments; it computes the
180 offset for the position based on the edits that have previously been created.
181 It does require that all of the edits that apply to text before the desired
182 cursor location have been created before the method is invoked.
183
184 For example, if you're implementing a quick assist to insert a to-do comment at
185 the cursor location, the code to create the insertion edit could look like the
186 following:
187
188 ```dart
189 fileEditBuilder.addReplacement(range, (DartEditBuilder editBuilder) {
190 editBuilder.write('/* TODO ');
191 editBuilder.selectHere();
192 editBuilder.write(' */');
193 }
194 ```
195
196 This will cause the cursor to be placed between the two spaces inside the
197 comment.
198
199 ## Non-Dart Files
200
201 All of the classes above are subclasses of more general classes (just drop the
202 prefix "Dart" from the subclass names). If you are editing files that do not
203 contain Dart code, the more general classes might be a better choice. These
204 classes are defined in
205 `package:analyzer_plugin/utilities/change_builder/change_builder_core.dart`.
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698