• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wurstscript / WurstScript / 227

29 Nov 2023 11:46AM UTC coverage: 62.48% (-0.09%) from 62.574%
227

Pull #1083

circleci

Frotty
remove confusing mpq error, make some mpq loads readonly
Pull Request #1083: Show dialog for choosing game path

17295 of 27681 relevant lines covered (62.48%)

0.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.83
de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/GetCompletions.java
1
package de.peeeq.wurstio.languageserver.requests;
2

3
import com.google.common.collect.ImmutableMultimap;
4
import com.google.common.collect.Lists;
5
import com.google.common.collect.Multimap;
6
import com.google.common.collect.Sets;
7
import de.peeeq.wurstio.languageserver.BufferManager;
8
import de.peeeq.wurstio.languageserver.ModelManager;
9
import de.peeeq.wurstio.languageserver.WFile;
10
import de.peeeq.wurstscript.WLogger;
11
import de.peeeq.wurstscript.WurstKeywords;
12
import de.peeeq.wurstscript.ast.*;
13
import de.peeeq.wurstscript.attributes.AttrExprType;
14
import de.peeeq.wurstscript.attributes.names.*;
15
import de.peeeq.wurstscript.types.*;
16
import de.peeeq.wurstscript.utils.Utils;
17
import org.eclipse.jdt.annotation.Nullable;
18
import org.eclipse.lsp4j.*;
19

20
import java.io.File;
21
import java.text.DecimalFormat;
22
import java.text.NumberFormat;
23
import java.text.ParseException;
24
import java.util.*;
25
import java.util.Map.Entry;
26
import java.util.regex.Pattern;
27
import java.util.stream.Collectors;
28

29
/**
30
 * Created by peter on 24.04.16.
31
 */
