Skip to content

Commit 3e32dac

Browse files
hoovercjstef-levesque
authored andcommitted
Compatibility mode for Source Depot (#48)
* Login isn't needed for Source Depot * Source Depot doesn't support ztags * Source Depot doesn't support the -T flag for fstat
1 parent bdb1ef1 commit 3e32dac

File tree

6 files changed

+48
-37
lines changed

6 files changed

+48
-37
lines changed

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,15 @@
6262
"type":"string",
6363
"default":"none",
6464
"description":"Configure a path to p4 or an alternate command if needed"
65+
},
66+
"perforce.compatibilityMode": {
67+
"type": "string",
68+
"default": "perforce",
69+
"enum": [
70+
"perforce",
71+
"sourcedepot"
72+
],
73+
"description": "Specify if we should run in compatibility mode, currently support 'perforce' and 'sourcedepot'"
6574
}
6675
}
6776
},

src/ContentProvider.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@ export class PerforceContentProvider {
99
private disposables: Disposable[] = [];
1010
dispose(): void { this.disposables.forEach(d => d.dispose()); }
1111

12-
constructor() {
12+
private compatibilityMode: string;
13+
14+
constructor(compatibilityMode: string) {
15+
this.compatibilityMode = compatibilityMode;
1316
this.disposables.push(
1417
workspace.registerTextDocumentContentProvider('perforce', this),
1518
);
1619
}
1720

1821
public provideTextDocumentContent(uri: Uri): Promise<string> {
19-
return Utils.isLoggedIn().then(value => {
22+
return Utils.isLoggedIn(this.compatibilityMode).then(value => {
2023
if (!value) {
2124
return '';
2225
}

src/ScmProvider.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { Status } from './scm/Status';
55
import * as Path from 'path';
66

77
export class PerforceSCMProvider {
8+
private compatibilityMode: string;
9+
810
private disposables: Disposable[] = [];
911
dispose(): void {
1012
this.disposables.forEach(d => d.dispose());
@@ -38,12 +40,13 @@ export class PerforceSCMProvider {
3840
return 'idle'
3941
}
4042

41-
constructor() {
43+
constructor(compatibilityMode: string) {
44+
this.compatibilityMode = compatibilityMode;
4245
this.Initialize();
4346
}
4447

4548
public Initialize() {
46-
this._model = new Model();
49+
this._model = new Model(this.compatibilityMode);
4750
// Hook up the model change event to trigger our own event
4851
this._model.onDidChange((groups: SourceControlResourceGroup[]) => this._onDidChange.fire(this));
4952
this._model.Refresh();

src/Utils.ts

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import { PerforceService } from './PerforceService';
55

66
export namespace Utils
77
{
8-
const ztagRegex = /^\.\.\.\s+(\w+)\s+(.+)/;
9-
108
export function normalizePath(path: string): string
119
{
1210
var normalizedPath = path;
@@ -29,14 +27,13 @@ export namespace Utils
2927
return path.replace('%', '%25').replace('*', '%2A').replace('#', '%23').replace('@', '%40');
3028
}
3129

32-
// process output from a p4 command executed with -ztag
33-
function processZtag(output): Map<string, string> {
30+
export function processInfo(output): Map<string, string> {
3431
const map = new Map<string, string>();
3532
const lines = output.trim().split('\n');
3633

3734
for (let i = 0, n = lines.length; i < n; ++i) {
38-
// ... key value
39-
const matches = lines[i].match(/\.\.\.\s(.[\w-]+)\s(.+)/);
35+
// Property Name: Property Value
36+
const matches = lines[i].match(/([^:]+): (.+)/);
4037

4138
if (matches) {
4239
map.set(matches[1], matches[2]);
@@ -47,19 +44,13 @@ export namespace Utils
4744
return map;
4845
}
4946

50-
// Get a map containing the keys and values of the command
51-
export function getZtag(command: string, file?: Uri | string, revision?: number, prefixArgs?: string): Promise<Map<string, string>> {
47+
export function isLoggedIn(compatibilityMode: string) : Promise<boolean> {
5248
return new Promise((resolve, reject) => {
53-
getOutput(command, file, revision, prefixArgs, '-ztag').then(output => {
54-
const map = processZtag(output);
55-
resolve( map );
56-
});
57-
});
58-
59-
}
49+
if(compatibilityMode === 'sourcedepot') {
50+
resolve(true);
51+
return;
52+
}
6053

61-
export function isLoggedIn() : Promise<boolean> {
62-
return new Promise((resolve, reject) => {
6354
PerforceService.execute('login', (err, stdout, stderr) => {
6455
if (err) {
6556
resolve(false);

src/extension.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
import { ExtensionContext } from 'vscode';
3+
import { ExtensionContext, workspace } from 'vscode';
44

55
import { PerforceCommands } from './PerforceCommands';
66
import { PerforceContentProvider } from './ContentProvider';
@@ -9,10 +9,12 @@ import { PerforceSCMProvider } from './ScmProvider';
99
import { Display } from './Display';
1010

1111
export function activate(ctx: ExtensionContext) : void {
12+
const compatibilityMode = workspace.getConfiguration('perforce').get('compatibilityMode', 'perforce');
13+
1214
//Register all commands
1315
PerforceCommands.registerCommands();
1416
Display.initialize();
15-
ctx.subscriptions.push(new PerforceContentProvider());
17+
ctx.subscriptions.push(new PerforceContentProvider(compatibilityMode));
1618
ctx.subscriptions.push(new FileSystemListener());
17-
ctx.subscriptions.push( new PerforceSCMProvider() );
19+
ctx.subscriptions.push( new PerforceSCMProvider(compatibilityMode));
1820
}

src/scm/Model.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export class Model implements Disposable {
3131
private _defaultGroup: SourceControlResourceGroup;
3232
private _pendingGroups = new Map<number, { description: string, group: SourceControlResourceGroup }>();
3333
private _shelvedGroups = new Map<number, { description: string, group: SourceControlResourceGroup }>();
34+
private _compatibilityMode: string;
3435

3536
public get ResourceGroups(): SourceControlResourceGroup[] {
3637
const result: SourceControlResourceGroup[] = [];
@@ -49,10 +50,12 @@ export class Model implements Disposable {
4950
return result;
5051
}
5152

52-
public constructor() {}
53+
public constructor(compatibilityMode: string) {
54+
this._compatibilityMode = compatibilityMode;
55+
}
5356

5457
public async Sync(): Promise<void> {
55-
const loggedin = await Utils.isLoggedIn();
58+
const loggedin = await Utils.isLoggedIn(this._compatibilityMode);
5659
if (!loggedin) {
5760
return;
5861
}
@@ -62,8 +65,7 @@ export class Model implements Disposable {
6265

6366
public async Refresh(): Promise<void> {
6467
this.clean();
65-
66-
const loggedin = await Utils.isLoggedIn();
68+
const loggedin = await Utils.isLoggedIn(this._compatibilityMode);
6769
if (!loggedin) {
6870
return;
6971
}
@@ -190,7 +192,7 @@ export class Model implements Disposable {
190192
}
191193

192194
public async ReopenFile(input: Resource): Promise<void> {
193-
const loggedin = await Utils.isLoggedIn();
195+
const loggedin = await Utils.isLoggedIn(this._compatibilityMode);
194196
if (!loggedin) {
195197
return;
196198
}
@@ -241,13 +243,11 @@ export class Model implements Disposable {
241243
}
242244

243245
private async updateInfo(): Promise<void> {
244-
this._infos = await Utils.getZtag('info');
245-
246+
this._infos = await Utils.processInfo(await Utils.getOutput('info'));
246247
}
247248

248249
private async updateStatus(): Promise<void> {
249-
250-
const loggedin = await Utils.isLoggedIn();
250+
const loggedin = await Utils.isLoggedIn(this._compatibilityMode);
251251
if (!loggedin) {
252252
return;
253253
}
@@ -259,7 +259,7 @@ export class Model implements Disposable {
259259
this._defaultGroup = this._sourceControl.createResourceGroup('default', 'Default Changelist');
260260
this._pendingGroups.clear(); // dispose ?
261261

262-
const pendingArgs = '-c ' + this._infos.get('clientName') + ' -s pending';
262+
const pendingArgs = '-c ' + this._infos.get('Client name') + ' -s pending';
263263
var output: string = await Utils.getOutput('changes', null, null, pendingArgs);
264264
output.trim().split('\n').forEach( (value) => {
265265
// Change num on date by user@client [status] description
@@ -310,10 +310,13 @@ export class Model implements Disposable {
310310
const change = matches[4];
311311
const type = matches[5];
312312

313-
const output: string = await Utils.getOutput('fstat', depotFile, null, '-T clientFile');
313+
// fstat -T isn't supported in Source Depot
314+
const output: string = await Utils.getOutput('fstat', depotFile, null);
315+
316+
const filteredOutput = output.trim().split('\n').filter((line) => line.startsWith('... clientFile '));
314317

315-
if (output.indexOf('... clientFile ') === 0) {
316-
const clientFile = output.substring(15, output.indexOf('\n')).trim();
318+
if (filteredOutput.length === 1) {
319+
const clientFile = filteredOutput[0].substring(15).trim();
317320
const uri = Uri.file(clientFile);
318321
const resource: Resource = new Resource(uri, change, action);
319322

0 commit comments

Comments
 (0)