2424import org .eclipse .core .runtime .IProgressMonitor ;
2525import org .eclipse .jdt .core .IBuffer ;
2626import org .eclipse .jdt .core .ICompilationUnit ;
27- import org .eclipse .jdt .core .IJavaElement ;
2827import org .eclipse .jdt .core .manipulation .ChangeCorrectionProposalCore ;
2928import org .eclipse .jdt .internal .corext .fix .LinkedProposalModelCore ;
3029import org .eclipse .jdt .internal .corext .fix .LinkedProposalPositionGroupCore ;
@@ -84,18 +83,32 @@ public CodeAction resolve(CodeAction params, IProgressMonitor monitor) {
8483 return params ;
8584 }
8685
86+ /***
87+ * Supports linked correction proposals by converting them to snippets.
88+ * Represents a
89+ * {@link org.eclipse.jdt.internal.corext.fix.LinkedProposalPositionGroupCore}
90+ * with snippet choice syntax if the group has multiple proposals, otherwise
91+ * represents it as a placeholder.
92+ *
93+ * @param proposal
94+ * the proposal to add support for
95+ * @param edit
96+ * the current edit to be returned with the code action
97+ * @throws CoreException
98+ */
8799 private static final void addSnippetsIfApplicable (LinkedCorrectionProposalCore proposal , WorkspaceEdit edit ) throws CoreException {
88100 Object modifiedElement = proposal .getChange ().getModifiedElement ();
89- ICompilationUnit compilationUnit = ( ICompilationUnit ) (( IJavaElement ) modifiedElement ). getAncestor ( IJavaElement . COMPILATION_UNIT );
101+ ICompilationUnit compilationUnit = proposal . getCompilationUnit ( );
90102 IBuffer buffer = compilationUnit .getBuffer ();
91103 LinkedProposalModelCore linkedProposals = proposal .getLinkedProposalModel ();
92104 List <Triple > snippets = new ArrayList <>();
93105 Iterator <LinkedProposalPositionGroupCore > it = linkedProposals .getPositionGroupCoreIterator ();
94- int snippetNumber = 1 ;
106+
95107 while (it .hasNext ()) {
96108 LinkedProposalPositionGroupCore group = it .next ();
97109 ProposalCore [] proposalList = group .getProposals ();
98110 PositionInformation [] positionList = group .getPositions ();
111+ // Sorts in ascending order to ensure first position in list has the smallest offset
99112 Arrays .sort (positionList , new Comparator <PositionInformation >() {
100113 @ Override
101114 public int compare (PositionInformation p1 , PositionInformation p2 ) {
@@ -104,12 +117,16 @@ public int compare(PositionInformation p1, PositionInformation p2) {
104117 });
105118 StringBuilder snippet = new StringBuilder ();
106119 snippet .append (SNIPPET_CHOICE_INDICATOR );
120+
107121 for (int i = 0 ; i < positionList .length ; i ++) {
108122 int offset = positionList [i ].getOffset ();
109123 int length = positionList [i ].getLength ();
124+
110125 // Create snippet on first iteration
111126 if (i == 0 ) {
112127 LinkedPosition linkedPosition = new LinkedPosition (JsonRpcHelpers .toDocument (buffer ), positionList [i ].getOffset (), positionList [i ].getLength (), positionList [i ].getSequenceRank ());
128+
129+ // Groups with no proposals will have the snippet text added while amending the WorkspaceEdit
113130 for (int j = 0 ; j < proposalList .length ; j ++) {
114131 org .eclipse .text .edits .TextEdit editWithText = findReplaceOrInsertEdit (proposalList [j ].computeEdits (0 , linkedPosition , '\u0000' , 0 , new LinkedModeModel ()));
115132 if (editWithText != null ) {
@@ -119,39 +136,42 @@ public int compare(PositionInformation p1, PositionInformation p2) {
119136 snippet .append (((ReplaceEdit ) editWithText ).getText ());
120137 }
121138 }
122- // // If snippet is empty, ignore this group
123- // if (snippet.toString().equals(String.valueOf(SNIPPET_CHOICE_INDICATOR))) {
124- // break;
125- // }
139+
126140 snippet .append (SNIPPET_CHOICE_POSTFIX );
127141 // If snippet only has one choice, remove choice indicators
128142 if (snippet .indexOf (SNIPPET_CHOICE_DELIMITER ) == -1 ) {
129143 snippet .setCharAt (0 , ':' );
130144 snippet .deleteCharAt (snippet .length () - 2 );
131145 }
146+ // Snippet is added with smallest offset as 0th element
132147 snippets .add (new Triple (snippet .toString (), offset , length ));
133148 } else {
149+ // Add offset/length values from additional positions in group to previously created snippet
134150 Triple currentSnippet = snippets .get (snippets .size () - 1 );
135151 currentSnippet .offset .add (offset );
136152 currentSnippet .length .add (length );
137153 }
138154 }
139155 }
140156 if (!snippets .isEmpty ()) {
141- // Sort snippets in descending order based on offset, so that the edits are applied in an order that does not alter the offset of later edits
157+ // Sort snippets based on offset of earliest occurrence to enable correct numbering
142158 snippets .sort (null );
143- // ListIterator<Triple> li = snippets.listIterator(snippets.size()) ;
159+ int snippetNumber = 1 ;
144160 for (int i = snippets .size () - 1 ; i >= 0 ; i --) {
145161 Triple element = snippets .get (i );
146162 element .snippet = SNIPPET_PREFIX + snippetNumber + element .snippet ;
147163 snippetNumber ++;
164+ // Separate snippets with multiple positions into individual instances in list
148165 for (int j = 1 ; j < element .offset .size (); j ++) {
149166 snippets .add (new Triple (element .snippet .toString (), element .offset .get (j ), element .length .get (j )));
150167 element .offset .remove (j );
151168 element .length .remove (j );
152169 }
153170 }
171+ // Re-sort snippets (with the added individual instances) by offset in descending order,
172+ // so that the amendments to the text edit are applied in an order that does not alter the offset of later amendments
154173 snippets .sort (null );
174+
155175 for (int i = 0 ; i < edit .getDocumentChanges ().size (); i ++) {
156176 if (edit .getDocumentChanges ().get (i ).isLeft ()) {
157177 List <TextEdit > edits = edit .getDocumentChanges ().get (i ).getLeft ().getEdits ();
@@ -160,20 +180,22 @@ public int compare(PositionInformation p1, PositionInformation p2) {
160180 StringBuilder replacementText = new StringBuilder (edits .get (j ).getNewText ());
161181 int rangeStart = JsonRpcHelpers .toOffset (buffer , editRange .getStart ().getLine (), editRange .getStart ().getCharacter ());
162182 int rangeEnd = rangeStart + replacementText .length ();
183+
163184 for (int k = 0 ; k < snippets .size (); k ++) {
164- Triple snippetHolder = snippets .get (k );
165- if (snippetHolder .offset .get (0 ) >= rangeStart && snippetHolder .offset .get (0 ) <= rangeEnd ) {
166- int replaceStart = snippetHolder .offset .get (0 ) - rangeStart ;
167- int replaceEnd = replaceStart + snippetHolder .length .get (0 );
168- if (snippetHolder .snippet .endsWith (":}" )) {
169- snippetHolder .snippet = snippetHolder .snippet .replaceFirst (":" , ":" + replacementText .substring (replaceStart , replaceEnd ));
185+ Triple currentSnippet = snippets .get (k );
186+ if (currentSnippet .offset .get (0 ) >= rangeStart && currentSnippet .offset .get (0 ) <= rangeEnd ) {
187+ int replaceStart = currentSnippet .offset .get (0 ) - rangeStart ;
188+ int replaceEnd = replaceStart + currentSnippet .length .get (0 );
189+ // If snippet text has not been added due to no elements in the proposal list, create snippet based on the text in the given position range
190+ if (currentSnippet .snippet .endsWith (":}" )) {
191+ currentSnippet .snippet = currentSnippet .snippet .replaceFirst (":" , ":" + replacementText .substring (replaceStart , replaceEnd ));
170192 }
171- replacementText .replace (replaceStart , replaceEnd , snippetHolder .snippet );
193+ replacementText .replace (replaceStart , replaceEnd , currentSnippet .snippet );
172194 }
173195 }
196+
174197 SnippetTextEdit newEdit = new SnippetTextEdit (editRange , replacementText .toString ());
175- edits .remove (j );
176- edits .add (j , newEdit );
198+ edits .set (j , newEdit );
177199 }
178200 }
179201 }
@@ -208,6 +230,7 @@ private static final class Triple implements Comparable<Triple> {
208230 this .length .add (length );
209231 }
210232
233+ // Sorts in descending order based on 0th (smallest) element of offset list
211234 @ Override
212235 public int compareTo (Triple other ) {
213236 return other .offset .get (0 ) - this .offset .get (0 );
0 commit comments