hiddenRangeModel.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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 { findFirstInSorted } from '../../../base/common/arrays.js';
  6. import { Emitter } from '../../../base/common/event.js';
  7. import { Range } from '../../common/core/range.js';
  8. import { countEOL } from '../../common/model/tokensStore.js';
  9. export class HiddenRangeModel {
  10. constructor(model) {
  11. this._updateEventEmitter = new Emitter();
  12. this._hasLineChanges = false;
  13. this._foldingModel = model;
  14. this._foldingModelListener = model.onDidChange(_ => this.updateHiddenRanges());
  15. this._hiddenRanges = [];
  16. if (model.regions.length) {
  17. this.updateHiddenRanges();
  18. }
  19. }
  20. get onDidChange() { return this._updateEventEmitter.event; }
  21. get hiddenRanges() { return this._hiddenRanges; }
  22. notifyChangeModelContent(e) {
  23. if (this._hiddenRanges.length && !this._hasLineChanges) {
  24. this._hasLineChanges = e.changes.some(change => {
  25. return change.range.endLineNumber !== change.range.startLineNumber || countEOL(change.text)[0] !== 0;
  26. });
  27. }
  28. }
  29. updateHiddenRanges() {
  30. let updateHiddenAreas = false;
  31. let newHiddenAreas = [];
  32. let i = 0; // index into hidden
  33. let k = 0;
  34. let lastCollapsedStart = Number.MAX_VALUE;
  35. let lastCollapsedEnd = -1;
  36. let ranges = this._foldingModel.regions;
  37. for (; i < ranges.length; i++) {
  38. if (!ranges.isCollapsed(i)) {
  39. continue;
  40. }
  41. let startLineNumber = ranges.getStartLineNumber(i) + 1; // the first line is not hidden
  42. let endLineNumber = ranges.getEndLineNumber(i);
  43. if (lastCollapsedStart <= startLineNumber && endLineNumber <= lastCollapsedEnd) {
  44. // ignore ranges contained in collapsed regions
  45. continue;
  46. }
  47. if (!updateHiddenAreas && k < this._hiddenRanges.length && this._hiddenRanges[k].startLineNumber === startLineNumber && this._hiddenRanges[k].endLineNumber === endLineNumber) {
  48. // reuse the old ranges
  49. newHiddenAreas.push(this._hiddenRanges[k]);
  50. k++;
  51. }
  52. else {
  53. updateHiddenAreas = true;
  54. newHiddenAreas.push(new Range(startLineNumber, 1, endLineNumber, 1));
  55. }
  56. lastCollapsedStart = startLineNumber;
  57. lastCollapsedEnd = endLineNumber;
  58. }
  59. if (this._hasLineChanges || updateHiddenAreas || k < this._hiddenRanges.length) {
  60. this.applyHiddenRanges(newHiddenAreas);
  61. }
  62. }
  63. applyMemento(state) {
  64. if (!Array.isArray(state) || state.length === 0) {
  65. return false;
  66. }
  67. let hiddenRanges = [];
  68. for (let r of state) {
  69. if (!r.startLineNumber || !r.endLineNumber) {
  70. return false;
  71. }
  72. hiddenRanges.push(new Range(r.startLineNumber + 1, 1, r.endLineNumber, 1));
  73. }
  74. this.applyHiddenRanges(hiddenRanges);
  75. return true;
  76. }
  77. /**
  78. * Collapse state memento, for persistence only, only used if folding model is not yet initialized
  79. */
  80. getMemento() {
  81. return this._hiddenRanges.map(r => ({ startLineNumber: r.startLineNumber - 1, endLineNumber: r.endLineNumber }));
  82. }
  83. applyHiddenRanges(newHiddenAreas) {
  84. this._hiddenRanges = newHiddenAreas;
  85. this._hasLineChanges = false;
  86. this._updateEventEmitter.fire(newHiddenAreas);
  87. }
  88. hasRanges() {
  89. return this._hiddenRanges.length > 0;
  90. }
  91. isHidden(line) {
  92. return findRange(this._hiddenRanges, line) !== null;
  93. }
  94. adjustSelections(selections) {
  95. let hasChanges = false;
  96. let editorModel = this._foldingModel.textModel;
  97. let lastRange = null;
  98. let adjustLine = (line) => {
  99. if (!lastRange || !isInside(line, lastRange)) {
  100. lastRange = findRange(this._hiddenRanges, line);
  101. }
  102. if (lastRange) {
  103. return lastRange.startLineNumber - 1;
  104. }
  105. return null;
  106. };
  107. for (let i = 0, len = selections.length; i < len; i++) {
  108. let selection = selections[i];
  109. let adjustedStartLine = adjustLine(selection.startLineNumber);
  110. if (adjustedStartLine) {
  111. selection = selection.setStartPosition(adjustedStartLine, editorModel.getLineMaxColumn(adjustedStartLine));
  112. hasChanges = true;
  113. }
  114. let adjustedEndLine = adjustLine(selection.endLineNumber);
  115. if (adjustedEndLine) {
  116. selection = selection.setEndPosition(adjustedEndLine, editorModel.getLineMaxColumn(adjustedEndLine));
  117. hasChanges = true;
  118. }
  119. selections[i] = selection;
  120. }
  121. return hasChanges;
  122. }
  123. dispose() {
  124. if (this.hiddenRanges.length > 0) {
  125. this._hiddenRanges = [];
  126. this._updateEventEmitter.fire(this._hiddenRanges);
  127. }
  128. if (this._foldingModelListener) {
  129. this._foldingModelListener.dispose();
  130. this._foldingModelListener = null;
  131. }
  132. }
  133. }
  134. function isInside(line, range) {
  135. return line >= range.startLineNumber && line <= range.endLineNumber;
  136. }
  137. function findRange(ranges, line) {
  138. let i = findFirstInSorted(ranges, r => line < r.startLineNumber) - 1;
  139. if (i >= 0 && ranges[i].endLineNumber >= line) {
  140. return ranges[i];
  141. }
  142. return null;
  143. }