From 9d0722bf2f961b3ca019069b575185774fcee9a6 Mon Sep 17 00:00:00 2001 From: Vitor Santos Costa Date: Fri, 22 Apr 2016 18:30:04 +0100 Subject: [PATCH] First step at a config file for CodeMirror: steal from Jan :) --- misc/editors/yap.js | 666 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 666 insertions(+) create mode 100644 misc/editors/yap.js diff --git a/misc/editors/yap.js b/misc/editors/yap.js new file mode 100644 index 000000000..7ba1db0de --- /dev/null +++ b/misc/editors/yap.js @@ -0,0 +1,666 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("prolog", function(cmConfig, parserConfig) { + + function chain(stream, state, f) { + state.tokenize = f; + return f(stream, state); + } + + /******************************* + * CONFIG DATA * + *******************************/ + + var config = { quasiQuotations: false, /* {|Syntax||Quotation|} */ + dicts: false, /* tag{k:v, ...} */ + unicodeEscape: true, /* \uXXXX and \UXXXXXXXX */ + multiLineQuoted: true, /* "...\n..." */ + groupedIntegers: false /* 10 000 or 10_000 */ + }; + + var quoteType = { '"': "string", + "'": "qatom", + "`": "bqstring" + }; + + var isSingleEscChar = /[abref\\'"nrtsv]/; + var isOctalDigit = /[0-7]/; + var isHexDigit = /[0-9a-fA-F]/; + + var isSymbolChar = /[-#$&*+./:<=>?@\\^~]/; /* Prolog glueing symbols chars */ + var isSoloChar = /[[\]{}(),;|!]/; /* Prolog solo chars */ + var isNeck = /^(:-|-->)$/; + var isControlOp = /^(,|;|->|\*->|\\+|\|)$/; + + + /******************************* + * CHARACTER ESCAPES * + *******************************/ + + function readDigits(stream, re, count) { + if ( count > 0 ) { + while( count-- > 0 ) { + if ( !re.test(stream.next()) ) + return false; + } + } else { + while ( re.test(stream.peek()) ) + stream.next(); + } + return true; + } + + function readEsc(stream) { + var next = stream.next(); + if ( isSingleEscChar.test(next) ) + return true; + switch( next ) + { case "u": + if ( config.unicodeEscape ) + return readDigits(stream, isHexDigit, 4); /* SWI */ + return false; + case "U": + if ( config.unicodeEscape ) + return readDigits(stream, isHexDigit, 8); /* SWI */ + return false; + case null: return true; /* end of line */ + case "c": stream.eatSpace(); return true; + case "x": return readDigits(stream, isHexDigit, 2); + } + if ( isOctalDigit.test(next) ) { + if ( !readDigits(stream, isOctalDigit, -1) ) + return false; + if ( stream.peek() == "\\" ) /* SWI: optional closing \ */ + stream.next(); + return true; + } + return false; + } + + function nextUntilUnescaped(stream, state, end) { + var next; + while ((next = stream.next()) != null) { + if ( next == end && end != stream.peek() ) + { state.nesting.pop(); + return false; + } + if ( next == "\\" ) + { if ( !readEsc(stream) ) + return false; + } + } + return config.multiLineQuoted; + } + + /******************************* + * CONTEXT NESTING * + *******************************/ + + function nesting(state) { + return state.nesting.slice(-1)[0]; + } + + /* Called on every non-comment token */ + function setArg1(state) { + var nest = nesting(state); + if ( nest ) { + if ( nest.arg == 0 ) /* nested in a compound */ + nest.arg = 1; + else if ( nest.type == "control" ) + state.goalStart = false; + } else + state.goalStart = false; + } + + function setArgAlignment(state) { + var nest = nesting(state); + if ( nest && !nest.alignment && nest.arg != undefined ) { + if ( nest.arg == 0 ) + nest.alignment = nest.leftCol ? nest.leftCol+4 : nest.column+4; + else + nest.alignment = nest.column+1; + } + } + + function nextArg(state) { + var nest = nesting(state); + if ( nest ) { + if ( nest.arg ) /* nested in a compound */ + nest.arg++; + else if ( nest.type == "control" ) + state.goalStart = true; /* FIXME: also needed for ; and -> */ + } else + state.goalStart = true; + } + + function isControl(state) { /* our terms are goals */ + var nest = nesting(state); + if ( nest ) { + if ( nest.type == "control" ) { + return true; + } + return false; + } else + return state.inBody; + } + + // Used as scratch variables to communicate multiple values without + // consing up tons of objects. + var type, content; + function ret(tp, style, cont) { + type = tp; content = cont; + return style; + } + + function peekSpace(stream) { /* TBD: handle block comment as space */ + if ( stream.eol() || + /[\s%]/.test(stream.peek()) ) + return true; + return false; + } + + + /******************************* + * SUB TOKENISERS * + *******************************/ + + function plTokenBase(stream, state) { + var ch = stream.next(); + + if ( ch == "(" ) { + if ( state.lastType == "functor" ) { + state.nesting.push({ functor: state.functorName, + column: stream.column(), + leftCol: state.functorColumn, + arg: 0 + }); + delete state.functorName; + delete state.functorColumn; + } else { + state.nesting.push({ type: "control", + closeColumn: stream.column(), + alignment: stream.column()+4 + }); + } + return ret("solo", null, "("); + } + + if ( ch == "{" && state.lastType == "tag" ) { + state.nesting.push({ tag: state.tagName, + column: stream.column(), + leftCol: state.tagColumn, + arg: 0 + }); + delete state.tagName; + delete state.tagColumn; + return ret("dict_open", null); + } + + if ( ch == "/" && stream.eat("*") ) + return chain(stream, state, plTokenComment); + + if ( ch == "%" ) { + stream.skipToEnd(); + return ret("comment", "comment"); + } + + setArg1(state); + + if ( isSoloChar.test(ch) ) { + switch ( ch ) + { case ")": + state.nesting.pop(); + break; + case "]": + state.nesting.pop(); + return ret("list_close", null); + case "}": + { var nest = nesting(state); + var type = (nest && nest.tag) ? "dict_close" : "brace_term_close"; + + state.nesting.pop(); + return ret(type, null); + } + case ",": + if ( stream.eol() ) + state.commaAtEOL = true; + nextArg(state); + /*FALLTHROUGH*/ + case ";": + if ( isControl(state) ) + state.goalStart = true; + break; + case "[": + state.nesting.push({ type: "list", + closeColumn: stream.column(), + alignment: stream.column()+2 + }); + return ret("list_open", null); + break; + case "{": + if ( config.quasiQuotations && stream.eat("|") ) { + state.nesting.push({ type: "quasi-quotation", + alignment: stream.column()+1 + }); + return ret("qq_open", "qq_open"); + } else { + state.nesting.push({ type: "curly", + closeColumn: stream.column(), + alignment: stream.column()+2 + }); + return ret("brace_term_open", null); + } + break; + case "|": + if ( config.quasiQuotations ) { + if ( stream.eat("|") ) { + state.tokenize = plTokenQuasiQuotation; + return ret("qq_sep", "qq_sep"); + } else if ( stream.eat("}") ) { + state.nesting.pop(); + return ret("qq_close", "qq_close"); + } + } + if ( isControl(state) ) + state.goalStart = true; + break; + } + return ret("solo", null, ch); + } + + if (ch == '"' || ch == "'" || ch == "`") + { state.nesting.push({ type: "quoted", + alignment: stream.column()+1 + }); + return chain(stream, state, plTokenString(ch)); + } + + if ( ch == "0" ) { + if ( stream.eat(/x/i)) { + stream.eatWhile(/[\da-f]/i); + return ret("number", "number"); + } + if ( stream.eat(/o/i)) { + stream.eatWhile(/[0-7]/i); + return ret("number", "number"); + } + if ( stream.eat(/'/) ) { /* 0' */ + var next = stream.next(); + if ( next == "\\" ) { + if ( !readEsc(stream) ) + return ret("error", "error"); + } + return ret("code", "code"); + } + } + + if ( /\d/.test(ch) || /[+-]/.test(ch) && stream.eat(/\d/)) { + if ( config.groupedIntegers ) + stream.match(/^\d*((_|\s+)\d+)*(?:\.\d+)?(?:[eE][+\-]?\d+)?/); + else + stream.match(/^\d*(?:\.\d+)?(?:[eE][+\-]?\d+)?/); + return ret(ch == "-" ? "neg-number" : + ch == "+" ? "pos-number" : + "number"); + } + + if ( isSymbolChar.test(ch) ) { + stream.eatWhile(isSymbolChar); + var atom = stream.current(); + if ( atom == "." && peekSpace(stream) ) { + if ( nesting(state) ) { + return ret("fullstop", "error", atom); + } else { + } return ret("fullstop", "fullstop", atom); + } else if ( isNeck.test(atom) ) { + return ret("neck", "neck", atom); + } else if ( isControl(state) && isControlOp.test(atom) ) { + state.goalStart = true; + return ret("symbol", "operator", atom); + } else + return ret("symbol", "operator", atom); + } + + stream.eatWhile(/[\w_]/); + var word = stream.current(); + if ( stream.peek() == "{" && config.dicts ) { + state.tagName = word; /* tmp state extension */ + state.tagColumn = stream.column(); + return ret("tag", "tag", word); + } else if ( ch == "_" ) { + if ( word.length == 1 ) { + return ret("var", "anon", word); + } else { + var sec = word.charAt(1); + if ( sec == sec.toUpperCase() ) + return ret("var", "var-2", word); + } + return ret("var", "var", word); + } else if ( ch == ch.toUpperCase() ) { + return ret("var", "var", word); + } else if ( stream.peek() == "(" ) { + state.functorName = word; /* tmp state extension */ + state.functorColumn = stream.column(); + return ret("functor", "functor", word); + } else + return ret("atom", "atom", word); + } + + function plTokenString(quote) { + return function(stream, state) { + if (!nextUntilUnescaped(stream, state, quote)) { + state.tokenize = plTokenBase; + if ( stream.peek() == "(" ) { /* 'quoted functor'() */ + var word = stream.current(); + state.functorName = word; /* tmp state extension */ + return ret("functor", "functor", word); + } + if ( stream.peek() == "{" && config.dicts ) { /* 'quoted tag'{} */ + var word = stream.current(); + state.tagName = word; /* tmp state extension */ + return ret("tag", "tag", word); + } + } + return ret(quoteType[quote], quoteType[quote]); + }; + } + + function plTokenQuasiQuotation(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "}" && maybeEnd) { + state.tokenize = plTokenBase; + stream.backUp(2); + break; + } + maybeEnd = (ch == "|"); + } + return ret("qq_content", "qq_content"); + } + + function plTokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = plTokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return ret("comment", "comment"); + } + + + /******************************* + * ACTIVE KEYS * + *******************************/ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Support if-then-else layout like this: + + goal :- + ( Condition + -> IfTrue + ; IfFalse + ). + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + CodeMirror.commands.prologStartIfThenElse = function(cm) { + var start = cm.getCursor("start"); + var token = cm.getTokenAt(start, true); + + if ( token.state.goalStart == true ) + { cm.replaceSelection("( ", "end"); + return; + } + + return CodeMirror.Pass; + } + + CodeMirror.commands.prologStartThen = function(cm) { + var start = cm.getCursor("start"); + var token = cm.getTokenAt(start, true); + + /* FIXME: These functions are copied from prolog.js. How + can we reuse these? + */ + function nesting(state) { + var len = state.nesting.length; + if ( len > 0 ) + return state.nesting[len-1]; + return null; + } + + function isControl(state) { /* our terms are goals */ + var nest = nesting(state); + if ( nest ) { + if ( nest.type == "control" ) { + return true; + } + return false; + } else + return state.inBody; + } + + if ( start.ch == token.end && + token.type == "operator" && + token.string == "-" && + isControl(token.state) ) + { cm.replaceSelection("> ", "end"); + return; + } + + return CodeMirror.Pass; + } + + CodeMirror.commands.prologStartElse = function(cm) { + var start = cm.getCursor("start"); + var token = cm.getTokenAt(start, true); + + if ( token.start == 0 && start.ch == token.end && + !/\S/.test(token.string) ) + { cm.replaceSelection("; ", "end"); + return; + } + + return CodeMirror.Pass; + } + + CodeMirror.defineOption("prologKeys", null, function(cm, val, prev) { + if (prev && prev != CodeMirror.Init) + cm.removeKeyMap("prolog"); + if ( val ) { + var map = { name: "prolog", + "'('": "prologStartIfThenElse", + "'>'": "prologStartThen", + "';'": "prologStartElse", + "Ctrl-L": "refreshHighlight" + }; + cm.addKeyMap(map); + } + }); + + }); + //Default (SWI-)Prolog operator table. To be used later to enhance the + //offline experience. + + var ops = { "-->": { p:1200, t:"xfx" }, + ":-": [ { p:1200, t:"xfx" }, + { p:1200, t:"fx" } + ], + "?-": { p:1200, t:"fx" }, + + "dynamic": { p:1150, t:"fx" }, + "discontiguous": { p:1150, t:"fx" }, + "initialization": { p:1150, t:"fx" }, + "meta_predicate": { p:1150, t:"fx" }, + "module_transparent": { p:1150, t:"fx" }, + "multifile": { p:1150, t:"fx" }, + "thread_local": { p:1150, t:"fx" }, + "volatile": { p:1150, t:"fx" }, + + ";": { p:1100, t:"xfy" }, + "|": { p:1100, t:"xfy" }, + + "->": { p:1050, t:"xfy" }, + "*->": { p:1050, t:"xfy" }, + + ",": { p:1000, t:"xfy" }, + + "\\+": { p:900, t:"fy" }, + + "~": { p:900, t:"fx" }, + + "<": { p:700, t:"xfx" }, + "=": { p:700, t:"xfx" }, + "=..": { p:700, t:"xfx" }, + "=@=": { p:700, t:"xfx" }, + "=:=": { p:700, t:"xfx" }, + "=<": { p:700, t:"xfx" }, + "==": { p:700, t:"xfx" }, + "=\\=": { p:700, t:"xfx" }, + ">": { p:700, t:"xfx" }, + ">=": { p:700, t:"xfx" }, + "@<": { p:700, t:"xfx" }, + "@=<": { p:700, t:"xfx" }, + "@>": { p:700, t:"xfx" }, + "@>=": { p:700, t:"xfx" }, + "\\=": { p:700, t:"xfx" }, + "\\==": { p:700, t:"xfx" }, + "is": { p:700, t:"xfx" }, + + ":": { p:600, t:"xfy" }, + + "+": [ { p:500, t:"yfx" }, + { p:200, t:"fy" } + ], + "-": [ { p:500, t:"yfx" }, + { p:200, t:"fy" } + ], + "/\\": { p:500, t:"yfx" }, + "\\/": { p:500, t:"yfx" }, + "xor": { p:500, t:"yfx" }, + + "?": { p:500, t:"fx" }, + + "*": { p:400, t:"yfx" }, + "/": { p:400, t:"yfx" }, + "//": { p:400, t:"yfx" }, + "rdiv": { p:400, t:"yfx" }, + "<<": { p:400, t:"yfx" }, + ">>": { p:400, t:"yfx" }, + "mod": { p:400, t:"yfx" }, + "rem": { p:400, t:"yfx" }, + + "**": { p:200, t:"xfx" }, + "^": { p:200, t:"xfy" }, + + "\\": { p:200, t:"fy" } + }; + + + /******************************* + * RETURN OBJECT * + *******************************/ + + return { + startState: function() { + return { + tokenize: plTokenBase, + inBody: false, + goalStart: false, + lastType: null, + nesting: new Array(), /* ([{}]) nesting FIXME: copy this */ + curTerm: null, /* term index in metainfo */ + curToken: null /* token in term */ + }; + }, + + token: function(stream, state) { + var nest; + + if ( state.curTerm == null && parserConfig.metainfo ) { + state.curTerm = 0; + state.curToken = 0; + } + + if ( stream.sol() ) + delete state.commaAtEOL; + + if ( state.tokenize == plTokenBase && stream.eatSpace() ) { + if ( stream.eol() ) + setArgAlignment(state); + return null; + } + + var style = state.tokenize(stream, state); + + if ( stream.eol() ) + setArgAlignment(state); + + if ( type == "neck" ) { + state.inBody = true; + state.goalStart = true; + } else if ( type == "fullstop" ) { + state.inBody = false; + state.goalStart = false; + } + + state.lastType = type; + + if ( typeof(parserConfig.enrich) == "function" ) + style = parserConfig.enrich(stream, state, type, content, style); + + return style; + }, + + indent: function(state, textAfter) { + if (state.tokenize == plTokenComment) return CodeMirror.Pass; + + var nest; + if ( (nest=nesting(state)) ) { + if ( nest.closeColumn && !state.commaAtEOL ) + return nest.closeColumn; + return nest.alignment; + } + if ( !state.inBody ) + return 0; + + return 4; + }, + + theme: "prolog", + + blockCommentStart: "/*", /* continuecomment.js support */ + blockCommentEnd: "*/", + blockCommentContinue: " * ", + lineComment: "%", + }; +}); + +CodeMirror.defineMIME("text/x-prolog", "prolog"); +}); +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict";