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

Make hidden fold ranges independent of range provider, add manual fol… #139779

Merged
merged 3 commits into from
Jul 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
112 changes: 68 additions & 44 deletions src/vs/editor/contrib/folding/browser/folding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,14 @@ import { ILanguageConfigurationService } from 'vs/editor/common/languages/langua
import { CollapseMemento, FoldingModel, getNextFoldLine, getParentFoldLine as getParentFoldLine, getPreviousFoldLine, setCollapseStateAtLevel, setCollapseStateForMatchingLines, setCollapseStateForRest, setCollapseStateForType, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateUp, toggleCollapseState } from 'vs/editor/contrib/folding/browser/foldingModel';
import { HiddenRangeModel } from 'vs/editor/contrib/folding/browser/hiddenRangeModel';
import { IndentRangeProvider } from 'vs/editor/contrib/folding/browser/indentRangeProvider';
import { ID_INIT_PROVIDER, InitializingRangeProvider } from 'vs/editor/contrib/folding/browser/intializingRangeProvider';
import * as nls from 'vs/nls';
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { editorSelectionBackground, iconForeground, registerColor, transparent } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { foldingCollapsedIcon, FoldingDecorationProvider, foldingExpandedIcon } from './foldingDecorations';
import { FoldingRegion, FoldingRegions } from './foldingRanges';
import { ID_SYNTAX_PROVIDER, SyntaxRangeProvider } from './syntaxRangeProvider';
import { foldingCollapsedIcon, FoldingDecorationProvider, foldingExpandedIcon, foldingManualIcon } from './foldingDecorations';
import { FoldingRegion, FoldingRegions, FoldRange } from './foldingRanges';
import { SyntaxRangeProvider } from './syntaxRangeProvider';
import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce';
Expand Down Expand Up @@ -84,8 +83,6 @@ export class FoldingController extends Disposable implements IEditorContribution
private rangeProvider: RangeProvider | null;
private foldingRegionPromise: CancelablePromise<FoldingRegions | null> | null;

private foldingStateMemento: FoldingStateMemento | null;

private foldingModelPromise: Promise<FoldingModel | null> | null;
private updateScheduler: Delayer<FoldingModel | null> | null;
private readonly updateDebounceInfo: IFeatureDebounceInformation;
Expand Down Expand Up @@ -120,7 +117,6 @@ export class FoldingController extends Disposable implements IEditorContribution
this.hiddenRangeModel = null;
this.rangeProvider = null;
this.foldingRegionPromise = null;
this.foldingStateMemento = null;
this.foldingModelPromise = null;
this.updateScheduler = null;
this.cursorChangedScheduler = null;
Expand Down Expand Up @@ -186,7 +182,7 @@ export class FoldingController extends Disposable implements IEditorContribution
return {};
}
if (this.foldingModel) { // disposed ?
const collapsedRegions = this.foldingModel.isInitialized ? this.foldingModel.getMemento() : this.hiddenRangeModel!.getMemento();
const collapsedRegions = this.foldingModel.getMemento();
const provider = this.rangeProvider ? this.rangeProvider.id : undefined;
return { collapsedRegions, lineCount: model.getLineCount(), provider, foldedImports: this._currentModelHasFoldedImports };
}
Expand All @@ -206,29 +202,12 @@ export class FoldingController extends Disposable implements IEditorContribution
}

this._currentModelHasFoldedImports = !!state.foldedImports;
if (!state.collapsedRegions) {
return;
}

if (state.provider === ID_SYNTAX_PROVIDER || state.provider === ID_INIT_PROVIDER) {
this.foldingStateMemento = state;
}

const collapsedRegions = state.collapsedRegions;
// set the hidden ranges right away, before waiting for the folding model.
if (this.hiddenRangeModel.applyMemento(collapsedRegions)) {
const foldingModel = this.getFoldingModel();
if (foldingModel) {
foldingModel.then(foldingModel => {
if (foldingModel) {
this._restoringViewState = true;
try {
foldingModel.applyMemento(collapsedRegions);
} finally {
this._restoringViewState = false;
}
}
}).then(undefined, onUnexpectedError);
if (state.collapsedRegions && state.collapsedRegions.length > 0 && this.foldingModel) {
this._restoringViewState = true;
try {
this.foldingModel.applyMemento(state.collapsedRegions!);
} finally {
this._restoringViewState = false;
}
}
}
Expand All @@ -243,7 +222,7 @@ export class FoldingController extends Disposable implements IEditorContribution
}