32
public class GetCompletions extends UserRequest<CompletionList> {
1✔
33

34

35
    private static final int MAX_COMPLETIONS = 100;
36
    private final WFile filename;
37
    private final String buffer;
38
    private final String[] lines;
39
    private final int line;
40
    private final int column;
41
    private String alreadyEntered;
42
    private String alreadyEnteredLower;
43
    private SearchMode searchMode;
44
    private Element elem;
45
    private WurstType expectedType;
46
    private ModelManager modelManager;
47
    private boolean isIncomplete = false;
1✔
48
    private CompilationUnit cu;
49

50

51
    public GetCompletions(CompletionParams position, BufferManager bufferManager) {
1✔
52
        this.filename = WFile.create(position.getTextDocument().getUri());
1✔
53
        this.buffer = bufferManager.getBuffer(position.getTextDocument());
1✔
54
        this.line = position.getPosition().getLine() + 1;
1✔
55
        this.column = position.getPosition().getCharacter();
1✔
56
        this.lines = buffer.split("\\n|\\r\\n");
1✔
57
        if (line <= lines.length) {
1✔
58
            WLogger.info("Get completions in line " + line + ": \n" + currentLine().replace('\t', ' ') + "\n" + Utils.repeat(' ', column > 0 ? column : 0) + "^\n" +
1✔
59
                    " at column " + column);
60
        }
61
    }
1✔
62

63
    private String currentLine() {
64
        return lines[line - 1];
1✔
65
    }
66

67
    @Override
68
    public CompletionList execute(ModelManager modelManager) {
69
        this.modelManager = modelManager;
1✔
70
        cu = modelManager.replaceCompilationUnitContent(filename, buffer, false);
1✔
71
        if (cu == null) {
1✔
72
            return new CompletionList(Collections.emptyList());
×
73
        }
74
        List<CompletionItem> result = computeCompletionProposals(cu);
1✔
75
        // sort: highest rating first, then sort by label
76
        if (result != null) {
1✔
77
            result.sort(completionItemComparator());
1✔
78
            return new CompletionList(isIncomplete, result);
1✔
79
        } else {
80
            return new CompletionList(isIncomplete, Collections.emptyList());
×
81
        }
82
    }
83

84
    private Comparator<CompletionItem> completionItemComparator() {
85
        return Comparator
1✔
86
                .comparing(CompletionItem::getSortText).reversed()
1✔
87
                .thenComparing(CompletionItem::getLabel);
1✔
88
    }
89

90
    private enum SearchMode {
1✔
91
        PREFIX, INFIX, SUBSEQENCE
1✔
92
    }
93

94
    /**
95
     * computes completions at the current position
96
     */
97
    public List<CompletionItem> computeCompletionProposals(CompilationUnit cu) {
98

99
        if (isEnteringRealNumber()) {
1✔
100
            return null;
×
101
        }
102

103
        alreadyEntered = getAlreadyEnteredText();
1✔
104
        alreadyEnteredLower = alreadyEntered.toLowerCase();
1✔
105
        WLogger.info("already entered = " + alreadyEntered);
1✔
106

107
        for (SearchMode mode : SearchMode.values()) {
1✔
108
            searchMode = mode;
1✔
109
            List<CompletionItem> completions = Lists.newArrayList();
1✔
110

111
            elem = Utils.getAstElementAtPos(cu, line, column + 1, false).get();
1✔
112
            WLogger.info("get completions at " + Utils.printElement(elem));
1✔
113
            expectedType = null;
1✔
114
            if (elem instanceof Expr) {
1✔
115
                Expr expr = (Expr) elem;
1✔
116
                expectedType = expr.attrExpectedTyp();
1✔
117
                WLogger.info("....expected type = " + expectedType);
1✔
118
            }
119

120
            calculateCompletions(completions);
1✔
121

122
            dropBadCompletions(completions);
1✔
123
            removeDuplicates(completions);
1✔
124

125
            if (completions.size() > 0) {
1✔
126
                return completions;
1✔
127
            }
128
        }
129
        return null;
×
130
    }
131

132
    private void calculateCompletions(List<CompletionItem> completions) {
133
        boolean isMemberAccess = false;
1✔
134
        if (elem instanceof ExprMember) {
1✔
135
            ExprMember e = (ExprMember) elem;
1✔
136

137
            if (elem instanceof ExprMemberMethod) {
1✔
138
                ExprMemberMethod c = (ExprMemberMethod) elem;
×
139
                if (isInParenthesis(c.getLeft().getSource().getEndColumn())) {
×
140
                    // cursor inside parenthesis
141
                    getCompletionsForExistingMemberCall(completions, c);
×
142
                    return;
×
143
                }
144
            }
145

146
            WurstType leftType = e.getLeft().attrTyp();
1✔
147

148
            if (leftType instanceof WurstTypeNamedScope) {
1✔
149
                WurstTypeNamedScope ct = (WurstTypeNamedScope) leftType;
1✔
150
                for (DefLink nameLink : ct.nameLinks().values()) {
1✔
151
                    if (isSuitableCompletion(nameLink.getName())
1✔
152
                            && (nameLink.getReceiverType() != null || nameLink instanceof TypeDefLink)
1✔
153
                            && nameLink.getVisibility() == Visibility.PUBLIC) {
1✔
154
                        CompletionItem completion = makeNameDefCompletion(nameLink);
1✔
155
                        completions.add(completion);
1✔
156
                    }
157
                }
1✔
158
            }
159

160
            isMemberAccess = true;
1✔
161
            WScope scope = elem.attrNearestScope();
1✔
162
            // add member vars
163
            while (scope != null) {
1✔
164
                ImmutableMultimap<String, DefLink> visibleNames = scope.attrNameLinks();
1✔
165
                completionsAddVisibleNames(alreadyEntered, completions, visibleNames, leftType, isMemberAccess, elem);
1✔
166
                completionsAddVisibleExtensionFunctions(completions, visibleNames, leftType);
1✔
167
                scope = scope.attrNextScope();
1✔
168
            }
1✔
169
        } else if (elem instanceof ExprRealVal) {
1✔
170
            // show no hints for reals
171
        } else if (elem instanceof WPackage) {
1✔
172
            // no hints at package level
173
        } else if (elem instanceof WImport) {
1✔
174
            //WImport imp = (WImport) elem;
175
            completeImport(completions);
×
176
        } else if (elem instanceof ExprNewObject) {
1✔
177
            ExprNewObject en = (ExprNewObject) elem;
×
178
            if (isInParenthesis(en.getSource().getStartColumn())) {
×
179
                // cursor inside parameters
180
                getCompletionsForExistingConstructorCall(completions, en);
×
181
                return;
×
182
            }
183
            WScope scope = elem.attrNearestScope();
×
184
            while (scope != null) {
×
185
                Multimap<String, DefLink> visibleNames = scope.attrNameLinks();
×
186
                for (NameLink n : visibleNames.values()) {
×
187
                    if (n.getDef() instanceof ClassDef && isSuitableCompletion(n.getName())) {
×
188
                        ClassDef c = (ClassDef) n.getDef();
×
189
                        for (ConstructorDef constr : c.getConstructors()) {
×
190
                            completions.add(makeConstructorCompletion(c, constr));
×
191
                        }
×
192
                    }
193
                }
×
194
                scope = scope.attrNextScope();
×
195
            }
×
196
        } else if (elem instanceof ExprFunctionCall) {
1✔
197
            ExprFunctionCall c = (ExprFunctionCall) elem;
1✔
198
            if (isInParenthesis(c.getSource().getStartColumn())) {
1✔
199
                // cursor is in parameter list
200
                getCompletionsForExistingCall(completions, c);
×
201
            } else {
202
                addDefaultCompletions(completions, elem, isMemberAccess);
1✔
203
            }
204
        } else {
1✔
205
            if (elem instanceof ExprEmpty) {
1✔
206
                if (elem.getParent() instanceof Arguments) {
×
207
                    Element grandParent = getGrandParent();
×
208
                    if (grandParent instanceof ExprFunctionCall) {
×
209
                        ExprFunctionCall c = (ExprFunctionCall) grandParent;
×
210
                        getCompletionsForExistingCall(completions, c);
×
211
                    } else if (grandParent instanceof ExprMemberMethod) {
×
212
                        ExprMemberMethod c = (ExprMemberMethod) grandParent;
×
213
                        getCompletionsForExistingMemberCall(completions, c);
×
214
                    } else if (grandParent instanceof ExprNewObject) {
×
215
                        ExprNewObject c = (ExprNewObject) grandParent;
×
216
                        getCompletionsForExistingConstructorCall(completions, c);
×
217
                    }
218
                    // TODO add overloaded funcs
219
                }
220
            }
221
            if (completions.isEmpty()) {
1✔
222
                // default completions:
223
                addDefaultCompletions(completions, elem, isMemberAccess);
1✔
224
            }
225
        }
226
        if (!isMemberAccess) {
1✔
227
            addKeywordCompletions(completions);
1✔
228
        }
229

230
    }
1✔
231

232
    private void addKeywordCompletions(List<CompletionItem> completions) {
233
        for (String keyword : WurstKeywords.KEYWORDS) {
1✔
234
            if (keyword.startsWith(alreadyEntered)) {
1✔
235
                completions.add(makeSimpleNameCompletion(keyword));
×
236
            }
237
        }
238
        for (String keyword : WurstKeywords.JASS_PRIMITIVE_TYPES) {
1✔
239
            if (keyword.startsWith(alreadyEntered)) {
1✔
240
                completions.add(makeSimpleNameCompletion(keyword));
×
241
            }
242
        }
243
    }
1✔
244

245
    private void completeImport(List<CompletionItem> completions) {
246
        ModelManager mm = modelManager;
×
247
        WurstModel model = elem.getModel();
×
248
        Set<String> usedPackages = Sets.newHashSet();
×
249
        for (WPackage p : model.attrPackages().values()) {
×
250
            if (!usedPackages.contains(p.getName()) && isSuitableCompletion(p.getName())) {
×
251
                completions.add(makeNameDefCompletion(PackageLink.create(p, p.attrNearestScope())));
×
252
                usedPackages.add(p.getName());
×
253
            }
254
        }
×
255
        for (File dep : mm.getDependencyWurstFiles()) {
×
256
            String libName = Utils.getLibName(dep);
×
257
            if (!usedPackages.contains(libName) && isSuitableCompletion(libName)) {
×
258
                usedPackages.add(libName);
×
259
                completions.add(makeSimpleNameCompletion(libName));
×
260
            }
261
        }
×
262
        if (isSuitableCompletion("NoWurst")) {
×
263
            completions.add(makeSimpleNameCompletion("NoWurst"));
×
264
        }
265
    }
×
266

267
    private @Nullable Element getGrandParent() {
268
        Element parent = elem.getParent();
×
269
        if (parent == null)
×
270
            return null;
×
271
        return parent.getParent();
×
272
    }
273

274
    /**
275
     * Assuming we have a method call like moo.bar(...)
276
     * <p>
277
     * checks if the cursor is currently in the parentheses,
278
     * given the position of the dot
279
     */
280
    private boolean isInParenthesis(int start) {
281
        try {
282
            String s = currentLine().substring(start, column - start);
1✔
283
            return s.contains("(");
1✔
284
        } catch (IndexOutOfBoundsException e) {
×
285
            return false;
×
286
        }
287
    }
288

289
    private void addDefaultCompletions(List<CompletionItem> completions, Element elem, boolean isMemberAccess) {
290
        WurstType leftType;
291
        leftType = AttrExprType.caclulateThistype(elem, true, null);
1✔
292
        if (leftType instanceof WurstTypeUnknown) {
1✔
293
            leftType = null;
1✔
294
        }
295
        WScope scope = elem.attrNearestScope();
1✔
296
        while (scope != null) {
1✔
297
            Multimap<String, DefLink> visibleNames = scope.attrNameLinks();
1✔
298
            completionsAddVisibleNames(alreadyEntered, completions, visibleNames, leftType, isMemberAccess, elem);
1✔
299
            scope = scope.attrNextScope();
1✔
300
        }
1✔
301
    }
1✔
302

303
    private void getCompletionsForExistingConstructorCall(List<CompletionItem> completions, ExprNewObject c) {
304
        ConstructorDef constructorDef = c.attrConstructorDef();
×
305
        if (constructorDef != null) {
×
306
            ClassDef classDef = constructorDef.attrNearestClassDef();
×
307
            assert classDef != null; // every constructor has a nearest class
×
308
            CompletionItem ci = makeConstructorCompletion(classDef, constructorDef);
×
309
            ci.setTextEdit(null);
×
310
            completions.add(ci);
×
311
        }
312
    }
×
313

314
    private void getCompletionsForExistingMemberCall(List<CompletionItem> completions, ExprMemberMethod c) {
315
        FuncLink funcDef = c.attrFuncLink();
×
316
        if (funcDef != null) {
×
317
            CompletionItem ci = makeFunctionCompletion(funcDef);
×
318
            ci.setTextEdit(null);
×
319
            completions.add(ci);
×
320
        }
321
    }
×
322

323
    private void getCompletionsForExistingCall(List<CompletionItem> completions, ExprFunctionCall c) {
324
        FuncLink funcDef = c.attrFuncLink();
×
325
        if (funcDef != null) {
×
326
            alreadyEntered = c.getFuncName();
×
327
            CompletionItem ci = makeFunctionCompletion(funcDef);
×
328
            ci.setTextEdit(null);
×
329
            completions.add(ci);
×
330
        }
331
    }
×
332

333
        /*
334
    private ICompletionProposal[] toCompletionsArray(List<CompletionItem> completions) {
335
                Collections.sort(completions);
336
                ICompletionProposal[] result = new ICompletionProposal[completions.size()];
337
                for (int i = 0; i < result.length; i++) {
338
                        result[i] = completions.get(i).getProposal();
339
                }
340
                return result;
341
        }
342
        */
343

344
    private boolean isSuitableCompletion(String name) {
345
        if (name.endsWith("Tests")) {
1✔
346
            return false;
×
347
        }
348
        switch (searchMode) {
1✔
349
            case PREFIX:
350
                return name.toLowerCase().startsWith(alreadyEnteredLower);
1✔
351
            case INFIX:
352
                return name.toLowerCase().contains(alreadyEnteredLower);
×
353
            default:
354
                return Utils.isSubsequenceIgnoreCase(alreadyEntered, name);
×
355
        }
356
    }
357

358
    private static final Pattern realPattern = Pattern.compile("[0-9]\\.");
1✔
359

360
    /**
361
     * checks if we are currently entering a real number
362
     * (autocomplete might have triggered because of the dot)
363
     */
364
    private boolean isEnteringRealNumber() {
365
        try {
366
            String currentLine = currentLine();
1✔
367
            String before = currentLine.substring(column - 2, 2);
1✔
368
            return realPattern.matcher(before).matches();
1✔
369
        } catch (IndexOutOfBoundsException e) {
1✔
370
            return false;
1✔
371
        }
372
    }
373

374

375
    /**
376
     * checks if cursor is directly before open parenthesis
377
     */
378
    private boolean isBeforeParenthesis() {
379
        try {
380
            return currentLine().charAt(column) == '(';
1✔
381
        } catch (IndexOutOfBoundsException e) {
1✔
382
            return false;
1✔
383
        }
384
    }
385

386
    private boolean isAtEndOfLine() {
387
        String line = currentLine();
1✔
388
        for (int i = column + 1; i < line.length(); i++) {
1✔
389
            if (!Character.isWhitespace(line.charAt(i))) {
×
390
                return false;
×
391
            }
392
        }
393
        return true;
1✔
394
    }
395

396
    private void removeDuplicates(List<CompletionItem> completions) {
397
        for (int i = 0; i < completions.size() - 1; i++) {
1✔
398
            for (int j = completions.size() - 1; j > i; j--) {
1✔
399
                if (completions.get(i).equals(completions.get(j))) {
1✔
400
                    completions.remove(j);
1✔
401
                }
402
            }
403
        }
404

405
    }
1✔
406

407
    /**
408
     * get the part of the input which belongs to the identifier being entered currently
409
     */
410
    private String getAlreadyEnteredText() {
411
        try {
412
            int start = column - 1;
1✔
413
            String currentLine = currentLine();
1✔
414
            while (start >= 0) {
1✔
415
                char c = currentLine.charAt(start);
1✔
416
                if (!Character.isJavaIdentifierPart(c)) {
1✔
417
                    break;
1✔
418
                }
419
                start--;
1✔
420
            }
1✔
421
            start++;
1✔
422
            return currentLine.substring(start, column);
1✔
423
        } catch (IndexOutOfBoundsException e) {
×
424
            return "";
×
425
        }
426
    }
427

428
    private void completionsAddVisibleNames(String alreadyEntered, List<CompletionItem> completions, Multimap<String, DefLink> visibleNames,
429
                                            @Nullable WurstType leftType, boolean isMemberAccess, Element pos) {
430
        Collection<Entry<String, DefLink>> entries = visibleNames.entries();
1✔
431
        for (Entry<String, DefLink> e : entries) {
1✔
432
            if (!isSuitableCompletion(e.getKey())) {
1✔
433
                continue;
1✔
434
            }
435
            DefLink defLink = e.getValue();
1✔
436

437
            // remove invisible functions
438
            if (defLink.getVisibility() == Visibility.PRIVATE_OTHER || defLink.getVisibility() == Visibility.PROTECTED_OTHER) {
1✔
439
                continue;
1✔
440
            }
441

442
            WurstType receiverType = defLink.getReceiverType();
1✔
443
            if (leftType == null) {
1✔
444
                if (receiverType != null && !receiverType.isStaticRef()) {
1✔
445
                    // skip extension functions, when not needed
446
                    continue;
×
447
                }
448
            } else { // leftType != null
449
                if (receiverType == null) {
1✔
450
                    if (isMemberAccess) {
1✔
451
                        // if this is a member access and receiver type is null, then don't show completion
452
                        continue;
1✔
453
                    }
454
                } else {
455
                    if (!leftType.isSubtypeOf(receiverType, pos)) {
1✔
456
                        // skip elements with wrong receiver type
457
                        continue;
1✔
458
                    }
459
                }
460
            }
461

462
            if (defLink instanceof FuncLink) {
1✔
463
                FuncLink funcLink = (FuncLink) defLink;
1✔
464
                CompletionItem completion = makeFunctionCompletion(funcLink);
1✔
465
                completions.add(completion);
1✔
466
            } else {
1✔
467
                completions.add(makeNameDefCompletion(defLink));
1✔
468
            }
469
            if (alreadyEntered.length() <= 3 && completions.size() >= MAX_COMPLETIONS) {
1✔
470
                // got enough completions
471
                isIncomplete = true;
×
472
                return;
×
473
            }
474
        }
1✔
475
    }
1✔
476

477
    private void dropBadCompletions(List<CompletionItem> completions) {
478
        completions.sort(completionItemComparator());
1✔
479
        NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.getDefault());
1✔
480
        for (int i = completions.size() - 1; i >= MAX_COMPLETIONS; i--) {
1✔
481
            try {
482
                if (numberFormat.parse(completions.get(i).getSortText()).doubleValue() > 0.4) {
×
483
                    // good enough
484
                    return;
×
485
                }
486
            } catch (NumberFormatException | ParseException e) {
×
487
                WLogger.severe(e);
×
488
            }
×
489
            completions.remove(i);
×
490
        }
491
    }
