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

wurstscript / WurstScript / 196

09 Oct 2023 10:49PM UTC coverage: 63.447% (+0.006%) from 63.441%
196

push

circleci

web-flow
small performance fixes (#1079)

17146 of 27024 relevant lines covered (63.45%)

0.63 hits per line

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

62.03
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" +
1✔
59
                    "" + currentLine().replace('\t', ' ') + "\n" +
1✔
60
                    "" + Utils.repeat(' ', column > 0 ? column : 0) + "^\n" +
1✔
61
                    " at column " + column);
62
        }
63
    }
1✔
64

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

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

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

92
    private enum SearchMode {
1✔
93
        PREFIX, INFIX, SUBSEQENCE
1✔
94
    }
95

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

101
        if (isEnteringRealNumber()) {
1✔
102
            return null;
×
103
        }
104

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

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

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

122
            calculateCompletions(completions);
1✔
123

124
            dropBadCompletions(completions);
1✔
125
            removeDuplicates(completions);
1✔
126

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

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

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

148
            WurstType leftType = e.getLeft().attrTyp();
1✔
149

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

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

232
    }
1✔
233

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

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

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

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

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

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

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

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

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

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

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

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

376

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

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

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

407
    }
1✔
408

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

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

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

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

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

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

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

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

508
        return completion;
1✔
509
    }
510

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

517
    private CompletionItem makeSimpleNameCompletion(String name) {
518
        CompletionItem completion = new CompletionItem(name);
×
519

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

525
        return completion;
×
526
    }
527

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

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

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

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

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

575

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

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

593

594
        return completion;
1✔
595
    }
596

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

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

630

631
        addParamSnippet(replacementString, paramNames, completion, lambdaReplacement);
1✔
632
    }
1✔
633

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

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

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

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

678

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

682
        return completion;
×
683
    }
684

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

700
    }
1✔
701

702
}
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