diff --git a/README.md b/README.md index 1edd150..083ee6a 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ When a domain-specific language that compiles to JavaScript uses JSON as a forma #### Editing forms/JSON -When a form also allows to edit JSON representation of data on the same screen, this module can be used to sinchronise navigation in JSON and in the form. +When a form also allows to edit JSON representation of data on the same screen, this module can be used to synchronise navigation in JSON and in the form. ## Usage @@ -107,11 +107,12 @@ Location object has properties (zero-based numbers): Options: - _bigint_: parse large integers as [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt). +- _tabWidth_: tab width for column calculation Whitespace: - the only character that increases line number in mappings is line feed ('\n'), so if your JSON string has '\r\n' sequence, it will still be counted as one line, - both '\r' and '\n' are counted as a character when determining `pos` (it is possible to slice sections of JSON string using `pos` property), but `column` counter is reset when `r` or `n` is encountered, -- tabs ('\t') are counted as four spaces when determining `column` but as a single character for `pos`. +- tabs ('\t') are by default counted as four spaces (configurable through the `tabWidth` option) when determining `column` but as a single character for `pos`. Comparison with the standard `JSON.parse`: - when it is not possible to parse JSON, a SyntaxError exception with exactly the same message is thrown, @@ -132,6 +133,7 @@ Comparison with the standard `JSON.stringify`: Options: - _space_: same as `space` parameter. - _es6_: stringify ES6 Maps, Sets and Typed arrays (as JSON arrays). +- _tabWidth_: need to be the same as for `parse` to get identical result ## License diff --git a/index.js b/index.js index dbf5860..e600700 100644 --- a/index.js +++ b/index.js @@ -20,6 +20,7 @@ exports.parse = function (source, _, options) { var column = 0; var pos = 0; var bigint = options && options.bigint && typeof BigInt != 'undefined'; + var tabWidth = (options && options.tabWidth && typeof options.tabWidth === 'number') ? options.tabWidth : 4; return { data: _parse('', true), pointers: pointers @@ -55,7 +56,7 @@ exports.parse = function (source, _, options) { while (pos < source.length) { switch (source[pos]) { case ' ': column++; break; - case '\t': column += 4; break; + case '\t': column += tabWidth; break; case '\r': column = 0; break; case '\n': column = 0; line++; break; default: break loop; @@ -243,6 +244,10 @@ exports.stringify = function (data, _, options) { var whitespace = typeof options == 'object' ? options.space : options; + var tabWidth = (typeof options == 'object' && + typeof options.tabWidth === 'number') + ? options.tabWidth + : 4; switch (typeof whitespace) { case 'number': var len = whitespace > 10 @@ -262,7 +267,7 @@ exports.stringify = function (data, _, options) { var char = whitespace[j]; switch (char) { case ' ': wsColumn++; break; - case '\t': wsColumn += 4; break; + case '\t': wsColumn += tabWidth; break; case '\r': wsColumn = 0; break; case '\n': wsColumn = 0; wsLine++; break; default: throw new Error('whitespace characters not allowed in JSON'); diff --git a/spec/index.js b/spec/index.js index f254f4e..d38244b 100644 --- a/spec/index.js +++ b/spec/index.js @@ -95,6 +95,40 @@ describe('parse', function() { }); }); + it.only('should support whitespace with tabs and custom tabWidth', function () { + var json = '{\n\ +\t"foo": [\n\ +\t\t{\n\ +\t\t\t"bar": true\n\ +\t\t}\n\ +\t]\n\ +}'; + + var pointers = testParse(json, JSON.parse(json), null, '\t', {tabWidth: 1}); + assert.deepStrictEqual(pointers, { + '': { + value: { line: 0, column: 0, pos: 0 }, + valueEnd: { line: 6, column: 1, pos: 39 } + }, + '/foo': { + key: { line: 1, column: 1, pos: 3 }, + keyEnd: { line: 1, column: 6, pos: 8 }, + value: { line: 1, column: 8, pos: 10 }, + valueEnd: { line: 5, column: 2, pos: 37 } + }, + '/foo/0': { + value: { line: 2, column: 2, pos: 14 }, + valueEnd: { line: 4, column: 3, pos: 34 } + }, + '/foo/0/bar': { + key: { line: 3, column: 3, pos: 19 }, + keyEnd: { line: 3, column: 8, pos: 24 }, + value: { line: 3, column: 10, pos: 26 }, + valueEnd: { line: 3, column: 14, pos: 30 } + } + }); + }); + it('should support whitespace with CRs', function () { var json = '{\r\n\ "foo": [\r\n\ @@ -293,15 +327,17 @@ describe('parse', function() { }); - function testParse(json, expectedData, skipReverseCheck, whitespace) { - var result = jsonMap.parse(json); + function testParse(json, expectedData, skipReverseCheck, whitespace, options) { + var result = jsonMap.parse(json, null, options); var data = result.data; var pointers = result.pointers; assert.deepStrictEqual(data, expectedData); testResult(json, pointers, data); if (!skipReverseCheck) { - var reverseResult = jsonMap.stringify(expectedData, null, whitespace); + var reverseResult = jsonMap.stringify(expectedData, null, { + space: whitespace, tabWidth: options && options.tabWidth + }); assert.strictEqual(json, reverseResult.json); assert.deepStrictEqual(pointers, reverseResult.pointers); }