📄 syntax-parser.js
字号:
var ie5 = "test".replace(/\w+/,'$&') == "$&";
/**
* @public
* @constructor
* @param <String> source
*/
function SyntaxParser(source){
this.initialize(source);
}
function cloneObject(src){
var dest = {};
for(var n in src){
dest[n] = src[n];
}
return dest;
}
/**
* @protected
*/
SyntaxParser.prototype.initialize = function(source){
this.source = source;
this.depths = [];
this.anchors = [];
this.partitionerMap = cloneObject(this.partitionerMap);
this.rendererMap = cloneObject(this.rendererMap);
this.depVisitorMap = cloneObject(this.depVisitorMap);
}
/**
* 设置分区渲染函数表
*/
SyntaxParser.prototype.setRendererMap = function(group){
this.rendererMap = {};
for(var n in group){
n = n || this.defaultType;
var v = group[n];
if(v instanceof Function){//is Function
this.rendererMap[n] = v;
}else{
if(v instanceof Array){
v = '(\\b'+v.join('\\b|\\b') + '\\b)';
}
var reg = new RegExp(v,'g');
this.rendererMap[n] = this.buildRenderer(reg,"<b class='xidea--syntax-keyword xidea--syntax-$1'>$1</b>");
}
}
}
SyntaxParser.prototype.buildRenderer = function(regexp,str){
return function(text){
return text.replace(regexp,str);
};
}
//IE5 does not support $&; and replacer function
/**
* 设置分区表
*/
SyntaxParser.prototype.setPartitionerMap = function(group){
this.partitionerMap = {};
function build(ps){
if(ps.exec){//is RegExp
return ps;
}else{
if(ps instanceof Array){
var ps = ps.join('|');
}
return new RegExp(ps,'gm');
}
}
for(var n in group){
this.partitionerMap[n || this.defaultType] = build(group[n])
}
}
/**
* parse the code
* @protected
*/
SyntaxParser.prototype.parse = function(){
if(!this.lines){
this.lines = [];
var lreg = // /\r\n|\n|\r/g;//
new RegExp("\\r\\n|\\n|\\r",'g');
var begin = 0,lm;
while(lm=lreg.exec(this.source)){
this.lines.push({begin:begin,end:lm.index});
begin = lm.index+lm[0].length;
}
if(begin<this.source.length){
this.lines.push({begin:begin,end:this.source.length});
}
//alert(this.lines.length)
this.partitions = this.buildPartitions();
}
}
/**
* private
*/
SyntaxParser.prototype.defaultType = "code";
SyntaxParser.prototype.guessType = function(){
for(var n in this.partitionerMap){
if(this.partitionerMap[n].test(p)){
return n;
}
}
return this.defaultType;
};
function Partition(value,type,begin,end){
this.value = value;
this.type = type;
this.begin = begin;
this.end = end;
}
Partition.prototype.toString = function(){
return '{value:'+this.value+
',type:'+this.type+
',begin:'+this.begin+
',end:'+this.end+'}\n';
}
/**
* parse partitions
* @protected
*/
SyntaxParser.prototype.parsePartitions = function(){
var exp = [];
for(var n in this.partitionerMap){
exp.push(this.partitionerMap[n].source);
}
exp = new RegExp(exp.join('|'),"gm");
var match,ps=[],s = this.source;
while(match = exp.exec(s)){
var token = match[0];
var type = this.guessType(token);
ps.push(new Partition(token,type,match.index,match.index + token.length));
}
return ps;
};
/**
* build partitions(fill blank part and compute depths)
* @protected
*/
SyntaxParser.prototype.buildPartitions = function(){
var nps = [],pos = 0,dep = 0,ps = this.parsePartitions();
for(var i = 0;i<ps.length;i++){
var p = ps[i];
if(p.begin >pos){
var bp = new Partition(this.source.substring(pos,p.begin),this.defaultType,pos,p.begin);
dep = this.computeDepth(bp,dep);
nps.push(bp);
}
dep = this.computeDepth(p,dep);
nps.push(p);
pos = p.end;
}
if(pos<this.source.length){
var p = new Partition(this.source.substr(pos),this.defaultType,pos,this.source.length);
dep = this.computeDepth(p,dep);
nps.push(p);
}
return nps;
}
/**
* compute depths.
* default implements is scan the code for '{'|'}',if '{' <b>++depth</b> else <b>depth--</b>
* @protected
*/
SyntaxParser.prototype.computeDepth = function(p,dep){
switch(p.type){
case 'code':
var reg = new RegExp("\{|\}",'g');
var match, code = p.value;
while(match = reg.exec(code)){
if(match[0] == '{'){
this.depths.push({position:p.begin+match.index,
preDepth:dep,minDepth:dep++,nextDepth:dep,
type:'open'});
}else{
this.depths.push({position:p.begin+match.index,
preDepth:dep,minDepth:--dep,nextDepth:dep,
type:'close'});
}
}
break;
case 'muti-comment':
case 'document':
this.depths.push({position:p.begin,
preDepth:dep,minDepth:dep++,nextDepth:dep,
type:'open'});
this.depths.push({position:p.end-1,
preDepth:dep,minDepth:--dep,nextDepth:dep,
type:'close'});
break;
}
return dep;
};
SyntaxParser.prototype.buildLineIterator = function(){
if(this.partitions.length == 0){
this.parse();
}
return new LineIterator(this);
};
/**
* @protected
* @constructor
* @param <String> sourceParser
*/
function LineIterator(sourceParser){
this.parser = sourceParser;
this.source = sourceParser.source;
this.partitions = sourceParser.partitions;
this.lines = sourceParser.lines;
this.depths = sourceParser.depths;
this.anchors = sourceParser.anchors;
this.depth = 0;
this.nextDepth = 0;
this.depthStack = [-1];
this.depthStart = -1
this.partitionIndex = 0;
this.lineIndex = 0;
this.depthIndex = 0;
this.anchorsIndex = 0;
}
LineIterator.prototype.hasNext = function(){
return (this.partitions[this.partitionIndex] && this.lines[this.lineIndex]);
};
LineIterator.prototype.next = function(){
var p = this.partitions[this.partitionIndex];
var l = this.lines[this.lineIndex];
//alert(l.begin+"/"+l.end)
if(!p || !l){return null}
try{
//dell with depths
var d = this.depths[this.depthIndex];
if(d != null){
if(d.position<l.end){
this.depth = d.minDepth;
while(d = this.depths[++this.depthIndex]){
if(d.position<l.end){
this.depth = Math.min(this.depth,d.minDepth);
}else{
break;
}
}
if(d){
this.nextDepth = d.preDepth;
}else{
this.nextDepth = 0;
this.depth = 0;
this.depthStack.length = 0;
this.depthStart = -2;
}
}else{
this.nextDepth = this.depth = d.preDepth;
}
}
//dell with depth stack
var i = this.depth - this.depthStack.length+1;
if(i>0){
while(i-->0){
this.depthStack.push(this.lineIndex);
this.depthStart = this.lineIndex;
}
}else if(i<0){
this.depthStack.length = this.depth+1;
this.depthStart = this.depthStack[this.depth];
}
//dell with anchors
var a = this.anchors[this.anchorsIndex];
this.anchor = "";
if(a && a.position<l.end){
this.anchor = "";
do{
this.anchor += "<a name=\""+a.name+"\" />";
a = this.anchors[++this.anchorsIndex];
}while(a && a.position<l.end)
}
//dell with line;
if(p.end>=l.end){
return this.render(l.begin,l.end,p.type);
}else{
var buf = [];
var i = l.begin;
while(p.end<l.end){
buf.push(this.render(i,i=p.end,p.type));
p = this.partitions[++this.partitionIndex];
}
buf.push(this.render(i,l.end,p.type));
return buf.join('');
}
}finally{
this.lineIndex++;
}
};
LineIterator.prototype.render = function(b,e,type){
if(e>b){
var text = this.source.substring(b,e);
text = this.encodeText(text);
//.replace(/ /g," ").replace(/\t/g," ");
var renderer = this.parser.rendererMap[type];
if(renderer){
text = renderer.call(this.parser,text);
//text.replace(/@([a-zA-Z\-]+)/,this.tagReplacer);
}
return "<span class='xidea--syntax-"+type+"'>"+text+"</span>";
}else{
return '';
}
};
LineIterator.prototype.encodeText = function(str){
if(str){
return str.replace(/[\r\n]/g,'').
replace(/&/g,'&').
replace(/>/g,'>').
replace(/</g,'<');
}
return str;
};
function ECMAParser(source){
this.initialize(source);
}
$JSI.extend(ECMAParser,SyntaxParser);
//ECMAParser.prototype = new SyntaxParser();
/**
* keywords Regexp.
* default is for javascript
* @protected
*/
ECMAParser.prototype.setRendererMap({
'code':['abstract','boolean','break','byte','case','catch','char','class','const','continue','debugger',
'default','delete','do','double','else','enum','export','extends','false','final','finally','float',
'for','function','goto','if','implements','import','in','instanceof','int','interface','long','native',
'new','null','package','private','protected','prototype','public','return','short','static','super','switch',
'synchronized','this','throw','throws','transient','true','try','typeof','var','void','volatile','while','with'],
'document':ECMAParser.prototype.buildRenderer(/@([\w-_\.\d]+)/g,"<b class='xidea--syntax-tag xidea--syntax-$1'>@$1</b>")
}
);
/**
* partitions Regexp.
* default is for javascript
* @protected
*/
ECMAParser.prototype.setPartitionerMap(
{
'document':'/\\*\\*(?:[^\\*]|\\*[^/])*\\*/'
,'muti-comment':'/\\*(?:[^\\*]|\\*[^/])*\\*/'//muti-comment
,'comment':'//.*$' //single-comment
,'regexp':'/(?:\\\\.|[^/\\n\\r])+/' //regexp
,'string':['"(?:\\\\.|[^"\\n\\r])*"',"'(?:\\\\.|[^'\\n\\r])*'"] //string
,'preprocessor':'^\\s*#.*' //process
}
);
/**
* 针对JavaScript优化
* guess the type of given partition.
* default is for javascript
* @protected
*/
ECMAParser.prototype.guessType = function(p){
var type = "";
switch(p.charAt(0)){
case '/':
var c = p.charAt(1);
if(c == '/'){
type = "comment";
}else if(c == '*'){
if(p.charAt(2) == '*' && p.charAt(3) != '/'){
type = "document";
}else{
type = "muti-comment";
}
}else{
type = "regexp";
}
break;
case '\'':
case '"':
type = "string";
break;
case ' ':
case '#':
type = "preprocessor";
break;
}
return type;
};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -