bd54ba1ddc3f5e38236af8b50c15b6441e166a87c7e7bd15420c2ba91f15fc9346ab30f715528dceb98a73a30b85935c2bb2f4ad36195119bb9fda061fcea8 6.8 KB

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