294ec9dd88dfc53c460689ff55d265a7ffba9fac43de046c28ce6cd63de6fae12dda7638c0347bc980cd139ca19fd5470f344a0e4f1141b3791c00ac5b4f37 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  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 { CursorColumns } from '../../../core/cursorColumns.js';
  6. import { lengthAdd, lengthGetLineCount, lengthToObj, lengthZero } from './length.js';
  7. import { SmallImmutableSet } from './smallImmutableSet.js';
  8. /**
  9. * The base implementation for all AST nodes.
  10. */
  11. class BaseAstNode {
  12. constructor(length) {
  13. this._length = length;
  14. }
  15. /**
  16. * The length of the entire node, which should equal the sum of lengths of all children.
  17. */
  18. get length() {
  19. return this._length;
  20. }
  21. }
  22. /**
  23. * Represents a bracket pair including its child (e.g. `{ ... }`).
  24. * Might be unclosed.
  25. * Immutable, if all children are immutable.
  26. */
  27. export class PairAstNode extends BaseAstNode {
  28. constructor(length, openingBracket, child, closingBracket, missingOpeningBracketIds) {
  29. super(length);
  30. this.openingBracket = openingBracket;
  31. this.child = child;
  32. this.closingBracket = closingBracket;
  33. this.missingOpeningBracketIds = missingOpeningBracketIds;
  34. }
  35. static create(openingBracket, child, closingBracket) {
  36. let length = openingBracket.length;
  37. if (child) {
  38. length = lengthAdd(length, child.length);
  39. }
  40. if (closingBracket) {
  41. length = lengthAdd(length, closingBracket.length);
  42. }
  43. return new PairAstNode(length, openingBracket, child, closingBracket, child ? child.missingOpeningBracketIds : SmallImmutableSet.getEmpty());
  44. }
  45. get kind() {
  46. return 2 /* AstNodeKind.Pair */;
  47. }
  48. get listHeight() {
  49. return 0;
  50. }
  51. get childrenLength() {
  52. return 3;
  53. }
  54. getChild(idx) {
  55. switch (idx) {
  56. case 0: return this.openingBracket;
  57. case 1: return this.child;
  58. case 2: return this.closingBracket;
  59. }
  60. throw new Error('Invalid child index');
  61. }
  62. /**
  63. * Avoid using this property, it allocates an array!
  64. */
  65. get children() {
  66. const result = new Array();
  67. result.push(this.openingBracket);
  68. if (this.child) {
  69. result.push(this.child);
  70. }
  71. if (this.closingBracket) {
  72. result.push(this.closingBracket);
  73. }
  74. return result;
  75. }
  76. canBeReused(openBracketIds) {
  77. if (this.closingBracket === null) {
  78. // Unclosed pair ast nodes only
  79. // end at the end of the document
  80. // or when a parent node is closed.
  81. // This could be improved:
  82. // Only return false if some next token is neither "undefined" nor a bracket that closes a parent.
  83. return false;
  84. }
  85. if (openBracketIds.intersects(this.missingOpeningBracketIds)) {
  86. return false;
  87. }
  88. return true;
  89. }
  90. deepClone() {
  91. return new PairAstNode(this.length, this.openingBracket.deepClone(), this.child && this.child.deepClone(), this.closingBracket && this.closingBracket.deepClone(), this.missingOpeningBracketIds);
  92. }
  93. computeMinIndentation(offset, textModel) {
  94. return this.child ? this.child.computeMinIndentation(lengthAdd(offset, this.openingBracket.length), textModel) : Number.MAX_SAFE_INTEGER;
  95. }
  96. }
  97. export class ListAstNode extends BaseAstNode {
  98. /**
  99. * Use ListAstNode.create.
  100. */
  101. constructor(length, listHeight, _missingOpeningBracketIds) {
  102. super(length);
  103. this.listHeight = listHeight;
  104. this._missingOpeningBracketIds = _missingOpeningBracketIds;
  105. this.cachedMinIndentation = -1;
  106. }
  107. /**
  108. * This method uses more memory-efficient list nodes that can only store 2 or 3 children.
  109. */
  110. static create23(item1, item2, item3, immutable = false) {
  111. let length = item1.length;
  112. let missingBracketIds = item1.missingOpeningBracketIds;
  113. if (item1.listHeight !== item2.listHeight) {
  114. throw new Error('Invalid list heights');
  115. }
  116. length = lengthAdd(length, item2.length);
  117. missingBracketIds = missingBracketIds.merge(item2.missingOpeningBracketIds);
  118. if (item3) {
  119. if (item1.listHeight !== item3.listHeight) {
  120. throw new Error('Invalid list heights');
  121. }
  122. length = lengthAdd(length, item3.length);
  123. missingBracketIds = missingBracketIds.merge(item3.missingOpeningBracketIds);
  124. }
  125. return immutable
  126. ? new Immutable23ListAstNode(length, item1.listHeight + 1, item1, item2, item3, missingBracketIds)
  127. : new TwoThreeListAstNode(length, item1.listHeight + 1, item1, item2, item3, missingBracketIds);
  128. }
  129. static getEmpty() {
  130. return new ImmutableArrayListAstNode(lengthZero, 0, [], SmallImmutableSet.getEmpty());
  131. }
  132. get kind() {
  133. return 4 /* AstNodeKind.List */;
  134. }
  135. get missingOpeningBracketIds() {
  136. return this._missingOpeningBracketIds;
  137. }
  138. throwIfImmutable() {
  139. // NOOP
  140. }
  141. makeLastElementMutable() {
  142. this.throwIfImmutable();
  143. const childCount = this.childrenLength;
  144. if (childCount === 0) {
  145. return undefined;
  146. }
  147. const lastChild = this.getChild(childCount - 1);
  148. const mutable = lastChild.kind === 4 /* AstNodeKind.List */ ? lastChild.toMutable() : lastChild;
  149. if (lastChild !== mutable) {
  150. this.setChild(childCount - 1, mutable);
  151. }
  152. return mutable;
  153. }
  154. makeFirstElementMutable() {
  155. this.throwIfImmutable();
  156. const childCount = this.childrenLength;
  157. if (childCount === 0) {
  158. return undefined;
  159. }
  160. const firstChild = this.getChild(0);
  161. const mutable = firstChild.kind === 4 /* AstNodeKind.List */ ? firstChild.toMutable() : firstChild;
  162. if (firstChild !== mutable) {
  163. this.setChild(0, mutable);
  164. }
  165. return mutable;
  166. }
  167. canBeReused(openBracketIds) {
  168. if (openBracketIds.intersects(this.missingOpeningBracketIds)) {
  169. return false;
  170. }
  171. let lastChild = this;
  172. let lastLength;
  173. while (lastChild.kind === 4 /* AstNodeKind.List */ && (lastLength = lastChild.childrenLength) > 0) {
  174. lastChild = lastChild.getChild(lastLength - 1);
  175. }
  176. return lastChild.canBeReused(openBracketIds);
  177. }
  178. handleChildrenChanged() {
  179. this.throwIfImmutable();
  180. const count = this.childrenLength;
  181. let length = this.getChild(0).length;
  182. let unopenedBrackets = this.getChild(0).missingOpeningBracketIds;
  183. for (let i = 1; i < count; i++) {
  184. const child = this.getChild(i);
  185. length = lengthAdd(length, child.length);
  186. unopenedBrackets = unopenedBrackets.merge(child.missingOpeningBracketIds);
  187. }
  188. this._length = length;
  189. this._missingOpeningBracketIds = unopenedBrackets;
  190. this.cachedMinIndentation = -1;
  191. }
  192. computeMinIndentation(offset, textModel) {
  193. if (this.cachedMinIndentation !== -1) {
  194. return this.cachedMinIndentation;
  195. }
  196. let minIndentation = Number.MAX_SAFE_INTEGER;
  197. let childOffset = offset;
  198. for (let i = 0; i < this.childrenLength; i++) {
  199. const child = this.getChild(i);
  200. if (child) {
  201. minIndentation = Math.min(minIndentation, child.computeMinIndentation(childOffset, textModel));
  202. childOffset = lengthAdd(childOffset, child.length);
  203. }
  204. }
  205. this.cachedMinIndentation = minIndentation;
  206. return minIndentation;
  207. }
  208. }
  209. class TwoThreeListAstNode extends ListAstNode {
  210. constructor(length, listHeight, _item1, _item2, _item3, missingOpeningBracketIds) {
  211. super(length, listHeight, missingOpeningBracketIds);
  212. this._item1 = _item1;
  213. this._item2 = _item2;
  214. this._item3 = _item3;
  215. }
  216. get childrenLength() {
  217. return this._item3 !== null ? 3 : 2;
  218. }
  219. getChild(idx) {
  220. switch (idx) {
  221. case 0: return this._item1;
  222. case 1: return this._item2;
  223. case 2: return this._item3;
  224. }
  225. throw new Error('Invalid child index');
  226. }
  227. setChild(idx, node) {
  228. switch (idx) {
  229. case 0:
  230. this._item1 = node;
  231. return;
  232. case 1:
  233. this._item2 = node;
  234. return;
  235. case 2:
  236. this._item3 = node;
  237. return;
  238. }
  239. throw new Error('Invalid child index');
  240. }
  241. get children() {
  242. return this._item3 ? [this._item1, this._item2, this._item3] : [this._item1, this._item2];
  243. }
  244. get item1() {
  245. return this._item1;
  246. }
  247. get item2() {
  248. return this._item2;
  249. }
  250. get item3() {
  251. return this._item3;
  252. }
  253. deepClone() {
  254. return new TwoThreeListAstNode(this.length, this.listHeight, this._item1.deepClone(), this._item2.deepClone(), this._item3 ? this._item3.deepClone() : null, this.missingOpeningBracketIds);
  255. }
  256. appendChildOfSameHeight(node) {
  257. if (this._item3) {
  258. throw new Error('Cannot append to a full (2,3) tree node');
  259. }
  260. this.throwIfImmutable();
  261. this._item3 = node;
  262. this.handleChildrenChanged();
  263. }
  264. unappendChild() {
  265. if (!this._item3) {
  266. throw new Error('Cannot remove from a non-full (2,3) tree node');
  267. }
  268. this.throwIfImmutable();
  269. const result = this._item3;
  270. this._item3 = null;
  271. this.handleChildrenChanged();
  272. return result;
  273. }
  274. prependChildOfSameHeight(node) {
  275. if (this._item3) {
  276. throw new Error('Cannot prepend to a full (2,3) tree node');
  277. }
  278. this.throwIfImmutable();
  279. this._item3 = this._item2;
  280. this._item2 = this._item1;
  281. this._item1 = node;
  282. this.handleChildrenChanged();
  283. }
  284. unprependChild() {
  285. if (!this._item3) {
  286. throw new Error('Cannot remove from a non-full (2,3) tree node');
  287. }
  288. this.throwIfImmutable();
  289. const result = this._item1;
  290. this._item1 = this._item2;
  291. this._item2 = this._item3;
  292. this._item3 = null;
  293. this.handleChildrenChanged();
  294. return result;
  295. }
  296. toMutable() {
  297. return this;
  298. }
  299. }
  300. /**
  301. * Immutable, if all children are immutable.
  302. */
  303. class Immutable23ListAstNode extends TwoThreeListAstNode {
  304. toMutable() {
  305. return new TwoThreeListAstNode(this.length, this.listHeight, this.item1, this.item2, this.item3, this.missingOpeningBracketIds);
  306. }
  307. throwIfImmutable() {
  308. throw new Error('this instance is immutable');
  309. }
  310. }
  311. /**
  312. * For debugging.
  313. */
  314. class ArrayListAstNode extends ListAstNode {
  315. constructor(length, listHeight, _children, missingOpeningBracketIds) {
  316. super(length, listHeight, missingOpeningBracketIds);
  317. this._children = _children;
  318. }
  319. get childrenLength() {
  320. return this._children.length;
  321. }
  322. getChild(idx) {
  323. return this._children[idx];
  324. }
  325. setChild(idx, child) {
  326. this._children[idx] = child;
  327. }
  328. get children() {
  329. return this._children;
  330. }
  331. deepClone() {
  332. const children = new Array(this._children.length);
  333. for (let i = 0; i < this._children.length; i++) {
  334. children[i] = this._children[i].deepClone();
  335. }
  336. return new ArrayListAstNode(this.length, this.listHeight, children, this.missingOpeningBracketIds);
  337. }
  338. appendChildOfSameHeight(node) {
  339. this.throwIfImmutable();
  340. this._children.push(node);
  341. this.handleChildrenChanged();
  342. }
  343. unappendChild() {
  344. this.throwIfImmutable();
  345. const item = this._children.pop();
  346. this.handleChildrenChanged();
  347. return item;
  348. }
  349. prependChildOfSameHeight(node) {
  350. this.throwIfImmutable();
  351. this._children.unshift(node);
  352. this.handleChildrenChanged();
  353. }
  354. unprependChild() {
  355. this.throwIfImmutable();
  356. const item = this._children.shift();
  357. this.handleChildrenChanged();
  358. return item;
  359. }
  360. toMutable() {
  361. return this;
  362. }
  363. }
  364. /**
  365. * Immutable, if all children are immutable.
  366. */
  367. class ImmutableArrayListAstNode extends ArrayListAstNode {
  368. toMutable() {
  369. return new ArrayListAstNode(this.length, this.listHeight, [...this.children], this.missingOpeningBracketIds);
  370. }
  371. throwIfImmutable() {
  372. throw new Error('this instance is immutable');
  373. }
  374. }
  375. const emptyArray = [];
  376. class ImmutableLeafAstNode extends BaseAstNode {
  377. get listHeight() {
  378. return 0;
  379. }
  380. get childrenLength() {
  381. return 0;
  382. }
  383. getChild(idx) {
  384. return null;
  385. }
  386. get children() {
  387. return emptyArray;
  388. }
  389. deepClone() {
  390. return this;
  391. }
  392. }
  393. export class TextAstNode extends ImmutableLeafAstNode {
  394. get kind() {
  395. return 0 /* AstNodeKind.Text */;
  396. }
  397. get missingOpeningBracketIds() {
  398. return SmallImmutableSet.getEmpty();
  399. }
  400. canBeReused(_openedBracketIds) {
  401. return true;
  402. }
  403. computeMinIndentation(offset, textModel) {
  404. const start = lengthToObj(offset);
  405. // Text ast nodes don't have partial indentation (ensured by the tokenizer).
  406. // Thus, if this text node does not start at column 0, the first line cannot have any indentation at all.
  407. const startLineNumber = (start.columnCount === 0 ? start.lineCount : start.lineCount + 1) + 1;
  408. const endLineNumber = lengthGetLineCount(lengthAdd(offset, this.length)) + 1;
  409. let result = Number.MAX_SAFE_INTEGER;
  410. for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
  411. const firstNonWsColumn = textModel.getLineFirstNonWhitespaceColumn(lineNumber);
  412. const lineContent = textModel.getLineContent(lineNumber);
  413. if (firstNonWsColumn === 0) {
  414. continue;
  415. }
  416. const visibleColumn = CursorColumns.visibleColumnFromColumn(lineContent, firstNonWsColumn, textModel.getOptions().tabSize);
  417. result = Math.min(result, visibleColumn);
  418. }
  419. return result;
  420. }
  421. }
  422. export class BracketAstNode extends ImmutableLeafAstNode {
  423. constructor(length, bracketInfo,
  424. /**
  425. * In case of a opening bracket, this is the id of the opening bracket.
  426. * In case of a closing bracket, this contains the ids of all opening brackets it can close.
  427. */
  428. bracketIds) {
  429. super(length);
  430. this.bracketInfo = bracketInfo;
  431. this.bracketIds = bracketIds;
  432. }
  433. static create(length, bracketInfo, bracketIds) {
  434. const node = new BracketAstNode(length, bracketInfo, bracketIds);
  435. return node;
  436. }
  437. get kind() {
  438. return 1 /* AstNodeKind.Bracket */;
  439. }
  440. get missingOpeningBracketIds() {
  441. return SmallImmutableSet.getEmpty();
  442. }
  443. get text() {
  444. return this.bracketInfo.bracketText;
  445. }
  446. get languageId() {
  447. return this.bracketInfo.languageId;
  448. }
  449. canBeReused(_openedBracketIds) {
  450. // These nodes could be reused,
  451. // but not in a general way.
  452. // Their parent may be reused.
  453. return false;
  454. }
  455. computeMinIndentation(offset, textModel) {
  456. return Number.MAX_SAFE_INTEGER;
  457. }
  458. }
  459. export class InvalidBracketAstNode extends ImmutableLeafAstNode {
  460. constructor(closingBrackets, length) {
  461. super(length);
  462. this.missingOpeningBracketIds = closingBrackets;
  463. }
  464. get kind() {
  465. return 3 /* AstNodeKind.UnexpectedClosingBracket */;
  466. }
  467. canBeReused(openedBracketIds) {
  468. return !openedBracketIds.intersects(this.missingOpeningBracketIds);
  469. }
  470. computeMinIndentation(offset, textModel) {
  471. return Number.MAX_SAFE_INTEGER;
  472. }
  473. }