Skip to content

Conversation

@jacekkopecky
Copy link
Contributor

The aim of this PR is mainly to address #124, but also includes fixes that could be added independent of tackling #124.

The new command looks like this:

Screenshot 2025-09-28 at 14 49 21

The other changes included are:

  • fix for an issue: when a diff has some files marked as viewed, and we move to a different tab and then back to the diff tab, the view updates and unnecessarily scrolls to the last viewed file. My change replicates diff2html functionality in order to avoid using element.click() which focuses on it, and scrolls to it.
  • and smaller clarifications and cleanups

@caponetto any comments are welcome; I think this is best reviewed commit-by-commit.

@caponetto
Copy link
Owner

Thanks for the PR @jacekkopecky! I'll take a look at it asap

@caponetto caponetto requested a review from Copilot October 3, 2025 14:18
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Adds a new diffviewer.openCollapsed command to open diffs with all files initially collapsed (treated as viewed), and refactors the webview/update logic to support collapsing without triggering focus/scroll side effects. Also optimizes viewed state persistence (removing empty state) and replaces click-driven collapse behavior with explicit DOM class manipulation to avoid unintended scrolling.

  • New openCollapsed command and menu contribution
  • Introduces collapseAll payload flag and logic to pre-collapse all diff files
  • Replaces toggle.click() usage with manual DOM updates and adjusts viewed state saving

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/webview/message/handler.ts Implements collapseAll flow, custom file collapse logic, revised viewed state messaging, and UI helper adjustments
src/webview/message/api.ts Updates UpdateWebviewPayload: removes diffContainer, adds collapseAll flag
src/webview/css/elements.ts Adds selector for viewed toggle label to support manual collapse styling
src/webview/css/classes.ts Adds enum with diff2html CSS class names used when emulating collapse behavior
src/extension/viewed-state.ts Avoids persisting empty viewed state by storing undefined
src/extension/provider.ts Registers openCollapsed command and propagates collapseAll flag to webview
package.json Contributes new command and adds it to the explorer context menu

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

if (toggle.checked !== isChecked) {
toggle.click();
}
this.updateDiff2HtmlFileCollapsed(toggle, viewed);
Copy link

Copilot AI Oct 3, 2025

Choose a reason for hiding this comment

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

When marking all files as viewed (viewed === true), CHANGED_SINCE_VIEWED is not removed from toggles, whereas the single-toggle path removes it in onViewedToggleChangedHandler. This leads to stale 'changed since viewed' indicators after 'mark all' operations. Remove the class when viewed is true, e.g. toggle.classList.remove(CHANGED_SINCE_VIEWED) inside the loop.

Suggested change
this.updateDiff2HtmlFileCollapsed(toggle, viewed);
this.updateDiff2HtmlFileCollapsed(toggle, viewed);
if (viewed) {
toggle.classList.remove(CHANGED_SINCE_VIEWED);
}

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

can be done unconditionally

const diffHash = await this.getDiffHash(toggle);
if (diffHash !== oldSha1) {
this.updateDiff2HtmlFileCollapsed(toggle, false);
toggle.classList.add(CHANGED_SINCE_VIEWED);
Copy link

Copilot AI Oct 3, 2025

Choose a reason for hiding this comment

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

For unchanged files (diffHash === oldSha1) the previous behavior (toggle.click()) would clear CHANGED_SINCE_VIEWED via onViewedToggleChangedHandler; now the class is never removed if it was previously present. Explicitly remove CHANGED_SINCE_VIEWED for the unchanged (still-collapsed) case to avoid stale indicators.

Suggested change
toggle.classList.add(CHANGED_SINCE_VIEWED);
toggle.classList.add(CHANGED_SINCE_VIEWED);
} else {
toggle.classList.remove(CHANGED_SINCE_VIEWED);

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

unnecessary because this code is only called after a rerender so the class isn't there


// first synchronously hide all viewed files so we don't render too much in case the file is huge and most is collapsed
// we'll do a second pass over hidden files, so we'll hold them in togglesToRevisit
const togglesToRevisit = [];
Copy link

Copilot AI Oct 3, 2025

Choose a reason for hiding this comment

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

[nitpick] togglesToRevisit is inferred as any[] (empty array literal) until first push; under strict/noImplicitAny this may degrade type safety. Add an explicit type: const togglesToRevisit: { toggle: HTMLInputElement; oldSha1: string }[] = [];.

Suggested change
const togglesToRevisit = [];
const togglesToRevisit: { toggle: HTMLInputElement; oldSha1: string }[] = [];

Copilot uses AI. Check for mistakes.
}
this.updateDiff2HtmlFileCollapsed(toggle, viewed);
// no await here so we synchronously return from this function, the sending will happen later as hashes get computed
this.sendFileViewedMessage(toggle, viewed);
Copy link

