Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Virtualize the Project Files tree list #2458

Merged
merged 19 commits into from
Dec 6, 2024
Merged

Conversation

dotNomad
Copy link
Collaborator

@dotNomad dotNomad commented Dec 3, 2024

Forgive the size on this one 😅. This PR improves the performance of the extension sidebar when Project Files has a large number of files. The list is now virtualized, only including the visible items in the DOM.

This is accomplished using vue-virtual-scroller. I chose it since it is the top recommendation under Vue's documentation for Virtualize Large Lists optimizations.

Intent

Resolves #2204

Type of Change

    • Bug Fix
    • New Feature
    • Breaking Change
    • Documentation
    • Refactor
    • Tooling

Approach

To keep things a bit more isolated I split out a new Files store from our Home store.

Only TreeItemCheckbox needed to change for this fix, but all functionality added was optional. This let me make the same functional changes to TreeItem so it could be used virtually in the future without affecting current usage.

The Flat File array to make this all work is calculated based on the files we get from the API and the expanded directories set.

Virtualization

vue-virtual-scroller has an important notes section for its RecycleScroller that is very relevant to the approach taken here:

I'll walk through each that are important:

  • "You need to set the size of the virtual-scroller element and the items elements"
    • Luckily the size of each item element was already set to 22px in the same way VS Code handles sizing
    • The whole scroller was not though. max-height was used in CSS
  • "The list item components must be reactive to the item prop being updated without being re-created (use computed props or watchers to properly react to props changes!)"
    • The ProjectFile component was created to handle this, and the virtual scroller added to the ProjectFiles view. The TreeProjectFiles component was removed entirely.
  • "Since DOM elements are reused for items, it's recommended to define hover styles using the provided hover class instead of the :hover state selector (e.g. .vue-recycle-scroller__item-view.hover or .hover .some-element-inside-the-item-view)."
    • This is why the CSS changed for TreeItem and TreeItemCheckbox

Another important note from this section: "The browsers have a size limitation on DOM elements, it means that currently the virtual scroller can't display more than ~500k items depending on the browser."

User Impact

A user sees very little impact unless they were running into the hanging sidebar described in #2204.

Expanded folders are now remembered until the deployment's directory changes. This mimics behavior seen in VS Code's Explorer view better where a parent collapsing doesn't reset the children's expanded state.

Automated Tests

Automated tests were added for the methods in the new File store.

Directions for Reviewers

Test with deployments with varying number of files. An ideal test would be a deployment with a directory containing 1000+ files.

Include, Exclude, Open actions should all work as they did before.

Note: that this PR doesn't change the speed of any of the files APIs. On a significantly large deployment directory the API could take upwards of 2 seconds. That bottleneck still exists, but is much less significant.

If the Files API takes awhile due to a very large directory size, and the user clicks a checkbox to include/exclude a item; the checkbox could be inaccurate when the parent directory is then collapsed and re-expanded. This is due to the item being removed then re-added to the DOM upon collapsing and expanding. We do not store a local state of included/excluded files and instead rely on the API response. This reliance also explains why including/excluding a directory does not change nested files until we hear back from the Files API.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vue-virtual-scroller doesn't have any types in its package. This keeps us from getting yelled at from tsc

@@ -1,37 +1,47 @@
import { ContentRecordFile } from "../../../../src/api";

export function splitFilesOnInclusion(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was no longer used after this work.

@@ -1,14 +1,18 @@
import { ContentRecordFile, FileMatchSource } from "../../../../../../src/api";

export function includedFileTooltip(file: ContentRecordFile) {
export function includedFileTooltip(
file: Pick<ContentRecordFile, "rel" | "reason">,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes allowed for easy usage for ContentRecordFile and the FlatFile type.

Comment on lines +202 to +205
// If the root file has changed, reset the expanded directories
if (msg.content.files.abs !== fileStore.files?.abs) {
fileStore.expandedDirs = new Set();
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expanded directories only reset when the root changes; meaning switching deployments that have the same directory won't reset it.

This feels like good behavior to me, but I want to highlight it for discussion.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me! 👍

@dotNomad dotNomad marked this pull request as ready for review December 3, 2024 01:02
Copy link
Collaborator

@marcosnav marcosnav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I'll proceed to try it out on vscode

Comment on lines +202 to +205
// If the root file has changed, reset the expanded directories
if (msg.content.files.abs !== fileStore.files?.abs) {
fileStore.expandedDirs = new Set();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me! 👍

return { vscodeAPI };
});

describe("File Store", () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding these tests 🙇 !

Copy link
Collaborator

@marcosnav marcosnav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validated and works great

Tried the previous implementation first, and the lag was really bad with a couple thousands of files.

Then, tried this new implementation and the files navigation and load times are much much better. 🎉

If the Files API takes awhile due to a very large directory size, and the user clicks a checkbox to include/exclude a item; the checkbox could be inaccurate when the parent directory is then collapsed and re-expanded.

Stumbled with the inaccurate checkbox state, it might deserve it's own GH issue. On the other hand, I was clicking and opening directories fiercely to see if the config file updated as expected, something that I don't think many users will do.

@dotNomad
Copy link
Collaborator Author

dotNomad commented Dec 4, 2024

Stumbled with the inaccurate checkbox state, it might deserve it's own GH issue. On the other hand, I was clicking and opening directories fiercely to see if the config file updated as expected, something that I don't think many users will do.

Agreed on all accounts. I'll create another issue for this. It is pretty separate logically from this, and to your point I don't think a lot of users will be opening and closing directories nearly as much as we are 😆

@dotNomad
Copy link
Collaborator Author

dotNomad commented Dec 6, 2024

Created #2467 for the inaccurate state for the checkbox while waiting for the Files API. Thanks for prompting that @marcosnav - going to get this merged.

@dotNomad dotNomad merged commit a13b259 into main Dec 6, 2024
14 checks passed
@dotNomad dotNomad deleted the dotnomad/virtual-files branch December 6, 2024 17:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TreeProjectFiles hangs sidebar when a deployment has too many folders
2 participants