this._currentModelHasFoldedImports = false;
this.foldingModel = new FoldingModel(model, this.foldingDecorationProvider);
this.foldingModel = new FoldingModel(model, this.foldingDecorationProvider, this.triggerFoldingModelChanged.bind(this));
this.localToDispose.add(this.foldingModel);

this.hiddenRangeModel = new HiddenRangeModel(this.foldingModel);
Expand Down Expand Up @@ -274,7 +253,6 @@ export class FoldingController extends Disposable implements IEditorContribution
this.foldingModelPromise = null;
this.hiddenRangeModel = null;
this.cursorChangedScheduler = null;
this.foldingStateMemento = null;
if (this.rangeProvider) {
this.rangeProvider.dispose();
}
Expand All @@ -297,21 +275,12 @@ export class FoldingController extends Disposable implements IEditorContribution
return this.rangeProvider;
}
this.rangeProvider = new IndentRangeProvider(editorModel, this.languageConfigurationService, this._maxFoldingRegions); // fallback

if (this._useFoldingProviders && this.foldingModel) {
const foldingProviders = this.languageFeaturesService.foldingRangeProvider.ordered(this.foldingModel.textModel);
if (foldingProviders.length === 0 && this.foldingStateMemento && this.foldingStateMemento.collapsedRegions) {
const rangeProvider = this.rangeProvider = new InitializingRangeProvider(editorModel, this.foldingStateMemento.collapsedRegions, () => {
// if after 30 the InitializingRangeProvider is still not replaced, force a refresh
this.foldingStateMemento = null;
this.onFoldingStrategyChanged();
}, 30000);
return rangeProvider; // keep memento in case there are still no foldingProviders on the next request.
} else if (foldingProviders.length > 0) {
if (foldingProviders.length > 0) {
this.rangeProvider = new SyntaxRangeProvider(editorModel, foldingProviders, () => this.triggerFoldingModelChanged(), this._maxFoldingRegions);
}
}
this.foldingStateMemento = null;
return this.rangeProvider;
}

Expand Down Expand Up @@ -1097,6 +1066,59 @@ class GotoNextFoldAction extends FoldingAction<void> {
}
}

class FoldSelectedAction extends FoldingAction<void> {

constructor() {
super({
id: 'editor.foldSelected',
label: nls.localize('foldSelectedAction.label', "Fold Selected Lines"),
alias: 'Fold Selected Lines',
precondition: CONTEXT_FOLDING_ENABLED,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.Period),
weight: KeybindingWeight.EditorContrib
}
});
}

invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
const collapseRanges: FoldRange[] = [];
const selections = editor.getSelections();
if (selections) {
for (const selection of selections) {
let endLineNumber = selection.endLineNumber;
if (selection.endColumn === 1) {
--endLineNumber;
}
if (endLineNumber > selection.startLineNumber) {
collapseRanges.push(<FoldRange>{
startLineNumber: selection.startLineNumber,
endLineNumber: endLineNumber,
type: undefined,
isCollapsed: true,
isManualSelection: true
});
editor.setSelection({
startLineNumber: selection.startLineNumber,
startColumn: 1,
endLineNumber: selection.startLineNumber,
endColumn: 1
});
}
}
if (collapseRanges.length > 0) {
collapseRanges.sort((a, b) => {
return a.startLineNumber - b.startLineNumber;
});
const newRanges = FoldingRegions.sanitizeAndMerge(foldingModel.regions, collapseRanges, editor.getModel()?.getLineCount());
foldingModel.updatePost(FoldingRegions.fromFoldRanges(newRanges));
}
}
}
}


registerEditorContribution(FoldingController.ID, FoldingController);
registerEditorAction(UnfoldAction);
registerEditorAction(UnFoldRecursivelyAction);
Expand All @@ -1113,6 +1135,7 @@ registerEditorAction(ToggleFoldAction);
registerEditorAction(GotoParentFoldAction);
registerEditorAction(GotoPreviousFoldAction);
registerEditorAction(GotoNextFoldAction);
registerEditorAction(FoldSelectedAction);

