Skip to content
Open
13 changes: 12 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
"command": "diffviewer.showSideBySide",
"title": "Show diff side by side",
"icon": "$(files)"
},
{
"command": "diffviewer.openCollapsed",
"title": "Open diff collapsed (all viewed)"
}
],
"customEditors": [
Expand Down Expand Up @@ -142,6 +146,13 @@
}
],
"menus": {
"explorer/context": [
{
"command": "diffviewer.openCollapsed",
"group": "navigation@99",
"when": "resourceLangId == 'diff'"
}
],
"editor/title": [
{
"command": "diffviewer.showLineByLine",
Expand All @@ -165,7 +176,7 @@
"pack:extension": "vsce package -o ./dist/vscode_diff_viewer_v$npm_package_version.vsix --githubBranch main",
"run:webmode": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=. --version=stable",
"jest:run": "jest --silent",
"test": "npm run jest:run",
"test": "npm run jest:run --",
"test:coverage": "jest --coverage --silent",
"test:coverage:ci": "jest --coverage --ci --silent",
"prepare": "husky install"
Expand Down
40 changes: 28 additions & 12 deletions src/extension/__tests__/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { MessageToExtensionHandlerImpl } from "../message/handler";
import { ViewedStateStore } from "../viewed-state";
import { buildSkeleton } from "../skeleton";
import { extractConfig, setOutputFormatConfig } from "../configuration";
import { SkeletonElementIds } from "../../shared/css/elements";
import { MessageToWebview } from "../../shared/message";

// Mock dependencies
Expand Down Expand Up @@ -188,7 +187,7 @@ describe("DiffViewerProvider", () => {
webviewPath: mockWebviewPath,
});

expect(disposables).toHaveLength(3);
expect(disposables).toHaveLength(4);
expect(mockRegisterCustomEditorProvider).toHaveBeenCalledWith("diffViewer", expect.any(DiffViewerProvider), {
webviewOptions: {
retainContextWhenHidden: true,
Expand All @@ -198,24 +197,42 @@ describe("DiffViewerProvider", () => {
});
expect(mockRegisterCommand).toHaveBeenCalledWith("diffviewer.showLineByLine", expect.any(Function));
expect(mockRegisterCommand).toHaveBeenCalledWith("diffviewer.showSideBySide", expect.any(Function));
expect(mockRegisterCommand).toHaveBeenCalledWith("diffviewer.openCollapsed", expect.any(Function));
});

it("should call setOutputFormatConfig when commands are executed", () => {
it.each([
["showSideBySide", "side-by-side"],
["showLineByLine", "line-by-line"],
])("should call setOutputFormatConfig when commands are executed", (cmd, expectedConfig) => {
DiffViewerProvider.registerContributions({
extensionContext: mockExtensionContext,
webviewPath: mockWebviewPath,
});

// Get the command functions
const lineByLineCommand = mockRegisterCommand.mock.calls[0][1];
const sideBySideCommand = mockRegisterCommand.mock.calls[1][1];
// Get the command function
const command = mockRegisterCommand.mock.calls.find((c) => c[0] == `diffviewer.${cmd}`)[1];

// Execute commands
lineByLineCommand();
sideBySideCommand();
command();

expect(mockSetOutputFormatConfig).toHaveBeenCalledWith("line-by-line");
expect(mockSetOutputFormatConfig).toHaveBeenCalledWith("side-by-side");
expect(mockSetOutputFormatConfig).toHaveBeenCalledWith(expectedConfig);
});

it("should open a diff with all files collapsed", () => {
DiffViewerProvider.registerContributions({
extensionContext: mockExtensionContext,
webviewPath: mockWebviewPath,
});

// Get the command function
const command = mockRegisterCommand.mock.calls.find((c) => c[0] == `diffviewer.openCollapsed`)[1];

(vscode.Uri.file as jest.Mock).mockReturnValueOnce({ with: jest.fn().mockReturnValue("uri-with-selected") });

// Execute commands
command(vscode.Uri.file("foo.patch"));

expect(vscode.commands.executeCommand).toHaveBeenCalledWith("vscode.openWith", "uri-with-selected", "diffViewer");
});
});

Expand Down Expand Up @@ -307,7 +324,7 @@ describe("DiffViewerProvider", () => {
config: expect.any(Object),
diffFiles: expect.any(Array),
viewedState: expect.any(Object),
diffContainer: SkeletonElementIds.DiffContainer,
collapseAll: false,
},
});
});
Expand Down Expand Up @@ -452,7 +469,6 @@ describe("DiffViewerProvider", () => {
config: expect.any(Object),
diffFiles: expect.any(Array),
viewedState: expect.any(Object),
diffContainer: SkeletonElementIds.DiffContainer,
}),
});
});
Expand Down
13 changes: 9 additions & 4 deletions src/extension/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { parse } from "diff2html";
import { ColorSchemeType } from "diff2html/lib/types";
import { basename } from "path";
import * as vscode from "vscode";
import { SkeletonElementIds } from "../shared/css/elements";
import { MessageToExtensionHandler, MessageToWebview } from "../shared/message";
import { APP_CONFIG_SECTION, AppConfig, extractConfig, setOutputFormatConfig } from "./configuration";
import { MessageToExtensionHandlerImpl } from "./message/handler";
Expand Down Expand Up @@ -44,6 +43,10 @@ export class DiffViewerProvider implements vscode.CustomTextEditorProvider {
}),
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 = file.with({ query: "collapsed" });
await vscode.commands.executeCommand("vscode.openWith", collapsedUri, "diffViewer");
}),
];
}

