foldingRanges.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  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. export const MAX_FOLDING_REGIONS = 0xFFFF;
  6. export const MAX_LINE_NUMBER = 0xFFFFFF;
  7. const MASK_INDENT = 0xFF000000;
  8. export class FoldingRegions {
  9. constructor(startIndexes, endIndexes, types) {
  10. if (startIndexes.length !== endIndexes.length || startIndexes.length > MAX_FOLDING_REGIONS) {
  11. throw new Error('invalid startIndexes or endIndexes size');
  12. }
  13. this._startIndexes = startIndexes;
  14. this._endIndexes = endIndexes;
  15. this._collapseStates = new Uint32Array(Math.ceil(startIndexes.length / 32));
  16. this._types = types;
  17. this._parentsComputed = false;
  18. }
  19. ensureParentIndices() {
  20. if (!this._parentsComputed) {
  21. this._parentsComputed = true;
  22. let parentIndexes = [];
  23. let isInsideLast = (startLineNumber, endLineNumber) => {
  24. let index = parentIndexes[parentIndexes.length - 1];
  25. return this.getStartLineNumber(index) <= startLineNumber && this.getEndLineNumber(index) >= endLineNumber;
  26. };
  27. for (let i = 0, len = this._startIndexes.length; i < len; i++) {
  28. let startLineNumber = this._startIndexes[i];
  29. let endLineNumber = this._endIndexes[i];
  30. if (startLineNumber > MAX_LINE_NUMBER || endLineNumber > MAX_LINE_NUMBER) {
  31. throw new Error('startLineNumber or endLineNumber must not exceed ' + MAX_LINE_NUMBER);
  32. }
  33. while (parentIndexes.length > 0 && !isInsideLast(startLineNumber, endLineNumber)) {
  34. parentIndexes.pop();
  35. }
  36. let parentIndex = parentIndexes.length > 0 ? parentIndexes[parentIndexes.length - 1] : -1;
  37. parentIndexes.push(i);
  38. this._startIndexes[i] = startLineNumber + ((parentIndex & 0xFF) << 24);
  39. this._endIndexes[i] = endLineNumber + ((parentIndex & 0xFF00) << 16);
  40. }
  41. }
  42. }
  43. get length() {
  44. return this._startIndexes.length;
  45. }
  46. getStartLineNumber(index) {
  47. return this._startIndexes[index] & MAX_LINE_NUMBER;
  48. }
  49. getEndLineNumber(index) {
  50. return this._endIndexes[index] & MAX_LINE_NUMBER;
  51. }
  52. getType(index) {
  53. return this._types ? this._types[index] : undefined;
  54. }
  55. hasTypes() {
  56. return !!this._types;
  57. }
  58. isCollapsed(index) {
  59. let arrayIndex = (index / 32) | 0;
  60. let bit = index % 32;
  61. return (this._collapseStates[arrayIndex] & (1 << bit)) !== 0;
  62. }
  63. setCollapsed(index, newState) {
  64. let arrayIndex = (index / 32) | 0;
  65. let bit = index % 32;
  66. let value = this._collapseStates[arrayIndex];
  67. if (newState) {
  68. this._collapseStates[arrayIndex] = value | (1 << bit);
  69. }
  70. else {
  71. this._collapseStates[arrayIndex] = value & ~(1 << bit);
  72. }
  73. }
  74. setCollapsedAllOfType(type, newState) {
  75. let hasChanged = false;
  76. if (this._types) {
  77. for (let i = 0; i < this._types.length; i++) {
  78. if (this._types[i] === type) {
  79. this.setCollapsed(i, newState);
  80. hasChanged = true;
  81. }
  82. }
  83. }
  84. return hasChanged;
  85. }
  86. toRegion(index) {
  87. return new FoldingRegion(this, index);
  88. }
  89. getParentIndex(index) {
  90. this.ensureParentIndices();
  91. let parent = ((this._startIndexes[index] & MASK_INDENT) >>> 24) + ((this._endIndexes[index] & MASK_INDENT) >>> 16);
  92. if (parent === MAX_FOLDING_REGIONS) {
  93. return -1;
  94. }
  95. return parent;
  96. }
  97. contains(index, line) {
  98. return this.getStartLineNumber(index) <= line && this.getEndLineNumber(index) >= line;
  99. }
  100. findIndex(line) {
  101. let low = 0, high = this._startIndexes.length;
  102. if (high === 0) {
  103. return -1; // no children
  104. }
  105. while (low < high) {
  106. let mid = Math.floor((low + high) / 2);
  107. if (line < this.getStartLineNumber(mid)) {
  108. high = mid;
  109. }
  110. else {
  111. low = mid + 1;
  112. }
  113. }
  114. return low - 1;
  115. }
  116. findRange(line) {
  117. let index = this.findIndex(line);
  118. if (index >= 0) {
  119. let endLineNumber = this.getEndLineNumber(index);
  120. if (endLineNumber >= line) {
  121. return index;
  122. }
  123. index = this.getParentIndex(index);
  124. while (index !== -1) {
  125. if (this.contains(index, line)) {
  126. return index;
  127. }
  128. index = this.getParentIndex(index);
  129. }
  130. }
  131. return -1;
  132. }
  133. toString() {
  134. let res = [];
  135. for (let i = 0; i < this.length; i++) {
  136. res[i] = `[${this.isCollapsed(i) ? '+' : '-'}] ${this.getStartLineNumber(i)}/${this.getEndLineNumber(i)}`;
  137. }
  138. return res.join(', ');
  139. }
  140. }
  141. export class FoldingRegion {
  142. constructor(ranges, index) {
  143. this.ranges = ranges;
  144. this.index = index;
  145. }
  146. get startLineNumber() {
  147. return this.ranges.getStartLineNumber(this.index);
  148. }
  149. get endLineNumber() {
  150. return this.ranges.getEndLineNumber(this.index);
  151. }
  152. get regionIndex() {
  153. return this.index;
  154. }
  155. get parentIndex() {
  156. return this.ranges.getParentIndex(this.index);
  157. }
  158. get isCollapsed() {
  159. return this.ranges.isCollapsed(this.index);
  160. }
  161. containedBy(range) {
  162. return range.startLineNumber <= this.startLineNumber && range.endLineNumber >= this.endLineNumber;
  163. }
  164. containsLine(lineNumber) {
  165. return this.startLineNumber <= lineNumber && lineNumber <= this.endLineNumber;
  166. }
  167. }