37698e234d87501cb8c2979838454e2bc7f7a3e217fc137d10a72e5915c87a1bcb1b878895056a624710b643d17823fb113a198e80b2041613c53cd25f31de 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  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. import { InvalidBracketAstNode, ListAstNode, PairAstNode } from './ast.js';
  6. import { BeforeEditPositionMapper } from './beforeEditPositionMapper.js';
  7. import { SmallImmutableSet } from './smallImmutableSet.js';
  8. import { lengthIsZero, lengthLessThan } from './length.js';
  9. import { concat23Trees, concat23TreesOfSameHeight } from './concat23Trees.js';
  10. import { NodeReader } from './nodeReader.js';
  11. /**
  12. * Non incrementally built ASTs are immutable.
  13. */
  14. export function parseDocument(tokenizer, edits, oldNode, createImmutableLists) {
  15. const parser = new Parser(tokenizer, edits, oldNode, createImmutableLists);
  16. return parser.parseDocument();
  17. }
  18. /**
  19. * Non incrementally built ASTs are immutable.
  20. */
  21. class Parser {
  22. constructor(tokenizer, edits, oldNode, createImmutableLists) {
  23. this.tokenizer = tokenizer;
  24. this.createImmutableLists = createImmutableLists;
  25. this._itemsConstructed = 0;
  26. this._itemsFromCache = 0;
  27. if (oldNode && createImmutableLists) {
  28. throw new Error('Not supported');
  29. }
  30. this.oldNodeReader = oldNode ? new NodeReader(oldNode) : undefined;
  31. this.positionMapper = new BeforeEditPositionMapper(edits, tokenizer.length);
  32. }
  33. parseDocument() {
  34. this._itemsConstructed = 0;
  35. this._itemsFromCache = 0;
  36. let result = this.parseList(SmallImmutableSet.getEmpty());
  37. if (!result) {
  38. result = ListAstNode.getEmpty();
  39. }
  40. return result;
  41. }
  42. parseList(openedBracketIds) {
  43. const items = new Array();
  44. while (true) {
  45. const token = this.tokenizer.peek();
  46. if (!token ||
  47. (token.kind === 2 /* TokenKind.ClosingBracket */ &&
  48. token.bracketIds.intersects(openedBracketIds))) {
  49. break;
  50. }
  51. const child = this.parseChild(openedBracketIds);
  52. if (child.kind === 4 /* AstNodeKind.List */ && child.childrenLength === 0) {
  53. continue;
  54. }
  55. items.push(child);
  56. }
  57. // When there is no oldNodeReader, all items are created from scratch and must have the same height.
  58. const result = this.oldNodeReader ? concat23Trees(items) : concat23TreesOfSameHeight(items, this.createImmutableLists);
  59. return result;
  60. }
  61. parseChild(openedBracketIds) {
  62. if (this.oldNodeReader) {
  63. const maxCacheableLength = this.positionMapper.getDistanceToNextChange(this.tokenizer.offset);
  64. if (!lengthIsZero(maxCacheableLength)) {
  65. const cachedNode = this.oldNodeReader.readLongestNodeAt(this.positionMapper.getOffsetBeforeChange(this.tokenizer.offset), curNode => {
  66. if (!lengthLessThan(curNode.length, maxCacheableLength)) {
  67. // Either the node contains edited text or touches edited text.
  68. // In the latter case, brackets might have been extended (`end` -> `ending`), so even touching nodes cannot be reused.
  69. return false;
  70. }
  71. const canBeReused = curNode.canBeReused(openedBracketIds);
  72. return canBeReused;
  73. });
  74. if (cachedNode) {
  75. this._itemsFromCache++;
  76. this.tokenizer.skip(cachedNode.length);
  77. return cachedNode;
  78. }
  79. }
  80. }
  81. this._itemsConstructed++;
  82. const token = this.tokenizer.read();
  83. switch (token.kind) {
  84. case 2 /* TokenKind.ClosingBracket */:
  85. return new InvalidBracketAstNode(token.bracketIds, token.length);
  86. case 0 /* TokenKind.Text */:
  87. return token.astNode;
  88. case 1 /* TokenKind.OpeningBracket */: {
  89. const set = openedBracketIds.merge(token.bracketIds);
  90. const child = this.parseList(set);
  91. const nextToken = this.tokenizer.peek();
  92. if (nextToken &&
  93. nextToken.kind === 2 /* TokenKind.ClosingBracket */ &&
  94. (nextToken.bracketId === token.bracketId || nextToken.bracketIds.intersects(token.bracketIds))) {
  95. this.tokenizer.read();
  96. return PairAstNode.create(token.astNode, child, nextToken.astNode);
  97. }
  98. else {
  99. return PairAstNode.create(token.astNode, child, null);
  100. }
  101. }
  102. default:
  103. throw new Error('unexpected');
  104. }
  105. }
  106. }