mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 04:49:35 +00:00
[folding] fold regions, initial, preconfigured support. For #12146
This commit is contained in:
parent
a678f60100
commit
9e05d4b635
|
@ -33,5 +33,11 @@
|
|||
"scopeName": "source.c.platform",
|
||||
"path": "./syntaxes/Platform.tmLanguage"
|
||||
}]
|
||||
},
|
||||
"folding": {
|
||||
"markers": {
|
||||
"start": "^\\s*#pragma\\s+region",
|
||||
"end": "^\\s*#pragma\\s+endregion"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,5 +23,11 @@
|
|||
["<", ">"],
|
||||
["'", "'"],
|
||||
["\"", "\""]
|
||||
]
|
||||
],
|
||||
"folding": {
|
||||
"markers": {
|
||||
"start": "^\\s*#region",
|
||||
"end": "^\\s*#endregion"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,5 +24,11 @@
|
|||
["'", "'"],
|
||||
["\"", "\""],
|
||||
["`", "`"]
|
||||
]
|
||||
],
|
||||
"folding": {
|
||||
"markers": {
|
||||
"start": "^\\s*//\\s*#region",
|
||||
"end": "^\\s*//\\s*#endregion"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,5 +22,11 @@
|
|||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
]
|
||||
],
|
||||
"folding": {
|
||||
"markers": {
|
||||
"start": "^\\s*#region",
|
||||
"end": "^\\s*#endregion"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,5 +24,11 @@
|
|||
["'", "'"],
|
||||
["\"", "\""],
|
||||
["`", "`"]
|
||||
]
|
||||
],
|
||||
"folding": {
|
||||
"markers": {
|
||||
"start": "^\\s*//\\s*#region",
|
||||
"end": "^\\s*//\\s*#endregion"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,5 +20,11 @@
|
|||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["<", ">"]
|
||||
]
|
||||
],
|
||||
"folding": {
|
||||
"markers": {
|
||||
"start": "^\\s*#Region",
|
||||
"end": "^\\s*#End Region"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,11 +12,13 @@ export class IndentRange {
|
|||
startLineNumber: number;
|
||||
endLineNumber: number;
|
||||
indent: number;
|
||||
marker: boolean;
|
||||
|
||||
constructor(startLineNumber: number, endLineNumber: number, indent: number) {
|
||||
constructor(startLineNumber: number, endLineNumber: number, indent: number, marker?: boolean) {
|
||||
this.startLineNumber = startLineNumber;
|
||||
this.endLineNumber = endLineNumber;
|
||||
this.indent = indent;
|
||||
this.marker = marker;
|
||||
}
|
||||
|
||||
public static deepCloneArr(indentRanges: IndentRange[]): IndentRange[] {
|
||||
|
@ -29,12 +31,27 @@ export class IndentRange {
|
|||
}
|
||||
}
|
||||
|
||||
export function computeRanges(model: ITextModel, offSide: boolean, minimumRangeSize: number = 1): IndentRange[] {
|
||||
export interface FoldMarkers {
|
||||
start: string;
|
||||
end: string;
|
||||
indent?: number;
|
||||
}
|
||||
|
||||
interface PreviousRegion { indent: number; line: number; marker: RegExp; };
|
||||
|
||||
export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldMarkers, minimumRangeSize: number = 1): IndentRange[] {
|
||||
|
||||
let result: IndentRange[] = [];
|
||||
|
||||
let previousRegions: { indent: number, line: number }[] = [];
|
||||
previousRegions.push({ indent: -1, line: model.getLineCount() + 1 }); // sentinel, to make sure there's at least one entry
|
||||
let pattern = void 0;
|
||||
let patternIndent = -1;
|
||||
if (markers) {
|
||||
pattern = new RegExp(`(${markers.start})|(?:${markers.end})`);
|
||||
patternIndent = typeof markers.indent === 'number' ? markers.indent : -1;
|
||||
}
|
||||
|
||||
let previousRegions: PreviousRegion[] = [];
|
||||
previousRegions.push({ indent: -1, line: model.getLineCount() + 1, marker: null }); // sentinel, to make sure there's at least one entry
|
||||
|
||||
for (let line = model.getLineCount(); line > 0; line--) {
|
||||
let indent = model.getIndentLevel(line);
|
||||
|
@ -46,8 +63,29 @@ export function computeRanges(model: ITextModel, offSide: boolean, minimumRangeS
|
|||
}
|
||||
continue; // only whitespace
|
||||
}
|
||||
let m;
|
||||
if (pattern && (patternIndent === -1 || patternIndent === indent) && (m = model.getLineContent(line).match(pattern))) {
|
||||
// folding pattern match
|
||||
if (m[1]) { // start pattern match
|
||||
if (previous.indent >= 0 && !previous.marker) {
|
||||
|
||||
|
||||
// discard all regions until the folding pattern
|
||||
do {
|
||||
previousRegions.pop();
|
||||
previous = previousRegions[previousRegions.length - 1];
|
||||
} while (previous.indent >= 0 && !previous.marker);
|
||||
}
|
||||
if (previous.marker) {
|
||||
// new folding range from pattern, includes the end line
|
||||
result.push(new IndentRange(line, previous.line, indent, true));
|
||||
previous.marker = null;
|
||||
previous.indent = indent;
|
||||
previous.line = line;
|
||||
}
|
||||
} else { // end pattern match
|
||||
previousRegions.push({ indent: -2, line, marker: pattern });
|
||||
}
|
||||
} else {
|
||||
if (previous.indent > indent) {
|
||||
// discard all regions with larger indent
|
||||
do {
|
||||
|
@ -65,7 +103,8 @@ export function computeRanges(model: ITextModel, offSide: boolean, minimumRangeS
|
|||
previous.line = line;
|
||||
} else { // previous.indent < indent
|
||||
// new region with a bigger indent
|
||||
previousRegions.push({ indent, line });
|
||||
previousRegions.push({ indent, line, marker: null });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -845,6 +845,8 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
|
|||
if (!this._indentRanges) {
|
||||
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(this._languageIdentifier.id);
|
||||
let offSide = foldingRules && foldingRules.offSide;
|
||||
let markers = foldingRules && foldingRules['markers'];
|
||||
this._indentRanges = computeRanges(this, offSide, markers);
|
||||
}
|
||||
return this._indentRanges;
|
||||
}
|
||||
|
|
|
@ -7,137 +7,266 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
import { Model } from 'vs/editor/common/model/model';
|
||||
import { computeRanges } from 'vs/editor/common/model/indentRanges';
|
||||
import { computeRanges, FoldMarkers } from 'vs/editor/common/model/indentRanges';
|
||||
|
||||
export interface IndentRange {
|
||||
startLineNumber: number;
|
||||
endLineNumber: number;
|
||||
indent: number;
|
||||
marker: boolean;
|
||||
}
|
||||
|
||||
suite('Indentation Folding', () => {
|
||||
function assertRanges(lines: string[], expected: IndentRange[], offside): void {
|
||||
function assertRanges(lines: string[], expected: IndentRange[], offside: boolean, markers?: FoldMarkers): void {
|
||||
let model = Model.createFromString(lines.join('\n'));
|
||||
let actual = computeRanges(model, offside);
|
||||
let actual = computeRanges(model, offside, markers);
|
||||
actual.sort((r1, r2) => r1.startLineNumber - r2.startLineNumber);
|
||||
assert.deepEqual(actual, expected);
|
||||
model.dispose();
|
||||
}
|
||||
|
||||
function r(startLineNumber: number, endLineNumber: number, indent: number): IndentRange {
|
||||
return { startLineNumber, endLineNumber, indent };
|
||||
function r(startLineNumber: number, endLineNumber: number, indent: number, marker?: boolean): IndentRange {
|
||||
return { startLineNumber, endLineNumber, indent, marker };
|
||||
}
|
||||
|
||||
test('Fold one level', () => {
|
||||
let range = [
|
||||
'A',
|
||||
' A',
|
||||
' A',
|
||||
' A'
|
||||
];
|
||||
assertRanges(range, [r(1, 4, 0)], true);
|
||||
assertRanges(range, [r(1, 4, 0)], false);
|
||||
});
|
||||
// suite('Indentation Folding', () => {
|
||||
|
||||
test('Fold two levels', () => {
|
||||
let range = [
|
||||
'A',
|
||||
' A',
|
||||
' A',
|
||||
' A',
|
||||
' A'
|
||||
];
|
||||
assertRanges(range, [r(1, 5, 0), r(3, 5, 2)], true);
|
||||
assertRanges(range, [r(1, 5, 0), r(3, 5, 2)], false);
|
||||
});
|
||||
// test('Fold one level', () => {
|
||||
// let range = [
|
||||
// 'A',
|
||||
// ' A',
|
||||
// ' A',
|
||||
// ' A'
|
||||
// ];
|
||||
// assertRanges(range, [r(1, 4, 0)], true);
|
||||
// assertRanges(range, [r(1, 4, 0)], false);
|
||||
// });
|
||||
|
||||
test('Fold three levels', () => {
|
||||
let range = [
|
||||
'A',
|
||||
' A',
|
||||
' A',
|
||||
' A',
|
||||
'A'
|
||||
];
|
||||
assertRanges(range, [r(1, 4, 0), r(2, 4, 2), r(3, 4, 4)], true);
|
||||
assertRanges(range, [r(1, 4, 0), r(2, 4, 2), r(3, 4, 4)], false);
|
||||
});
|
||||
// test('Fold two levels', () => {
|
||||
// let range = [
|
||||
// 'A',
|
||||
// ' A',
|
||||
// ' A',
|
||||
// ' A',
|
||||
// ' A'
|
||||
// ];
|
||||
// assertRanges(range, [r(1, 5, 0), r(3, 5, 2)], true);
|
||||
// assertRanges(range, [r(1, 5, 0), r(3, 5, 2)], false);
|
||||
// });
|
||||
|
||||
test('Fold decreasing indent', () => {
|
||||
let range = [
|
||||
' A',
|
||||
' A',
|
||||
'A'
|
||||
];
|
||||
assertRanges(range, [], true);
|
||||
assertRanges(range, [], false);
|
||||
});
|
||||
// test('Fold three levels', () => {
|
||||
// let range = [
|
||||
// 'A',
|
||||
// ' A',
|
||||
// ' A',
|
||||
// ' A',
|
||||
// 'A'
|
||||
// ];
|
||||
// assertRanges(range, [r(1, 4, 0), r(2, 4, 2), r(3, 4, 4)], true);
|
||||
// assertRanges(range, [r(1, 4, 0), r(2, 4, 2), r(3, 4, 4)], false);
|
||||
// });
|
||||
|
||||
test('Fold Java', () => {
|
||||
// test('Fold decreasing indent', () => {
|
||||
// let range = [
|
||||
// ' A',
|
||||
// ' A',
|
||||
// 'A'
|
||||
// ];
|
||||
// assertRanges(range, [], true);
|
||||
// assertRanges(range, [], false);
|
||||
// });
|
||||
|
||||
// test('Fold Java', () => {
|
||||
// assertRanges([
|
||||
// /* 1*/ 'class A {',
|
||||
// /* 2*/ ' void foo() {',
|
||||
// /* 3*/ ' console.log();',
|
||||
// /* 4*/ ' console.log();',
|
||||
// /* 5*/ ' }',
|
||||
// /* 6*/ '',
|
||||
// /* 7*/ ' void bar() {',
|
||||
// /* 8*/ ' console.log();',
|
||||
// /* 9*/ ' }',
|
||||
// /*10*/ '}',
|
||||
// /*11*/ 'interface B {',
|
||||
// /*12*/ ' void bar();',
|
||||
// /*13*/ '}',
|
||||
// ], [r(1, 9, 0), r(2, 4, 2), r(7, 8, 2), r(11, 12, 0)], false);
|
||||
// });
|
||||
|
||||
// test('Fold Javadoc', () => {
|
||||
// assertRanges([
|
||||
// /* 1*/ '/**',
|
||||
// /* 2*/ ' * Comment',
|
||||
// /* 3*/ ' */',
|
||||
// /* 4*/ 'class A {',
|
||||
// /* 5*/ ' void foo() {',
|
||||
// /* 6*/ ' }',
|
||||
// /* 7*/ '}',
|
||||
// ], [r(1, 3, 0), r(4, 6, 0)], false);
|
||||
// });
|
||||
// test('Fold Whitespace Java', () => {
|
||||
// assertRanges([
|
||||
// /* 1*/ 'class A {',
|
||||
// /* 2*/ '',
|
||||
// /* 3*/ ' void foo() {',
|
||||
// /* 4*/ ' ',
|
||||
// /* 5*/ ' return 0;',
|
||||
// /* 6*/ ' }',
|
||||
// /* 7*/ ' ',
|
||||
// /* 8*/ '}',
|
||||
// ], [r(1, 7, 0), r(3, 5, 2)], false);
|
||||
// });
|
||||
|
||||
// test('Fold Whitespace Python', () => {
|
||||
// assertRanges([
|
||||
// /* 1*/ 'def a:',
|
||||
// /* 2*/ ' pass',
|
||||
// /* 3*/ ' ',
|
||||
// /* 4*/ ' def b:',
|
||||
// /* 5*/ ' pass',
|
||||
// /* 6*/ ' ',
|
||||
// /* 7*/ ' ',
|
||||
// /* 8*/ 'def c: # since there was a deintent here'
|
||||
// ], [r(1, 5, 0), r(4, 5, 2)], true);
|
||||
// });
|
||||
|
||||
// test('Fold Tabs', () => {
|
||||
// assertRanges([
|
||||
// /* 1*/ 'class A {',
|
||||
// /* 2*/ '\t\t',
|
||||
// /* 3*/ '\tvoid foo() {',
|
||||
// /* 4*/ '\t \t//hello',
|
||||
// /* 5*/ '\t return 0;',
|
||||
// /* 6*/ ' \t}',
|
||||
// /* 7*/ ' ',
|
||||
// /* 8*/ '}',
|
||||
// ], [r(1, 7, 0), r(3, 5, 4)], false);
|
||||
// });
|
||||
// });
|
||||
|
||||
let foldPattern: FoldMarkers = {
|
||||
start: '^\\s*#region',
|
||||
end: '^\\s*#endregion'
|
||||
};
|
||||
|
||||
suite('Folding with regions', () => {
|
||||
test('Inside region, indented', () => {
|
||||
assertRanges([
|
||||
/* 1*/ 'class A {',
|
||||
/* 2*/ ' void foo() {',
|
||||
/* 3*/ ' console.log();',
|
||||
/* 4*/ ' console.log();',
|
||||
/* 5*/ ' }',
|
||||
/* 6*/ '',
|
||||
/* 7*/ ' void bar() {',
|
||||
/* 8*/ ' console.log();',
|
||||
/* 9*/ ' }',
|
||||
/*10*/ '}',
|
||||
/*11*/ 'interface B {',
|
||||
/*12*/ ' void bar();',
|
||||
/*13*/ '}',
|
||||
], [r(1, 9, 0), r(2, 4, 2), r(7, 8, 2), r(11, 12, 0)], false);
|
||||
});
|
||||
|
||||
test('Fold Javadoc', () => {
|
||||
assertRanges([
|
||||
/* 1*/ '/**',
|
||||
/* 2*/ ' * Comment',
|
||||
/* 3*/ ' */',
|
||||
/* 4*/ 'class A {',
|
||||
/* 5*/ ' void foo() {',
|
||||
/* 6*/ ' }',
|
||||
/* 7*/ '}',
|
||||
], [r(1, 3, 0), r(4, 6, 0)], false);
|
||||
});
|
||||
test('Fold Whitespace Java', () => {
|
||||
assertRanges([
|
||||
/* 1*/ 'class A {',
|
||||
/* 2*/ '',
|
||||
/* 2*/ ' #region',
|
||||
/* 3*/ ' void foo() {',
|
||||
/* 4*/ ' ',
|
||||
/* 5*/ ' return 0;',
|
||||
/* 6*/ ' }',
|
||||
/* 7*/ ' ',
|
||||
/* 7*/ ' #endregion',
|
||||
/* 8*/ '}',
|
||||
], [r(1, 7, 0), r(3, 5, 2)], false);
|
||||
], [r(1, 7, 0), r(2, 7, 2, true), r(3, 5, 2)], false, foldPattern);
|
||||
});
|
||||
|
||||
test('Fold Whitespace Python', () => {
|
||||
test('Inside region, not indented', () => {
|
||||
assertRanges([
|
||||
/* 1*/ 'def a:',
|
||||
/* 2*/ ' pass',
|
||||
/* 3*/ ' ',
|
||||
/* 4*/ ' def b:',
|
||||
/* 5*/ ' pass',
|
||||
/* 6*/ ' ',
|
||||
/* 7*/ ' ',
|
||||
/* 8*/ 'def c: # since there was a deintent here'
|
||||
], [r(1, 5, 0), r(4, 5, 2)], true);
|
||||
/* 1*/ 'var x;',
|
||||
/* 2*/ '#region',
|
||||
/* 3*/ 'void foo() {',
|
||||
/* 4*/ ' ',
|
||||
/* 5*/ ' return 0;',
|
||||
/* 6*/ ' }',
|
||||
/* 7*/ '#endregion',
|
||||
/* 8*/ '',
|
||||
], [r(2, 7, 0, true), r(3, 6, 0)], false, foldPattern);
|
||||
});
|
||||
|
||||
test('Fold Tabs', () => {
|
||||
test('Empty Regions', () => {
|
||||
assertRanges([
|
||||
/* 1*/ 'var x;',
|
||||
/* 2*/ '#region',
|
||||
/* 3*/ '#endregion',
|
||||
/* 4*/ '#region',
|
||||
/* 5*/ '',
|
||||
/* 6*/ '#endregion',
|
||||
/* 7*/ 'var y;',
|
||||
], [r(2, 3, 0, true), r(4, 6, 0, true)], false, foldPattern);
|
||||
});
|
||||
test('Nested Regions', () => {
|
||||
assertRanges([
|
||||
/* 1*/ 'var x;',
|
||||
/* 2*/ '#region',
|
||||
/* 3*/ '#region',
|
||||
/* 4*/ '',
|
||||
/* 5*/ '#endregion',
|
||||
/* 6*/ '#endregion',
|
||||
/* 7*/ 'var y;',
|
||||
], [r(2, 6, 0, true), r(3, 5, 0, true)], false, foldPattern);
|
||||
});
|
||||
test('Nested Regions 2', () => {
|
||||
assertRanges([
|
||||
/* 1*/ 'class A {',
|
||||
/* 2*/ '\t\t',
|
||||
/* 3*/ '\tvoid foo() {',
|
||||
/* 4*/ '\t \t//hello',
|
||||
/* 5*/ '\t return 0;',
|
||||
/* 6*/ ' \t}',
|
||||
/* 7*/ ' ',
|
||||
/* 8*/ '}',
|
||||
], [r(1, 7, 0), r(3, 5, 4)], false);
|
||||
/* 2*/ ' #region',
|
||||
/* 3*/ '',
|
||||
/* 4*/ ' #region',
|
||||
/* 5*/ '',
|
||||
/* 6*/ ' #endregion',
|
||||
/* 7*/ ' // comment',
|
||||
/* 8*/ ' #endregion',
|
||||
/* 9*/ '}',
|
||||
], [r(1, 8, 0), r(2, 8, 2, true), r(4, 6, 2, true)], false, foldPattern);
|
||||
});
|
||||
test('Incomplete Regions', () => {
|
||||
assertRanges([
|
||||
/* 1*/ 'class A {',
|
||||
/* 2*/ '#region',
|
||||
/* 3*/ ' // comment',
|
||||
/* 4*/ '}',
|
||||
], [], false, foldPattern);
|
||||
});
|
||||
test('Incomplete Regions', () => {
|
||||
assertRanges([
|
||||
/* 1*/ '',
|
||||
/* 2*/ '#region',
|
||||
/* 3*/ '#region',
|
||||
/* 4*/ '#region',
|
||||
/* 5*/ ' // comment',
|
||||
/* 6*/ '#endregion',
|
||||
/* 7*/ '#endregion',
|
||||
/* 8*/ ' // hello',
|
||||
], [r(3, 7, 0, true), r(4, 6, 0, true)], false, foldPattern);
|
||||
});
|
||||
test('Indented region before', () => {
|
||||
assertRanges([
|
||||
/* 1*/ 'if (x)',
|
||||
/* 2*/ ' return;',
|
||||
/* 3*/ '',
|
||||
/* 4*/ '#region',
|
||||
/* 5*/ ' // comment',
|
||||
/* 6*/ '#endregion',
|
||||
], [r(1, 3, 0), r(4, 6, 0, true)], false, foldPattern);
|
||||
});
|
||||
test('Indented region before 2', () => {
|
||||
assertRanges([
|
||||
/* 1*/ 'if (x)',
|
||||
/* 2*/ ' log();',
|
||||
/* 3*/ '',
|
||||
/* 4*/ ' #region',
|
||||
/* 5*/ ' // comment',
|
||||
/* 6*/ ' #endregion',
|
||||
], [r(1, 6, 0), r(2, 6, 2), r(4, 6, 4, true)], false, foldPattern);
|
||||
});
|
||||
test('Indented region in-between', () => {
|
||||
assertRanges([
|
||||
/* 1*/ '#region',
|
||||
/* 2*/ ' // comment',
|
||||
/* 3*/ ' if (x)',
|
||||
/* 4*/ ' return;',
|
||||
/* 5*/ '',
|
||||
/* 6*/ '#endregion',
|
||||
], [r(1, 6, 0, true), r(3, 5, 2)], false, foldPattern);
|
||||
});
|
||||
test('Indented region after', () => {
|
||||
assertRanges([
|
||||
/* 1*/ '#region',
|
||||
/* 2*/ ' // comment',
|
||||
/* 3*/ '',
|
||||
/* 4*/ '#endregion',
|
||||
/* 5*/ ' if (x)',
|
||||
/* 6*/ ' return;',
|
||||
], [r(1, 4, 0, true), r(5, 6, 2)], false, foldPattern);
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue