提交 24cd191a 编写于 作者: J Joao Moreno

scm: require uris on resources and groups

上级 e52ce128
......@@ -137,7 +137,7 @@ export class CommandCenter {
return resource.original.with({ scheme: 'git', query: 'HEAD' });
case Status.MODIFIED:
return resource.uri.with({ scheme: 'git', query: '~' });
return resource.sourceUri.with({ scheme: 'git', query: '~' });
}
}
......@@ -146,34 +146,34 @@ export class CommandCenter {
case Status.INDEX_MODIFIED:
case Status.INDEX_ADDED:
case Status.INDEX_COPIED:
return resource.uri.with({ scheme: 'git' });
return resource.sourceUri.with({ scheme: 'git' });
case Status.INDEX_RENAMED:
return resource.uri.with({ scheme: 'git' });
return resource.sourceUri.with({ scheme: 'git' });
case Status.INDEX_DELETED:
case Status.DELETED:
return resource.uri.with({ scheme: 'git', query: 'HEAD' });
return resource.sourceUri.with({ scheme: 'git', query: 'HEAD' });
case Status.MODIFIED:
case Status.UNTRACKED:
case Status.IGNORED:
const uriString = resource.uri.toString();
const [indexStatus] = this.model.indexGroup.resources.filter(r => r.uri.toString() === uriString);
const uriString = resource.sourceUri.toString();
const [indexStatus] = this.model.indexGroup.resources.filter(r => r.sourceUri.toString() === uriString);
if (indexStatus && indexStatus.rename) {
return indexStatus.rename;
}
return resource.uri;
return resource.sourceUri;
case Status.BOTH_MODIFIED:
return resource.uri;
return resource.sourceUri;
}
}
private getTitle(resource: Resource): string {
const basename = path.basename(resource.uri.fsPath);
const basename = path.basename(resource.sourceUri.fsPath);
switch (resource.type) {
case Status.INDEX_MODIFIED:
......@@ -274,7 +274,7 @@ export class CommandCenter {
return;
}
return await commands.executeCommand<void>('vscode.open', resource.uri);
return await commands.executeCommand<void>('vscode.open', resource.sourceUri);
}
@command('git.openChange')
......@@ -449,7 +449,7 @@ export class CommandCenter {
}
const message = resources.length === 1
? localize('confirm discard', "Are you sure you want to discard changes in {0}?", path.basename(resources[0].uri.fsPath))
? localize('confirm discard', "Are you sure you want to discard changes in {0}?", path.basename(resources[0].sourceUri.fsPath))
: localize('confirm discard multiple', "Are you sure you want to discard changes in {0} files?", resources.length);
const yes = localize('discard', "Discard Changes");
......@@ -792,9 +792,18 @@ export class CommandCenter {
return undefined;
}
if (uri.scheme === 'scm' && uri.authority === 'git') {
const resource = scm.getResourceFromURI(uri);
return resource instanceof Resource ? resource : undefined;
if (uri.scheme === 'git-resource') {
const {resourceGroupId} = JSON.parse(uri.query) as { resourceGroupId: string, sourceUri: string };
const [resourceGroup] = this.model.resources.filter(g => g.id === resourceGroupId);
if (!resourceGroup) {
return;
}
const uriStr = uri.toString();
const [resource] = resourceGroup.resources.filter(r => r.uri.toString() === uriStr);
return resource;
}
if (uri.scheme === 'git') {
......@@ -804,8 +813,8 @@ export class CommandCenter {
if (uri.scheme === 'file') {
const uriString = uri.toString();
return this.model.workingTreeGroup.resources.filter(r => r.uri.toString() === uriString)[0]
|| this.model.indexGroup.resources.filter(r => r.uri.toString() === uriString)[0];
return this.model.workingTreeGroup.resources.filter(r => r.sourceUri.toString() === uriString)[0]
|| this.model.indexGroup.resources.filter(r => r.sourceUri.toString() === uriString)[0];
}
}
......
......@@ -62,7 +62,7 @@ class TextEditorMergeDecorator {
return;
}
if (this.model.mergeGroup.resources.some(r => r.type === Status.BOTH_MODIFIED && r.uri.toString() === this.uri)) {
if (this.model.mergeGroup.resources.some(r => r.type === Status.BOTH_MODIFIED && r.sourceUri.toString() === this.uri)) {
decorations = decorate(this.editor.document);
}
......
......@@ -54,16 +54,28 @@ export enum Status {
export class Resource implements SCMResource {
@memoize
get uri(): Uri {
return new Uri().with({
scheme: 'git-resource',
query: JSON.stringify({
resourceGroupId: this.resourceGroupId,
sourceUri: this.sourceUri.toString()
})
});
}
@memoize
get sourceUri(): Uri {
if (this.rename && (this._type === Status.MODIFIED || this._type === Status.DELETED || this._type === Status.INDEX_RENAMED)) {
return this.rename;
}
return this._uri;
return this._sourceUri;
}
get type(): Status { return this._type; }
get original(): Uri { return this._uri; }
get original(): Uri { return this._sourceUri; }
get rename(): Uri | undefined { return this._rename; }
private static Icons = {
......@@ -130,13 +142,16 @@ export class Resource implements SCMResource {
return { strikeThrough: this.strikeThrough, light, dark };
}
constructor(private _uri: Uri, private _type: Status, private _rename?: Uri) {
constructor(private resourceGroupId: string, private _sourceUri: Uri, private _type: Status, private _rename?: Uri) {
// console.log(this);
}
}
export class ResourceGroup implements SCMResourceGroup {
@memoize
get uri(): Uri { return Uri.parse(`git-resource-group:${this.id}`); }
get id(): string { return this._id; }
get label(): string { return this._label; }
get resources(): Resource[] { return this._resources; }
......@@ -399,7 +414,7 @@ export class Model implements Disposable {
@throttle
async add(...resources: Resource[]): Promise<void> {
await this.run(Operation.Add, () => this.repository.add(resources.map(r => r.uri.fsPath)));
await this.run(Operation.Add, () => this.repository.add(resources.map(r => r.sourceUri.fsPath)));
}
@throttle
......@@ -410,7 +425,7 @@ export class Model implements Disposable {
@throttle
async revertFiles(...resources: Resource[]): Promise<void> {
await this.run(Operation.RevertFiles, () => this.repository.revertFiles('HEAD', resources.map(r => r.uri.fsPath)));
await this.run(Operation.RevertFiles, () => this.repository.revertFiles('HEAD', resources.map(r => r.sourceUri.fsPath)));
}
@throttle
......@@ -434,11 +449,11 @@ export class Model implements Disposable {
switch (r.type) {
case Status.UNTRACKED:
case Status.IGNORED:
toClean.push(r.uri.fsPath);
toClean.push(r.sourceUri.fsPath);
break;
default:
toCheckout.push(r.uri.fsPath);
toCheckout.push(r.sourceUri.fsPath);
break;
}
});
......@@ -629,30 +644,30 @@ export class Model implements Disposable {
const renameUri = raw.rename ? Uri.file(path.join(this.repository.root, raw.rename)) : undefined;
switch (raw.x + raw.y) {
case '??': return workingTree.push(new Resource(uri, Status.UNTRACKED));
case '!!': return workingTree.push(new Resource(uri, Status.IGNORED));
case 'DD': return merge.push(new Resource(uri, Status.BOTH_DELETED));
case 'AU': return merge.push(new Resource(uri, Status.ADDED_BY_US));
case 'UD': return merge.push(new Resource(uri, Status.DELETED_BY_THEM));
case 'UA': return merge.push(new Resource(uri, Status.ADDED_BY_THEM));
case 'DU': return merge.push(new Resource(uri, Status.DELETED_BY_US));
case 'AA': return merge.push(new Resource(uri, Status.BOTH_ADDED));
case 'UU': return merge.push(new Resource(uri, Status.BOTH_MODIFIED));
case '??': return workingTree.push(new Resource(WorkingTreeGroup.ID, uri, Status.UNTRACKED));
case '!!': return workingTree.push(new Resource(WorkingTreeGroup.ID, uri, Status.IGNORED));
case 'DD': return merge.push(new Resource(MergeGroup.ID, uri, Status.BOTH_DELETED));
case 'AU': return merge.push(new Resource(MergeGroup.ID, uri, Status.ADDED_BY_US));
case 'UD': return merge.push(new Resource(MergeGroup.ID, uri, Status.DELETED_BY_THEM));
case 'UA': return merge.push(new Resource(MergeGroup.ID, uri, Status.ADDED_BY_THEM));
case 'DU': return merge.push(new Resource(MergeGroup.ID, uri, Status.DELETED_BY_US));
case 'AA': return merge.push(new Resource(MergeGroup.ID, uri, Status.BOTH_ADDED));
case 'UU': return merge.push(new Resource(MergeGroup.ID, uri, Status.BOTH_MODIFIED));
}
let isModifiedInIndex = false;
switch (raw.x) {
case 'M': index.push(new Resource(uri, Status.INDEX_MODIFIED)); isModifiedInIndex = true; break;
case 'A': index.push(new Resource(uri, Status.INDEX_ADDED)); break;
case 'D': index.push(new Resource(uri, Status.INDEX_DELETED)); break;
case 'R': index.push(new Resource(uri, Status.INDEX_RENAMED, renameUri)); break;
case 'C': index.push(new Resource(uri, Status.INDEX_COPIED)); break;
case 'M': index.push(new Resource(IndexGroup.ID, uri, Status.INDEX_MODIFIED)); isModifiedInIndex = true; break;
case 'A': index.push(new Resource(IndexGroup.ID, uri, Status.INDEX_ADDED)); break;
case 'D': index.push(new Resource(IndexGroup.ID, uri, Status.INDEX_DELETED)); break;
case 'R': index.push(new Resource(IndexGroup.ID, uri, Status.INDEX_RENAMED, renameUri)); break;
case 'C': index.push(new Resource(IndexGroup.ID, uri, Status.INDEX_COPIED)); break;
}
switch (raw.y) {
case 'M': workingTree.push(new Resource(uri, Status.MODIFIED, renameUri)); break;
case 'D': workingTree.push(new Resource(uri, Status.DELETED, renameUri)); break;
case 'M': workingTree.push(new Resource(WorkingTreeGroup.ID, uri, Status.MODIFIED, renameUri)); break;
case 'D': workingTree.push(new Resource(WorkingTreeGroup.ID, uri, Status.DELETED, renameUri)); break;
}
});
......
......@@ -619,15 +619,27 @@ declare module 'vscode' {
}
/**
* An SCM resource is the source control state of an underlying resource.
* An SCM resource represents a state of an underlying workspace resource
* within a certain SCM provider state.
*
* For example, consider file A to be modified. An SCM resource which would
* represent such state could have the following properties:
*
* - `uri = 'git:workingtree/A'`
* - `sourceUri = 'file:A'`
*/
export interface SCMResource {
/**
* The [uri](#Uri) of the underlying resource.
* The [uri](#Uri) of this SCM resource.
*/
readonly uri: Uri;
/**
* The [uri](#Uri) of the underlying resource inside the workspace.
*/
readonly sourceUri: Uri;
/**
* The [decorations](#SCMResourceDecorations) for this SCM resource.
*/
......@@ -640,7 +652,13 @@ declare module 'vscode' {
export interface SCMResourceGroup {
/**
* The identifier of the SCM resource group.
* The [uri](#Uri) of this SCM resource group.
*/
readonly uri: Uri;
/**
* The identifier of the SCM resource group, which will be used to populate
* the value of the `scmResourceGroup` context key.
*/
readonly id: string;
......@@ -662,7 +680,8 @@ declare module 'vscode' {
export interface SCMProvider {
/**
* The identifier of the SCM provider.
* The identifier of the SCM provider, which will be used to populate
* the value of the `scmProvider` context key.
*/
readonly id: string;
......@@ -753,9 +772,6 @@ declare module 'vscode' {
*/
export const inputBox: SCMInputBox;
// TODO@Joao
export function getResourceFromURI(uri: Uri): SCMResource | SCMResourceGroup | undefined;
/**
* Registers an [SCM provider](#SCMProvider).
*
......
......@@ -456,11 +456,6 @@ export function createApiFactory(initData: IInitData, threadService: IThreadServ
return extHostSCM.inputBox;
}
@proposed(extension)
getResourceFromURI(uri) {
return extHostSCM.getResourceFromURI(uri);
}
@proposed(extension)
registerSCMProvider(provider) {
return extHostSCM.registerSCMProvider(provider);
......
......@@ -258,10 +258,17 @@ export interface SCMProviderFeatures {
export type SCMRawResource = [
string /*uri*/,
string /*sourceUri*/,
string[] /*icons: light, dark*/,
boolean /*strike through*/
];
export type SCMRawResourceGroup = [string /*id*/, string /*label*/, SCMRawResource[]];
export type SCMRawResourceGroup = [
string /*uri*/,
string /*id*/,
string /*label*/,
SCMRawResource[]
];
export abstract class MainThreadSCMShape {
$register(id: string, features: SCMProviderFeatures): void { throw ni(); }
......@@ -409,7 +416,7 @@ export abstract class ExtHostTerminalServiceShape {
}
export abstract class ExtHostSCMShape {
$open(id: string, resourceGroupId: string, uri: string): TPromise<void> { throw ni(); }
$open(id: string, uri: string): TPromise<void> { throw ni(); }
$acceptChanges(id: string): TPromise<void> { throw ni(); }
$getOriginalResource(id: string, uri: URI): TPromise<URI> { throw ni(); }
$onInputBoxValueChange(value: string): TPromise<void> { throw ni(); }
......
......@@ -26,10 +26,7 @@ function getIconPath(decorations: vscode.SCMResourceThemableDecorations) {
export interface Cache {
[providerId: string]: {
[groupId: string]: {
resourceGroup: vscode.SCMResourceGroup,
resources: { [uri: string]: vscode.SCMResource }
};
[resourceUri: string]: vscode.SCMResource;
};
}
......@@ -87,60 +84,6 @@ export class ExtHostSCM {
this._inputBox = new ExtHostSCMInputBox(this._proxy);
}
getResourceFromURI(uri: vscode.Uri): vscode.SCMResource | vscode.SCMResourceGroup | undefined {
if (uri.scheme !== 'scm') {
return undefined;
}
const providerId = uri.authority;
const providerCache = this.cache[providerId];
if (!providerCache) {
return undefined;
}
const match = /^\/([^/]+)(\/(.*))?$/.exec(uri.path);
if (!match) {
return undefined;
}
const resourceGroupId = match[1];
const resourceGroupRef = providerCache[resourceGroupId];
if (!resourceGroupRef) {
return undefined;
}
const rawResourceUri = match[3];
if (!rawResourceUri) {
return resourceGroupRef.resourceGroup;
}
let resourceUri: string;
try {
const rawResource = JSON.parse(rawResourceUri);
const resource = URI.from(rawResource);
resourceUri = resource.toString();
} catch (err) {
resourceUri = undefined;
}
if (!resourceUri) {
return undefined;
}
const resource = resourceGroupRef.resources[resourceUri];
if (!resource) {
return undefined;
}
return resource;
}
registerSCMProvider(provider: vscode.SCMProvider): Disposable {
const providerId = provider.id;
......@@ -163,10 +106,11 @@ export class ExtHostSCM {
this.cache[providerId] = Object.create(null);
const rawResourceGroups = resourceGroups.map(g => {
const resources: { [id: string]: vscode.SCMResource; } = Object.create(null);
const rawResources = g.resources.map(r => {
const uri = r.uri.toString();
this.cache[providerId][uri] = r;
const sourceUri = r.sourceUri.toString();
const iconPath = getIconPath(r.decorations);
const lightIconPath = r.decorations && getIconPath(r.decorations.light) || iconPath;
const darkIconPath = r.decorations && getIconPath(r.decorations.dark) || iconPath;
......@@ -181,14 +125,11 @@ export class ExtHostSCM {
}
const strikeThrough = r.decorations && !!r.decorations.strikeThrough;
resources[uri] = r;
return [uri, icons, strikeThrough] as SCMRawResource;
return [uri, sourceUri, icons, strikeThrough] as SCMRawResource;
});
this.cache[providerId][g.id] = { resourceGroup: g, resources };
return [g.id, g.label, rawResources] as SCMRawResourceGroup;
return [g.uri.toString(), g.id, g.label, rawResources] as SCMRawResourceGroup;
});
this._proxy.$onChange(providerId, rawResourceGroups, provider.count, provider.state);
......@@ -201,16 +142,14 @@ export class ExtHostSCM {
});
}
$open(providerId: string, resourceGroupId: string, uri: string): TPromise<void> {
$open(providerId: string, uri: string): TPromise<void> {
const provider = this._providers[providerId];
if (!provider) {
return TPromise.as(null);
}
const providerCache = this.cache[providerId];
const resourceGroup = providerCache[resourceGroupId];
const resource = resourceGroup && resourceGroup.resources[uri];
const resource = this.cache[providerId][uri];
if (!resource) {
return TPromise.as(null);
......
......@@ -49,7 +49,7 @@ class MainThreadSCMProvider implements ISCMProvider {
return TPromise.as(null);
}
return this.proxy.$open(this.id, resource.resourceGroupId, resource.uri.toString());
return this.proxy.$open(this.id, resource.uri.toString());
}
acceptChanges(): TPromise<void> {
......@@ -76,10 +76,10 @@ class MainThreadSCMProvider implements ISCMProvider {
$onChange(rawResourceGroups: SCMRawResourceGroup[], count: number | undefined, state: string | undefined): void {
this._resources = rawResourceGroups.map(rawGroup => {
const [id, label, rawResources] = rawGroup;
const [uri, id, label, rawResources] = rawGroup;
const resources = rawResources.map(rawResource => {
const [uri, icons, strikeThrough] = rawResource;
const [uri, sourceUri, icons, strikeThrough] = rawResource;
const icon = icons[0];
const iconDark = icons[1] || icon;
......@@ -93,11 +93,12 @@ class MainThreadSCMProvider implements ISCMProvider {
return {
resourceGroupId: id,
uri: URI.parse(uri),
sourceUri: URI.parse(sourceUri),
decorations
};
});
return { id, label, resources };
return { uri: URI.parse(uri), id, label, resources };
});
this._count = count;
......
......@@ -13,7 +13,7 @@ import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { IAction } from 'vs/base/common/actions';
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { ISCMService, ISCMProvider, ISCMResource, ISCMResourceGroup } from 'vs/workbench/services/scm/common/scm';
import { getSCMResourceURI, getSCMResourceGroupId } from './scmUtil';
import { getSCMResourceGroupId } from './scmUtil';
export class SCMMenus implements IDisposable {
......@@ -105,7 +105,7 @@ export class SCMMenus implements IDisposable {
const primary = [];
const secondary = [];
const result = { primary, secondary };
fillInActions(menu, getSCMResourceURI(this.activeProviderId, resource), result, g => g === 'inline');
fillInActions(menu, resource.uri, result, g => g === 'inline');
menu.dispose();
contextKeyService.dispose();
......
......@@ -6,26 +6,9 @@
'use strict';
import { ISCMResourceGroup, ISCMResource } from 'vs/workbench/services/scm/common/scm';
import URI from 'vs/base/common/uri';
export function isSCMResource(element: ISCMResourceGroup | ISCMResource): element is ISCMResource {
return !!(element as ISCMResource).uri;
}
export function getSCMResourceURI(providerId: string, resource: ISCMResourceGroup | ISCMResource): URI {
if (isSCMResource(resource)) {
return URI.from({
scheme: 'scm',
authority: providerId,
path: `/${resource.resourceGroupId}/${JSON.stringify(resource.uri)}`
});
} else {
return URI.from({
scheme: 'scm',
authority: providerId,
path: `/${resource.id}`
});
}
return !!(element as ISCMResource).sourceUri;
}
export function getSCMResourceGroupId(resource: ISCMResourceGroup | ISCMResource): string {
......
......@@ -40,12 +40,12 @@ import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { IModelService } from 'vs/editor/common/services/modelService';
import { comparePaths } from 'vs/base/common/comparers';
import URI from 'vs/base/common/uri';
import { isSCMResource, getSCMResourceURI } from './scmUtil';
import { isSCMResource } from './scmUtil';
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
function getElementId(element: ISCMResourceGroup | ISCMResource) {
if (isSCMResource(element)) {
return `${element.resourceGroupId}:${element.uri.toString()}`;
return `${element.resourceGroupId}:${element.sourceUri.toString()}`;
} else {
return `${element.id}`;
}
......@@ -155,7 +155,7 @@ class ResourceRenderer implements IRenderer<ISCMResource, ResourceTemplate> {
}
renderElement(resource: ISCMResource, index: number, template: ResourceTemplate): void {
template.fileLabel.setFile(resource.uri);
template.fileLabel.setFile(resource.sourceUri);
template.actionBar.clear();
template.actionBar.push(this.scmMenus.getResourceActions(resource));
toggleClass(template.name, 'strike-through', resource.decorations.strikeThrough);
......@@ -185,7 +185,7 @@ class Delegate implements IDelegate<ISCMResourceGroup | ISCMResource> {
}
function resourceSorter(a: ISCMResource, b: ISCMResource): number {
return comparePaths(a.uri.fsPath, b.uri.fsPath);
return comparePaths(a.sourceUri.fsPath, b.sourceUri.fsPath);
}
export class SCMViewlet extends Viewlet {
......@@ -368,8 +368,7 @@ export class SCMViewlet extends Viewlet {
}
private getSelectedResources(): URI[] {
return this.list.getSelectedElements()
.map(r => getSCMResourceURI(this.activeProviderId, r));
return this.list.getSelectedElements().map(r => r.uri);
}
dispose(): void {
......
......@@ -26,10 +26,12 @@ export interface ISCMResourceDecorations {
export interface ISCMResource {
readonly resourceGroupId: string;
readonly uri: URI;
readonly sourceUri: URI;
readonly decorations: ISCMResourceDecorations;
}
export interface ISCMResourceGroup {
readonly uri: URI;
readonly id: string;
readonly label: string;
readonly resources: ISCMResource[];
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册