├── .eslintrc.json
├── .gitattributes
├── .github
└── FUNDING.yml
├── .gitignore
├── .vscode
├── extensions.json
├── launch.json
└── settings.json
├── .vscodeignore
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── extension
└── index.js
├── images
└── icon.png
├── package-lock.json
├── package.json
├── rollup.config.js
└── src
├── extension.js
├── hover
├── filters.json
├── functions.json
└── twig.json
├── languages
└── twig.configuration.json
├── snippets
└── snippets.json
└── syntaxes
└── twig.tmLanguage
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": false,
4 | "commonjs": true,
5 | "es6": true,
6 | "node": true
7 | },
8 | "parserOptions": {
9 | "ecmaFeatures": {
10 | "jsx": true
11 | },
12 | "sourceType": "module"
13 | },
14 | "rules": {
15 | "no-const-assign": "warn",
16 | "no-this-before-super": "warn",
17 | "no-undef": "warn",
18 | "no-unreachable": "warn",
19 | "no-unused-vars": "warn",
20 | "constructor-super": "warn",
21 | "valid-typeof": "warn"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set default behavior to automatically normalize line endings.
2 | * text=auto
3 |
4 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [mblode]
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .vscode-test/
3 | *.vsix
4 |
5 | ### OSX ###
6 | # General
7 | .DS_Store
8 | .AppleDouble
9 | .LSOverride
10 |
11 | # Icon must end with two \r
12 | Icon
13 |
14 | # Thumbnails
15 | ._*
16 |
17 | # Files that might appear in the root of a volume
18 | .DocumentRevisions-V100
19 | .fseventsd
20 | .Spotlight-V100
21 | .TemporaryItems
22 | .Trashes
23 | .VolumeIcon.icns
24 | .com.apple.timemachine.donotpresent
25 |
26 | # Directories potentially created on remote AFP share
27 | .AppleDB
28 | .AppleDesktop
29 | Network Trash Folder
30 | Temporary Items
31 | .apdisk
32 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See http://go.microsoft.com/fwlink/?LinkId=827846
3 | // for the documentation about the extensions.json format
4 | "recommendations": [
5 | "dbaeumer.vscode-eslint"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | // A launch configuration that launches the extension inside a new window
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | {
6 | "version": "0.2.0",
7 | "configurations": [
8 | {
9 | "name": "Extension",
10 | "type": "extensionHost",
11 | "request": "launch",
12 | "runtimeExecutable": "${execPath}",
13 | "args": [
14 | "--extensionDevelopmentPath=${workspaceFolder}"
15 | ]
16 | },
17 | {
18 | "name": "Extension Tests",
19 | "type": "extensionHost",
20 | "request": "launch",
21 | "runtimeExecutable": "${execPath}",
22 | "args": [
23 | "--extensionDevelopmentPath=${workspaceFolder}",
24 | "--extensionTestsPath=${workspaceFolder}/test"
25 | ]
26 | }
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.enable": true,
3 | "vscode-journal-view.expanded": true
4 | }
5 |
--------------------------------------------------------------------------------
/.vscodeignore:
--------------------------------------------------------------------------------
1 | .vscode/**
2 | .vscode-test/**
3 | test/**
4 | .gitignore
5 | jsconfig.json
6 | vsc-extension-quickstart.md
7 | .eslintrc.json
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | ## [0.8.0] - 2019-04-17
6 |
7 | ### Changed
8 |
9 | - Pretty Diff formatting bug has been solved correctly
10 |
11 | ### Added
12 |
13 | - Added many more configuration options
14 |
15 | ## [0.7.0] - 2019-04-01
16 |
17 | ### Changed
18 |
19 | - Fixed bug that clears entire document and only leaves the script tag
20 | - Preserved new lines
21 | - Updated packages
22 | - Converted extension to ES6
23 |
24 | ## [0.6.0] - 2019-02-26
25 |
26 | ### Changed
27 |
28 | - Updated snippets for Craft CMS 3
29 |
30 | ## [0.5.1] - 2019-01-31
31 |
32 | ## Added
33 |
34 | - Add changelog (finally)
35 |
36 | ### Changed
37 |
38 | - Update Prettydiff package to 100.1.7
39 |
40 | ## [0.4.4] - 2019-01-05
41 |
42 | ### Changed
43 |
44 | - Move to Pretty Diff 3
45 | - Clean up package.json
46 | - Refactor format selection based on Unibeautify and Prettier
47 | - Fix extension settings to match Pretty Diff 3's option changes
48 | - Fix tab size issue
49 | - Update readme to be more clear about limitations of the extension
50 |
51 | ## [0.3.2] - 2018-04-02
52 |
53 | ### Changed
54 |
55 | - Fix issues with snippes
56 | - Refine readme documentation
57 |
58 | ## [0.2.11] - 2018-01-20
59 |
60 | ### Added
61 |
62 | - Option to disable formatter
63 |
64 | ### Changed
65 |
66 | - Experiment with activation events
67 | - Fix configuration options
68 |
69 | ## [0.1.5] - 2017-11-13
70 |
71 | ### Added
72 |
73 | - Add options for configuration in VS Code
74 | - Format selection functionality
75 | - Add Prettty Diff 2 as an NPM package
76 | - Create hover features for VS Code
77 |
78 | ### Changed
79 |
80 | - Update readme
81 | - Fix gitignore
82 | - Change extension.js to add configuration to Pretty Diff
83 |
84 | ### Removed
85 |
86 | - Remove Pretty Diff 2 as a library
87 |
88 | ## [0.1.1] - 2017-11-12
89 |
90 | ### Added
91 |
92 | - Create a comprehensive readme file
93 | - Initial changelog
94 | - Add Craft/Twig snippets
95 | - Syntax highlighting for twig files
96 | - Initialise VS Code extension using Yeoman
97 | - Add icon for Twig Language extension
98 | - Use Pretty Diff 2 as the primary Twig formatter
99 | - Create extension.js file
100 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2019 mblode
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ### VSCode Twig Language
6 |
7 | - Syntax highlighting
8 | - Snippets
9 | - Emmet
10 | - Pretty Diff Formatting
11 | - Hover
12 | - HTML intellisense
13 |
14 | ## Installation
15 |
16 | Install through Visual Studio Code extensions. Search for `Twig Language`
17 |
18 | [Visual Studio Code Market Place: Twig Language](https://marketplace.visualstudio.com/items?itemName=mblode.twig-language)
19 |
20 | ## Documentation
21 |
22 | Twig Language is a Visual Studio Code extension that provides snippets, syntax highlighting, hover, and formatting for the Twig file format.
23 |
24 | ### Twig syntax highlighting and language support
25 |
26 | This extension provides language support for the Twig syntax.
27 |
28 | ### Code formatter/beautifier for Twig files
29 |
30 | Using PrettyDiff, this extension implements the only working code formatter for Twig files in VSCode.
31 |
32 | ### Information about Twig code on hover
33 |
34 | VSCode Twig language shows information about the symbol/object that's below the mouse cursor when you hover within Twig files.
35 |
36 | ### Craft CMS/Twig code snippets
37 |
38 | Adds a set of Craft CMS/Twig code snippets to use in your Twig templates.
39 |
40 | ### Generic Triggers
41 |
42 | ```twig
43 |
44 | do {% do ... %}
45 | extends {% extends 'template' %}
46 | from {% from 'template' import 'macro' %}
47 | import {% import 'template' as name %}
48 | importself {% import _self as name %}
49 | inc, include {% include 'template' %}
50 | incp {% include 'template' with params %}
51 | inckv {% include 'template' with { key: value } %}
52 | use {% use 'template' %}
53 |
54 | autoescape {% autoescape 'type' %}...{% endautoescape %}
55 | block, blockb {% block name %} ... {% endblock %}
56 | blockf {{ block('...') }}
57 | embed {% embed "template" %}...{% endembed %}
58 | filter, filterb {% filter name %} ... {% endfilter %}
59 | macro {% macro name(params) %}...{% endmacro %}
60 | set, setb {% set var = value %}
61 | spaceless {% spaceless %}...{% endspaceless %}
62 | verbatim {% verbatim %}...{% endverbatim %}
63 |
64 | if, ifb {% if condition %} ... {% endif %}
65 | ife {% if condition %} ... {% else %} ... {% endif %}
66 | for {% for item in seq %} ... {% endfor %}
67 | fore {% for item in seq %} ... {% else %} ... {% endfor %}
68 |
69 | else {% else %}
70 | endif {% endif %}
71 | endfor {% endfor %}
72 | endset {% endset %}
73 | endblock {% endblock %}
74 | endfilter {% endfilter %}
75 | endautoescape {% endautoescape %}
76 | endembed {% endembed %}
77 | endfilter {% endfilter %}
78 | endmacro {% endmacro %}
79 | endspaceless {% endspaceless %}
80 | endverbatim {% endverbatim %}
81 |
82 | ```
83 |
84 | ### Craft Triggers
85 |
86 | ```twig
87 | asset craft.assets.one()
88 | assets, assetso craft.assets loop
89 | categories, categorieso craft.categories loop
90 | entries, entrieso craft.entries loop
91 | feed craft.app.feeds.getFeedItems loop
92 | t | t
93 | replace | replace('search', 'replace')
94 | replacex | replace('/(search)/i', 'replace')
95 | split | split('\n')
96 | tags, tagso craft.tags loop
97 | users, userso craft.users loop
98 |
99 | cache {% cache %}...{% endcache %}
100 | children {% children %}
101 | exit {% exit 404 %}
102 | ifchildren {% ifchildren %}...{% endifchildren %}
103 | css {% css %}...{% endcss %}
104 | registercssfile {% do view.registerCssFile("/resources/css/global.css") %}
105 | js {% js %}...{% endjs %}
106 | registerjsfile {% do view.registerJsFile("/resources/js/global.js") %}
107 | matrix, matrixif Basic Matrix field loop using if statements
108 | matrixifelse Basic Matrix field loop using if/elseif
109 | matrixswitch Basic Matrix field loop using switch
110 | nav {% nav item in items %}...{% endnav %}
111 | paginate Outputs example of pagination and prev/next links
112 | redirect {% redirect 'login' %}
113 | requirelogin {% requireLogin %}
114 | requirepermission {% requirePermission "spendTheNight" %}
115 | switch {% switch variable %}...{% endswitch %}
116 |
117 | csrf {{ csrfInput() }}
118 | endbody {{ endBody() }}
119 | head {{ head() }}
120 |
121 | getparam craft.app.request.getParam()
122 | getbodyparam craft.app.request.getBodyParam()
123 | getqueryparam craft.app.request.getQueryParam()
124 | getsegment craft.app.request.getSegment()
125 |
126 | case {% case "value" %}
127 | endcache {% endcache %}
128 | endifchildren {% endifchildren %}
129 | endcss {% endcss %}
130 | endjs {% endjs %}
131 | endnav {% endnav %}
132 |
133 | ceil ceil()
134 | floor floor()
135 | max max()
136 | min min()
137 | shuffle shuffle()
138 | random random()
139 | round num | round()
140 | url, urla url('path'), url('path', params, 'http', false)
141 |
142 | rss Example rss feed
143 |
144 | dd
{{ dump() }}
{% exit %}
145 | dump
{{ dump() }}
146 | ```
147 |
148 | ### Example Forms
149 |
150 | ```twig
151 |
152 | formlogin Example login form
153 | formuserprofile Example user profile form
154 | formuserregistration Example user registration form
155 | formforgotpassword Example forgot password form
156 | formsetpassword Example set password form
157 | formsearch Example search form
158 | formsearchresults Example search form results
159 |
160 | ```
161 |
162 | ### Reference Hints
163 |
164 | ```twig
165 |
166 | info All craft.assets properties and template tags
167 | info All craft.crategories properties and template tags
168 | info All craft.config properties and template tags
169 | info All craft.entries properties and template tags
170 | info All craft.feeds properties and template tags
171 | info All craft.fields properties and template tags
172 | info All craft.globals properties and template tags
173 | info All craft.request properties and template tags
174 | info All craft.sections properties and template tags
175 | info All craft.session properties and template tags
176 | info All craft.tags properties and template tags
177 | info All craft.users properties and template tags
178 | info All craft globals (site info, date, users, template tags)
179 |
180 | ```
181 |
--------------------------------------------------------------------------------
/extension/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var vscode = require('vscode');
4 | var prettydiff = require('prettydiff');
5 |
6 | function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
7 |
8 | var vscode__default = /*#__PURE__*/_interopDefaultLegacy(vscode);
9 | var prettydiff__default = /*#__PURE__*/_interopDefaultLegacy(prettydiff);
10 |
11 | const abs={text:"abs",body:"abs",description:"filter returns the absolute value"};const batch={prefix:"batch",body:"batch(${size}, ${fill})",text:"batch(size, fill)",description:"filter \"batches\" items by returning a list of lists with the given number of items. A second parameter can be provided and used to fill in missing items"};const capitalize={text:"capitalize",body:"capitalize",description:"filter capitalizes a value. The first character will be uppercase, all others lowercase"};const convert_encoding={prefix:"convert_encoding",body:"convert_encoding('${to}', '${from}')",text:"convert_encoding('to', 'from')",description:"filter converts a string from one encoding to another. The first argument is the expected output charset and the second one is the input charset"};const date={prefix:"date",body:"date(\"${m/d/Y}\")",text:"date(\"m/d/Y\")",description:"filter formats a date to a given format"};const date_modify={prefix:"date_modify",body:"date_modify(\"${+1 day}\")",text:"date_modify(\"+1 day\")",description:"filter modifies a date with a given modifier string"};const first={text:"first",body:"first",description:"filter returns the first \"element\" of a sequence, a mapping, or a string"};const format={prefix:"format",body:"format($1)",text:"format()",description:"filter formats a given string by replacing the placeholders (placeholders follows the sprintf notation)",example:"{% set foo = \"foo\" %}\n{{ \"I like %s and %s.\"| format(foo, \"bar\") }}\n\n{# outputs I like foo and bar #}"};const join={prefix:"join",body:"join${('optional')}",text:"join",description:"filter returns a string which is the concatenation of the items of a sequence"};const json_encode={prefix:"json_encode",body:"json_encode()",text:"json_encode()",description:"filter returns the JSON representation of a value. Internally, Twig uses the PHP json_encode function."};const keys={text:"keys",body:"keys",description:"filter returns the keys of an array. It is useful when you want to iterate over the keys of an array"};const last={text:"last",body:"last",description:"filter returns the last \"element\" of a sequence, a mapping, or a string"};const length={text:"length",body:"length",description:"filter returns the number of items of a sequence or mapping, or the length of a string"};const lower={text:"lower",body:"lower",description:"filter converts a value to lowercase"};const merge={prefix:"merge",body:"merge(${array})",text:"merge(array)",description:"filter merges an array with another array"};const nl2br={text:"nl2br",body:"nl2br",description:"filter inserts HTML line breaks before all newlines in a string"};const number_format={prefix:"number_format",body:"number_format(${0}, '${.}', '${,}')",text:"number_format",description:"filter formats numbers. It is a wrapper around PHP's number_format function"};const raw={text:"raw",body:"raw",description:"filter marks the value as being \"safe\", which means that in an environment with automatic escaping enabled this variable will not be escaped if raw is the last filter applied to it."};const replace={prefix:"replace",body:"replace('${search}' : '${replace}')",text:"replace('search' : 'replace')",description:"filter formats a given string by replacing the placeholders."};const reverse={text:"reverse",body:"reverse",description:"filter reverses a sequence, a mapping, or a string"};const round={prefix:"round",body:"${0} | round(1, '${floor}')",text:"round",description:"filter rounds a number to a given precision"};const slice={prefix:"slice",body:"slice(${start}, ${length})",text:"slice(start, length)",description:"filter extracts a slice of a sequence, a mapping, or a string"};const sort={text:"sort",body:"sort",description:"filter sorts an array"};const split={prefix:"split",body:"split('$1')",text:"split('')",description:"filter splits a string by the given delimiter and returns a list of strings"};const striptags={text:"striptags",body:"striptags",description:"filter strips SGML/XML tags and replace adjacent whitespace by one space"};const title={text:"title",body:"title",description:"filter returns a titlecased version of the value. Words will start with uppercase letters, all remaining characters are lowercase"};const trim={text:"trim",body:"trim",description:"filter strips whitespace (or other characters) from the beginning and end of a string"};const upper={text:"upper",body:"upper",description:"filter converts a value to uppercase"};const url_encode={text:"url_encode",body:"url_encode",description:"filter percent encodes a given string as URL segment or an array as query string"};var snippetsArr = {abs:abs,batch:batch,capitalize:capitalize,convert_encoding:convert_encoding,date:date,date_modify:date_modify,"default": {prefix:"default",body:"default('${default value}')",text:"default('default value')",description:"filter returns the passed default value if the value is undefined or empty, otherwise the value of the variable"},"escape": {text:"escape",body:"escape",description:"filter escapes a string for safe insertion into the final output. It supports different escaping strategies depending on the template context"},first:first,format:format,join:join,json_encode:json_encode,keys:keys,last:last,length:length,lower:lower,merge:merge,nl2br:nl2br,number_format:number_format,raw:raw,replace:replace,reverse:reverse,round:round,slice:slice,"slice [] notation": {prefix:"slice [] notation",body:"[${start}:${length}]",description:"filter extracts a slice of a sequence, a mapping, or a string"},sort:sort,split:split,striptags:striptags,title:title,trim:trim,"trim()": {prefix:"trim()",body:"trim('$1')",description:"filter strips whitespace (or other characters) from the beginning and end of a string"},upper:upper,url_encode:url_encode};
12 |
13 | const attribute={prefix:"attribute",body:"{{ attribute($1) }}$2",description:"The attribute function can be used to access a \"dynamic\" attribute of a variable",example:""};const block={prefix:"block",body:"{{ block('${block name}') }}$1",description:"When a template uses inheritance and if you want to print a block multiple times, use the block function",example:""};const constant={prefix:"constant",body:"{{ constant('${const name}') }}$1",description:"constant returns the constant value for a given string",example:"{{ some_date | date(constant('DATE_W3C')) }}\n{{ constant('Namespace\\Classname::CONSTANT_NAME') }}"};const cycle={prefix:"cycle",body:"{{ cycle(${array}, ${position}) }}$1",description:"The cycle function cycles on an array of values",example:""};const date$1={prefix:"date",body:"{% set ${currentDate} = date($1) %}$2",description:"Converts an argument to a date to allow date comparison",example:"{% date() %}\n{% date('-2days') %}\n{% date('-2days', 'Europe/Paris') %}"};const dump={prefix:"dump",body:"{{ dump(${array}) }}$1",description:"(function) dumps information about a template variable. This is mostly useful to debug a template that does not behave as expected by introspecting its variables",example:""};const include={prefix:"include function",body:"{{ include('${filename}.twig') }}$1",description:"(function) returns the rendered content of a template",example:""};const max={prefix:"max",body:"{% set ${result} = max(${array}) %}$1",description:"(function) returns the biggest value of a sequence or a set of values",example:"{{ max(1, 3, 2) }}\n{# returns \"3\" #}\n\n{{ max({2: \"e\", 3: \"a\", 1: \"b\", 5: \"d\", 4: \"c\"}) }}\n{# returns \"e\" #}"};const min={prefix:"min",body:"{% set ${result} = min(${array}) %}$1",description:"(function) returns the lowest value of a sequence or a set of values",example:"{{ min(1, 3, 2) }}\n{# returns \"1\" #}\n\n{{ min({2: \"e\", 3: \"a\", 1: \"b\", 5: \"d\", 4: \"c\"}) }}\n{# returns \"a\" #}"};const parent={prefix:"parent",body:"{{ parent() }}",description:"(function) return the content of the block as defined in the base template",example:"{% extends \"base.html\" %}\n\n{% block sidebar %}\n\t
Table Of Contents
\n\t...\n\t{{ parent() }}\n{% endblock %}"};const random={prefix:"random",hover:"",body:"{% set ${result} = random($1) %}$2",description:"(function) returns a random value depending on the supplied parameter type",example:"{{ random(['apple', 'orange', 'citrus']) }}\n{# example output: orange #}\n\n{{ random('ABC') }}\n{# example output: C #}\n\n{{ random() }}\n{# example output: 15386094 (works as the native PHP mt_rand function) #}\n\n{{ random(5) }}\n{# example output: 3 #}"};const range={prefix:"range",body:"range(${low}, ${high}, ${step})",description:"(function) Returns an array of elements from low to high, inclusive",example:"{% set result = range(0, 6, 2) %}\n{% dump(result) %}\n{# output: array(0, 2, 4, 6) #}"};const source={prefix:"source",body:"{{ source('${template}.twig') }}$1",description:"(function) returns the content of a template without rendering it",example:""};const template_from_string={prefix:"template_from_string",body:"{{ include(template_from_string(\"$1\")) }}$2",description:"(function) loads a template from a string",example:"{{ include(template_from_string(\"Hello {{ name }}\")) }}"};var functionsArr = {attribute:attribute,block:block,constant:constant,cycle:cycle,date:date$1,dump:dump,include:include,max:max,min:min,parent:parent,random:random,"range set": {prefix:"range set",body:"{% set ${result} = range(${low}, ${high}, ${step}) %}$1",description:"(function) Returns an array of elements from low to high, inclusive",example:"{% set result = range(0, 6, 2) %}\n{% dump(result) %}\n{# output: array(0, 2, 4, 6) #}"},range:range,source:source,template_from_string:template_from_string};
14 |
15 | const show={prefix:"show",body:"{{ $1 }}",description:"{{ }}"};const execute={prefix:"execute",body:"{% $1 %}",description:"{% %}"};const autoescape={prefix:"autoescape",body:["{% autoescape %}","\t$1","{% endautoescape %}"],description:"Whether automatic escaping is enabled or not, you can mark a section of a template to be escaped or not by using the autoescape tag",example:"{% autoescape %}\n Everything will be automatically escaped in this block\n using the HTML strategy\n{% endautoescape %}\n\n{% autoescape 'html' %}\n Everything will be automatically escaped in this block\n using the HTML strategy\n{% endautoescape %}\n\n{% autoescape 'js' %}\n Everything will be automatically escaped in this block\n using the js escaping strategy\n{% endautoescape %}\n\n{% autoescape false %}\n Everything will be outputted as is in this block\n{% endautoescape %}"};const block$1={prefix:"block",body:["{% block ${name} %}","\t$1","{% endblock ${name} %}"],description:"When a template uses inheritance and if you want to print a block multiple times, use the block function"};const embed={prefix:"embed",body:["{% embed \"${filename}.twig\" %}","\t$1","{% endembed %}"],description:"The embed tag combines the behaviour of include and extends. It allows you to include another template's contents, just like include does. But it also allows you to override any block defined inside the included template, like when extending a template"};const filter={prefix:"filter",body:["{% filter ${filter name} %}","\t$1","{% endfilter %}"],description:"Filter sections allow you to apply regular Twig filters on a block of template data. Just wrap the code in the special filter section",example:"{% filter lower | escape %}\n SOME TEXT\n{% endfilter %}\n\n{# outputs \"<strong>some text</strong>\" #}"};const flush={prefix:"flush",body:["{% flush %}"],description:"The flush tag tells Twig to flush the output buffer",example:"{% flush %}"};const loop={prefix:"loop",body:"loop.",description:"special variables inside of a for loop block"};const _self={prefix:"_self",body:"_self",description:"To import macros from the current file, use the special _self variable for the source"};const include$1={prefix:"include",body:"{% include \"${filename}.twig\" %}",description:"The include statement includes a template and returns the rendered content of that file into the current namespace"};const macro={prefix:"macro",body:["{% macro ${name}($1) %}","\t$2","{% endmacro %}"],description:"Twig snippets"};const sandbox={prefix:"sandbox",body:["{% sandbox %}","\t$1","{% endsandbox %}"],description:"The sandbox tag can be used to enable the sandboxing mode for an included template, when sandboxing is not enabled globally for the Twig environment"};const set={prefix:"set",body:["{% set ${name} = ${value} %}$1"],description:"Assign values to variables"};const spaceless={prefix:"spaceless",body:["{% spaceless %}","\t$1","{% endspaceless %}"],description:"Use the spaceless tag to remove whitespace between HTML tags, not whitespace within HTML tags or whitespace in plain text"};const use={prefix:"use",body:"{% use \"${filename}.twig\" %}",description:"Twig snippets"};const verbatim={prefix:"verbatim",body:["{% verbatim %}","\t$1","{% endverbatim %}"],description:"The verbatim tag marks sections as being raw text that should not be parsed. For example to put Twig syntax as example into a template you can use this snippet"};var twigArr = {show:show,execute:execute,autoescape:autoescape,block:block$1,"do": {prefix:"do",body:["{% do $1 %}"],description:"The do tag works exactly like the regular variable expression ({{ ... }}) just that it doesn't print anything",example:"{% do 1 + 2 %}"},embed:embed,"extends": {prefix:"extends",body:"{% extends \"${filename}.twig\" %}",description:"Twig snippets"},filter:filter,flush:flush,"for": {prefix:"for",body:["{% for ${row} in ${array} %}","\t$1","{% endfor %}"],description:"Loop over each item in a sequence"},"for if": {prefix:"for if",body:["{% for ${row} in ${array} if ${condition} %}","\t$1","{% endfor %}"],description:"Loop over each item in a sequence"},"for else": {prefix:"for else",body:["{% for ${row} in ${array} %}","\t$1","{% else %}","\t$2","{% endfor %}"],description:"Loop over each item in a sequence"},"for if else": {prefix:"for if else",body:["{% for ${row} in ${array} if ${condition} %}","\t$1","{% else %}","\t$2","{% endfor %}"],description:"Loop over each item in a sequence"},loop:loop,"if": {prefix:"if",body:["{% if ${condition} %}","\t$1","{% endif %}"],description:"The if statement in Twig is comparable with the if statements of PHP"},"if else": {prefix:"if else",body:["{% if ${condition} %}","\t$1","{% else %}","\t$2","{% endif %}"],description:"The if statement in Twig is comparable with the if statements of PHP"},"else": {prefix:"else",body:"{% else %}",description:"The if statement in Twig is comparable with the if statements of PHP"},"else if": {prefix:"else if",body:"{% elseif ${condition} %}",description:"The if statement in Twig is comparable with the if statements of PHP"},"import": {prefix:"import",body:"{% import \"${filename}.twig\" as ${alias}%}",description:"Twig supports putting often used code into macros. These macros can go into different templates and get imported from there."},_self:_self,include:include$1,macro:macro,sandbox:sandbox,set:set,"set block": {prefix:"set (block)",body:["{% set ${name} %}","\t$1","{% endset %}"],description:"Inside code blocks you can also assign values to variables. Assignments use the set tag and can have multiple targets"},spaceless:spaceless,use:use,verbatim:verbatim};
16 |
17 | const editor = vscode__default['default'].workspace.getConfiguration('editor');
18 | const config = vscode__default['default'].workspace.getConfiguration('twig-language');
19 |
20 | function createHover(snippet, type) {
21 | const example =
22 | typeof snippet.example == 'undefined' ? '' : snippet.example;
23 | const description =
24 | typeof snippet.description == 'undefined' ? '' : snippet.description;
25 | return new vscode__default['default'].Hover({
26 | language: type,
27 | value: description + '\n\n' + example
28 | })
29 | }
30 |
31 | function prettyDiff(document, range) {
32 | const result = [];
33 | let output = "";
34 | let options = prettydiff__default['default'].options;
35 |
36 | let tabSize = editor.tabSize;
37 | let indentChar = " ";
38 |
39 | if (config.tabSize > 0) {
40 | tabSize = config.tabSize;
41 | }
42 |
43 | if (config.indentStyle == "tab") {
44 | tabSize = 0;
45 | indentChar = "\t";
46 | }
47 |
48 | options.source = document.getText(range);
49 | options.mode = 'beautify';
50 | options.language = 'html';
51 | options.lexer = 'markup';
52 | options.brace_line = config.braceLine;
53 | options.brace_padding = config.bracePadding;
54 | options.brace_style = config.braceStyle;
55 | options.braces = config.braces;
56 | options.comment_line = config.commentLine;
57 | options.comments = config.comments;
58 | options.compressed_css = config.compressedCss;
59 | options.correct = config.correct;
60 | options.cssInsertLines = config.cssInsertLines;
61 | options.else_line = config.elseLine;
62 | options.end_comma = config.endComma;
63 | options.force_attribute = config.forceAttribute;
64 | options.force_indent = config.forceIndent;
65 | options.format_array = config.formatArray;
66 | options.format_object = config.formatObject;
67 | options.function_name = config.functionName;
68 | options.indent_level = config.indentLevel;
69 | options.indent_char = indentChar;
70 | options.indent_size = tabSize;
71 | options.method_chain = config.methodChain;
72 | options.never_flatten = config.neverFlatten;
73 | options.new_line = config.newLine;
74 | options.no_case_indent = config.noCaseIndent;
75 | options.no_lead_zero = config.noLeadZero;
76 | options.object_sort = config.objectSort;
77 | options.preserve = config.preserve;
78 | options.preserve_comment = config.preserveComment;
79 | options.quote_convert = config.quoteConvert;
80 | options.space = config.space;
81 | options.space_close = config.spaceSlose;
82 | options.tag_merge = config.tagMerge;
83 | options.tag_sort = config.tagSort;
84 | options.ternary_line = config.ternaryLine;
85 | options.unformatted = config.unformatted;
86 | options.variable_list = config.variableList;
87 | options.vertical = config.vertical;
88 | options.wrap = config.wrap;
89 |
90 | output = prettydiff__default['default']();
91 |
92 | options.end = 0;
93 | options.start = 0;
94 |
95 | result.push(vscode__default['default'].TextEdit.replace(range, output));
96 | return result;
97 | }
98 | function activate(context) {
99 | const active = vscode__default['default'].window.activeTextEditor;
100 | if (!active || !active.document) return
101 |
102 | registerDocType('html');
103 |
104 | function registerDocType(type) {
105 | if (config.hover === true) {
106 | context.subscriptions.push(
107 | vscode__default['default'].languages.registerHoverProvider(type, {
108 | provideHover(document, position) {
109 | const range = document.getWordRangeAtPosition(position);
110 | const word = document.getText(range);
111 |
112 | for (const snippet in snippetsArr) {
113 | if (
114 | snippetsArr[snippet].prefix == word ||
115 | snippetsArr[snippet].hover == word
116 | ) {
117 | return createHover(snippetsArr[snippet], type)
118 | }
119 | }
120 |
121 | for (const snippet in functionsArr) {
122 | if (
123 | functionsArr[snippet].prefix == word ||
124 | functionsArr[snippet].hover == word
125 | ) {
126 | return createHover(functionsArr[snippet], type)
127 | }
128 | }
129 |
130 | for (const snippet in twigArr) {
131 | if (
132 | twigArr[snippet].prefix == word ||
133 | twigArr[snippet].hover == word
134 | ) {
135 | return createHover(twigArr[snippet], type)
136 | }
137 | }
138 | }
139 | })
140 | );
141 | }
142 |
143 | if (config.formatting === true) {
144 | context.subscriptions.push(
145 | vscode__default['default'].languages.registerDocumentFormattingEditProvider(type, {
146 | provideDocumentFormattingEdits: function (
147 | document
148 | ) {
149 | const start = new vscode__default['default'].Position(0, 0);
150 | const end = new vscode__default['default'].Position(
151 | document.lineCount - 1,
152 | document.lineAt(document.lineCount - 1).text.length
153 | );
154 | const rng = new vscode__default['default'].Range(start, end);
155 | return prettyDiff(document, rng);
156 | }
157 | })
158 | );
159 |
160 | context.subscriptions.push(
161 | vscode__default['default'].languages.registerDocumentRangeFormattingEditProvider(
162 | type,
163 | {
164 | provideDocumentRangeFormattingEdits: function (
165 | document,
166 | range
167 | ) {
168 | let end = range.end;
169 |
170 | if (end.character === 0) {
171 | end = end.translate(-1, Number.MAX_VALUE);
172 | } else {
173 | end = end.translate(0, Number.MAX_VALUE);
174 | }
175 |
176 | const rng = new vscode__default['default'].Range(new vscode__default['default'].Position(range.start.line, 0), end);
177 | return prettyDiff(document, rng);
178 | }
179 | }
180 | )
181 | );
182 | }
183 | }
184 | }
185 |
186 | exports.activate = activate;
187 |
--------------------------------------------------------------------------------
/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mblode/vscode-twig-language/a5ad3064e3512911e2e74c518228a809e3546474/images/icon.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "twig-language",
3 | "displayName": "Twig Language",
4 | "description": "Snippets, Syntax Highlighting, Hover, and Formatting for Twig",
5 | "version": "0.9.4",
6 | "publisher": "mblode",
7 | "license": "MIT",
8 | "author": {
9 | "name": "Matthew Blode",
10 | "email": "m@blode.co",
11 | "url": "https://matthewblode.com"
12 | },
13 | "homepage": "https://github.com/mblode/vscode-twig-language",
14 | "bugs": {
15 | "url": "https://github.com/mblode/vscode-twig-language/issues"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/mblode/vscode-twig-language.git"
20 | },
21 | "icon": "images/icon.png",
22 | "engines": {
23 | "vscode": "^1.30.0"
24 | },
25 | "categories": [
26 | "Programming Languages",
27 | "Snippets",
28 | "Other"
29 | ],
30 | "keywords": [
31 | "php",
32 | "twig",
33 | "snippets",
34 | "craft",
35 | "beautify"
36 | ],
37 | "activationEvents": [
38 | "*"
39 | ],
40 | "main": "./extension/index",
41 | "contributes": {
42 | "languages": [
43 | {
44 | "id": "html",
45 | "aliases": [
46 | "HTML",
47 | "twig"
48 | ],
49 | "extensions": [
50 | ".twig",
51 | ".html",
52 | ".html.twig"
53 | ],
54 | "configuration": "./src/languages/twig.configuration.json"
55 | }
56 | ],
57 | "grammars": [
58 | {
59 | "language": "html",
60 | "scopeName": "text.html.twig",
61 | "path": "./src/syntaxes/twig.tmLanguage",
62 | "embeddedLanguages": {
63 | "source.json": "json",
64 | "source.css": "css",
65 | "source.css.scss": "scss",
66 | "source.js": "javascript",
67 | "source.ts": "typescript"
68 | }
69 | }
70 | ],
71 | "snippets": [
72 | {
73 | "language": "html",
74 | "path": "./src/snippets/snippets.json"
75 | }
76 | ],
77 | "configuration": {
78 | "type": "object",
79 | "title": "Twig Language",
80 | "properties": {
81 | "twig-language.hover": {
82 | "type": "boolean",
83 | "default": true,
84 | "description": "Whether to enable/disable Twig hover."
85 | },
86 | "twig-language.formatting": {
87 | "type": "boolean",
88 | "default": true,
89 | "description": "Whether to enable/disable Twig PrettyDiff formatting."
90 | },
91 | "twig-language.braceLine": {
92 | "type": "boolean",
93 | "default": false,
94 | "description": "If true an empty line will be inserted after opening curly braces and before closing curly braces."
95 | },
96 | "twig-language.bracePadding": {
97 | "type": "boolean",
98 | "default": false,
99 | "description": "Inserts a space after the start of a container and before the end of the container if the contents of that container are not indented; such as: conditions, function arguments, and escaped sequences of template strings."
100 | },
101 | "twig-language.braceStyle": {
102 | "type": "string",
103 | "enum": [
104 | "collapse",
105 | "collapse-preserve-inline",
106 | "expand",
107 | "none"
108 | ],
109 | "default": "none",
110 | "description": "Emulates JSBeautify's brace_style option using existing Pretty Diff options."
111 | },
112 | "twig-language.braces": {
113 | "type": "boolean",
114 | "default": false,
115 | "description": "Determines if opening curly braces will exist on the same line as their condition or be forced onto a new line."
116 | },
117 | "twig-language.commentLine": {
118 | "type": "boolean",
119 | "default": false,
120 | "description": "If a blank new line should be forced above comments."
121 | },
122 | "twig-language.comments": {
123 | "type": "boolean",
124 | "default": false,
125 | "description": "This will determine whether comments should always start at position 0 of each line or if comments should be indented according to the code."
126 | },
127 | "twig-language.compressedCss": {
128 | "type": "boolean",
129 | "default": false,
130 | "description": "If CSS should be beautified in a style where the properties and values are minifed for faster reading of selectors."
131 | },
132 | "twig-language.correct": {
133 | "type": "boolean",
134 | "default": false,
135 | "description": "Automatically correct some sloppiness in code."
136 | },
137 | "twig-language.cssInsertLines": {
138 | "type": "boolean",
139 | "default": false,
140 | "description": "Inserts new line characters between every CSS code block."
141 | },
142 | "twig-language.elseLine": {
143 | "type": "boolean",
144 | "default": false,
145 | "description": "If else_line is true then the keyword 'else' is forced onto a new line."
146 | },
147 | "twig-language.endComma": {
148 | "type": "string",
149 | "enum": [
150 | "always",
151 | "never",
152 | "none"
153 | ],
154 | "default": false,
155 | "description": "If there should be a trailing comma in arrays and objects. Value multiline only applies to modes beautify and diff."
156 | },
157 | "twig-language.forceAttribute": {
158 | "type": "boolean",
159 | "default": false,
160 | "description": "If all markup attributes should be indented each onto their own line."
161 | },
162 | "twig-language.forceIndent": {
163 | "type": "boolean",
164 | "default": false,
165 | "description": "Will force indentation upon all content and tags without regard for the creation of new text nodes."
166 | },
167 | "twig-language.formatArray": {
168 | "type": "string",
169 | "enum": [
170 | "default",
171 | "indent",
172 | "inline"
173 | ],
174 | "default": "default",
175 | "description": "Determines if all array indexes should be indented, never indented, or left to the default."
176 | },
177 | "twig-language.formatObject": {
178 | "type": "string",
179 | "enum": [
180 | "default",
181 | "indent",
182 | "inline"
183 | ],
184 | "default": "default",
185 | "description": "Determines if all object keys should be indented, never indented, or left to the default."
186 | },
187 | "twig-language.functionName": {
188 | "type": "boolean",
189 | "default": false,
190 | "description": "If a space should follow a JavaScript function name."
191 | },
192 | "twig-language.indentStyle": {
193 | "type": "string",
194 | "enum": [
195 | "space",
196 | "tab"
197 | ],
198 | "default": "tab",
199 | "description": "Choose to indent using tabs or spaces."
200 | },
201 | "twig-language.indentLevel": {
202 | "type": "integer",
203 | "default": 0,
204 | "description": "How much indentation padding should be applied to beautification? This option is internally used for code that requires switching between libraries."
205 | },
206 | "twig-language.tabSize": {
207 | "type": "integer",
208 | "default": 0,
209 | "description": "0 will default to the editor's tab size. Stores the number of 'inchar' values to comprise a single indentation."
210 | },
211 | "twig-language.methodChain": {
212 | "type": "integer",
213 | "default": 0,
214 | "description": "When to break consecutively chained methods and properties onto separate lines. A negative value disables this option. A value of 0 ensures method chains are never broken."
215 | },
216 | "twig-language.neverFlatten": {
217 | "type": "boolean",
218 | "default": false,
219 | "description": "If destructured lists in script should never be flattend."
220 | },
221 | "twig-language.newLine": {
222 | "type": "boolean",
223 | "default": true,
224 | "description": "Insert an empty line at the end of output."
225 | },
226 | "twig-language.noCaseIndent": {
227 | "type": "boolean",
228 | "default": false,
229 | "description": "If a case statement should receive the same indentation as the containing switch block."
230 | },
231 | "twig-language.noLeadZero": {
232 | "type": "boolean",
233 | "default": false,
234 | "description": "Whether leading 0s in CSS values immediately preceeding a decimal should be removed or prevented."
235 | },
236 | "twig-language.objectSort": {
237 | "type": "boolean",
238 | "default": false,
239 | "description": "Sorts markup attributes and properties by key name in script and style."
240 | },
241 | "twig-language.preserve": {
242 | "type": "integer",
243 | "default": 2,
244 | "description": "The maximum number of consecutive empty lines to retain."
245 | },
246 | "twig-language.preserveComment": {
247 | "type": "boolean",
248 | "default": false,
249 | "description": "Prevent comment reformatting due to option wrap."
250 | },
251 | "twig-language.quoteConvert": {
252 | "type": "string",
253 | "enum": [
254 | "double",
255 | "none",
256 | "single"
257 | ],
258 | "default": "none",
259 | "description": "If the quotes of script strings or markup attributes should be converted to single quotes or double quotes."
260 | },
261 | "twig-language.space": {
262 | "type": "boolean",
263 | "default": true,
264 | "description": "Inserts a space following the function keyword for anonymous functions."
265 | },
266 | "twig-language.spaceClose": {
267 | "type": "boolean",
268 | "default": false,
269 | "description": "Markup self-closing tags end will end with ' />' instead of '/>'."
270 | },
271 | "twig-language.tagMerge": {
272 | "type": "boolean",
273 | "default": false,
274 | "description": "Allows immediately adjacement start and end markup tags of the same name to be combined into a single self-closing tag."
275 | },
276 | "twig-language.tagSort": {
277 | "type": "boolean",
278 | "default": false,
279 | "description": "Sort child items of each respective markup parent element."
280 | },
281 | "twig-language.ternaryLine": {
282 | "type": "boolean",
283 | "default": true,
284 | "description": "If ternary operators in JavaScript ? and : should remain on the same line."
285 | },
286 | "twig-language.unformatted": {
287 | "type": "boolean",
288 | "default": false,
289 | "description": "If markup tags should have their insides preserved. This option is only available to markup and does not support child tokens that require a different lexer."
290 | },
291 | "twig-language.variableList": {
292 | "type": "string",
293 | "enum": [
294 | "each",
295 | "list",
296 | "none"
297 | ],
298 | "default": "none",
299 | "description": "If consecutive JavaScript variables should be merged into a comma separated list or if variables in a list should be separated."
300 | },
301 | "twig-language.vertical": {
302 | "type": "boolean",
303 | "default": false,
304 | "description": "If lists of assignments and properties should be vertically aligned. This option is not used with the markup lexer."
305 | },
306 | "twig-language.wrap": {
307 | "type": "integer",
308 | "default": 0,
309 | "description": "Character width limit before applying word wrap. A 0 value disables this option. A negative value concatenates script strings."
310 | }
311 | }
312 | }
313 | },
314 | "capabilities": {
315 | "hoverProvider": "true"
316 | },
317 | "scripts": {
318 | "start": "rollup -c",
319 | "build": "rollup -c",
320 | "watch": "rollup -c -w"
321 | },
322 | "devDependencies": {
323 | "@types/mocha": "^8.0.3",
324 | "@types/node": "^14.6.0",
325 | "eslint": "^7.7.0",
326 | "eslint-config-prettier": "^6.11.0",
327 | "typescript": "^4.0.2",
328 | "vscode": "^1.1.37"
329 | },
330 | "dependencies": {
331 | "prettydiff": "^101.2.6",
332 | "rollup": "^2.26.5",
333 | "rollup-plugin-babel": "^4.4.0",
334 | "rollup-plugin-json": "^4.0.0"
335 | }
336 | }
337 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import json from 'rollup-plugin-json';
2 |
3 | export default [
4 | {
5 | input: 'src/extension.js',
6 | output: {
7 | file: 'extension/index.js',
8 | format: 'cjs'
9 | },
10 | plugins: [
11 | json({
12 | // All JSON files will be parsed by default,
13 | // but you can also specifically include/exclude files
14 | exclude: ['node_modules/**'],
15 |
16 | // for tree-shaking, properties will be declared as
17 | // variables, using either `var` or `const`
18 | preferConst: true, // Default: false
19 |
20 | // specify indentation for the generated default export —
21 | // defaults to '\t'
22 | indent: ' ',
23 |
24 | // ignores indent and generates the smallest code
25 | compact: true, // Default: false
26 |
27 | // generate a named export for every property of the JSON object
28 | namedExports: true // Default: true
29 | })
30 | ]
31 | },
32 | ]
33 |
--------------------------------------------------------------------------------
/src/extension.js:
--------------------------------------------------------------------------------
1 | import vscode from 'vscode'
2 | import prettydiff from 'prettydiff'
3 |
4 | import snippetsArr from './hover/filters.json'
5 | import functionsArr from './hover/functions.json'
6 | import twigArr from './hover/twig.json'
7 |
8 | const editor = vscode.workspace.getConfiguration('editor');
9 | const config = vscode.workspace.getConfiguration('twig-language');
10 |
11 | function createHover(snippet, type) {
12 | const example =
13 | typeof snippet.example == 'undefined' ? '' : snippet.example
14 | const description =
15 | typeof snippet.description == 'undefined' ? '' : snippet.description
16 | return new vscode.Hover({
17 | language: type,
18 | value: description + '\n\n' + example
19 | })
20 | }
21 |
22 | function prettyDiff(document, range) {
23 | const result = [];
24 | let output = "";
25 | let options = prettydiff.options;
26 |
27 | let tabSize = editor.tabSize;
28 | let indentChar = " ";
29 |
30 | if (config.tabSize > 0) {
31 | tabSize = config.tabSize;
32 | }
33 |
34 | if (config.indentStyle == "tab") {
35 | tabSize = 0;
36 | indentChar = "\t";
37 | }
38 |
39 | options.source = document.getText(range);
40 | options.mode = 'beautify';
41 | options.language = 'html';
42 | options.lexer = 'markup';
43 | options.brace_line = config.braceLine;
44 | options.brace_padding = config.bracePadding;
45 | options.brace_style = config.braceStyle;
46 | options.braces = config.braces;
47 | options.comment_line = config.commentLine;
48 | options.comments = config.comments;
49 | options.compressed_css = config.compressedCss;
50 | options.correct = config.correct;
51 | options.cssInsertLines = config.cssInsertLines;
52 | options.else_line = config.elseLine;
53 | options.end_comma = config.endComma;
54 | options.force_attribute = config.forceAttribute;
55 | options.force_indent = config.forceIndent;
56 | options.format_array = config.formatArray;
57 | options.format_object = config.formatObject;
58 | options.function_name = config.functionName;
59 | options.indent_level = config.indentLevel;
60 | options.indent_char = indentChar;
61 | options.indent_size = tabSize;
62 | options.method_chain = config.methodChain;
63 | options.never_flatten = config.neverFlatten;
64 | options.new_line = config.newLine;
65 | options.no_case_indent = config.noCaseIndent;
66 | options.no_lead_zero = config.noLeadZero;
67 | options.object_sort = config.objectSort;
68 | options.preserve = config.preserve;
69 | options.preserve_comment = config.preserveComment;
70 | options.quote_convert = config.quoteConvert;
71 | options.space = config.space;
72 | options.space_close = config.spaceSlose;
73 | options.tag_merge = config.tagMerge;
74 | options.tag_sort = config.tagSort;
75 | options.ternary_line = config.ternaryLine;
76 | options.unformatted = config.unformatted;
77 | options.variable_list = config.variableList;
78 | options.vertical = config.vertical;
79 | options.wrap = config.wrap;
80 |
81 | output = prettydiff();
82 |
83 | options.end = 0;
84 | options.start = 0;
85 |
86 | result.push(vscode.TextEdit.replace(range, output));
87 | return result;
88 | };
89 |
90 | function activate(context) {
91 | const active = vscode.window.activeTextEditor
92 | if (!active || !active.document) return
93 |
94 | registerDocType('html')
95 |
96 | function registerDocType(type) {
97 | if (config.hover === true) {
98 | context.subscriptions.push(
99 | vscode.languages.registerHoverProvider(type, {
100 | provideHover(document, position) {
101 | const range = document.getWordRangeAtPosition(position)
102 | const word = document.getText(range)
103 |
104 | for (const snippet in snippetsArr) {
105 | if (
106 | snippetsArr[snippet].prefix == word ||
107 | snippetsArr[snippet].hover == word
108 | ) {
109 | return createHover(snippetsArr[snippet], type)
110 | }
111 | }
112 |
113 | for (const snippet in functionsArr) {
114 | if (
115 | functionsArr[snippet].prefix == word ||
116 | functionsArr[snippet].hover == word
117 | ) {
118 | return createHover(functionsArr[snippet], type)
119 | }
120 | }
121 |
122 | for (const snippet in twigArr) {
123 | if (
124 | twigArr[snippet].prefix == word ||
125 | twigArr[snippet].hover == word
126 | ) {
127 | return createHover(twigArr[snippet], type)
128 | }
129 | }
130 | }
131 | })
132 | )
133 | }
134 |
135 | if (config.formatting === true) {
136 | context.subscriptions.push(
137 | vscode.languages.registerDocumentFormattingEditProvider(type, {
138 | provideDocumentFormattingEdits: function (
139 | document
140 | ) {
141 | const start = new vscode.Position(0, 0)
142 | const end = new vscode.Position(
143 | document.lineCount - 1,
144 | document.lineAt(document.lineCount - 1).text.length
145 | )
146 | const rng = new vscode.Range(start, end)
147 | return prettyDiff(document, rng);
148 | }
149 | })
150 | )
151 |
152 | context.subscriptions.push(
153 | vscode.languages.registerDocumentRangeFormattingEditProvider(
154 | type,
155 | {
156 | provideDocumentRangeFormattingEdits: function (
157 | document,
158 | range
159 | ) {
160 | let end = range.end
161 |
162 | if (end.character === 0) {
163 | end = end.translate(-1, Number.MAX_VALUE);
164 | } else {
165 | end = end.translate(0, Number.MAX_VALUE);
166 | }
167 |
168 | const rng = new vscode.Range(new vscode.Position(range.start.line, 0), end)
169 | return prettyDiff(document, rng);
170 | }
171 | }
172 | )
173 | )
174 | }
175 | }
176 | }
177 |
178 | exports.activate = activate
179 |
--------------------------------------------------------------------------------
/src/hover/filters.json:
--------------------------------------------------------------------------------
1 | {
2 | "abs": {
3 | "text": "abs",
4 | "body": "abs",
5 | "description": "filter returns the absolute value"
6 | },
7 | "batch": {
8 | "prefix": "batch",
9 | "body": "batch(${size}, ${fill})",
10 | "text": "batch(size, fill)",
11 | "description":
12 | "filter \"batches\" items by returning a list of lists with the given number of items. A second parameter can be provided and used to fill in missing items"
13 | },
14 | "capitalize": {
15 | "text": "capitalize",
16 | "body": "capitalize",
17 | "description":
18 | "filter capitalizes a value. The first character will be uppercase, all others lowercase"
19 | },
20 | "convert_encoding": {
21 | "prefix": "convert_encoding",
22 | "body": "convert_encoding('${to}', '${from}')",
23 | "text": "convert_encoding('to', 'from')",
24 | "description":
25 | "filter converts a string from one encoding to another. The first argument is the expected output charset and the second one is the input charset"
26 | },
27 | "date": {
28 | "prefix": "date",
29 | "body": "date(\"${m/d/Y}\")",
30 | "text": "date(\"m/d/Y\")",
31 | "description": "filter formats a date to a given format"
32 | },
33 | "date_modify": {
34 | "prefix": "date_modify",
35 | "body": "date_modify(\"${+1 day}\")",
36 | "text": "date_modify(\"+1 day\")",
37 | "description": "filter modifies a date with a given modifier string"
38 | },
39 | "default": {
40 | "prefix": "default",
41 | "body": "default('${default value}')",
42 | "text": "default('default value')",
43 | "description":
44 | "filter returns the passed default value if the value is undefined or empty, otherwise the value of the variable"
45 | },
46 | "escape": {
47 | "text": "escape",
48 | "body": "escape",
49 | "description":
50 | "filter escapes a string for safe insertion into the final output. It supports different escaping strategies depending on the template context"
51 | },
52 | "first": {
53 | "text": "first",
54 | "body": "first",
55 | "description":
56 | "filter returns the first \"element\" of a sequence, a mapping, or a string"
57 | },
58 | "format": {
59 | "prefix": "format",
60 | "body": "format($1)",
61 | "text": "format()",
62 | "description":
63 | "filter formats a given string by replacing the placeholders (placeholders follows the sprintf notation)",
64 | "example":
65 | "{% set foo = \"foo\" %}\n{{ \"I like %s and %s.\"| format(foo, \"bar\") }}\n\n{# outputs I like foo and bar #}"
66 | },
67 | "join": {
68 | "prefix": "join",
69 | "body": "join${('optional')}",
70 | "text": "join",
71 | "description":
72 | "filter returns a string which is the concatenation of the items of a sequence"
73 | },
74 | "json_encode": {
75 | "prefix": "json_encode",
76 | "body": "json_encode()",
77 | "text": "json_encode()",
78 | "description":
79 | "filter returns the JSON representation of a value. Internally, Twig uses the PHP json_encode function."
80 | },
81 | "keys": {
82 | "text": "keys",
83 | "body": "keys",
84 | "description":
85 | "filter returns the keys of an array. It is useful when you want to iterate over the keys of an array"
86 | },
87 | "last": {
88 | "text": "last",
89 | "body": "last",
90 | "description":
91 | "filter returns the last \"element\" of a sequence, a mapping, or a string"
92 | },
93 | "length": {
94 | "text": "length",
95 | "body": "length",
96 | "description":
97 | "filter returns the number of items of a sequence or mapping, or the length of a string"
98 | },
99 | "lower": {
100 | "text": "lower",
101 | "body": "lower",
102 | "description": "filter converts a value to lowercase"
103 | },
104 | "merge": {
105 | "prefix": "merge",
106 | "body": "merge(${array})",
107 | "text": "merge(array)",
108 | "description": "filter merges an array with another array"
109 | },
110 | "nl2br": {
111 | "text": "nl2br",
112 | "body": "nl2br",
113 | "description":
114 | "filter inserts HTML line breaks before all newlines in a string"
115 | },
116 | "number_format": {
117 | "prefix": "number_format",
118 | "body": "number_format(${0}, '${.}', '${,}')",
119 | "text": "number_format",
120 | "description":
121 | "filter formats numbers. It is a wrapper around PHP's number_format function"
122 | },
123 | "raw": {
124 | "text": "raw",
125 | "body": "raw",
126 | "description":
127 | "filter marks the value as being \"safe\", which means that in an environment with automatic escaping enabled this variable will not be escaped if raw is the last filter applied to it."
128 | },
129 | "replace": {
130 | "prefix": "replace",
131 | "body": "replace('${search}' : '${replace}')",
132 | "text": "replace('search' : 'replace')",
133 | "description":
134 | "filter formats a given string by replacing the placeholders."
135 | },
136 | "reverse": {
137 | "text": "reverse",
138 | "body": "reverse",
139 | "description": "filter reverses a sequence, a mapping, or a string"
140 | },
141 | "round": {
142 | "prefix": "round",
143 | "body": "${0} | round(1, '${floor}')",
144 | "text": "round",
145 | "description": "filter rounds a number to a given precision"
146 | },
147 | "slice": {
148 | "prefix": "slice",
149 | "body": "slice(${start}, ${length})",
150 | "text": "slice(start, length)",
151 | "description":
152 | "filter extracts a slice of a sequence, a mapping, or a string"
153 | },
154 | "slice [] notation": {
155 | "prefix": "slice [] notation",
156 | "body": "[${start}:${length}]",
157 | "description":
158 | "filter extracts a slice of a sequence, a mapping, or a string"
159 | },
160 | "sort": {
161 | "text": "sort",
162 | "body": "sort",
163 | "description": "filter sorts an array"
164 | },
165 | "split": {
166 | "prefix": "split",
167 | "body": "split('$1')",
168 | "text": "split('')",
169 | "description":
170 | "filter splits a string by the given delimiter and returns a list of strings"
171 | },
172 | "striptags": {
173 | "text": "striptags",
174 | "body": "striptags",
175 | "description":
176 | "filter strips SGML/XML tags and replace adjacent whitespace by one space"
177 | },
178 | "title": {
179 | "text": "title",
180 | "body": "title",
181 | "description":
182 | "filter returns a titlecased version of the value. Words will start with uppercase letters, all remaining characters are lowercase"
183 | },
184 | "trim": {
185 | "text": "trim",
186 | "body": "trim",
187 | "description":
188 | "filter strips whitespace (or other characters) from the beginning and end of a string"
189 | },
190 | "trim()": {
191 | "prefix": "trim()",
192 | "body": "trim('$1')",
193 | "description":
194 | "filter strips whitespace (or other characters) from the beginning and end of a string"
195 | },
196 | "upper": {
197 | "text": "upper",
198 | "body": "upper",
199 | "description": "filter converts a value to uppercase"
200 | },
201 | "url_encode": {
202 | "text": "url_encode",
203 | "body": "url_encode",
204 | "description":
205 | "filter percent encodes a given string as URL segment or an array as query string"
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/src/hover/functions.json:
--------------------------------------------------------------------------------
1 | {
2 | "attribute": {
3 | "prefix": "attribute",
4 | "body": "{{ attribute($1) }}$2",
5 | "description":
6 | "The attribute function can be used to access a \"dynamic\" attribute of a variable",
7 | "example": ""
8 | },
9 | "block": {
10 | "prefix": "block",
11 | "body": "{{ block('${block name}') }}$1",
12 | "description":
13 | "When a template uses inheritance and if you want to print a block multiple times, use the block function",
14 | "example": ""
15 | },
16 | "constant": {
17 | "prefix": "constant",
18 | "body": "{{ constant('${const name}') }}$1",
19 | "description": "constant returns the constant value for a given string",
20 | "example":
21 | "{{ some_date | date(constant('DATE_W3C')) }}\n{{ constant('Namespace\\Classname::CONSTANT_NAME') }}"
22 | },
23 | "cycle": {
24 | "prefix": "cycle",
25 | "body": "{{ cycle(${array}, ${position}) }}$1",
26 | "description": "The cycle function cycles on an array of values",
27 | "example": ""
28 | },
29 | "date": {
30 | "prefix": "date",
31 | "body": "{% set ${currentDate} = date($1) %}$2",
32 | "description":
33 | "Converts an argument to a date to allow date comparison",
34 | "example":
35 | "{% date() %}\n{% date('-2days') %}\n{% date('-2days', 'Europe/Paris') %}"
36 | },
37 | "dump": {
38 | "prefix": "dump",
39 | "body": "{{ dump(${array}) }}$1",
40 | "description":
41 | "(function) dumps information about a template variable. This is mostly useful to debug a template that does not behave as expected by introspecting its variables",
42 | "example": ""
43 | },
44 | "include": {
45 | "prefix": "include function",
46 | "body": "{{ include('${filename}.twig') }}$1",
47 | "description": "(function) returns the rendered content of a template",
48 | "example": ""
49 | },
50 | "max": {
51 | "prefix": "max",
52 | "body": "{% set ${result} = max(${array}) %}$1",
53 | "description":
54 | "(function) returns the biggest value of a sequence or a set of values",
55 | "example":
56 | "{{ max(1, 3, 2) }}\n{# returns \"3\" #}\n\n{{ max({2: \"e\", 3: \"a\", 1: \"b\", 5: \"d\", 4: \"c\"}) }}\n{# returns \"e\" #}"
57 | },
58 | "min": {
59 | "prefix": "min",
60 | "body": "{% set ${result} = min(${array}) %}$1",
61 | "description":
62 | "(function) returns the lowest value of a sequence or a set of values",
63 | "example":
64 | "{{ min(1, 3, 2) }}\n{# returns \"1\" #}\n\n{{ min({2: \"e\", 3: \"a\", 1: \"b\", 5: \"d\", 4: \"c\"}) }}\n{# returns \"a\" #}"
65 | },
66 | "parent": {
67 | "prefix": "parent",
68 | "body": "{{ parent() }}",
69 | "description":
70 | "(function) return the content of the block as defined in the base template",
71 | "example":
72 | "{% extends \"base.html\" %}\n\n{% block sidebar %}\n\t
Table Of Contents
\n\t...\n\t{{ parent() }}\n{% endblock %}"
73 | },
74 | "random": {
75 | "prefix": "random",
76 | "hover": "",
77 | "body": "{% set ${result} = random($1) %}$2",
78 | "description":
79 | "(function) returns a random value depending on the supplied parameter type",
80 | "example":
81 | "{{ random(['apple', 'orange', 'citrus']) }}\n{# example output: orange #}\n\n{{ random('ABC') }}\n{# example output: C #}\n\n{{ random() }}\n{# example output: 15386094 (works as the native PHP mt_rand function) #}\n\n{{ random(5) }}\n{# example output: 3 #}"
82 | },
83 | "range set": {
84 | "prefix": "range set",
85 | "body": "{% set ${result} = range(${low}, ${high}, ${step}) %}$1",
86 | "description":
87 | "(function) Returns an array of elements from low to high, inclusive",
88 | "example":
89 | "{% set result = range(0, 6, 2) %}\n{% dump(result) %}\n{# output: array(0, 2, 4, 6) #}"
90 | },
91 | "range": {
92 | "prefix": "range",
93 | "body": "range(${low}, ${high}, ${step})",
94 | "description":
95 | "(function) Returns an array of elements from low to high, inclusive",
96 | "example":
97 | "{% set result = range(0, 6, 2) %}\n{% dump(result) %}\n{# output: array(0, 2, 4, 6) #}"
98 | },
99 | "source": {
100 | "prefix": "source",
101 | "body": "{{ source('${template}.twig') }}$1",
102 | "description":
103 | "(function) returns the content of a template without rendering it",
104 | "example": ""
105 | },
106 | "template_from_string": {
107 | "prefix": "template_from_string",
108 | "body": "{{ include(template_from_string(\"$1\")) }}$2",
109 | "description": "(function) loads a template from a string",
110 | "example": "{{ include(template_from_string(\"Hello {{ name }}\")) }}"
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/hover/twig.json:
--------------------------------------------------------------------------------
1 | {
2 | "show": {
3 | "prefix": "show",
4 | "body": "{{ $1 }}",
5 | "description": "{{ }}"
6 | },
7 | "execute": {
8 | "prefix": "execute",
9 | "body": "{% $1 %}",
10 | "description": "{% %}"
11 | },
12 | "autoescape": {
13 | "prefix": "autoescape",
14 | "body": ["{% autoescape %}", "\t$1", "{% endautoescape %}"],
15 | "description":
16 | "Whether automatic escaping is enabled or not, you can mark a section of a template to be escaped or not by using the autoescape tag",
17 | "example":
18 | "{% autoescape %}\n Everything will be automatically escaped in this block\n using the HTML strategy\n{% endautoescape %}\n\n{% autoescape 'html' %}\n Everything will be automatically escaped in this block\n using the HTML strategy\n{% endautoescape %}\n\n{% autoescape 'js' %}\n Everything will be automatically escaped in this block\n using the js escaping strategy\n{% endautoescape %}\n\n{% autoescape false %}\n Everything will be outputted as is in this block\n{% endautoescape %}"
19 | },
20 | "block": {
21 | "prefix": "block",
22 | "body": ["{% block ${name} %}", "\t$1", "{% endblock ${name} %}"],
23 | "description":
24 | "When a template uses inheritance and if you want to print a block multiple times, use the block function"
25 | },
26 | "do": {
27 | "prefix": "do",
28 | "body": ["{% do $1 %}"],
29 | "description":
30 | "The do tag works exactly like the regular variable expression ({{ ... }}) just that it doesn't print anything",
31 | "example": "{% do 1 + 2 %}"
32 | },
33 | "embed": {
34 | "prefix": "embed",
35 | "body": ["{% embed \"${filename}.twig\" %}", "\t$1", "{% endembed %}"],
36 | "description":
37 | "The embed tag combines the behaviour of include and extends. It allows you to include another template's contents, just like include does. But it also allows you to override any block defined inside the included template, like when extending a template"
38 | },
39 | "extends": {
40 | "prefix": "extends",
41 | "body": "{% extends \"${filename}.twig\" %}",
42 | "description": "Twig snippets"
43 | },
44 | "filter": {
45 | "prefix": "filter",
46 | "body": ["{% filter ${filter name} %}", "\t$1", "{% endfilter %}"],
47 | "description":
48 | "Filter sections allow you to apply regular Twig filters on a block of template data. Just wrap the code in the special filter section",
49 | "example":
50 | "{% filter lower | escape %}\n SOME TEXT\n{% endfilter %}\n\n{# outputs \"<strong>some text</strong>\" #}"
51 | },
52 | "flush": {
53 | "prefix": "flush",
54 | "body": ["{% flush %}"],
55 | "description": "The flush tag tells Twig to flush the output buffer",
56 | "example": "{% flush %}"
57 | },
58 | "for": {
59 | "prefix": "for",
60 | "body": ["{% for ${row} in ${array} %}", "\t$1", "{% endfor %}"],
61 | "description": "Loop over each item in a sequence"
62 | },
63 | "for if": {
64 | "prefix": "for if",
65 | "body": [
66 | "{% for ${row} in ${array} if ${condition} %}",
67 | "\t$1",
68 | "{% endfor %}"
69 | ],
70 | "description": "Loop over each item in a sequence"
71 | },
72 | "for else": {
73 | "prefix": "for else",
74 | "body": [
75 | "{% for ${row} in ${array} %}",
76 | "\t$1",
77 | "{% else %}",
78 | "\t$2",
79 | "{% endfor %}"
80 | ],
81 | "description": "Loop over each item in a sequence"
82 | },
83 | "for if else": {
84 | "prefix": "for if else",
85 | "body": [
86 | "{% for ${row} in ${array} if ${condition} %}",
87 | "\t$1",
88 | "{% else %}",
89 | "\t$2",
90 | "{% endfor %}"
91 | ],
92 | "description": "Loop over each item in a sequence"
93 | },
94 | "loop": {
95 | "prefix": "loop",
96 | "body": "loop.",
97 | "description": "special variables inside of a for loop block"
98 | },
99 | "if": {
100 | "prefix": "if",
101 | "body": ["{% if ${condition} %}", "\t$1", "{% endif %}"],
102 | "description":
103 | "The if statement in Twig is comparable with the if statements of PHP"
104 | },
105 | "if else": {
106 | "prefix": "if else",
107 | "body": [
108 | "{% if ${condition} %}",
109 | "\t$1",
110 | "{% else %}",
111 | "\t$2",
112 | "{% endif %}"
113 | ],
114 | "description":
115 | "The if statement in Twig is comparable with the if statements of PHP"
116 | },
117 | "else": {
118 | "prefix": "else",
119 | "body": "{% else %}",
120 | "description":
121 | "The if statement in Twig is comparable with the if statements of PHP"
122 | },
123 | "else if": {
124 | "prefix": "else if",
125 | "body": "{% elseif ${condition} %}",
126 | "description":
127 | "The if statement in Twig is comparable with the if statements of PHP"
128 | },
129 | "import": {
130 | "prefix": "import",
131 | "body": "{% import \"${filename}.twig\" as ${alias}%}",
132 | "description":
133 | "Twig supports putting often used code into macros. These macros can go into different templates and get imported from there."
134 | },
135 | "_self": {
136 | "prefix": "_self",
137 | "body": "_self",
138 | "description":
139 | "To import macros from the current file, use the special _self variable for the source"
140 | },
141 | "include": {
142 | "prefix": "include",
143 | "body": "{% include \"${filename}.twig\" %}",
144 | "description":
145 | "The include statement includes a template and returns the rendered content of that file into the current namespace"
146 | },
147 | "macro": {
148 | "prefix": "macro",
149 | "body": ["{% macro ${name}($1) %}", "\t$2", "{% endmacro %}"],
150 | "description": "Twig snippets"
151 | },
152 | "sandbox": {
153 | "prefix": "sandbox",
154 | "body": ["{% sandbox %}", "\t$1", "{% endsandbox %}"],
155 | "description":
156 | "The sandbox tag can be used to enable the sandboxing mode for an included template, when sandboxing is not enabled globally for the Twig environment"
157 | },
158 | "set": {
159 | "prefix": "set",
160 | "body": ["{% set ${name} = ${value} %}$1"],
161 | "description": "Assign values to variables"
162 | },
163 | "set block": {
164 | "prefix": "set (block)",
165 | "body": ["{% set ${name} %}", "\t$1", "{% endset %}"],
166 | "description":
167 | "Inside code blocks you can also assign values to variables. Assignments use the set tag and can have multiple targets"
168 | },
169 | "spaceless": {
170 | "prefix": "spaceless",
171 | "body": ["{% spaceless %}", "\t$1", "{% endspaceless %}"],
172 | "description":
173 | "Use the spaceless tag to remove whitespace between HTML tags, not whitespace within HTML tags or whitespace in plain text"
174 | },
175 | "use": {
176 | "prefix": "use",
177 | "body": "{% use \"${filename}.twig\" %}",
178 | "description": "Twig snippets"
179 | },
180 | "verbatim": {
181 | "prefix": "verbatim",
182 | "body": ["{% verbatim %}", "\t$1", "{% endverbatim %}"],
183 | "description":
184 | "The verbatim tag marks sections as being raw text that should not be parsed. For example to put Twig syntax as example into a template you can use this snippet"
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/src/languages/twig.configuration.json:
--------------------------------------------------------------------------------
1 | {
2 | "comments": {
3 | "blockComment": [ "{#", "#}" ]
4 | },
5 | "brackets": [
6 | ["{{", "}}"],
7 | ["{%", "%}"],
8 | ["[", "]"],
9 | ["(", ")"]
10 | ],
11 | "surroundingPairs": [
12 | { "open": "'", "close": "'" },
13 | { "open": "\"", "close": "\"" },
14 | { "open": "{{", "close": "}}"},
15 | { "open": "{%", "close": "%}"},
16 | { "open": "[", "close": "]"},
17 | { "open": "(", "close": ")" },
18 | { "open": "<", "close": ">" }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/src/snippets/snippets.json:
--------------------------------------------------------------------------------
1 | {
2 | "asset": {
3 | "prefix": "asset",
4 | "body":
5 | "{% set asset = ${1:entry.assetFieldHandle}.one() %}\n\n{% if asset %}\n\t\n{% endif %}",
6 | "description": "asset",
7 | "scope": "text.html.twig"
8 | },
9 | "assets": {
10 | "prefix": "assets",
11 | "body":
12 | "{% for image in craft.assets.\n\t.sourceId(\"${1:1}\")\n\t.kind(\"${2:image}\")\n\t.limit(${3:10})\n}).all() %}\n\t\n{% endfor %}\n$0",
13 | "description": "craft.assets",
14 | "scope": "text.html.twig"
15 | },
16 | "autoescape": {
17 | "prefix": "autoescape",
18 | "body": "{% autoescape \"${1:type}\" %}\n\t$0\n{% endautoescape %}",
19 | "description": "autoescape",
20 | "scope": "text.html.twig"
21 | },
22 | "blockb": {
23 | "prefix": "blockb",
24 | "body": "{% block ${1:name} %}\n\t$0\n{% endblock %}",
25 | "description": "block (block)",
26 | "scope": "text.html.twig"
27 | },
28 | "block": {
29 | "prefix": "block",
30 | "body": "{% block ${1:name} %}$0{% endblock %}",
31 | "description": "block",
32 | "scope": "text.html.twig"
33 | },
34 | "blockf": {
35 | "prefix": "blockf",
36 | "body": "{{ block(\"${1:name}\") }}$0",
37 | "description": "blockf",
38 | "scope": "text.html.twig"
39 | },
40 | "cache": {
41 | "prefix": "cache",
42 | "body": "{% cache %}\n\t$1\n{% endcache %}\n$0",
43 | "description": "cache",
44 | "scope": "text.html.twig"
45 | },
46 | "case": {
47 | "prefix": "case",
48 | "body": "{% case \"${1:value}\" %}\n\t$0",
49 | "description": "case",
50 | "scope": "text.html.twig"
51 | },
52 | "children": {
53 | "prefix": "children",
54 | "body": "{% children %}$0",
55 | "description": "children",
56 | "scope": "text.html.twig"
57 | },
58 | "ceil": {
59 | "prefix": "ceil",
60 | "body": "ceil($1)$0",
61 | "description": "ceil",
62 | "scope": "text.html.twig"
63 | },
64 | "formlogin": {
65 | "prefix": "formlogin",
66 | "body":
67 | "\n\n