Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/commandTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,27 @@ export function getActiveDiffTabUriPair(): ComparisonUriPair | undefined {
return undefined;
}

/**
* Reports the revision comparison shown by the diff editor that currently has
* focus, if it is one.
*
* This is what lets the editor context menu offer the rendered diff while a
* committed revision diff is open: unlike the working tree and index, a commit
* cannot be recovered from the file on disk, so it has to be read from the tab.
*
* @returns The two sides when the active tab is a revision diff, otherwise
* undefined.
*/
export function getActiveRevisionComparison(): ComparisonUriPair | undefined {
const pair = getActiveDiffTabUriPair();

if (!pair) {
return undefined;
}

return getRevisionComparison(pair.originalUri, pair.modifiedUri);
}

export const __test__ = {
extractComparisonUris,
inferComparisonHint,
Expand Down
64 changes: 48 additions & 16 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
CommandTarget,
ComparisonUriPair,
getActiveDiffTabUriPair,
getActiveRevisionComparison,
getCommandTarget,
getFileUriFromCommandArg,
getRevisionComparison,
Expand Down Expand Up @@ -472,6 +473,34 @@ function isActionableSingleFileComparison(
return comparison.kind !== "cleanHeadToWorkingTree" || isDirty;
}

/**
* Sets the `canShowRenderedDiff` context key, but only when the value actually
* changed and this update has not been superseded by a newer one.
*
* @param canShow - Whether the rendered diff command should be offered.
* @param generation - The generation this update belongs to; a mismatch means a
* later update already ran, so this one is dropped.
*/
async function setCanShowRenderedDiff(
canShow: boolean,
generation: number,
): Promise<void> {
if (generation !== contextUpdateGeneration) {
return;
}

if (lastCanShowRenderedDiff === canShow) {
return;
}

lastCanShowRenderedDiff = canShow;
await vscode.commands.executeCommand(
"setContext",
"rich-markdown-diff.canShowRenderedDiff",
canShow,
);
}

async function updateRenderedDiffContext(
editor: vscode.TextEditor | undefined = vscode.window.activeTextEditor,
) {
Expand All @@ -497,14 +526,7 @@ async function updateRenderedDiffContext(
if (!hasVisibleMarkdownEditor) {
activeEditorRepositorySubscription?.dispose();
activeEditorRepositorySubscription = undefined;
if (lastCanShowRenderedDiff !== false) {
lastCanShowRenderedDiff = false;
await vscode.commands.executeCommand(
"setContext",
"rich-markdown-diff.canShowRenderedDiff",
false,
);
}
await setCanShowRenderedDiff(false, myGen);
} else if (!lastCanShowRenderedDiff) {
// A markdown editor is visible but context was previously disabled or
// uninitialized — re-evaluate by finding the visible markdown editor.
Expand All @@ -520,6 +542,23 @@ async function updateRenderedDiffContext(
return;
}

// A committed revision diff (opened from the Source Control Graph, say) is
// renderable, but the comparison resolved below only sees the working tree
// and index and would classify the file as unchanged. Detect it from the
// active diff tab so the editor context menu offers the command too, matching
// the title bar button that the diff editor already shows. The markdown check
// stops a non-markdown diff in another editor group from enabling the key.
const activeRevision = getActiveRevisionComparison();
if (
activeRevision &&
isMarkdownPath(toFileBackedUri(activeRevision.modifiedUri).fsPath)
) {
activeEditorRepositorySubscription?.dispose();
activeEditorRepositorySubscription = undefined;
await setCanShowRenderedDiff(true, myGen);
return;
}

// Normalize git: / vscode-userdata: URIs to file: URIs so the git API
// can locate the repository (e.g. when the built-in diff editor is focused).
const editorUri = editor.document.uri;
Expand All @@ -545,14 +584,7 @@ async function updateRenderedDiffContext(
comparison,
editor.document.isDirty,
);
if (lastCanShowRenderedDiff !== canShow) {
lastCanShowRenderedDiff = canShow;
await vscode.commands.executeCommand(
"setContext",
"rich-markdown-diff.canShowRenderedDiff",
canShow,
);
}
await setCanShowRenderedDiff(canShow, myGen);

if (myGen !== contextUpdateGeneration) {
return;
Expand Down
57 changes: 57 additions & 0 deletions src/test/suite/commandTarget.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import * as vscode from "vscode";
import {
__test__,
getActiveDiffTabUriPair,
getActiveRevisionComparison,
getCommandTarget,
getRevisionComparison,
refersToSameFile,
Expand Down Expand Up @@ -312,3 +313,59 @@ describe("Same File Detection", () => {
);
});
});

describe("Active Revision Comparison", () => {
const fileUri = vscode.Uri.file(
path.join(os.tmpdir(), "rmd-active-rev", "doc.md"),
);
const parentSha = "a1b2c3d4e5f60718293a4b5c6d7e8f9012345678";
const commitSha = "c02e3e4a1b2c3d4e5f60718293a4b5c6d7e8f901";

const gitUri = (ref: string) =>
fileUri.with({
scheme: "git",
query: JSON.stringify({ path: fileUri.fsPath, ref }),
});

afterEach(async () => {
await vscode.commands.executeCommand("workbench.action.closeAllEditors");
});

it("detects a commit-to-commit diff tab", async () => {
await vscode.commands.executeCommand(
"vscode.diff",
gitUri(parentSha),
gitUri(commitSha),
"doc.md (commit)",
);

const revision = getActiveRevisionComparison();

assert.ok(revision, "Expected a revision comparison");
assert.strictEqual(
revision?.modifiedUri.toString(),
gitUri(commitSha).toString(),
);
});

it("ignores a HEAD-to-working-tree diff tab", async () => {
await vscode.commands.executeCommand(
"vscode.diff",
gitUri("HEAD"),
fileUri,
"doc.md (changes)",
);

assert.strictEqual(getActiveRevisionComparison(), undefined);
});

it("ignores a plain markdown editor", async () => {
const document = await vscode.workspace.openTextDocument({
language: "markdown",
content: "# Heading\n",
});
await vscode.window.showTextDocument(document);

assert.strictEqual(getActiveRevisionComparison(), undefined);
});
});