[Pkg-javascript-commits] [node-doctrine] 01/05: New upstream version 2.1.0

Julien Puydt julien.puydt at laposte.net
Wed Jan 10 16:21:20 UTC 2018


This is an automated email from the git hooks/post-receive script.

jpuydt-guest pushed a commit to branch master
in repository node-doctrine.

commit b5ed464171c04c4f19579fa2e221abc9ccb38d3a
Author: Julien Puydt <julien.puydt at laposte.net>
Date:   Wed Jan 10 13:43:12 2018 +0100

    New upstream version 2.1.0
---
 CHANGELOG.md      |   4 +
 README.md         |   1 +
 lib/doctrine.js   | 106 +++++-----
 lib/typed.js      | 162 ++++++++------
 package-lock.json |   2 +-
 package.json      |   2 +-
 test/parse.js     | 622 +++++++++++++++++++++++++++++++++++-------------------
 7 files changed, 562 insertions(+), 337 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b5caec3..57140d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+v2.1.0 - January 6, 2018
+
+* 827f314 Update: support node ranges (fixes #89) (#190) (Teddy Katz)
+
 v2.0.2 - November 25, 2017
 
 * 5049ee3 Fix: Remove redundant LICENSE/README names from files (#203) (Kevin Partington)
diff --git a/README.md b/README.md
index dcfe776..26fad18 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,7 @@ The primary method is `parse()`, which accepts two arguments: the JSDoc comment
 * `recoverable` - set to `true` to keep parsing even when syntax errors occur. Default: `false`.
 * `sloppy` - set to `true` to allow optional parameters to be specified in brackets (`@param {string} [foo]`). Default: `false`.
 * `lineNumbers` - set to `true` to add `lineNumber` to each node, specifying the line on which the node is found in the source. Default: `false`.
+* `range` - set to `true` to add `range` to each node, specifying the start and end index of the node in the original comment. Default: `false`.
 
 Here's a simple example:
 
diff --git a/lib/doctrine.js b/lib/doctrine.js
index bdaa0e0..1665afe 100644
--- a/lib/doctrine.js
+++ b/lib/doctrine.js
@@ -90,59 +90,49 @@
             title === 'public' || title === 'private' || title === 'protected';
     }
 
+    // A regex character class that contains all whitespace except linebreak characters (\r, \n, \u2028, \u2029)
+    var WHITESPACE = '[ \\f\\t\\v\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]';
+
+    var STAR_MATCHER = '(' + WHITESPACE + '*(?:\\*' + WHITESPACE + '?)?)(.+|[\r\n\u2028\u2029])';
+
     function unwrapComment(doc) {
         // JSDoc comment is following form
         //   /**
         //    * .......
         //    */
-        // remove /**, */ and *
-        var BEFORE_STAR = 0,
-            STAR = 1,
-            AFTER_STAR = 2,
-            index,
-            len,
-            mode,
-            result,
-            ch;
-
-        doc = doc.replace(/^\/\*\*?/, '').replace(/\*\/$/, '');
-        index = 0;
-        len = doc.length;
-        mode = BEFORE_STAR;
-        result = '';
-
-        while (index < len) {
-            ch = doc.charCodeAt(index);
-            switch (mode) {
-            case BEFORE_STAR:
-                if (esutils.code.isLineTerminator(ch)) {
-                    result += String.fromCharCode(ch);
-                } else if (ch === 0x2A  /* '*' */) {
-                    mode = STAR;
-                } else if (!esutils.code.isWhiteSpace(ch)) {
-                    result += String.fromCharCode(ch);
-                    mode = AFTER_STAR;
-                }
-                break;
 
-            case STAR:
-                if (!esutils.code.isWhiteSpace(ch)) {
-                    result += String.fromCharCode(ch);
-                }
-                mode = esutils.code.isLineTerminator(ch) ? BEFORE_STAR : AFTER_STAR;
-                break;
+        return doc.
+            // remove /**
+            replace(/^\/\*\*?/, '').
+            // remove */
+            replace(/\*\/$/, '').
+            // remove ' * ' at the beginning of a line
+            replace(new RegExp(STAR_MATCHER, 'g'), '$2').
+            // remove trailing whitespace
+            replace(/\s*$/, '');
+    }
 
-            case AFTER_STAR:
-                result += String.fromCharCode(ch);
-                if (esutils.code.isLineTerminator(ch)) {
-                    mode = BEFORE_STAR;
-                }
-                break;
+    /**
+     * Converts an index in an "unwrapped" JSDoc comment to the corresponding index in the original "wrapped" version
+     * @param {string} originalSource The original wrapped comment
+     * @param {number} unwrappedIndex The index of a character in the unwrapped string
+     * @returns {number} The index of the corresponding character in the original wrapped string
+     */
+    function convertUnwrappedCommentIndex(originalSource, unwrappedIndex) {
+        var replacedSource = originalSource.replace(/^\/\*\*?/, '');
+        var numSkippedChars = 0;
+        var matcher = new RegExp(STAR_MATCHER, 'g');
+        var match;
+
+        while ((match = matcher.exec(replacedSource))) {
+            numSkippedChars += match[1].length;
+
+            if (match.index + match[0].length > unwrappedIndex + numSkippedChars) {
+                return unwrappedIndex + numSkippedChars + originalSource.length - replacedSource.length;
             }
-            index += 1;
         }
 
-        return result.replace(/\s+$/, '');
+        return originalSource.replace(/\*\/$/, '').replace(/\s*$/, '').length;
     }
 
     // JSDoc Tag Parser
@@ -153,6 +143,7 @@
             lineNumber,
             length,
             source,
+            originalSource,
             recoverable,
             sloppy,
             strict;
@@ -203,8 +194,8 @@
         // { { ok: string } }
         //
         // therefore, scanning type expression with balancing braces.
-        function parseType(title, last) {
-            var ch, brace, type, direct = false;
+        function parseType(title, last, addRange) {
+            var ch, brace, type, startIndex, direct = false;
 
 
             // search '{'
@@ -244,6 +235,9 @@
                     } else if (ch === 0x7B  /* '{' */) {
                         brace += 1;
                     }
+                    if (type === '') {
+                        startIndex = index;
+                    }
                     type += advance();
                 }
             }
@@ -254,10 +248,10 @@
             }
 
             if (isAllowedOptional(title)) {
-                return typed.parseParamType(type);
+                return typed.parseParamType(type, {startIndex: convertIndex(startIndex), range: addRange});
             }
 
-            return typed.parseType(type);
+            return typed.parseType(type, {startIndex: convertIndex(startIndex), range: addRange});
         }
 
         function scanIdentifier(last) {
@@ -402,6 +396,13 @@
             return true;
         }
 