Copilot AI Oct 3, 2025

Choose a reason for hiding this comment

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

[nitpick] sendFileViewedMessage returns a Promise but the result is neither awaited nor intentionally ignored; if it rejects it could cause an unhandled rejection. Consider prefixing with void or adding a .catch(...) to document intentional fire-and-forget behavior: void this.sendFileViewedMessage(toggle, viewed);.

Suggested change
this.sendFileViewedMessage(toggle, viewed);
void this.sendFileViewedMessage(toggle, viewed);

Copilot uses AI. Check for mistakes.
vscode.commands.registerCommand("diffviewer.showLineByLine", () => setOutputFormatConfig("line-by-line")),
vscode.commands.registerCommand("diffviewer.showSideBySide", () => setOutputFormatConfig("side-by-side")),
vscode.commands.registerCommand("diffviewer.openCollapsed", async (file) => {
const collapsedUri = vscode.Uri.from({ ...file, query: "collapsed" });
Copy link

Copilot AI Oct 3, 2025

Choose a reason for hiding this comment

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

[nitpick] Use the built-in URI.with API instead of spreading the URI object to preserve all internal fields safely: const collapsedUri = file.with({ query: 'collapsed' });.

Suggested change
const collapsedUri = vscode.Uri.from({ ...file, query: "collapsed" });
const collapsedUri = file.with({ query: "collapsed" });

Copilot uses AI. Check for mistakes.
@jacekkopecky
Copy link
Contributor Author

Thanks @caponetto ; I can look at the AI's suggestions tomorrow, it seems I really missed something but I'm not sure about how the AI suggests to fix it.

@caponetto
Copy link
Owner

@jacekkopecky Take these comments only as suggestions. Ignore them if they don't make sense.
Mind to rebase your PR with main? I've added a bunch of tests.

@jacekkopecky
Copy link
Contributor Author

@jacekkopecky Take these comments only as suggestions. Ignore them if they don't make sense.

Of course. 😄

Mind to rebase your PR with main? I've added a bunch of tests.

Happy to.

The webView already uses SkeletonElementIds and we
never use a different payload.diffContainer value anyway.
isVisible is a bit ambiguous - we're making the main
body *not* visible and the loading messages visible.
Then a consistent renaming for showEmpty().
This makes the workspace storage file a bit smaller.
When we use toggle.click(), that toggle gets
focused, which causes the window to scroll to the
last collapsed file when we switch to that tab.
That's disruptive and this fixes it.
This command is in the context menu for diff files in
the explorer. It opens the diff file with all parts
collapsed (marked as viewed) to potentially make it
easier to deal with very large diff files.
This omission was spotted by CoPilot
@codecov
Copy link

codecov bot commented Oct 4, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
src/extension/provider.ts 91.17% <100.00%> (+0.40%) ⬆️
src/extension/viewed-state.ts 100.00% <100.00%> (ø)
src/webview/css/classes.ts 100.00% <100.00%> (ø)
src/webview/css/elements.ts 100.00% <100.00%> (ø)
src/webview/message/handler.ts 91.58% <100.00%> (+1.52%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

The previous test would still pass if the two commands
used the opposite setOutputFormatConfig calls.
@jacekkopecky
Copy link
Contributor Author

@caponetto I think everything is handled now. I hope I got the tests right, your way of mocking things is different from what I'm used to…

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.

2 participants