Bläddra i källkod

Add css/less/scss definitions and grammar

Martin Aeschlimann 9 år sedan
förälder
incheckning
4be02a4e82
6 ändrade filer med 667 tillägg och 1 borttagningar
  1. 3 0
      gulpfile.js
  2. 1 1
      package.json
  3. 190 0
      src/css.ts
  4. 175 0
      src/less.ts
  5. 21 0
      src/monaco.contribution.ts
  6. 277 0
      src/scss.ts

+ 3 - 0
gulpfile.js

@@ -46,6 +46,7 @@ gulp.task('release', ['clean-release','compile'], function() {
 	return merge(
 			bundleOne('src/monaco.contribution'),
 			bundleOne('src/bat'),
+			bundleOne('src/css'),
 			bundleOne('src/coffee'),
 			bundleOne('src/cpp'),
 			bundleOne('src/csharp'),
@@ -55,6 +56,7 @@ gulp.task('release', ['clean-release','compile'], function() {
 			bundleOne('src/ini'),
 			bundleOne('src/jade'),
 			bundleOne('src/java'),
+			bundleOne('src/less'),
 			bundleOne('src/lua'),
 			bundleOne('src/markdown'),
 			bundleOne('src/objective-c'),
@@ -62,6 +64,7 @@ gulp.task('release', ['clean-release','compile'], function() {
 			bundleOne('src/python'),
 			bundleOne('src/r'),
 			bundleOne('src/ruby'),
+			bundleOne('src/scss'),
 			bundleOne('src/sql'),
 			bundleOne('src/swift'),
 			bundleOne('src/vb'),

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "monaco-languages",
-  "version": "0.2.0",
+  "version": "0.2.1",
   "description": "Bundle of many languages for the Monaco Editor.",
   "scripts": {
     "test": "node_modules/.bin/mocha",

+ 190 - 0
src/css.ts

@@ -0,0 +1,190 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+'use strict';
+
+import LanguageConfiguration = monaco.languages.LanguageConfiguration;
+import IMonarchLanguage = monaco.languages.IMonarchLanguage;
+
+export var conf:LanguageConfiguration = {
+    wordPattern: /(#?-?\d*\.\d\w*%?)|((::|[@#.!:])?[\w-?]+%?)|::|[@#.!:]/g,
+
+    comments: {
+        blockComment: ['/*', '*/']
+    },
+
+    brackets: [
+        ['{', '}'],
+        ['[', ']'],
+        ['(', ')']
+    ],
+
+    autoClosingPairs: [
+        { open: '{', close: '}' },
+        { open: '[', close: ']' },
+        { open: '(', close: ')' },
+        { open: '"', close: '"', notIn: ['string'] },
+        { open: '\'', close: '\'', notIn: ['string'] }
+    ]
+};
+
+const TOKEN_SELECTOR = 'entity.name.selector';
+const TOKEN_SELECTOR_TAG = 'entity.name.tag';
+const TOKEN_PROPERTY = 'support.type.property-name';
+const TOKEN_VALUE = 'support.property-value';
+const TOKEN_AT_KEYWORD = 'keyword.control.at-rule';
+
+export var language = <IMonarchLanguage> {
+    defaultToken: '',
+    tokenPostfix: '.css',
+
+    ws: '[ \t\n\r\f]*', // whitespaces (referenced in several rules)
+    identifier: '-?-?([a-zA-Z]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))([\\w\\-]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))*',
+
+    brackets: [
+        { open: '{', close: '}', token: 'punctuation.curly' },
+        { open: '[', close: ']', token: 'punctuation.bracket' },
+        { open: '(', close: ')', token: 'punctuation.parenthesis' },
+        { open: '<', close: '>', token: 'punctuation.angle' }
+    ],
+
+    tokenizer: {
+        root: [
+            { include: '@selector' },
+        ],
+
+        selector: [
+            { include: '@comments' },
+            { include: '@import' },
+            ['[@](keyframes|-webkit-keyframes|-moz-keyframes|-o-keyframes)', { token: TOKEN_AT_KEYWORD, next: '@keyframedeclaration' }],
+            ['[@](page|content|font-face|-moz-document)', { token: TOKEN_AT_KEYWORD }],
+            ['[@](charset|namespace)', { token: TOKEN_AT_KEYWORD, next: '@declarationbody' }],
+            ['url(\\-prefix)?\\(', { token: 'support.function.name', bracket: '@open', next: '@urldeclaration' }],
+            { include: '@selectorname' },
+            ['[\\*]', TOKEN_SELECTOR_TAG], // selector symbols
+            ['[>\\+,]', 'punctuation'], // selector operators
+            ['\\[', { token: 'punctuation.bracket', bracket: '@open', next: '@selectorattribute' }],
+            ['{', { token: 'punctuation.curly', bracket: '@open', next: '@selectorbody' }]
+        ],
+
+        selectorbody: [
+            ['[*_]?@identifier@ws:(?=(\\s|\\d|[^{;}]*[;}]))', TOKEN_PROPERTY, '@rulevalue'], // rule definition: to distinguish from a nested selector check for whitespace, number or a semicolon
+            ['}', { token: 'punctuation.curly', bracket: '@close', next: '@pop' }]
+        ],
+
+        selectorname: [
+            ['(\\.|#(?=[^{])|%|(@identifier)|:)+', TOKEN_SELECTOR], // selector (.foo, div, ...)
+        ],
+
+        selectorattribute: [
+            { include: '@term' },
+            [']', { token: 'punctuation.bracket', bracket: '@close', next: '@pop' }],
+        ],
+
+        term: [
+            { include: '@comments' },
+            ['url(\\-prefix)?\\(', { token: 'support.function.name', bracket: '@open', next: '@urldeclaration' }],
+            { include: '@functioninvocation' },
+            { include: '@numbers' },
+            { include: '@name' },
+            ['([<>=\\+\\-\\*\\/\\^\\|\\~,])', 'keyword.operator'],
+            [',', 'punctuation']
+        ],
+
+        rulevalue: [
+            { include: '@term' },
+            ['!important', 'literal'],
+            [';', 'punctuation', '@pop'],
+            ['(?=})', { token: '', next: '@pop' }] // missing semicolon
+        ],
+
+        warndebug: [
+            ['[@](warn|debug)', { token: TOKEN_AT_KEYWORD, next: '@declarationbody' }]
+        ],
+
+        import: [
+            ['[@](import)', { token: TOKEN_AT_KEYWORD, next: '@declarationbody' }]
+        ],
+
+        urldeclaration: [
+            { include: '@strings' },
+            ['[^)\r\n]+', 'string'],
+            ['\\)', { token: 'support.function.name', bracket: '@close', next: '@pop' }]
+        ],
+
+        parenthizedterm: [
+            { include: '@term' },
+            ['\\)', { token: 'punctuation.parenthesis', bracket: '@close', next: '@pop' }]
+        ],
+
+        declarationbody: [
+            { include: '@term' },
+            [';', 'punctuation', '@pop'],
+            ['(?=})', { token: '', next: '@pop' }] // missing semicolon
+        ],
+
+        comments: [
+            ['\\/\\*', 'comment', '@comment'],
+            ['\\/\\/+.*', 'comment']
+        ],
+
+        comment: [
+            ['\\*\\/', 'comment', '@pop'],
+            ['.', 'comment']
+        ],
+
+        name: [
+            ['@identifier', TOKEN_VALUE]
+        ],
+
+        numbers: [
+            ['(\\d*\\.)?\\d+([eE][\\-+]?\\d+)?', { token: 'constant.numeric', next: '@units' }],
+            ['#[0-9a-fA-F_]+(?!\\w)', 'constant.rgb-value']
+        ],
+
+        units: [
+            ['(em|ex|ch|rem|vw|vh|vm|cm|mm|in|px|pt|pc|deg|grad|rad|turn|s|ms|Hz|kHz|%)?', 'constant.numeric', '@pop']
+        ],
+
+        keyframedeclaration: [
+            ['@identifier', 'support.function.name'],
+            ['{', { token: 'punctuation.curly', bracket: '@open', switchTo: '@keyframebody' }],
+        ],
+
+        keyframebody: [
+            { include: '@term' },
+            ['{', { token: 'punctuation.curly', bracket: '@open', next: '@selectorbody' }],
+            ['}', { token: 'punctuation.curly', bracket: '@close', next: '@pop' }],
+        ],
+
+        functioninvocation: [
+            ['@identifier\\(', { token: 'support.function.name', bracket: '@open', next: '@functionarguments' }],
+        ],
+
+        functionarguments: [
+            ['\\$@identifier@ws:', TOKEN_PROPERTY],
+            ['[,]', 'punctuation'],
+            { include: '@term' },
+            ['\\)', { token: 'support.function.name', bracket: '@close', next: '@pop' }],
+        ],
+
+        strings: [
+            ['~?"', { token: 'string.punctuation', bracket: '@open', next: '@stringenddoublequote' }],
+            ['~?\'', { token: 'string.punctuation', bracket: '@open', next: '@stringendquote' }]
+        ],
+
+        stringenddoublequote: [
+            ['\\\\.', 'string'],
+            ['"', { token: 'string.punctuation', next: '@pop', bracket: '@close' }],
+            ['.', 'string']
+        ],
+
+        stringendquote: [
+            ['\\\\.', 'string'],
+            ['\'', { token: 'string.punctuation', next: '@pop', bracket: '@close' }],
+            ['.', 'string']
+        ]
+    }
+};

+ 175 - 0
src/less.ts

@@ -0,0 +1,175 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+'use strict';
+
+import IRichLanguageConfiguration = monaco.languages.LanguageConfiguration;
+import ILanguage = monaco.languages.IMonarchLanguage;
+
+export var lessLanguageConfiguration: IRichLanguageConfiguration = {
+    wordPattern: /(#?-?\d*\.\d\w*%?)|([@#!.:]?[\w-?]+%?)|[@#!.]/g,
+    comments: {
+        blockComment: ['/*', '*/'],
+        lineComment: '//'
+    },
+    brackets: [['{','}'], ['[',']'], ['(',')'], ['<','>']],
+    autoClosingPairs: [
+        { open: '"', close: '"', notIn: ['string', 'comment'] },
+        { open: '\'', close: '\'', notIn: ['string', 'comment'] },
+        { open: '{', close: '}', notIn: ['string', 'comment'] },
+        { open: '[', close: ']', notIn: ['string', 'comment'] },
+        { open: '(', close: ')', notIn: ['string', 'comment'] },
+        { open: '<', close: '>', notIn: ['string', 'comment'] },
+    ]
+};
+
+const TOKEN_SELECTOR = 'entity.name.selector';
+const TOKEN_SELECTOR_TAG = 'entity.name.tag';
+const TOKEN_PROPERTY = 'support.type.property-name';
+const TOKEN_VALUE = 'support.property-value';
+const TOKEN_AT_KEYWORD = 'keyword.control.at-rule';
+
+export var language = <ILanguage> {
+	defaultToken: '',
+	tokenPostfix: '.less',
+
+	identifier: '-?-?([a-zA-Z]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))([\\w\\-]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))*',
+	identifierPlus: '-?-?([a-zA-Z:.]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))([\\w\\-:.]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))*',
+
+	brackets: [
+		{ open: '{', close: '}', token: 'punctuation.curly' },
+		{ open: '[', close: ']', token: 'punctuation.bracket' },
+		{ open: '(', close: ')', token: 'punctuation.parenthesis' },
+		{ open: '<', close: '>', token: 'punctuation.angle' }
+	],
+
+	tokenizer: {
+		root: <any[]>[
+			{ include: '@nestedJSBegin' },
+
+			['[ \\t\\r\\n]+', ''],
+
+			{ include: '@comments' },
+			{ include: '@keyword' },
+			{ include: '@strings' },
+			{ include: '@numbers' },
+			['[*_]?[a-zA-Z\\-\\s]+(?=:.*(;|(\\\\$)))', TOKEN_PROPERTY, '@attribute'],
+
+			['url(\\-prefix)?\\(', { token: 'function', bracket: '@open', next: '@urldeclaration'}],
+
+			['[{}()\\[\\]]', '@brackets'],
+			['[,:;]', 'punctuation'],
+
+			['#@identifierPlus', TOKEN_SELECTOR + '.id'],
+			['&', TOKEN_SELECTOR_TAG],
+
+			['\\.@identifierPlus(?=\\()', TOKEN_SELECTOR + '.class', '@attribute'],
+			['\\.@identifierPlus', TOKEN_SELECTOR + '.class'],
+
+			['@identifierPlus', TOKEN_SELECTOR_TAG],
+			{ include: '@operators' },
+
+			['@(@identifier(?=[:,\\)]))', 'variable', '@attribute'],
+			['@(@identifier)', 'variable'],
+			['@', 'key', '@atRules']
+		],
+
+		nestedJSBegin: [
+			['``', 'punctuation.backtick'],
+			<any[]>['`', { token: 'punctuation.backtick', bracket: '@open', next: '@nestedJSEnd', nextEmbedded: 'text/javascript' }],
+		],
+
+		nestedJSEnd: [
+			<any[]>['`', { token: 'punctuation.backtick', bracket: '@close', next: '@pop' }],
+			<any[]>['.', { token: '@rematch', next: '@javascript_block' }],
+		],
+
+		javascript_block: [
+			<any[]>['`', { token: '@rematch', next: '@pop', nextEmbedded: '@pop' }],
+		],
+
+		operators: [
+			['[<>=\\+\\-\\*\\/\\^\\|\\~]', 'operator']
+		],
+
+		keyword: [
+			['(@[\\s]*import|![\\s]*important|true|false|when|iscolor|isnumber|isstring|iskeyword|isurl|ispixel|ispercentage|isem|hue|saturation|lightness|alpha|lighten|darken|saturate|desaturate|fadein|fadeout|fade|spin|mix|round|ceil|floor|percentage)\\b', 'keyword']
+		],
+
+		urldeclaration: [
+			{ include: '@strings'},
+			[ '[^)\r\n]+', 'string' ],
+			['\\)', { token: 'tag', bracket: '@close', next: '@pop'}],
+		],
+
+		attribute: <any[]>[
+			{ include: '@nestedJSBegin' },
+			{ include: '@comments' },
+			{ include: '@strings' },
+			{ include: '@numbers' },
+
+			{ include: '@keyword' },
+
+			['[a-zA-Z\\-]+(?=\\()', TOKEN_VALUE, '@attribute'],
+			['>', 'operator', '@pop'],
+			['@identifier', TOKEN_VALUE],
+			{ include: '@operators' },
+			['@(@identifier)', 'variable'],
+
+			['[)\\}]', '@brackets', '@pop'],
+			['[{}()\\[\\]>]', '@brackets'],
+
+			['[;]', 'punctuation', '@pop'],
+			['[,=:]', 'punctuation'],
+
+			['\\s', ''],
+			['.', TOKEN_VALUE]
+		],
+
+		comments: [
+			['\\/\\*', 'comment', '@comment'],
+			['\\/\\/+.*', 'comment'],
+		],
+
+		comment: [
+			['\\*\\/', 'comment', '@pop'],
+			['.', 'comment'],
+		],
+
+		numbers: [
+			<any[]>['(\\d*\\.)?\\d+([eE][\\-+]?\\d+)?', { token: TOKEN_VALUE + '.numeric', next: '@units' }],
+			['#[0-9a-fA-F_]+(?!\\w)', TOKEN_VALUE + '.rgb-value']
+		],
+
+		units: [
+			['((em|ex|ch|rem|vw|vh|vm|cm|mm|in|px|pt|pc|deg|grad|rad|turn|s|ms|Hz|kHz|%)\\b)?', TOKEN_VALUE + '.unit', '@pop']
+		],
+
+		strings: [
+			<any[]>['~?"', { token: 'string.punctuation', bracket: '@open', next: '@stringsEndDoubleQuote' }],
+			<any[]>['~?\'', { token: 'string.punctuation', bracket: '@open', next: '@stringsEndQuote' }]
+		],
+
+		stringsEndDoubleQuote: [
+			['\\\\"', 'string'],
+			<any[]>['"', { token: 'string.punctuation', next: '@popall', bracket: '@close' }],
+			['.', 'string']
+		],
+
+		stringsEndQuote: [
+			['\\\\\'', 'string'],
+			<any[]>['\'', { token: 'string.punctuation', next: '@popall', bracket: '@close' }],
+			['.', 'string']
+		],
+
+		atRules: <any[]>[
+			{ include: '@comments' },
+			{ include: '@strings' },
+			['[()]', 'punctuation'],
+			['[\\{;]', 'punctuation', '@pop'],
+			['.', 'key']
+		]
+	}
+};

+ 21 - 0
src/monaco.contribution.ts

@@ -183,3 +183,24 @@ registerLanguage({
 	mimetypes: ['text/xml', 'application/xml', 'application/xaml+xml', 'application/xml-dtd'],
 	module: './xml'
 });
+registerLanguage({
+	id: 'less',
+	extensions: ['.less'],
+	aliases: ['Less', 'less'],
+	mimetypes: ['text/x-less', 'text/less'],
+	module: './less'
+});
+registerLanguage({
+	id: 'scss',
+	extensions: ['.scss'],
+	aliases: ['Sass', 'sass', 'scss'],
+	mimetypes: ['text/x-scss', 'text/scss'],
+	module: './scss'
+});
+registerLanguage({
+	id: 'css',
+	extensions: ['.css'],
+	aliases: ['CSS', 'css'],
+	mimetypes: ['text/css'],
+	module: './css'
+});

+ 277 - 0
src/scss.ts

@@ -0,0 +1,277 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+'use strict';
+
+import LanguageConfiguration = monaco.languages.LanguageConfiguration;
+import IMonarchLanguage = monaco.languages.IMonarchLanguage;
+
+export var conf: LanguageConfiguration = {
+    wordPattern: /(#?-?\d*\.\d\w*%?)|([@#!.:]?[\w-?]+%?)|[@#!.]/g,
+    comments: {
+        blockComment: ['/*', '*/'],
+        lineComment: '//'
+    },
+    brackets: [['{', '}'], ['[', ']'], ['(', ')'], ['<', '>']],
+    autoClosingPairs: [
+        { open: '"', close: '"', notIn: ['string', 'comment'] },
+        { open: '\'', close: '\'', notIn: ['string', 'comment'] },
+        { open: '{', close: '}', notIn: ['string', 'comment'] },
+        { open: '[', close: ']', notIn: ['string', 'comment'] },
+        { open: '(', close: ')', notIn: ['string', 'comment'] },
+        { open: '<', close: '>', notIn: ['string', 'comment'] },
+    ]
+};
+
+const TOKEN_SELECTOR = 'entity.name.selector';
+const TOKEN_SELECTOR_TAG = 'entity.name.tag';
+const TOKEN_PROPERTY = 'support.type.property-name';
+const TOKEN_VALUE = 'support.property-value';
+const TOKEN_AT_KEYWORD = 'keyword.control.at-rule';
+
+export var language = <IMonarchLanguage> {
+    defaultToken: '',
+    tokenPostfix: '.scss',
+
+    ws: '[ \t\n\r\f]*', // whitespaces (referenced in several rules)
+    identifier: '-?-?([a-zA-Z]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))([\\w\\-]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))*',
+
+    brackets: [
+        { open: '{', close: '}', token: 'punctuation.curly' },
+        { open: '[', close: ']', token: 'punctuation.bracket' },
+        { open: '(', close: ')', token: 'punctuation.parenthesis' },
+        { open: '<', close: '>', token: 'punctuation.angle' }
+    ],
+
+    tokenizer: {
+        root: [
+            { include: '@selector' },
+        ],
+
+        selector: [
+            { include: '@comments' },
+            { include: '@import' },
+            { include: '@variabledeclaration' },
+            { include: '@warndebug' }, // sass: log statements
+            ['[@](include)', { token: TOKEN_AT_KEYWORD, next: '@includedeclaration' }], // sass: include statement
+            ['[@](keyframes|-webkit-keyframes|-moz-keyframes|-o-keyframes)', { token: TOKEN_AT_KEYWORD, next: '@keyframedeclaration' }],
+            ['[@](page|content|font-face|-moz-document)', { token: TOKEN_AT_KEYWORD }], // sass: placeholder for includes
+            ['[@](charset|namespace)', { token: TOKEN_AT_KEYWORD, next: '@declarationbody' }],
+            ['[@](function)', { token: TOKEN_AT_KEYWORD, next: '@functiondeclaration' }],
+            ['[@](mixin)', { token: TOKEN_AT_KEYWORD, next: '@mixindeclaration' }],
+            ['url(\\-prefix)?\\(', { token: 'support.function.name', bracket: '@open', next: '@urldeclaration' }],
+            { include: '@controlstatement' }, // sass control statements
+            { include: '@selectorname' },
+            ['[&\\*]', TOKEN_SELECTOR_TAG], // selector symbols
+            ['[>\\+,]', 'punctuation'], // selector operators
+            ['\\[', { token: 'punctuation.bracket', bracket: '@open', next: '@selectorattribute' }],
+            ['{', { token: 'punctuation.curly', bracket: '@open', next: '@selectorbody' }],
+        ],
+
+        selectorbody: [
+            ['[*_]?@identifier@ws:(?=(\\s|\\d|[^{;}]*[;}]))', TOKEN_PROPERTY, '@rulevalue'], // rule definition: to distinguish from a nested selector check for whitespace, number or a semicolon
+            { include: '@selector' }, // sass: nested selectors
+            ['[@](extend)', { token: TOKEN_AT_KEYWORD, next: '@extendbody' }], // sass: extend other selectors
+            ['[@](return)', { token: TOKEN_AT_KEYWORD, next: '@declarationbody' }],
+            ['}', { token: 'punctuation.curly', bracket: '@close', next: '@pop' }],
+        ],
+
+        selectorname: [
+            ['#{', { token: 'support.function.interpolation', bracket: '@open', next: '@variableinterpolation' }], // sass: interpolation
+            ['(\\.|#(?=[^{])|%|(@identifier)|:)+', TOKEN_SELECTOR], // selector (.foo, div, ...)
+        ],
+
+        selectorattribute: [
+            { include: '@term' },
+            [']', { token: 'punctuation.bracket', bracket: '@close', next: '@pop' }],
+        ],
+
+        term: [
+            { include: '@comments' },
+            ['url(\\-prefix)?\\(', { token: 'support.function.name', bracket: '@open', next: '@urldeclaration' }],
+            { include: '@functioninvocation' },
+            { include: '@numbers' },
+            { include: '@strings' },
+            { include: '@variablereference' },
+            ['(and\\b|or\\b|not\\b)', 'keyword.operator'],
+            { include: '@name' },
+            ['([<>=\\+\\-\\*\\/\\^\\|\\~,])', 'keyword.operator'],
+            [',', 'punctuation'],
+            ['!default', 'literal'],
+            ['\\(', { token: 'punctuation.parenthesis', bracket: '@open', next: '@parenthizedterm' }],
+        ],
+
+        rulevalue: [
+            { include: '@term' },
+            ['!important', 'literal'],
+            [';', 'punctuation', '@pop'],
+            ['{', { token: 'punctuation.curly', bracket: '@open', switchTo: '@nestedproperty' }], // sass: nested properties
+            ['(?=})', { token: '', next: '@pop' }], // missing semicolon
+        ],
+
+        nestedproperty: [
+            ['[*_]?@identifier@ws:', TOKEN_PROPERTY, '@rulevalue'],
+            { include: '@comments' },
+            ['}', { token: 'punctuation.curly', bracket: '@close', next: '@pop' }],
+        ],
+
+        warndebug: [
+            ['[@](warn|debug)', { token: TOKEN_AT_KEYWORD, next: '@declarationbody' }],
+        ],
+
+        import: [
+            ['[@](import)', { token: TOKEN_AT_KEYWORD, next: '@declarationbody' }],
+        ],
+
+        variabledeclaration: [ // sass variables
+            ['\\$@identifier@ws:', 'variable.decl', '@declarationbody'],
+        ],
+
+        urldeclaration: [
+            { include: '@strings' },
+            ['[^)\r\n]+', 'string'],
+            ['\\)', { token: 'support.function.name', bracket: '@close', next: '@pop' }],
+        ],
+
+        parenthizedterm: [
+            { include: '@term' },
+            ['\\)', { token: 'punctuation.parenthesis', bracket: '@close', next: '@pop' }],
+        ],
+
+        declarationbody: [
+            { include: '@term' },
+            [';', 'punctuation', '@pop'],
+            ['(?=})', { token: '', next: '@pop' }], // missing semicolon
+        ],
+
+        extendbody: [
+            { include: '@selectorname' },
+            ['!optional', 'literal'],
+            [';', 'punctuation', '@pop'],
+            ['(?=})', { token: '', next: '@pop' }], // missing semicolon
+        ],
+
+        variablereference: [ // sass variable reference
+            ['\\$@identifier', 'variable.ref'],
+            ['\\.\\.\\.', 'keyword.operator'], // var args in reference
+            ['#{', { token: 'support.function.interpolation', bracket: '@open', next: '@variableinterpolation' }], // sass var resolve
+        ],
+
+        variableinterpolation: [
+            { include: '@variablereference' },
+            ['}', { token: 'support.function.interpolation', bracket: '@close', next: '@pop' }],
+        ],
+
+        comments: [
+            ['\\/\\*', 'comment', '@comment'],
+            ['\\/\\/+.*', 'comment'],
+        ],
+
+        comment: [
+            ['\\*\\/', 'comment', '@pop'],
+            ['.', 'comment'],
+        ],
+
+        name: [
+            ['@identifier', TOKEN_VALUE],
+        ],
+
+        numbers: [
+            ['(\\d*\\.)?\\d+([eE][\\-+]?\\d+)?', { token: 'constant.numeric', next: '@units' }],
+            ['#[0-9a-fA-F_]+(?!\\w)', 'constant.rgb-value'],
+        ],
+
+        units: [
+            ['(em|ex|ch|rem|vw|vh|vm|cm|mm|in|px|pt|pc|deg|grad|rad|turn|s|ms|Hz|kHz|%)?', 'constant.numeric', '@pop']
+        ],
+
+        functiondeclaration: [
+            ['@identifier@ws\\(', { token: 'support.function.name', bracket: '@open', next: '@parameterdeclaration' }],
+            ['{', { token: 'punctuation.curly', bracket: '@open', switchTo: '@functionbody' }],
+        ],
+
+        mixindeclaration: [
+            // mixin with parameters
+            ['@identifier@ws\\(', { token: 'support.function.name', bracket: '@open', next: '@parameterdeclaration' }],
+            // mixin without parameters
+            ['@identifier', 'support.function.name'],
+            ['{', { token: 'punctuation.curly', bracket: '@open', switchTo: '@selectorbody' }],
+        ],
+
+        parameterdeclaration: [
+            ['\\$@identifier@ws:', 'variable'],
+            ['\\.\\.\\.', 'keyword.operator'], // var args in declaration
+            [',', 'punctuation'],
+            { include: '@term' },
+            ['\\)', { token: 'support.function.name', bracket: '@close', next: '@pop' }],
+        ],
+
+        includedeclaration: [
+            { include: '@functioninvocation' },
+            ['@identifier', 'support.function.name'],
+            [';', 'punctuation', '@pop'],
+            ['(?=})', { token: '', next: '@pop' }], // missing semicolon
+            ['{', { token: 'punctuation.curly', bracket: '@open', switchTo: '@selectorbody' }],
+        ],
+
+        keyframedeclaration: [
+            ['@identifier', 'support.function.name'],
+            ['{', { token: 'punctuation.curly', bracket: '@open', switchTo: '@keyframebody' }],
+        ],
+
+        keyframebody: [
+            { include: '@term' },
+            ['{', { token: 'punctuation.curly', bracket: '@open', next: '@selectorbody' }],
+            ['}', { token: 'punctuation.curly', bracket: '@close', next: '@pop' }],
+        ],
+
+        controlstatement: [
+            ['[@](if|else|for|while|each|media)', { token: 'keyword.flow.control.at-rule', next: '@controlstatementdeclaration' }],
+        ],
+
+        controlstatementdeclaration: [
+            ['(in|from|through|if|to)\\b', { token: 'keyword.flow.control.at-rule' }],
+            { include: '@term' },
+            ['{', { token: 'punctuation.curly', bracket: '@open', switchTo: '@selectorbody' }],
+        ],
+
+        functionbody: [
+            ['[@](return)', { token: TOKEN_AT_KEYWORD }],
+            { include: '@variabledeclaration' },
+            { include: '@term' },
+            { include: '@controlstatement' },
+            [';', 'punctuation'],
+            ['}', { token: 'punctuation.curly', bracket: '@close', next: '@pop' }],
+        ],
+
+        functioninvocation: [
+            ['@identifier\\(', { token: 'support.function.name', bracket: '@open', next: '@functionarguments' }],
+        ],
+
+        functionarguments: [
+            ['\\$@identifier@ws:', TOKEN_PROPERTY],
+            ['[,]', 'punctuation'],
+            { include: '@term' },
+            ['\\)', { token: 'support.function.name', bracket: '@close', next: '@pop' }],
+        ],
+
+        strings: [
+            ['~?"', { token: 'string.punctuation', bracket: '@open', next: '@stringenddoublequote' }],
+            ['~?\'', { token: 'string.punctuation', bracket: '@open', next: '@stringendquote' }]
+        ],
+
+        stringenddoublequote: [
+            ['\\\\.', 'string'],
+            ['"', { token: 'string.punctuation', next: '@pop', bracket: '@close' }],
+            ['.', 'string']
+        ],
+
+        stringendquote: [
+            ['\\\\.', 'string'],
+            ['\'', { token: 'string.punctuation', next: '@pop', bracket: '@close' }],
+            ['.', 'string']
+        ]
+    }
+};