1✔
492

493
    private CompletionItem makeNameDefCompletion(NameLink n) {
494
        if (n instanceof FuncLink) {
1✔
495
            return makeFunctionCompletion((FuncLink) n);
1✔
496
        }
497
        CompletionItem completion = new CompletionItem(n.getName());
1✔
498

499
        completion.setDetail(HoverInfo.descriptionString(n.getDef()));
1✔
500
        completion.setDocumentation(n.getDef().attrComment());
1✔
501
        double rating = calculateRating(n.getName(), n.getTyp());
1✔
502
        completion.setSortText(ratingToString(rating));
1✔
503
        String newText = n.getName();
1✔
504
        completion.setInsertText(newText);
1✔
505

506
        return completion;
1✔
507
    }
508

509
    private String ratingToString(double rating) {
510
        rating = Math.min(10, rating);
1✔
511
        DecimalFormat format = new DecimalFormat("####.000");
1✔
512
        return format.format(10. - rating); // TODO add label?
1✔
513
    }
514

515
    private CompletionItem makeSimpleNameCompletion(String name) {
516
        CompletionItem completion = new CompletionItem(name);
×
517

518
        completion.setDetail("");
×
519
        double rating = calculateRating(name, WurstTypeUnknown.instance());
×
520
        completion.setSortText(ratingToString(rating));
×
521
        completion.setInsertText(name);
×
522

523
        return completion;
×
524
    }
