Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/codemirror/mode/less.js
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6LESS mode - http://www.lesscss.org/7Ported to CodeMirror by Peter Kroon <[email protected]>8Report bugs/issues here: https://github.com/marijnh/CodeMirror/issues9GitHub: @peterkroon10*/1112import * as CodeMirror from "codemirror";1314CodeMirror.defineMode("less", function(config) {15var indentUnit = config.indentUnit, type;16function ret(style, tp) {type = tp; return style;}1718var selectors = /(^\:root$|^\:nth\-child$|^\:nth\-last\-child$|^\:nth\-of\-type$|^\:nth\-last\-of\-type$|^\:first\-child$|^\:last\-child$|^\:first\-of\-type$|^\:last\-of\-type$|^\:only\-child$|^\:only\-of\-type$|^\:empty$|^\:link|^\:visited$|^\:active$|^\:hover$|^\:focus$|^\:target$|^\:lang$|^\:enabled^\:disabled$|^\:checked$|^\:first\-line$|^\:first\-letter$|^\:before$|^\:after$|^\:not$|^\:required$|^\:invalid$)/;1920function tokenBase(stream, state) {21var ch = stream.next();2223if (ch == "@") {stream.eatWhile(/[\w\-]/); return ret("meta", stream.current());}24else if (ch == "/" && stream.eat("*")) {25state.tokenize = tokenCComment;26return tokenCComment(stream, state);27} else if (ch == "<" && stream.eat("!")) {28state.tokenize = tokenSGMLComment;29return tokenSGMLComment(stream, state);30} else if (ch == "=") ret(null, "compare");31else if (ch == "|" && stream.eat("=")) return ret(null, "compare");32else if (ch == "\"" || ch == "'") {33state.tokenize = tokenString(ch);34return state.tokenize(stream, state);35} else if (ch == "/") { // e.g.: .png will not be parsed as a class36if(stream.eat("/")){37state.tokenize = tokenSComment;38return tokenSComment(stream, state);39} else {40if(type == "string" || type == "(") return ret("string", "string");41if(state.stack[state.stack.length-1] !== undefined) return ret(null, ch);42stream.eatWhile(/[\a-zA-Z0-9\-_.\s]/);43if( /\/|\)|#/.test(stream.peek() || (stream.eatSpace() && stream.peek() === ")")) || stream.eol() )return ret("string", "string"); // let url(/images/logo.png) without quotes return as string44}45} else if (ch == "!") {46stream.match(/^\s*\w*/);47return ret("keyword", "important");48} else if (/\d/.test(ch)) {49stream.eatWhile(/[\w.%]/);50return ret("number", "unit");51} else if (/[,+<>*\/]/.test(ch)) {52if(stream.peek() == "=" || type == "a")return ret("string", "string");53if(ch === ",")return ret(null, ch);54return ret(null, "select-op");55} else if (/[;{}:\[\]()~\|]/.test(ch)) {56if(ch == ":"){57stream.eatWhile(/[a-z\\\-]/);58if( selectors.test(stream.current()) ){59return ret("tag", "tag");60} else if(stream.peek() == ":"){//::-webkit-search-decoration61stream.next();62stream.eatWhile(/[a-z\\\-]/);63if(stream.current().match(/\:\:\-(o|ms|moz|webkit)\-/))return ret("string", "string");64if( selectors.test(stream.current().substring(1)) )return ret("tag", "tag");65return ret(null, ch);66} else {67return ret(null, ch);68}69} else if(ch == "~"){70if(type == "r")return ret("string", "string");71} else {72return ret(null, ch);73}74} else if (ch == ".") {75if(type == "(")return ret("string", "string"); // allow url(../image.png)76stream.eatWhile(/[\a-zA-Z0-9\-_]/);77if(stream.peek() === " ")stream.eatSpace();78if(stream.peek() === ")" || type === ":")return ret("number", "unit");//rgba(0,0,0,.25);79else if(stream.current().length >1){80if(state.stack[state.stack.length-1] === "rule" && stream.peek().match(/{|,|\+|\(/) === null)return ret("number", "unit");81}82return ret("tag", "tag");83} else if (ch == "#") {84//we don't eat white-space, we want the hex color and or id only85stream.eatWhile(/[A-Za-z0-9]/);86//check if there is a proper hex color length e.g. #eee || #eeeEEE87if(stream.current().length == 4 || stream.current().length == 7){88if(stream.current().match(/[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/,false) != null){//is there a valid hex color value present in the current stream89//when not a valid hex value, parse as id90if(stream.current().substring(1) != stream.current().match(/[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/,false))return ret("atom", "tag");91//eat white-space92stream.eatSpace();93//when hex value declaration doesn't end with [;,] but is does with a slash/cc comment treat it as an id, just like the other hex values that don't end with[;,]94if( /[\/<>.(){!$%^&*_\-\\?=+\|#'~`]/.test(stream.peek()) ){95if(type === "select-op")return ret("number", "unit"); else return ret("atom", "tag");96}97//#time { color: #aaa }98else if(stream.peek() == "}" )return ret("number", "unit");99//we have a valid hex color value, parse as id whenever an element/class is defined after the hex(id) value e.g. #eee aaa || #eee .aaa100else if( /[a-zA-Z\\]/.test(stream.peek()) )return ret("atom", "tag");101//when a hex value is on the end of a line, parse as id102else if(stream.eol())return ret("atom", "tag");103//default104else return ret("number", "unit");105} else {//when not a valid hexvalue in the current stream e.g. #footer106stream.eatWhile(/[\w\\\-]/);107return ret("atom", stream.current());108}109} else {//when not a valid hexvalue length110stream.eatWhile(/[\w\\\-]/);111if(state.stack[state.stack.length-1] === "rule")return ret("atom", stream.current());return ret("atom", stream.current());112return ret("atom", "tag");113}114} else if (ch == "&") {115stream.eatWhile(/[\w\-]/);116return ret(null, ch);117} else {118stream.eatWhile(/[\w\\\-_%.{]/);119if(stream.current().match(/\\/) !== null){120if(stream.current().charAt(stream.current().length-1) === "\\"){121stream.eat(/\'|\"|\)|\(/);122while(stream.eatWhile(/[\w\\\-_%.{]/)){123stream.eat(/\'|\"|\)|\(/);124}125return ret("string", stream.current());126}127} //else if(type === "tag")return ret("tag", "tag");128else if(type == "string"){129if(state.stack[state.stack.length-1] === "{" && stream.peek() === ":")return ret("variable", "variable");130if(stream.peek() === "/")stream.eatWhile(/[\w\\\-_%.{:\/]/);131return ret(type, stream.current());132} else if(stream.current().match(/(^http$|^https$)/) != null){133stream.eatWhile(/[\w\\\-_%.{:\/]/);134if(stream.peek() === "/")stream.eatWhile(/[\w\\\-_%.{:\/]/);135return ret("string", "string");136} else if(stream.peek() == "<" || stream.peek() == ">" || stream.peek() == "+"){137if(type === "(" && (stream.current() === "n" || stream.current() === "-n"))return ret("string", stream.current());138return ret("tag", "tag");139} else if( /\(/.test(stream.peek()) ){140if(stream.current() === "when")return ret("variable","variable");141else if(state.stack[state.stack.length-1] === "@media" && stream.current() === "and")return ret("variable",stream.current());142return ret(null, ch);143} else if (stream.peek() == "/" && state.stack[state.stack.length-1] !== undefined){ // url(dir/center/image.png)144if(stream.peek() === "/")stream.eatWhile(/[\w\\\-_%.{:\/]/);145return ret("string", stream.current());146} else if( stream.current().match(/\-\d|\-.\d/) ){ // match e.g.: -5px -0.4 etc... only colorize the minus sign147//commment out these 2 comment if you want the minus sign to be parsed as null -500px148//stream.backUp(stream.current().length-1);149//return ret(null, ch);150return ret("number", "unit");151} else if( /\/|[\s\)]/.test(stream.peek() || stream.eol() || (stream.eatSpace() && stream.peek() == "/")) && stream.current().indexOf(".") !== -1){152if(stream.current().substring(stream.current().length-1,stream.current().length) == "{"){153stream.backUp(1);154return ret("tag", "tag");155}//end if156stream.eatSpace();157if( /[{<>.a-zA-Z\/]/.test(stream.peek()) || stream.eol() )return ret("tag", "tag"); // e.g. button.icon-plus158return ret("string", "string"); // let url(/images/logo.png) without quotes return as string159} else if( stream.eol() || stream.peek() == "[" || stream.peek() == "#" || type == "tag" ){160161if(stream.current().substring(stream.current().length-1,stream.current().length) == "{")stream.backUp(1);162else if(state.stack[state.stack.length-1] === "border-color" || state.stack[state.stack.length-1] === "background-position" || state.stack[state.stack.length-1] === "font-family")return ret(null, stream.current());163else if(type === "tag")return ret("tag", "tag");164else if((type === ":" || type === "unit") && state.stack[state.stack.length-1] === "rule")return ret(null, stream.current());165else if(state.stack[state.stack.length-1] === "rule" && type === "tag")return ret("string", stream.current());166else if(state.stack[state.stack.length-1] === ";" && type === ":")return ret(null, stream.current());167//else if(state.stack[state.stack.length-1] === ";" || type === "")return ret("variable", stream.current());168else if(stream.peek() === "#" && type !== undefined && type.match(/\+|,|tag|select\-op|}|{|;/g) === null)return ret("string", stream.current());169else if(type === "variable")return ret(null, stream.current());170else if(state.stack[state.stack.length-1] === "{" && type === "comment")return ret("variable", stream.current());171else if(state.stack.length === 0 && (type === ";" || type === "comment"))return ret("tag", stream.current());172else if((state.stack[state.stack.length-1] === "{" || type === ";") && state.stack[state.stack.length-1] !== "@media{")return ret("variable", stream.current());173else if(state.stack[state.stack.length-2] === "{" && state.stack[state.stack.length-1] === ";")return ret("variable", stream.current());174175return ret("tag", "tag");176} else if(type == "compare" || type == "a" || type == "("){177return ret("string", "string");178} else if(type == "|" || stream.current() == "-" || type == "["){179if(type == "|" && stream.peek().match(/\]|=|\~/) !== null)return ret("number", stream.current());180else if(type == "|" )return ret("tag", "tag");181else if(type == "["){182stream.eatWhile(/\w\-/);183return ret("number", stream.current());184}185return ret(null, ch);186} else if((stream.peek() == ":") || ( stream.eatSpace() && stream.peek() == ":")) {187stream.next();188var t_v = stream.peek() == ":" ? true : false;189if(!t_v){190var old_pos = stream.pos;191var sc = stream.current().length;192stream.eatWhile(/[a-z\\\-]/);193var new_pos = stream.pos;194if(stream.current().substring(sc-1).match(selectors) != null){195stream.backUp(new_pos-(old_pos-1));196return ret("tag", "tag");197} else stream.backUp(new_pos-(old_pos-1));198} else {199stream.backUp(1);200}201if(t_v)return ret("tag", "tag"); else return ret("variable", "variable");202} else if(state.stack[state.stack.length-1] === "font-family" || state.stack[state.stack.length-1] === "background-position" || state.stack[state.stack.length-1] === "border-color"){203return ret(null, null);204} else {205206if(state.stack[state.stack.length-1] === null && type === ":")return ret(null, stream.current());207208//else if((type === ")" && state.stack[state.stack.length-1] === "rule") || (state.stack[state.stack.length-2] === "{" && state.stack[state.stack.length-1] === "rule" && type === "variable"))return ret(null, stream.current());209210else if(/\^|\$/.test(stream.current()) && stream.peek().match(/\~|=/) !== null)return ret("string", "string");//att^=val211212else if(type === "unit" && state.stack[state.stack.length-1] === "rule")return ret(null, "unit");213else if(type === "unit" && state.stack[state.stack.length-1] === ";")return ret(null, "unit");214else if(type === ")" && state.stack[state.stack.length-1] === "rule")return ret(null, "unit");215else if(type && type.match("@") !== null && state.stack[state.stack.length-1] === "rule")return ret(null, "unit");216//else if(type === "unit" && state.stack[state.stack.length-1] === "rule")return ret(null, stream.current());217218else if((type === ";" || type === "}" || type === ",") && state.stack[state.stack.length-1] === ";")return ret("tag", stream.current());219else if((type === ";" && stream.peek() !== undefined && stream.peek().match(/{|./) === null) || (type === ";" && stream.eatSpace() && stream.peek().match(/{|./) === null))return ret("variable", stream.current());220else if((type === "@media" && state.stack[state.stack.length-1] === "@media") || type === "@namespace")return ret("tag", stream.current());221222else if(type === "{" && state.stack[state.stack.length-1] === ";" && stream.peek() === "{")return ret("tag", "tag");223else if((type === "{" || type === ":") && state.stack[state.stack.length-1] === ";")return ret(null, stream.current());224else if((state.stack[state.stack.length-1] === "{" && stream.eatSpace() && stream.peek().match(/.|#/) === null) || type === "select-op" || (state.stack[state.stack.length-1] === "rule" && type === ",") )return ret("tag", "tag");225else if(type === "variable" && state.stack[state.stack.length-1] === "rule")return ret("tag", "tag");226else if((stream.eatSpace() && stream.peek() === "{") || stream.eol() || stream.peek() === "{")return ret("tag", "tag");227//this one messes up indentation228//else if((type === "}" && stream.peek() !== ":") || (type === "}" && stream.eatSpace() && stream.peek() !== ":"))return(type, "tag");229230else if(type === ")" && (stream.current() == "and" || stream.current() == "and "))return ret("variable", "variable");231else if(type === ")" && (stream.current() == "when" || stream.current() == "when "))return ret("variable", "variable");232else if(type === ")" || type === "comment" || type === "{")return ret("tag", "tag");233else if(stream.sol())return ret("tag", "tag");234else if((stream.eatSpace() && stream.peek() === "#") || stream.peek() === "#")return ret("tag", "tag");235else if(state.stack.length === 0)return ret("tag", "tag");236else if(type === ";" && stream.peek() !== undefined && stream.peek().match(/^[.|\#]/g) !== null)return ret("tag", "tag");237238else if(type === ":"){stream.eatSpace();return ret(null, stream.current());}239240else if(stream.current() === "and " || stream.current() === "and")return ret("variable", stream.current());241else if(type === ";" && state.stack[state.stack.length-1] === "{")return ret("variable", stream.current());242243else if(state.stack[state.stack.length-1] === "rule")return ret(null, stream.current());244245return ret("tag", stream.current());246}247}248}249250function tokenSComment(stream, state) { // SComment = Slash comment251stream.skipToEnd();252state.tokenize = tokenBase;253return ret("comment", "comment");254}255256function tokenCComment(stream, state) {257var maybeEnd = false, ch;258while ((ch = stream.next()) != null) {259if (maybeEnd && ch == "/") {260state.tokenize = tokenBase;261break;262}263maybeEnd = (ch == "*");264}265return ret("comment", "comment");266}267268function tokenSGMLComment(stream, state) {269var dashes = 0, ch;270while ((ch = stream.next()) != null) {271if (dashes >= 2 && ch == ">") {272state.tokenize = tokenBase;273break;274}275dashes = (ch == "-") ? dashes + 1 : 0;276}277return ret("comment", "comment");278}279280function tokenString(quote) {281return function(stream, state) {282var escaped = false, ch;283while ((ch = stream.next()) != null) {284if (ch == quote && !escaped)285break;286escaped = !escaped && ch == "\\";287}288if (!escaped) state.tokenize = tokenBase;289return ret("string", "string");290};291}292293return {294startState: function(base) {295return {tokenize: tokenBase,296baseIndent: base || 0,297stack: []};298},299300token: function(stream, state) {301if (stream.eatSpace()) return null;302var style = state.tokenize(stream, state);303304var context = state.stack[state.stack.length-1];305if (type == "hash" && context == "rule") style = "atom";306else if (style == "variable") {307if (context == "rule") style = null; //"tag"308else if (!context || context == "@media{") {309style = stream.current() == "when" ? "variable" :310/[\s,|\s\)|\s]/.test(stream.peek()) ? "tag" : type;311}312}313314if (context == "rule" && /^[\{\};]$/.test(type))315state.stack.pop();316if (type == "{") {317if (context == "@media") state.stack[state.stack.length-1] = "@media{";318else state.stack.push("{");319}320else if (type == "}") state.stack.pop();321else if (type == "@media") state.stack.push("@media");322else if (stream.current() === "font-family") state.stack[state.stack.length-1] = "font-family";323else if (stream.current() === "background-position") state.stack[state.stack.length-1] = "background-position";324else if (stream.current() === "border-color") state.stack[state.stack.length-1] = "border-color";325else if (context == "{" && type != "comment" && type !== "tag") state.stack.push("rule");326else if (stream.peek() === ":" && stream.current().match(/@|#/) === null) style = type;327if(type === ";" && (state.stack[state.stack.length-1] == "font-family" || state.stack[state.stack.length-1] == "background-position" || state.stack[state.stack.length-1] == "border-color"))state.stack[state.stack.length-1] = stream.current();328else if(type === "tag" && stream.peek() === ")" && stream.current().match(/\:/) === null){type = null; style = null;}329// ????330else if((type === "variable" && stream.peek() === ")") || (type === "variable" && stream.eatSpace() && stream.peek() === ")"))return ret(null,stream.current());331return style;332},333334indent: function(state, textAfter) {335var n = state.stack.length;336if (/^\}/.test(textAfter))337n -= state.stack[state.stack.length-1] === "rule" ? 2 : 1;338else if (state.stack[state.stack.length-2] === "{")339n -= state.stack[state.stack.length-1] === "rule" ? 1 : 0;340return state.baseIndent + n * indentUnit;341},342343electricChars: "}",344blockCommentStart: "/*",345blockCommentEnd: "*/",346lineComment: "//"347};348});349350CodeMirror.defineMIME("text/x-less", "less");351if (!CodeMirror.mimeModes.hasOwnProperty("text/css"))352CodeMirror.defineMIME("text/css", "less");353354355