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