525

526
    private double calculateRating(String name, WurstType wurstType) {
527
        double r = calculateNameBasedRating(name);
1✔
528
        if (expectedType != null && wurstType.isSubtypeOf(expectedType, elem)) {
1✔
529
            r += 0.1;
1✔
530
        }
531
        if (name.contains("BJ") || name.contains("Swapped")) {
1✔
532
            // common.j functions that Frotty does not want to see
533
            r -= 0.05;
1✔
534
        }
535
        return r;
1✔
536
    }
537

538
    private double calculateNameBasedRating(String name) {
539
        if (alreadyEntered.isEmpty()) {
1✔
540
            return 0.5;
1✔
541
        }
542
        if (name.startsWith(alreadyEntered)) {
1✔
543
            // perfect match
544
            return 1.23;
1✔
545
        }
546
        String nameLower = name.toLowerCase();
×
547
        if (nameLower.startsWith(alreadyEnteredLower)) {
×
548
            // close to perfect
549
            return 0.999;
×
550
        }
551

552
        int ssLen;
553
        if (Utils.isSubsequence(alreadyEntered, name)) {
×
554
            ssLen = Math.min(Utils.subsequenceLengthes(alreadyEntered, name).size(), Utils.subsequenceLengthes(alreadyEnteredLower, nameLower).size());
×
555
        } else {
556
            ssLen = Utils.subsequenceLengthes(alreadyEnteredLower, nameLower).size();
×
557
        }
558
        return 1 - ssLen * 1. / alreadyEntered.length();
×
559
    }
