Kaynağa Gözat

Adds Contributors view

main
Eric Amodio 4 yıl önce
ebeveyn
işleme
36747ce341
6 değiştirilmiş dosya ile 363 ekleme ve 16 silme
  1. +134
    -0
      package.json
  2. +11
    -0
      src/container.ts
  3. +192
    -0
      src/views/contributorsView.ts
  4. +10
    -9
      src/views/nodes/contributorNode.ts
  5. +13
    -7
      src/views/nodes/contributorsNode.ts
  6. +3
    -0
      src/views/viewBase.ts

+ 134
- 0
package.json Dosyayı Görüntüle

@ -1612,6 +1612,40 @@
"markdownDescription": "Specifies where to show the _Compare_ view",
"scope": "window"
},
"gitlens.views.contributors.avatars": {
"type": "boolean",
"default": true,
"markdownDescription": "Specifies whether to show avatar images instead of commit (or status) icons in the _Contributors_ view",
"scope": "window"
},
"gitlens.views.contributors.files.compact": {
"type": "boolean",
"default": true,
"markdownDescription": "Specifies whether to compact (flatten) unnecessary file nesting in the _Contributors_ view. Only applies when `#gitlens.views.contributors.files.layout#` is set to `tree` or `auto`",
"scope": "window"
},
"gitlens.views.contributors.files.layout": {
"type": "string",
"default": "auto",
"enum": [
"auto",
"list",
"tree"
],
"enumDescriptions": [
"Automatically switches between displaying files as a `tree` or `list` based on the `#gitlens.views.contributors.files.threshold#` value and the number of files at each nesting level",
"Displays files as a list",
"Displays files as a tree"
],
"markdownDescription": "Specifies how the _Contributors_ view will display files",
"scope": "window"
},
"gitlens.views.contributors.files.threshold": {
"type": "number",
"default": 5,
"markdownDescription": "Specifies when to switch between displaying files as a `tree` or `list` based on the number of files in a nesting level in the _Contributors_ view. Only applies when `#gitlens.views.contributors.files.layout#` is set to `auto`",
"scope": "window"
},
"gitlens.views.defaultItemLimit": {
"type": "number",
"default": 10,
@ -3641,6 +3675,48 @@
}
},
{
"command": "gitlens.views.contributors.copy",
"title": "Copy",
"category": "GitLens"
},
{
"command": "gitlens.views.contributors.refresh",
"title": "Refresh",
"category": "GitLens",
"icon": "$(refresh)"
},
{
"command": "gitlens.views.contributors.setFilesLayoutToAuto",
"title": "Toggle File Layout (Tree)",
"category": "GitLens",
"icon": "$(list-tree)"
},
{
"command": "gitlens.views.contributors.setFilesLayoutToList",
"title": "Toggle File Layout (Auto)",
"category": "GitLens",
"icon": {
"dark": "images/dark/icon-view-auto.svg",
"light": "images/light/icon-view-auto.svg"
}
},
{
"command": "gitlens.views.contributors.setFilesLayoutToTree",
"title": "Toggle File Layout (List)",
"category": "GitLens",
"icon": "$(list-flat)"
},
{
"command": "gitlens.views.contributors.setShowAvatarsOn",
"title": "Show Avatars",
"category": "GitLens"
},
{
"command": "gitlens.views.contributors.setShowAvatarsOff",
"title": "Hide Avatars",
"category": "GitLens"
},
{
"command": "gitlens.views.remotes.copy",
"title": "Copy",
"category": "GitLens"
@ -4685,6 +4761,34 @@
"when": "false"
},
{
"command": "gitlens.views.contributors.copy",
"when": "false"
},
{
"command": "gitlens.views.contributors.refresh",
"when": "false"
},
{
"command": "gitlens.views.contributors.setFilesLayoutToAuto",
"when": "false"
},
{
"command": "gitlens.views.contributors.setFilesLayoutToList",
"when": "false"
},
{
"command": "gitlens.views.contributors.setFilesLayoutToTree",
"when": "false"
},
{
"command": "gitlens.views.contributors.setShowAvatarsOn",
"when": "false"
},
{
"command": "gitlens.views.contributors.setShowAvatarsOff",
"when": "false"
},
{
"command": "gitlens.views.remotes.copy",
"when": "false"
},
@ -5473,6 +5577,36 @@
"group": "1_gitlens@0"
},
{
"command": "gitlens.views.contributors.setFilesLayoutToList",
"when": "view =~ /^gitlens\\.views\\.contributors/ && config.gitlens.views.contributors.files.layout == auto",
"group": "navigation@13"
},
{
"command": "gitlens.views.contributors.setFilesLayoutToTree",
"when": "view =~ /^gitlens\\.views\\.contributors/ && config.gitlens.views.contributors.files.layout == list",
"group": "navigation@13"
},
{
"command": "gitlens.views.contributors.setFilesLayoutToAuto",
"when": "view =~ /^gitlens\\.views\\.contributors/ && config.gitlens.views.contributors.files.layout == tree",
"group": "navigation@13"
},
{
"command": "gitlens.views.contributors.refresh",
"when": "view =~ /^gitlens\\.views\\.contributors/",
"group": "navigation@99"
},
{
"command": "gitlens.views.contributors.setShowAvatarsOn",
"when": "view =~ /^gitlens\\.views\\.contributors/ && !config.gitlens.views.contributors.avatars",
"group": "1_gitlens@0"
},
{
"command": "gitlens.views.contributors.setShowAvatarsOff",
"when": "view =~ /^gitlens\\.views\\.contributors/ && config.gitlens.views.contributors.avatars",
"group": "1_gitlens@0"
},
{
"command": "gitlens.views.history.setFilesLayoutToList",
"when": "view =~ /^gitlens\\.views\\.history/ && config.gitlens.views.history.files.layout == auto",
"group": "navigation@13"

+ 11
- 0
src/container.ts Dosyayı Görüntüle

@ -25,6 +25,7 @@ import { GitDocumentTracker } from './trackers/gitDocumentTracker';
import { GitLineTracker } from './trackers/gitLineTracker';
import { BranchesView } from './views/branchesView';
import { CompareView } from './views/compareView';
import { ContributorsView } from './views/contributorsView';
import { FileHistoryView } from './views/fileHistoryView';
import { HistoryView } from './views/historyView';
import { LineHistoryView } from './views/lineHistoryView';
@ -67,6 +68,7 @@ export class Container {
context.subscriptions.push((this._welcomeWebview = new WelcomeWebview()));
context.subscriptions.push((this._branchesView = new BranchesView()));
context.subscriptions.push((this._contributorsView = new ContributorsView()));
context.subscriptions.push((this._historyView = new HistoryView()));
context.subscriptions.push((this._remotesView = new RemotesView()));
context.subscriptions.push((this._tagsView = new TagsView()));
@ -213,6 +215,15 @@ export class Container {
return this._context;
}
private static _contributorsView: ContributorsView | undefined;
static get contributorsView() {
if (this._contributorsView === undefined) {
this._context.subscriptions.push((this._contributorsView = new ContributorsView()));
}
return this._contributorsView;
}
private static _fileAnnotationController: FileAnnotationController;
static get fileAnnotations() {
return this._fileAnnotationController;

+ 192
- 0
src/views/contributorsView.ts Dosyayı Görüntüle

@ -0,0 +1,192 @@
'use strict';
import { commands, ConfigurationChangeEvent, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { configuration, ContributorsViewConfig, ViewFilesLayout } from '../configuration';
import { Container } from '../container';
import { Repository, RepositoryChange, RepositoryChangeEvent } from '../git/git';
import { GitUri } from '../git/gitUri';
import { ContextValues, ContributorsNode, MessageNode, SubscribeableViewNode, unknownGitUri, ViewNode } from './nodes';
import { debug, gate } from '../system';
import { ViewBase } from './viewBase';
export class ContributorsRepositoryNode extends SubscribeableViewNode<ContributorsView> {
private child: ContributorsNode | undefined;
constructor(
uri: GitUri,
view: ContributorsView,
parent: ViewNode,
public readonly repo: Repository,
private readonly root: boolean,
) {
super(uri, view, parent);
}
async getChildren(): Promise<ViewNode[]> {
if (this.child == null) {
this.child = new ContributorsNode(this.uri, this.view, this, this.repo);
void this.ensureSubscription();
}
return this.child.getChildren();
}
getTreeItem(): TreeItem {
const item = new TreeItem(
this.repo.formattedName ?? this.uri.repoPath ?? '',
TreeItemCollapsibleState.Expanded,
);
item.contextValue = ContextValues.RepositoryFolder;
void this.ensureSubscription();
return item;
}
@gate()
@debug()
async refresh(reset: boolean = false) {
await this.child?.triggerChange(reset);
await this.ensureSubscription();
}
@debug()
protected subscribe() {
return this.repo.onDidChange(this.onRepositoryChanged, this);
}
@debug({
args: {
0: (e: RepositoryChangeEvent) =>
`{ repository: ${e.repository ? e.repository.name : ''}, changes: ${e.changes.join()} }`,
},
})
private onRepositoryChanged(e: RepositoryChangeEvent) {
if (e.changed(RepositoryChange.Closed)) {
this.dispose();
void this.parent?.triggerChange(true);
return;
}
if (e.changed(RepositoryChange.Heads)) {
void this.triggerChange(true);
if (this.root) {
void this.parent?.triggerChange(true);
}
}
}
}
export class ContributorsViewNode extends ViewNode<ContributorsView> {
private children: ContributorsRepositoryNode[] | undefined;
constructor(view: ContributorsView) {
super(unknownGitUri, view);
}
async getChildren(): Promise<ViewNode[]> {
if (this.children != null) {
for (const child of this.children) {
child.dispose?.();
}
this.children = undefined;
}
const repositories = await Container.git.getOrderedRepositories();
if (repositories.length === 0) return [new MessageNode(this.view, this, 'No contributors could be found.')];
const root = repositories.length === 1;
this.children = repositories.map(
r => new ContributorsRepositoryNode(GitUri.fromRepoPath(r.path), this.view, this, r, root),
);
if (root) {
const [child] = this.children;
const contributors = await child.repo.getContributors();
this.view.description = contributors.length === 0 ? undefined : `(${contributors.length})`;
return child.getChildren();
}
return this.children;
}
getTreeItem(): TreeItem {
const item = new TreeItem('Contributors', TreeItemCollapsibleState.Expanded);
return item;
}
}
export class ContributorsView extends ViewBase<ContributorsViewNode, ContributorsViewConfig> {
protected readonly configKey = 'contributors';
constructor() {
super('gitlens.views.contributors', 'Contributors');
}
getRoot() {
return new ContributorsViewNode(this);
}
protected registerCommands() {
void Container.viewCommands;
commands.registerCommand(
this.getQualifiedCommand('copy'),
() => commands.executeCommand('gitlens.views.copy', this.selection),
this,
);
commands.registerCommand(this.getQualifiedCommand('refresh'), () => this.refresh(true), this);
commands.registerCommand(
this.getQualifiedCommand('setFilesLayoutToAuto'),
() => this.setFilesLayout(ViewFilesLayout.Auto),
this,
);
commands.registerCommand(
this.getQualifiedCommand('setFilesLayoutToList'),
() => this.setFilesLayout(ViewFilesLayout.List),
this,
);
commands.registerCommand(
this.getQualifiedCommand('setFilesLayoutToTree'),
() => this.setFilesLayout(ViewFilesLayout.Tree),
this,
);
commands.registerCommand(this.getQualifiedCommand('setShowAvatarsOn'), () => this.setShowAvatars(true), this);
commands.registerCommand(this.getQualifiedCommand('setShowAvatarsOff'), () => this.setShowAvatars(false), this);
}
protected filterConfigurationChanged(e: ConfigurationChangeEvent) {
const changed = super.filterConfigurationChanged(e);
if (
!changed &&
!configuration.changed(e, 'defaultDateFormat') &&
!configuration.changed(e, 'defaultDateSource') &&
!configuration.changed(e, 'defaultDateStyle') &&
!configuration.changed(e, 'defaultGravatarsStyle')
) {
return false;
}
return true;
}
protected onConfigurationChanged(e: ConfigurationChangeEvent) {
if (configuration.initializing(e)) {
this.initialize(undefined, { showCollapseAll: true });
}
if (!configuration.initializing(e) && this._root != null) {
void this.refresh(true);
}
}
private setFilesLayout(layout: ViewFilesLayout) {
return configuration.updateEffective('views', this.configKey, 'files', 'layout', layout);
}
private setShowAvatars(enabled: boolean) {
return configuration.updateEffective('views', this.configKey, 'avatars', enabled);
}
}

+ 10
- 9
src/views/nodes/contributorNode.ts Dosyayı Görüntüle

@ -1,19 +1,20 @@
'use strict';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { CommitNode } from './commitNode';
import { MessageNode, ShowMoreNode } from './common';
import { GlyphChars } from '../../constants';
import { Container } from '../../container';
import { ContributorsView } from '../contributorsView';
import { GitContributor, GitLog } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { debug, gate, Iterables, Strings } from '../../system';
import { RepositoriesView } from '../repositoriesView';
import { ContextValues, PageableViewNode, ViewNode } from './viewNode';
import { Container } from '../../container';
import { MessageNode, ShowMoreNode } from './common';
import { insertDateMarkers } from './helpers';
import { CommitNode } from './commitNode';
import { GlyphChars } from '../../constants';
import { RepositoriesView } from '../repositoriesView';
import { RepositoryNode } from './repositoryNode';
import { debug, gate, Iterables, Strings } from '../../system';
import { ContextValues, PageableViewNode, ViewNode } from './viewNode';
import { ContactPresence } from '../../vsls/vsls';
export class ContributorNode extends ViewNode<RepositoriesView> implements PageableViewNode {
export class ContributorNode extends ViewNode<ContributorsView | RepositoriesView> implements PageableViewNode {
static key = ':contributor';
static getId(repoPath: string, name: string, email: string): string {
return `${RepositoryNode.getId(repoPath)}${this.key}(${name}|${email})`;
@ -21,7 +22,7 @@ export class ContributorNode extends ViewNode implements Pagea
constructor(
uri: GitUri,
view: RepositoriesView,
view: ContributorsView | RepositoriesView,
parent: ViewNode,
public readonly contributor: GitContributor,
private readonly _presenceMap: Map<string, ContactPresence> | undefined,

+ 13
- 7
src/views/nodes/contributorsNode.ts Dosyayı Görüntüle

@ -1,22 +1,28 @@
'use strict';
import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { GitContributor, Repository } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { RepositoriesView } from '../repositoriesView';
import { MessageNode } from './common';
import { ContributorNode } from './contributorNode';
import { ContextValues, ViewNode } from './viewNode';
import { Container } from '../../container';
import { ContributorNode } from './contributorNode';
import { ContributorsView } from '../contributorsView';
import { GitContributor, Repository } from '../../git/git';
import { GitUri } from '../../git/gitUri';
import { RepositoryNode } from './repositoryNode';
import { RepositoriesView } from '../repositoriesView';
import { debug, timeout } from '../../system';
import { ContextValues, ViewNode } from './viewNode';
export class ContributorsNode extends ViewNode<RepositoriesView> {
export class ContributorsNode extends ViewNode<ContributorsView | RepositoriesView> {
static key = ':contributors';
static getId(repoPath: string): string {
return `${RepositoryNode.getId(repoPath)}${this.key}`;
}
constructor(uri: GitUri, view: RepositoriesView, parent: ViewNode, public readonly repo: Repository) {
constructor(
uri: GitUri,
view: ContributorsView | RepositoriesView,
parent: ViewNode,
public readonly repo: Repository,
) {
super(uri, view, parent);
}

+ 3
- 0
src/views/viewBase.ts Dosyayı Görüntüle

@ -48,10 +48,12 @@ import { RepositoriesView } from './repositoriesView';
import { SearchView } from './searchView';
import { debug, Functions, log, Promises, Strings } from '../system';
import { TagsView } from './tagsView';
import { ContributorsView } from './contributorsView';
export type View =
| BranchesView
| CompareView
| ContributorsView
| FileHistoryView
| HistoryView
| LineHistoryView
@ -62,6 +64,7 @@ export type View =
export type ViewsWithFiles =
| BranchesView
| CompareView
| ContributorsView
| HistoryView
| RemotesView
| RepositoriesView

Yükleniyor…
İptal
Kaydet