+        function convertIndex(rangeIndex) {
+            if (source === originalSource) {
+                return rangeIndex;
+            }
+            return convertUnwrappedCommentIndex(originalSource, rangeIndex);
+        }
+
         function TagParser(options, title) {
             this._options = options;
             this._title = title.toLowerCase();
@@ -412,6 +413,7 @@
             if (this._options.lineNumbers) {
                 this._tag.lineNumber = lineNumber;
             }
+            this._first = index - title.length - 1;
             this._last = 0;
             // space to save special information for title parsers.
             this._extra = { };
@@ -442,7 +444,7 @@
             // type required titles
             if (isTypeParameterRequired(this._title)) {
                 try {
-                    this._tag.type = parseType(this._title, this._last);
+                    this._tag.type = parseType(this._title, this._last, this._options.range);
                     if (!this._tag.type) {
                         if (!isParamTitle(this._title) && !isReturnTitle(this._title)) {
                             if (!this.addError('Missing or invalid tag type')) {
@@ -459,7 +461,7 @@
             } else if (isAllowedType(this._title)) {
                 // optional types
                 try {
-                    this._tag.type = parseType(this._title, this._last);
+                    this._tag.type = parseType(this._title, this._last, this._options.range);
                 } catch (e) {
                     //For optional types, lets drop the thrown error when we hit the end of the file
                 }
@@ -751,6 +753,10 @@
             // Seek to content last index.
             this._last = seekContent(this._title);
 
+            if (this._options.range) {
+                this._tag.range = [this._first, source.slice(0, this._last).replace(/\s*$/, '').length].map(convertIndex);
+            }
+
             if (hasOwnProperty(Rules, this._title)) {
                 sequences = Rules[this._title];
             } else {
@@ -831,6 +837,8 @@
                 source = comment;
             }
 
+            originalSource = comment;
+
             // array of relevant tags
             if (options.tags) {
                 if (Array.isArray(options.tags)) {
diff --git a/lib/typed.js b/lib/typed.js
index e5b14f7..bdd3c39 100644
--- a/lib/typed.js
+++ b/lib/typed.js
@@ -19,7 +19,9 @@
         token,
         value,
         esutils,
-        utility;
+        utility,
+        rangeOffset,
+        addRange;
 
     esutils = require('esutils');
     utility = require('./utility');
@@ -94,6 +96,13 @@
         return new Context(previous, index, token, value);
     };
 
+    function maybeAddRange(node, range) {
+        if (addRange) {
+            node.range = [range[0] + rangeOffset, range[1] + rangeOffset];
+        }
+        return node;
+    }
+
     function advance() {
         var ch = source.charAt(index);
         index += 1;
@@ -508,7 +517,7 @@
     //     TypeExpression
     //   | TypeExpression '|' NonemptyTypeUnionList
     function parseUnionType() {
-        var elements;
+        var elements, startIndex = index - 1;
         consume(Token.LPAREN, 'UnionType should start with (');
         elements = [];
         if (token !== Token.RPAREN) {
@@ -521,10 +530,10 @@
             }
         }
         consume(Token.RPAREN, 'UnionType should end with )');
-        return {
+        return maybeAddRange({
             type: Syntax.UnionType,
             elements: elements
-        };
+        }, [startIndex, previous]);
     }
 
     // ArrayType := '[' ElementTypeList ']'
@@ -535,16 +544,17 @@
     //  | '...' TypeExpression
     //  | TypeExpression ',' ElementTypeList
     function parseArrayType() {
-        var elements;
+        var elements, startIndex = index - 1, restStartIndex;
         consume(Token.LBRACK, 'ArrayType should start with [');
         elements = [];
         while (token !== Token.RBRACK) {
             if (token === Token.REST) {
+                restStartIndex = index - 3;
                 consume(Token.REST);
-                elements.push({
+                elements.push(maybeAddRange({
                     type: Syntax.RestType,
                     expression: parseTypeExpression()
-                });
+                }, [restStartIndex, previous]));
                 break;
             } else {
                 elements.push(parseTypeExpression());
@@ -554,10 +564,10 @@
             }
         }
         expect(Token.RBRACK);
-        return {
+        return maybeAddRange({
             type: Syntax.ArrayType,
             elements: elements
-        };
+        }, [startIndex, previous]);
     }
 
     function parseFieldName() {
@@ -585,22 +595,22 @@
     //   | NumberLiteral
     //   | ReservedIdentifier
     function parseFieldType() {
-        var key;
+        var key, rangeStart = previous;
 
         key = parseFieldName();
         if (token === Token.COLON) {
             consume(Token.COLON);
-            return {
+            return maybeAddRange({
                 type: Syntax.FieldType,
                 key: key,
                 value: parseTypeExpression()
-            };
+            }, [rangeStart, previous]);
         }
-        return {
+        return maybeAddRange({
             type: Syntax.FieldType,
             key: key,
             value: null
-        };
+        }, [rangeStart, previous]);
     }
 
     // RecordType := '{' FieldTypeList '}'
@@ -610,7 +620,7 @@
     //   | FieldType
     //   | FieldType ',' FieldTypeList
     function parseRecordType() {
-        var fields;
+        var fields, rangeStart = index - 1, rangeEnd;
 
         consume(Token.LBRACE, 'RecordType should start with {');
         fields = [];
@@ -624,11 +634,12 @@
                 }
             }
         }
+        rangeEnd = index;
         expect(Token.RBRACE);
-        return {
+        return maybeAddRange({
             type: Syntax.RecordType,
             fields: fields
-        };
+        }, [rangeStart, rangeEnd]);
     }
 
     // NameExpression :=
@@ -639,7 +650,7 @@
     // Identifier is the same as Token.NAME, including any dots, something like
     // namespace.module.MyClass
     function parseNameExpression() {
-        var name = value;
+        var name = value, rangeStart = index - name.length;
         expect(Token.NAME);
 
         if (token === Token.COLON && (
@@ -651,10 +662,10 @@
             expect(Token.NAME);
         }
 
-        return {
+        return maybeAddRange({
             type: Syntax.NameExpression,
             name: name
-        };
+        }, [rangeStart, previous]);
     }
 
     // TypeExpressionList :=
@@ -679,18 +690,18 @@
     //     '.<' TypeExpressionList '>'
     //   | '<' TypeExpressionList '>'   // this is extension of doctrine
     function parseTypeName() {
-        var expr, applications;
+        var expr, applications, startIndex = index - value.length;
 
         expr = parseNameExpression();
         if (token === Token.DOT_LT || token === Token.LT) {
             next();
             applications = parseTypeExpressionList();
             expect(Token.GT);
-            return {
+            return maybeAddRange({
                 type: Syntax.TypeApplication,
                 expression: expr,
                 applications: applications
-            };
+            }, [startIndex, previous]);
         }
         return expr;
     }
@@ -737,7 +748,7 @@
     //
     // Identifier is "new" or "this"
     function parseParametersType() {
-        var params = [], optionalSequence = false, expr, rest = false;
+        var params = [], optionalSequence = false, expr, rest = false, startIndex, restStartIndex = index - 3, nameStartIndex;
 
         while (token !== Token.RPAREN) {
             if (token === Token.REST) {
@@ -746,22 +757,25 @@
                 rest = true;
             }
 
+            startIndex = previous;
+
             expr = parseTypeExpression();
             if (expr.type === Syntax.NameExpression && token === Token.COLON) {
+                nameStartIndex = previous - expr.name.length;
                 // Identifier ':' TypeExpression
                 consume(Token.COLON);
-                expr = {
+                expr = maybeAddRange({
                     type: Syntax.ParameterType,
                     name: expr.name,
                     expression: parseTypeExpression()
-                };
+                }, [nameStartIndex, previous]);
             }
             if (token === Token.EQUAL) {
                 consume(Token.EQUAL);
-                expr = {
+                expr = maybeAddRange({
                     type: Syntax.OptionalType,
                     expression: expr
-                };
+                }, [startIndex, previous]);
                 optionalSequence = true;
             } else {
                 if (optionalSequence) {
@@ -769,10 +783,10 @@
                 }
             }
             if (rest) {
-                expr = {
+                expr = maybeAddRange({
                     type: Syntax.RestType,
                     expression: expr
-                };
+                }, [restStartIndex, previous]);
             }
             params.push(expr);
             if (token !== Token.RPAREN) {
@@ -790,7 +804,7 @@
     //   | TypeParameters '(' 'this' ':' TypeName ')' ResultType
     //   | TypeParameters '(' 'this' ':' TypeName ',' ParametersType ')' ResultType
     function parseFunctionType() {
-        var isNew, thisBinding, params, result, fnType;
+        var isNew, thisBinding, params, result, fnType, startIndex = index - value.length;
         utility.assert(token === Token.NAME && value === 'function', 'FunctionType should start with \'function\'');
         consume(Token.NAME);
 
@@ -827,11 +841,11 @@
             result = parseResultType();
         }
 
-        fnType = {
+        fnType = maybeAddRange({
             type: Syntax.FunctionType,
             params: params,
             result: result
-        };
+        }, [startIndex, previous]);
         if (thisBinding) {
             // avoid adding null 'new' and 'this' properties
             fnType['this'] = thisBinding;
@@ -852,13 +866,13 @@
     //   | RecordType
     //   | ArrayType
     function parseBasicTypeExpression() {
-        var context;
+        var context, startIndex;
         switch (token) {
         case Token.STAR:
             consume(Token.STAR);
-            return {
+            return maybeAddRange({
                 type: Syntax.AllLiteral
-            };
+            }, [previous - 1, previous]);
 
         case Token.LPAREN:
             return parseUnionType();
@@ -870,26 +884,28 @@
             return parseRecordType();
 
         case Token.NAME:
+            startIndex = index - value.length;
+
             if (value === 'null') {
                 consume(Token.NAME);
-                return {
+                return maybeAddRange({
                     type: Syntax.NullLiteral
-                };
+                }, [startIndex, previous]);
             }
 
             if (value === 'undefined') {
                 consume(Token.NAME);
-                return {
+                return maybeAddRange({
                     type: Syntax.UndefinedLiteral
-                };
+                }, [startIndex, previous]);
             }
 
             if (value === 'true' || value === 'false') {
                 consume(Token.NAME);
-                return {
+                return maybeAddRange({
                     type: Syntax.BooleanLiteralType,
                     value: value === 'true'
-                };
+                }, [startIndex, previous]);
             }
 
             context = Context.save();
@@ -905,17 +921,17 @@
 
         case Token.STRING:
             next();
-            return {
+            return maybeAddRange({
                 type: Syntax.StringLiteralType,
                 value: value
-            };
+            }, [previous - value.length - 2, previous]);
 
         case Token.NUMBER:
             next();
-            return {
+            return maybeAddRange({
                 type: Syntax.NumericLiteralType,
                 value: value
-            };
+            }, [previous - String(value).length, previous]);
 
         default:
             utility.throwError('unexpected token');
@@ -931,63 +947,65 @@
     //   | '?'
     //   | BasicTypeExpression '[]'
     function parseTypeExpression() {
-        var expr;
+        var expr, rangeStart;
 
         if (token === Token.QUESTION) {
+            rangeStart = index - 1;
             consume(Token.QUESTION);
             if (token === Token.COMMA || token === Token.EQUAL || token === Token.RBRACE ||
                     token === Token.RPAREN || token === Token.PIPE || token === Token.EOF ||
                     token === Token.RBRACK || token === Token.GT) {
-                return {
+                return maybeAddRange({
                     type: Syntax.NullableLiteral
-                };
+                }, [rangeStart, previous]);
             }
-            return {
+            return maybeAddRange({
                 type: Syntax.NullableType,
                 expression: parseBasicTypeExpression(),
                 prefix: true
-            };
-        }
-
-        if (token === Token.BANG) {
+            }, [rangeStart, previous]);
+        } else if (token === Token.BANG) {
+            rangeStart = index - 1;
             consume(Token.BANG);
-            return {
+            return maybeAddRange({
                 type: Syntax.NonNullableType,
                 expression: parseBasicTypeExpression(),
                 prefix: true
-            };
+            }, [rangeStart, previous]);
+        } else {
+            rangeStart = previous;
         }
 
         expr = parseBasicTypeExpression();
         if (token === Token.BANG) {
             consume(Token.BANG);
-            return {
+            return maybeAddRange({
                 type: Syntax.NonNullableType,
                 expression: expr,
                 prefix: false
-            };
+            }, [rangeStart, previous]);
         }
 
         if (token === Token.QUESTION) {
             consume(Token.QUESTION);
-            return {
+            return maybeAddRange({
                 type: Syntax.NullableType,
                 expression: expr,
                 prefix: false
-            };
+            }, [rangeStart, previous]);
         }
 
         if (token === Token.LBRACK) {
             consume(Token.LBRACK);
             expect(Token.RBRACK, 'expected an array-style type declaration (' + value + '[])');
-            return {
+            return maybeAddRange({
                 type: Syntax.TypeApplication,
-                expression: {
+                expression: maybeAddRange({
                     type: Syntax.NameExpression,
                     name: 'Array'
-                },
+                }, [rangeStart, previous]),
                 applications: [expr]
-            };
+            }, [rangeStart, previous]);
         }
 
         return expr;
@@ -1020,10 +1038,10 @@
             consume(Token.PIPE);
         }
 
-        return {
+        return maybeAddRange({
             type: Syntax.UnionType,
             elements: elements
-        };
+        }, [0, index]);
     }
 
     function parseTopParamType() {
@@ -1031,19 +1049,19 @@
 
         if (token === Token.REST) {
             consume(Token.REST);
-            return {
+            return maybeAddRange({
                 type: Syntax.RestType,
                 expression: parseTop()
-            };
+            }, [0, index]);
         }
 
         expr = parseTop();
         if (token === Token.EQUAL) {
             consume(Token.EQUAL);
-            return {
+            return maybeAddRange({
                 type: Syntax.OptionalType,
                 expression: expr
-            };
+            }, [0, index]);
         }
 
         return expr;
@@ -1056,6 +1074,8 @@
         length = source.length;
         index = 0;
         previous = 0;
+        addRange = opt && opt.range;
+        rangeOffset = opt && opt.startIndex || 0;
 
         next();
         expr = parseTop();
@@ -1081,6 +1101,8 @@
         length = source.length;
         index = 0;
         previous = 0;
+        addRange = opt && opt.range;
+        rangeOffset = opt && opt.startIndex || 0;
 
         next();
         expr = parseTopParamType();
diff --git a/package-lock.json b/package-lock.json
index 197d707..9e20b5a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "doctrine",
-  "version": "2.0.2",
+  "version": "2.1.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/package.json b/package.json
index d309199..92667d3 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
   "description": "JSDoc parser",
   "homepage": "https://github.com/eslint/doctrine",
   "main": "lib/doctrine.js",
-  "version": "2.0.2",
+  "version": "2.1.0",
   "engines": {
     "node": ">=0.10.0"
   },
diff --git a/test/parse.js b/test/parse.js
index 51886db..224efd6 100644
--- a/test/parse.js
+++ b/test/parse.js
@@ -1387,125 +1387,149 @@ describe('parse', function () {
 
 describe('parseType', function () {
     it('union type closure-compiler extended', function () {
-        var type = doctrine.parseType("string|number");
+        var type = doctrine.parseType("string|number", {range: true});
         type.should.eql({
             type: 'UnionType',
             elements: [{
                 type: 'NameExpression',
-                name: 'string'
+                name: 'string',
+                range: [0, 6]
             }, {
                 type: 'NameExpression',
-                name: 'number'
-            }]
+                name: 'number',
+                range: [7, 13]
+            }],
+            range: [0, 13]
         });
     });
 
     it('empty union type', function () {
-        var type = doctrine.parseType("()");
+        var type = doctrine.parseType("()", {range: true});
         type.should.eql({
             type: 'UnionType',
-            elements: []
+            elements: [],
+            range: [0, 2]
         });
     });
 
     it('comma last array type', function () {
-        var type = doctrine.parseType("[string,]");
+        var type = doctrine.parseType("[string,]", {range: true});
         type.should.eql({
             type: 'ArrayType',
             elements: [{
                 type: 'NameExpression',
-                name: 'string'
-            }]
+                name: 'string',
+                range: [1, 7]
+            }],
+            range: [0, 9]
         });
     });
 
     it('array type of all literal', function () {
-        var type = doctrine.parseType("[*]");
+        var type = doctrine.parseType("[*]", {range: true});
         type.should.eql({
             type: 'ArrayType',
             elements: [{
-                type: 'AllLiteral'
-            }]
+                type: 'AllLiteral',
+                range: [1, 2]
+            }],
+            range: [0, 3]
         });
     });
 
     it('array type of nullable literal', function () {
-        var type = doctrine.parseType("[?]");
+        var type = doctrine.parseType("[?]", {range: true});
         type.should.eql({
             type: 'ArrayType',
             elements: [{
-                type: 'NullableLiteral'
-            }]
+                type: 'NullableLiteral',
+                range: [1, 2]
+            }],
+            range: [0, 3]
         });
     });
 
     it('comma last record type', function () {
-        var type = doctrine.parseType("{,}");
+        var type = doctrine.parseType("{,}", {range: true});
         type.should.eql({
             type: 'RecordType',
-            fields: []
+            fields: [],
+            range: [0, 3]
         });
     });
 
     it('type application', function () {
-        var type = doctrine.parseType("Array.<String>");
+        var type = doctrine.parseType("Array.<String>", {range: true});
         type.should.eql({
             type: 'TypeApplication',
             expression: {
                 type: 'NameExpression',
-                name: 'Array'
+                name: 'Array',
+                range: [0, 5]
             },
             applications: [{
                 type: 'NameExpression',
-                name: 'String'
-            }]
+                name: 'String',
+                range: [7, 13]
+            }],
+            range: [0, 14]
         });
     });
 
     it('type application with NullableLiteral', function () {
-        var type = doctrine.parseType("Array<?>");
+        var type = doctrine.parseType("Array<?>", {range: true});
         type.should.eql({
             type: 'TypeApplication',
             expression: {
                 type: 'NameExpression',
-                name: 'Array'
+                name: 'Array',
+                range: [0, 5]
             },
             applications: [{
-                type: 'NullableLiteral'
-            }]
+                type: 'NullableLiteral',
+                range: [6, 7]
+            }],
+            range: [0, 8]
         });
     });
 
     it('type application with multiple patterns', function () {
-        var type = doctrine.parseType("Array.<String, Number>");
+        var type = doctrine.parseType("Array.<String, Number>", {range: true});
         type.should.eql({
             type: 'TypeApplication',
             expression: {
                 type: 'NameExpression',
-                name: 'Array'
+                name: 'Array',
+                range: [0, 5]
             },
             applications: [{
                 type: 'NameExpression',
-                name: 'String'
+                name: 'String',
+                range: [7, 13]
             }, {
                 type: 'NameExpression',
-                name: 'Number'
-            }]
+                name: 'Number',
+                range: [15, 21]
+            }],
+            range: [0, 22]
         });
     });
 
     it('type application without dot', function () {
-        var type = doctrine.parseType("Array<String>");
+        var type = doctrine.parseType("Array<String>", {range: true});
         type.should.eql({
             type: 'TypeApplication',
             expression: {
                 type: 'NameExpression',
-                name: 'Array'
+                name: 'Array',
+                range: [0, 5]
             },
             applications: [{
                 type: 'NameExpression',
-                name: 'String'
-            }]
+                name: 'String',
+                range: [6, 12]
+            }],
+            range: [0, 13]
         });
     });
 
