Skip to content

Commit 4ceeafe

Browse files
authored
fix: Using custom file system provider over content provider (#1053)
1 parent 8d0da6b commit 4ceeafe

16 files changed

+1852
-1499
lines changed

package.json

+1,284-1,274
Large diffs are not rendered by default.

src/common/types.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ export enum Operation {
121121
Status = "Status",
122122
StatusRemote = "StatusRemote",
123123
SwitchBranch = "SwitchBranch",
124-
Update = "Update"
124+
Update = "Update",
125+
List = "List"
125126
}
126127

127128
export interface ISvnResourceGroup extends SourceControlResourceGroup {

src/extension.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ import { RepoLogProvider } from "./historyView/repoLogProvider";
1717
import * as messages from "./messages";
1818
import { SourceControlManager } from "./source_control_manager";
1919
import { Svn } from "./svn";
20-
import { SvnContentProvider } from "./svnContentProvider";
2120
import { SvnFinder } from "./svnFinder";
2221
import SvnProvider from "./treeView/dataProviders/svnProvider";
2322
import { toDisposable } from "./util";
2423
import { BranchChangesProvider } from "./historyView/branchChangesProvider";
2524
import { IsSvn19orGreater } from "./contexts/isSvn19orGreater";
2625
import { IsSvn18orGreater } from "./contexts/isSvn18orGreater";
2726
import { tempSvnFs } from "./temp_svn_fs";
27+
import { SvnFileSystemProvider } from "./svnFileSystemProvider";
2828

2929
async function init(
3030
_context: ExtensionContext,
@@ -46,7 +46,7 @@ async function init(
4646
disposables.push(
4747
sourceControlManager,
4848
tempSvnFs,
49-
new SvnContentProvider(sourceControlManager),
49+
new SvnFileSystemProvider(sourceControlManager),
5050
new SvnProvider(sourceControlManager),
5151
new RepoLogProvider(sourceControlManager),
5252
new ItemLogProvider(sourceControlManager),

src/repository.ts

+37-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ import {
2626
SvnDepth,
2727
SvnUriAction,
2828
ISvnPathChange,
29-
IStoredAuth
29+
IStoredAuth,
30+
ISvnListItem
3031
} from "./common/types";
3132
import { debounce, globalSequentialize, memoize, throttle } from "./decorators";
3233
import { exists } from "./fs";
@@ -754,6 +755,15 @@ export class Repository implements IRemoteRepository {
754755
});
755756
}
756757

758+
public async showBuffer(
759+
filePath: string | Uri,
760+
revision?: string
761+
): Promise<Buffer> {
762+
return this.run<Buffer>(Operation.Show, () => {
763+
return this.repository.showBuffer(filePath, revision);
764+
});
765+
}
766+
757767
public async addFiles(files: string[]) {
758768
return this.run(Operation.Add, () => this.repository.addFiles(files));
759769
}
@@ -837,6 +847,10 @@ export class Repository implements IRemoteRepository {
837847
return this.run(Operation.Patch, () => this.repository.patch(files));
838848
}
839849

850+
public async patchBuffer(files: string[]) {
851+
return this.run(Operation.Patch, () => this.repository.patchBuffer(files));
852+
}
853+
840854
public async patchChangelist(changelistName: string) {
841855
return this.run(Operation.Patch, () =>
842856
this.repository.patchChangelist(changelistName)
@@ -853,18 +867,34 @@ export class Repository implements IRemoteRepository {
853867
return this.run(Operation.Log, () => this.repository.plainLog());
854868
}
855869

870+
public async plainLogBuffer() {
871+
return this.run(Operation.Log, () => this.repository.plainLogBuffer());
872+
}
873+
856874
public async plainLogByRevision(revision: number) {
857875
return this.run(Operation.Log, () =>
858876
this.repository.plainLogByRevision(revision)
859877
);
860878
}
861879

880+
public async plainLogByRevisionBuffer(revision: number) {
881+
return this.run(Operation.Log, () =>
882+
this.repository.plainLogByRevisionBuffer(revision)
883+
);
884+
}
885+
862886
public async plainLogByText(search: string) {
863887
return this.run(Operation.Log, () =>
864888
this.repository.plainLogByText(search)
865889
);
866890
}
867891

892+
public async plainLogByTextBuffer(search: string) {
893+
return this.run(Operation.Log, () =>
894+
this.repository.plainLogByTextBuffer(search)
895+
);
896+
}
897+
868898
public async log(
869899
rfrom: string,
870900
rto: string,
@@ -922,6 +952,12 @@ export class Repository implements IRemoteRepository {
922952
);
923953
}
924954

955+
public async list(filePath: string): Promise<ISvnListItem[]> {
956+
return this.run<ISvnListItem[]>(Operation.List, () => {
957+
return this.repository.ls(filePath);
958+
});
959+
}
960+
925961
public getPathNormalizer(): PathNormalizer {
926962
return new PathNormalizer(this.repository.info);
927963
}

src/source_control_manager.ts

+29-1
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,13 @@ import {
3030
IDisposable,
3131
isDescendant,
3232
isSvnFolder,
33-
normalizePath
33+
normalizePath,
34+
eventToPromise
3435
} from "./util";
3536
import { matchAll } from "./util/globMatch";
3637

38+
type State = "uninitialized" | "initialized";
39+
3740
export class SourceControlManager implements IDisposable {
3841
private _onDidOpenRepository = new EventEmitter<Repository>();
3942
public readonly onDidOpenRepository: Event<Repository> = this
@@ -60,6 +63,29 @@ export class SourceControlManager implements IDisposable {
6063

6164
private configurationChangeDisposable: Disposable;
6265

66+
private _onDidChangeState = new EventEmitter<State>();
67+
readonly onDidchangeState = this._onDidChangeState.event;
68+
69+
private _state: State = "uninitialized";
70+
get state(): State {
71+
return this._state;
72+
}
73+
74+
setState(state: State): void {
75+
this._state = state;
76+
this._onDidChangeState.fire(state);
77+
}
78+
79+
get isInitialized(): Promise<void> {
80+
if (this._state === "initialized") {
81+
return Promise.resolve();
82+
}
83+
84+
return eventToPromise(
85+
filterEvent(this.onDidchangeState, s => s === "initialized")
86+
) as Promise<any>;
87+
}
88+
6389
get repositories(): Repository[] {
6490
return this.openRepositories.map(r => r.repository);
6591
}
@@ -149,6 +175,8 @@ export class SourceControlManager implements IDisposable {
149175
this.disposables
150176
);
151177

178+
this.setState("initialized");
179+
152180
await this.scanWorkspaceFolders();
153181
}
154182

src/svn.ts

+111
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ export function cpErrorHandler(
5858
};
5959
}
6060

61+
export interface BufferResult {
62+
exitCode: number;
63+
stdout: Buffer;
64+
stderr: string;
65+
}
66+
6167
export class Svn {
6268
public version: string;
6369

@@ -223,6 +229,111 @@ export class Svn {
223229
return { exitCode, stdout: decodedStdout, stderr };
224230
}
225231

232+
public async execBuffer(
233+
cwd: string,
234+
args: any[],
235+
options: ICpOptions = {}
236+
): Promise<BufferResult> {
237+
if (cwd) {
238+
this.lastCwd = cwd;
239+
options.cwd = cwd;
240+
}
241+
242+
if (options.log !== false) {
243+
const argsOut = args.map(arg => (/ |^$/.test(arg) ? `'${arg}'` : arg));
244+
this.logOutput(
245+
`[${this.lastCwd.split(/[\\\/]+/).pop()}]$ svn ${argsOut.join(" ")}\n`
246+
);
247+
}
248+
249+
if (options.username) {
250+
args.push("--username", options.username);
251+
}
252+
if (options.password) {
253+
args.push("--password", options.password);
254+
}
255+
256+
if (options.username || options.password) {
257+
// Configuration format: FILE:SECTION:OPTION=[VALUE]
258+
// Disable password store
259+
args.push("--config-option", "config:auth:password-stores=");
260+
// Disable store auth credentials
261+
args.push("--config-option", "servers:global:store-auth-creds=no");
262+
}
263+
264+
// Force non interactive environment
265+
args.push("--non-interactive");
266+
267+
const defaults: cp.SpawnOptions = {
268+
env: proc.env
269+
};
270+
if (cwd) {
271+
defaults.cwd = cwd;
272+
}
273+
274+
defaults.env = Object.assign({}, proc.env, options.env || {}, {
275+
LC_ALL: "en_US.UTF-8",
276+
LANG: "en_US.UTF-8"
277+
});
278+
279+
const process = cp.spawn(this.svnPath, args, defaults);
280+
281+
const disposables: IDisposable[] = [];
282+
283+
const once = (
284+
ee: NodeJS.EventEmitter,
285+
name: string,
286+
fn: (...args: any[]) => void
287+
) => {
288+
ee.once(name, fn);
289+
disposables.push(toDisposable(() => ee.removeListener(name, fn)));
290+
};
291+
292+
const on = (
293+
ee: NodeJS.EventEmitter,
294+
name: string,
295+
fn: (...args: any[]) => void
296+
) => {
297+
ee.on(name, fn);
298+
disposables.push(toDisposable(() => ee.removeListener(name, fn)));
299+
};
300+
301+
const [exitCode, stdout, stderr] = await Promise.all<any>([
302+
new Promise<number>((resolve, reject) => {
303+
once(process, "error", reject);
304+
once(process, "exit", resolve);
305+
}),
306+
new Promise<Buffer>(resolve => {
307+
const buffers: Buffer[] = [];
308+
on(process.stdout as Readable, "data", (b: Buffer) => buffers.push(b));
309+
once(process.stdout as Readable, "close", () =>
310+
resolve(Buffer.concat(buffers))
311+
);
312+
}),
313+
new Promise<string>(resolve => {
314+
const buffers: Buffer[] = [];
315+
on(process.stderr as Readable, "data", (b: Buffer) => buffers.push(b));
316+
once(process.stderr as Readable, "close", () =>
317+
resolve(Buffer.concat(buffers).toString())
318+
);
319+
})
320+
]);
321+
322+
dispose(disposables);
323+
324+
if (options.log !== false && stderr.length > 0) {
325+
const name = this.lastCwd.split(/[\\\/]+/).pop();
326+
const err = stderr
327+
.split("\n")
328+
.filter((line: string) => line)
329+
.map((line: string) => `[${name}]$ ${line}`)
330+
.join("\n");
331+
this.logOutput(err);
332+
}
333+
334+
return { exitCode, stdout, stderr };
335+
}
336+
226337
public async getRepositoryRoot(path: string) {
227338
try {
228339
const result = await this.exec(path, ["info", "--xml"]);

0 commit comments

Comments
 (0)