rangeMap.ts 4.3 KB
Newer Older
J
Joao Moreno 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

J
Joao Moreno 已提交
6 7
import { IRange, Range } from 'vs/base/common/range';

J
Joao Moreno 已提交
8
export interface IItem {
J
Joao Moreno 已提交
9 10 11
	size: number;
}

J
Joao Moreno 已提交
12 13 14 15
export interface IRangedGroup {
	range: IRange;
	size: number;
}
J
Joao Moreno 已提交
16

J
Joao Moreno 已提交
17 18 19 20
/**
 * Returns the intersection between a ranged group and a range.
 * Returns `[]` if the intersection is empty.
 */
J
Joao Moreno 已提交
21 22
export function groupIntersect(range: IRange, groups: IRangedGroup[]): IRangedGroup[] {
	const result: IRangedGroup[] = [];
J
Joao Moreno 已提交
23

J
Johannes Rieken 已提交
24
	for (let r of groups) {
J
Joao Moreno 已提交
25 26
		if (range.start >= r.range.end) {
			continue;
J
Joao Moreno 已提交
27 28
		}

J
Joao Moreno 已提交
29 30 31
		if (range.end < r.range.start) {
			break;
		}
J
Joao Moreno 已提交
32

J
Joao Moreno 已提交
33
		const intersection = Range.intersect(range, r.range);
J
Joao Moreno 已提交
34

J
Joao Moreno 已提交
35
		if (Range.isEmpty(intersection)) {
J
Joao Moreno 已提交
36 37
			continue;
		}
J
Joao Moreno 已提交
38

J
Joao Moreno 已提交
39 40 41 42 43
		result.push({
			range: intersection,
			size: r.size
		});
	}
J
Joao Moreno 已提交
44

J
Joao Moreno 已提交
45 46
	return result;
}
J
Joao Moreno 已提交
47

J
Joao Moreno 已提交
48 49 50
/**
 * Shifts a range by that `much`.
 */
J
Joao Moreno 已提交
51
export function shift({ start, end }: IRange, much: number): IRange {
J
Joao Moreno 已提交
52 53
	return { start: start + much, end: end + much };
}
J
Joao Moreno 已提交
54

J
Joao Moreno 已提交
55 56 57 58 59 60 61 62
/**
 * Consolidates a collection of ranged groups.
 *
 * Consolidation is the process of merging consecutive ranged groups
 * that share the same `size`.
 */
