提交 6de9982a 编写于 作者: M Martin Aeschlimann

Fixes #3316 [folding] Opening a large file freezes VSCode due to folding decorations

上级 a14b405d
......@@ -17,17 +17,14 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
import {CommonEditorRegistry, ContextKey, EditorActionDescriptor} from 'vs/editor/common/editorCommonExtensions';
import {ICodeEditor, IEditorMouseEvent} from 'vs/editor/browser/editorBrowser';
import {EditorBrowserRegistry} from 'vs/editor/browser/editorBrowserExtensions';
import {IFoldingRange, toString as rangeToString} from 'vs/editor/contrib/folding/common/foldingRange';
import {computeRanges} from 'vs/editor/contrib/folding/common/indentFoldStrategy';
let log = function(msg: string) {
//console.log(msg);
};
import {IFoldingRange} from 'vs/editor/contrib/folding/common/foldingRange';
import {computeRanges, limitByIndent} from 'vs/editor/contrib/folding/common/indentFoldStrategy';
class CollapsibleRegion {
private decorationIds: string[];
private _isCollapsed: boolean;
private _indent: number;
private _lastRange: IFoldingRange;
......@@ -40,6 +37,10 @@ class CollapsibleRegion {
return this._isCollapsed;
}
public get indent(): number {
return this._indent;
}
public setCollapsed(isCollaped: boolean, changeAccessor:editorCommon.IModelDecorationsChangeAccessor): void {
this._isCollapsed = isCollaped;
if (this.decorationIds.length > 0) {
......@@ -78,6 +79,7 @@ class CollapsibleRegion {
public update(newRange:IFoldingRange, model:editorCommon.IModel, changeAccessor:editorCommon.IModelDecorationsChangeAccessor): void {
this._lastRange = newRange;
this._isCollapsed = !!newRange.isCollapsed;
this._indent = newRange.indent;
let newDecorations : editorCommon.IModelDeltaDecoration[] = [];
......@@ -176,7 +178,7 @@ export class FoldingController implements editorCommon.IEditorContribution {
if (d.isCollapsed) {
var range = d.getDecorationRange(model);
if (range) {
collapsedRegions.push({ startLineNumber: range.startLineNumber, endLineNumber: range.endLineNumber, isCollapsed: true});
collapsedRegions.push({ startLineNumber: range.startLineNumber, endLineNumber: range.endLineNumber, indent: d.indent, isCollapsed: true});
}
}
});
......@@ -202,9 +204,7 @@ export class FoldingController implements editorCommon.IEditorContribution {
if (!model) {
return;
}
regions = regions.sort((r1, r2) => r1.startLineNumber - r2.startLineNumber);
log('imput ranges ' + regions.map(rangeToString).join(', '));
regions = limitByIndent(regions, 10000).sort((r1, r2) => r1.startLineNumber - r2.startLineNumber);
this.editor.changeDecorations(changeAccessor => {
......@@ -215,19 +215,16 @@ export class FoldingController implements editorCommon.IEditorContribution {
let dec = this.decorations[i];
let decRange = dec.getDecorationRange(model);
if (!decRange) {
log('range no longer valid, was ' + dec.toString());
dec.dispose(changeAccessor);
i++;
} else {
while (k < regions.length && decRange.startLineNumber > regions[k].startLineNumber) {
log('new range ' + rangeToString(regions[k]));
newDecorations.push(new CollapsibleRegion(regions[k], model, changeAccessor));
k++;
}
if (k < regions.length) {
let currRange = regions[k];
if (decRange.startLineNumber < currRange.startLineNumber) {
log('range no longer valid, was ' + dec.toString());
dec.dispose(changeAccessor);
i++;
} else if (decRange.startLineNumber === currRange.startLineNumber) {
......@@ -241,12 +238,10 @@ export class FoldingController implements editorCommon.IEditorContribution {
}
}
while (i < this.decorations.length) {
log('range no longer valid, was ' + this.decorations[i].toString());
this.decorations[i].dispose(changeAccessor);
i++;
}
while (k < regions.length) {
log('new range ' + rangeToString(regions[k]));
newDecorations.push(new CollapsibleRegion(regions[k], model, changeAccessor));
k++;
}
......
......@@ -8,9 +8,10 @@
export interface IFoldingRange {
startLineNumber:number;
endLineNumber:number;
indent:number;
isCollapsed?:boolean;
}
export function toString(range: IFoldingRange): string {
return (range ? range.startLineNumber + '/' + range.endLineNumber : 'null') + range.isCollapsed ? ' (collapsed)' : '';
return (range ? range.startLineNumber + '/' + range.endLineNumber : 'null') + (range.isCollapsed ? ' (collapsed)' : '') + ' - ' + range.indent;
}
\ No newline at end of file
......@@ -33,7 +33,7 @@ export function computeRanges(model: IModel, tabSize: number, minimumRangeSize:
// new folding range
let endLineNumber = previous.line - 1;
if (endLineNumber - line >= minimumRangeSize) {
result.push({ startLineNumber: line, endLineNumber });
result.push({ startLineNumber: line, endLineNumber, indent: indent });
}
}
if (previous.indent === indent) {
......@@ -67,3 +67,31 @@ function computeIndentLevel(line: string, tabSize: number): number {
}
return indent;
}
/**
* Limits the number of folding ranges by removing ranges with larger indent levels
*/
export function limitByIndent(ranges: IFoldingRange[], maxEntries: number): IFoldingRange[] {
if (ranges.length <= maxEntries) {
return ranges;
}
let indentOccurrences = [];
ranges.forEach(r => {
if (r.indent < 1000) {
indentOccurrences[r.indent] = (indentOccurrences[r.indent] || 0) + 1;
}
});
let maxIndent = indentOccurrences.length;
for (let i = 0; i < indentOccurrences.length; i++) {
if (indentOccurrences[i]) {
maxEntries -= indentOccurrences[i];
if (maxEntries < 0) {
maxIndent = i;
break;
}
}
}
return ranges.filter(r => r.indent < maxIndent);
}
\ No newline at end of file
......@@ -7,10 +7,10 @@
import * as assert from 'assert';
import {Model} from 'vs/editor/common/model/model';
import {IFoldingRange} from 'vs/editor/contrib/folding/common/foldingRange';
import {computeRanges} from 'vs/editor/contrib/folding/common/indentFoldStrategy';
import {computeRanges, limitByIndent} from 'vs/editor/contrib/folding/common/indentFoldStrategy';
import {DefaultEndOfLine} from 'vs/editor/common/editorCommon';
suite('Folding', () => {
suite('Indentation Folding', () => {
function assertRanges(lines: string[], tabSize: number, expected:IFoldingRange[]): void {
let model = new Model(lines.join('\n'), DefaultEndOfLine.LF, null);
let actual = computeRanges(model, tabSize);
......@@ -19,40 +19,40 @@ suite('Folding', () => {
model.dispose();
}
function r(startLineNumber: number, endLineNumber: number): IFoldingRange {
return { startLineNumber, endLineNumber };
function r(startLineNumber: number, endLineNumber: number, indent: number): IFoldingRange {
return { startLineNumber, endLineNumber, indent };
}
test('t1', () => {
test('Fold one level', () => {
assertRanges([
'A',
' A',
' A',
' A'
], 4, [r(1, 4)]);
], 4, [r(1, 4, 0)]);
});
test('t2', () => {
test('Fold two levels', () => {
assertRanges([
'A',
' A',
' A',
' A',
' A'
], 4, [r(1, 5), r(3, 5)] );
], 4, [r(1, 5, 0), r(3, 5, 2)] );
});
test('t3', () => {
test('Fold three levels', () => {
assertRanges([
'A',
' A',
' A',
' A',
'A'
], 4, [r(1, 4), r(2, 4), r(3, 4)] );
], 4, [r(1, 4, 0), r(2, 4, 2), r(3, 4, 4)] );
});
test('t4', () => {
test('Fold decreasing indent', () => {
assertRanges([
' A',
' A',
......@@ -60,7 +60,7 @@ suite('Folding', () => {
], 4, [] );
});
test('Java', () => {
test('Fold Java', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ ' void foo() {',
......@@ -75,10 +75,10 @@ suite('Folding', () => {
/*11*/ 'interface B {',
/*12*/ ' void bar();',
/*13*/ '}',
], 4, [r(1, 9), r(2, 4), r(7, 8), r(11, 12)] );
], 4, [r(1, 9, 0), r(2, 4, 2), r(7, 8, 2), r(11, 12, 0)] );
});
test('Javadoc', () => {
test('Fold Javadoc', () => {
assertRanges([
/* 1*/ '/**',
/* 2*/ ' * Comment',
......@@ -87,9 +87,9 @@ suite('Folding', () => {
/* 5*/ ' void foo() {',
/* 6*/ ' }',
/* 7*/ '}',
], 4, [r(1, 3), r(4, 6)] );
], 4, [r(1, 3, 0), r(4, 6, 0)] );
});
test('Whitespace', () => {
test('Fold Whitespace', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ '',
......@@ -99,8 +99,20 @@ suite('Folding', () => {
/* 6*/ ' }',
/* 7*/ ' ',
/* 8*/ '}',
], 4, [r(1, 7), r(3, 5)] );
], 4, [r(1, 7, 0), r(3, 5, 2)] );
});
test('Limit By indent', () => {
let ranges = [r(1, 4, 0), r(3, 4, 2), r(5, 8, 0), r(6, 7, 1), r(9, 15, 0), r(10, 15, 10), r(11, 12, 2000), r(14, 15, 2000)];
assert.deepEqual(limitByIndent(ranges, 8), [r(1, 4, 0), r(3, 4, 2), r(5, 8, 0), r(6, 7, 1), r(9, 15, 0), r(10, 15, 10), r(11, 12, 2000), r(14, 15, 2000)]);
assert.deepEqual(limitByIndent(ranges, 7), [r(1, 4, 0), r(3, 4, 2), r(5, 8, 0), r(6, 7, 1), r(9, 15, 0), r(10, 15, 10)]);
assert.deepEqual(limitByIndent(ranges, 6), [r(1, 4, 0), r(3, 4, 2), r(5, 8, 0), r(6, 7, 1), r(9, 15, 0), r(10, 15, 10)]);
assert.deepEqual(limitByIndent(ranges, 5), [r(1, 4, 0), r(3, 4, 2), r(5, 8, 0), r(6, 7, 1), r(9, 15, 0)]);
assert.deepEqual(limitByIndent(ranges, 4), [r(1, 4, 0), r(5, 8, 0), r(6, 7, 1), r(9, 15, 0)]);
assert.deepEqual(limitByIndent(ranges, 3), [r(1, 4, 0), r(5, 8, 0), r(9, 15, 0)]);
assert.deepEqual(limitByIndent(ranges, 2), []);
assert.deepEqual(limitByIndent(ranges, 1), []);
assert.deepEqual(limitByIndent(ranges, 0), []);
});
});
\ No newline at end of file
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册