Passed
Push — v1 ( 532872...bd3ac4 )
by Andrew
15:20 queued 08:26
created

TwigLanguageAutocomplete::init()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 4
rs 10
1
<?php
2
/**
3
 * Twigfield for Craft CMS
4
 *
5
 * Provides a twig editor field with Twig & Craft API autocomplete
6
 *
7
 * @link      https://nystudio107.com
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @copyright tag
Loading history...
8
 * @copyright Copyright (c) 2022 nystudio107
0 ignored issues
show
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
9
 */
0 ignored issues
show
Coding Style introduced by
PHP version not specified
Loading history...
Coding Style introduced by
Missing @category tag in file comment
Loading history...
Coding Style introduced by
Missing @package tag in file comment
Loading history...
Coding Style introduced by
Missing @author tag in file comment
Loading history...
Coding Style introduced by
Missing @license tag in file comment
Loading history...
10
11
namespace nystudio107\twigfield\autocompletes;
12
13
use Craft;
14
use nystudio107\twigfield\base\Autocomplete;
15
use nystudio107\twigfield\models\CompleteItem;
16
use nystudio107\twigfield\types\AutocompleteTypes;
17
use nystudio107\twigfield\types\CompleteItemKind;
18
use Twig\Environment;
19
20
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
21
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Coding Style introduced by
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
22
 * @package   twigfield
0 ignored issues
show
Coding Style introduced by
Package name "twigfield" is not valid; consider "Twigfield" instead
Loading history...
Coding Style introduced by
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
23
 * @since     1.0.0
