syntaxRangeProvider.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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 { onUnexpectedExternalError } from '../../../base/common/errors.js';
  6. import { DisposableStore } from '../../../base/common/lifecycle.js';
  7. import { FoldingRegions, MAX_LINE_NUMBER } from './foldingRanges.js';
  8. const MAX_FOLDING_REGIONS = 5000;
  9. const foldingContext = {};
  10. export const ID_SYNTAX_PROVIDER = 'syntax';
  11. export class SyntaxRangeProvider {
  12. constructor(editorModel, providers, handleFoldingRangesChange, limit = MAX_FOLDING_REGIONS) {
  13. this.editorModel = editorModel;
  14. this.providers = providers;
  15. this.limit = limit;
  16. this.id = ID_SYNTAX_PROVIDER;
  17. for (const provider of providers) {
  18. if (typeof provider.onDidChange === 'function') {
  19. if (!this.disposables) {
  20. this.disposables = new DisposableStore();
  21. }
  22. this.disposables.add(provider.onDidChange(handleFoldingRangesChange));
  23. }
  24. }
  25. }
  26. compute(cancellationToken) {
  27. return collectSyntaxRanges(this.providers, this.editorModel, cancellationToken).then(ranges => {
  28. if (ranges) {
  29. let res = sanitizeRanges(ranges, this.limit);
  30. return res;
  31. }
  32. return null;
  33. });
  34. }
  35. dispose() {
  36. var _a;
  37. (_a = this.disposables) === null || _a === void 0 ? void 0 : _a.dispose();
  38. }
  39. }
  40. function collectSyntaxRanges(providers, model, cancellationToken) {
  41. let rangeData = null;
  42. let promises = providers.map((provider, i) => {
  43. return Promise.resolve(provider.provideFoldingRanges(model, foldingContext, cancellationToken)).then(ranges => {
  44. if (cancellationToken.isCancellationRequested) {
  45. return;
  46. }
  47. if (Array.isArray(ranges)) {
  48. if (!Array.isArray(rangeData)) {
  49. rangeData = [];
  50. }
  51. let nLines = model.getLineCount();
  52. for (let r of ranges) {
  53. if (r.start > 0 && r.end > r.start && r.end <= nLines) {
  54. rangeData.push({ start: r.start, end: r.end, rank: i, kind: r.kind });
  55. }
  56. }
  57. }
  58. }, onUnexpectedExternalError);
  59. });
  60. return Promise.all(promises).then(_ => {
  61. return rangeData;
  62. });
  63. }
  64. export class RangesCollector {
  65. constructor(foldingRangesLimit) {
  66. this._startIndexes = [];
  67. this._endIndexes = [];
  68. this._nestingLevels = [];
  69. this._nestingLevelCounts = [];
  70. this._types = [];
  71. this._length = 0;
  72. this._foldingRangesLimit = foldingRangesLimit;
  73. }
  74. add(startLineNumber, endLineNumber, type, nestingLevel) {
  75. if (startLineNumber > MAX_LINE_NUMBER || endLineNumber > MAX_LINE_NUMBER) {
  76. return;
  77. }
  78. let index = this._length;
  79. this._startIndexes[index] = startLineNumber;
  80. this._endIndexes[index] = endLineNumber;
  81. this._nestingLevels[index] = nestingLevel;
  82. this._types[index] = type;
  83. this._length++;
  84. if (nestingLevel < 30) {
  85. this._nestingLevelCounts[nestingLevel] = (this._nestingLevelCounts[nestingLevel] || 0) + 1;
  86. }
  87. }
  88. toIndentRanges() {
  89. if (this._length <= this._foldingRangesLimit) {
  90. let startIndexes = new Uint32Array(this._length);
  91. let endIndexes = new Uint32Array(this._length);
  92. for (let i = 0; i < this._length; i++) {
  93. startIndexes[i] = this._startIndexes[i];
  94. endIndexes[i] = this._endIndexes[i];
  95. }
  96. return new FoldingRegions(startIndexes, endIndexes, this._types);
  97. }
  98. else {
  99. let entries = 0;
  100. let maxLevel = this._nestingLevelCounts.length;
  101. for (let i = 0; i < this._nestingLevelCounts.length; i++) {
  102. let n = this._nestingLevelCounts[i];
  103. if (n) {
  104. if (n + entries > this._foldingRangesLimit) {
  105. maxLevel = i;
  106. break;
  107. }
  108. entries += n;
  109. }
  110. }
  111. let startIndexes = new Uint32Array(this._foldingRangesLimit);
  112. let endIndexes = new Uint32Array(this._foldingRangesLimit);
  113. let types = [];
  114. for (let i = 0, k = 0; i < this._length; i++) {
  115. let level = this._nestingLevels[i];
  116. if (level < maxLevel || (level === maxLevel && entries++ < this._foldingRangesLimit)) {
  117. startIndexes[k] = this._startIndexes[i];
  118. endIndexes[k] = this._endIndexes[i];
  119. types[k] = this._types[i];
  120. k++;
  121. }
  122. }
  123. return new FoldingRegions(startIndexes, endIndexes, types);
  124. }
  125. }
  126. }
  127. export function sanitizeRanges(rangeData, limit) {
  128. let sorted = rangeData.sort((d1, d2) => {
  129. let diff = d1.start - d2.start;
  130. if (diff === 0) {
  131. diff = d1.rank - d2.rank;
  132. }
  133. return diff;
  134. });
  135. let collector = new RangesCollector(limit);
  136. let top = undefined;
  137. let previous = [];
  138. for (let entry of sorted) {
  139. if (!top) {
  140. top = entry;
  141. collector.add(entry.start, entry.end, entry.kind && entry.kind.value, previous.length);
  142. }
  143. else {
  144. if (entry.start > top.start) {
  145. if (entry.end <= top.end) {
  146. previous.push(top);
  147. top = entry;
  148. collector.add(entry.start, entry.end, entry.kind && entry.kind.value, previous.length);
  149. }
  150. else {
  151. if (entry.start > top.end) {
  152. do {
  153. top = previous.pop();
  154. } while (top && entry.start > top.end);
  155. if (top) {
  156. previous.push(top);
  157. }
  158. top = entry;
  159. }
  160. collector.add(entry.start, entry.end, entry.kind && entry.kind.value, previous.length);
  161. }
  162. }
  163. }
  164. }
  165. return collector.toIndentRanges();
  166. }