mirror of
https://github.com/facebook/sapling.git
synced 2024-10-05 14:28:17 +03:00
Make list of VSCodeRepos into a class
Summary: In the next diff, we need to be able to lookup the repository for a given path in the vscode extension. Let's make this function that was managing VSCodeRepo creation into a class that keeps a list that we can query from. Reviewed By: quark-zju Differential Revision: D47385149 fbshipit-source-id: c90934162d552b14f716782da62168f7c258dcc8
This commit is contained in:
parent
124062ab92
commit
737705df1b
@ -19,55 +19,79 @@ import {repositoryCache} from 'isl-server/src/RepositoryCache';
|
||||
import {ComparisonType} from 'shared/Comparison';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
/**
|
||||
* Construct Repositories and VSCodeRepos for every workspace folder.
|
||||
* Treats repositoryCache as the source of truth for re-using repositories.
|
||||
*/
|
||||
export function watchAndCreateRepositoriesForWorkspaceFolders(logger: Logger): vscode.Disposable {
|
||||
const knownRepos = new Map<string, RepositoryReference>();
|
||||
const vscodeRepos = new Map<string, VSCodeRepo>();
|
||||
function updateRepos(
|
||||
export class VSCodeReposList {
|
||||
private knownRepos = new Map</* attached folder root */ string, RepositoryReference>();
|
||||
private vscodeRepos = new Map</* repo root path */ string, VSCodeRepo>();
|
||||
private disposables: Array<vscode.Disposable> = [];
|
||||
|
||||
private reposByPath = new Map</* arbitrary subpath of repo */ string, VSCodeRepo>();
|
||||
|
||||
constructor(private logger: Logger) {
|
||||
if (vscode.workspace.workspaceFolders) {
|
||||
this.updateRepos(vscode.workspace.workspaceFolders, []);
|
||||
}
|
||||
this.disposables.push(
|
||||
vscode.workspace.onDidChangeWorkspaceFolders(e => {
|
||||
this.updateRepos(e.added, e.removed);
|
||||
}),
|
||||
);
|
||||
// TODO: consider also listening for vscode.workspace.onDidOpenTextDocument to support repos
|
||||
// for ad-hoc non-workspace-folder files
|
||||
}
|
||||
|
||||
private updateRepos(
|
||||
added: ReadonlyArray<vscode.WorkspaceFolder>,
|
||||
removed: ReadonlyArray<vscode.WorkspaceFolder>,
|
||||
) {
|
||||
for (const add of added) {
|
||||
const {fsPath} = add.uri;
|
||||
if (knownRepos.has(fsPath)) {
|
||||
if (this.knownRepos.has(fsPath)) {
|
||||
throw new Error(`Attempted to add workspace folder path twice: ${fsPath}`);
|
||||
}
|
||||
const repoReference = repositoryCache.getOrCreate(getCLICommand(), logger, fsPath);
|
||||
knownRepos.set(fsPath, repoReference);
|
||||
const repoReference = repositoryCache.getOrCreate(getCLICommand(), this.logger, fsPath);
|
||||
this.knownRepos.set(fsPath, repoReference);
|
||||
repoReference.promise.then(repo => {
|
||||
if (repo instanceof Repository) {
|
||||
const root = repo?.info.repoRoot;
|
||||
const existing = vscodeRepos.get(root);
|
||||
const existing = this.vscodeRepos.get(root);
|
||||
if (existing) {
|
||||
return;
|
||||
}
|
||||
const vscodeRepo = new VSCodeRepo(repo, logger);
|
||||
vscodeRepos.set(root, vscodeRepo);
|
||||
const vscodeRepo = new VSCodeRepo(repo, this.logger);
|
||||
this.vscodeRepos.set(root, vscodeRepo);
|
||||
repo.onDidDispose(() => {
|
||||
vscodeRepo.dispose();
|
||||
vscodeRepos.delete(root);
|
||||
this.vscodeRepos.delete(root);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
for (const remove of removed) {
|
||||
const {fsPath} = remove.uri;
|
||||
const repo = knownRepos.get(fsPath);
|
||||
const repo = this.knownRepos.get(fsPath);
|
||||
repo?.unref();
|
||||
knownRepos.delete(fsPath);
|
||||
this.knownRepos.delete(fsPath);
|
||||
}
|
||||
}
|
||||
if (vscode.workspace.workspaceFolders) {
|
||||
updateRepos(vscode.workspace.workspaceFolders, []);
|
||||
|
||||
/** return the VSCodeRepo that contains the given path */
|
||||
public repoForPath(path: string): VSCodeRepo | undefined {
|
||||
if (this.reposByPath.has(path)) {
|
||||
return this.reposByPath.get(path);
|
||||
}
|
||||
for (const value of this.vscodeRepos.values()) {
|
||||
if (path.startsWith(value.rootPath)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
for (const disposable of this.disposables) {
|
||||
disposable.dispose();
|
||||
}
|
||||
}
|
||||
return vscode.workspace.onDidChangeWorkspaceFolders(e => {
|
||||
updateRepos(e.added, e.removed);
|
||||
});
|
||||
// TODO: consider also listening for vscode.workspace.onDidOpenTextDocument to support repos
|
||||
// for ad-hoc non-workspace-folder files
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,11 +105,13 @@ export class VSCodeRepo implements vscode.QuickDiffProvider {
|
||||
'changes' | 'untracked' | 'unresolved' | 'resolved',
|
||||
vscode.SourceControlResourceGroup
|
||||
>;
|
||||
private rootUri: vscode.Uri;
|
||||
public rootUri: vscode.Uri;
|
||||
public rootPath: string;
|
||||
|
||||
constructor(public repo: Repository, private logger: Logger) {
|
||||
repo.onDidDispose(() => this.dispose());
|
||||
this.rootUri = vscode.Uri.file(repo.info.repoRoot);
|
||||
this.rootPath = repo.info.repoRoot;
|
||||
|
||||
this.sourceControl = vscode.scm.createSourceControl(
|
||||
'sapling',
|
||||
@ -226,5 +252,3 @@ const themeColors = {
|
||||
untracked: new vscode.ThemeColor('gitDecoration.untrackedResourceForeground'),
|
||||
conflicting: new vscode.ThemeColor('gitDecoration.conflictingResourceForeground'),
|
||||
};
|
||||
|
||||
export const __TEST__ = {watchAndCreateRepositoriesForWorkspaceFolders};
|
||||
|
@ -9,14 +9,12 @@ import type {Repository} from 'isl-server/src/Repository';
|
||||
import type {Logger} from 'isl-server/src/logger';
|
||||
import type {RepoInfo, ValidatedRepoInfo} from 'isl/src/types';
|
||||
|
||||
import {__TEST__} from '../VSCodeRepo';
|
||||
import {VSCodeReposList} from '../VSCodeRepo';
|
||||
import {repositoryCache} from 'isl-server/src/RepositoryCache';
|
||||
import {TypedEventEmitter} from 'shared/TypedEventEmitter';
|
||||
import {nextTick} from 'shared/testUtils';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
const {watchAndCreateRepositoriesForWorkspaceFolders} = __TEST__;
|
||||
|
||||
const mockLogger: Logger = {info: jest.fn(), warn: jest.fn(), log: jest.fn(), error: jest.fn()};
|
||||
|
||||
jest.mock('isl-server/src/Repository', () => {
|
||||
@ -75,7 +73,7 @@ describe('adding and removing repositories', () => {
|
||||
});
|
||||
|
||||
it('creates repositories for workspace folders', async () => {
|
||||
const dispose = watchAndCreateRepositoriesForWorkspaceFolders(mockLogger);
|
||||
const repos = new VSCodeReposList(mockLogger);
|
||||
foldersEmitter.emit('value', {
|
||||
added: [{name: 'my folder', index: 0, uri: vscode.Uri.file('/path/to/repo1')}],
|
||||
removed: [],
|
||||
@ -83,11 +81,11 @@ describe('adding and removing repositories', () => {
|
||||
await nextTick();
|
||||
|
||||
expect(vscode.scm.createSourceControl).toHaveBeenCalledTimes(1);
|
||||
dispose.dispose();
|
||||
repos.dispose();
|
||||
});
|
||||
|
||||
it('deduplicates among shared repos', async () => {
|
||||
const dispose = watchAndCreateRepositoriesForWorkspaceFolders(mockLogger);
|
||||
const repos = new VSCodeReposList(mockLogger);
|
||||
foldersEmitter.emit('value', {
|
||||
added: [{name: 'my folder', index: 0, uri: vscode.Uri.file('/path/to/repo1/foo')}],
|
||||
removed: [],
|
||||
@ -108,11 +106,11 @@ describe('adding and removing repositories', () => {
|
||||
await nextTick();
|
||||
expect(vscode.scm.createSourceControl).toHaveBeenCalledTimes(2);
|
||||
|
||||
dispose.dispose();
|
||||
repos.dispose();
|
||||
});
|
||||
|
||||
it('deletes repositories for workspace folders', async () => {
|
||||
const dispose = watchAndCreateRepositoriesForWorkspaceFolders(mockLogger);
|
||||
const repos = new VSCodeReposList(mockLogger);
|
||||
|
||||
// add repo twice, only creates 1 repo
|
||||
foldersEmitter.emit('value', {
|
||||
@ -147,6 +145,31 @@ describe('adding and removing repositories', () => {
|
||||
await nextTick();
|
||||
expect(vscode.scm.createSourceControl).toHaveBeenCalledTimes(2);
|
||||
|
||||
dispose.dispose();
|
||||
repos.dispose();
|
||||
});
|
||||
|
||||
it('looks up repos by prefix', async () => {
|
||||
const repos = new VSCodeReposList(mockLogger);
|
||||
|
||||
foldersEmitter.emit('value', {
|
||||
added: [{name: 'my folder', index: 0, uri: vscode.Uri.file('/path/to/repo1/foo')}],
|
||||
removed: [],
|
||||
});
|
||||
await nextTick();
|
||||
|
||||
expect(repos.repoForPath('/path/to/repo1/foo')).not.toBeUndefined();
|
||||
expect(repos.repoForPath('/path/to/repo1/foo/myFile.txt')).not.toBeUndefined();
|
||||
expect(repos.repoForPath('/path/to/repo2/foo')).toBeUndefined();
|
||||
|
||||
foldersEmitter.emit('value', {
|
||||
added: [],
|
||||
removed: [{name: 'my folder', index: 1, uri: vscode.Uri.file('/path/to/repo1/foo')}],
|
||||
});
|
||||
await nextTick();
|
||||
|
||||
expect(repos.repoForPath('/path/to/repo1/foo')).toBeUndefined();
|
||||
expect(repos.repoForPath('/path/to/repo1/foo/myFile.txt')).toBeUndefined();
|
||||
|
||||
repos.dispose();
|
||||
});
|
||||
});
|
||||
|
@ -9,7 +9,7 @@ import type {Logger} from 'isl-server/src/logger';
|
||||
|
||||
import packageJson from '../package.json';
|
||||
import {registerSaplingDiffContentProvider} from './DiffContentProvider';
|
||||
import {watchAndCreateRepositoriesForWorkspaceFolders} from './VSCodeRepo';
|
||||
import {VSCodeReposList} from './VSCodeRepo';
|
||||
import {registerCommands} from './commands';
|
||||
import {ensureTranslationsLoaded} from './i18n';
|
||||
import {registerISLCommands} from './islWebviewPanel';
|
||||
@ -26,7 +26,8 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
await ensureTranslationsLoaded(context);
|
||||
context.subscriptions.push(registerISLCommands(context, logger));
|
||||
context.subscriptions.push(outputChannel);
|
||||
context.subscriptions.push(watchAndCreateRepositoriesForWorkspaceFolders(logger));
|
||||
const reposList = new VSCodeReposList(logger);
|
||||
context.subscriptions.push(reposList);
|
||||
context.subscriptions.push(registerSaplingDiffContentProvider(logger));
|
||||
context.subscriptions.push(...registerCommands(extensionTracker));
|
||||
extensionTracker.track('VSCodeExtensionActivated', {duration: Date.now() - start});
|
||||
|
Loading…
Reference in New Issue
Block a user