| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- import { Range } from '../../../common/range.js';
- /**
- * Returns the intersection between a ranged group and a range.
- * Returns `[]` if the intersection is empty.
- */
- export function groupIntersect(range, groups) {
- const result = [];
- for (const r of groups) {
- if (range.start >= r.range.end) {
- continue;
- }
- if (range.end < r.range.start) {
- break;
- }
- const intersection = Range.intersect(range, r.range);
- if (Range.isEmpty(intersection)) {
- continue;
- }
- result.push({
- range: intersection,
- size: r.size
- });
- }
- return result;
- }
- /**
- * Shifts a range by that `much`.
- */
- export function shift({ start, end }, much) {
- return { start: start + much, end: end + much };
- }
- /**
- * Consolidates a collection of ranged groups.
- *
- * Consolidation is the process of merging consecutive ranged groups
- * that share the same `size`.
- */
- export function consolidate(groups) {
- const result = [];
- let previousGroup = null;
- for (const group of groups) {
- const start = group.range.start;
- const end = group.range.end;
- const size = group.size;
- if (previousGroup && size === previousGroup.size) {
- previousGroup.range.end = end;
- continue;
- }
- previousGroup = { range: { start, end }, size };
- result.push(previousGroup);
- }
- return result;
- }
- /**
- * Concatenates several collections of ranged groups into a single
- * collection.
- */
- function concat(...groups) {
- return consolidate(groups.reduce((r, g) => r.concat(g), []));
- }
- export class RangeMap {
- constructor() {
- this.groups = [];
- this._size = 0;
- }
- splice(index, deleteCount, items = []) {
- const diff = items.length - deleteCount;
- const before = groupIntersect({ start: 0, end: index }, this.groups);
- const after = groupIntersect({ start: index + deleteCount, end: Number.POSITIVE_INFINITY }, this.groups)
- .map(g => ({ range: shift(g.range, diff), size: g.size }));
- const middle = items.map((item, i) => ({
- range: { start: index + i, end: index + i + 1 },
- size: item.size
- }));
- this.groups = concat(before, middle, after);
- this._size = this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0);
- }
- /**
- * Returns the number of items in the range map.
- */
- get count() {
- const len = this.groups.length;
- if (!len) {
- return 0;
- }
- return this.groups[len - 1].range.end;
- }
- /**
- * Returns the sum of the sizes of all items in the range map.
- */
- get size() {
- return this._size;
- }
- /**
- * Returns the index of the item at the given position.
- */
- indexAt(position) {
- if (position < 0) {
- return -1;
- }
- let index = 0;
- let size = 0;
- for (const group of this.groups) {
- const count = group.range.end - group.range.start;
- const newSize = size + (count * group.size);
- if (position < newSize) {
- return index + Math.floor((position - size) / group.size);
- }
- index += count;
- size = newSize;
- }
- return index;
- }
- /**
- * Returns the index of the item right after the item at the
- * index of the given position.
- */
- indexAfter(position) {
- return Math.min(this.indexAt(position) + 1, this.count);
- }
- /**
- * Returns the start position of the item at the given index.
- */
- positionAt(index) {
- if (index < 0) {
- return -1;
- }
- let position = 0;
- let count = 0;
- for (const group of this.groups) {
- const groupCount = group.range.end - group.range.start;
- const newCount = count + groupCount;
- if (index < newCount) {
- return position + ((index - count) * group.size);
- }
- position += groupCount * group.size;
- count = newCount;
- }
- return -1;
- }
- }
|