Expand Down Expand Up @@ -72,6 +75,8 @@ export class DiffViewerProvider implements vscode.CustomTextEditorProvider {
docId: diffDocument.uri.fsPath,
});

const collapseAll = new URLSearchParams(diffDocument.uri.query).has("collapsed");

const messageReceivedHandler = new MessageToExtensionHandlerImpl({
diffDocument,
viewedStateStore,
Expand All @@ -83,7 +88,7 @@ export class DiffViewerProvider implements vscode.CustomTextEditorProvider {
const webviewContext: WebviewContext = { document: diffDocument, panel: webviewPanel, viewedStateStore };

this.registerEventHandlers({ webviewContext, messageHandler: messageReceivedHandler });
this.updateWebview(webviewContext);
this.updateWebview(webviewContext, collapseAll);
}

private registerEventHandlers(args: { webviewContext: WebviewContext; messageHandler: MessageToExtensionHandler }) {
Expand Down Expand Up @@ -113,7 +118,7 @@ export class DiffViewerProvider implements vscode.CustomTextEditorProvider {
args.webviewContext.panel.onDidDispose(() => disposables.dispose());
}

private updateWebview(webviewContext: WebviewContext): void {
private updateWebview(webviewContext: WebviewContext, collapseAll = false): void {
this.postMessageToWebviewWrapper({
webview: webviewContext.panel.webview,
message: {
Expand Down Expand Up @@ -154,7 +159,7 @@ export class DiffViewerProvider implements vscode.CustomTextEditorProvider {
config: config,
diffFiles: diffFiles,
viewedState: viewedState,
diffContainer: SkeletonElementIds.DiffContainer,
collapseAll,
},
},
});
Expand Down
4 changes: 3 additions & 1 deletion src/extension/viewed-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export class ViewedStateStore {
if (!this.args.docId) {
this.transientViewedState = viewedState;
} else {
this.args.context.workspaceState.update(this.args.docId, viewedState);
// remove the state if no files are marked as viewed
const stateToSave = Object.keys(viewedState).length === 0 ? undefined : viewedState;
this.args.context.workspaceState.update(this.args.docId, stateToSave);
}
}
}
4 changes: 4 additions & 0 deletions src/webview/css/classes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum Diff2HtmlCssClasses {
Div__DiffFileContent__Collapsed = "d2h-d-none",
Input__ViewedToggle__Selected = "d2h-selected",
}
1 change: 1 addition & 0 deletions src/webview/css/elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export enum Diff2HtmlCssClassElements {
Div__File = ".d2h-file-wrapper",
Div__LeftDiffOnSideBySide__FirstChild = ".d2h-file-side-diff:first-child",
Div__LineNumberRightOnLineByLine = ".line-num2",
Label__ViewedToggle = ".d2h-file-collapse",
Input__ViewedToggle = ".d2h-file-collapse-input",
Input__ViewedToggle__Checked = ".d2h-file-collapse-input:checked",
Td__DeletedLine = ".d2h-del",
Expand Down
Loading
Loading