提交 fea449cc 编写于 作者: M Martin Aeschlimann

[folding] collapsed regions are not always preserved. Fixes #51230

上级 08d06343
......@@ -31,12 +31,14 @@ import { IPosition } from 'vs/editor/common/core/position';
import { FoldingRangeProviderRegistry, FoldingRangeKind } from 'vs/editor/common/modes';
import { SyntaxRangeProvider } from './syntaxRangeProvider';
import { CancellationToken } from 'vs/base/common/cancellation';
import { InitializingRangeProvider } from 'vs/editor/contrib/folding/intializingRangeProvider';
export const ID = 'editor.contrib.folding';
export interface RangeProvider {
compute(editorModel: ITextModel, cancelationToken: CancellationToken): Thenable<FoldingRegions>;
readonly id: string;
compute(cancelationToken: CancellationToken): Thenable<FoldingRegions>;
dispose(): void;
}
interface FoldingStateMemento {
......@@ -153,13 +155,15 @@ export class FoldingController implements IEditorContribution {
return;
}
if (state.provider !== 'indent') {
this.foldingStateMemento = state;
}
// set the hidden ranges right away, before waiting for the folding model.
if (this.hiddenRangeModel.applyMemento(state.collapsedRegions)) {
this.getFoldingModel().then(foldingModel => {
if (foldingModel && this.rangeProvider && this.rangeProvider.id === state.provider) {
if (foldingModel) {
foldingModel.applyMemento(state.collapsedRegions);
} else if (state.provider) {
this.foldingStateMemento = state;
}
});
}
......@@ -202,39 +206,44 @@ export class FoldingController implements IEditorContribution {
this.foldingModelPromise = null;
this.hiddenRangeModel = null;
this.cursorChangedScheduler = null;
this.rangeProvider = null;
this.foldingStateMemento = null;
if (this.rangeProvider) {
this.rangeProvider.dispose();
}
this.rangeProvider = null;
}
});
this.onModelContentChanged();
}
private onFoldingStrategyChanged() {
if (this.rangeProvider) {
this.rangeProvider.dispose();
}
this.rangeProvider = null;
this.onModelContentChanged();
if (this.foldingStateMemento) {
this.getFoldingModel().then(model => {
if (this.foldingStateMemento) {
let provider = this.getRangeProvider();
if (model && provider && provider.id === this.foldingStateMemento.provider && model.textModel.getLineCount() === this.foldingStateMemento.lineCount) {
model.applyMemento(this.foldingStateMemento.collapsedRegions);
this.foldingStateMemento = null;
}
}
});
}
}
private getRangeProvider(): RangeProvider {
if (!this.rangeProvider) {
if (this._useFoldingProviders) {
let foldingProviders = FoldingRangeProviderRegistry.ordered(this.foldingModel.textModel);
this.rangeProvider = foldingProviders.length ? new SyntaxRangeProvider(foldingProviders) : new IndentRangeProvider();
} else {
this.rangeProvider = new IndentRangeProvider();
private getRangeProvider(editorModel: ITextModel): RangeProvider {
if (this.rangeProvider) {
return this.rangeProvider;
}
this.rangeProvider = new IndentRangeProvider(editorModel); // fallback
if (this._useFoldingProviders) {
let foldingProviders = FoldingRangeProviderRegistry.ordered(this.foldingModel.textModel);
if (foldingProviders.length === 0 && this.foldingStateMemento) {
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 this.rangeProvider; // keep memento in case there are still no foldingProviders on the next request.
} else if (foldingProviders.length > 0) {
this.rangeProvider = new SyntaxRangeProvider(editorModel, foldingProviders);
}
}
this.foldingStateMemento = null;
return this.rangeProvider;
}
......@@ -252,7 +261,7 @@ export class FoldingController implements IEditorContribution {
if (!this.foldingModel) { // null if editor has been disposed, or folding turned off
return null;
}
let foldingRegionPromise = this.foldingRegionPromise = asWinJsPromise<FoldingRegions>(token => this.getRangeProvider().compute(this.foldingModel.textModel, token));
let foldingRegionPromise = this.foldingRegionPromise = asWinJsPromise<FoldingRegions>(token => this.getRangeProvider(this.foldingModel.textModel).compute(token));
return foldingRegionPromise.then(foldingRanges => {
if (foldingRanges && foldingRegionPromise === this.foldingRegionPromise) { // new request or cancelled in the meantime?
// some cursors might have moved into hidden regions, make sure they are in expanded regions
......
......@@ -19,11 +19,19 @@ const MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT = 5000;
export class IndentRangeProvider implements RangeProvider {
readonly id = 'indent';
compute(editorModel: ITextModel, cancelationToken: CancellationToken): Thenable<FoldingRegions> {
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editorModel.getLanguageIdentifier().id);
readonly decorations;
constructor(private editorModel: ITextModel) {
}
dispose() {
}
compute(cancelationToken: CancellationToken): Thenable<FoldingRegions> {
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(this.editorModel.getLanguageIdentifier().id);
let offSide = foldingRules && foldingRules.offSide;
let markers = foldingRules && foldingRules.markers;
return TPromise.as(computeRanges(editorModel, offSide, markers));
return TPromise.as(computeRanges(this.editorModel, offSide, markers));
}
}
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
import { FoldingRegions, ILineRange } from 'vs/editor/contrib/folding/foldingRanges';
import { RangeProvider } from './folding';
import { TPromise } from 'vs/base/common/winjs.base';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IFoldingRangeData, sanitizeRanges } from 'vs/editor/contrib/folding/syntaxRangeProvider';
export class InitializingRangeProvider implements RangeProvider {
readonly id = 'init';
private decorationIds: string[] | undefined;
private timeout: number;
constructor(private editorModel: ITextModel, initialRanges: ILineRange[], onTimeout: () => void, timeoutTime: number) {
if (initialRanges.length) {
let toDecorationRange = (range: ILineRange): IModelDeltaDecoration => {
return {
range: {
startLineNumber: range.startLineNumber,
startColumn: 0,
endLineNumber: range.endLineNumber,
endColumn: editorModel.getLineLength(range.endLineNumber)
},
options: {
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
}
};
};
this.decorationIds = editorModel.deltaDecorations([], initialRanges.map(toDecorationRange));
this.timeout = setTimeout(onTimeout, timeoutTime);
}
}
dispose(): void {
if (this.decorationIds) {
this.editorModel.deltaDecorations(this.decorationIds, []);
this.decorationIds = void 0;
}
if (typeof this.timeout === 'number') {
clearTimeout(this.timeout);
this.timeout = void 0;
}
}
compute(cancelationToken: CancellationToken): Thenable<FoldingRegions> {
let foldingRangeData: IFoldingRangeData[] = [];
if (this.decorationIds) {
for (let id of this.decorationIds) {
let range = this.editorModel.getDecorationRange(id);
if (range) {
foldingRangeData.push({ start: range.startLineNumber, end: range.endLineNumber, rank: 1 });
}
}
}
return TPromise.as(sanitizeRanges(foldingRangeData, Number.MAX_VALUE));
}
}
......@@ -25,12 +25,13 @@ const foldingContext: FoldingContext = {
export class SyntaxRangeProvider implements RangeProvider {
constructor(private providers: FoldingRangeProvider[], private limit = MAX_FOLDING_REGIONS) {
}
readonly id = 'syntax';
compute(model: ITextModel, cancellationToken: CancellationToken): Thenable<FoldingRegions> {
return collectSyntaxRanges(this.providers, model, cancellationToken).then(ranges => {
constructor(private editorModel: ITextModel, private providers: FoldingRangeProvider[], private limit = MAX_FOLDING_REGIONS) {
}
compute(cancellationToken: CancellationToken): Thenable<FoldingRegions> {
return collectSyntaxRanges(this.providers, this.editorModel, cancellationToken).then(ranges => {
if (ranges) {
let res = sanitizeRanges(ranges, this.limit);
return res;
......@@ -39,6 +40,9 @@ export class SyntaxRangeProvider implements RangeProvider {
});
}
dispose() {
}
}
function collectSyntaxRanges(providers: FoldingRangeProvider[], model: ITextModel, cancellationToken: CancellationToken): Thenable<IFoldingRangeData[] | null> {
......@@ -165,14 +169,16 @@ export function sanitizeRanges(rangeData: IFoldingRangeData[], limit: number): F
previous.push(top);
top = entry;
collector.add(entry.start, entry.end, entry.kind && entry.kind.value, previous.length);
} else if (entry.start > top.end) {
do {
top = previous.pop();
} while (top && entry.start > top.end);
if (top) {
previous.push(top);
} else {
if (entry.start > top.end) {
do {
top = previous.pop();
} while (top && entry.start > top.end);
if (top) {
previous.push(top);
}
top = entry;
}
top = entry;
collector.add(entry.start, entry.end, entry.kind && entry.kind.value, previous.length);
}
}
......
......@@ -76,7 +76,7 @@ suite('Syntax folding', () => {
let providers = [new TestFoldingRangeProvider(model, ranges)];
async function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) {
let indentRanges = await new SyntaxRangeProvider(providers, maxEntries).compute(model, CancellationToken.None);
let indentRanges = await new SyntaxRangeProvider(model, providers, maxEntries).compute(CancellationToken.None);
let actual = [];
for (let i = 0; i < indentRanges.length; i++) {
actual.push({ start: indentRanges.getStartLineNumber(i), end: indentRanges.getEndLineNumber(i) });
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册