Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Collapsed Text #170447

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0086d4b
support showing collapsed text at the end of the first line of the fo…
mtbaqer Nov 23, 2022
e5aa75d
Merge branch 'microsoft:main' into collapsed-text
mtbaqer Nov 23, 2022
9357128
support startColumn in FoldingRange
mtbaqer Dec 19, 2022
44a9949
Merge branch 'main' into collapsed-text
mtbaqer Dec 19, 2022
461a54d
clean up
mtbaqer Dec 19, 2022
7b15ee6
pass missing foldingOffset arguments in couple places
mtbaqer Dec 19, 2022
098c709
unfold hidden ranges if selection is within the column range
mtbaqer Dec 20, 2022
783c1ed
support clicking collapsedText to unfold ranges with a set startColumn
mtbaqer Dec 20, 2022
373aaa2
hide decorations after the folding offset.
mtbaqer Dec 21, 2022
3a3fe9d
fix startColumn comparison in FoldingRegions.sanitizeAndMerge
mtbaqer Dec 21, 2022
78c3e7f
update foldingModel tests to include startColumn cases
mtbaqer Dec 21, 2022
d591451
update sanitizeAndMerge validation to include cases where ranges star…
mtbaqer Dec 21, 2022
c2dbf98
fix sanitizeAndMerge not throwing out ranges starting at line 0 as a …
mtbaqer Dec 21, 2022
a5706ed
update foldingRanges tests to include startColumn cases
mtbaqer Dec 21, 2022
001846b
test foldingDecorations
mtbaqer Dec 22, 2022
e353b18
support having an empty collapsedText
mtbaqer Dec 23, 2022
8bec5d8
update modelLineProjection tests to include inline fold cases
mtbaqer Dec 23, 2022
e98387c
move collapsedText and startColumn properties to vscode.proposed.coll…
mtbaqer Jan 2, 2023
b9b4ece
Merge branch 'main' into collapsed-text
mtbaqer Jan 2, 2023
b642001
make sanitizeAndMerge consider ranges duplicate if one has startColum…
mtbaqer Jan 2, 2023
84ba9e5
clean up
mtbaqer Jan 2, 2023
6e15aa2
Merge branch 'main' into collapsed-text
mtbaqer Jan 3, 2023
5ed4abc
enforce enabledApiProposals checking for collapsedText
mtbaqer Jan 5, 2023
4159152
Merge branch 'main' into collapsed-text
mtbaqer Jan 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
test foldingDecorations
  • Loading branch information
mtbaqer committed Dec 22, 2022
commit 001846b806d74791767a3ee13e4c9896b77e9b79
4 changes: 2 additions & 2 deletions src/vs/editor/contrib/folding/browser/foldingDecorations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ import { editorSelectionBackground, iconForeground, registerColor, transparent }
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
import { themeColorFromId, ThemeIcon } from 'vs/platform/theme/common/themeService';

const foldBackground = registerColor('editor.foldBackground', { light: transparent(editorSelectionBackground, 0.3), dark: transparent(editorSelectionBackground, 0.3), hcDark: null, hcLight: null }, localize('foldBackgroundBackground', "Background color behind folded ranges. The color must not be opaque so as not to hide underlying decorations."), true);
export const foldBackground = registerColor('editor.foldBackground', { light: transparent(editorSelectionBackground, 0.3), dark: transparent(editorSelectionBackground, 0.3), hcDark: null, hcLight: null }, localize('foldBackgroundBackground', "Background color behind folded ranges. The color must not be opaque so as not to hide underlying decorations."), true);
registerColor('editorGutter.foldingControlForeground', { dark: iconForeground, light: iconForeground, hcDark: iconForeground, hcLight: iconForeground }, localize('editorGutter.foldingControlForeground', 'Color of the folding control in the editor gutter.'));

export const foldingExpandedIcon = registerIcon('folding-expanded', Codicon.chevronDown, localize('foldingExpandedIcon', 'Icon for expanded ranges in the editor glyph margin.'));
export const foldingCollapsedIcon = registerIcon('folding-collapsed', Codicon.chevronRight, localize('foldingCollapsedIcon', 'Icon for collapsed ranges in the editor glyph margin.'));
export const foldingManualCollapsedIcon = registerIcon('folding-manual-collapsed', foldingCollapsedIcon, localize('foldingManualCollapedIcon', 'Icon for manually collapsed ranges in the editor glyph margin.'));
export const foldingManualExpandedIcon = registerIcon('folding-manual-expanded', foldingExpandedIcon, localize('foldingManualExpandedIcon', 'Icon for manually expanded ranges in the editor glyph margin.'));

const foldedBackgroundMinimap = { color: themeColorFromId(foldBackground), position: MinimapPosition.Inline };
export const foldedBackgroundMinimap = { color: themeColorFromId(foldBackground), position: MinimapPosition.Inline };

