generated from redhat-developer/new-project-template
-
Notifications
You must be signed in to change notification settings - Fork 20
/
LSPCompletionFeature.java
252 lines (226 loc) · 10.8 KB
/
LSPCompletionFeature.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/*******************************************************************************
* Copyright (c) 2024 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution,
* and is available at https://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package com.redhat.devtools.lsp4ij.client.features;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.PrioritizedLookupElement;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.psi.PsiFile;
import com.redhat.devtools.lsp4ij.LanguageServerItem;
import com.redhat.devtools.lsp4ij.features.completion.CompletionPrefix;
import com.redhat.devtools.lsp4ij.internal.StringUtils;
import com.redhat.devtools.lsp4ij.server.capabilities.CompletionCapabilityRegistry;
import com.redhat.devtools.lsp4ij.ui.IconMapper;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.CompletionItemTag;
import org.eclipse.lsp4j.ServerCapabilities;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* LSP completion feature.
*/
@ApiStatus.Experimental
public class LSPCompletionFeature extends AbstractLSPDocumentFeature {
private CompletionCapabilityRegistry completionCapabilityRegistry;
public static class LSPCompletionContext {
private final @NotNull CompletionParameters parameters;
private final @NotNull LanguageServerItem languageServer;
private Boolean signatureHelpSupported;
private Boolean resolveCompletionSupported;
public LSPCompletionContext(@NotNull CompletionParameters parameters, @NotNull LanguageServerItem languageServer) {
this.parameters = parameters;
this.languageServer = languageServer;
}
public @NotNull CompletionParameters getParameters() {
return parameters;
}
public boolean isSignatureHelpSupported() {
if (signatureHelpSupported == null) {
signatureHelpSupported = languageServer.getClientFeatures().getSignatureHelpFeature().isSupported(parameters.getOriginalFile());
}
return signatureHelpSupported;
}
public boolean isResolveCompletionSupported() {
if (resolveCompletionSupported == null) {
resolveCompletionSupported = languageServer.getClientFeatures().getCompletionFeature().isResolveCompletionSupported(parameters.getOriginalFile());
}
return resolveCompletionSupported;
}
@NotNull
@ApiStatus.Internal
LanguageServerItem getLanguageServer() {
return languageServer;
}
}
@Override
public boolean isSupported(@NotNull PsiFile file) {
return isCompletionSupported(file);
}
/**
* Returns true if the file associated with a language server can support completion and false otherwise.
*
* @param file the file.
* @return true if the file associated with a language server can support completion and false otherwise.
*/
public boolean isCompletionSupported(@NotNull PsiFile file) {
return getCompletionCapabilityRegistry().isCompletionSupported(file);
}
/**
* Returns true if the file associated with a language server can support resolve completion and false otherwise.
*
* @param file the file.
* @return true the file associated with a language server can support resolve completion and false otherwise.
*/
public boolean isResolveCompletionSupported(@Nullable PsiFile file) {
return getCompletionCapabilityRegistry().isResolveCompletionSupported(file);
}
/**
* Returns true if the given character is defined as "completion trigger" in the server capability of the language server and false otherwise.
*
* @param file the file.
* @param charTyped the current typed character.
* @return true if the given character is defined as "completion trigger" in the server capability of the language server and false otherwise.
*/
public boolean isCompletionTriggerCharactersSupported(@NotNull PsiFile file, String charTyped) {
return getCompletionCapabilityRegistry().isCompletionTriggerCharactersSupported(file, charTyped);
}
/**
* Create a completion lookup element from the given LSP completion item and context and null otherwise.
*
* @param item the LSP completion item.
* @param context the LSP completion context.
* @return a completion lookup element from the given LSP completion item and context and null otherwise.
*/
@Nullable
public LookupElement createLookupElement(@NotNull CompletionItem item,
@NotNull LSPCompletionContext context) {
if (StringUtils.isBlank(item.getLabel())) {
// Invalid completion Item, ignore it
return null;
}
// Update text edit range, commitCharacters, ... with item defaults if needed
return new LSPCompletionProposal(item, context, this);
}
/**
* Update the given IntelliJ lookup element presentation with the given LSP completion item.
*
* @param presentation the lookup element presentation to update.
* @param item the LSP completion .
*/
public void renderLookupElement(@NotNull LookupElementPresentation presentation,
@NotNull CompletionItem item) {
presentation.setItemText(this.getItemText(item));
presentation.setTypeText(this.getTypeText(item));
presentation.setIcon(this.getIcon(item));
presentation.setStrikeout(this.isStrikeout(item));
presentation.setTailText(this.getTailText(item));
presentation.setItemTextBold(this.isItemTextBold(item));
}
/**
* Returns the IntelliJ lookup item text from the given LSP completion item and null otherwise.
*
* @param item the LSP completion item.
* @return the IntelliJ lookup item text from the given LSP completion item and null otherwise.
*/
@Nullable
public String getItemText(@NotNull CompletionItem item) {
return item.getLabel();
}
/**
* Returns the IntelliJ lookup type text from the given LSP completion item and null otherwise.
*
* @param item the LSP completion item.
* @return the IntelliJ lookup type text from the given LSP completion item and null otherwise.
*/
@Nullable
public String getTypeText(CompletionItem item) {
return item.getDetail();
}
/**
* Returns the IntelliJ lookup icon from the given LSP completion item and null otherwise.
*
* @param item the LSP completion item.
* @return the IntelliJ lookup icon from the given LSP completion item and null otherwise.
*/
@Nullable
public Icon getIcon(@NotNull CompletionItem item) {
return IconMapper.getIcon(item);
}
/**
* Returns true if the IntelliJ lookup is strike out and false otherwise.
*
* @param item
* @return true if the IntelliJ lookup is strike out and false otherwise.
*/
public boolean isStrikeout(@NotNull CompletionItem item) {
return (item.getTags() != null && item.getTags().contains(CompletionItemTag.Deprecated))
|| (item.getDeprecated() != null && item.getDeprecated().booleanValue());
}
/**
* Returns the IntelliJ lookup tail text from the given LSP completion item and null otherwise.
*
* @param item the LSP completion item.
* @return the IntelliJ lookup tail text from the given LSP completion item and null otherwise.
*/
@Nullable
public String getTailText(@NotNull CompletionItem item) {
var labelDetails = item.getLabelDetails();
return labelDetails != null ? labelDetails.getDetail() : null;
}
/**
* Returns the IntelliJ lookup item text bold from the given LSP completion item and null otherwise.
*
* @param item the LSP completion item.
* @return the IntelliJ lookup item text bold from the given LSP completion item and null otherwise.
*/
public boolean isItemTextBold(@NotNull CompletionItem item) {
return item.getKind() != null && item.getKind() == CompletionItemKind.Keyword;
}
public void addLookupItem(@NotNull CompletionPrefix completionPrefix,
@NotNull CompletionResultSet result,
@NotNull LookupElement lookupItem,
int priority,
@NotNull CompletionItem item) {
var prioritizedLookupItem = PrioritizedLookupElement.withPriority(lookupItem, priority);
// Compute the prefix
var textEditRange = ((LSPCompletionProposal) lookupItem).getTextEditRange();
String prefix = textEditRange != null ? completionPrefix.getPrefixFor(textEditRange, item) : null;
if (prefix != null) {
// Add the IJ completion item (lookup item) by using the computed prefix
result.withPrefixMatcher(prefix)
.caseInsensitive()
.addElement(prioritizedLookupItem);
} else {
// Should happen rarely, only when text edit is for multi-lines or if completion is triggered outside the text edit range.
// Add the IJ completion item (lookup item) which will use the IJ prefix
result.caseInsensitive()
.addElement(prioritizedLookupItem);
}
}
public CompletionCapabilityRegistry getCompletionCapabilityRegistry() {
if (completionCapabilityRegistry == null) {
var clientFeatures = getClientFeatures();
completionCapabilityRegistry = new CompletionCapabilityRegistry(clientFeatures);
completionCapabilityRegistry.setServerCapabilities(clientFeatures.getServerWrapper().getServerCapabilitiesSync());
}
return completionCapabilityRegistry;
}
@Override
public void setServerCapabilities(@Nullable ServerCapabilities serverCapabilities) {
if(completionCapabilityRegistry != null) {
completionCapabilityRegistry.setServerCapabilities(serverCapabilities);
}
}
}