export function consolidate(groups: IRangedGroup[]): IRangedGroup[] {
	const result: IRangedGroup[] = [];
63
	let previousGroup: IRangedGroup | null = null;
J
Joao Moreno 已提交
64

J
Johannes Rieken 已提交
65
	for (let group of groups) {
J
Joao Moreno 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
		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;
}

J
Joao Moreno 已提交
82 83 84 85 86
/**
 * Concatenates several collections of ranged groups into a single
 * collection.
 */
function concat(...groups: IRangedGroup[][]): IRangedGroup[] {
B
Benjamin Pasero 已提交
87
	return consolidate(groups.reduce((r, g) => r.concat(g), []));
J
Joao Moreno 已提交
88 89
}

J
Joao Moreno 已提交
90 91 92
export class RangeMap {

	private groups: IRangedGroup[] = [];
J
Joao Moreno 已提交
93
	private _size = 0;
J
Joao Moreno 已提交
94

J
Joao Moreno 已提交
95
	splice(index: number, deleteCount: number, items: IItem[] = []): void {
J
Joao Moreno 已提交
96
		const diff = items.length - deleteCount;
J
Joao Moreno 已提交
97 98 99 100
		const before = groupIntersect({ start: 0, end: index }, this.groups);
		const after = groupIntersect({ start: index + deleteCount, end: Number.POSITIVE_INFINITY }, this.groups)
			.map<IRangedGroup>(g => ({ range: shift(g.range, diff), size: g.size }));

J
Joao Moreno 已提交
101 102 103 104 105
		const middle = items.map<IRangedGroup>((item, i) => ({
			range: { start: index + i, end: index + i + 1 },
			size: item.size
		}));

J
Joao Moreno 已提交
106
		this.groups = concat(before, middle, after);
J
Joao Moreno 已提交
107
		this._size = this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0);
J
Joao Moreno 已提交
108 109
	}

J
Joao Moreno 已提交
110 111 112
	/**
	 * Returns the number of items in the range map.
	 */
J
Joao Moreno 已提交
113
	get count(): number {
J
Joao Moreno 已提交
114 115
		const len = this.groups.length;

P
pi1024e 已提交
116 117
		if (!len) {
			return 0;
J
Joao Moreno 已提交
118 119
		}

P
pi1024e 已提交
120
		return this.groups[len - 1].range.end;
J
Joao Moreno 已提交
121 122
	}

J
Joao Moreno 已提交
123 124 125
	/**
	 * Returns the sum of the sizes of all items in the range map.
	 */
J
Joao Moreno 已提交
126
	get size(): number {
J
Joao Moreno 已提交
127
		return this._size;
J
Joao Moreno 已提交
128 129
	}

J
Joao Moreno 已提交
130
	/**
J
Joao Moreno 已提交
131
	 * Returns the index of the item at the given position.
J
Joao Moreno 已提交
132
	 */
J
Joao Moreno 已提交
133
	indexAt(position: number): number {
J
Joao Moreno 已提交
134 135 136 137
		if (position < 0) {
			return -1;
		}

J
Joao Moreno 已提交
138 139
		let index = 0;
		let size = 0;
J
Joao Moreno 已提交
140

J
Johannes Rieken 已提交
141
		for (let group of this.groups) {
142 143
			const count = group.range.end - group.range.start;
			const newSize = size + (count * group.size);
J
Joao Moreno 已提交
144

J
Joao Moreno 已提交
145 146 147
			if (position < newSize) {
				return index + Math.floor((position - size) / group.size);
			}
J
Joao Moreno 已提交
148

149
			index += count;
J
Joao Moreno 已提交
150 151
			size = newSize;
		}
J
Joao Moreno 已提交
152

153
		return index;
J
Joao Moreno 已提交
154
	}
J
Joao Moreno 已提交
155

J
Joao Moreno 已提交
156 157 158 159 160 161 162 163
	/**
	 * Returns the index of the item right after the item at the
	 * index of the given position.
	 */
	indexAfter(position: number): number {
		return Math.min(this.indexAt(position) + 1, this.count);
	}

J
Joao Moreno 已提交
164 165 166
	/**
	 * Returns the start position of the item at the given index.
	 */
J
Joao Moreno 已提交
167
	positionAt(index: number): number {
J
Joao Moreno 已提交
168 169 170 171
		if (index < 0) {
			return -1;
		}

J
Joao Moreno 已提交
172 173
		let position = 0;
		let count = 0;
J
Joao Moreno 已提交
174

J
Johannes Rieken 已提交
175
		for (let group of this.groups) {
J
Joao Moreno 已提交
176 177
			const groupCount = group.range.end - group.range.start;
			const newCount = count + groupCount;
J
Joao Moreno 已提交
178

J
Joao Moreno 已提交
179 180 181
			if (index < newCount) {
				return position + ((index - count) * group.size);
			}
J
Joao Moreno 已提交
182

J
Joao Moreno 已提交
183 184 185
			position += groupCount * group.size;
			count = newCount;
		}
J
Joao Moreno 已提交
186

J
Joao Moreno 已提交
187 188
		return -1;
	}
J
Joao Moreno 已提交
189 190

	dispose() {
M
Matt Bierner 已提交
191
		this.groups = null!; // StrictNullOverride: nulling out ok in dispose
J
Joao Moreno 已提交
192
	}
P
pi1024e 已提交
193
}