560

561
    private static String nearestScopeName(NameDef n) {
562
        if (n.attrNearestNamedScope() != null) {
1✔
563
            return Utils.printElement(n.attrNearestNamedScope());
1✔
564
        } else {
565
            return "Global";
1✔
566
        }
567
    }
568

569
    private CompletionItem makeFunctionCompletion(FuncLink f) {
570
        String replacementString = f.getName();
1✔
571
        List<WurstType> params = f.getParameterTypes();
1✔
572

573

574
        CompletionItem completion = new CompletionItem(f.getName());
1✔
575
        completion.setKind(CompletionItemKind.Function);
1✔
576
        completion.setDetail(getFunctionDescriptionShort(f.getDef()));
1✔
577
        completion.setDocumentation(HoverInfo.descriptionString(f.getDef()));
1✔
578
        completion.setInsertText(replacementString);
1✔
579
                double rating = calculateRating(f.getName(), f.getReturnType());
1✔
580
                if (f.getDef().attrHasAnnotation("deprecated")) {
1✔
581
                        rating -= 0.05;
1✔
582
                }
583
                completion.setSortText(ratingToString(rating));
1✔
584
        // TODO use call signature instead for generics
585
//        completion.set
586

587
        if (!isBeforeParenthesis()) {
1✔
588
            addParamSnippet(replacementString, f, completion);
1✔
589
        }
590

591

592
        return completion;
1✔
593
    }
