Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Using custom file system provider over content provider #1053

Merged
merged 8 commits into from
Aug 25, 2020
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
2,558 changes: 1,284 additions & 1,274 deletions package.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ export enum Operation {
Status = "Status",
StatusRemote = "StatusRemote",
SwitchBranch = "SwitchBranch",
Update = "Update"
Update = "Update",
List = "List"
}

export interface ISvnResourceGroup extends SourceControlResourceGroup {
Expand Down
4 changes: 2 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ import { RepoLogProvider } from "./historyView/repoLogProvider";
import * as messages from "./messages";
import { SourceControlManager } from "./source_control_manager";
import { Svn } from "./svn";
import { SvnContentProvider } from "./svnContentProvider";
import { SvnFinder } from "./svnFinder";
import SvnProvider from "./treeView/dataProviders/svnProvider";
import { toDisposable } from "./util";
import { BranchChangesProvider } from "./historyView/branchChangesProvider";
import { IsSvn19orGreater } from "./contexts/isSvn19orGreater";
import { IsSvn18orGreater } from "./contexts/isSvn18orGreater";
import { tempSvnFs } from "./temp_svn_fs";
import { SvnFileSystemProvider } from "./svnFileSystemProvider";

async function init(
_context: ExtensionContext,
Expand All @@ -46,7 +46,7 @@ async function init(
disposables.push(
sourceControlManager,
tempSvnFs,
new SvnContentProvider(sourceControlManager),
new SvnFileSystemProvider(sourceControlManager),
new SvnProvider(sourceControlManager),
new RepoLogProvider(sourceControlManager),
new ItemLogProvider(sourceControlManager),
Expand Down
38 changes: 37 additions & 1 deletion src/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ import {
SvnDepth,
SvnUriAction,
ISvnPathChange,
IStoredAuth
IStoredAuth,
ISvnListItem
} from "./common/types";
import { debounce, globalSequentialize, memoize, throttle } from "./decorators";
import { exists } from "./fs";
Expand Down Expand Up @@ -754,6 +755,15 @@ export class Repository implements IRemoteRepository {
});
}

public async showBuffer(
filePath: string | Uri,
revision?: string
): Promise<Buffer> {
return this.run<Buffer>(Operation.Show, () => {
return this.repository.showBuffer(filePath, revision);
});
}

public async addFiles(files: string[]) {
return this.run(Operation.Add, () => this.repository.addFiles(files));
}
Expand Down Expand Up @@ -837,6 +847,10 @@ export class Repository implements IRemoteRepository {
return this.run(Operation.Patch, () => this.repository.patch(files));
}

public async patchBuffer(files: string[]) {
return this.run(Operation.Patch, () => this.repository.patchBuffer(files));
}

public async patchChangelist(changelistName: string) {
return this.run(Operation.Patch, () =>
this.repository.patchChangelist(changelistName)
Expand All @@ -853,18 +867,34 @@ export class Repository implements IRemoteRepository {
return this.run(Operation.Log, () => this.repository.plainLog());
}

public async plainLogBuffer() {
return this.run(Operation.Log, () => this.repository.plainLogBuffer());
}

public async plainLogByRevision(revision: number) {
return this.run(Operation.Log, () =>
this.repository.plainLogByRevision(revision)
);
}

public async plainLogByRevisionBuffer(revision: number) {
return this.run(Operation.Log, () =>
this.repository.plainLogByRevisionBuffer(revision)
);
}

public async plainLogByText(search: string) {
return this.run(Operation.Log, () =>
this.repository.plainLogByText(search)
);
}

public async plainLogByTextBuffer(search: string) {
return this.run(Operation.Log, () =>
this.repository.plainLogByTextBuffer(search)
);
}

public async log(
rfrom: string,
rto: string,
Expand Down Expand Up @@ -922,6 +952,12 @@ export class Repository implements IRemoteRepository {
);
}

public async list(filePath: string): Promise<ISvnListItem[]> {
return this.run<ISvnListItem[]>(Operation.List, () => {
return this.repository.ls(filePath);
});
}

public getPathNormalizer(): PathNormalizer {
return new PathNormalizer(this.repository.info);
}
Expand Down
30 changes: 29 additions & 1 deletion src/source_control_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ import {
IDisposable,
isDescendant,
isSvnFolder,
normalizePath
normalizePath,
eventToPromise
} from "./util";
import { matchAll } from "./util/globMatch";

type State = "uninitialized" | "initialized";

export class SourceControlManager implements IDisposable {
private _onDidOpenRepository = new EventEmitter<Repository>();
public readonly onDidOpenRepository: Event<Repository> = this
Expand All @@ -60,6 +63,29 @@ export class SourceControlManager implements IDisposable {

private configurationChangeDisposable: Disposable;

private _onDidChangeState = new EventEmitter<State>();
readonly onDidchangeState = this._onDidChangeState.event;

private _state: State = "uninitialized";
get state(): State {
return this._state;
}

setState(state: State): void {
this._state = state;
this._onDidChangeState.fire(state);
}

get isInitialized(): Promise<void> {
if (this._state === "initialized") {
return Promise.resolve();
}

return eventToPromise(
filterEvent(this.onDidchangeState, s => s === "initialized")
) as Promise<any>;
}

get repositories(): Repository[] {
return this.openRepositories.map(r => r.repository);
}
Expand Down Expand Up @@ -149,6 +175,8 @@ export class SourceControlManager implements IDisposable {
this.disposables
);

this.setState("initialized");

await this.scanWorkspaceFolders();
}

Expand Down
111 changes: 111 additions & 0 deletions src/svn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ export function cpErrorHandler(
};
}

export interface BufferResult {
exitCode: number;
stdout: Buffer;
stderr: string;
}

export class Svn {
public version: string;

Expand Down Expand Up @@ -223,6 +229,111 @@ export class Svn {
return { exitCode, stdout: decodedStdout, stderr };
}

public async execBuffer(
cwd: string,
args: any[],
options: ICpOptions = {}
): Promise<BufferResult> {
if (cwd) {
this.lastCwd = cwd;
options.cwd = cwd;
}

if (options.log !== false) {
const argsOut = args.map(arg => (/ |^$/.test(arg) ? `'${arg}'` : arg));
this.logOutput(
`[${this.lastCwd.split(/[\\\/]+/).pop()}]$ svn ${argsOut.join(" ")}\n`
);
}

if (options.username) {
args.push("--username", options.username);
}
if (options.password) {
args.push("--password", options.password);
}

if (options.username || options.password) {
// Configuration format: FILE:SECTION:OPTION=[VALUE]
// Disable password store
args.push("--config-option", "config:auth:password-stores=");
// Disable store auth credentials
args.push("--config-option", "servers:global:store-auth-creds=no");
}

// Force non interactive environment
args.push("--non-interactive");

const defaults: cp.SpawnOptions = {
env: proc.env
};
if (cwd) {
defaults.cwd = cwd;
}

defaults.env = Object.assign({}, proc.env, options.env || {}, {
LC_ALL: "en_US.UTF-8",
LANG: "en_US.UTF-8"
});

const process = cp.spawn(this.svnPath, args, defaults);

const disposables: IDisposable[] = [];

const once = (
ee: NodeJS.EventEmitter,
name: string,
fn: (...args: any[]) => void
) => {
ee.once(name, fn);
disposables.push(toDisposable(() => ee.removeListener(name, fn)));
};

const on = (
ee: NodeJS.EventEmitter,
name: string,
fn: (...args: any[]) => void
) => {
ee.on(name, fn);
disposables.push(toDisposable(() => ee.removeListener(name, fn)));
};

const [exitCode, stdout, stderr] = await Promise.all<any>([
new Promise<number>((resolve, reject) => {
once(process, "error", reject);
once(process, "exit", resolve);
}),
new Promise<Buffer>(resolve => {
const buffers: Buffer[] = [];
on(process.stdout as Readable, "data", (b: Buffer) => buffers.push(b));
once(process.stdout as Readable, "close", () =>
resolve(Buffer.concat(buffers))
);
}),
new Promise<string>(resolve => {
const buffers: Buffer[] = [];
on(process.stderr as Readable, "data", (b: Buffer) => buffers.push(b));
once(process.stderr as Readable, "close", () =>
resolve(Buffer.concat(buffers).toString())
);
})
]);

dispose(disposables);

if (options.log !== false && stderr.length > 0) {
const name = this.lastCwd.split(/[\\\/]+/).pop();
const err = stderr
.split("\n")
.filter((line: string) => line)
.map((line: string) => `[${name}]$ ${line}`)
.join("\n");
this.logOutput(err);
}

return { exitCode, stdout, stderr };
}

public async getRepositoryRoot(path: string) {
try {
const result = await this.exec(path, ["info", "--xml"]);
Expand Down
Loading