Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/smartSelect/browser/bracketSelections.ts
3296 views
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
6
import { LinkedList } from '../../../../base/common/linkedList.js';
7
import { Position } from '../../../common/core/position.js';
8
import { Range } from '../../../common/core/range.js';
9
import { ITextModel } from '../../../common/model.js';
10
import { SelectionRange, SelectionRangeProvider } from '../../../common/languages.js';
11
12
export class BracketSelectionRangeProvider implements SelectionRangeProvider {
13
14
async provideSelectionRanges(model: ITextModel, positions: Position[]): Promise<SelectionRange[][]> {
15
const result: SelectionRange[][] = [];
16
17
for (const position of positions) {
18
const bucket: SelectionRange[] = [];
19
result.push(bucket);
20
21
const ranges = new Map<string, LinkedList<Range>>();
22
await new Promise<void>(resolve => BracketSelectionRangeProvider._bracketsRightYield(resolve, 0, model, position, ranges));
23
await new Promise<void>(resolve => BracketSelectionRangeProvider._bracketsLeftYield(resolve, 0, model, position, ranges, bucket));
24
}
25
26
return result;
27
}
28
29
public static _maxDuration = 30;
30
private static readonly _maxRounds = 2;
31
32
private static _bracketsRightYield(resolve: () => void, round: number, model: ITextModel, pos: Position, ranges: Map<string, LinkedList<Range>>): void {
33
const counts = new Map<string, number>();
34
const t1 = Date.now();
35
while (true) {
36
if (round >= BracketSelectionRangeProvider._maxRounds) {
37
resolve();
38
break;
39
}
40
if (!pos) {
41
resolve();
42
break;
43
}
44
const bracket = model.bracketPairs.findNextBracket(pos);
45
if (!bracket) {
46
resolve();
47
break;
48
}
49
const d = Date.now() - t1;
50
if (d > BracketSelectionRangeProvider._maxDuration) {
51
setTimeout(() => BracketSelectionRangeProvider._bracketsRightYield(resolve, round + 1, model, pos, ranges));
52
break;
53
}
54
if (bracket.bracketInfo.isOpeningBracket) {
55
const key = bracket.bracketInfo.bracketText;
56
// wait for closing
57
const val = counts.has(key) ? counts.get(key)! : 0;
58
counts.set(key, val + 1);
59
} else {
60
const key = bracket.bracketInfo.getOpeningBrackets()[0].bracketText;
61
// process closing
62
let val = counts.has(key) ? counts.get(key)! : 0;
63
val -= 1;
64
counts.set(key, Math.max(0, val));
65
if (val < 0) {
66
let list = ranges.get(key);
67
if (!list) {
68
list = new LinkedList();
69
ranges.set(key, list);
70
}
71
list.push(bracket.range);
72
}
73
}
74
pos = bracket.range.getEndPosition();
75
}
76
}
77
78
private static _bracketsLeftYield(resolve: () => void, round: number, model: ITextModel, pos: Position, ranges: Map<string, LinkedList<Range>>, bucket: SelectionRange[]): void {
79
const counts = new Map<string, number>();
80
const t1 = Date.now();
81
while (true) {
82
if (round >= BracketSelectionRangeProvider._maxRounds && ranges.size === 0) {
83
resolve();
84
break;
85
}
86
if (!pos) {
87
resolve();
88
break;
89
}
90
const bracket = model.bracketPairs.findPrevBracket(pos);
91
if (!bracket) {
92
resolve();
93
break;
94
}
95
const d = Date.now() - t1;
96
if (d > BracketSelectionRangeProvider._maxDuration) {
97
setTimeout(() => BracketSelectionRangeProvider._bracketsLeftYield(resolve, round + 1, model, pos, ranges, bucket));
98
break;
99
}
100
if (!bracket.bracketInfo.isOpeningBracket) {
101
const key = bracket.bracketInfo.getOpeningBrackets()[0].bracketText;
102
// wait for opening
103
const val = counts.has(key) ? counts.get(key)! : 0;
104
counts.set(key, val + 1);
105
} else {
106
const key = bracket.bracketInfo.bracketText;
107
// opening
108
let val = counts.has(key) ? counts.get(key)! : 0;
109
val -= 1;
110
counts.set(key, Math.max(0, val));
111
if (val < 0) {
112
const list = ranges.get(key);
113
if (list) {
114
const closing = list.shift();
115
if (list.size === 0) {
116
ranges.delete(key);
117
}
118
const innerBracket = Range.fromPositions(bracket.range.getEndPosition(), closing!.getStartPosition());
119
const outerBracket = Range.fromPositions(bracket.range.getStartPosition(), closing!.getEndPosition());
120
bucket.push({ range: innerBracket });
121
bucket.push({ range: outerBracket });
122
BracketSelectionRangeProvider._addBracketLeading(model, outerBracket, bucket);
123
}
124
}
125
}
126
pos = bracket.range.getStartPosition();
127
}
128
}
129
130
private static _addBracketLeading(model: ITextModel, bracket: Range, bucket: SelectionRange[]): void {
131
if (bracket.startLineNumber === bracket.endLineNumber) {
132
return;
133
}
134
// xxxxxxxx {
135
//
136
// }
137
const startLine = bracket.startLineNumber;
138
const column = model.getLineFirstNonWhitespaceColumn(startLine);
139
if (column !== 0 && column !== bracket.startColumn) {
140
bucket.push({ range: Range.fromPositions(new Position(startLine, column), bracket.getEndPosition()) });
141
bucket.push({ range: Range.fromPositions(new Position(startLine, 1), bracket.getEndPosition()) });
142
}
143
144
// xxxxxxxx
145
// {
146
//
147
// }
148
const aboveLine = startLine - 1;
149
if (aboveLine > 0) {
150
const column = model.getLineFirstNonWhitespaceColumn(aboveLine);
151
if (column === bracket.startColumn && column !== model.getLineLastNonWhitespaceColumn(aboveLine)) {
152
bucket.push({ range: Range.fromPositions(new Position(aboveLine, column), bracket.getEndPosition()) });
153
bucket.push({ range: Range.fromPositions(new Position(aboveLine, 1), bracket.getEndPosition()) });
154
}
155
}
156
}
157
}
158
159