export class FoldingDecorationProvider implements IDecorationProvider {

Expand Down
253 changes: 253 additions & 0 deletions src/vs/editor/contrib/folding/test/browser/foldingDecorations.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IModelDecorationOptions, InjectedTextCursorStops, InjectedTextOptions, TrackedRangeStickiness } from 'vs/editor/common/model';
import { ModelDecorationOptions, TextModel } from 'vs/editor/common/model/textModel';
import { foldedBackgroundMinimap, foldingCollapsedIcon, FoldingDecorationProvider, foldingExpandedIcon, foldingManualCollapsedIcon, foldingManualExpandedIcon } from 'vs/editor/contrib/folding/browser/foldingDecorations';
import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { createTextModel } from 'vs/editor/test/common/testTextModel';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';

suite('Folding Decoration Provider', () => {
const ELLIPSES = '\u22EF'; /* ellipses unicode character */

const INJECTED_COLLAPSED_TEXT_OPTIONS: InjectedTextOptions = {
content: ELLIPSES,
inlineClassName: 'collapsed-text',
inlineClassNameAffectsLetterSpacing: true,
cursorStops: InjectedTextCursorStops.None
};

const COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION: IModelDecorationOptions = {
description: 'folding-collapsed-highlighted-visual-decoration',
stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges,
className: 'folded-background',
minimap: foldedBackgroundMinimap,
isWholeLine: true,
firstLineDecorationClassName: ThemeIcon.asClassName(foldingCollapsedIcon),
hideContent: true
};

const MANUALLY_COLLAPSED_VISUAL_DECORATION: IModelDecorationOptions = {
description: 'folding-manually-collapsed-visual-decoration',
stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges,
isWholeLine: true,
firstLineDecorationClassName: ThemeIcon.asClassName(foldingManualCollapsedIcon),
hideContent: true
};

const MANUALLY_COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION: IModelDecorationOptions = {
description: 'folding-manually-collapsed-highlighted-visual-decoration',
stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges,
className: 'folded-background',
minimap: foldedBackgroundMinimap,
isWholeLine: true,
firstLineDecorationClassName: ThemeIcon.asClassName(foldingManualCollapsedIcon),
hideContent: true
};

const NO_CONTROLS_COLLAPSED_HIGHLIGHTED_RANGE_DECORATION: IModelDecorationOptions = {
description: 'folding-no-controls-range-decoration',
stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges,
className: 'folded-background',
minimap: foldedBackgroundMinimap,
isWholeLine: true,
hideContent: true
};

const NO_CONTROLS_COLLAPSED_RANGE_DECORATION: IModelDecorationOptions = {
description: 'folding-no-controls-range-decoration',
stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges,
isWholeLine: true,
hideContent: true
};

const COLLAPSED_VISUAL_DECORATION: IModelDecorationOptions = {
description: 'folding-collapsed-visual-decoration',
stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges,
isWholeLine: true,
firstLineDecorationClassName: ThemeIcon.asClassName(foldingCollapsedIcon),
hideContent: true,
};

const MANUALLY_EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({
description: 'folding-manually-expanded-visual-decoration',
stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges,
isWholeLine: true,
firstLineDecorationClassName: 'alwaysShowFoldIcons ' + ThemeIcon.asClassName(foldingManualExpandedIcon),
before: { content: '' }
});

const MANUALLY_EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({
description: 'folding-manually-expanded-auto-hide-visual-decoration',
stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges,
isWholeLine: true,
firstLineDecorationClassName: ThemeIcon.asClassName(foldingManualExpandedIcon),
before: { content: '' }
});

const EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({
description: 'folding-expanded-visual-decoration',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
isWholeLine: true,
firstLineDecorationClassName: 'alwaysShowFoldIcons ' + ThemeIcon.asClassName(foldingExpandedIcon),
before: { content: '' }
});

const EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({
description: 'folding-expanded-auto-hide-visual-decoration',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
isWholeLine: true,
firstLineDecorationClassName: ThemeIcon.asClassName(foldingExpandedIcon),
before: { content: '' }
});

const NO_CONTROLS_EXPANDED_RANGE_DECORATION = ModelDecorationOptions.register({
description: 'folding-no-controls-range-decoration',
stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges,
isWholeLine: true,
before: { content: '' }
});

const HIDDEN_RANGE_DECORATION = ModelDecorationOptions.register({
description: 'folding-hidden-range-decoration',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
before: { content: '' }
});

let model: TextModel;
let editor: ICodeEditor;

setup(() => {
model = createTextModel('');
editor = createTestCodeEditor(model);
});

teardown(() => {
model.dispose();
editor.dispose();
});

test('getDecorationOption, hidden', () => {
const provider = new FoldingDecorationProvider(editor);
const options = provider.getDecorationOption(false, true, false);
assert.deepStrictEqual(options, HIDDEN_RANGE_DECORATION);
});

test('getDecorationOption, no controls', () => {
const provider = new FoldingDecorationProvider(editor);
provider.showFoldingControls = 'never';
const options = provider.getDecorationOption(false, false, false);
assert.deepStrictEqual(options, NO_CONTROLS_EXPANDED_RANGE_DECORATION);
});

test('getDecorationOption, auto hide controls', () => {
const provider = new FoldingDecorationProvider(editor);
const options = provider.getDecorationOption(false, false, false);
assert.deepStrictEqual(options, EXPANDED_AUTO_HIDE_VISUAL_DECORATION);
});

test('getDecorationOption, always show controls', () => {
const provider = new FoldingDecorationProvider(editor);
provider.showFoldingControls = 'always';
const options = provider.getDecorationOption(false, false, false);
assert.deepStrictEqual(options, EXPANDED_VISUAL_DECORATION);
});

test('getDecorationOption, auto hide controls, manually expanded', () => {
const provider = new FoldingDecorationProvider(editor);
const options = provider.getDecorationOption(false, false, true);
assert.deepStrictEqual(options, MANUALLY_EXPANDED_AUTO_HIDE_VISUAL_DECORATION);
});

test('getDecorationOption, always show controls, manually expanded', () => {
const provider = new FoldingDecorationProvider(editor);
provider.showFoldingControls = 'always';
const options = provider.getDecorationOption(false, false, true);
assert.deepStrictEqual(options, MANUALLY_EXPANDED_VISUAL_DECORATION);
});

test('getDecorationOption, no controls, collapsed', () => {
const provider = new FoldingDecorationProvider(editor);
provider.showFoldingHighlights = false;
provider.showFoldingControls = 'never';
const options = provider.getDecorationOption(true, false, false);
assert.deepStrictEqual(options, applyCollapsedText(NO_CONTROLS_COLLAPSED_RANGE_DECORATION, ELLIPSES));
});

test('getDecorationOption, auto hide controls, collapsed', () => {
const provider = new FoldingDecorationProvider(editor);
provider.showFoldingHighlights = false;
const options = provider.getDecorationOption(true, false, false);
assert.deepStrictEqual(options, applyCollapsedText(COLLAPSED_VISUAL_DECORATION, ELLIPSES));
});

test('getDecorationOption, always show controls, collapsed', () => {
const provider = new FoldingDecorationProvider(editor);
provider.showFoldingHighlights = false;
provider.showFoldingControls = 'always';
const options = provider.getDecorationOption(true, false, false);
assert.deepStrictEqual(options, applyCollapsedText(COLLAPSED_VISUAL_DECORATION, ELLIPSES));
});

test('getDecorationOption, no controls, collapsed, highlighted', () => {
const provider = new FoldingDecorationProvider(editor);
provider.showFoldingControls = 'never';
const options = provider.getDecorationOption(true, false, false);
assert.deepStrictEqual(options, applyCollapsedText(NO_CONTROLS_COLLAPSED_HIGHLIGHTED_RANGE_DECORATION, ELLIPSES));
});

test('getDecorationOption, auto hide controls, collapsed, highlighted', () => {
const provider = new FoldingDecorationProvider(editor);
const options = provider.getDecorationOption(true, false, false);
assert.deepStrictEqual(options, applyCollapsedText(COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION, ELLIPSES));
});

test('getDecorationOption, always show controls, collapsed, highlighted', () => {
const provider = new FoldingDecorationProvider(editor);
provider.showFoldingControls = 'always';
const options = provider.getDecorationOption(true, false, false);
assert.deepStrictEqual(options, applyCollapsedText(COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION, ELLIPSES));
});

test('getDecorationOption, manually collapsed', () => {
const provider = new FoldingDecorationProvider(editor);
provider.showFoldingHighlights = false;
const options = provider.getDecorationOption(true, false, true);
assert.deepStrictEqual(options, applyCollapsedText(MANUALLY_COLLAPSED_VISUAL_DECORATION, ELLIPSES));
});

test('getDecorationOption, manually collapsed, highlighted', () => {
const provider = new FoldingDecorationProvider(editor);
const options = provider.getDecorationOption(true, false, true);
assert.deepStrictEqual(options, applyCollapsedText(MANUALLY_COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION, ELLIPSES));
});

test('getDecorationOption, collapsed with custom text', () => {
const provider = new FoldingDecorationProvider(editor);
provider.showFoldingHighlights = false;
const options = provider.getDecorationOption(true, false, false, 'custom text');
assert.deepStrictEqual(options, applyCollapsedText(COLLAPSED_VISUAL_DECORATION, 'custom text'));
});

test('getDecorationOption, collapsed with custom text, highlighted', () => {
const provider = new FoldingDecorationProvider(editor);
const options = provider.getDecorationOption(true, false, false, 'custom text');
assert.deepStrictEqual(options, applyCollapsedText(COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION, 'custom text'));
});

function applyCollapsedText(decorationOptions: IModelDecorationOptions, collapsedText: string) {
const before: InjectedTextOptions = { ...INJECTED_COLLAPSED_TEXT_OPTIONS, content: replaceVisibleWhiteSpace(collapsedText) };
return ModelDecorationOptions.register({ ...decorationOptions, before });
}

function replaceVisibleWhiteSpace(str: string) {
const noBreakWhitespace = '\xa0';
return str.replace(/[ \t]/g, noBreakWhitespace);
}
});