0 ignored issues
show
Coding Style introduced by
The tag in position 3 should be the @author tag
Loading history...
Coding Style introduced by
Tag value for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
24
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
25
class TwigLanguageAutocomplete extends Autocomplete
26
{
27
    // Constants
28
    // =========================================================================
29
30
    const CRAFT_FILTER_DOCS_URL = 'https://craftcms.com/docs/4.x/dev/filters.html';
31
    const FILTER_DOCS = [
32
        'abs' => '[abs](https://twig.symfony.com/doc/3.x/filters/abs.html) | Returns the absolute value of a number.',
33
        'address' => '[address](#address) | Formats an address.',
34
        'append' => '[append](#append) | Appends HTML to the end of another element.',
35
        'ascii' => '[ascii](#ascii) | Converts a string to ASCII characters.',
36
        'atom' => '[atom](#atom) | Converts a date to an ISO-8601 timestamp.',
37
        'attr' => '[attr](#attr) | Modifies an HTML tag’s attributes.',
38
        'batch' => '[batch](https://twig.symfony.com/doc/3.x/filters/batch.html) | Batches items in an array.',
39
        'camel' => '[camel](#camel) | Formats a string into camelCase.',
40
        'capitalize' => '[capitalize](https://twig.symfony.com/doc/3.x/filters/capitalize.html) | Capitalizes the first character of a string.',
41
        'column' => '[column](#column) | Returns the values from a single property or key in an array.',
42
        'contains' => '[contains](#contains) | Returns whether an array contains a nested item with a given key-value pair.',
43
        'convert_encoding' => '[convert_encoding](https://twig.symfony.com/doc/3.x/filters/convert_encoding.html) | Converts a string from one encoding to another.',
44
        'currency' => '[currency](#currency) | Formats a number as currency.',
45
        'date' => '[date](#date) | Formats a date.',
46
        'date_modify' => '[date_modify](https://twig.symfony.com/doc/3.x/filters/date_modify.html) | Modifies a date.',
47
        'datetime' => '[datetime](#datetime) | Formats a date with its time.',
48
        'default' => '[default](https://twig.symfony.com/doc/3.x/filters/default.html) | Returns the value or a default value if empty.',
49
        'diff' => '[diff](#diff) | Returns the difference between arrays.',
50
        'duration' => '[duration](#duration) | Returns a `DateInterval` object.',
51
        'e' => '[e](https://twig.symfony.com/doc/3.x/filters/escape.html) | Escapes a string.',
52
        'encenc' => '[encenc](#encenc) | Encrypts and base64-encodes a string.',
53
        'escape' => '[escape](https://twig.symfony.com/doc/3.x/filters/escape.html) | Escapes a string.',
54
        'explodeClass' => '[explodeClass](#explodeclass) | Converts a `class` attribute value into an array of class names.',
55
        'explodeStyle' => '[explodeStyle](#explodestyle) | Converts a `style` attribute value into an array of property name/value pairs.',
56
        'filesize' => '[filesize](#filesize) | Formats a number of bytes into something else.',
57
        'filter' => '[filter](#filter) | Filters the items in an array.',
58
        'first' => '[first](https://twig.symfony.com/doc/3.x/filters/first.html) | Returns the first character/item of a string/array.',
59
        'format' => '[format](https://twig.symfony.com/doc/3.x/filters/format.html) | Formats a string by replacing placeholders.',
60
        'group' => '[group](#group) | Groups items in an array.',
61
        'hash' => '[hash](#hash) | Prefixes a string with a keyed-hash message authentication code (HMAC).',
62
        'httpdate' => '[httpdate](#httpdate) | Converts a date to the HTTP format.',
63
        'id' => '[id](#id) | Normalizes an element ID into only alphanumeric characters, underscores, and dashes.',
64
        'index' => '[index](#index) | Indexes the items in an array.',
65
        'indexOf' => '[indexOf](#indexof) | Returns the index of a given value within an array, or the position of a passed-in string within another string.',
66
        'intersect' => '[intersect](#intersect) | Returns the intersecting items of two arrays.',
67
        'join' => '[join](https://twig.symfony.com/doc/3.x/filters/join.html) | Concatenates multiple strings into one.',
68
        'json_decode' => '[json_decode](#json_decode) | JSON-decodes a value.',
69
        'json_encode' => '[json_encode](#json_encode) | JSON-encodes a value.',
70
        'kebab' => '[kebab](#kebab) | Formats a string into “kebab-case”.',
71
        'keys' => '[keys](https://twig.symfony.com/doc/3.x/filters/keys.html) | Returns the keys of an array.',
72
        'last' => '[last](https://twig.symfony.com/doc/3.x/filters/last.html) | Returns the last character/item of a string/array.',
73
        'lcfirst' => '[lcfirst](#lcfirst) | Lowercases the first character of a string.',
74
        'length' => '[length](https://twig.symfony.com/doc/3.x/filters/length.html) | Returns the length of a string or array.',
75
        'literal' => '[literal](#literal) | Escapes an untrusted string for use with element query params.',
76
        'lower' => '[lower](https://twig.symfony.com/doc/3.x/filters/lower.html) | Lowercases a string.',
77
        'map' => '[map](https://twig.symfony.com/doc/3.x/filters/map.html) | Applies an arrow function to the items in an array.',
78
        'markdown' => '[markdown](#markdown-or-md) | Processes a string as Markdown.',
79
        'md' => '[md](#markdown-or-md) | Processes a string as Markdown.',
80
        'merge' => '[merge](#merge) | Merges an array with another one.',
81
        'money' => '[money](#money) | Outputs a value from a Money object.',
82
        'multisort' => '[multisort](#multisort) | Sorts an array by one or more keys within its sub-arrays.',
83
        'namespace' => '[namespace](#namespace) | Namespaces input names and other HTML attributes, as well as CSS selectors.',
84
        'namespaceAttributes' => '',
85
        'namespaceInputId' => '[namespaceInputId](#namespaceinputid) | Namespaces an element ID.',
86
        'namespaceInputName' => '[namespaceInputName](#namespaceinputname) | Namespaces an input name.',
87
        'nl2br' => '[nl2br](https://twig.symfony.com/doc/3.x/filters/nl2br.html) | Replaces newlines with `<br>` tags.',
88
        'ns' => '[ns](#namespace) | Namespaces input names and other HTML attributes, as well as CSS selectors.',
89
        'number' => '[number](#number) | Formats a number.',
90
        'number_format' => '[number_format](https://twig.symfony.com/doc/3.x/filters/number_format.html) | Formats numbers.',
91
        'parseAttr' => '',
92
        'parseRefs' => '[parseRefs](#parserefs) | Parses a string for reference tags.',
93
        'pascal' => '[pascal](#pascal) | Formats a string into “PascalCase”.',
94
        'percentage' => '[percentage](#percentage) | Formats a percentage.',
95
        'prepend' => '[prepend](#prepend) | Prepends HTML to the beginning of another element.',
96
        'purify' => '[purify](#purify) | Runs HTML code through HTML Purifier.',
97
        'push' => '[push](#push) | Appends one or more items onto the end of an array.',
98
        'raw' => '[raw](https://twig.symfony.com/doc/3.x/filters/raw.html) | Marks as value as safe for the current escaping strategy.',
99
        'reduce' => '[reduce](https://twig.symfony.com/doc/3.x/filters/reduce.html) | Iteratively reduces a sequence or mapping to a single value.',
100
        'removeClass' => '[removeClass](#removeclass) | Removes a class (or classes) from the given HTML tag.',
101
        'replace' => '[replace](#replace) | Replaces parts of a string with other things.',
102
        'reverse' => '[reverse](https://twig.symfony.com/doc/3.x/filters/reverse.html) | Reverses a string or array.',
103
        'round' => '[round](https://twig.symfony.com/doc/3.x/filters/round.html) | Rounds a number.',
104
        'rss' => '[rss](#rss) | Converts a date to RSS date format.',
105
        'slice' => '[slice](https://twig.symfony.com/doc/3.x/filters/slice.html) | Extracts a slice of a string or array.',
106
        'snake' => '[snake](#snake) | Formats a string into “snake_case”.',
107
        'sort' => '[sort](https://twig.symfony.com/doc/3.x/filters/sort.html) | Sorts an array.',
108
        'spaceless' => '[spaceless](https://twig.symfony.com/doc/3.x/filters/spaceless.html) | Removes whitespace between HTML tags.',
109
        'split' => '[split](https://twig.symfony.com/doc/3.x/filters/split.html) | Splits a string by a delimiter.',
110
        'striptags' => '[striptags](https://twig.symfony.com/doc/3.x/filters/striptags.html) | Strips SGML/XML tags from a string.',
111
        't' => '[t](#translate-or-t) | Translates a message.',
112
        'time' => '[time](#time) | Formats a time.',
113
        'timestamp' => '[timestamp](#timestamp) | Formats a human-readable timestamp.',
114
        'title' => '[title](https://twig.symfony.com/doc/3.x/filters/title.html) | Formats a string into “Title Case”.',
115
        'translate' => '[translate](#translate-or-t) | Translates a message.',
116
        'trim' => '[trim](https://twig.symfony.com/doc/3.x/filters/trim.html) | Strips whitespace from the beginning and end of a string.',
117
        'truncate' => '[truncate](#truncate) | Truncates a string to a given length, while ensuring that it does not split words.',
118
        'ucfirst' => '[ucfirst](#ucfirst) | Capitalizes the first character of a string.',
119
        'ucwords' => '',
120
        'unique' => '[unique](#unique) | Removes duplicate values from an array.',
121
        'unshift' => '[unshift](#unshift) | Prepends one or more items to the beginning of an array.',
122
        'upper' => '[upper](https://twig.symfony.com/doc/3.x/filters/upper.html) | Formats a string into “UPPER CASE”.',
123
        'url_encode' => '[url_encode](https://twig.symfony.com/doc/3.x/filters/url_encode.html) | Percent-encodes a string as a URL segment or an array as a query string.',
124
        'values' => '[values](#values) | Returns all the values in an array, resetting its keys.',
125
        'where' => '[where](#where) | Filters an array by key-value pairs.',
126
        'widont' => '',
127
        'without' => '[without](#without) | Returns an array without the specified element(s).',
128
        'withoutKey' => '[withoutKey](#withoutkey) | Returns an array without the specified key.',
129
    ];
130
131
    const CRAFT_FUNCTION_DOCS_URL = 'https://craftcms.com/docs/4.x/dev/functions.html';
132
    const FUNCTION_DOCS = [
133
        'actionInput' => '[actionInput](#actioninput) | Outputs a hidden `action` input.',
134
        'actionUrl' => '[actionUrl](#actionurl) | Generates a controller action URL.',
135
        'alias' => '[alias](#alias) | Parses a string as an alias.',
136
        'attr' => '[attr](#attr) | Generates HTML attributes.',
137
        'beginBody' => '[beginBody](#beginbody) | Outputs scripts and styles that were registered for the “begin body” position.',
138
        'ceil' => '[ceil](#ceil) | Rounds a number up.',
139
        'className' => '[className](#classname) | Returns the fully qualified class name of a given object.',
140
        'clone' => '[clone](#clone) | Clones an object.',
141
        'collect' => '[collect](#collect) | Returns a new collection.',
142
        'combine' => '[combine](#combine) | Combines two arrays into one.',
143
        'configure' => '[configure](#configure) | Sets attributes on the passed object.',
144
        'constant' => '[constant](https://twig.symfony.com/doc/3.x/functions/constant.html) | Returns the constant value for a given string.',
145
        'cpUrl' => '[cpUrl](#cpurl) | Generates a control panel URL.',
146
        'create' => '[create](#create) | Creates a new object.',
147
        'csrfInput' => '[csrfInput](#csrfinput) | Returns a hidden CSRF token input.',
148
        'cycle' => '[cycle](https://twig.symfony.com/doc/3.x/functions/cycle.html) | Cycles on an array of values.',
149
        'dataUrl' => '[dataUrl](#dataurl) | Outputs an asset or file as a base64-encoded data URL.',
150
        'date' => '[date](#date) | Creates a date.',
151
        'dump' => '[dump](https://twig.symfony.com/doc/3.x/functions/dump.html) | Dumps information about a variable.',
152
        'endBody' => '[endBody](#endbody) | Outputs scripts and styles that were registered for the “end body” position.',
153
        'expression' => '[expression](#expression) | Creates a database expression object.',
154
        'failMessageInput' => '[failMessageInput](#failmessageinput) | Outputs a hidden `failMessage` input.',
155
        'floor' => '[floor](#floor) | Rounds a number down.',
156
        'getenv' => '[getenv](#getenv) | Returns the value of an environment variable.',
157
        'gql' => '[gql](#gql) | Executes a GraphQL query against the full schema.',
158
        'head' => '[head](#head) | Outputs scripts and styles that were registered for the “head” position.',
159
        'hiddenInput' => '[hiddenInput](#hiddeninput) | Outputs a hidden input.',
160
        'include' => '[include](https://twig.symfony.com/doc/3.x/functions/include.html) | Returns the rendered content of a template.',
161
        'input' => '[input](#input) | Outputs an HTML input.',
162
        'max' => '[max](https://twig.symfony.com/doc/3.x/functions/max.html) | Returns the biggest value in an array.',
163
        'min' => '[min](https://twig.symfony.com/doc/3.x/functions/min.html) | Returns the lowest value in an array.',
164
        'ol' => '[ol](#ol) | Outputs an array of items as an ordered list.',
165
        'parseBooleanEnv' => '[parseBooleanEnv](#parsebooleanenv) | Parses a string as an environment variable or alias having a boolean value.',
166
        'parseEnv' => '[parseEnv](#parseenv) | Parses a string as an environment variable or alias.',
167
        'plugin' => '[plugin](#plugin) | Returns a plugin instance by its handle.',
168
        'random' => '[random](https://twig.symfony.com/doc/3.x/functions/random.html) | Returns a random value.',
169
        'range' => '[range](https://twig.symfony.com/doc/3.x/functions/range.html) | Returns a list containing an arithmetic progression of integers.',
170
        'raw' => '[raw](#raw) | Wraps the given string in a `Twig\Markup` object to prevent it from getting HTML-encoded when output.',
171
        'redirectInput' => '[redirectInput](#redirectinput) | Outputs a hidden `redirect` input.',
172
        'renderObjectTemplate' => '',
173
        'seq' => '[seq](#seq) | Outputs the next or current number in a sequence.',
174
        'shuffle' => '[shuffle](#shuffle) | Randomizes the order of the items in an array.',
175
        'siteUrl' => '[siteUrl](#siteurl) | Generates a front-end URL.',
176
        'source' => '[source](https://twig.symfony.com/doc/3.x/functions/source.html) | Returns the content of a template without rendering it.',
177
        'successMessageInput' => '[successMessageInput](#successmessageinput) | Outputs a hidden `successMessage` input.',
178
        'svg' => '[svg](#svg) | Outputs an SVG document.',
179
        'tag' => '[tag](#tag) | Outputs an HTML tag.',
180
        'template_from_string' => '[template_from_string](https://twig.symfony.com/doc/3.x/functions/template_from_string.html) | Loads a template from a string.',
181
        'ul' => '[ul](#ul) | Outputs an array of items as an unordered list.',
182
        'url' => '[url](#url) | Generates a URL.',
183
    ];
184
185
    const CRAFT_TAG_DOCS_URL = 'https://craftcms.com/docs/4.x/dev/tags.html';
186
    const TAG_DOCS = [
187
        'apply' => '[apply](https://twig.symfony.com/doc/3.x/tags/apply.html) | Applies Twig filters to the nested template code.',
188
        'autoescape' => '[autoescape](https://twig.symfony.com/doc/3.x/tags/autoescape.html) | Controls the escaping strategy for the nested template code.',
189
        'block' => '[block](https://twig.symfony.com/doc/3.x/tags/block.html) | Defines a template block.',
190
        'cache' => '[cache](#cache) | Caches a portion of your template.',
191
        'css' => '[css](#css) | Registers a `<style>` tag on the page.',
192
        'dd' => '[dd](#dd) | Dump and die.',
193
        'deprecated' => '[deprecated](https://twig.symfony.com/doc/3.x/tags/deprecated.html) | Triggers a PHP deprecation error.',
194
        'do' => '[do](https://twig.symfony.com/doc/3.x/tags/do.html) | Does.',
195
        'else' => '[else](https://twig.symfony.com/doc/3.x/tags/if.html) | Else conditional.',
196
        'elseif' => '[else](https://twig.symfony.com/doc/3.x/tags/if.html) | Else if conditional.',
197
        'embed' => '[embed](https://twig.symfony.com/doc/3.x/tags/embed.html) | Embeds another template.',
198
        'endblock' => '[endblock](https://twig.symfony.com/doc/3.x/tags/block.html) | End a template block.',
199
        'endif' => '[endif](https://twig.symfony.com/doc/3.x/tags/if.html) | End a conditional if block.',
200
        'exit' => '[exit](#exit) | Ends the request.',
201
        'extends' => '[extends](https://twig.symfony.com/doc/3.x/tags/extends.html) | Extends another template.',
202
        'flush' => '',
203
        'for' => '[for](https://twig.symfony.com/doc/3.x/tags/for.html) | Loops through an array.',
204
        'from' => '[from](https://twig.symfony.com/doc/3.x/tags/from.html) | Imports macros from a template.',
205
        'header' => '[header](#header) | Sets an HTTP header on the response.',
206
        'hook' => '[hook](#hook) | Invokes a template hook.',
207
        'html' => '[html](#html) | Registers arbitrary HTML code on the page.',
208
        'if' => '[if](https://twig.symfony.com/doc/3.x/tags/if.html) | Conditionally executes the nested template code.',
209
        'import' => '[import](https://twig.symfony.com/doc/3.x/tags/import.html) | Imports macros from a template.',
210
        'include' => '[include](https://twig.symfony.com/doc/3.x/tags/include.html) | Includes another template.',
211
        'js' => '[js](#js) | Registers a `<script>` tag on the page.',
212
        'macro' => '[macro](https://twig.symfony.com/doc/3.x/tags/macro.html) | Defines a macro.',
213
        'namespace' => '[namespace](#namespace) | Namespaces input names and other HTML attributes, as well as CSS selectors.',
214
        'nav' => '[nav](#nav) | Creates a hierarchical nav menu.',
215
        'paginate' => '[paginate](#paginate) | Paginates an element query.',
216
        'redirect' => '[redirect](#redirect) | Redirects the browser.',
217
        'requireAdmin' => '',
218
        'requireEdition' => '',
219
        'requireGuest' => '[requireGuest](#requireguest) | Requires that no user is logged-in.',
220
        'requireLogin' => '[requireLogin](#requirelogin) | Requires that a user is logged-in.',
221
        'requirePermission' => '[requirePermission](#requirepermission) | Requires that a user is logged-in with a given permission.',
222
        'script' => '[script](#script) | Renders an HTML script tag on the page.',
223
        'set' => '[set](https://twig.symfony.com/doc/3.x/tags/set.html) | Sets a variable.',
224
        'switch' => '[switch](#switch) | Switch the template output based on a give value.',
225
        'tag' => '[tag](#tag) | Renders an HTML tag on the page.',
226
        'use' => '[use](https://twig.symfony.com/doc/3.x/tags/use.html) | Inherits from another template horizontally.',
227
        'with' => '[with](https://twig.symfony.com/doc/3.x/tags/with.html) | Creates a nested template scope.',
228
    ];
229
230
    const ADDITIONAL_TAGS = [
231
        'else',
232
        'elseif',
233
        'endblock',
234
        'endif',
235
    ];
236
237
    // Public Properties
238
    // =========================================================================
239
240
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
241
     * @var string The name of the autocomplete
242
     */
243
    public $name = 'TwigLanguageAutocomplete';
244
245
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
246
     * @var string The type of the autocomplete
247
     */
248
    public $type = AutocompleteTypes::TwigExpressionAutocomplete;
249
250
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
251
     * @var string Whether the autocomplete should be parsed with . -delimited nested sub-properties
252
     */
253
    public $hasSubProperties = false;
254
255
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
256
     * @var ?Environment The Twig environment to parse for functions/filters/tags
257
     */
258
    public $twig;
259
260
    // Public Methods
261
    // =========================================================================
262
263
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
264
     * @inerhitDoc
265
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
266
    public function init(): void
267
    {
268
        if (empty($this->twig)) {
269
            $this->twig = Craft::$app->getView()->getTwig();
270
        }
271
    }
272
273
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
274
     * @inerhitDoc
275
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
276
    public function generateCompleteItems(): void
277
    {
278
        $twig = $this->twig;
279
        // Twig Filters
280
        $filters = array_keys($twig->getFilters());
0 ignored issues
show
Bug introduced by
The method getFilters() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

280
        $filters = array_keys($twig->/** @scrutinizer ignore-call */ getFilters());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
281
        foreach ($filters as $filter) {
282
            $docs = self::FILTER_DOCS[$filter] ?? '';
283
            $docs = str_replace('(#', '(' . self::CRAFT_FILTER_DOCS_URL . '#', $docs);
284
            CompleteItem::create()
285
                ->label($filter)
286
                ->insertText($filter)
287
                ->detail(Craft::t('twigfield', 'Twig Filter'))
288
                ->documentation($docs)
289
                ->kind(CompleteItemKind::MethodKind)
290
                ->add($this);
291
        }
292
        // Twig Functions
293
        $functions = array_keys($twig->getFunctions());
294
        foreach ($functions as $function) {
295
            $functionLabel = $function . '()';
296
            $docs = self::FUNCTION_DOCS[$function] ?? '';
297
            $docs = str_replace('(#', '(' . self::CRAFT_FUNCTION_DOCS_URL . '#', $docs);
298
            CompleteItem::create()
299
                ->label($functionLabel)
300
                ->insertText($functionLabel)
301
                ->detail(Craft::t('twigfield', 'Twig Function'))
302
                ->documentation($docs)
303
                ->kind(CompleteItemKind::FunctionKind)
304
                ->add($this);
305
        }
306
        // Twig Tags
307
        $tags = array_merge(self::ADDITIONAL_TAGS, array_keys($twig->getTokenParsers()));
308
        foreach ($tags as $tag) {
309
            $docs = self::TAG_DOCS[$tag] ?? '';
310
            $docs = str_replace('(#', '(' . self::CRAFT_TAG_DOCS_URL . '#', $docs);
311
            CompleteItem::create()
312
                ->label($tag)
313
                ->insertText($tag)
314
                ->detail(Craft::t('twigfield', 'Twig Tag'))
315
                ->documentation($docs)
316
                ->kind(CompleteItemKind::FieldKind)
317
                ->add($this);
318
        }
319
    }
320
}
321