594

595
    private void addParamSnippet(String replacementString, FuncLink f, CompletionItem completion) {
596
        List<String> paramNames = f.getParameterNames();
1✔
597
        StringBuilder lambdaReplacement = null;
1✔
598
        List<WurstType> parameterTypes = f.getParameterTypes();
1✔
599
        if (isAtEndOfLine() && !parameterTypes.isEmpty()) {
1✔
600
            WurstType lastParamType = Utils.getLast(parameterTypes);
1✔
601
            if (lastParamType instanceof WurstTypeClassOrInterface) {
1✔
602
                WurstTypeClassOrInterface it = (WurstTypeClassOrInterface) lastParamType;
×
603
                FuncLink singleAbstractMethod = it.findSingleAbstractMethod(elem);
×
604
                if (singleAbstractMethod != null) {
×
605
                    paramNames = Utils.init(paramNames);
×
606

607
                    if (singleAbstractMethod.getParameterTypes().size() == 0) {
×
608
                        lambdaReplacement = new StringBuilder(" -> \n");
×
609
                        cu.getCuInfo().getIndentationMode().appendIndent(lambdaReplacement, 1);
×
610
                    } else {
611
                        lambdaReplacement = new StringBuilder(" (");
×
612
                        for (int i = 0; i < singleAbstractMethod.getParameterTypes().size(); i++) {
×
613
                            if (i > 0) {
×
614
                                lambdaReplacement.append(", ");
×
615
                            }
616
                            lambdaReplacement.append(singleAbstractMethod.getParameterType(i));
×
617
                            lambdaReplacement.append(" ");
×
618
                            lambdaReplacement.append(singleAbstractMethod.getParameterName(i));
×
619
                        }
620
                        lambdaReplacement.append(") ->\n");
×
621
                        // only need to add one indent here, because \n already indents to the same line as before
622
                        cu.getCuInfo().getIndentationMode().appendIndent(lambdaReplacement, 1);
×
623
                    }
624
                }
625
            }
626
        }
627

628

629
        addParamSnippet(replacementString, paramNames, completion, lambdaReplacement);
1✔
630
    }
