Skip to content

Commit 78cb0b4

Browse files
committed
Fixes microsoft#136474, fixes microsoft#136475. Rewrites bracket guides.
1 parent 8b7086a commit 78cb0b4

File tree

4 files changed

+219
-187
lines changed

4 files changed

+219
-187
lines changed

src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,13 @@ export class IndentGuidesOverlay extends DynamicViewOverlay {
131131
let result = '';
132132
const leftOffset = ctx.visibleRangeForPosition(new Position(lineNumber, 1))?.left ?? 0;
133133
for (const guide of indent) {
134-
const left = leftOffset + (guide.visibleColumn - 1) * this._spaceWidth;
134+
const left =
135+
guide.column === -1
136+
? leftOffset + (guide.visibleColumn - 1) * this._spaceWidth
137+
: ctx.visibleRangeForPosition(
138+
new Position(lineNumber, guide.column)
139+
)!.left;
140+
135141
if (left > scrollWidth || (this._maxIndentLeft > 0 && left > this._maxIndentLeft)) {
136142
break;
137143
}
@@ -217,8 +223,11 @@ export class IndentGuidesOverlay extends DynamicViewOverlay {
217223
lineGuides.push(
218224
new IndentGuide(
219225
indentGuide,
226+
-1,
220227
isActive ? 'core-guide-indent-active' : 'core-guide-indent',
221-
null
228+
null,
229+
-1,
230+
-1,
222231
)
223232
);
224233
}

src/vs/editor/common/model/guidesTextModelPart.ts

Lines changed: 127 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { ArrayQueue, findLast } from 'vs/base/common/arrays';
6+
import { findLast } from 'vs/base/common/arrays';
77
import * as strings from 'vs/base/common/strings';
88
import { CursorColumns } from 'vs/editor/common/core/cursorColumns';
99
import { IPosition, Position } from 'vs/editor/common/core/position';
1010
import { Range } from 'vs/editor/common/core/range';
11-
import { BracketPairInfo } from 'vs/editor/common/textModelBracketPairs';
1211
import type { TextModel } from 'vs/editor/common/model/textModel';
1312
import { TextModelPart } from 'vs/editor/common/model/textModelPart';
1413
import { computeIndentLevel } from 'vs/editor/common/model/utils';
@@ -278,6 +277,13 @@ export class GuidesTextModelPart extends TextModelPart implements IGuidesTextMod
278277
options: BracketGuideOptions
279278
): IndentGuide[][] {
280279
const result: IndentGuide[][] = [];
280+
for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
281+
result.push([]);
282+
}
283+
284+
// If requested, this could be made configurable.
285+
const includeSingleLinePairs = true;
286+
281287
const bracketPairs =
282288
this.textModel.bracketPairs.getBracketPairsInRangeWithMinIndentation(
283289
new Range(
@@ -302,205 +308,151 @@ export class GuidesTextModelPart extends TextModelPart implements IGuidesTextMod
302308

303309
activeBracketPairRange = findLast(
304310
bracketsContainingActivePosition,
305-
/* Exclude single line bracket pairs for cases such as
306-
* ```
307-
* function test() {
308-
* if (true) { | }
309-
* }
310-
* ```
311-
*/
312-
(i) => i.range.startLineNumber !== i.range.endLineNumber
311+
(i) => includeSingleLinePairs || i.range.startLineNumber !== i.range.endLineNumber
313312
)?.range;
314313
}
315314

316-
const queue = new ArrayQueue(bracketPairs);
317-
/** Indexed by nesting level */
318-
const activeGuides = new Array<{
319-
nestingLevel: number;
320-
nestingLevelOfEqualBracketType: number;
321-
guideVisibleColumn: number;
322-
start: Position;
323-
visibleStartColumn: number;
324-
end: Position;
325-
visibleEndColumn: number;
326-
bracketPair: BracketPairInfo;
327-
renderHorizontalEndLineAtTheBottom: boolean;
328-
} | null>();
329-
const nextGuides = new Array<IndentGuide>();
315+
const independentColorPoolPerBracketType = this.textModel.getOptions().bracketPairColorizationOptions.independentColorPoolPerBracketType;
330316
const colorProvider = new BracketPairGuidesClassNames();
331-
const independentColorPoolPerBracketType = this.textModel.getOptions().bracketPairColorizationOptions
332-
.independentColorPoolPerBracketType;
333317

334-
for (
335-
let lineNumber = startLineNumber;
336-
lineNumber <= endLineNumber;
337-
lineNumber++
338-
) {
339-
let guides = new Array<IndentGuide>();
340-
if (nextGuides.length > 0) {
341-
guides = guides.concat(nextGuides);
342-
nextGuides.length = 0;
318+
for (const pair of bracketPairs) {
319+
/*
320+
321+
322+
{
323+
|
324+
}
325+
326+
{
327+
|
328+
----}
329+
330+
____{
331+
|test
332+
----}
333+
334+
renderHorizontalEndLineAtTheBottom:
335+
{
336+
|
337+
|x}
338+
--
339+
renderHorizontalEndLineAtTheBottom:
340+
____{
341+
|test
342+
| x }
343+
----
344+
*/
345+
346+
const isActive = activeBracketPairRange && pair.range.equalsRange(activeBracketPairRange);
347+
348+
if (!isActive && !options.includeInactive) {
349+
continue;
343350
}
344-
result.push(guides);
345-
346-
// Update activeGuides
347-
for (const pair of queue.takeWhile(
348-
(b) => b.openingBracketRange.startLineNumber <= lineNumber
349-
) || []) {
350-
if (pair.range.startLineNumber === pair.range.endLineNumber) {
351-
// ignore single line brackets
352-
continue;
353-
}
354-
const guideVisibleColumn = Math.min(
355-
this.getVisibleColumnFromPosition(
356-
pair.openingBracketRange.getStartPosition()
357-
),
358-
this.getVisibleColumnFromPosition(
359-
pair.closingBracketRange?.getStartPosition() ??
360-
pair.range.getEndPosition()
361-
),
362-
pair.minVisibleColumnIndentation + 1
363-
);
364-
let renderHorizontalEndLineAtTheBottom = false;
365-
if (pair.closingBracketRange) {
366-
const firstNonWsIndex = strings.firstNonWhitespaceIndex(
367-
this.textModel.getLineContent(
368-
pair.closingBracketRange.startLineNumber
351+
352+
const className =
353+
colorProvider.getInlineClassName(pair.nestingLevel, pair.nestingLevelOfEqualBracketType, independentColorPoolPerBracketType) +
354+
(options.highlightActive && isActive
355+
? ' ' + colorProvider.activeClassName
356+
: '');
357+
358+
359+
const start = pair.openingBracketRange.getStartPosition();
360+
const end =
361+
pair.closingBracketRange?.getStartPosition() ??
362+
pair.range.getEndPosition();
363+
364+
const horizontalGuides = options.horizontalGuides === HorizontalGuidesState.Enabled || (options.horizontalGuides === HorizontalGuidesState.EnabledForActive && isActive);
365+
366+
if (pair.range.startLineNumber === pair.range.endLineNumber) {
367+
if (includeSingleLinePairs && horizontalGuides) {
368+
369+
result[pair.range.startLineNumber - startLineNumber].push(
370+
new IndentGuide(
371+
-1,
372+
pair.openingBracketRange.getEndPosition().column,
373+
className,
374+
new IndentGuideHorizontalLine(false, end.column),
375+
-1,
376+
-1,
369377
)
370378
);
371-
if (firstNonWsIndex < pair.closingBracketRange.startColumn - 1) {
372-
renderHorizontalEndLineAtTheBottom = true;
373-
}
374-
}
375379

376-
const start = pair.openingBracketRange.getStartPosition();
377-
const end =
378-
pair.closingBracketRange?.getStartPosition() ??
379-
pair.range.getEndPosition();
380-
381-
if (pair.closingBracketRange === undefined) {
382-
// Don't show guides for bracket pairs that are not balanced.
383-
// See #135125.
384-
activeGuides[pair.nestingLevel] = null;
385-
} else {
386-
activeGuides[pair.nestingLevel] = {
387-
nestingLevel: pair.nestingLevel,
388-
nestingLevelOfEqualBracketType: pair.nestingLevelOfEqualBracketType,
389-
guideVisibleColumn,
390-
start,
391-
visibleStartColumn: this.getVisibleColumnFromPosition(start),
392-
end,
393-
visibleEndColumn: this.getVisibleColumnFromPosition(end),
394-
bracketPair: pair,
395-
renderHorizontalEndLineAtTheBottom,
396-
};
397380
}
381+
continue;
398382
}
399383

400-
for (const line of activeGuides) {
401-
if (!line) {
402-
continue;
403-
}
404-
const isActive =
405-
activeBracketPairRange &&
406-
line.bracketPair.range.equalsRange(activeBracketPairRange);
407-
408-
const className =
409-
colorProvider.getInlineClassName(line.nestingLevel, line.nestingLevelOfEqualBracketType, independentColorPoolPerBracketType) +
410-
(options.highlightActive && isActive
411-
? ' ' + colorProvider.activeClassName
412-
: '');
384+
const endVisibleColumn = this.getVisibleColumnFromPosition(end);
385+
const startVisibleColumn = this.getVisibleColumnFromPosition(
386+
pair.openingBracketRange.getStartPosition()
387+
);
388+
const guideVisibleColumn = Math.min(startVisibleColumn, endVisibleColumn, pair.minVisibleColumnIndentation + 1);
413389

414-
if (
415-
(isActive &&
416-
options.horizontalGuides !==
417-
HorizontalGuidesState.Disabled) ||
418-
(options.includeInactive &&
419-
options.horizontalGuides === HorizontalGuidesState.Enabled)
420-
) {
421-
if (line.start.lineNumber === lineNumber) {
422-
if (line.guideVisibleColumn < line.visibleStartColumn) {
423-
guides.push(
424-
new IndentGuide(
425-
line.guideVisibleColumn,
426-
className,
427-
new IndentGuideHorizontalLine(false, line.start.column)
428-
)
429-
);
430-
}
431-
}
432-
if (line.end.lineNumber === lineNumber + 1) {
433-
// The next line might have horizontal guides.
434-
// However, the next line might also have a new bracket pair with the same indentation,
435-
// so the current bracket pair might get replaced. That's why we push the guide to nextGuides one line ahead.
436-
if (line.guideVisibleColumn < line.visibleEndColumn) {
437-
nextGuides.push(
438-
new IndentGuide(
439-
line.guideVisibleColumn,
440-
className,
441-
new IndentGuideHorizontalLine(
442-
!line.renderHorizontalEndLineAtTheBottom,
443-
line.end.column
444-
)
445-
)
446-
);
447-
}
448-
}
390+
let renderHorizontalEndLineAtTheBottom = false;
391+
if (pair.closingBracketRange) {
392+
const firstNonWsIndex = strings.firstNonWhitespaceIndex(
393+
this.textModel.getLineContent(
394+
pair.closingBracketRange.startLineNumber
395+
)
396+
);
397+
const hasTextBeforeClosingBracket = firstNonWsIndex < pair.closingBracketRange.startColumn - 1;
398+
if (hasTextBeforeClosingBracket) {
399+
renderHorizontalEndLineAtTheBottom = true;
449400
}
450401
}
451402

452-
let lastVisibleColumnCount = Number.MAX_SAFE_INTEGER;
453-
// Going backwards, so the last guide potentially replaces others
454-
for (let i = activeGuides.length - 1; i >= 0; i--) {
455-
const line = activeGuides[i];
456-
if (!line) {
457-
continue;
458-
}
459-
const isActive =
460-
options.highlightActive &&
461-
activeBracketPairRange &&
462-
line.bracketPair.range.equalsRange(activeBracketPairRange);
463-
464-
const className =
465-
colorProvider.getInlineClassName(
466-
line.nestingLevel,
467-
line.nestingLevelOfEqualBracketType,
468-
independentColorPoolPerBracketType
469-
) + (isActive ? ' ' + colorProvider.activeClassName : '');
470-
471-
if (isActive || options.includeInactive) {
472-
if (
473-
line.renderHorizontalEndLineAtTheBottom &&
474-
line.end.lineNumber === lineNumber + 1
475-
) {
476-
nextGuides.push(
477-
new IndentGuide(line.guideVisibleColumn, className, null)
478-
);
479-
}
480-
}
481403

482-
if (
483-
line.end.lineNumber <= lineNumber ||
484-
line.start.lineNumber >= lineNumber
485-
) {
486-
continue;
487-
}
404+
const visibleGuideStartLineNumber = Math.max(start.lineNumber, startLineNumber);
405+
const visibleGuideEndLineNumber = Math.min(end.lineNumber, endLineNumber);
488406

489-
if (line.guideVisibleColumn >= lastVisibleColumnCount && !isActive) {
490-
// Don't render a guide on top of an existing guide, unless it is active.
491-
continue;
407+
const offset = renderHorizontalEndLineAtTheBottom ? 1 : 0;
408+
409+
for (let l = visibleGuideStartLineNumber; l < visibleGuideEndLineNumber + offset; l++) {
410+
result[l - startLineNumber].push(
411+
new IndentGuide(
412+
guideVisibleColumn,
413+
-1,
414+
className,
415+
null,
416+
l === start.lineNumber ? start.column : -1,
417+
// TODO: Investigate if this is correct
418+
l === end.lineNumber ? end.column : -1
419+
)
420+
);
421+
}
422+
423+
if (horizontalGuides) {
424+
if (start.lineNumber >= startLineNumber && startVisibleColumn > guideVisibleColumn) {
425+
result[start.lineNumber - startLineNumber].push(
426+
new IndentGuide(
427+
guideVisibleColumn,
428+
-1,
429+
className,
430+
new IndentGuideHorizontalLine(false, start.column),
431+
-1,
432+
-1,
433+
)
434+
);
492435
}
493-
lastVisibleColumnCount = line.guideVisibleColumn;
494436

495-
if (isActive || options.includeInactive) {
496-
guides.push(
497-
new IndentGuide(line.guideVisibleColumn, className, null)
437+
if (end.lineNumber <= endLineNumber && endVisibleColumn > guideVisibleColumn) {
438+
result[end.lineNumber - startLineNumber].push(
439+
new IndentGuide(
440+
guideVisibleColumn,
441+
-1,
442+
className,
443+
new IndentGuideHorizontalLine(!renderHorizontalEndLineAtTheBottom, end.column),
444+
-1,
445+
-1,
446+
)
498447
);
499448
}
500449
}
450+
}
501451

452+
for (const guides of result) {
502453
guides.sort((a, b) => a.visibleColumn - b.visibleColumn);
503454
}
455+
504456
return result;
505457
}
506458

0 commit comments

Comments
 (0)