+++ /dev/null
-CodeMirror.defineMode("sass", function(config) {
- var tokenRegexp = function(words){
- return new RegExp("^" + words.join("|"));
- };
-
- var keywords = ["true", "false", "null", "auto"];
- var keywordsRegexp = new RegExp("^" + keywords.join("|"));
-
- var operators = ["\\(", "\\)", "=", ">", "<", "==", ">=", "<=", "\\+", "-", "\\!=", "/", "\\*", "%", "and", "or", "not"];
- var opRegexp = tokenRegexp(operators);
-
- var pseudoElementsRegexp = /^::?[\w\-]+/;
-
- var urlTokens = function(stream, state){
- var ch = stream.peek();
-
- if (ch === ")"){
- stream.next();
- state.tokenizer = tokenBase;
- return "operator";
- }else if (ch === "("){
- stream.next();
- stream.eatSpace();
-
- return "operator";
- }else if (ch === "'" || ch === '"'){
- state.tokenizer = buildStringTokenizer(stream.next());
- return "string";
- }else{
- state.tokenizer = buildStringTokenizer(")", false);
- return "string";
- }
- };
- var multilineComment = function(stream, state) {
- if (stream.skipTo("*/")){
- stream.next();
- stream.next();
- state.tokenizer = tokenBase;
- }else {
- stream.next();
- }
-
- return "comment";
- };
-
- var buildStringTokenizer = function(quote, greedy){
- if(greedy == null){ greedy = true; }
-
- function stringTokenizer(stream, state){
- var nextChar = stream.next();
- var peekChar = stream.peek();
- var previousChar = stream.string.charAt(stream.pos-2);
-
- var endingString = ((nextChar !== "\\" && peekChar === quote) || (nextChar === quote && previousChar !== "\\"));
-
- /*
- console.log("previousChar: " + previousChar);
- console.log("nextChar: " + nextChar);
- console.log("peekChar: " + peekChar);
- console.log("ending: " + endingString);
- */
-
- if (endingString){
- if (nextChar !== quote && greedy) { stream.next(); }
- state.tokenizer = tokenBase;
- return "string";
- }else if (nextChar === "#" && peekChar === "{"){
- state.tokenizer = buildInterpolationTokenizer(stringTokenizer);
- stream.next();
- return "operator";
- }else {
- return "string";
- }
- }
-
- return stringTokenizer;
- };
-
- var buildInterpolationTokenizer = function(currentTokenizer){
- return function(stream, state){
- if (stream.peek() === "}"){
- stream.next();
- state.tokenizer = currentTokenizer;
- return "operator";
- }else{
- return tokenBase(stream, state);
- }
- };
- };
-
- var indent = function(state){
- if (state.indentCount == 0){
- state.indentCount++;
- var lastScopeOffset = state.scopes[0].offset;
- var currentOffset = lastScopeOffset + config.indentUnit;
- state.scopes.unshift({ offset:currentOffset });
- }
- };
-
- var dedent = function(state){
- if (state.scopes.length == 1) { return; }
-
- state.scopes.shift();
- };
-
- var tokenBase = function(stream, state) {
- var ch = stream.peek();
-
- // Single line Comment
- if (stream.match('//')) {
- stream.skipToEnd();
- return "comment";
- }
-
- // Multiline Comment
- if (stream.match('/*')){
- state.tokenizer = multilineComment;
- return state.tokenizer(stream, state);
- }
-
- // Interpolation
- if (stream.match('#{')){
- state.tokenizer = buildInterpolationTokenizer(tokenBase);
- return "operator";
- }
-
- if (ch === "."){
- stream.next();
-
- // Match class selectors
- if (stream.match(/^[\w-]+/)){
- indent(state);
- return "atom";
- }else if (stream.peek() === "#"){
- indent(state);
- return "atom";
- }else{
- return "operator";
- }
- }
-
- if (ch === "#"){
- stream.next();
-
- // Hex numbers
- if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)){
- return "number";
- }
-
- // ID selectors
- if (stream.match(/^[\w-]+/)){
- indent(state);
- return "atom";
- }
-
- if (stream.peek() === "#"){
- indent(state);
- return "atom";
- }
- }
-
- // Numbers
- if (stream.match(/^-?[0-9\.]+/)){
- return "number";
- }
-
- // Units
- if (stream.match(/^(px|em|in)\b/)){
- return "unit";
- }
-
- if (stream.match(keywordsRegexp)){
- return "keyword";
- }
-
- if (stream.match(/^url/) && stream.peek() === "("){
- state.tokenizer = urlTokens;
- return "atom";
- }
-
- // Variables
- if (ch === "$"){
- stream.next();
- stream.eatWhile(/[\w-]/);
-
- if (stream.peek() === ":"){
- stream.next();
- return "variable-2";
- }else{
- return "variable-3";
- }
- }
-
- if (ch === "!"){
- stream.next();
-
- if (stream.match(/^[\w]+/)){
- return "keyword";
- }
-
- return "operator";
- }
-
- if (ch === "="){
- stream.next();
-
- // Match shortcut mixin definition
- if (stream.match(/^[\w-]+/)){
- indent(state);
- return "meta";
- }else {
- return "operator";
- }
- }
-
- if (ch === "+"){
- stream.next();
-
- // Match shortcut mixin definition
- if (stream.match(/^[\w-]+/)){
- return "variable-3";
- }else {
- return "operator";
- }
- }
-
- // Indent Directives
- if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)){
- indent(state);
- return "meta";
- }
-
- // Other Directives
- if (ch === "@"){
- stream.next();
- stream.eatWhile(/[\w-]/);
- return "meta";
- }
-
- // Strings
- if (ch === '"' || ch === "'"){
- stream.next();
- state.tokenizer = buildStringTokenizer(ch);
- return "string";
- }
-
- // Pseudo element selectors
- if (ch == ':' && stream.match(pseudoElementsRegexp)){
- return "keyword";
- }
-
- // atoms
- if (stream.eatWhile(/[\w-&]/)){
- // matches a property definition
- if (stream.peek() === ":" && !stream.match(pseudoElementsRegexp, false))
- return "property";
- else
- return "atom";
- }
-
- if (stream.match(opRegexp)){
- return "operator";
- }
-
- // If we haven't returned by now, we move 1 character
- // and return an error
- stream.next();
- return null;
- };
-
- var tokenLexer = function(stream, state) {
- if (stream.sol()){
- state.indentCount = 0;
- }
- var style = state.tokenizer(stream, state);
- var current = stream.current();
-
- if (current === "@return"){
- dedent(state);
- }
-
- if (style === "atom"){
- indent(state);
- }
-
- if (style !== null){
- var startOfToken = stream.pos - current.length;
- var withCurrentIndent = startOfToken + (config.indentUnit * state.indentCount);
-
- var newScopes = [];
-
- for (var i = 0; i < state.scopes.length; i++){
- var scope = state.scopes[i];
-
- if (scope.offset <= withCurrentIndent){
- newScopes.push(scope);
- }
- }
-
- state.scopes = newScopes;
- }
-
-
- return style;
- };
-
- return {
- startState: function() {
- return {
- tokenizer: tokenBase,
- scopes: [{offset: 0, type: 'sass'}],
- definedVars: [],
- definedMixins: []
- };
- },
- token: function(stream, state) {
- var style = tokenLexer(stream, state);
-
- state.lastToken = { style: style, content: stream.current() };
-
- return style;
- },
-
- indent: function(state) {
- return state.scopes[0].offset;
- }
- };
-});
-
-CodeMirror.defineMIME("text/x-sass", "sass");