Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for other languages #2

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .verb.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,42 @@ Results in:
original: 'var foo = "bar";' } ] }
```

**Different comment styles and other languages**

The `options` parameter can be used to specify a file-type and load comment-patterns for
different languages. With the following Handlebars-file:

```hbs
{{!
This is a handlebars-file
}}
{{title}}
```

the following snippet

```js
var extract = require('extract-comments');

// pass a string of javascript, CSS, LESS etc
extract(string, { filename: 'abc.hbs'});
```

results in

```js
{
'1': {
begin: 1,
code: '{{title}}',
codeStart: 5,
content: 'This is a handlebars-file\n',
end: 3
}
}
```


## Extracting from files

Prior to v0.7.0, there was a method to extract code comments from files. Here is the equivalent code to accomplish the same thing:
Expand Down
27 changes: 18 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ Results in:

_(The reason the key is the starting line number is that it's easy to use this format with templates)_


**Customize output**

```js
Expand Down Expand Up @@ -105,14 +104,17 @@ function extractComments(patterns, opts) {
```

## Related
* [parse-comments](https://github.com/jonschlinkert/parse-comments): Parse code comments from JavaScript or any language that uses the same format.
* [code-context](https://github.com/jonschlinkert/code-context): Parse a string of javascript to determine the context for functions, variables and comments based on the code that follows.
* [esprima-extract-comments](https://github.com/jonschlinkert/esprima-extract-comments): Extract code comments from string or from a glob of files using esprima.

* [code-context](https://github.com/jonschlinkert/code-context): Parse a string of javascript to determine the context for… [more](https://github.com/jonschlinkert/code-context)
* [esprima-extract-comments](https://github.com/jonschlinkert/esprima-extract-comments): Extract code comments from string or from a glob of… [more](https://github.com/jonschlinkert/esprima-extract-comments)
* [parse-comments](https://github.com/jonschlinkert/parse-comments): Parse code comments from JavaScript or any language that uses… [more](https://github.com/jonschlinkert/parse-comments)

## Contributing

Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/extract-comments/issues)

## Run tests

Install dev dependencies:

```bash
Expand All @@ -123,15 +125,22 @@ npm i -d && npm test

**Jon Schlinkert**

+ [github/jonschlinkert](https://github.com/jonschlinkert)
+ [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
[github/jonschlinkert](https://github.com/jonschlinkert)
[twitter/jonschlinkert](http://twitter.com/jonschlinkert)

## License
Copyright (c) 2014-2015 Jon Schlinkert
Released under the MIT license

Copyright (c) 2014-2015 Jon Schlinkert
Released under the MIT license.

***

_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on April 02, 2015._
_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on May 02, 2015._

[map-files]: https://github.com/jonschlinkert/map-files

<!-- reflinks generated by verb-reflinks plugin -->

[verb]: https://github.com/assemble/verb
[template]: https://github.com/jonschlinkert/template
[assemble]: http://assemble.io
4 changes: 4 additions & 0 deletions fixtures/abc.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{{!
This is a handlebars-file
}}
{{title}}
162 changes: 113 additions & 49 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
'use strict';

var isWhitespace = require('is-whitespace');
var commentPattern = require('comment-patterns');
var qm = require('quotemeta');

/**
* expose `extract`
Expand All @@ -31,47 +33,73 @@ module.exports = extract;
* });
* ```
*
* @param {String} `string`
* @param {String} `str` the source code to extract comments from
* @param {Function} [`fn`] the transformer-callback
* @param {Object} `options` an object with optional paramters
* @param {String} [`options.filename`] a file name to determine the comment-patterns
* @return {Object} Object of code comments.
*
* @api public
*/

function extract(str, fn) {
var start = /^\/\*\*?/;
var middle = /^\*([^*][^\/])*/;
var end = /^\*\//;
function extract(str, fn, options) {
if (typeof fn !== 'function' && typeof options === "undefined") {
options = fn;
fn = null;
}
// default filename is a javascript file (for backwards compatibility)
var filename = (options && options.filename) || "abc.js";
var patterns = buildRegexes(commentPattern(filename));

var lines = str.split(/[\r\n]/);
var len = lines.length, i = 0, m;
var len = lines.length, i = 0;
var comments = {};
var isComment = false, afterCount;
var from, to, b, o = {};
var afterCount;
var b, o = {};

// See the `checkStart`-function for possible values.
var currentComment = null;

while (i < len) {
var line = lines[i++].trim();

if (!isComment && start.test(line)) {
afterCount = 0;
isComment = true;
o = {begin: null, end: null};
o.begin = b = i;
o.code = '';
o.content = '';
}

if (isComment && end.test(line)) {
o.end = i;
comments[b] = o;
isComment = false;
}

if (isComment && i > b) {
if (isMiddle(line)) {
o.content += stripStars(line) + '\n';
if (currentComment == null) {
// Check if any of the comment-patterns matches
currentComment = checkStart(line,patterns);
if (currentComment != null) {
// We are now inside a comment
afterCount = 0;
o = {begin: null, end: null};
o.begin = b = i;
o.code = '';
o.content = '';
}
} else {
// Inside a comment...
if (currentComment.multi) {
// Inside a multiline comment
if (currentComment.pattern.end.test(line)) {
// End of comment reached
o.end = i;
comments[b] = o;
currentComment = null;
} else if (currentComment.pattern.middle.test(line)) {
o.content += line.replace(currentComment.pattern.middle,"") + '\n';
}
} else {
// Last line was a single-line comment
if (currentComment.pattern.test(line)) {
o.content += line.replace(currentComment.pattern,"") + '\n';
} else {
// End of comment was reached one line before
o.end = i-1;
comments[b] = o;
currentComment = null;
}
}
}

if (!isComment && o.end && i > o.end && afterCount < 2) {

if (currentComment == null && o.end && i > o.end && afterCount < 2) {
if (!isWhitespace(line)) {
o.codeStart = i;
}
Expand All @@ -89,34 +117,70 @@ function extract(str, fn) {
}
}
return comments;
};
}

/**
* Strip the leading `*` from a line, ensuring
* not to eat too many whitespaces after the delimiter.
* Check, whether the current line starts a comment.
* Return the pattern-set marking the current comment:
* This can be either `null`, if the current line is no comment,
* or `{multi:true, pattern:{start:,middle:,end:}}` for multiline-comments
* or `{multi:false, pattern: 'string'}` for single-line-commments
* @param {String} `line` the current line of code
* @param {Object} `patterns` the comment-patterns for the current language
*/

function stripStars(str) {
str = str.replace(/^\s*/, '');
if (str.charAt(0) === '/') {
str = str.slice(1);
}
if (str.charAt(0) === '*') {
str = str.slice(1);
function checkStart(line, patterns) {

var i;
// Check for multiline-comment. `pattern.multiLine` is never `null` due to the `buildRegexes`-function
for (i = 0; i < patterns.multiLine.length; i++) {
if (patterns.multiLine[i].start.test(line)) {
return {
multi: true,
pattern: patterns.multiLine[i]
}
}
}
if (str.charAt(0) === ' ') {
str = str.slice(1);
// Check for single-line-comment. `pattern.singleLine` is never `null` due to the `buildRegexes`-function
for (i = 0; i < patterns.singleLine.length; i++) {
if (patterns.singleLine[i].test(line)) {
return {
multi: false,
pattern: patterns.singleLine[i]
}
}
}
return str;
// No comment start detected
return null;
}

/**
* Detect if the given line is in the middle
* of a comment.
* Convert the string-based comment-delimiters from the `comment-patterns` result
* into regex-based comment-patterns.
* @param {Object} `patterns` the `comment-patterns`
* @returns {{multiLine: Array, singleLine: Array}} regexes for multi-line and single-line comments
*/

function isMiddle(str) {
return typeof str === 'string'
&& str.charAt(0) === '*'
&& str.charAt(1) !== '/';
function buildRegexes(patterns) {
var i = 0;
var result = {
multiLine: [],
singleLine: []
};
if (patterns.multiLineComment) {
for (i = 0; i < patterns.multiLineComment.length; i++) {

var ml = patterns.multiLineComment[i];
result.multiLine.push({
start: new RegExp("^" + qm(ml.start)),
middle: new RegExp("^" + qm(ml.middle)+"\\s?"),
end: new RegExp("^" + qm(ml.end))
});
}
}
if (patterns.singleLineComment) {
for (i = 0; i < patterns.singleLineComment.length; i++) {
var sl = patterns.singleLineComment[i];
result.singleLine.push(new RegExp("^" + qm(sl)));
}
}
return result;
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
"test": "mocha"
},
"dependencies": {
"is-whitespace": "^0.3.0"
"comment-patterns": "0.0.7",
"is-whitespace": "^0.3.0",
"quotemeta": "0.0.0"
},
"devDependencies": {
"code-context": "^0.5.0",
Expand Down
12 changes: 12 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,16 @@ describe('extract comments', function () {
var comments = extract(str);
comments['1274'].codeStart.should.equal(1291);
});

it('should extract Handlebars-style comments when the filename is set to abc.hbs', function() {
extract(read("abc.hbs"),{ filename: 'abc.hbs' }).should.eql( {
'1': {
begin: 1,
code: '{{title}}',
codeStart: 5,
content: 'This is a handlebars-file\n',
end: 3
}
});
})
});