mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-25 11:08:51 +01:00
Git - add file decoration provider for incoming changes (#204919)
* Initial implementation of a file decoration provider and quick diff provider * Refactor file decoration provider * Add incomingChanges to history provider * Move decoration provider * Move things around * Add separate color for renamed incoming change * Remove include that is not needed
This commit is contained in:
@@ -3,13 +3,13 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { window, workspace, Uri, Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, ThemeColor } from 'vscode';
|
||||
import { window, workspace, Uri, Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, ThemeColor, l10n } from 'vscode';
|
||||
import * as path from 'path';
|
||||
import { Repository, GitResourceGroup } from './repository';
|
||||
import { Model } from './model';
|
||||
import { debounce } from './decorators';
|
||||
import { filterEvent, dispose, anyEvent, fireEvent, PromiseSource } from './util';
|
||||
import { GitErrorCodes, Status } from './api/git';
|
||||
import { filterEvent, dispose, anyEvent, fireEvent, PromiseSource, combinedDisposable } from './util';
|
||||
import { Change, GitErrorCodes, Status } from './api/git';
|
||||
|
||||
class GitIgnoreDecorationProvider implements FileDecorationProvider {
|
||||
|
||||
@@ -153,6 +153,100 @@ class GitDecorationProvider implements FileDecorationProvider {
|
||||
}
|
||||
}
|
||||
|
||||
class GitIncomingChangesFileDecorationProvider implements FileDecorationProvider {
|
||||
|
||||
private readonly _onDidChangeDecorations = new EventEmitter<Uri[]>();
|
||||
readonly onDidChangeFileDecorations: Event<Uri[]> = this._onDidChangeDecorations.event;
|
||||
|
||||
private decorations = new Map<string, FileDecoration>();
|
||||
private readonly disposables: Disposable[] = [];
|
||||
|
||||
constructor(private readonly repository: Repository) {
|
||||
this.disposables.push(window.registerFileDecorationProvider(this));
|
||||
repository.historyProvider.onDidChangeCurrentHistoryItemGroupBase(this.onDidChangeCurrentHistoryItemGroupBase, this, this.disposables);
|
||||
}
|
||||
|
||||
private async onDidChangeCurrentHistoryItemGroupBase(): Promise<void> {
|
||||
const newDecorations = new Map<string, FileDecoration>();
|
||||
await this.collectIncomingChangesFileDecorations(newDecorations);
|
||||
const uris = new Set([...this.decorations.keys()].concat([...newDecorations.keys()]));
|
||||
|
||||
this.decorations = newDecorations;
|
||||
this._onDidChangeDecorations.fire([...uris.values()].map(value => Uri.parse(value, true)));
|
||||
}
|
||||
|
||||
private async collectIncomingChangesFileDecorations(bucket: Map<string, FileDecoration>): Promise<void> {
|
||||
for (const change of await this.getIncomingChanges()) {
|
||||
switch (change.status) {
|
||||
case Status.INDEX_ADDED:
|
||||
bucket.set(change.uri.toString(), {
|
||||
badge: '↓A',
|
||||
color: new ThemeColor('gitDecoration.incomingAddedForegroundColor'),
|
||||
tooltip: l10n.t('Incoming Changes (added)'),
|
||||
});
|
||||
break;
|
||||
case Status.DELETED:
|
||||
bucket.set(change.uri.toString(), {
|
||||
badge: '↓D',
|
||||
color: new ThemeColor('gitDecoration.incomingDeletedForegroundColor'),
|
||||
tooltip: l10n.t('Incoming Changes (deleted)'),
|
||||
});
|
||||
break;
|
||||
case Status.INDEX_RENAMED:
|
||||
bucket.set(change.originalUri.toString(), {
|
||||
badge: '↓R',
|
||||
color: new ThemeColor('gitDecoration.incomingRenamedForegroundColor'),
|
||||
tooltip: l10n.t('Incoming Changes (renamed)'),
|
||||
});
|
||||
break;
|
||||
case Status.MODIFIED:
|
||||
bucket.set(change.uri.toString(), {
|
||||
badge: '↓M',
|
||||
color: new ThemeColor('gitDecoration.incomingModifiedForegroundColor'),
|
||||
tooltip: l10n.t('Incoming Changes (modified)'),
|
||||
});
|
||||
break;
|
||||
default: {
|
||||
bucket.set(change.uri.toString(), {
|
||||
badge: '↓~',
|
||||
color: new ThemeColor('gitDecoration.incomingModifiedForegroundColor'),
|
||||
tooltip: l10n.t('Incoming Changes'),
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async getIncomingChanges(): Promise<Change[]> {
|
||||
try {
|
||||
const historyProvider = this.repository.historyProvider;
|
||||
const currentHistoryItemGroup = historyProvider.currentHistoryItemGroup;
|
||||
|
||||
if (!currentHistoryItemGroup?.base) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const ancestor = await historyProvider.resolveHistoryItemGroupCommonAncestor(currentHistoryItemGroup.id, currentHistoryItemGroup.base.id);
|
||||
if (!ancestor) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const changes = await this.repository.diffBetween(ancestor.id, currentHistoryItemGroup.base.id);
|
||||
return changes;
|
||||
} catch (err) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
provideFileDecoration(uri: Uri): FileDecoration | undefined {
|
||||
return this.decorations.get(uri.toString());
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
|
||||
export class GitDecorations {
|
||||
|
||||
@@ -191,8 +285,12 @@ export class GitDecorations {
|
||||
}
|
||||
|
||||
private onDidOpenRepository(repository: Repository): void {
|
||||
const provider = new GitDecorationProvider(repository);
|
||||
this.providers.set(repository, provider);
|
||||
const providers = combinedDisposable([
|
||||
new GitDecorationProvider(repository),
|
||||
new GitIncomingChangesFileDecorationProvider(repository)
|
||||
]);
|
||||
|
||||
this.providers.set(repository, providers);
|
||||
}
|
||||
|
||||
private onDidCloseRepository(repository: Repository): void {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, SourceControlHistoryItem, SourceControlHistoryItemChange, SourceControlHistoryItemGroup, SourceControlHistoryOptions, SourceControlHistoryProvider, ThemeIcon, Uri, window, LogOutputChannel } from 'vscode';
|
||||
import { Repository, Resource } from './repository';
|
||||
import { IDisposable, filterEvent } from './util';
|
||||
import { IDisposable, dispose, filterEvent } from './util';
|
||||
import { toGitUri } from './uri';
|
||||
import { Branch, RefType, UpstreamRef } from './api/git';
|
||||
import { emojify, ensureEmojis } from './emoji';
|
||||
@@ -17,16 +17,20 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
|
||||
private readonly _onDidChangeCurrentHistoryItemGroup = new EventEmitter<void>();
|
||||
readonly onDidChangeCurrentHistoryItemGroup: Event<void> = this._onDidChangeCurrentHistoryItemGroup.event;
|
||||
|
||||
private readonly _onDidChangeCurrentHistoryItemGroupBase = new EventEmitter<void>();
|
||||
readonly onDidChangeCurrentHistoryItemGroupBase: Event<void> = this._onDidChangeCurrentHistoryItemGroupBase.event;
|
||||
|
||||
private readonly _onDidChangeDecorations = new EventEmitter<Uri[]>();
|
||||
readonly onDidChangeFileDecorations: Event<Uri[]> = this._onDidChangeDecorations.event;
|
||||
|
||||
private _HEAD: Branch | undefined;
|
||||
private _currentHistoryItemGroup: SourceControlHistoryItemGroup | undefined;
|
||||
|
||||
get currentHistoryItemGroup(): SourceControlHistoryItemGroup | undefined { return this._currentHistoryItemGroup; }
|
||||
set currentHistoryItemGroup(value: SourceControlHistoryItemGroup | undefined) {
|
||||
this._currentHistoryItemGroup = value;
|
||||
this._onDidChangeCurrentHistoryItemGroup.fire();
|
||||
|
||||
this.logger.trace('GitHistoryProvider:onDidRunGitStatus - currentHistoryItemGroup:', JSON.stringify(value));
|
||||
}
|
||||
|
||||
private historyItemDecorations = new Map<string, FileDecoration>();
|
||||
@@ -55,26 +59,35 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
|
||||
return;
|
||||
}
|
||||
|
||||
this._HEAD = this.repository.HEAD;
|
||||
|
||||
// Check if HEAD does not support incoming/outgoing (detached commit, tag)
|
||||
if (!this._HEAD?.name || !this._HEAD?.commit || this._HEAD.type === RefType.Tag) {
|
||||
this.currentHistoryItemGroup = undefined;
|
||||
if (!this.repository.HEAD?.name || !this.repository.HEAD?.commit || this.repository.HEAD.type === RefType.Tag) {
|
||||
this.logger.trace('GitHistoryProvider:onDidRunGitStatus - HEAD does not support incoming/outgoing');
|
||||
|
||||
this.currentHistoryItemGroup = undefined;
|
||||
this._HEAD = this.repository.HEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentHistoryItemGroup = {
|
||||
id: `refs/heads/${this._HEAD.name ?? ''}`,
|
||||
label: this._HEAD.name ?? '',
|
||||
base: this._HEAD.upstream ?
|
||||
id: `refs/heads/${this.repository.HEAD.name ?? ''}`,
|
||||
label: this.repository.HEAD.name ?? '',
|
||||
base: this.repository.HEAD.upstream ?
|
||||
{
|
||||
id: `refs/remotes/${this._HEAD.upstream.remote}/${this._HEAD.upstream.name}`,
|
||||
label: `${this._HEAD.upstream.remote}/${this._HEAD.upstream.name}`,
|
||||
id: `refs/remotes/${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`,
|
||||
label: `${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`,
|
||||
} : undefined
|
||||
};
|
||||
|
||||
this.logger.trace('GitHistoryProvider:onDidRunGitStatus - currentHistoryItemGroup:', JSON.stringify(this.currentHistoryItemGroup));
|
||||
// Check if Upstream has changed
|
||||
if (force ||
|
||||
this._HEAD?.upstream?.name !== this.repository.HEAD?.upstream?.name ||
|
||||
this._HEAD?.upstream?.remote === this.repository.HEAD?.upstream?.remote ||
|
||||
this._HEAD?.upstream?.commit === this.repository.HEAD?.upstream?.commit) {
|
||||
this.logger.trace('GitHistoryProvider:onDidRunGitStatus - Upstream has changed');
|
||||
this._onDidChangeCurrentHistoryItemGroupBase.fire();
|
||||
}
|
||||
|
||||
this._HEAD = this.repository.HEAD;
|
||||
}
|
||||
|
||||
async provideHistoryItems(historyItemGroupId: string, options: SourceControlHistoryOptions): Promise<SourceControlHistoryItem[]> {
|
||||
@@ -216,6 +229,6 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.forEach(d => d.dispose());
|
||||
dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user