1✔
631

632
    private void addParamSnippet(String replacementString, List<String> paramNames, CompletionItem completion, StringBuilder lambdaReplacement) {
633
        if (paramNames.isEmpty()) {
1✔
634
            replacementString += "()";
1✔
635
        } else {
636
            List<String> paramSnippets = new ArrayList<>();
1✔
637
            for (int i = 0; i < paramNames.size(); i++) {
1✔
638
                String paramName = paramNames.get(i);
1✔
639
                paramSnippets.add("${" + (i + 1) + ":" + paramName + "}");
1✔
640
            }
641
            replacementString += "(" + String.join(", ", paramSnippets) + ")";
1✔
642
            completion.setInsertTextFormat(InsertTextFormat.Snippet);
1✔
643
        }
644
        if (lambdaReplacement != null) {
1✔
645
            replacementString += lambdaReplacement;
×
646
            completion.setInsertTextFormat(InsertTextFormat.Snippet);
×
647
        }
648
        completion.setInsertText(replacementString);
1✔
649
    }
1✔
650

651
    private String getFunctionDescriptionShort(FunctionDefinition f) {
652
        String displayString = "(" + Utils.getParameterListText(f) + ")";
1✔
653
        WurstType returnType = f.attrReturnTyp();
1✔
654
        if (!(returnType instanceof WurstTypeVoid)) {
1✔
655
            displayString += " returns " + returnType;
1✔
656
        }
657
        displayString += " [" + nearestScopeName(f) + "]";
1✔
658
        return displayString;
1✔
659
    }
