Conditions | 19 |
Total Lines | 106 |
Code Lines | 76 |
Lines | 0 |
Ratio | 0 % |
Changes | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like twig-autocomplete.ts ➔ addCompletionItemsToMonaco often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | /** |
||
37 | |||
38 | /** |
||
39 | * Register completion items with the Monaco editor, for the Twig language |
||
40 | * |
||
41 | * @param {AutocompleteItem} completionItems - completion items, with sub-properties in `COMPLETION_KEY` |
||
42 | * @param {AutocompleteTypes} autocompleteType - the type of autocomplete |
||
43 | * @param {boolean} hasSubProperties - whether the autocomplete has sub-properties, and should be parsed as such |
||
44 | */ |
||
45 | function addCompletionItemsToMonaco(completionItems: AutocompleteItem, autocompleteType: AutocompleteTypes, hasSubProperties: boolean): void { |
||
46 | monaco.languages.registerCompletionItemProvider('twig', { |
||
47 | triggerCharacters: ['.', '('], |
||
48 | provideCompletionItems: function (model, position, token) { |
||
49 | const result: monaco.languages.CompletionItem[] = []; |
||
50 | let currentItems = completionItems; |
||
51 | // Get the last word the user has typed |
||
52 | const currentLine = model.getValueInRange({ |
||
53 | startLineNumber: position.lineNumber, |
||
54 | startColumn: 0, |
||
55 | endLineNumber: position.lineNumber, |
||
56 | endColumn: position.column |
||
57 | }); |
||
58 | let inTwigExpression = true; |
||
59 | // Ensure we're inside of a Twig expression |
||
60 | if (currentLine.lastIndexOf('{') === -1) { |
||
61 | inTwigExpression = false; |
||
62 | } |
||
63 | const startExpression = currentLine.substring(currentLine.lastIndexOf('{')); |
||
64 | if (startExpression.indexOf('}') !== -1) { |
||
65 | inTwigExpression = false; |
||
66 | } |
||
67 | // We are not in a Twig expression, and this is a TwigExpressionAutocomplete, return nothing |
||
68 | if (!inTwigExpression && autocompleteType === 'TwigExpressionAutocomplete') { |
||
69 | return null; |
||
70 | } |
||
71 | // Get the current word we're typing |
||
72 | const currentWords = currentLine.replace("\t", "").split(" "); |
||
73 | let currentWord = currentWords[currentWords.length - 1]; |
||
74 | // If the current word includes { or ( or >, split on that, too, to allow the autocomplete to work in nested functions and HTML tags |
||
75 | if (currentWord.includes('{')) { |
||
76 | currentWord = getLastItem(currentWord.split('{')); |
||
77 | } |
||
78 | if (currentWord.includes('(')) { |
||
79 | currentWord = getLastItem(currentWord.split('(')); |
||
80 | } |
||
81 | if (currentWord.includes('>')) { |
||
82 | currentWord = getLastItem(currentWord.split('>')); |
||
83 | } |
||
84 | const isSubProperty = currentWord.charAt(currentWord.length - 1) === "."; |
||
85 | // If we're in a sub-property (following a .) don't present non-TwigExpressionAutocomplete items |
||
86 | if (isSubProperty && autocompleteType !== 'TwigExpressionAutocomplete') { |
||
87 | return null; |
||
88 | } |
||
89 | // We are in a Twig expression, handle TwigExpressionAutocomplete by walking through the properties |
||
90 | if (inTwigExpression && autocompleteType === 'TwigExpressionAutocomplete') { |
||
91 | // If the last character typed is a period, then we need to look up a sub-property of the completionItems |
||
92 | if (isSubProperty) { |
||
93 | // If we're in a sub-property, and this autocomplete doesn't have sub-properties, don't return its items |
||
94 | if (!hasSubProperties) { |
||
95 | return null; |
||
96 | } |
||
97 | // Is a sub-property, get a list of parent properties |
||
98 | const parents = currentWord.substring(0, currentWord.length - 1).split("."); |
||
99 | if (typeof completionItems[parents[0]] !== 'undefined') { |
||
100 | currentItems = completionItems[parents[0]]; |
||
101 | // Loop through all the parents to traverse the completion items and find the current one |
||
102 | for (let i = 1; i < parents.length; i++) { |
||
103 | if (currentItems.hasOwnProperty(parents[i])) { |
||
104 | currentItems = currentItems[parents[i]]; |
||
105 | } else { |
||
106 | const finalItems: monaco.languages.ProviderResult<monaco.languages.CompletionList> = { |
||
107 | suggestions: result |
||
108 | } |
||
109 | return finalItems; |
||
110 | } |
||
111 | } |
||
112 | } |
||
113 | } |
||
114 | } |
||
115 | // Get all the child properties |
||
116 | if (typeof currentItems !== 'undefined') { |
||
117 | for (const item in currentItems) { |
||
118 | if (currentItems.hasOwnProperty(item) && !item.startsWith("__")) { |
||
119 | const completionItem = currentItems[item][COMPLETION_KEY]; |
||
120 | if (typeof completionItem !== 'undefined') { |
||
121 | // Monaco adds a 'range' to the object, to denote where the autocomplete is triggered from, |
||
122 | // which needs to be removed each time the autocomplete objects are re-used |
||
123 | delete completionItem.range; |
||
124 | if ('documentation' in completionItem && typeof completionItem.documentation !== 'object') { |
||
125 | const docs = completionItem.documentation; |
||
126 | completionItem.documentation = { |
||
127 | value: docs, |
||
128 | isTrusted: true, |
||
129 | supportsHtml: true |
||
130 | } |
||
131 | } |
||
132 | // Add to final results |
||
133 | result.push(completionItem); |
||
134 | } |
||
135 | } |
||
136 | } |
||
137 | } |
||
138 | |||
139 | const finalItems: monaco.languages.ProviderResult<monaco.languages.CompletionList> = { |
||
140 | suggestions: result |
||
141 | } |
||
142 | return finalItems; |
||
143 | } |
||
265 |