1212
1313import static org .eclipse .che .ide .api .theme .Style .theme ;
1414
15+ import com .google .common .annotations .VisibleForTesting ;
1516import com .google .gwt .dom .client .Style ;
1617import com .google .gwt .dom .client .Style .Overflow ;
1718import com .google .gwt .safehtml .shared .SafeHtmlBuilder ;
1819import com .google .gwt .user .client .rpc .AsyncCallback ;
1920import com .google .gwt .user .client .ui .HTML ;
2021import com .google .gwt .user .client .ui .Widget ;
22+ import java .util .ArrayList ;
2123import java .util .List ;
2224import org .eclipse .che .api .languageserver .shared .model .ExtendedCompletionItem ;
2325import org .eclipse .che .api .promises .client .Promise ;
2426import org .eclipse .che .ide .api .editor .codeassist .Completion ;
2527import org .eclipse .che .ide .api .editor .codeassist .CompletionProposal ;
2628import org .eclipse .che .ide .api .editor .document .Document ;
29+ import org .eclipse .che .ide .api .editor .link .HasLinkedMode ;
30+ import org .eclipse .che .ide .api .editor .link .LinkedModel ;
2731import org .eclipse .che .ide .api .editor .text .LinearRange ;
2832import org .eclipse .che .ide .api .editor .text .TextPosition ;
2933import org .eclipse .che .ide .api .icon .Icon ;
3034import org .eclipse .che .ide .filters .Match ;
35+ import org .eclipse .che .ide .util .Pair ;
3136import org .eclipse .che .plugin .languageserver .ide .LanguageServerResources ;
37+ import org .eclipse .che .plugin .languageserver .ide .editor .codeassist .snippet .SnippetResolver ;
38+ import org .eclipse .che .plugin .languageserver .ide .editor .quickassist .ApplyWorkspaceEditAction ;
3239import org .eclipse .che .plugin .languageserver .ide .service .TextDocumentServiceClient ;
3340import org .eclipse .lsp4j .CompletionItem ;
41+ import org .eclipse .lsp4j .InsertTextFormat ;
42+ import org .eclipse .lsp4j .Position ;
3443import org .eclipse .lsp4j .Range ;
3544import org .eclipse .lsp4j .ServerCapabilities ;
3645import org .eclipse .lsp4j .TextEdit ;
@@ -50,8 +59,10 @@ public class CompletionItemBasedCompletionProposal implements CompletionProposal
5059 private final int offset ;
5160 private ExtendedCompletionItem completionItem ;
5261 private boolean resolved ;
62+ private HasLinkedMode editor ;
5363
5464 CompletionItemBasedCompletionProposal (
65+ HasLinkedMode editor ,
5566 ExtendedCompletionItem completionItem ,
5667 String currentWord ,
5768 TextDocumentServiceClient documentServiceClient ,
@@ -60,6 +71,7 @@ public class CompletionItemBasedCompletionProposal implements CompletionProposal
6071 ServerCapabilities serverCapabilities ,
6172 List <Match > highlights ,
6273 int offset ) {
74+ this .editor = editor ;
6375 this .completionItem = completionItem ;
6476 this .currentWord = currentWord ;
6577 this .documentServiceClient = documentServiceClient ;
@@ -165,10 +177,11 @@ public void getCompletion(final CompletionCallback callback) {
165177 .then (
166178 completionItem -> {
167179 callback .onCompletion (
168- new CompletionImpl (completionItem .getItem (), currentWord , offset ));
180+ new CompletionImpl (editor , completionItem .getItem (), currentWord , offset ));
169181 });
170182 } else {
171- callback .onCompletion (new CompletionImpl (completionItem .getItem (), currentWord , offset ));
183+ callback .onCompletion (
184+ new CompletionImpl (editor , completionItem .getItem (), currentWord , offset ));
172185 }
173186 }
174187
@@ -183,59 +196,116 @@ private Promise<ExtendedCompletionItem> resolve() {
183196 return documentServiceClient .resolveCompletionItem (completionItem );
184197 }
185198
186- private static class CompletionImpl implements Completion {
187-
199+ @ VisibleForTesting
200+ static class CompletionImpl implements Completion {
188201 private CompletionItem completionItem ;
189202 private String currentWord ;
190- private String insertedText ;
191203 private int offset ;
204+ private LinearRange lastSelection ;
205+ private HasLinkedMode editor ;
192206
193- public CompletionImpl (CompletionItem completionItem , String currentWord , int offset ) {
207+ public CompletionImpl (
208+ HasLinkedMode editor , CompletionItem completionItem , String currentWord , int offset ) {
209+ this .editor = editor ;
194210 this .completionItem = completionItem ;
195211 this .currentWord = currentWord ;
196212 this .offset = offset ;
197213 }
198214
199215 @ Override
200216 public void apply (Document document ) {
217+ List <TextEdit > edits = new ArrayList <>();
218+ TextPosition cursorPosition = document .getCursorPosition ();
201219 if (completionItem .getTextEdit () != null ) {
202- Range range = completionItem .getTextEdit (). getRange ( );
203- int startOffset =
204- document . getIndexFromPosition (
205- new TextPosition ( range . getStart (). getLine (), range . getStart (). getCharacter ()));
206- int endOffset =
207- offset
208- + document . getIndexFromPosition (
209- new TextPosition ( range . getEnd (). getLine (), range . getEnd (). getCharacter ()));
210- document . replace (
211- startOffset , endOffset - startOffset , completionItem .getTextEdit (). getNewText ( ));
220+ edits . add ( adjustForOffset ( completionItem .getTextEdit (), cursorPosition , offset ) );
221+ } else if ( completionItem . getInsertText () == null ) {
222+ edits . add (
223+ new TextEdit (
224+ newRange (
225+ cursorPosition . getLine (),
226+ cursorPosition . getCharacter () - currentWord . length (),
227+ cursorPosition . getLine (),
228+ cursorPosition . getCharacter ()),
229+ completionItem .getLabel () ));
212230 } else {
213- int currentWordLength = currentWord .length ();
214- int cursorOffset = document .getCursorOffset ();
215- if (completionItem .getInsertText () == null ) {
216- document .replace (
217- cursorOffset - currentWordLength , currentWordLength , completionItem .getLabel ());
218- insertedText = completionItem .getLabel ();
231+ edits .add (
232+ new TextEdit (
233+ newRange (
234+ cursorPosition .getLine (),
235+ cursorPosition .getCharacter () - offset ,
236+ cursorPosition .getLine (),
237+ cursorPosition .getCharacter ()),
238+ completionItem .getInsertText ()));
239+ }
240+ if (completionItem .getAdditionalTextEdits () != null ) {
241+ completionItem
242+ .getAdditionalTextEdits ()
243+ .forEach (e -> edits .add (adjustForOffset (e , cursorPosition , offset )));
244+ }
245+ TextEdit firstEdit = edits .get (0 );
246+ if (completionItem .getInsertTextFormat () == InsertTextFormat .Snippet ) {
247+ Position startPos = firstEdit .getRange ().getStart ();
248+ TextPosition startTextPosition =
249+ new TextPosition (startPos .getLine (), startPos .getCharacter ());
250+ int startOffset = document .getIndexFromPosition (startTextPosition );
251+ Pair <String , LinkedModel > resolved =
252+ new SnippetResolver (new DocumentVariableResolver (document , startTextPosition ))
253+ .resolve (firstEdit .getNewText (), editor , startOffset );
254+ firstEdit .setNewText (resolved .first );
255+ ApplyWorkspaceEditAction .applyTextEdits (document , edits );
256+ if (resolved .second != null ) {
257+ editor .getLinkedMode ().enterLinkedMode (resolved .second );
258+ lastSelection = null ;
219259 } else {
220- document .replace (cursorOffset - offset , offset , completionItem .getInsertText ());
221- insertedText = completionItem .getInsertText ();
260+ lastSelection = computeLastSelection (document , firstEdit );
222261 }
262+ } else {
263+ ApplyWorkspaceEditAction .applyTextEdits (document , edits );
264+ lastSelection = computeLastSelection (document , firstEdit );
223265 }
224266 }
225267
226- @ Override
227- public LinearRange getSelection (Document document ) {
228- final TextEdit textEdit = completionItem .getTextEdit ();
229- if (textEdit == null ) {
230- return LinearRange .createWithStart (document .getCursorOffset () + insertedText .length ())
231- .andLength (0 );
232- }
268+ private LinearRange computeLastSelection (Document document , TextEdit textEdit ) {
233269 Range range = textEdit .getRange ();
234270 TextPosition textPosition =
235271 new TextPosition (range .getStart ().getLine (), range .getStart ().getCharacter ());
236272 int startOffset =
237273 document .getIndexFromPosition (textPosition ) + textEdit .getNewText ().length ();
238274 return LinearRange .createWithStart (startOffset ).andLength (0 );
239275 }
276+
277+ private Range newRange (int startLine , int startChar , int endLine , int endChar ) {
278+ return new Range (new Position (startLine , startChar ), new Position (endLine , endChar ));
279+ }
280+
281+ private TextEdit adjustForOffset (TextEdit textEdit , TextPosition pos , int delta ) {
282+ Range range = textEdit .getRange ();
283+ int originalStart = pos .getCharacter () - delta ;
284+ if (range .getStart ().getLine () != pos .getLine ()
285+ || textEdit .getRange ().getEnd ().getCharacter () < originalStart ) {
286+ return textEdit ;
287+ } else if (originalStart < range .getStart ().getCharacter ()) {
288+ return new TextEdit (
289+ newRange (
290+ range .getStart ().getLine (),
291+ range .getStart ().getCharacter () + delta ,
292+ range .getEnd ().getLine (),
293+ range .getEnd ().getCharacter () + delta ),
294+ textEdit .getNewText ());
295+ } else {
296+ return new TextEdit (
297+ newRange (
298+ range .getStart ().getLine (),
299+ range .getStart ().getCharacter (),
300+ range .getEnd ().getLine (),
301+ range .getEnd ().getCharacter () + delta ),
302+ textEdit .getNewText ());
303+ }
304+ }
305+
306+ @ Override
307+ public LinearRange getSelection (Document document ) {
308+ return lastSelection ;
309+ }
240310 }
241311}
0 commit comments