Path: blob/main/src/vs/editor/contrib/smartSelect/browser/bracketSelections.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { LinkedList } from '../../../../base/common/linkedList.js';6import { Position } from '../../../common/core/position.js';7import { Range } from '../../../common/core/range.js';8import { ITextModel } from '../../../common/model.js';9import { SelectionRange, SelectionRangeProvider } from '../../../common/languages.js';1011export class BracketSelectionRangeProvider implements SelectionRangeProvider {1213async provideSelectionRanges(model: ITextModel, positions: Position[]): Promise<SelectionRange[][]> {14const result: SelectionRange[][] = [];1516for (const position of positions) {17const bucket: SelectionRange[] = [];18result.push(bucket);1920const ranges = new Map<string, LinkedList<Range>>();21await new Promise<void>(resolve => BracketSelectionRangeProvider._bracketsRightYield(resolve, 0, model, position, ranges));22await new Promise<void>(resolve => BracketSelectionRangeProvider._bracketsLeftYield(resolve, 0, model, position, ranges, bucket));23}2425return result;26}2728public static _maxDuration = 30;29private static readonly _maxRounds = 2;3031private static _bracketsRightYield(resolve: () => void, round: number, model: ITextModel, pos: Position, ranges: Map<string, LinkedList<Range>>): void {32const counts = new Map<string, number>();33const t1 = Date.now();34while (true) {35if (round >= BracketSelectionRangeProvider._maxRounds) {36resolve();37break;38}39if (!pos) {40resolve();41break;42}43const bracket = model.bracketPairs.findNextBracket(pos);44if (!bracket) {45resolve();46break;47}48const d = Date.now() - t1;49if (d > BracketSelectionRangeProvider._maxDuration) {50setTimeout(() => BracketSelectionRangeProvider._bracketsRightYield(resolve, round + 1, model, pos, ranges));51break;52}53if (bracket.bracketInfo.isOpeningBracket) {54const key = bracket.bracketInfo.bracketText;55// wait for closing56const val = counts.has(key) ? counts.get(key)! : 0;57counts.set(key, val + 1);58} else {59const key = bracket.bracketInfo.getOpeningBrackets()[0].bracketText;60// process closing61let val = counts.has(key) ? counts.get(key)! : 0;62val -= 1;63counts.set(key, Math.max(0, val));64if (val < 0) {65let list = ranges.get(key);66if (!list) {67list = new LinkedList();68ranges.set(key, list);69}70list.push(bracket.range);71}72}73pos = bracket.range.getEndPosition();74}75}7677private static _bracketsLeftYield(resolve: () => void, round: number, model: ITextModel, pos: Position, ranges: Map<string, LinkedList<Range>>, bucket: SelectionRange[]): void {78const counts = new Map<string, number>();79const t1 = Date.now();80while (true) {81if (round >= BracketSelectionRangeProvider._maxRounds && ranges.size === 0) {82resolve();83break;84}85if (!pos) {86resolve();87break;88}89const bracket = model.bracketPairs.findPrevBracket(pos);90if (!bracket) {91resolve();92break;93}94const d = Date.now() - t1;95if (d > BracketSelectionRangeProvider._maxDuration) {96setTimeout(() => BracketSelectionRangeProvider._bracketsLeftYield(resolve, round + 1, model, pos, ranges, bucket));97break;98}99if (!bracket.bracketInfo.isOpeningBracket) {100const key = bracket.bracketInfo.getOpeningBrackets()[0].bracketText;101// wait for opening102const val = counts.has(key) ? counts.get(key)! : 0;103counts.set(key, val + 1);104} else {105const key = bracket.bracketInfo.bracketText;106// opening107let val = counts.has(key) ? counts.get(key)! : 0;108val -= 1;109counts.set(key, Math.max(0, val));110if (val < 0) {111const list = ranges.get(key);112if (list) {113const closing = list.shift();114if (list.size === 0) {115ranges.delete(key);116}117const innerBracket = Range.fromPositions(bracket.range.getEndPosition(), closing!.getStartPosition());118const outerBracket = Range.fromPositions(bracket.range.getStartPosition(), closing!.getEndPosition());119bucket.push({ range: innerBracket });120bucket.push({ range: outerBracket });121BracketSelectionRangeProvider._addBracketLeading(model, outerBracket, bucket);122}123}124}125pos = bracket.range.getStartPosition();126}127}128129private static _addBracketLeading(model: ITextModel, bracket: Range, bucket: SelectionRange[]): void {130if (bracket.startLineNumber === bracket.endLineNumber) {131return;132}133// xxxxxxxx {134//135// }136const startLine = bracket.startLineNumber;137const column = model.getLineFirstNonWhitespaceColumn(startLine);138if (column !== 0 && column !== bracket.startColumn) {139bucket.push({ range: Range.fromPositions(new Position(startLine, column), bracket.getEndPosition()) });140bucket.push({ range: Range.fromPositions(new Position(startLine, 1), bracket.getEndPosition()) });141}142143// xxxxxxxx144// {145//146// }147const aboveLine = startLine - 1;148if (aboveLine > 0) {149const column = model.getLineFirstNonWhitespaceColumn(aboveLine);150if (column === bracket.startColumn && column !== model.getLineLastNonWhitespaceColumn(aboveLine)) {151bucket.push({ range: Range.fromPositions(new Position(aboveLine, column), bracket.getEndPosition()) });152bucket.push({ range: Range.fromPositions(new Position(aboveLine, 1), bracket.getEndPosition()) });153}154}155}156}157158159