for (let i = 1; i <= 7; i++) {
registerInstantiatedEditorAction(
Expand Down Expand Up @@ -1143,7 +1166,8 @@ registerThemingParticipant((theme, collector) => {
if (editorFoldColor) {
collector.addRule(`
.monaco-editor .cldr${ThemeIcon.asCSSSelector(foldingExpandedIcon)},
.monaco-editor .cldr${ThemeIcon.asCSSSelector(foldingCollapsedIcon)} {
.monaco-editor .cldr${ThemeIcon.asCSSSelector(foldingCollapsedIcon)},
.monaco-editor .cldr${ThemeIcon.asCSSSelector(foldingManualIcon)} {
color: ${editorFoldColor} !important;
}
`);
Expand Down
29 changes: 25 additions & 4 deletions src/vs/editor/contrib/folding/browser/foldingDecorations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { Codicon } from 'vs/base/common/codicons';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IModelDecorationsChangeAccessor, TrackedRangeStickiness } from 'vs/editor/common/model';
import { IModelDecorationOptions, IModelDecorationsChangeAccessor, TrackedRangeStickiness } from 'vs/editor/common/model';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { IDecorationProvider } from 'vs/editor/contrib/folding/browser/foldingModel';
import { localize } from 'vs/nls';
Expand All @@ -14,6 +14,7 @@ import { ThemeIcon } from 'vs/platform/theme/common/themeService';

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 foldingManualIcon = registerIcon('folding-manual', Codicon.ellipsis, localize('foldingManualIcon', 'Icon for manually collapsed ranges in the editor glyph margin.'));
export class FoldingDecorationProvider implements IDecorationProvider {

private static readonly COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({
Expand All @@ -33,6 +34,23 @@ export class FoldingDecorationProvider implements IDecorationProvider {
firstLineDecorationClassName: ThemeIcon.asClassName(foldingCollapsedIcon)
});

private static readonly MANUALLY_COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({
description: 'folding-manually-collapsed-visual-decoration',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
afterContentClassName: 'inline-folded',
isWholeLine: true,
firstLineDecorationClassName: ThemeIcon.asClassName(foldingManualIcon)
});

private static readonly MANUALLY_COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION = ModelDecorationOptions.register({
description: 'folding-manually-collapsed-highlighted-visual-decoration',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
afterContentClassName: 'inline-folded',
className: 'folded-background',
isWholeLine: true,
firstLineDecorationClassName: ThemeIcon.asClassName(foldingManualIcon)
});

private static readonly EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({
description: 'folding-expanded-auto-hide-visual-decoration',
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
Expand All @@ -59,12 +77,15 @@ export class FoldingDecorationProvider implements IDecorationProvider {
constructor(private readonly editor: ICodeEditor) {
}

getDecorationOption(isCollapsed: boolean, isHidden: boolean): ModelDecorationOptions {
if (isHidden || this.showFoldingControls === 'never') {
getDecorationOption(isCollapsed: boolean, isHidden: boolean, isManualSelection: boolean): IModelDecorationOptions {
if (isHidden // is inside another collapsed region
|| this.showFoldingControls === 'never' || (isManualSelection && !isCollapsed)) { //
return FoldingDecorationProvider.HIDDEN_RANGE_DECORATION;
}
if (isCollapsed) {
return this.showFoldingHighlights ? FoldingDecorationProvider.COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION : FoldingDecorationProvider.COLLAPSED_VISUAL_DECORATION;
return isManualSelection ?
(this.showFoldingHighlights ? FoldingDecorationProvider.MANUALLY_COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION : FoldingDecorationProvider.MANUALLY_COLLAPSED_VISUAL_DECORATION)
: (this.showFoldingHighlights ? FoldingDecorationProvider.COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION : FoldingDecorationProvider.COLLAPSED_VISUAL_DECORATION);
} else if (this.showFoldingControls === 'mouseover') {
return FoldingDecorationProvider.EXPANDED_AUTO_HIDE_VISUAL_DECORATION;
} else {
Expand Down
Loading