Skip to content
6 changes: 4 additions & 2 deletions src/vs/base/browser/ui/list/listView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1560,9 +1560,11 @@ export class ListView<T> implements IListView<T> {
}

private getVisibleRange(renderTop: number, renderHeight: number): IRange {
const start = this.rangeMap.indexAt(renderTop);
return {
start: this.rangeMap.indexAt(renderTop),
end: this.rangeMap.indexAfter(renderTop + renderHeight - 1)
start,
// Clamp so a collapsed viewport (renderHeight <= 0) cannot produce an inverted range.
end: Math.max(start, this.rangeMap.indexAfter(renderTop + renderHeight - 1))
Comment thread
vs-code-engineering[bot] marked this conversation as resolved.
};
}

Expand Down
25 changes: 25 additions & 0 deletions src/vs/base/test/browser/ui/list/listView.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -577,4 +577,29 @@ suite('ListView', function () {
element.remove();
}
});

test('does not throw when laid out with a collapsed viewport and zero-height dynamic items', function () {
type TestElement = { height: number };
const delegate: IListVirtualDelegate<TestElement> = {
getHeight() { return 0; },
getTemplateId() { return 'template'; },
getDynamicHeight(element) { return element.height; }
};
const renderer: IListRenderer<TestElement, void> = {
templateId: 'template',
renderTemplate() { },
renderElement() { },
disposeTemplate() { }
};

const elements: TestElement[] = [{ height: 0 }, { height: 0 }, { height: 0 }];
const listView = new ListView<TestElement>(document.createElement('div'), delegate, [renderer], { supportDynamicHeights: true });
try {
// Collapsing the viewport before splicing zero-height items previously yielded an inverted range that crashed probeDynamicHeights.
listView.layout(0, 200);
assert.doesNotThrow(() => listView.splice(0, 0, elements));
} finally {
listView.dispose();
}
});
});
Loading