CompareFolders — a Visual Studio Code extension journey — Part II

September 12, 2019

In the last post, we talked about the plugin, the requirements and the motivations. Like promised, the post will includes an explanation about the product challenge and how to solve it.

The Challenge

In order to let the users to compare folders, we need to allow them to run a command that will open the “choose a folder” dialog so they could choose a folder from their file system. Once they chosen, the plugin will run the code that compare them. So:

How to add a command to commands palette in vscode extension?

Basically it well explained in the docs so let’s do it quick

First, we need to add a entity in package.json root

1"contributes": {
2 "commands": [
3 {
4 "command": "",
5 "title": "Compare Folders"
6 }
7 ]

Second, the command need to be registered during the plugin activation (extension.ts).

1export function activate(context: vscode.ExtensionContext) {
2 let disposable = vscode.commands.registerCommand('', () => {
3 compare(); // we'll get this later
4 });
6 context.subscriptions.push(disposable);

Notice that the command in the package.json and the one which sent to registerCommand have to be the same.

Now, when users will open the command palette they will get this (if not, they will need to start typing

Compare Folders command in the command palette Compare Folders command in the command palette

Now that plugin have an “entry point” followed by a user interaction, we need another piece in the puzzle, let the users to choose a folder.

How to let users to choose a folder in vscode extension?

The API for opening the file system is window.showOpenDialog (docs). It takes an “options” object as single argument and returns Thenable (Something like Promise). By default it allows to choose only files. That can be changes by passing canSelectFolder: true in the options object.

1const options: vscode.OpenDialogOptions = {
2 canSelectMany: false,
3 canSelectFolders: true,
6vscode.window.showOpenDialog(options).then(fileUri => {
7 if (fileUri && fileUri[0]) {
8 console.log(fileUri[0].fsPath);
9 }

The last part for this post is how to, eventually, compare the folders and display the diff in a diff view?

How to compare folders by content?

The extension uses the dir-compare package for this. Here is the code that does it (with comments)

1// open folder picker dialog to choose first folder
2const folder1 = await openFolder();
4// open folder picker dialog to choose second folder
5const folder2 = await openFolder();
7// compare folders by contents
8const options = {compareContent: true};
10// do the compare
11const res = compareSync(
12 folder1,
13 folder2,
14 options
17// get the diffs
18const { diffSet = [] } = res;
20// diffSet contains all the files and filter only the not equals files and map them to pairs of Uris
21const diffs = diffSet
22 .filter(diff => diff.state === 'distinct')
23 .map(diff => [`${diff.path1}/${diff.name1}`, `${diff.path2}/${diff.name2}`]);

And finally

How to ask vscode to display a “compare” view?

Using the vscode command vscode.diff the extension opens a diff view with the contents of both of the files.

1await vscode.commands.executeCommand('vscode.diff',
2 vscode.Uri.file(diffs[0][0]),
3 vscode.Uri.file(diffs[0][1]),
4 'My great Diff'

Notice that currently, it takes only the first pair. This code meant to simplify the explanation and to show only how to use the package and the diff API. In the next post, I’ll show the real approach that present a list of all the changes and let the user to pick them one by one.

Source code for this post

Have something to say? I’ll love to 👂

Original Post