| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| <!doctype html> | ||
| <html> | ||
| <head> | ||
| <meta charset="utf-8"> | ||
| <title>CodeMirror: Q mode</title> | ||
| <link rel="stylesheet" href="../../lib/codemirror.css"> | ||
| <script src="../../lib/codemirror.js"></script> | ||
| <script src="../../addon/edit/matchbrackets.js"></script> | ||
| <script src="q.js"></script> | ||
| <link rel="stylesheet" href="../../doc/docs.css"> | ||
| <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style> | ||
| </head> | ||
| <body> | ||
| <h1>CodeMirror: Q mode</h1> | ||
|
|
||
| <div><textarea id="code" name="code"> | ||
| / utilities to quickly load a csv file - for more exhaustive analysis of the csv contents see csvguess.q | ||
| / 2009.09.20 - updated to match latest csvguess.q | ||
|
|
||
| / .csv.colhdrs[file] - return a list of colhdrs from file | ||
| / info:.csv.info[file] - return a table of information about the file | ||
| / columns are: | ||
| / c - column name; ci - column index; t - load type; mw - max width; | ||
| / dchar - distinct characters in values; rule - rule that caught the type | ||
| / maybe - needs checking, _could_ be say a date, but perhaps just a float? | ||
| / .csv.info0[file;onlycols] - like .csv.info except that it only analyses <onlycols> | ||
| / example: | ||
| / info:.csv.info0[file;(.csv.colhdrs file)like"*price"] | ||
| / info:.csv.infolike[file;"*price"] | ||
| / show delete from info where t=" " | ||
| / .csv.data[file;info] - use the info from .csv.info to read the data | ||
| / .csv.data10[file;info] - like .csv.data but only returns the first 10 rows | ||
| / bulkload[file;info] - bulk loads file into table DATA (which must be already defined :: DATA:() ) | ||
| / .csv.read[file]/read10[file] - for when you don't care about checking/tweaking the <info> before reading | ||
|
|
||
| \d .csv | ||
| DELIM:"," | ||
| ZAPHDRS:0b / lowercase and remove _ from colhdrs (junk characters are always removed) | ||
| WIDTHHDR:25000 / number of characters read to get the header | ||
| READLINES:222 / number of lines read and used to guess the types | ||
| SYMMAXWIDTH:11 / character columns narrower than this are stored as symbols | ||
| SYMMAXGR:10 / max symbol granularity% before we give up and keep as a * string | ||
| FORCECHARWIDTH:30 / every field (of any type) with values this wide or more is forced to character "*" | ||
| DISCARDEMPTY:0b / completely ignore empty columns if true else set them to "C" | ||
| CHUNKSIZE:50000000 / used in fs2 (modified .Q.fs) | ||
|
|
||
| k)nameltrim:{$[~@x;.z.s'x;~(*x)in aA:.Q.a,.Q.A;(+/&\~x in aA)_x;x]} | ||
| k)fs2:{[f;s]((-7!s)>){[f;s;x]i:1+last@&0xa=r:1:(s;x;CHUNKSIZE);f@`\:i#r;x+i}[f;s]/0j} | ||
| cleanhdrs:{{$[ZAPHDRS;lower x except"_";x]}x where x in DELIM,.Q.an} | ||
| cancast:{nw:x$"";if[not x in"BXCS";nw:(min 0#;max 0#;::)@\:nw];$[not any nw in x$(11&count y)#y;$[11<count y;not any nw in x$y;1b];0b]} | ||
|
|
||
| read:{[file]data[file;info[file]]} | ||
| read10:{[file]data10[file;info[file]]} | ||
|
|
||
| colhdrs:{[file] | ||
| `$nameltrim DELIM vs cleanhdrs first read0(file;0;1+first where 0xa=read1(file;0;WIDTHHDR))} | ||
| data:{[file;info] | ||
| (exec c from info where not t=" ")xcol(exec t from info;enlist DELIM)0:file} | ||
| data10:{[file;info] | ||
| data[;info](file;0;1+last 11#where 0xa=read1(file;0;15*WIDTHHDR))} | ||
| info0:{[file;onlycols] | ||
| colhdrs:`$nameltrim DELIM vs cleanhdrs first head:read0(file;0;1+last where 0xa=read1(file;0;WIDTHHDR)); | ||
| loadfmts:(count colhdrs)#"S";if[count onlycols;loadfmts[where not colhdrs in onlycols]:"C"]; | ||
| breaks:where 0xa=read1(file;0;floor(10+READLINES)*WIDTHHDR%count head); | ||
| nas:count as:colhdrs xcol(loadfmts;enlist DELIM)0:(file;0;1+last((1+READLINES)&count breaks)#breaks); | ||
| info:([]c:key flip as;v:value flip as);as:(); | ||
| reserved:key`.q;reserved,:.Q.res;reserved,:`i; | ||
| info:update res:c in reserved from info; | ||
| info:update ci:i,t:"?",ipa:0b,mdot:0,mw:0,rule:0,gr:0,ndv:0,maybe:0b,empty:0b,j10:0b,j12:0b from info; | ||
| info:update ci:`s#ci from info; | ||
| if[count onlycols;info:update t:" ",rule:10 from info where not c in onlycols]; | ||
| info:update sdv:{string(distinct x)except`}peach v from info; | ||
| info:update ndv:count each sdv from info; | ||
| info:update gr:floor 0.5+100*ndv%nas,mw:{max count each x}peach sdv from info where 0<ndv; | ||
| info:update t:"*",rule:20 from info where mw>.csv.FORCECHARWIDTH; / long values | ||
| info:update t:"C "[.csv.DISCARDEMPTY],rule:30,empty:1b from info where t="?",mw=0; / empty columns | ||
| info:update dchar:{asc distinct raze x}peach sdv from info where t="?"; | ||
| info:update mdot:{max sum each"."=x}peach sdv from info where t="?",{"."in x}each dchar; | ||
| info:update t:"n",rule:40 from info where t="?",{any x in"0123456789"}each dchar; / vaguely numeric.. | ||
| info:update t:"I",rule:50,ipa:1b from info where t="n",mw within 7 15,mdot=3,{all x in".0123456789"}each dchar,.csv.cancast["I"]peach sdv; / ip-address | ||
| info:update t:"J",rule:60 from info where t="n",mdot=0,{all x in"+-0123456789"}each dchar,.csv.cancast["J"]peach sdv; | ||
| info:update t:"I",rule:70 from info where t="J",mw<12,.csv.cancast["I"]peach sdv; | ||
| info:update t:"H",rule:80 from info where t="I",mw<7,.csv.cancast["H"]peach sdv; | ||
| info:update t:"F",rule:90 from info where t="n",mdot<2,mw>1,.csv.cancast["F"]peach sdv; | ||
| info:update t:"E",rule:100,maybe:1b from info where t="F",mw<9; | ||
| info:update t:"M",rule:110,maybe:1b from info where t in"nIHEF",mdot<2,mw within 4 7,.csv.cancast["M"]peach sdv; | ||
| info:update t:"D",rule:120,maybe:1b from info where t in"nI",mdot in 0 2,mw within 6 11,.csv.cancast["D"]peach sdv; | ||
| info:update t:"V",rule:130,maybe:1b from info where t="I",mw in 5 6,7<count each dchar,{all x like"*[0-9][0-5][0-9][0-5][0-9]"}peach sdv,.csv.cancast["V"]peach sdv; / 235959 12345 | ||
| info:update t:"U",rule:140,maybe:1b from info where t="H",mw in 3 4,7<count each dchar,{all x like"*[0-9][0-5][0-9]"}peach sdv,.csv.cancast["U"]peach sdv; /2359 | ||
| info:update t:"U",rule:150,maybe:0b from info where t="n",mw in 4 5,mdot=0,{all x like"*[0-9]:[0-5][0-9]"}peach sdv,.csv.cancast["U"]peach sdv; | ||
| info:update t:"T",rule:160,maybe:0b from info where t="n",mw within 7 12,mdot<2,{all x like"*[0-9]:[0-5][0-9]:[0-5][0-9]*"}peach sdv,.csv.cancast["T"]peach sdv; | ||
| info:update t:"V",rule:170,maybe:0b from info where t="T",mw in 7 8,mdot=0,.csv.cancast["V"]peach sdv; | ||
| info:update t:"T",rule:180,maybe:1b from info where t in"EF",mw within 7 10,mdot=1,{all x like"*[0-9][0-5][0-9][0-5][0-9].*"}peach sdv,.csv.cancast["T"]peach sdv; | ||
| info:update t:"Z",rule:190,maybe:0b from info where t="n",mw within 11 24,mdot<4,.csv.cancast["Z"]peach sdv; | ||
| info:update t:"P",rule:200,maybe:1b from info where t="n",mw within 12 29,mdot<4,{all x like"[12]*"}peach sdv,.csv.cancast["P"]peach sdv; | ||
| info:update t:"N",rule:210,maybe:1b from info where t="n",mw within 3 28,mdot=1,.csv.cancast["N"]peach sdv; | ||
| info:update t:"?",rule:220,maybe:0b from info where t="n"; / reset remaining maybe numeric | ||
| info:update t:"C",rule:230,maybe:0b from info where t="?",mw=1; / char | ||
| info:update t:"B",rule:240,maybe:0b from info where t in"HC",mw=1,mdot=0,{$[all x in"01tTfFyYnN";(any"0fFnN"in x)and any"1tTyY"in x;0b]}each dchar; / boolean | ||
| info:update t:"B",rule:250,maybe:1b from info where t in"HC",mw=1,mdot=0,{all x in"01tTfFyYnN"}each dchar; / boolean | ||
| info:update t:"X",rule:260,maybe:0b from info where t="?",mw=2,{$[all x in"0123456789abcdefABCDEF";(any .Q.n in x)and any"abcdefABCDEF"in x;0b]}each dchar; /hex | ||
| info:update t:"S",rule:270,maybe:1b from info where t="?",mw<.csv.SYMMAXWIDTH,mw>1,gr<.csv.SYMMAXGR; / symbols (max width permitting) | ||
| info:update t:"*",rule:280,maybe:0b from info where t="?"; / the rest as strings | ||
| / flag those S/* columns which could be encoded to integers (.Q.j10/x10/j12/x12) to avoid symbols | ||
| info:update j12:1b from info where t in"S*",mw<13,{all x in .Q.nA}each dchar; | ||
| info:update j10:1b from info where t in"S*",mw<11,{all x in .Q.b6}each dchar; | ||
| select c,ci,t,maybe,empty,res,j10,j12,ipa,mw,mdot,rule,gr,ndv,dchar from info} | ||
| info:info0[;()] / by default don't restrict columns | ||
| infolike:{[file;pattern] info0[file;{x where x like y}[lower colhdrs[file];pattern]]} / .csv.infolike[file;"*time"] | ||
|
|
||
| \d . | ||
| / DATA:() | ||
| bulkload:{[file;info] | ||
| if[not`DATA in system"v";'`DATA.not.defined]; | ||
| if[count DATA;'`DATA.not.empty]; | ||
| loadhdrs:exec c from info where not t=" ";loadfmts:exec t from info; | ||
| .csv.fs2[{[file;loadhdrs;loadfmts] `DATA insert $[count DATA;flip loadhdrs!(loadfmts;.csv.DELIM)0:file;loadhdrs xcol(loadfmts;enlist .csv.DELIM)0:file]}[file;loadhdrs;loadfmts]]; | ||
| count DATA} | ||
| @[.:;"\\l csvutil.custom.q";::]; / save your custom settings in csvutil.custom.q to override those set at the beginning of the file | ||
| </textarea></div> | ||
|
|
||
| <script> | ||
| var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | ||
| lineNumbers: true, | ||
| matchBrackets: true | ||
| }); | ||
| </script> | ||
|
|
||
| <p><strong>MIME type defined:</strong> <code>text/x-q</code>.</p> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| CodeMirror.defineMode("q",function(config){ | ||
| var indentUnit=config.indentUnit, | ||
| curPunc, | ||
| keywords=buildRE(["abs","acos","aj","aj0","all","and","any","asc","asin","asof","atan","attr","avg","avgs","bin","by","ceiling","cols","cor","cos","count","cov","cross","csv","cut","delete","deltas","desc","dev","differ","distinct","div","do","each","ej","enlist","eval","except","exec","exit","exp","fby","fills","first","fkeys","flip","floor","from","get","getenv","group","gtime","hclose","hcount","hdel","hopen","hsym","iasc","idesc","if","ij","in","insert","inter","inv","key","keys","last","like","list","lj","load","log","lower","lsq","ltime","ltrim","mavg","max","maxs","mcount","md5","mdev","med","meta","min","mins","mmax","mmin","mmu","mod","msum","neg","next","not","null","or","over","parse","peach","pj","plist","prd","prds","prev","prior","rand","rank","ratios","raze","read0","read1","reciprocal","reverse","rload","rotate","rsave","rtrim","save","scan","select","set","setenv","show","signum","sin","sqrt","ss","ssr","string","sublist","sum","sums","sv","system","tables","tan","til","trim","txf","type","uj","ungroup","union","update","upper","upsert","value","var","view","views","vs","wavg","where","where","while","within","wj","wj1","wsum","xasc","xbar","xcol","xcols","xdesc","xexp","xgroup","xkey","xlog","xprev","xrank"]), | ||
| E=/[|/&^!+:\\\-*%$=~#;@><,?_\'\"\[\(\]\)\s{}]/; | ||
| function buildRE(w){return new RegExp("^("+w.join("|")+")$");} | ||
| function tokenBase(stream,state){ | ||
| var sol=stream.sol(),c=stream.next(); | ||
| curPunc=null; | ||
| if(sol) | ||
| if(c=="/") | ||
| return(state.tokenize=tokenLineComment)(stream,state); | ||
| else if(c=="\\"){ | ||
| if(stream.eol()||/\s/.test(stream.peek())) | ||
| return stream.skipToEnd(),/^\\\s*$/.test(stream.current())?(state.tokenize=tokenCommentToEOF)(stream, state):state.tokenize=tokenBase,"comment"; | ||
| else | ||
| return state.tokenize=tokenBase,"builtin"; | ||
| } | ||
| if(/\s/.test(c)) | ||
| return stream.peek()=="/"?(stream.skipToEnd(),"comment"):"whitespace"; | ||
| if(c=='"') | ||
| return(state.tokenize=tokenString)(stream,state); | ||
| if(c=='`') | ||
| return stream.eatWhile(/[A-Z|a-z|\d|_|:|\/|\.]/),"symbol"; | ||
| if(("."==c&&/\d/.test(stream.peek()))||/\d/.test(c)){ | ||
| var t=null; | ||
| stream.backUp(1); | ||
| if(stream.match(/^\d{4}\.\d{2}(m|\.\d{2}([D|T](\d{2}(:\d{2}(:\d{2}(\.\d{1,9})?)?)?)?)?)/) | ||
| || stream.match(/^\d+D(\d{2}(:\d{2}(:\d{2}(\.\d{1,9})?)?)?)/) | ||
| || stream.match(/^\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?/) | ||
| || stream.match(/^\d+[ptuv]{1}/)) | ||
| t="temporal"; | ||
| else if(stream.match(/^0[NwW]{1}/) | ||
| || stream.match(/^0x[\d|a-f|A-F]*/) | ||
| || stream.match(/^[0|1]+[b]{1}/) | ||
| || stream.match(/^\d+[chijn]{1}/) | ||
| || stream.match(/-?\d*(\.\d*)?(e[+\-]?\d+)?(e|f)?/)) | ||
| t="number"; | ||
| return(t&&(!(c=stream.peek())||E.test(c)))?t:(stream.next(),"error"); | ||
| } | ||
| if(/[A-Z|a-z]|\./.test(c)) | ||
| return stream.eatWhile(/[A-Z|a-z|\.|_|\d]/),keywords.test(stream.current())?"keyword":"variable"; | ||
| if(/[|/&^!+:\\\-*%$=~#;@><\.,?_\']/.test(c)) | ||
| return null; | ||
| if(/[{}\(\[\]\)]/.test(c)) | ||
| return null; | ||
| return"error"; | ||
| } | ||
| function tokenLineComment(stream,state){ | ||
| return stream.skipToEnd(),/\/\s*$/.test(stream.current())?(state.tokenize=tokenBlockComment)(stream,state):(state.tokenize=tokenBase),"comment"; | ||
| } | ||
| function tokenBlockComment(stream,state){ | ||
| var f=stream.sol()&&stream.peek()=="\\"; | ||
| stream.skipToEnd(); | ||
| if(f&&/^\\\s*$/.test(stream.current())) | ||
| state.tokenize=tokenBase; | ||
| return"comment"; | ||
| } | ||
| function tokenCommentToEOF(stream){return stream.skipToEnd(),"comment";} | ||
| function tokenString(stream,state){ | ||
| var escaped=false,next,end=false; | ||
| while((next=stream.next())){ | ||
| if(next=="\""&&!escaped){end=true;break;} | ||
| escaped=!escaped&&next=="\\"; | ||
| } | ||
| if(end)state.tokenize=tokenBase; | ||
| return"string"; | ||
| } | ||
| function pushContext(state,type,col){state.context={prev:state.context,indent:state.indent,col:col,type:type};} | ||
| function popContext(state){state.indent=state.context.indent;state.context=state.context.prev;} | ||
| return{ | ||
| startState:function(){ | ||
| return{tokenize:tokenBase, | ||
| context:null, | ||
| indent:0, | ||
| col:0}; | ||
| }, | ||
| token:function(stream,state){ | ||
| if(stream.sol()){ | ||
| if(state.context&&state.context.align==null) | ||
| state.context.align=false; | ||
| state.indent=stream.indentation(); | ||
| } | ||
| //if (stream.eatSpace()) return null; | ||
| var style=state.tokenize(stream,state); | ||
| if(style!="comment"&&state.context&&state.context.align==null&&state.context.type!="pattern"){ | ||
| state.context.align=true; | ||
| } | ||
| if(curPunc=="(")pushContext(state,")",stream.column()); | ||
| else if(curPunc=="[")pushContext(state,"]",stream.column()); | ||
| else if(curPunc=="{")pushContext(state,"}",stream.column()); | ||
| else if(/[\]\}\)]/.test(curPunc)){ | ||
| while(state.context&&state.context.type=="pattern")popContext(state); | ||
| if(state.context&&curPunc==state.context.type)popContext(state); | ||
| } | ||
| else if(curPunc=="."&&state.context&&state.context.type=="pattern")popContext(state); | ||
| else if(/atom|string|variable/.test(style)&&state.context){ | ||
| if(/[\}\]]/.test(state.context.type)) | ||
| pushContext(state,"pattern",stream.column()); | ||
| else if(state.context.type=="pattern"&&!state.context.align){ | ||
| state.context.align=true; | ||
| state.context.col=stream.column(); | ||
| } | ||
| } | ||
| return style; | ||
| }, | ||
| indent:function(state,textAfter){ | ||
| var firstChar=textAfter&&textAfter.charAt(0); | ||
| var context=state.context; | ||
| if(/[\]\}]/.test(firstChar)) | ||
| while (context&&context.type=="pattern")context=context.prev; | ||
| var closing=context&&firstChar==context.type; | ||
| if(!context) | ||
| return 0; | ||
| else if(context.type=="pattern") | ||
| return context.col; | ||
| else if(context.align) | ||
| return context.col+(closing?0:1); | ||
| else | ||
| return context.indent+(closing?0:indentUnit); | ||
| } | ||
| }; | ||
| }); | ||
| CodeMirror.defineMIME("text/x-q","q"); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| <!doctype html> | ||
| <html> | ||
| <head> | ||
| <meta charset="utf-8"> | ||
| <title>CodeMirror: Turtle mode</title> | ||
| <link rel="stylesheet" href="../../lib/codemirror.css"> | ||
| <script src="../../lib/codemirror.js"></script> | ||
| <script src="turtle.js"></script> | ||
| <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style> | ||
| <link rel="stylesheet" href="../../doc/docs.css"> | ||
| </head> | ||
| <body> | ||
| <h1>CodeMirror: Turtle mode</h1> | ||
| <form><textarea id="code" name="code"> | ||
| @prefix foaf: <http://xmlns.com/foaf/0.1/> . | ||
| @prefix geo: <http://www.w3.org/2003/01/geo/wgs84_pos#> . | ||
| @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | ||
|
|
||
| <http://purl.org/net/bsletten> | ||
| a foaf:Person; | ||
| foaf:interest <http://www.w3.org/2000/01/sw/>; | ||
| foaf:based_near [ | ||
| geo:lat "34.0736111" ; | ||
| geo:lon "-118.3994444" | ||
| ] | ||
|
|
||
| </textarea></form> | ||
| <script> | ||
| var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | ||
| mode: "text/turtle", | ||
| tabMode: "indent", | ||
| matchBrackets: true | ||
| }); | ||
| </script> | ||
|
|
||
| <p><strong>MIME types defined:</strong> <code>text/turtle</code>.</p> | ||
|
|
||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| CodeMirror.defineMode("turtle", function(config) { | ||
| var indentUnit = config.indentUnit; | ||
| var curPunc; | ||
|
|
||
| function wordRegexp(words) { | ||
| return new RegExp("^(?:" + words.join("|") + ")$", "i"); | ||
| } | ||
| var ops = wordRegexp([]); | ||
| var keywords = wordRegexp(["@prefix", "@base", "a"]); | ||
| var operatorChars = /[*+\-<>=&|]/; | ||
|
|
||
| function tokenBase(stream, state) { | ||
| var ch = stream.next(); | ||
| curPunc = null; | ||
| if (ch == "<" && !stream.match(/^[\s\u00a0=]/, false)) { | ||
| stream.match(/^[^\s\u00a0>]*>?/); | ||
| return "atom"; | ||
| } | ||
| else if (ch == "\"" || ch == "'") { | ||
| state.tokenize = tokenLiteral(ch); | ||
| return state.tokenize(stream, state); | ||
| } | ||
| else if (/[{}\(\),\.;\[\]]/.test(ch)) { | ||
| curPunc = ch; | ||
| return null; | ||
| } | ||
| else if (ch == "#") { | ||
| stream.skipToEnd(); | ||
| return "comment"; | ||
| } | ||
| else if (operatorChars.test(ch)) { | ||
| stream.eatWhile(operatorChars); | ||
| return null; | ||
| } | ||
| else if (ch == ":") { | ||
| return "operator"; | ||
| } else { | ||
| stream.eatWhile(/[_\w\d]/); | ||
| if(stream.peek() == ":") { | ||
| return "variable-3"; | ||
| } else { | ||
| var word = stream.current(); | ||
|
|
||
| if(keywords.test(word)) { | ||
| return "meta"; | ||
| } | ||
|
|
||
| if(ch >= "A" && ch <= "Z") { | ||
| return "comment"; | ||
| } else { | ||
| return "keyword"; | ||
| } | ||
| } | ||
| var word = stream.current(); | ||
| if (ops.test(word)) | ||
| return null; | ||
| else if (keywords.test(word)) | ||
| return "meta"; | ||
| else | ||
| return "variable"; | ||
| } | ||
| } | ||
|
|
||
| function tokenLiteral(quote) { | ||
| return function(stream, state) { | ||
| var escaped = false, ch; | ||
| while ((ch = stream.next()) != null) { | ||
| if (ch == quote && !escaped) { | ||
| state.tokenize = tokenBase; | ||
| break; | ||
| } | ||
| escaped = !escaped && ch == "\\"; | ||
| } | ||
| return "string"; | ||
| }; | ||
| } | ||
|
|
||
| function pushContext(state, type, col) { | ||
| state.context = {prev: state.context, indent: state.indent, col: col, type: type}; | ||
| } | ||
| function popContext(state) { | ||
| state.indent = state.context.indent; | ||
| state.context = state.context.prev; | ||
| } | ||
|
|
||
| return { | ||
| startState: function() { | ||
| return {tokenize: tokenBase, | ||
| context: null, | ||
| indent: 0, | ||
| col: 0}; | ||
| }, | ||
|
|
||
| token: function(stream, state) { | ||
| if (stream.sol()) { | ||
| if (state.context && state.context.align == null) state.context.align = false; | ||
| state.indent = stream.indentation(); | ||
| } | ||
| if (stream.eatSpace()) return null; | ||
| var style = state.tokenize(stream, state); | ||
|
|
||
| if (style != "comment" && state.context && state.context.align == null && state.context.type != "pattern") { | ||
| state.context.align = true; | ||
| } | ||
|
|
||
| if (curPunc == "(") pushContext(state, ")", stream.column()); | ||
| else if (curPunc == "[") pushContext(state, "]", stream.column()); | ||
| else if (curPunc == "{") pushContext(state, "}", stream.column()); | ||
| else if (/[\]\}\)]/.test(curPunc)) { | ||
| while (state.context && state.context.type == "pattern") popContext(state); | ||
| if (state.context && curPunc == state.context.type) popContext(state); | ||
| } | ||
| else if (curPunc == "." && state.context && state.context.type == "pattern") popContext(state); | ||
| else if (/atom|string|variable/.test(style) && state.context) { | ||
| if (/[\}\]]/.test(state.context.type)) | ||
| pushContext(state, "pattern", stream.column()); | ||
| else if (state.context.type == "pattern" && !state.context.align) { | ||
| state.context.align = true; | ||
| state.context.col = stream.column(); | ||
| } | ||
| } | ||
|
|
||
| return style; | ||
| }, | ||
|
|
||
| indent: function(state, textAfter) { | ||
| var firstChar = textAfter && textAfter.charAt(0); | ||
| var context = state.context; | ||
| if (/[\]\}]/.test(firstChar)) | ||
| while (context && context.type == "pattern") context = context.prev; | ||
|
|
||
| var closing = context && firstChar == context.type; | ||
| if (!context) | ||
| return 0; | ||
| else if (context.type == "pattern") | ||
| return context.col; | ||
| else if (context.align) | ||
| return context.col + (closing ? 0 : 1); | ||
| else | ||
| return context.indent + (closing ? 0 : indentUnit); | ||
| } | ||
| }; | ||
| }); | ||
|
|
||
| CodeMirror.defineMIME("text/turtle", "turtle"); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,329 @@ | ||
| (function() { | ||
| // A minilanguage for instantiating linked CodeMirror instances and Docs | ||
| function instantiateSpec(spec, place, opts) { | ||
| var names = {}, pos = 0, l = spec.length, editors = []; | ||
| while (spec) { | ||
| var m = spec.match(/^(\w+)(\*?)(?:='([^\']*)'|<(~?)(\w+)(?:\/(\d+)-(\d+))?)\s*/); | ||
| var name = m[1], isDoc = m[2], cur; | ||
| if (m[3]) { | ||
| cur = isDoc ? CodeMirror.Doc(m[3]) : CodeMirror(place, clone(opts, {value: m[3]})); | ||
| } else { | ||
| var other = m[5]; | ||
| if (!names.hasOwnProperty(other)) { | ||
| names[other] = editors.length; | ||
| editors.push(CodeMirror(place, opts)); | ||
| } | ||
| var doc = editors[names[other]].linkedDoc({ | ||
| sharedHist: !m[4], | ||
| from: m[6] ? Number(m[6]) : null, | ||
| to: m[7] ? Number(m[7]) : null | ||
| }); | ||
| cur = isDoc ? doc : CodeMirror(place, clone(opts, {value: doc})); | ||
| } | ||
| names[name] = editors.length; | ||
| editors.push(cur); | ||
| spec = spec.slice(m[0].length); | ||
| } | ||
| return editors; | ||
| } | ||
|
|
||
| function clone(obj, props) { | ||
| if (!obj) return; | ||
| clone.prototype = obj; | ||
| var inst = new clone(); | ||
| if (props) for (var n in props) if (props.hasOwnProperty(n)) | ||
| inst[n] = props[n]; | ||
| return inst; | ||
| } | ||
|
|
||
| function eqAll(val) { | ||
| var end = arguments.length, msg = null; | ||
| if (typeof arguments[end-1] == "string") | ||
| msg = arguments[--end]; | ||
| if (i == end) throw new Error("No editors provided to eqAll"); | ||
| for (var i = 1; i < end; ++i) | ||
| eq(arguments[i].getValue(), val, msg) | ||
| } | ||
|
|
||
| function testDoc(name, spec, run, opts, expectFail) { | ||
| if (!opts) opts = {}; | ||
|
|
||
| return test("doc_" + name, function() { | ||
| var place = document.getElementById("testground"); | ||
| var editors = instantiateSpec(spec, place, opts); | ||
| var successful = false; | ||
|
|
||
| try { | ||
| run.apply(null, editors); | ||
| successful = true; | ||
| } finally { | ||
| if ((debug && !successful) || verbose) { | ||
| place.style.visibility = "visible"; | ||
| } else { | ||
| for (var i = 0; i < editors.length; ++i) | ||
| if (editors[i] instanceof CodeMirror) | ||
| place.removeChild(editors[i].getWrapperElement()); | ||
| } | ||
| } | ||
| }, expectFail); | ||
| } | ||
|
|
||
| var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); | ||
|
|
||
| function testBasic(a, b) { | ||
| eqAll("x", a, b); | ||
| a.setValue("hey"); | ||
| eqAll("hey", a, b); | ||
| b.setValue("wow"); | ||
| eqAll("wow", a, b); | ||
| a.replaceRange("u\nv\nw", Pos(0, 3)); | ||
| b.replaceRange("i", Pos(0, 4)); | ||
| b.replaceRange("j", Pos(2, 1)); | ||
| eqAll("wowui\nv\nwj", a, b); | ||
| } | ||
|
|
||
| testDoc("basic", "A='x' B<A", testBasic); | ||
| testDoc("basicSeparate", "A='x' B<~A", testBasic); | ||
|
|
||
| testDoc("sharedHist", "A='ab\ncd\nef' B<A", function(a, b) { | ||
| a.replaceRange("x", Pos(0)); | ||
| b.replaceRange("y", Pos(1)); | ||
| a.replaceRange("z", Pos(2)); | ||
| eqAll("abx\ncdy\nefz", a, b); | ||
| a.undo(); | ||
| a.undo(); | ||
| eqAll("abx\ncd\nef", a, b); | ||
| a.redo(); | ||
| eqAll("abx\ncdy\nef", a, b); | ||
| b.redo(); | ||
| eqAll("abx\ncdy\nefz", a, b); | ||
| a.undo(); b.undo(); a.undo(); a.undo(); | ||
| eqAll("ab\ncd\nef", a, b); | ||
| }, null, ie_lt8); | ||
|
|
||
| testDoc("undoIntact", "A='ab\ncd\nef' B<~A", function(a, b) { | ||
| a.replaceRange("x", Pos(0)); | ||
| b.replaceRange("y", Pos(1)); | ||
| a.replaceRange("z", Pos(2)); | ||
| a.replaceRange("q", Pos(0)); | ||
| eqAll("abxq\ncdy\nefz", a, b); | ||
| a.undo(); | ||
| a.undo(); | ||
| eqAll("abx\ncdy\nef", a, b); | ||
| b.undo(); | ||
| eqAll("abx\ncd\nef", a, b); | ||
| a.redo(); | ||
| eqAll("abx\ncd\nefz", a, b); | ||
| a.redo(); | ||
| eqAll("abxq\ncd\nefz", a, b); | ||
| a.undo(); a.undo(); a.undo(); a.undo(); | ||
| eqAll("ab\ncd\nef", a, b); | ||
| b.redo(); | ||
| eqAll("ab\ncdy\nef", a, b); | ||
| }); | ||
|
|
||
| testDoc("undoConflict", "A='ab\ncd\nef' B<~A", function(a, b) { | ||
| a.replaceRange("x", Pos(0)); | ||
| a.replaceRange("z", Pos(2)); | ||
| // This should clear the first undo event in a, but not the second | ||
| b.replaceRange("y", Pos(0)); | ||
| a.undo(); a.undo(); | ||
| eqAll("abxy\ncd\nef", a, b); | ||
| a.replaceRange("u", Pos(2)); | ||
| a.replaceRange("v", Pos(0)); | ||
| // This should clear both events in a | ||
| b.replaceRange("w", Pos(0)); | ||
| a.undo(); a.undo(); | ||
| eqAll("abxyvw\ncd\nefu", a, b); | ||
| }); | ||
|
|
||
| testDoc("doubleRebase", "A='ab\ncd\nef\ng' B<~A C<B", function(a, b, c) { | ||
| c.replaceRange("u", Pos(3)); | ||
| a.replaceRange("", Pos(0, 0), Pos(1, 0)); | ||
| c.undo(); | ||
| eqAll("cd\nef\ng", a, b, c); | ||
| }); | ||
|
|
||
| testDoc("undoUpdate", "A='ab\ncd\nef' B<~A", function(a, b) { | ||
| a.replaceRange("x", Pos(2)); | ||
| b.replaceRange("u\nv\nw\n", Pos(0, 0)); | ||
| a.undo(); | ||
| eqAll("u\nv\nw\nab\ncd\nef", a, b); | ||
| a.redo(); | ||
| eqAll("u\nv\nw\nab\ncd\nefx", a, b); | ||
| a.undo(); | ||
| eqAll("u\nv\nw\nab\ncd\nef", a, b); | ||
| b.undo(); | ||
| a.redo(); | ||
| eqAll("ab\ncd\nefx", a, b); | ||
| a.undo(); | ||
| eqAll("ab\ncd\nef", a, b); | ||
| }); | ||
|
|
||
| testDoc("undoKeepRanges", "A='abcdefg' B<A", function(a, b) { | ||
| var m = a.markText(Pos(0, 1), Pos(0, 3), {className: "foo"}); | ||
| b.replaceRange("x", Pos(0, 0)); | ||
| eqPos(m.find().from, Pos(0, 2)); | ||
| b.replaceRange("yzzy", Pos(0, 1), Pos(0)); | ||
| eq(m.find(), null); | ||
| b.undo(); | ||
| eqPos(m.find().from, Pos(0, 2)); | ||
| b.undo(); | ||
| eqPos(m.find().from, Pos(0, 1)); | ||
| }); | ||
|
|
||
| testDoc("longChain", "A='uv' B<A C<B D<C", function(a, b, c, d) { | ||
| a.replaceSelection("X"); | ||
| eqAll("Xuv", a, b, c, d); | ||
| d.replaceRange("Y", Pos(0)); | ||
| eqAll("XuvY", a, b, c, d); | ||
| }); | ||
|
|
||
| testDoc("broadCast", "B<A C<A D<A E<A", function(a, b, c, d, e) { | ||
| b.setValue("uu"); | ||
| eqAll("uu", a, b, c, d, e); | ||
| a.replaceRange("v", Pos(0, 1)); | ||
| eqAll("uvu", a, b, c, d, e); | ||
| }); | ||
|
|
||
| // A and B share a history, C and D share a separate one | ||
| testDoc("islands", "A='x\ny\nz' B<A C<~A D<C", function(a, b, c, d) { | ||
| a.replaceRange("u", Pos(0)); | ||
| d.replaceRange("v", Pos(2)); | ||
| b.undo(); | ||
| eqAll("x\ny\nzv", a, b, c, d); | ||
| c.undo(); | ||
| eqAll("x\ny\nz", a, b, c, d); | ||
| a.redo(); | ||
| eqAll("xu\ny\nz", a, b, c, d); | ||
| d.redo(); | ||
| eqAll("xu\ny\nzv", a, b, c, d); | ||
| }); | ||
|
|
||
| testDoc("unlink", "B<A C<A D<B", function(a, b, c, d) { | ||
| a.setValue("hi"); | ||
| b.unlinkDoc(a); | ||
| d.setValue("aye"); | ||
| eqAll("hi", a, c); | ||
| eqAll("aye", b, d); | ||
| a.setValue("oo"); | ||
| eqAll("oo", a, c); | ||
| eqAll("aye", b, d); | ||
| }); | ||
|
|
||
| testDoc("bareDoc", "A*='foo' B*<A C<B", function(a, b, c) { | ||
| is(a instanceof CodeMirror.Doc); | ||
| is(b instanceof CodeMirror.Doc); | ||
| is(c instanceof CodeMirror); | ||
| eqAll("foo", a, b, c); | ||
| a.replaceRange("hey", Pos(0, 0), Pos(0)); | ||
| c.replaceRange("!", Pos(0)); | ||
| eqAll("hey!", a, b, c); | ||
| b.unlinkDoc(a); | ||
| b.setValue("x"); | ||
| eqAll("x", b, c); | ||
| eqAll("hey!", a); | ||
| }); | ||
|
|
||
| testDoc("swapDoc", "A='a' B*='b' C<A", function(a, b, c) { | ||
| var d = a.swapDoc(b); | ||
| d.setValue("x"); | ||
| eqAll("x", c, d); | ||
| eqAll("b", a, b); | ||
| }); | ||
|
|
||
| testDoc("docKeepsScroll", "A='x' B*='y'", function(a, b) { | ||
| addDoc(a, 200, 200); | ||
| a.scrollIntoView(Pos(199, 200)); | ||
| var c = a.swapDoc(b); | ||
| a.swapDoc(c); | ||
| var pos = a.getScrollInfo(); | ||
| is(pos.left > 0, "not at left"); | ||
| is(pos.top > 0, "not at top"); | ||
| }); | ||
|
|
||
| testDoc("copyDoc", "A='u'", function(a) { | ||
| var copy = a.getDoc().copy(true); | ||
| a.setValue("foo"); | ||
| copy.setValue("bar"); | ||
| var old = a.swapDoc(copy); | ||
| eq(a.getValue(), "bar"); | ||
| a.undo(); | ||
| eq(a.getValue(), "u"); | ||
| a.swapDoc(old); | ||
| eq(a.getValue(), "foo"); | ||
| eq(old.historySize().undo, 1); | ||
| eq(old.copy(false).historySize().undo, 0); | ||
| }); | ||
|
|
||
| testDoc("docKeepsMode", "A='1+1'", function(a) { | ||
| var other = CodeMirror.Doc("hi", "text/x-markdown"); | ||
| a.setOption("mode", "text/javascript"); | ||
| var old = a.swapDoc(other); | ||
| eq(a.getOption("mode"), "text/x-markdown"); | ||
| eq(a.getMode().name, "markdown"); | ||
| a.swapDoc(old); | ||
| eq(a.getOption("mode"), "text/javascript"); | ||
| eq(a.getMode().name, "javascript"); | ||
| }); | ||
|
|
||
| testDoc("subview", "A='1\n2\n3\n4\n5' B<~A/1-3", function(a, b) { | ||
| eq(b.getValue(), "2\n3"); | ||
| eq(b.firstLine(), 1); | ||
| b.setCursor(Pos(4)); | ||
| eqPos(b.getCursor(), Pos(2, 1)); | ||
| a.replaceRange("-1\n0\n", Pos(0, 0)); | ||
| eq(b.firstLine(), 3); | ||
| eqPos(b.getCursor(), Pos(4, 1)); | ||
| a.undo(); | ||
| eqPos(b.getCursor(), Pos(2, 1)); | ||
| b.replaceRange("oyoy\n", Pos(2, 0)); | ||
| eq(a.getValue(), "1\n2\noyoy\n3\n4\n5"); | ||
| b.undo(); | ||
| eq(a.getValue(), "1\n2\n3\n4\n5"); | ||
| }); | ||
|
|
||
| testDoc("subviewEditOnBoundary", "A='11\n22\n33\n44\n55' B<~A/1-4", function(a, b) { | ||
| a.replaceRange("x\nyy\nz", Pos(0, 1), Pos(2, 1)); | ||
| eq(b.firstLine(), 2); | ||
| eq(b.lineCount(), 2); | ||
| eq(b.getValue(), "z3\n44"); | ||
| a.replaceRange("q\nrr\ns", Pos(3, 1), Pos(4, 1)); | ||
| eq(b.firstLine(), 2); | ||
| eq(b.getValue(), "z3\n4q"); | ||
| eq(a.getValue(), "1x\nyy\nz3\n4q\nrr\ns5"); | ||
| a.execCommand("selectAll"); | ||
| a.replaceSelection("!"); | ||
| eqAll("!", a, b); | ||
| }); | ||
|
|
||
|
|
||
| testDoc("sharedMarker", "A='ab\ncd\nef\ngh' B<A C<~A/1-2", function(a, b, c) { | ||
| var mark = b.markText(Pos(0, 1), Pos(3, 1), | ||
| {className: "cm-searching", shared: true}); | ||
| var found = a.findMarksAt(Pos(0, 2)); | ||
| eq(found.length, 1); | ||
| eq(found[0], mark); | ||
| eq(c.findMarksAt(Pos(1, 1)).length, 1); | ||
| eqPos(mark.find().from, Pos(0, 1)); | ||
| eqPos(mark.find().to, Pos(3, 1)); | ||
| b.replaceRange("x\ny\n", Pos(0, 0)); | ||
| eqPos(mark.find().from, Pos(2, 1)); | ||
| eqPos(mark.find().to, Pos(5, 1)); | ||
| var cleared = 0; | ||
| CodeMirror.on(mark, "clear", function() {++cleared;}); | ||
| b.operation(function(){mark.clear();}); | ||
| eq(a.findMarksAt(Pos(3, 1)).length, 0); | ||
| eq(b.findMarksAt(Pos(3, 1)).length, 0); | ||
| eq(c.findMarksAt(Pos(3, 1)).length, 0); | ||
| eq(mark.find(), null); | ||
| eq(cleared, 1); | ||
| }); | ||
|
|
||
| testDoc("undoInSubview", "A='line 0\nline 1\nline 2\nline 3\nline 4' B<A/1-4", function(a, b) { | ||
| b.replaceRange("x", Pos(2, 0)); | ||
| a.undo(); | ||
| eq(a.getValue(), "line 0\nline 1\nline 2\nline 3\nline 4"); | ||
| eq(b.getValue(), "line 1\nline 2\nline 3"); | ||
| }); | ||
| })(); |