Skip to content

Commit ff3c403

Browse files
committed
[html] folding for self-closing tags
1 parent 294337b commit ff3c403

3 files changed

Lines changed: 43 additions & 13 deletions

File tree

extensions/html/server/src/modes/htmlFolding.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { LanguageService as HTMLLanguageService, TokenType, Range } from 'vscode
88

99
import { FoldingRangeType, FoldingRange, FoldingRangeList } from '../protocol/foldingProvider.proposed';
1010
import { LanguageModes } from './languageModes';
11+
import { binarySearch } from '../utils/arrays';
1112

1213
export function getFoldingRegions(languageModes: LanguageModes, document: TextDocument, maxRanges: number | undefined, cancellationToken: CancellationToken | null): FoldingRangeList {
1314
let htmlMode = languageModes.getMode('html');
@@ -90,6 +91,11 @@ function limitRanges(ranges: FoldingRange[], maxRanges: number) {
9091
return ranges.filter((r, index) => (typeof nestingLevels[index] === 'number') && nestingLevels[index] < maxLevel);
9192
}
9293

94+
export const EMPTY_ELEMENTS: string[] = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'];
95+
96+
export function isEmptyElement(e: string): boolean {
97+
return !!e && binarySearch(EMPTY_ELEMENTS, e.toLowerCase(), (s1: string, s2: string) => s1.localeCompare(s2)) >= 0;
98+
}
9399

94100
export function getHTMLFoldingRegions(htmlLanguageService: HTMLLanguageService, document: TextDocument, range: Range): FoldingRange[] {
95101
const scanner = htmlLanguageService.createScanner(document.getText());
@@ -121,6 +127,11 @@ export function getHTMLFoldingRegions(htmlLanguageService: HTMLLanguageService,
121127
lastTagName = scanner.getTokenText();
122128
break;
123129
}
130+
case TokenType.StartTagClose:
131+
if (!isEmptyElement(lastTagName)) {
132+
break;
133+
}
134+
// fallthrough
124135
case TokenType.EndTagClose:
125136
case TokenType.StartTagSelfClose: {
126137
let name = elementNames.pop();

extensions/html/server/src/test/folding.test.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,20 @@ suite('Object Folding', () => {
6969
assertRanges(input, [r(0, 6), r(1, 2), r(4, 5)]);
7070
});
7171

72-
// test('Fold self-closing tags', () => {
73-
// let input = [
74-
// /*0*/'<div>',
75-
// /*1*/'<a src="top">',
76-
// /*2*/'<img ',
77-
// /*3*/'</head>',
78-
// /*4*/'<body class="f">',
79-
// /*5*/'Body',
80-
// /*6*/'</body>',
81-
// /*7*/'</html>'
82-
// ];
83-
// assertRanges(input, [r(0, 6), r(1, 2), r(4, 5)]);
84-
// });
72+
test('Fold self-closing tags', () => {
73+
let input = [
74+
/*0*/'<div>',
75+
/*1*/'<a href="top"/>',
76+
/*2*/'<img src="s">',
77+
/*3*/'<br/>',
78+
/*4*/'<br>',
79+
/*5*/'<img class="c"',
80+
/*6*/' src="top"',
81+
/*7*/'>',
82+
/*8*/'</div>'
83+
];
84+
assertRanges(input, [r(0, 7), r(5, 6)]);
85+
});
8586

8687
// test('Fold commment', () => {
8788
// let input = [

extensions/html/server/src/utils/arrays.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,21 @@ function _divideAndMerge<T>(data: T[], compare: (a: T, b: T) => number): void {
5757
data[i++] = right[rightIdx++];
5858
}
5959
}
60+
61+
export function binarySearch<T>(array: T[], key: T, comparator: (op1: T, op2: T) => number): number {
62+
let low = 0,
63+
high = array.length - 1;
64+
65+
while (low <= high) {
66+
let mid = ((low + high) / 2) | 0;
67+
let comp = comparator(array[mid], key);
68+
if (comp < 0) {
69+
low = mid + 1;
70+
} else if (comp > 0) {
71+
high = mid - 1;
72+
} else {
73+
return mid;
74+
}
75+
}
76+
return -(low + 1);
77+
}

0 commit comments

Comments
 (0)