crojsdoc
Version:
A documentation generator for JavaScript and CoffeeScript
229 lines (220 loc) • 6.59 kB
JavaScript
// Generated by CoffeeScript 1.8.0
(function() {
var escape, markdown;
markdown = require('marked');
escape = require('./utils').escape;
exports.parseComments = function(js, options) {
var buf, code, comment, comments, i, ignore, len, raw, withinMultiline, withinSingle;
if (options == null) {
options = {};
}
js = js.replace(/\r\n/gm, '\n');
comments = [];
raw = options.raw;
buf = '';
withinMultiline = false;
withinSingle = false;
i = 0;
len = js.length;
while (i < len) {
if (!withinMultiline && !withinSingle && '/' === js[i] && '*' === js[i + 1]) {
if (buf.trim().length) {
comment = comments[comments.length - 1];
if (comment) {
comment.code = code = buf.trim();
comment.ctx = exports.parseCodeContext(code);
}
buf = '';
}
i += 2;
withinMultiline = true;
ignore = '!' === js[i];
} else if (withinMultiline && !withinSingle && '*' === js[i] && '/' === js[i + 1]) {
i += 2;
buf = buf.replace(/^[ \t]*\* ?/gm, '');
comment = exports.parseComment(buf, options);
comment.ignore = ignore;
comments.push(comment);
withinMultiline = ignore = false;
buf = '';
} else if (!withinSingle && !withinMultiline && '/' === js[i] && '/' === js[i + 1]) {
withinSingle = true;
buf += js[i];
} else if (withinSingle && !withinMultiline && '\n' === js[i]) {
withinSingle = false;
buf += js[i];
} else {
buf += js[i];
}
i++;
}
if (comments.length === 0) {
comments.push({
tags: [],
description: {
full: '',
summary: '',
body: ''
},
isPrivate: false
});
}
if (buf.trim().length) {
comment = comments[comments.length - 1];
code = buf.trim();
comment.code = code;
comment.ctx = exports.parseCodeContext(code);
}
return comments;
};
exports.parseComment = function(str, options) {
var comment, description, raw, tags;
if (options == null) {
options = {};
}
str = str.trim();
comment = {
tags: []
};
raw = options.raw;
description = {};
if (str[0] === '@') {
str = '\n' + str;
}
description.full = str.split('\n@')[0];
description.summary = description.full.split('\n\n')[0];
description.body = description.full.split('\n\n').slice(1).join('\n\n');
comment.description = description;
if (~str.indexOf('\n@')) {
tags = '@' + str.split('\n@').slice(1).join('\n@');
comment.tags = tags.split('\n').map(exports.parseTag);
comment.isPrivate = comment.tags.some(function(tag) {
return 'api' === tag.type && 'private' === tag.visibility;
});
}
if (!raw) {
description.full = markdown(description.full);
description.summary = markdown(description.summary);
description.body = markdown(description.body);
}
return comment;
};
exports.parseTag = function(str) {
var parts, tag, type;
tag = {};
parts = str.split(/\ +/);
type = tag.type = parts.shift().replace('@', '').toLowerCase();
switch (type) {
case 'param':
tag.types = /{.*}/.test(parts[0]) ? exports.parseTagTypes(parts.shift()) : [];
tag.name = parts.shift() || '';
tag.description = parts.join(' ');
break;
case 'return':
tag.types = /{.*}/.test(parts[0]) ? exports.parseTagTypes(parts.shift()) : [];
tag.description = parts.join(' ');
break;
case 'see':
if (~str.indexOf('http')) {
tag.title = parts.length > 1 ? parts.shift() : '';
tag.url = parts.join(' ');
} else {
tag.local = parts.join(' ');
}
break;
case 'api':
tag.visibility = parts.shift();
break;
case 'type':
tag.types = exports.parseTagTypes(parts.shift());
break;
case 'memberof':
tag.parent = parts.shift();
break;
case 'augments':
tag.otherClass = parts.shift();
break;
case 'borrows':
tag.otherMemberName = parts.join(' ').split(' as ')[0];
tag.thisMemberName = parts.join(' ').split(' as ')[1];
break;
case 'throws':
tag.string = parts.join(' ');
break;
default:
tag.string = parts.join(' ');
}
return tag;
};
exports.parseTagTypes = function(str) {
return str.replace(/[{}]/g, '').split(/\ *[|,\/] */);
};
exports.parseCodeContext = function(str) {
str = str.split('\n')[0];
if (/^function (\w+) *\(/.exec(str)) {
return {
type: 'function',
name: RegExp.$1,
string: RegExp.$1 + '()'
};
} else if (/^var *(\w+)[ \t]*=[ \t]*function/.exec(str)) {
return {
type: 'function',
name: RegExp.$1,
string: RegExp.$1 + '()'
};
} else if (/^(\w+)\.prototype\.(\w+)[ \t]*=[ \t]*function/.exec(str)) {
return {
type: 'method',
constructor: RegExp.$1,
cons: RegExp.$1,
name: RegExp.$2,
string: RegExp.$1 + '.prototype.' + RegExp.$2 + '()'
};
} else if (/^(\w+)\.prototype\.(\w+)[ \t]*=[ \t]*([^\n;]+)/.exec(str)) {
return {
type: 'property',
constructor: RegExp.$1,
cons: RegExp.$1,
name: RegExp.$2,
value: RegExp.$3,
string: RegExp.$1 + '.prototype.' + RegExp.$2
};
} else if (/^([\w.]+)\.(\w+)[ \t]*=[ \t]*function/.exec(str)) {
return {
type: 'method',
receiver: RegExp.$1,
name: RegExp.$2,
string: RegExp.$1 + '.' + RegExp.$2 + '()'
};
} else if (/^(\w+)\.(\w+)[ \t]*=[ \t]*([^\n;]+)/.exec(str)) {
return {
type: 'property',
receiver: RegExp.$1,
name: RegExp.$2,
value: RegExp.$3,
string: RegExp.$1 + '.' + RegExp.$2
};
} else if (/^var +(\w+)[ \t]*=[ \t]*([^\n;]+)/.exec(str)) {
return {
type: 'declaration',
name: RegExp.$1,
value: RegExp.$2,
string: RegExp.$1
};
} else if (/^(\w+) *: *function/.exec(str)) {
return {
type: 'method',
name: RegExp.$1,
string: RegExp.$1 + '()'
};
} else if (/^(\w+) *: *([^\n;]+)/.exec(str)) {
return {
type: 'property',
name: RegExp.$1,
value: RegExp.$2,
string: RegExp.$1
};
}
};
}).call(this);