@@ -1525,163 +1549,193 @@ describe('parseType', function () {
     });
 
     it('function type simple', function () {
-        var type = doctrine.parseType("function()");
+        var type = doctrine.parseType("function()", {range: true});
         type.should.eql({
-		 "type": "FunctionType",
-		 "params": [],
-		 "result": null
-		});
+            "type": "FunctionType",
+            "params": [],
+            "result": null,
+            range: [0, 10]
+        });
     });
 
     it('function type with name', function () {
-        var type = doctrine.parseType("function(a)");
+        var type = doctrine.parseType("function(a)", {range: true});
         type.should.eql({
-		 "type": "FunctionType",
-		 "params": [
-		{
-		   "type": "NameExpression",
-		   "name": "a"
-		  }
-			 ],
-		 "result": null
-		});
+            "type": "FunctionType",
+            "params": [
+                {
+                    "type": "NameExpression",
+                    "name": "a",
+                    range: [9, 10]
+                }
+            ],
+            "result": null,
+            range: [0, 11]
+        });
     });
     it('function type with name and type', function () {
-        var type = doctrine.parseType("function(a:b)");
+        var type = doctrine.parseType("function(a:b)", {range: true});
         type.should.eql({
-		 "type": "FunctionType",
-		 "params": [
-		  {
-		   "type": "ParameterType",
-		   "name": "a",
-		   "expression": {
-		    "type": "NameExpression",
-		    "name": "b"
-		   }
-		  }
-		 ],
-		 "result": null
-		});
+            "type": "FunctionType",
+            "params": [
+                {
+                    "type": "ParameterType",
+                    "name": "a",
+                    "expression": {
+                        "type": "NameExpression",
+                        "name": "b",
+                        range: [11, 12]
+                    },
+                    range: [9, 12]
+                }
+            ],
+            "result": null,
+            range: [0, 13]
+        });
     });
     it('function type with optional param', function () {
-        var type = doctrine.parseType("function(a=)");
+        var type = doctrine.parseType("function(a=)", {range: true});
         type.should.eql({
-		 "type": "FunctionType",
-		 "params": [
-		  {
-		   "type": "OptionalType",
-		   "expression": {
-		    "type": "NameExpression",
-		    "name": "a"
-		   }
-		  }
-		 ],
-		 "result": null
-		});
+            "type": "FunctionType",
+            "params": [
+                {
+                    "type": "OptionalType",
+                    "expression": {
+                        "type": "NameExpression",
+                        "name": "a",
+                        range: [9, 10]
+                    },
+                    range: [9, 11]
+                }
+            ],
+            "result": null,
+            range: [0, 12]
+        });
     });
     it('function type with optional param name and type', function () {
-        var type = doctrine.parseType("function(a:b=)");
+        var type = doctrine.parseType("function(a:b=)", {range: true});
         type.should.eql({
-		 "type": "FunctionType",
-		 "params": [
-		  {
-		   "type": "OptionalType",
-		   "expression": {
-		    "type": "ParameterType",
-		    "name": "a",
-		    "expression": {
-		     "type": "NameExpression",
-		     "name": "b"
-		    }
-		   }
-		  }
-		 ],
-		 "result": null
-		});
+            "type": "FunctionType",
+            "params": [
+                {
+                    "type": "OptionalType",
+                    "expression": {
+                        "type": "ParameterType",
+                        "name": "a",
+                        "expression": {
+                            "type": "NameExpression",
+                            "name": "b",
+                            range: [11, 12]
+                        },
+                        range: [9, 12]
+                    },
+                    range: [9, 13]
+                }
+            ],
+            "result": null,
+            range: [0, 14]
+        });
     });
     it('function type with rest param', function () {
-        var type = doctrine.parseType("function(...a)");
+        var type = doctrine.parseType("function(...a)", {range: true});
         type.should.eql({
-		 "type": "FunctionType",
-		 "params": [
-		  {
-		   "type": "RestType",
-		   "expression": {
-		    "type": "NameExpression",
-		    "name": "a"
-		   }
-		  }
-		 ],
-		 "result": null
-		});
+            "type": "FunctionType",
+            "params": [
+                {
+                    "type": "RestType",
+                    "expression": {
+                        "type": "NameExpression",
+                        "name": "a",
+                        range: [12, 13]
+                    },
+                    range: [9, 13]
+                }
+            ],
+            "result": null,
+            range: [0, 14]
+        });
     });
     it('function type with rest param name and type', function () {
-        var type = doctrine.parseType("function(...a:b)");
+        var type = doctrine.parseType("function(...a:b)", {range: true});
         type.should.eql({
-		 "type": "FunctionType",
-		 "params": [
-		  {
-		   "type": "RestType",
-		   "expression": {
-		    "type": "ParameterType",
-		    "name": "a",
-		    "expression": {
-		     "type": "NameExpression",
-		     "name": "b"
-		    }
-		   }
-		  }
-		 ],
-		 "result": null
-		});
+            "type": "FunctionType",
+            "params": [
+                {
+                    "type": "RestType",
+                    "expression": {
+                        "type": "ParameterType",
+                        "name": "a",
+                        "expression": {
+                            "type": "NameExpression",
+                            "name": "b",
+                            range: [14, 15]
+                        },
+                        range: [12, 15]
+                    },
+                    range: [9, 15]
+                }
+            ],
+            "result": null,
+            range: [0, 16]
+        });
     });
 
     it('function type with optional rest param', function () {
-        var type = doctrine.parseType("function(...a=)");
+        var type = doctrine.parseType("function(...a=)", {range: true});
         type.should.eql({
-		 "type": "FunctionType",
-		 "params": [
-		  {
-			"type": "RestType",
-			"expression": {
-			   "type": "OptionalType",
-			   "expression": {
-			    "type": "NameExpression",
-			    "name": "a"
-			   }
-			  }
-			}
-		 ],
-		 "result": null
-		});
+            "type": "FunctionType",
+            "params": [
+                {
+                    "type": "RestType",
+                    "expression": {
+                        "type": "OptionalType",
+                        "expression": {
+                            "type": "NameExpression",
+                            "name": "a",
+                            range: [12, 13]
+                        },
+                        range: [12, 14]
+                    },
+                    range: [9, 14]
+                }
+            ],
+            "result": null,
+            range: [0, 15]
+        });
     });
     it('function type with optional rest param name and type', function () {
-        var type = doctrine.parseType("function(...a:b=)");
+        var type = doctrine.parseType("function(...a:b=)", {range: true});
         type.should.eql({
-		 "type": "FunctionType",
-		 "params": [
-		  {
-			"type": "RestType",
-			"expression": {
-			   "type": "OptionalType",
-			   "expression": {
-			    "type": "ParameterType",
-			    "name": "a",
-			    "expression": {
-			     "type": "NameExpression",
-			     "name": "b"
-			    }
-			  }
-			}
-		 }],
-		 "result": null
-		});
+            "type": "FunctionType",
+            "params": [
+                {
+                    "type": "RestType",
+                    "expression": {
+                        "type": "OptionalType",
+                        "expression": {
+                            "type": "ParameterType",
+                            "name": "a",
+                            "expression": {
+                                "type": "NameExpression",
+                                "name": "b",
+                                range: [14, 15]
+                            },
+                            range: [12, 15]
+                        },
+                        range: [12, 16]
+                    },
+                    range: [9, 16]
+                }
+            ],
+            "result": null,
+            range: [0, 17]
+        });
     });
 
     it('string value in type', function () {
         var type;
 
-        type = doctrine.parseType("{'ok':String}");
+        type = doctrine.parseType("{'ok':String}", {range: true});
         type.should.eql({
             "fields": [
                 {
@@ -1689,14 +1743,17 @@ describe('parseType', function () {
                     "type": "FieldType",
                     "value": {
                         "name": "String",
-                        "type": "NameExpression"
-                    }
+                        "type": "NameExpression",
+                        range: [6, 12]
+                    },
+                    range: [1, 12]
                 }
             ],
-            "type": "RecordType"
+            "type": "RecordType",
+            range: [0, 13]
         });
 
-        type = doctrine.parseType('{"\\r\\n\\t\\u2028\\x20\\u20\\b\\f\\v\\\r\n\\\n\\0\\07\\012\\o":String}');
+        type = doctrine.parseType('{"\\r\\n\\t\\u2028\\x20\\u20\\b\\f\\v\\\r\n\\\n\\0\\07\\012\\o":String}', {range: true});
         type.should.eql({
             "fields": [
                 {
@@ -1704,11 +1761,14 @@ describe('parseType', function () {
                     "type": "FieldType",
                     "value": {
                         "name": "String",
-                        "type": "NameExpression"
-                    }
+                        "type": "NameExpression",
+                        range: [46, 52]
+                    },
+                    range: [1, 52]
                 }
             ],
-            "type": "RecordType"
+            "type": "RecordType",
+            range: [0, 53]
         });
 
         doctrine.parseType.bind(doctrine, "{'ok\":String}").should.throw('unexpected quote');
@@ -1718,7 +1778,7 @@ describe('parseType', function () {
     it('number value in type', function () {
         var type;
 
-        type = doctrine.parseType("{20:String}");
+        type = doctrine.parseType("{20:String}", {range: true});
         type.should.eql({
             "fields": [
                 {
@@ -1726,11 +1786,14 @@ describe('parseType', function () {
                     "type": "FieldType",
                     "value": {
                         "name": "String",
-                        "type": "NameExpression"
-                    }
+                        "type": "NameExpression",
+                        range: [4, 10]
+                    },
+                    range: [1, 10]
                 }
             ],
-            "type": "RecordType"
+            "type": "RecordType",
+            range: [0, 11]
         });
 
         type = doctrine.parseType("{.2:String, 30:Number, 0x20:String}");
@@ -1824,116 +1887,134 @@ describe('parseType', function () {
 
     it('dotted type', function () {
         var type;
-        type = doctrine.parseType("Cocoa.Cappuccino");
+        type = doctrine.parseType("Cocoa.Cappuccino", {range: true});
         type.should.eql({
             "name": "Cocoa.Cappuccino",
-            "type": "NameExpression"
+            "type": "NameExpression",
+            range: [0, 16]
         });
     });
 
     it('rest array type', function () {
         var type;
-        type = doctrine.parseType("[string,...string]");
+        type = doctrine.parseType("[string,...string]", {range: true});
         type.should.eql({
             "elements": [
                 {
                     "name": "string",
-                    "type": "NameExpression"
+                    "type": "NameExpression",
+                    range: [1, 7]
                 },
                 {
                     "expression": {
                         "name": "string",
-                        "type": "NameExpression"
+                        "type": "NameExpression",
+                        range: [11, 17]
                     },
-                "type": "RestType"
+                    "type": "RestType",
+                    range: [8, 17]
                 }
             ],
-            "type": "ArrayType"
+            "type": "ArrayType",
+            range: [0, 18]
         });
     });
 
     it ('nullable type', function () {
         var type;
-        type = doctrine.parseType("string?");
+        type = doctrine.parseType("string?", {range: true});
         type.should.eql({
             "expression": {
                 "name": "string",
-                "type": "NameExpression"
+                "type": "NameExpression",
+                range: [0, 6]
             },
             "prefix": false,
-            "type": "NullableType"
+            "type": "NullableType",
+            range: [0, 7]
         });
     });
 
     it ('non-nullable type', function () {
         var type;
-        type = doctrine.parseType("string!");
+        type = doctrine.parseType("string!", {range: true});
         type.should.eql({
             "expression": {
                 "name": "string",
-                "type": "NameExpression"
+                "type": "NameExpression",
+                range: [0, 6]
             },
             "prefix": false,
-            "type": "NonNullableType"
+            "type": "NonNullableType",
+            range: [0, 7]
         });
     });
 
     it ('toplevel multiple pipe type', function () {
         var type;
-        type = doctrine.parseType("string|number|Test");
+        type = doctrine.parseType("string|number|Test", {range: true});
         type.should.eql({
             "elements": [
                 {
                     "name": "string",
-                    "type": "NameExpression"
+                    "type": "NameExpression",
+                    range: [0, 6]
                 },
                 {
                     "name": "number",
-                    "type": "NameExpression"
+                    "type": "NameExpression",
+                    range: [7, 13]
                 },
                 {
                     "name": "Test",
-                    "type": "NameExpression"
+                    "type": "NameExpression",
+                    range: [14, 18]
                 }
             ],
-            "type": "UnionType"
+            "type": "UnionType",
+            range: [0, 18]
         });
     });
 
     it('string literal type', function () {
         var type;
-        type = doctrine.parseType('"Hello, World"');
+        type = doctrine.parseType('"Hello, World"', {range: true});
         type.should.eql({
             type: 'StringLiteralType',
-            value: 'Hello, World'
+            value: 'Hello, World',
+            range: [0, 14]
         });
     });
 
     it('numeric literal type', function () {
         var type;
-        type = doctrine.parseType('32');
+        type = doctrine.parseType('32', {range: true});
         type.should.eql({
             type: 'NumericLiteralType',
-            value: 32
+            value: 32,
+            range: [0, 2]
         });
-        type = doctrine.parseType('-142.42');
+        type = doctrine.parseType('-142.42', {range: true});
         type.should.eql({
             type: 'NumericLiteralType',
-            value: -142.42
+            value: -142.42,
+            range: [0, 7]
         });
     });
 
     it('boolean literal type', function () {
         var type;
-        type = doctrine.parseType('true');
+        type = doctrine.parseType('true', {range: true});
         type.should.eql({
             type: 'BooleanLiteralType',
-            value: true
+            value: true,
+            range: [0, 4]
         });
-        type = doctrine.parseType('false');
+        type = doctrine.parseType('false', {range: true});
         type.should.eql({
             type: 'BooleanLiteralType',
-            value: false
+            value: false,
+            range: [0, 5]
         });
     });
 
@@ -1947,64 +2028,77 @@ describe('parseType', function () {
 
 describe('parseParamType', function () {
     it('question', function () {
-        var type = doctrine.parseParamType("?");
+        var type = doctrine.parseParamType("?", {range: true});
         type.should.eql({
-            type: 'NullableLiteral'
+            type: 'NullableLiteral',
+            range: [0, 1]
         });
     });
 
     it('question option', function () {
-        var type = doctrine.parseParamType("?=");
+        var type = doctrine.parseParamType("?=", {range: true});
         type.should.eql({
             type: 'OptionalType',
             expression: {
-                type: 'NullableLiteral'
-            }
+                type: 'NullableLiteral',
+                range: [0, 1]
+            },
+            range: [0, 2]
         });
     });
 
     it('function option parameters former', function () {
-        var type = doctrine.parseParamType("function(?, number)");
+        var type = doctrine.parseParamType("function(?, number)", {range: true});
         type.should.eql({
             type: 'FunctionType',
             params: [{
-                type: 'NullableLiteral'
+                type: 'NullableLiteral',
+                range: [9, 10]
             }, {
                 type: 'NameExpression',
-                name: 'number'
+                name: 'number',
+                range: [12, 18]
             }],
-            result: null
+            result: null,
+            range: [0, 19]
         });
     });
 
     it('function option parameters latter', function () {
-        var type = doctrine.parseParamType("function(number, ?)");
+        var type = doctrine.parseParamType("function(number, ?)", {range: true});
         type.should.eql({
             type: 'FunctionType',
             params: [{
                 type: 'NameExpression',
-                name: 'number'
+                name: 'number',
+                range: [9, 15]
             }, {
-                type: 'NullableLiteral'
+                type: 'NullableLiteral',
+                range: [17, 18]
             }],
-            result: null
+            result: null,
+            range: [0, 19]
         });
     });
 
     it('function type union', function () {
-        var type = doctrine.parseParamType("function(): ?|number");
+        var type = doctrine.parseParamType("function(): ?|number", {range: true});
         type.should.eql({
             type: 'UnionType',
             elements: [{
                 type: 'FunctionType',
                 params: [],
                 result: {
-                    type: 'NullableLiteral'
-                }
+                    type: 'NullableLiteral',
+                    range: [12, 13]
+                },
+                range: [0, 13]
             }, {
                 type: 'NameExpression',
-                name: 'number'
-            }]
+                name: 'number',
+                range: [14, 20]
+            }],
+            range: [0, 20]
         });
     });
 });
@@ -2365,6 +2459,102 @@ describe('optional params', function() {
         res.tags[3].should.have.property('lineNumber', 5);
     });
 
+    it('range', function() {
+        var res = doctrine.parse(
+            [
+                "/**",
+                " * foo",
+                " * @constructor",
+                " * @param {string} foo",
+                " * @returns {string}",
+                " *",
+                " * @example",
+                " * f('blah'); // => undefined",
+                " */"
+            ].join('\n'),
+            { unwrap: true, range: true }
+        );
+
+
+        res.should.eql({
+          description: 'foo',
+          tags: [
+            { title: "constructor", description: null, range: [14, 26], type: null, name: null },
+            { title: "param", description: null, range: [30, 49], type: { type: "NameExpression", name: "string", range: [38, 44] }, name: "foo" },
+            { title: "returns", description: null, range: [53, 70], type: { type: "NameExpression", name: "string", range: [63, 69] } },
+            { title: "example", description: "f('blah'); // => undefined", range: [77, 115] }
+          ]
+        });
+    });
+
+    it('range with nested types', function() {
+        var res = doctrine.parse("@param {{foo: string,bar: {baz: number}}} beep boop", { range: true });
+
+        res.should.eql({
+          description: '',
+          tags: [
+              {
+                  title: "param",
+                  description: "boop",
+                  name: "beep",
+                  type: {
+                      type: "RecordType",
+                      fields: [
+                          {
+                              type: "FieldType",
+                              key: "foo",
+                              value: {
+                                  type: "NameExpression",
+                                  name: "string",
+                                  range: [14, 20]
+                              },
+                              range: [9, 20]
+                          },
+                          {
+                              type: "FieldType",
+                              key: "bar",
+                              value: {
+                                  type: "RecordType",
+                                  fields: [
+                                      {
+                                          type: "FieldType",
+                                          key: "baz",
+                                          value: {
+                                              type: "NameExpression",
+                                              name: "number",
+                                              range: [32, 38]
+                                          },
+                                          range: [27, 38]
+                                      }
+                                  ],
+                                  range: [26, 39]
+                              },
+                              range: [21, 39]
+                          }
+                      ],
+                      range: [8, 40]
+                  },
+                  range: [0, 51]
+              }
+          ]
+        });
+    });
+
+    it('range reaching end of tag', function() {
+        var res = doctrine.parse("/**@example", { range: true, unwrap: true });
+
+        res.should.eql({
+            description: '',
+            tags: [
+                {
+                    title: 'example',
+                    description: '',
+                    range: [3, 11]
+                }
+            ]
+        })
+    });
+
     it('example caption', function() {
         var res = doctrine.parse(
             [

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-doctrine.git



More information about the Pkg-javascript-commits mailing list