/
snippetForFunctionCall.ts
104 lines (94 loc) · 3.08 KB
/
snippetForFunctionCall.ts
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
import type * as Proto from '../protocol';
import * as PConst from '../protocol.const';
export function snippetForFunctionCall(
item: { insertText?: string; label: string; },
displayParts: ReadonlyArray<Proto.SymbolDisplayPart>
): { snippet: string; parameterCount: number; } {
if (item.insertText && typeof item.insertText !== 'string') {
return { snippet: item.insertText, parameterCount: 0 };
}
let _tabstop = 1;
const parameterListParts = getParameterListParts(displayParts);
let snippet = '';
snippet += `${item.insertText || item.label}(`;
snippet = appendJoinedPlaceholders(snippet, parameterListParts.parts, ', ');
if (parameterListParts.hasOptionalParameters) {
snippet += '$' + _tabstop++;
}
snippet += ')';
snippet += '$' + _tabstop++;
return { snippet, parameterCount: parameterListParts.parts.length + (parameterListParts.hasOptionalParameters ? 1 : 0) };
function appendJoinedPlaceholders(
snippet: string,
parts: ReadonlyArray<Proto.SymbolDisplayPart>,
joiner: string
) {
for (let i = 0; i < parts.length; ++i) {
const paramterPart = parts[i];
snippet += '${' + _tabstop++ + ':' + paramterPart.text + '}';
if (i !== parts.length - 1) {
snippet += joiner;
}
}
return snippet;
}
}
interface ParamterListParts {
readonly parts: ReadonlyArray<Proto.SymbolDisplayPart>;
readonly hasOptionalParameters: boolean;
}
function getParameterListParts(
displayParts: ReadonlyArray<Proto.SymbolDisplayPart>
): ParamterListParts {
const parts: Proto.SymbolDisplayPart[] = [];
let isInMethod = false;
let hasOptionalParameters = false;
let parenCount = 0;
let braceCount = 0;
outer: for (let i = 0; i < displayParts.length; ++i) {
const part = displayParts[i];
switch (part.kind) {
case PConst.DisplayPartKind.methodName:
case PConst.DisplayPartKind.functionName:
case PConst.DisplayPartKind.text:
case PConst.DisplayPartKind.propertyName:
if (parenCount === 0 && braceCount === 0) {
isInMethod = true;
}
break;
case PConst.DisplayPartKind.parameterName:
if (parenCount === 1 && braceCount === 0 && isInMethod) {
// Only take top level paren names
const next = displayParts[i + 1];
// Skip optional parameters
const nameIsFollowedByOptionalIndicator = next && next.text === '?';
// Skip this parameter
const nameIsThis = part.text === 'this';
if (!nameIsFollowedByOptionalIndicator && !nameIsThis) {
parts.push(part);
}
hasOptionalParameters = hasOptionalParameters || nameIsFollowedByOptionalIndicator;
}
break;
case PConst.DisplayPartKind.punctuation:
if (part.text === '(') {
++parenCount;
} else if (part.text === ')') {
--parenCount;
if (parenCount <= 0 && isInMethod) {
break outer;
}
} else if (part.text === '...' && parenCount === 1) {
// Found rest parmeter. Do not fill in any further arguments
hasOptionalParameters = true;
break outer;
} else if (part.text === '{') {
++braceCount;
} else if (part.text === '}') {
--braceCount;
}
break;
}
}
return { hasOptionalParameters, parts };
}