BlueMonkMN
BlueMonkMN

Reputation: 25601

Showing differences from VS Code source control extension

I thought that setting quickDiffProvider would at least provide an entry point for when a file is selected in the UI to show diffs. But I can't even get the code to react to a click on a file in the Source Control panel for my custom source control provider.

extension.ts

export function activate(context: vscode.ExtensionContext) {

    // Use the console to output diagnostic information (console.log) and errors (console.error)
    // This line of code will only be executed once when your extension is activated
    console.log('Extension "AccuRev" is now active.');
    let folder: string = vscode.env.appRoot;
    let scm: vscode.SourceControl | undefined;
    if (vscode.workspace.workspaceFolders) {
        let rootUri = vscode.workspace.workspaceFolders[0].uri;
        scm = vscode.scm.createSourceControl("accurev", "AccuRev", rootUri);
        folder = rootUri.fsPath;
    }

    const repo = new AccuRevRepo(getOutputChannel(), folder);
    if (scm) {
        scm.quickDiffProvider = repo;
        let modified = scm.createResourceGroup("modified", "Modified");
        repo.getResourceStates().then((result) => {
            modified.resourceStates = result;
        });
        context.subscriptions.push(modified);
    }

    // The command has been defined in the package.json file
    // Now provide the implementation of the command with registerCommand
    // The commandId parameter must match the command field in package.json
    let disposable = vscode.commands.registerCommand('accurev.refresh', () => {
        // The code you place here will be executed every time your command is executed

        // Display a message box to the user
        getOutputChannel().appendLine('Hello World!');
        repo.getPending();
    });

repository.ts

export class AccuRevRepo {
[...]
    public provideOriginalResource?(uri: vscode.Uri, token: vscode.CancellationToken): vscode.ProviderResult<vscode.Uri> {
        return this.provideOriginalResourceAsync(uri);
    }

    public async provideOriginalResourceAsync(uri: vscode.Uri): Promise<vscode.Uri | null> {
        let originalText = await this.execute(`cat -v ${this.basisName} \"${uri.fsPath}\"`);
        let tempExists = await new Promise<boolean>((resolve) => {
[...]

I'm getting a proper listing of files in the source control view, but when I click on one nothing happens. I expected to be able to put a breakpoint in provideOriginalResource and stop there, but nothing happens. How do I implement the ability to show differences with latest checked in files -- where do I hook into the API?

Upvotes: 2

Views: 916

Answers (1)

BlueMonkMN
BlueMonkMN

Reputation: 25601

QuickDiff does not involve the source control panel, but rather applies when any source-controlled file is displayed in the editor. Therefore you will not see any quick-diff relevant code executing as a result of selecting files in the source control view, but rather when activating a different file in the editor. QuickDiff information appears as colored bars on the left side of the source code indicating what code has changed versus the version in source control:

Source code with a bluish bar on the left

The same provideOriginalResource function used with QuickDiff, however, can be used for the functionality mentioned in the question (clicking on a file in the source control view to show differences). Firstly, you would need to define a command that can be referenced to activate this behavior in the package.json contributes section in the commands block:

{
    "command": "accurev.openDiffBasis",
    "category": "AccuRev",
    "title": "Open diff with basis",
    "icon": {
        "dark": "icons/dark/undo2.svg",
        "light": "icons/light/undo2.svg"
    }
},

Then you'd need to register the command, often done from extension.ts with code like this:

let diff = vscode.commands.registerCommand('accurev.openDiffBasis', async (file: vscode.Uri) => {
    try {
        let original = await repo.provideOriginalResource(file);
        if (original !== null) {
            let filename = vscode.workspace.asRelativePath(file);
            vscode.commands.executeCommand('vscode.diff', original, file,  `${repo.basisName}\\${filename} ↔ ${filename}`);
        }
    }
    catch(err) {
        getOutputChannel().appendLine(err);
    }
});

Note that provideOriginalResource is used here, the same function that QuickDiff implicitly calls. Also note that calling the vscode.diff command is what actually presents the diff viewer, and can be done in response to any action - it's not just an implicit reaction.

Finally, the items returned by getResourceStates need to implement the SourceControlResourceState interface, which allows the command to be linked to each one. This is where a diff command can be linked to the selection of each item:

export class AccuRevFile implements vscode.SourceControlResourceState {
    readonly resourceUri: vscode.Uri;
    readonly command?: vscode.Command | undefined;
    readonly decorations?: vscode.SourceControlResourceDecorations | undefined;
    public readonly elementId: number;

    constructor(uri: vscode.Uri, elementId: number, state: AccuRevState) {
        this.resourceUri = uri;
        this.decorations = state;
        this.command = { title: "diff", command: "accurev.openDiffBasis", tooltip: "Diff against basis version", arguments: [uri]};
        this.elementId = elementId;
    }
}

Upvotes: 2

Related Questions