UNPKG

nim-codemirror-mode

Version:
680 lines (626 loc) 14.4 kB
// CodeMirror mode based on PMunch's https://github.com/PMunch/nim-playground-frontend module.exports.nim = function nim(conf, parserConf) { let ERRORCLASS = "error"; function wordRegexp(words) { return new RegExp("^((" + words.join(")|(") + "))\\b"); } let operators = new RegExp( "\\=\\+\\-\\*\\/\\<\\>\\@\\$\\~\\&\\%\\|\\!\\?\\^\\:\\\\" ); let identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*"); let commonkeywords = [ "addr", "asm", "bind", "block", "break", "case", "cast", "concept", "const", "continue", "converter", "defer", "discard", "distinct", "do", "elif", "else", "end", "enum", "except", "export", "finally", "for", "from", "func", "if", "import", "include", "interface", "iterator", "let", "macro", "method", "mixin", "nil", "object", "out", "proc", "ptr", "raise", "ref", "return", "shared", "static", "template", "try", "tuple", "type", "using", "var", "when", "while", "with", "without", "yield", // keyword operators "shl", "shr", "and", "or", "xor", "not", "div", "mod", "is", "isnot", "in", "as", "of", ]; let commonBuiltins = [ "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float", "float32", "float64", "bool", "char", "string", "cstring", "pointer", "range", "array", "openarray", "seq", "set", "byte", "Natural", "Positive", "RootObj", "RootRef", "Conversion", "ByteAddress", "BiggestUInt", "BiggestInt", "BiggestFloat", "cchar", "cschar", "cshort", "cint", "csize", "cuchar", "cushort", "clong", "clonglong", "cfloat", "cdouble", "clongdouble", "cuint", "culong", "culonglong", "cchar", "cstringArray", "Endianness", "PFloat32", "PFloat64", "PInt64", "PInt32", "GC_Strategy", "File", "FileMode", "FileHandle", "isMainModule", "CompileDate", "CompileTime", "NimVersion", "NimMajor", "NimMinor", "NimPatch", "cpuEndian", "hostOS", "hostCPU", "Inf", "NegInf", "NaN", "QuitSuccess", "QuitFailure", "dbgLineHook", "stdin", "stdout", "stderr", "defined", "new", "high", "low", "sizeof", "succ", "pred", "inc", "dec", "newSeq", "len", "incl", "excl", "card", "ord", "chr", "ze", "ze64", "toU8", "toU16", "toU32", "abs", "min", "max", "add", "repr", "contains", "toFloat", "toBiggestFloat", "toInt", "toBiggestInt", "addQuitProc", "copy", "setLen", "newString", "zeroMem", "copyMem", "moveMem", "equalMem", "alloc", "alloc0", "realloc", "dealloc", "setLen", "assert", "swap", "getRefcount", "getCurrentException", "Msg", "getOccupiedMem", "getFreeMem", "getTotalMem", "isNil", "seqToPtr", "find", "pop", "GC_disable", "GC_enable", "GC_fullCollect", "GC_setStrategy", "GC_enableMarkAnd", "Sweep", "GC_disableMarkAnd", "Sweep", "GC_getStatistics", "GC_ref", "GC_ref", "GC_ref", "GC_unref", "GC_unref", "GC_unref", "quit", "OpenFile", "OpenFile", "CloseFile", "EndOfFile", "readChar", "FlushFile", "readFile", "write", "readLine", "writeln", "writeln", "getFileSize", "ReadBytes", "ReadChars", "readBuffer", "writeBytes", "writeChars", "writeBuffer", "setFilePos", "getFilePos", "fileHandle", "countdown", "countup", "items", "lines", "ashr", "owned", "TaintedString", "NimNode", "ForLoopStmt", "declared", "declaredInScope", "unsafeAddr", "reset", "typeof", "toOpenArray", "cmp", "compileOption", "Ordinal", "SomeOrdinal", "SomeFloat", "SomeInteger", "SomeSignedInt", "SomeUnsignedInt", "SomeNumber", "typeOfProc", "typeOfIter", "HSlice", "Slice", "sink", "lent", "typedesc", "void", "auto", "any", "untyped", "typed", "true", "false", // exceptions "Exception", "Defect", "CatchableError", "IOError", "EOFError", "OSError", "LibraryError", "ResourceExhaustedError", "ArithmeticError", "DivByZeroError", "OverflowError", "AccessViolationError", "AssertionError", "ValueError", "KeyError", "OutOfMemError", "IndexError", "FieldError", "RangeError", "StackOverflowError", "ReraiseError", "ObjectAssignmentError", "ObjectConversionError", "FloatingPointError", "FloatInvalidOpError", "FloatDivByZeroError", "FloatOverflowError", "FloatInexactError", "FloatUnderflowError", "DeadThreadError", "NilAccessError", ]; if (parserConf.extra_keywords != undefined) commonkeywords = commonkeywords.concat(parserConf.extra_keywords); if (parserConf.extra_builtins != undefined) commonBuiltins = commonBuiltins.concat(parserConf.extra_builtins); let keywords = wordRegexp(commonkeywords); let builtins = wordRegexp(commonBuiltins); let indentInfo = null; let stringPrefixes = new RegExp("^(('{3}|\"{3}|['\"]))", "i"); // tokenizers function tokenBase(stream, state) { // Handle scope changes if (stream.sol()) { let scopeOffset = state.scopes[0].offset; if (stream.eatSpace()) { let lineOffset = stream.indentation(); if (lineOffset > scopeOffset) { indentInfo = "indent"; } else if (lineOffset < scopeOffset) { indentInfo = "dedent"; } return null; } else { if (scopeOffset > 0) { dedent(stream, state); } } } if (stream.eatSpace()) return null; let ch = stream.peek(); // Handle Comments if (ch === "#") { stream.next(); if (stream.eat("[")) { state.tokenize = tokenComment; return tokenComment(stream, state); } else { stream.skipToEnd(); return "comment"; } } // Handle Number Literals if (stream.match(/^[0-9\.]/, false)) { let floatLiteral = false; // Floats if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; } if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; } if (stream.match(/^\.\d+/)) { floatLiteral = true; } if (floatLiteral) { // Float literals may be "imaginary" stream.eat(/J/i); return "number"; } // Integers let intLiteral = false; // Hex if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; } // Binary if (stream.match(/^0b[01]+/i)) { intLiteral = true; } // Octal if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; } // Decimal if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) { // Decimal literals may be "imaginary" stream.eat(/J/i); // TODO - Can you have imaginary longs? intLiteral = true; } // Zero by itself with no other piece of number. if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; } if (intLiteral) { // Integer literals may be "long" stream.eat(/L/i); return "number"; } } // Handle Strings if (stream.match(stringPrefixes)) { state.tokenize = tokenStringFactory(stream.current()); return state.tokenize(stream, state); } if (stream.match(operators)) return "operator"; if (stream.match(keywords)) return "keyword"; if (stream.match(builtins)) return "builtin"; if (stream.match(identifiers)) { if ( state.lastToken != null && state.lastToken.match( /proc|iterator|macro|template|class|converter|func/ ) ) { return "def"; } return "variable"; } // Handle non-detected items stream.next(); return null; } function tokenStringFactory(delimiter) { let singleline = delimiter.length == 1; let OUTCLASS = "string"; function tokenString(stream, state) { while (!stream.eol()) { stream.eatWhile(/[^'"\\]/); if (stream.eat("\\")) { stream.next(); if (singleline && stream.eol()) { return OUTCLASS; } } else if (stream.match(delimiter)) { state.tokenize = tokenBase; return OUTCLASS; } else { stream.eat(/['"]/); } } if (singleline) { if (parserConf.singleLineStringErrors) { return ERRORCLASS; } else { state.tokenize = tokenBase; } } return OUTCLASS; } tokenString.isString = true; return tokenString; } function indent(stream, state, type) { type = type || "nim"; let indentUnit = 0; if (type === "nim") { if (state.scopes[0].type !== "nim") { state.scopes[0].offset = stream.indentation(); return; } for (let i = 0; i < state.scopes.length; ++i) { if (state.scopes[i].type === "nim") { indentUnit = state.scopes[i].offset + conf.indentUnit; break; } } } else { indentUnit = stream.column() + stream.current().length; } state.scopes.unshift({ offset: indentUnit, type: type, }); } function dedent(stream, state, type) { type = type || "nim"; if (state.scopes.length == 1) return; if (state.scopes[0].type === "nim") { let _indent = stream.indentation(); let _indent_index = -1; for (let i = 0; i < state.scopes.length; ++i) { if (_indent === state.scopes[i].offset) { _indent_index = i; break; } } if (_indent_index === -1) { return true; } while (state.scopes[0].offset !== _indent) { state.scopes.shift(); } return false; } else { if (type === "nim") { state.scopes[0].offset = stream.indentation(); return false; } else { if (state.scopes[0].type != type) { return true; } state.scopes.shift(); return false; } } } function tokenComment(stream, state) { let maybeEnd = false, ch; while ((ch = stream.next())) { if (ch == "#" && maybeEnd) { state.tokenize = tokenBase; break; } maybeEnd = ch == "]"; } return "comment"; } function tokenLexer(stream, state) { indentInfo = null; let style = state.tokenize(stream, state); let current = stream.current(); // Handle '.' connected identifiers if (current === ".") { style = null; if (state.lastStyle === "meta") { // Apply 'meta' style to '.' connected identifiers when // appropriate. style = "meta"; } return style; } if ( (style === "variable" || style === "builtin") && state.lastStyle === "meta" ) { style = "meta"; } // Handle scope changes. if ( current.match(/return|break|continue|raise/) || (current === "discard" && stream.eol()) ) state.dedent += 1; if (current === "lambda" || current === "proc") state.lambda = true; let delimiter_index = "[({".indexOf(current); if (delimiter_index !== -1) { indent(stream, state, "])}".slice(delimiter_index, delimiter_index + 1)); } else if ( stream.eol() && current.match(/\=|\:|import|include|type|const|let|let/) ) { indent(stream, state); } if (indentInfo === "dedent") { if (dedent(stream, state)) { return ERRORCLASS; } } delimiter_index = "])}".indexOf(current); if (delimiter_index !== -1) { if (dedent(stream, state, current)) { return ERRORCLASS; } } if (state.dedent > 0 && stream.eol() && state.scopes[0].type == "nim") { if (state.scopes.length > 1) state.scopes.shift(); state.dedent -= 1; } return style; } let external = { startState: function (basecolumn) { return { tokenize: tokenBase, scopes: [{ offset: basecolumn || 0, type: "nim" }], lastStyle: null, lastToken: null, lambda: false, dedent: 0, }; }, token: function (stream, state) { let style = tokenLexer(stream, state); state.lastStyle = style; let current = stream.current(); if (current && style) state.lastToken = current; if (stream.eol() && state.lambda) state.lambda = false; return style; }, indent: function (state) { if (state.tokenize != tokenBase) return state.tokenize.isString ? CodeMirror.Pass : 0; return state.scopes[0].offset; }, lineComment: "#", blockCommentStart: "#[", blockCommentEnd: "]#", fold: "indent", }; return external; }; module.exports.register = (CodeMirror) => { CodeMirror.defineMode("nim", nim); CodeMirror.defineMIME("text/x-nim", "nim"); CodeMirror.modeInfo.push({ name: "Nim", mime: "text/x-nim", mode: "name", ext: ["nim"], }); };