mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 13:39:01 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			460 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			460 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
// CodeMirror, copyright (c) by Marijn Haverbeke and others
 | 
						|
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
 | 
						|
 | 
						|
(function(mod) {
 | 
						|
  if (typeof exports == "object" && typeof module == "object") // CommonJS
 | 
						|
    mod(require("../../lib/codemirror"), require("../css/css"));
 | 
						|
  else if (typeof define == "function" && define.amd) // AMD
 | 
						|
    define(["../../lib/codemirror", "../css/css"], mod);
 | 
						|
  else // Plain browser env
 | 
						|
    mod(CodeMirror);
 | 
						|
})(function(CodeMirror) {
 | 
						|
"use strict";
 | 
						|
 | 
						|
CodeMirror.defineMode("sass", function(config) {
 | 
						|
  var cssMode = CodeMirror.mimeModes["text/css"];
 | 
						|
  var propertyKeywords = cssMode.propertyKeywords || {},
 | 
						|
      colorKeywords = cssMode.colorKeywords || {},
 | 
						|
      valueKeywords = cssMode.valueKeywords || {},
 | 
						|
      fontProperties = cssMode.fontProperties || {};
 | 
						|
 | 
						|
  function tokenRegexp(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 = /^::?[a-zA-Z_][\w\-]*/;
 | 
						|
 | 
						|
  var word;
 | 
						|
 | 
						|
  function isEndLine(stream) {
 | 
						|
    return !stream.peek() || stream.match(/\s+$/, false);
 | 
						|
  }
 | 
						|
 | 
						|
  function urlTokens(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";
 | 
						|
    }
 | 
						|
  }
 | 
						|
  function comment(indentation, multiLine) {
 | 
						|
    return function(stream, state) {
 | 
						|
      if (stream.sol() && stream.indentation() <= indentation) {
 | 
						|
        state.tokenizer = tokenBase;
 | 
						|
        return tokenBase(stream, state);
 | 
						|
      }
 | 
						|
 | 
						|
      if (multiLine && stream.skipTo("*/")) {
 | 
						|
        stream.next();
 | 
						|
        stream.next();
 | 
						|
        state.tokenizer = tokenBase;
 | 
						|
      } else {
 | 
						|
        stream.skipToEnd();
 | 
						|
      }
 | 
						|
 | 
						|
      return "comment";
 | 
						|
    };
 | 
						|
  }
 | 
						|
 | 
						|
  function buildStringTokenizer(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 !== "\\"));
 | 
						|
 | 
						|
      if (endingString) {
 | 
						|
        if (nextChar !== quote && greedy) { stream.next(); }
 | 
						|
        if (isEndLine(stream)) {
 | 
						|
          state.cursorHalf = 0;
 | 
						|
        }
 | 
						|
        state.tokenizer = tokenBase;
 | 
						|
        return "string";
 | 
						|
      } else if (nextChar === "#" && peekChar === "{") {
 | 
						|
        state.tokenizer = buildInterpolationTokenizer(stringTokenizer);
 | 
						|
        stream.next();
 | 
						|
        return "operator";
 | 
						|
      } else {
 | 
						|
        return "string";
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return stringTokenizer;
 | 
						|
  }
 | 
						|
 | 
						|
  function buildInterpolationTokenizer(currentTokenizer) {
 | 
						|
    return function(stream, state) {
 | 
						|
      if (stream.peek() === "}") {
 | 
						|
        stream.next();
 | 
						|
        state.tokenizer = currentTokenizer;
 | 
						|
        return "operator";
 | 
						|
      } else {
 | 
						|
        return tokenBase(stream, state);
 | 
						|
      }
 | 
						|
    };
 | 
						|
  }
 | 
						|
 | 
						|
  function indent(state) {
 | 
						|
    if (state.indentCount == 0) {
 | 
						|
      state.indentCount++;
 | 
						|
      var lastScopeOffset = state.scopes[0].offset;
 | 
						|
      var currentOffset = lastScopeOffset + config.indentUnit;
 | 
						|
      state.scopes.unshift({ offset:currentOffset });
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  function dedent(state) {
 | 
						|
    if (state.scopes.length == 1) return;
 | 
						|
 | 
						|
    state.scopes.shift();
 | 
						|
  }
 | 
						|
 | 
						|
  function tokenBase(stream, state) {
 | 
						|
    var ch = stream.peek();
 | 
						|
 | 
						|
    // Comment
 | 
						|
    if (stream.match("/*")) {
 | 
						|
      state.tokenizer = comment(stream.indentation(), true);
 | 
						|
      return state.tokenizer(stream, state);
 | 
						|
    }
 | 
						|
    if (stream.match("//")) {
 | 
						|
      state.tokenizer = comment(stream.indentation(), false);
 | 
						|
      return state.tokenizer(stream, state);
 | 
						|
    }
 | 
						|
 | 
						|
    // Interpolation
 | 
						|
    if (stream.match("#{")) {
 | 
						|
      state.tokenizer = buildInterpolationTokenizer(tokenBase);
 | 
						|
      return "operator";
 | 
						|
    }
 | 
						|
 | 
						|
    // Strings
 | 
						|
    if (ch === '"' || ch === "'") {
 | 
						|
      stream.next();
 | 
						|
      state.tokenizer = buildStringTokenizer(ch);
 | 
						|
      return "string";
 | 
						|
    }
 | 
						|
 | 
						|
    if(!state.cursorHalf){// state.cursorHalf === 0
 | 
						|
    // first half i.e. before : for key-value pairs
 | 
						|
    // including selectors
 | 
						|
 | 
						|
      if (ch === "-") {
 | 
						|
        if (stream.match(/^-\w+-/)) {
 | 
						|
          return "meta";
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (ch === ".") {
 | 
						|
        stream.next();
 | 
						|
        if (stream.match(/^[\w-]+/)) {
 | 
						|
          indent(state);
 | 
						|
          return "qualifier";
 | 
						|
        } else if (stream.peek() === "#") {
 | 
						|
          indent(state);
 | 
						|
          return "tag";
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (ch === "#") {
 | 
						|
        stream.next();
 | 
						|
        // ID selectors
 | 
						|
        if (stream.match(/^[\w-]+/)) {
 | 
						|
          indent(state);
 | 
						|
          return "builtin";
 | 
						|
        }
 | 
						|
        if (stream.peek() === "#") {
 | 
						|
          indent(state);
 | 
						|
          return "tag";
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      // Variables
 | 
						|
      if (ch === "$") {
 | 
						|
        stream.next();
 | 
						|
        stream.eatWhile(/[\w-]/);
 | 
						|
        return "variable-2";
 | 
						|
      }
 | 
						|
 | 
						|
      // 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";
 | 
						|
      }
 | 
						|
 | 
						|
      if (ch === "=") {
 | 
						|
        // Match shortcut mixin definition
 | 
						|
        if (stream.match(/^=[\w-]+/)) {
 | 
						|
          indent(state);
 | 
						|
          return "meta";
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (ch === "+") {
 | 
						|
        // Match shortcut mixin definition
 | 
						|
        if (stream.match(/^\+[\w-]+/)){
 | 
						|
          return "variable-3";
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if(ch === "@"){
 | 
						|
        if(stream.match('@extend')){
 | 
						|
          if(!stream.match(/\s*[\w]/))
 | 
						|
            dedent(state);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
 | 
						|
      // Indent Directives
 | 
						|
      if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)) {
 | 
						|
        indent(state);
 | 
						|
        return "def";
 | 
						|
      }
 | 
						|
 | 
						|
      // Other Directives
 | 
						|
      if (ch === "@") {
 | 
						|
        stream.next();
 | 
						|
        stream.eatWhile(/[\w-]/);
 | 
						|
        return "def";
 | 
						|
      }
 | 
						|
 | 
						|
      if (stream.eatWhile(/[\w-]/)){
 | 
						|
        if(stream.match(/ *: *[\w-\+\$#!\("']/,false)){
 | 
						|
          word = stream.current().toLowerCase();
 | 
						|
          var prop = state.prevProp + "-" + word;
 | 
						|
          if (propertyKeywords.hasOwnProperty(prop)) {
 | 
						|
            return "property";
 | 
						|
          } else if (propertyKeywords.hasOwnProperty(word)) {
 | 
						|
            state.prevProp = word;
 | 
						|
            return "property";
 | 
						|
          } else if (fontProperties.hasOwnProperty(word)) {
 | 
						|
            return "property";
 | 
						|
          }
 | 
						|
          return "tag";
 | 
						|
        }
 | 
						|
        else if(stream.match(/ *:/,false)){
 | 
						|
          indent(state);
 | 
						|
          state.cursorHalf = 1;
 | 
						|
          state.prevProp = stream.current().toLowerCase();
 | 
						|
          return "property";
 | 
						|
        }
 | 
						|
        else if(stream.match(/ *,/,false)){
 | 
						|
          return "tag";
 | 
						|
        }
 | 
						|
        else{
 | 
						|
          indent(state);
 | 
						|
          return "tag";
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if(ch === ":"){
 | 
						|
        if (stream.match(pseudoElementsRegexp)){ // could be a pseudo-element
 | 
						|
          return "variable-3";
 | 
						|
        }
 | 
						|
        stream.next();
 | 
						|
        state.cursorHalf=1;
 | 
						|
        return "operator";
 | 
						|
      }
 | 
						|
 | 
						|
    } // cursorHalf===0 ends here
 | 
						|
    else{
 | 
						|
 | 
						|
      if (ch === "#") {
 | 
						|
        stream.next();
 | 
						|
        // Hex numbers
 | 
						|
        if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)){
 | 
						|
          if (isEndLine(stream)) {
 | 
						|
            state.cursorHalf = 0;
 | 
						|
          }
 | 
						|
          return "number";
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      // Numbers
 | 
						|
      if (stream.match(/^-?[0-9\.]+/)){
 | 
						|
        if (isEndLine(stream)) {
 | 
						|
          state.cursorHalf = 0;
 | 
						|
        }
 | 
						|
        return "number";
 | 
						|
      }
 | 
						|
 | 
						|
      // Units
 | 
						|
      if (stream.match(/^(px|em|in)\b/)){
 | 
						|
        if (isEndLine(stream)) {
 | 
						|
          state.cursorHalf = 0;
 | 
						|
        }
 | 
						|
        return "unit";
 | 
						|
      }
 | 
						|
 | 
						|
      if (stream.match(keywordsRegexp)){
 | 
						|
        if (isEndLine(stream)) {
 | 
						|
          state.cursorHalf = 0;
 | 
						|
        }
 | 
						|
        return "keyword";
 | 
						|
      }
 | 
						|
 | 
						|
      if (stream.match(/^url/) && stream.peek() === "(") {
 | 
						|
        state.tokenizer = urlTokens;
 | 
						|
        if (isEndLine(stream)) {
 | 
						|
          state.cursorHalf = 0;
 | 
						|
        }
 | 
						|
        return "atom";
 | 
						|
      }
 | 
						|
 | 
						|
      // Variables
 | 
						|
      if (ch === "$") {
 | 
						|
        stream.next();
 | 
						|
        stream.eatWhile(/[\w-]/);
 | 
						|
        if (isEndLine(stream)) {
 | 
						|
          state.cursorHalf = 0;
 | 
						|
        }
 | 
						|
        return "variable-2";
 | 
						|
      }
 | 
						|
 | 
						|
      // bang character for !important, !default, etc.
 | 
						|
      if (ch === "!") {
 | 
						|
        stream.next();
 | 
						|
        state.cursorHalf = 0;
 | 
						|
        return stream.match(/^[\w]+/) ? "keyword": "operator";
 | 
						|
      }
 | 
						|
 | 
						|
      if (stream.match(opRegexp)){
 | 
						|
        if (isEndLine(stream)) {
 | 
						|
          state.cursorHalf = 0;
 | 
						|
        }
 | 
						|
        return "operator";
 | 
						|
      }
 | 
						|
 | 
						|
      // attributes
 | 
						|
      if (stream.eatWhile(/[\w-]/)) {
 | 
						|
        if (isEndLine(stream)) {
 | 
						|
          state.cursorHalf = 0;
 | 
						|
        }
 | 
						|
        word = stream.current().toLowerCase();
 | 
						|
        if (valueKeywords.hasOwnProperty(word)) {
 | 
						|
          return "atom";
 | 
						|
        } else if (colorKeywords.hasOwnProperty(word)) {
 | 
						|
          return "keyword";
 | 
						|
        } else if (propertyKeywords.hasOwnProperty(word)) {
 | 
						|
          state.prevProp = stream.current().toLowerCase();
 | 
						|
          return "property";
 | 
						|
        } else {
 | 
						|
          return "tag";
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      //stream.eatSpace();
 | 
						|
      if (isEndLine(stream)) {
 | 
						|
        state.cursorHalf = 0;
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
 | 
						|
    } // else ends here
 | 
						|
 | 
						|
    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;
 | 
						|
  }
 | 
						|
 | 
						|
  function tokenLexer(stream, state) {
 | 
						|
    if (stream.sol()) state.indentCount = 0;
 | 
						|
    var style = state.tokenizer(stream, state);
 | 
						|
    var current = stream.current();
 | 
						|
 | 
						|
    if (current === "@return" || current === "}"){
 | 
						|
      dedent(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"}],
 | 
						|
        indentCount: 0,
 | 
						|
        cursorHalf: 0,  // cursor half tells us if cursor lies after (1)
 | 
						|
                        // or before (0) colon (well... more or less)
 | 
						|
        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;
 | 
						|
    },
 | 
						|
 | 
						|
    blockCommentStart: "/*",
 | 
						|
    blockCommentEnd: "*/",
 | 
						|
    lineComment: "//",
 | 
						|
    fold: "indent"
 | 
						|
  };
 | 
						|
}, "css");
 | 
						|
 | 
						|
CodeMirror.defineMIME("text/x-sass", "sass");
 | 
						|
 | 
						|
});
 |