My dotfiles
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

985 line
38 KiB

  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. 'use strict';
  6. import { format as _format } from './format';
  7. import { setProperty, applyEdit } from './edit';
  8. export var ScanError;
  9. (function (ScanError) {
  10. ScanError[ScanError["None"] = 0] = "None";
  11. ScanError[ScanError["UnexpectedEndOfComment"] = 1] = "UnexpectedEndOfComment";
  12. ScanError[ScanError["UnexpectedEndOfString"] = 2] = "UnexpectedEndOfString";
  13. ScanError[ScanError["UnexpectedEndOfNumber"] = 3] = "UnexpectedEndOfNumber";
  14. ScanError[ScanError["InvalidUnicode"] = 4] = "InvalidUnicode";
  15. ScanError[ScanError["InvalidEscapeCharacter"] = 5] = "InvalidEscapeCharacter";
  16. ScanError[ScanError["InvalidCharacter"] = 6] = "InvalidCharacter";
  17. })(ScanError || (ScanError = {}));
  18. export var SyntaxKind;
  19. (function (SyntaxKind) {
  20. SyntaxKind[SyntaxKind["Unknown"] = 0] = "Unknown";
  21. SyntaxKind[SyntaxKind["OpenBraceToken"] = 1] = "OpenBraceToken";
  22. SyntaxKind[SyntaxKind["CloseBraceToken"] = 2] = "CloseBraceToken";
  23. SyntaxKind[SyntaxKind["OpenBracketToken"] = 3] = "OpenBracketToken";
  24. SyntaxKind[SyntaxKind["CloseBracketToken"] = 4] = "CloseBracketToken";
  25. SyntaxKind[SyntaxKind["CommaToken"] = 5] = "CommaToken";
  26. SyntaxKind[SyntaxKind["ColonToken"] = 6] = "ColonToken";
  27. SyntaxKind[SyntaxKind["NullKeyword"] = 7] = "NullKeyword";
  28. SyntaxKind[SyntaxKind["TrueKeyword"] = 8] = "TrueKeyword";
  29. SyntaxKind[SyntaxKind["FalseKeyword"] = 9] = "FalseKeyword";
  30. SyntaxKind[SyntaxKind["StringLiteral"] = 10] = "StringLiteral";
  31. SyntaxKind[SyntaxKind["NumericLiteral"] = 11] = "NumericLiteral";
  32. SyntaxKind[SyntaxKind["LineCommentTrivia"] = 12] = "LineCommentTrivia";
  33. SyntaxKind[SyntaxKind["BlockCommentTrivia"] = 13] = "BlockCommentTrivia";
  34. SyntaxKind[SyntaxKind["LineBreakTrivia"] = 14] = "LineBreakTrivia";
  35. SyntaxKind[SyntaxKind["Trivia"] = 15] = "Trivia";
  36. SyntaxKind[SyntaxKind["EOF"] = 16] = "EOF";
  37. })(SyntaxKind || (SyntaxKind = {}));
  38. /**
  39. * Creates a JSON scanner on the given text.
  40. * If ignoreTrivia is set, whitespaces or comments are ignored.
  41. */
  42. export function createScanner(text, ignoreTrivia) {
  43. if (ignoreTrivia === void 0) { ignoreTrivia = false; }
  44. var pos = 0, len = text.length, value = '', tokenOffset = 0, token = SyntaxKind.Unknown, scanError = ScanError.None;
  45. function scanHexDigits(count, exact) {
  46. var digits = 0;
  47. var value = 0;
  48. while (digits < count || !exact) {
  49. var ch = text.charCodeAt(pos);
  50. if (ch >= 48 /* _0 */ && ch <= 57 /* _9 */) {
  51. value = value * 16 + ch - 48 /* _0 */;
  52. }
  53. else if (ch >= 65 /* A */ && ch <= 70 /* F */) {
  54. value = value * 16 + ch - 65 /* A */ + 10;
  55. }
  56. else if (ch >= 97 /* a */ && ch <= 102 /* f */) {
  57. value = value * 16 + ch - 97 /* a */ + 10;
  58. }
  59. else {
  60. break;
  61. }
  62. pos++;
  63. digits++;
  64. }
  65. if (digits < count) {
  66. value = -1;
  67. }
  68. return value;
  69. }
  70. function setPosition(newPosition) {
  71. pos = newPosition;
  72. value = '';
  73. tokenOffset = 0;
  74. token = SyntaxKind.Unknown;
  75. scanError = ScanError.None;
  76. }
  77. function scanNumber() {
  78. var start = pos;
  79. if (text.charCodeAt(pos) === 48 /* _0 */) {
  80. pos++;
  81. }
  82. else {
  83. pos++;
  84. while (pos < text.length && isDigit(text.charCodeAt(pos))) {
  85. pos++;
  86. }
  87. }
  88. if (pos < text.length && text.charCodeAt(pos) === 46 /* dot */) {
  89. pos++;
  90. if (pos < text.length && isDigit(text.charCodeAt(pos))) {
  91. pos++;
  92. while (pos < text.length && isDigit(text.charCodeAt(pos))) {
  93. pos++;
  94. }
  95. }
  96. else {
  97. scanError = ScanError.UnexpectedEndOfNumber;
  98. return text.substring(start, pos);
  99. }
  100. }
  101. var end = pos;
  102. if (pos < text.length && (text.charCodeAt(pos) === 69 /* E */ || text.charCodeAt(pos) === 101 /* e */)) {
  103. pos++;
  104. if (pos < text.length && text.charCodeAt(pos) === 43 /* plus */ || text.charCodeAt(pos) === 45 /* minus */) {
  105. pos++;
  106. }
  107. if (pos < text.length && isDigit(text.charCodeAt(pos))) {
  108. pos++;
  109. while (pos < text.length && isDigit(text.charCodeAt(pos))) {
  110. pos++;
  111. }
  112. end = pos;
  113. }
  114. else {
  115. scanError = ScanError.UnexpectedEndOfNumber;
  116. }
  117. }
  118. return text.substring(start, end);
  119. }
  120. function scanString() {
  121. var result = '', start = pos;
  122. while (true) {
  123. if (pos >= len) {
  124. result += text.substring(start, pos);
  125. scanError = ScanError.UnexpectedEndOfString;
  126. break;
  127. }
  128. var ch = text.charCodeAt(pos);
  129. if (ch === 34 /* doubleQuote */) {
  130. result += text.substring(start, pos);
  131. pos++;
  132. break;
  133. }
  134. if (ch === 92 /* backslash */) {
  135. result += text.substring(start, pos);
  136. pos++;
  137. if (pos >= len) {
  138. scanError = ScanError.UnexpectedEndOfString;
  139. break;
  140. }
  141. ch = text.charCodeAt(pos++);
  142. switch (ch) {
  143. case 34 /* doubleQuote */:
  144. result += '\"';
  145. break;
  146. case 92 /* backslash */:
  147. result += '\\';
  148. break;
  149. case 47 /* slash */:
  150. result += '/';
  151. break;
  152. case 98 /* b */:
  153. result += '\b';
  154. break;
  155. case 102 /* f */:
  156. result += '\f';
  157. break;
  158. case 110 /* n */:
  159. result += '\n';
  160. break;
  161. case 114 /* r */:
  162. result += '\r';
  163. break;
  164. case 116 /* t */:
  165. result += '\t';
  166. break;
  167. case 117 /* u */:
  168. var ch_1 = scanHexDigits(4, true);
  169. if (ch_1 >= 0) {
  170. result += String.fromCharCode(ch_1);
  171. }
  172. else {
  173. scanError = ScanError.InvalidUnicode;
  174. }
  175. break;
  176. default:
  177. scanError = ScanError.InvalidEscapeCharacter;
  178. }
  179. start = pos;
  180. continue;
  181. }
  182. if (ch >= 0 && ch <= 0x1f) {
  183. if (isLineBreak(ch)) {
  184. result += text.substring(start, pos);
  185. scanError = ScanError.UnexpectedEndOfString;
  186. break;
  187. }
  188. else {
  189. scanError = ScanError.InvalidCharacter;
  190. // mark as error but continue with string
  191. }
  192. }
  193. pos++;
  194. }
  195. return result;
  196. }
  197. function scanNext() {
  198. value = '';
  199. scanError = ScanError.None;
  200. tokenOffset = pos;
  201. if (pos >= len) {
  202. // at the end
  203. tokenOffset = len;
  204. return token = SyntaxKind.EOF;
  205. }
  206. var code = text.charCodeAt(pos);
  207. // trivia: whitespace
  208. if (isWhiteSpace(code)) {
  209. do {
  210. pos++;
  211. value += String.fromCharCode(code);
  212. code = text.charCodeAt(pos);
  213. } while (isWhiteSpace(code));
  214. return token = SyntaxKind.Trivia;
  215. }
  216. // trivia: newlines
  217. if (isLineBreak(code)) {
  218. pos++;
  219. value += String.fromCharCode(code);
  220. if (code === 13 /* carriageReturn */ && text.charCodeAt(pos) === 10 /* lineFeed */) {
  221. pos++;
  222. value += '\n';
  223. }
  224. return token = SyntaxKind.LineBreakTrivia;
  225. }
  226. switch (code) {
  227. // tokens: []{}:,
  228. case 123 /* openBrace */:
  229. pos++;
  230. return token = SyntaxKind.OpenBraceToken;
  231. case 125 /* closeBrace */:
  232. pos++;
  233. return token = SyntaxKind.CloseBraceToken;
  234. case 91 /* openBracket */:
  235. pos++;
  236. return token = SyntaxKind.OpenBracketToken;
  237. case 93 /* closeBracket */:
  238. pos++;
  239. return token = SyntaxKind.CloseBracketToken;
  240. case 58 /* colon */:
  241. pos++;
  242. return token = SyntaxKind.ColonToken;
  243. case 44 /* comma */:
  244. pos++;
  245. return token = SyntaxKind.CommaToken;
  246. // strings
  247. case 34 /* doubleQuote */:
  248. pos++;
  249. value = scanString();
  250. return token = SyntaxKind.StringLiteral;
  251. // comments
  252. case 47 /* slash */:
  253. var start = pos - 1;
  254. // Single-line comment
  255. if (text.charCodeAt(pos + 1) === 47 /* slash */) {
  256. pos += 2;
  257. while (pos < len) {
  258. if (isLineBreak(text.charCodeAt(pos))) {
  259. break;
  260. }
  261. pos++;
  262. }
  263. value = text.substring(start, pos);
  264. return token = SyntaxKind.LineCommentTrivia;
  265. }
  266. // Multi-line comment
  267. if (text.charCodeAt(pos + 1) === 42 /* asterisk */) {
  268. pos += 2;
  269. var commentClosed = false;
  270. while (pos < len) {
  271. var ch = text.charCodeAt(pos);
  272. if (ch === 42 /* asterisk */ && (pos + 1 < len) && text.charCodeAt(pos + 1) === 47 /* slash */) {
  273. pos += 2;
  274. commentClosed = true;
  275. break;
  276. }
  277. pos++;
  278. }
  279. if (!commentClosed) {
  280. pos++;
  281. scanError = ScanError.UnexpectedEndOfComment;
  282. }
  283. value = text.substring(start, pos);
  284. return token = SyntaxKind.BlockCommentTrivia;
  285. }
  286. // just a single slash
  287. value += String.fromCharCode(code);
  288. pos++;
  289. return token = SyntaxKind.Unknown;
  290. // numbers
  291. case 45 /* minus */:
  292. value += String.fromCharCode(code);
  293. pos++;
  294. if (pos === len || !isDigit(text.charCodeAt(pos))) {
  295. return token = SyntaxKind.Unknown;
  296. }
  297. // found a minus, followed by a number so
  298. // we fall through to proceed with scanning
  299. // numbers
  300. case 48 /* _0 */:
  301. case 49 /* _1 */:
  302. case 50 /* _2 */:
  303. case 51 /* _3 */:
  304. case 52 /* _4 */:
  305. case 53 /* _5 */:
  306. case 54 /* _6 */:
  307. case 55 /* _7 */:
  308. case 56 /* _8 */:
  309. case 57 /* _9 */:
  310. value += scanNumber();
  311. return token = SyntaxKind.NumericLiteral;
  312. // literals and unknown symbols
  313. default:
  314. // is a literal? Read the full word.
  315. while (pos < len && isUnknownContentCharacter(code)) {
  316. pos++;
  317. code = text.charCodeAt(pos);
  318. }
  319. if (tokenOffset !== pos) {
  320. value = text.substring(tokenOffset, pos);
  321. // keywords: true, false, null
  322. switch (value) {
  323. case 'true': return token = SyntaxKind.TrueKeyword;
  324. case 'false': return token = SyntaxKind.FalseKeyword;
  325. case 'null': return token = SyntaxKind.NullKeyword;
  326. }
  327. return token = SyntaxKind.Unknown;
  328. }
  329. // some
  330. value += String.fromCharCode(code);
  331. pos++;
  332. return token = SyntaxKind.Unknown;
  333. }
  334. }
  335. function isUnknownContentCharacter(code) {
  336. if (isWhiteSpace(code) || isLineBreak(code)) {
  337. return false;
  338. }
  339. switch (code) {
  340. case 125 /* closeBrace */:
  341. case 93 /* closeBracket */:
  342. case 123 /* openBrace */:
  343. case 91 /* openBracket */:
  344. case 34 /* doubleQuote */:
  345. case 58 /* colon */:
  346. case 44 /* comma */:
  347. return false;
  348. }
  349. return true;
  350. }
  351. function scanNextNonTrivia() {
  352. var result;
  353. do {
  354. result = scanNext();
  355. } while (result >= SyntaxKind.LineCommentTrivia && result <= SyntaxKind.Trivia);
  356. return result;
  357. }
  358. return {
  359. setPosition: setPosition,
  360. getPosition: function () { return pos; },
  361. scan: ignoreTrivia ? scanNextNonTrivia : scanNext,
  362. getToken: function () { return token; },
  363. getTokenValue: function () { return value; },
  364. getTokenOffset: function () { return tokenOffset; },
  365. getTokenLength: function () { return pos - tokenOffset; },
  366. getTokenError: function () { return scanError; }
  367. };
  368. }
  369. function isWhiteSpace(ch) {
  370. return ch === 32 /* space */ || ch === 9 /* tab */ || ch === 11 /* verticalTab */ || ch === 12 /* formFeed */ ||
  371. ch === 160 /* nonBreakingSpace */ || ch === 5760 /* ogham */ || ch >= 8192 /* enQuad */ && ch <= 8203 /* zeroWidthSpace */ ||
  372. ch === 8239 /* narrowNoBreakSpace */ || ch === 8287 /* mathematicalSpace */ || ch === 12288 /* ideographicSpace */ || ch === 65279 /* byteOrderMark */;
  373. }
  374. function isLineBreak(ch) {
  375. return ch === 10 /* lineFeed */ || ch === 13 /* carriageReturn */ || ch === 8232 /* lineSeparator */ || ch === 8233 /* paragraphSeparator */;
  376. }
  377. function isDigit(ch) {
  378. return ch >= 48 /* _0 */ && ch <= 57 /* _9 */;
  379. }
  380. /**
  381. * Takes JSON with JavaScript-style comments and remove
  382. * them. Optionally replaces every none-newline character
  383. * of comments with a replaceCharacter
  384. */
  385. export function stripComments(text, replaceCh) {
  386. var _scanner = createScanner(text), parts = [], kind, offset = 0, pos;
  387. do {
  388. pos = _scanner.getPosition();
  389. kind = _scanner.scan();
  390. switch (kind) {
  391. case SyntaxKind.LineCommentTrivia:
  392. case SyntaxKind.BlockCommentTrivia:
  393. case SyntaxKind.EOF:
  394. if (offset !== pos) {
  395. parts.push(text.substring(offset, pos));
  396. }
  397. if (replaceCh !== void 0) {
  398. parts.push(_scanner.getTokenValue().replace(/[^\r\n]/g, replaceCh));
  399. }
  400. offset = _scanner.getPosition();
  401. break;
  402. }
  403. } while (kind !== SyntaxKind.EOF);
  404. return parts.join('');
  405. }
  406. export var ParseErrorCode;
  407. (function (ParseErrorCode) {
  408. ParseErrorCode[ParseErrorCode["InvalidSymbol"] = 0] = "InvalidSymbol";
  409. ParseErrorCode[ParseErrorCode["InvalidNumberFormat"] = 1] = "InvalidNumberFormat";
  410. ParseErrorCode[ParseErrorCode["PropertyNameExpected"] = 2] = "PropertyNameExpected";
  411. ParseErrorCode[ParseErrorCode["ValueExpected"] = 3] = "ValueExpected";
  412. ParseErrorCode[ParseErrorCode["ColonExpected"] = 4] = "ColonExpected";
  413. ParseErrorCode[ParseErrorCode["CommaExpected"] = 5] = "CommaExpected";
  414. ParseErrorCode[ParseErrorCode["CloseBraceExpected"] = 6] = "CloseBraceExpected";
  415. ParseErrorCode[ParseErrorCode["CloseBracketExpected"] = 7] = "CloseBracketExpected";
  416. ParseErrorCode[ParseErrorCode["EndOfFileExpected"] = 8] = "EndOfFileExpected";
  417. ParseErrorCode[ParseErrorCode["InvalidCommentToken"] = 9] = "InvalidCommentToken";
  418. ParseErrorCode[ParseErrorCode["UnexpectedEndOfComment"] = 10] = "UnexpectedEndOfComment";
  419. ParseErrorCode[ParseErrorCode["UnexpectedEndOfString"] = 11] = "UnexpectedEndOfString";
  420. ParseErrorCode[ParseErrorCode["UnexpectedEndOfNumber"] = 12] = "UnexpectedEndOfNumber";
  421. ParseErrorCode[ParseErrorCode["InvalidUnicode"] = 13] = "InvalidUnicode";
  422. ParseErrorCode[ParseErrorCode["InvalidEscapeCharacter"] = 14] = "InvalidEscapeCharacter";
  423. ParseErrorCode[ParseErrorCode["InvalidCharacter"] = 15] = "InvalidCharacter";
  424. })(ParseErrorCode || (ParseErrorCode = {}));
  425. function getLiteralNodeType(value) {
  426. switch (typeof value) {
  427. case 'boolean': return 'boolean';
  428. case 'number': return 'number';
  429. case 'string': return 'string';
  430. default: return 'null';
  431. }
  432. }
  433. /**
  434. * For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
  435. */
  436. export function getLocation(text, position) {
  437. var segments = []; // strings or numbers
  438. var earlyReturnException = new Object();
  439. var previousNode = void 0;
  440. var previousNodeInst = {
  441. value: {},
  442. offset: 0,
  443. length: 0,
  444. type: 'object'
  445. };
  446. var isAtPropertyKey = false;
  447. function setPreviousNode(value, offset, length, type) {
  448. previousNodeInst.value = value;
  449. previousNodeInst.offset = offset;
  450. previousNodeInst.length = length;
  451. previousNodeInst.type = type;
  452. previousNodeInst.columnOffset = void 0;
  453. previousNode = previousNodeInst;
  454. }
  455. try {
  456. visit(text, {
  457. onObjectBegin: function (offset, length) {
  458. if (position <= offset) {
  459. throw earlyReturnException;
  460. }
  461. previousNode = void 0;
  462. isAtPropertyKey = position > offset;
  463. segments.push(''); // push a placeholder (will be replaced)
  464. },
  465. onObjectProperty: function (name, offset, length) {
  466. if (position < offset) {
  467. throw earlyReturnException;
  468. }
  469. setPreviousNode(name, offset, length, 'property');
  470. segments[segments.length - 1] = name;
  471. if (position <= offset + length) {
  472. throw earlyReturnException;
  473. }
  474. },
  475. onObjectEnd: function (offset, length) {
  476. if (position <= offset) {
  477. throw earlyReturnException;
  478. }
  479. previousNode = void 0;
  480. segments.pop();
  481. },
  482. onArrayBegin: function (offset, length) {
  483. if (position <= offset) {
  484. throw earlyReturnException;
  485. }
  486. previousNode = void 0;
  487. segments.push(0);
  488. },
  489. onArrayEnd: function (offset, length) {
  490. if (position <= offset) {
  491. throw earlyReturnException;
  492. }
  493. previousNode = void 0;
  494. segments.pop();
  495. },
  496. onLiteralValue: function (value, offset, length) {
  497. if (position < offset) {
  498. throw earlyReturnException;
  499. }
  500. setPreviousNode(value, offset, length, getLiteralNodeType(value));
  501. if (position <= offset + length) {
  502. throw earlyReturnException;
  503. }
  504. },
  505. onSeparator: function (sep, offset, length) {
  506. if (position <= offset) {
  507. throw earlyReturnException;
  508. }
  509. if (sep === ':' && previousNode && previousNode.type === 'property') {
  510. previousNode.columnOffset = offset;
  511. isAtPropertyKey = false;
  512. previousNode = void 0;
  513. }
  514. else if (sep === ',') {
  515. var last = segments[segments.length - 1];
  516. if (typeof last === 'number') {
  517. segments[segments.length - 1] = last + 1;
  518. }
  519. else {
  520. isAtPropertyKey = true;
  521. segments[segments.length - 1] = '';
  522. }
  523. previousNode = void 0;
  524. }
  525. }
  526. });
  527. }
  528. catch (e) {
  529. if (e !== earlyReturnException) {
  530. throw e;
  531. }
  532. }
  533. return {
  534. path: segments,
  535. previousNode: previousNode,
  536. isAtPropertyKey: isAtPropertyKey,
  537. matches: function (pattern) {
  538. var k = 0;
  539. for (var i = 0; k < pattern.length && i < segments.length; i++) {
  540. if (pattern[k] === segments[i] || pattern[k] === '*') {
  541. k++;
  542. }
  543. else if (pattern[k] !== '**') {
  544. return false;
  545. }
  546. }
  547. return k === pattern.length;
  548. }
  549. };
  550. }
  551. /**
  552. * Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
  553. * Therefore always check the errors list to find out if the input was valid.
  554. */
  555. export function parse(text, errors, options) {
  556. if (errors === void 0) { errors = []; }
  557. var currentProperty = null;
  558. var currentParent = [];
  559. var previousParents = [];
  560. function onValue(value) {
  561. if (Array.isArray(currentParent)) {
  562. currentParent.push(value);
  563. }
  564. else if (currentProperty) {
  565. currentParent[currentProperty] = value;
  566. }
  567. }
  568. var visitor = {
  569. onObjectBegin: function () {
  570. var object = {};
  571. onValue(object);
  572. previousParents.push(currentParent);
  573. currentParent = object;
  574. currentProperty = null;
  575. },
  576. onObjectProperty: function (name) {
  577. currentProperty = name;
  578. },
  579. onObjectEnd: function () {
  580. currentParent = previousParents.pop();
  581. },
  582. onArrayBegin: function () {
  583. var array = [];
  584. onValue(array);
  585. previousParents.push(currentParent);
  586. currentParent = array;
  587. currentProperty = null;
  588. },
  589. onArrayEnd: function () {
  590. currentParent = previousParents.pop();
  591. },
  592. onLiteralValue: onValue,
  593. onError: function (error, offset, length) {
  594. errors.push({ error: error, offset: offset, length: length });
  595. }
  596. };
  597. visit(text, visitor, options);
  598. return currentParent[0];
  599. }
  600. /**
  601. * Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
  602. */
  603. export function parseTree(text, errors, options) {
  604. if (errors === void 0) { errors = []; }
  605. var currentParent = { type: 'array', offset: -1, length: -1, children: [] }; // artificial root
  606. function ensurePropertyComplete(endOffset) {
  607. if (currentParent.type === 'property') {
  608. currentParent.length = endOffset - currentParent.offset;
  609. currentParent = currentParent.parent;
  610. }
  611. }
  612. function onValue(valueNode) {
  613. currentParent.children.push(valueNode);
  614. return valueNode;
  615. }
  616. var visitor = {
  617. onObjectBegin: function (offset) {
  618. currentParent = onValue({ type: 'object', offset: offset, length: -1, parent: currentParent, children: [] });
  619. },
  620. onObjectProperty: function (name, offset, length) {
  621. currentParent = onValue({ type: 'property', offset: offset, length: -1, parent: currentParent, children: [] });
  622. currentParent.children.push({ type: 'string', value: name, offset: offset, length: length, parent: currentParent });
  623. },
  624. onObjectEnd: function (offset, length) {
  625. currentParent.length = offset + length - currentParent.offset;
  626. currentParent = currentParent.parent;
  627. ensurePropertyComplete(offset + length);
  628. },
  629. onArrayBegin: function (offset, length) {
  630. currentParent = onValue({ type: 'array', offset: offset, length: -1, parent: currentParent, children: [] });
  631. },
  632. onArrayEnd: function (offset, length) {
  633. currentParent.length = offset + length - currentParent.offset;
  634. currentParent = currentParent.parent;
  635. ensurePropertyComplete(offset + length);
  636. },
  637. onLiteralValue: function (value, offset, length) {
  638. onValue({ type: getLiteralNodeType(value), offset: offset, length: length, parent: currentParent, value: value });
  639. ensurePropertyComplete(offset + length);
  640. },
  641. onSeparator: function (sep, offset, length) {
  642. if (currentParent.type === 'property') {
  643. if (sep === ':') {
  644. currentParent.columnOffset = offset;
  645. }
  646. else if (sep === ',') {
  647. ensurePropertyComplete(offset);
  648. }
  649. }
  650. },
  651. onError: function (error, offset, length) {
  652. errors.push({ error: error, offset: offset, length: length });
  653. }
  654. };
  655. visit(text, visitor, options);
  656. var result = currentParent.children[0];
  657. if (result) {
  658. delete result.parent;
  659. }
  660. return result;
  661. }
  662. /**
  663. * Finds the node at the given path in a JSON DOM.
  664. */
  665. export function findNodeAtLocation(root, path) {
  666. if (!root) {
  667. return void 0;
  668. }
  669. var node = root;
  670. for (var _i = 0, path_1 = path; _i < path_1.length; _i++) {
  671. var segment = path_1[_i];
  672. if (typeof segment === 'string') {
  673. if (node.type !== 'object' || !Array.isArray(node.children)) {
  674. return void 0;
  675. }
  676. var found = false;
  677. for (var _a = 0, _b = node.children; _a < _b.length; _a++) {
  678. var propertyNode = _b[_a];
  679. if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment) {
  680. node = propertyNode.children[1];
  681. found = true;
  682. break;
  683. }
  684. }
  685. if (!found) {
  686. return void 0;
  687. }
  688. }
  689. else {
  690. var index = segment;
  691. if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {
  692. return void 0;
  693. }
  694. node = node.children[index];
  695. }
  696. }
  697. return node;
  698. }
  699. /**
  700. * Evaluates the JavaScript object of the given JSON DOM node
  701. */
  702. export function getNodeValue(node) {
  703. if (node.type === 'array') {
  704. return node.children.map(getNodeValue);
  705. }
  706. else if (node.type === 'object') {
  707. var obj = Object.create(null);
  708. for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
  709. var prop = _a[_i];
  710. obj[prop.children[0].value] = getNodeValue(prop.children[1]);
  711. }
  712. return obj;
  713. }
  714. return node.value;
  715. }
  716. /**
  717. * Parses the given text and invokes the visitor functions for each object, array and literal reached.
  718. */
  719. export function visit(text, visitor, options) {
  720. var _scanner = createScanner(text, false);
  721. function toNoArgVisit(visitFunction) {
  722. return visitFunction ? function () { return visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength()); } : function () { return true; };
  723. }
  724. function toOneArgVisit(visitFunction) {
  725. return visitFunction ? function (arg) { return visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength()); } : function () { return true; };
  726. }
  727. var onObjectBegin = toNoArgVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisit(visitor.onObjectProperty), onObjectEnd = toNoArgVisit(visitor.onObjectEnd), onArrayBegin = toNoArgVisit(visitor.onArrayBegin), onArrayEnd = toNoArgVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisit(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError);
  728. var disallowComments = options && options.disallowComments;
  729. var allowTrailingComma = options && options.allowTrailingComma;
  730. function scanNext() {
  731. while (true) {
  732. var token = _scanner.scan();
  733. switch (_scanner.getTokenError()) {
  734. case ScanError.InvalidUnicode:
  735. handleError(ParseErrorCode.InvalidUnicode);
  736. break;
  737. case ScanError.InvalidEscapeCharacter:
  738. handleError(ParseErrorCode.InvalidEscapeCharacter);
  739. break;
  740. case ScanError.UnexpectedEndOfNumber:
  741. handleError(ParseErrorCode.UnexpectedEndOfNumber);
  742. break;
  743. case ScanError.UnexpectedEndOfComment:
  744. if (!disallowComments) {
  745. handleError(ParseErrorCode.UnexpectedEndOfComment);
  746. }
  747. break;
  748. case ScanError.UnexpectedEndOfString:
  749. handleError(ParseErrorCode.UnexpectedEndOfString);
  750. break;
  751. case ScanError.InvalidCharacter:
  752. handleError(ParseErrorCode.InvalidCharacter);
  753. break;
  754. }
  755. switch (token) {
  756. case SyntaxKind.LineCommentTrivia:
  757. case SyntaxKind.BlockCommentTrivia:
  758. if (disallowComments) {
  759. handleError(ParseErrorCode.InvalidCommentToken);
  760. }
  761. else {
  762. onComment();
  763. }
  764. break;
  765. case SyntaxKind.Unknown:
  766. handleError(ParseErrorCode.InvalidSymbol);
  767. break;
  768. case SyntaxKind.Trivia:
  769. case SyntaxKind.LineBreakTrivia:
  770. break;
  771. default:
  772. return token;
  773. }
  774. }
  775. }
  776. function handleError(error, skipUntilAfter, skipUntil) {
  777. if (skipUntilAfter === void 0) { skipUntilAfter = []; }
  778. if (skipUntil === void 0) { skipUntil = []; }
  779. onError(error);
  780. if (skipUntilAfter.length + skipUntil.length > 0) {
  781. var token = _scanner.getToken();
  782. while (token !== SyntaxKind.EOF) {
  783. if (skipUntilAfter.indexOf(token) !== -1) {
  784. scanNext();
  785. break;
  786. }
  787. else if (skipUntil.indexOf(token) !== -1) {
  788. break;
  789. }
  790. token = scanNext();
  791. }
  792. }
  793. }
  794. function parseString(isValue) {
  795. var value = _scanner.getTokenValue();
  796. if (isValue) {
  797. onLiteralValue(value);
  798. }
  799. else {
  800. onObjectProperty(value);
  801. }
  802. scanNext();
  803. return true;
  804. }
  805. function parseLiteral() {
  806. switch (_scanner.getToken()) {
  807. case SyntaxKind.NumericLiteral:
  808. var value = 0;
  809. try {
  810. value = JSON.parse(_scanner.getTokenValue());
  811. if (typeof value !== 'number') {
  812. handleError(ParseErrorCode.InvalidNumberFormat);
  813. value = 0;
  814. }
  815. }
  816. catch (e) {
  817. handleError(ParseErrorCode.InvalidNumberFormat);
  818. }
  819. onLiteralValue(value);
  820. break;
  821. case SyntaxKind.NullKeyword:
  822. onLiteralValue(null);
  823. break;
  824. case SyntaxKind.TrueKeyword:
  825. onLiteralValue(true);
  826. break;
  827. case SyntaxKind.FalseKeyword:
  828. onLiteralValue(false);
  829. break;
  830. default:
  831. return false;
  832. }
  833. scanNext();
  834. return true;
  835. }
  836. function parseProperty() {
  837. if (_scanner.getToken() !== SyntaxKind.StringLiteral) {
  838. handleError(ParseErrorCode.PropertyNameExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken]);
  839. return false;
  840. }
  841. parseString(false);
  842. if (_scanner.getToken() === SyntaxKind.ColonToken) {
  843. onSeparator(':');
  844. scanNext(); // consume colon
  845. if (!parseValue()) {
  846. handleError(ParseErrorCode.ValueExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken]);
  847. }
  848. }
  849. else {
  850. handleError(ParseErrorCode.ColonExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken]);
  851. }
  852. return true;
  853. }
  854. function parseObject() {
  855. onObjectBegin();
  856. scanNext(); // consume open brace
  857. var needsComma = false;
  858. while (_scanner.getToken() !== SyntaxKind.CloseBraceToken && _scanner.getToken() !== SyntaxKind.EOF) {
  859. if (_scanner.getToken() === SyntaxKind.CommaToken) {
  860. if (!needsComma) {
  861. handleError(ParseErrorCode.ValueExpected, [], []);
  862. }
  863. onSeparator(',');
  864. scanNext(); // consume comma
  865. if (_scanner.getToken() === SyntaxKind.CloseBraceToken && allowTrailingComma) {
  866. break;
  867. }
  868. }
  869. else if (needsComma) {
  870. handleError(ParseErrorCode.CommaExpected, [], []);
  871. }
  872. if (!parseProperty()) {
  873. handleError(ParseErrorCode.ValueExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken]);
  874. }
  875. needsComma = true;
  876. }
  877. onObjectEnd();
  878. if (_scanner.getToken() !== SyntaxKind.CloseBraceToken) {
  879. handleError(ParseErrorCode.CloseBraceExpected, [SyntaxKind.CloseBraceToken], []);
  880. }
  881. else {
  882. scanNext(); // consume close brace
  883. }
  884. return true;
  885. }
  886. function parseArray() {
  887. onArrayBegin();
  888. scanNext(); // consume open bracket
  889. var needsComma = false;
  890. while (_scanner.getToken() !== SyntaxKind.CloseBracketToken && _scanner.getToken() !== SyntaxKind.EOF) {
  891. if (_scanner.getToken() === SyntaxKind.CommaToken) {
  892. if (!needsComma) {
  893. handleError(ParseErrorCode.ValueExpected, [], []);
  894. }
  895. onSeparator(',');
  896. scanNext(); // consume comma
  897. if (_scanner.getToken() === SyntaxKind.CloseBracketToken && allowTrailingComma) {
  898. break;
  899. }
  900. }
  901. else if (needsComma) {
  902. handleError(ParseErrorCode.CommaExpected, [], []);
  903. }
  904. if (!parseValue()) {
  905. handleError(ParseErrorCode.ValueExpected, [], [SyntaxKind.CloseBracketToken, SyntaxKind.CommaToken]);
  906. }
  907. needsComma = true;
  908. }
  909. onArrayEnd();
  910. if (_scanner.getToken() !== SyntaxKind.CloseBracketToken) {
  911. handleError(ParseErrorCode.CloseBracketExpected, [SyntaxKind.CloseBracketToken], []);
  912. }
  913. else {
  914. scanNext(); // consume close bracket
  915. }
  916. return true;
  917. }
  918. function parseValue() {
  919. switch (_scanner.getToken()) {
  920. case SyntaxKind.OpenBracketToken:
  921. return parseArray();
  922. case SyntaxKind.OpenBraceToken:
  923. return parseObject();
  924. case SyntaxKind.StringLiteral:
  925. return parseString(true);
  926. default:
  927. return parseLiteral();
  928. }
  929. }
  930. scanNext();
  931. if (_scanner.getToken() === SyntaxKind.EOF) {
  932. return true;
  933. }
  934. if (!parseValue()) {
  935. handleError(ParseErrorCode.ValueExpected, [], []);
  936. return false;
  937. }
  938. if (_scanner.getToken() !== SyntaxKind.EOF) {
  939. handleError(ParseErrorCode.EndOfFileExpected, [], []);
  940. }
  941. return true;
  942. }
  943. /**
  944. * Computes the edits needed to format a JSON document.
  945. *
  946. * @param documentText The input text
  947. * @param range The range to format or `undefined` to format the full content
  948. * @param options The formatting options
  949. * @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or
  950. * removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of
  951. * text in the original document. However, multiple edits can have
  952. * the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first.
  953. * To apply edits to an input, you can use `applyEdits`
  954. */
  955. export function format(documentText, range, options) {
  956. return _format(documentText, range, options);
  957. }
  958. /**
  959. * Computes the edits needed to modify a value in the JSON document.
  960. *
  961. * @param documentText The input text
  962. * @param path The path of the value to change. The path represents either to the document root, a property or an array item.
  963. * If the path points to an non-existing property or item, it will be created.
  964. * @param value The new value for the specified property or item. If the value is undefined,
  965. * the property or item will be removed.
  966. * @param options Options
  967. * @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or
  968. * removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of
  969. * text in the original document. However, multiple edits can have
  970. * the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first.
  971. * To apply edits to an input, you can use `applyEdits`
  972. */
  973. export function modify(text, path, value, options) {
  974. return setProperty(text, path, value, options.formattingOptions, options.getInsertionIndex);
  975. }
  976. /**
  977. * Applies edits to a input string.
  978. */
  979. export function applyEdits(text, edits) {
  980. for (var i = edits.length - 1; i >= 0; i--) {
  981. text = applyEdit(text, edits[i]);
  982. }
  983. return text;
  984. }
  985. //# sourceMappingURL=main.js.map