660

661
    private CompletionItem makeConstructorCompletion(ClassDef c, ConstructorDef constr) {
662
        String replacementString = c.getName();
×
663
//                if (!isBeforeParenthesis()) {
664
//                        replacementString += "()";
665
//                }
666

667
        CompletionItem completion = new CompletionItem(c.getName());
×
668
        completion.setKind(CompletionItemKind.Constructor);
×
669
        String params = Utils.getParameterListText(constr);
×
670
        completion.setDetail("(" + params + ")");
×
671
        completion.setDocumentation(HoverInfo.descriptionString(constr));
×
672
        completion.setInsertTextFormat(InsertTextFormat.Snippet);
×
673
        completion.setInsertText(replacementString);
×
674
        completion.setSortText(ratingToString(calculateRating(c.getName(), c.attrTyp().dynamic())));
×
675

676

677
        List<String> parameterNames = constr.getParameters().stream().map(WParameter::getName).collect(Collectors.toList());
×
678
        addParamSnippet(replacementString, parameterNames, completion, null);
×
679

680
        return completion;
×
681
    }
682

683
    private void completionsAddVisibleExtensionFunctions(List<CompletionItem> completions, Multimap<String, DefLink> visibleNames,
684
                                                         WurstType leftType) {
685
        for (Entry<String, DefLink> e : visibleNames.entries()) {
1✔
686
            if (!isSuitableCompletion(e.getKey())) {
1✔
687
                continue;
1✔
688
            }
689
            if (e.getValue() instanceof FuncLink && e.getValue().getVisibility().isPublic()) {
1✔
690
                FuncLink ef = (FuncLink) e.getValue();
1✔
691
                FuncLink ef2 = ef.adaptToReceiverType(leftType);
1✔
692
                if (ef2 != null) {
1✔
693
                    completions.add(makeFunctionCompletion(ef2));
1✔
694
                }
695
            }
696
        }
1✔
697

698
    }
1✔
699

700
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc