瀏覽代碼

Adds favoriting to repos & branches

main
Eric Amodio 6 年之前
父節點
當前提交
2b0673b38e
共有 15 個文件被更改,包括 228 次插入17 次删除
  1. +1
    -0
      CHANGELOG.md
  2. +4
    -0
      images/dark/icon-star-filled.svg
  3. +4
    -0
      images/dark/icon-star.svg
  4. +4
    -0
      images/light/icon-star-filled.svg
  5. +4
    -0
      images/light/icon-star.svg
  6. +66
    -0
      package.json
  7. +10
    -0
      src/constants.ts
  8. +1
    -1
      src/git/gitService.ts
  9. +34
    -0
      src/git/models/branch.ts
  10. +32
    -0
      src/git/models/repository.ts
  11. +25
    -9
      src/views/nodes/branchNode.ts
  12. +6
    -2
      src/views/nodes/branchesNode.ts
  13. +6
    -2
      src/views/nodes/remoteNode.ts
  14. +18
    -3
      src/views/nodes/repositoryNode.ts
  15. +13
    -0
      src/views/viewCommands.ts

+ 1
- 0
CHANGELOG.md 查看文件

@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Added
- Adds favoriting of repositories and branches in the _Repositories_ view to allow for better (user-customized) sorting
- Adds the ability to turn on file annotations (blame, heatmap, and recent changes) via user-defined modes — closes [#542](https://github.com/eamodio/vscode-gitlens/issues/542)
## [9.2.4] - 2018-12-26

+ 4
- 0
images/dark/icon-star-filled.svg 查看文件

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 22">
<path fill="#C5C5C5" stroke="#C5C5C5" d="M 15 9.5 L 10.1 8.86 L 8 4.5 L 5.9 8.86 L 1 9.5 L 4.6 12.76 L 3.67 17.5 L 8 15.17 L 12.33 17.5 L 11.4 12.76 L 15 9.5 Z"/>
</svg>

+ 4
- 0
images/dark/icon-star.svg 查看文件

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 22">
<path fill="none" stroke="#C5C5C5" d="M 15 9.5 L 10.1 8.86 L 8 4.5 L 5.9 8.86 L 1 9.5 L 4.6 12.76 L 3.67 17.5 L 8 15.17 L 12.33 17.5 L 11.4 12.76 L 15 9.5 Z"/>
</svg>

+ 4
- 0
images/light/icon-star-filled.svg 查看文件

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 22">
<path fill="#424242" stroke="#424242" d="M 15 9.5 L 10.1 8.86 L 8 4.5 L 5.9 8.86 L 1 9.5 L 4.6 12.76 L 3.67 17.5 L 8 15.17 L 12.33 17.5 L 11.4 12.76 L 15 9.5 Z"/>
</svg>

+ 4
- 0
images/light/icon-star.svg 查看文件

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 22">
<path fill="none" stroke="#424242" d="M 15 9.5 L 10.1 8.86 L 8 4.5 L 5.9 8.86 L 1 9.5 L 4.6 12.76 L 3.67 17.5 L 8 15.17 L 12.33 17.5 L 11.4 12.76 L 15 9.5 Z"/>
</svg>

+ 66
- 0
package.json 查看文件

@ -2266,6 +2266,24 @@
}
},
{
"command": "gitlens.views.star",
"title": "Add to Favorites",
"category": "GitLens",
"icon": {
"dark": "images/dark/icon-star.svg",
"light": "images/light/icon-star.svg"
}
},
{
"command": "gitlens.views.unstar",
"title": "Remove from Favorites",
"category": "GitLens",
"icon": {
"dark": "images/dark/icon-star-filled.svg",
"light": "images/light/icon-star-filled.svg"
}
},
{
"command": "gitlens.views.openDirectoryDiff",
"title": "Open Directory Compare",
"category": "GitLens"
@ -3024,6 +3042,14 @@
"when": "false"
},
{
"command": "gitlens.views.star",
"when": "false"
},
{
"command": "gitlens.views.unstar",
"when": "false"
},
{
"command": "gitlens.views.openChanges",
"when": "false"
},
@ -3798,6 +3824,16 @@
"group": "1_gitlens@1"
},
{
"command": "gitlens.views.star",
"when": "viewItem =~ /gitlens:branch\\b(?!.*?\\+starred\\b.*?)/",
"group": "inline@1"
},
{
"command": "gitlens.views.unstar",
"when": "viewItem =~ /gitlens:branch\\b.*?\\+starred\\b.*?/",
"group": "inline@2"
},
{
"command": "gitlens.views.checkout",
"when": "!gitlens:readonly && viewItem =~ /gitlens:branch\\b(?!.*?\\+current\\b.*?)/",
"group": "inline@10"
@ -3834,6 +3870,16 @@
"group": "2_gitlens@1"
},
{
"command": "gitlens.views.star",
"when": "viewItem =~ /gitlens:branch\\b(?!.*?\\+starred\\b.*?)/",
"group": "3_gitlens@1"
},
{
"command": "gitlens.views.unstar",
"when": "viewItem =~ /gitlens:branch\\b.*?\\+starred\\b.*?/",
"group": "3_gitlens@1"
},
{
"command": "gitlens.views.compareWithRemote",
"when": "viewItem =~ /gitlens:branch\\b.*?\\+tracking\\b.*?/",
"group": "7_gitlens@1"
@ -4153,9 +4199,19 @@
{
"command": "gitlens.showCommitSearch",
"when": "viewItem =~ /gitlens:repository\\b/",
"group": "inline@10"
},
{
"command": "gitlens.views.star",
"when": "viewItem =~ /gitlens:repository\\b(?!.*?\\+starred\\b.*?)/",
"group": "inline@1"
},
{
"command": "gitlens.views.unstar",
"when": "viewItem =~ /gitlens:repository\\b.*?\\+starred\\b.*?/",
"group": "inline@2"
},
{
"command": "gitlens.views.push",
"when": "gitlens:hasRemotes && !gitlens:readonly && viewItem =~ /gitlens:repository\\b/",
"group": "inline@97",
@ -4192,6 +4248,16 @@
"group": "1_gitlens@2"
},
{
"command": "gitlens.views.star",
"when": "viewItem =~ /gitlens:repository\\b(?!.*?\\+starred\\b.*?)/",
"group": "2_gitlens@1"
},
{
"command": "gitlens.views.unstar",
"when": "viewItem =~ /gitlens:repository\\b.*?\\+starred\\b.*?/",
"group": "2_gitlens@1"
},
{
"command": "gitlens.views.pull",
"when": "gitlens:hasRemotes && !gitlens:readonly && viewItem == gitlens:status:upstream:behind",
"group": "inline@1"

+ 10
- 0
src/constants.ts 查看文件

@ -128,7 +128,17 @@ export const ImageMimetypes: { [key: string]: string } = {
'.bmp': 'image/bmp'
};
export interface StarredBranches {
[id: string]: boolean;
}
export interface StarredRepositories {
[id: string]: boolean;
}
export enum WorkspaceState {
StarredBranches = 'gitlens:starred:branches',
StarredRepositories = 'gitlens:starred:repositories',
ViewsCompareKeepResults = 'gitlens:views:compare:keepResults',
ViewsRepositoriesAutoRefresh = 'gitlens:views:repositories:autoRefresh',
ViewsSearchKeepResults = 'gitlens:views:search:keepResults'

+ 1
- 1
src/git/gitService.ts 查看文件

@ -1771,7 +1771,7 @@ export class GitService implements Disposable {
const repositories = [...(await this.getRepositories())];
if (repositories.length === 0) return repositories;
return repositories.sort((a, b) => a.index - b.index);
return repositories.sort((a, b) => (a.starred ? -1 : 1) - (b.starred ? -1 : 1) || a.index - b.index);
}
private async getRepositoryTree(): Promise<TernarySearchTree<Repository>> {

+ 34
- 0
src/git/models/branch.ts 查看文件

@ -1,4 +1,6 @@
'use strict';
import { StarredBranches, WorkspaceState } from '../../constants';
import { Container } from '../../container';
import { Git } from '../git';
import { GitStatus } from './status';
@ -9,6 +11,7 @@ export interface GitTrackingState {
export class GitBranch {
readonly detached: boolean;
readonly id: string;
readonly name: string;
readonly remote: boolean;
readonly tracking?: string;
@ -24,6 +27,8 @@ export class GitBranch {
behind: number = 0,
detached: boolean = false
) {
this.id = `${repoPath}|${name}`;
if (name.startsWith('remotes/')) {
name = name.substring(8);
this.remote = true;
@ -88,6 +93,35 @@ export class GitBranch {
return GitStatus.getUpstreamStatus(this.tracking, this.state, options);
}
get starred() {
const starred = Container.context.workspaceState.get<StarredBranches>(WorkspaceState.StarredBranches);
return starred !== undefined && starred[this.id] === true;
}
star() {
return this.updateStarred(true);
}
unstar() {
return this.updateStarred(false);
}
private async updateStarred(star: boolean) {
let starred = Container.context.workspaceState.get<StarredBranches>(WorkspaceState.StarredBranches);
if (starred === undefined) {
starred = Object.create(null);
}
if (star) {
starred![this.id] = true;
}
else {
const { [this.id]: _, ...rest } = starred!;
starred = rest;
}
await Container.context.workspaceState.update(WorkspaceState.StarredBranches, starred);
}
static getRemote(branch: string): string {
return branch.substring(0, branch.indexOf('/'));
}

+ 32
- 0
src/git/models/repository.ts 查看文件

@ -15,6 +15,7 @@ import {
WorkspaceFolder
} from 'vscode';
import { configuration, RemotesConfig } from '../../configuration';
import { StarredRepositories, WorkspaceState } from '../../constants';
import { Container } from '../../container';
import { Functions, gate, log } from '../../system';
import { GitBranch, GitDiffShortStat, GitRemote, GitStash, GitStatus, GitTag } from '../git';
@ -71,6 +72,7 @@ export class Repository implements Disposable {
}
readonly formattedName: string;
readonly id: string;
readonly index: number;
readonly name: string;
readonly normalizedPath: string;
@ -115,6 +117,7 @@ export class Repository implements Disposable {
this.index = folder.index;
this.normalizedPath = (path.endsWith('/') ? path : `${path}/`).toLowerCase();
this.id = this.normalizedPath;
this._suspended = suspended;
this._closed = closed;
@ -378,6 +381,35 @@ export class Repository implements Disposable {
}
}
get starred() {
const starred = Container.context.workspaceState.get<StarredRepositories>(WorkspaceState.StarredRepositories);
return starred !== undefined && starred[this.id] === true;
}
star() {
return this.updateStarred(true);
}
unstar() {
return this.updateStarred(false);
}
private async updateStarred(star: boolean) {
let starred = Container.context.workspaceState.get<StarredRepositories>(WorkspaceState.StarredRepositories);
if (starred === undefined) {
starred = Object.create(null);
}
if (star) {
starred![this.id] = true;
}
else {
const { [this.id]: _, ...rest } = starred!;
starred = rest;
}
await Container.context.workspaceState.update(WorkspaceState.StarredRepositories, starred);
}
startWatchingFileSystem() {
this._fsWatchCounter++;
if (this._fsWatcherDisposable !== undefined) return;

+ 25
- 9
src/views/nodes/branchNode.ts 查看文件

@ -4,7 +4,7 @@ import { ViewBranchesLayout } from '../../configuration';
import { GlyphChars } from '../../constants';
import { Container } from '../../container';
import { GitBranch, GitUri } from '../../git/gitService';
import { debug, gate, Iterables } from '../../system';
import { debug, gate, Iterables, log } from '../../system';
import { RepositoriesView } from '../repositoriesView';
import { BranchTrackingStatusNode } from './branchTrackingStatusNode';
import { CommitNode } from './commitNode';
@ -30,9 +30,9 @@ export class BranchNode extends ViewRefNode implements Pageabl
}
get id(): string {
return `gitlens:repository(${this.branch.repoPath}):${this._root ? 'root:' : ''}branch(${this.branch.name})${
this.branch.remote ? ':remote' : ''
}`;
return `gitlens:repository(${this.branch.repoPath})${this._root ? ':root:' : ''}:branch(${this.branch.name})${
this.branch.current ? '+current:' : ''
}${this.branch.remote ? '+remote' : ''}${this.branch.starred ? '+starred:' : ''}`;
}
get current(): boolean {
@ -43,13 +43,19 @@ export class BranchNode extends ViewRefNode implements Pageabl
const branchName = this.branch.getName();
if (this.view.config.branches.layout === ViewBranchesLayout.List) return branchName;
return this.current || GitBranch.isDetached(branchName) ? branchName : this.branch.getBasename();
return (this._root && this.current) || this.branch.detached || this.branch.starred
? branchName
: this.branch.getBasename();
}
get ref(): string {
return this.branch.ref;
}
get treeHierarchy(): string[] {
return this.branch.detached || this.branch.starred ? [this.branch.name] : this.branch.getName().split('/');
}
async getChildren(): Promise<ViewNode[]> {
if (this._children === undefined) {
const children = [];
@ -136,10 +142,8 @@ export class BranchNode extends ViewRefNode implements Pageabl
if (this.branch.remote) {
item.contextValue += '+remote';
}
else if (this.current) {
item.contextValue = this.branch.tracking
? ResourceType.CurrentBranchWithTracking
: ResourceType.CurrentBranch;
if (this.branch.starred) {
item.contextValue += '+starred';
}
if (this.branch.tracking) {
item.contextValue += '+tracking';
@ -156,6 +160,18 @@ export class BranchNode extends ViewRefNode implements Pageabl
return item;
}
@log()
async star() {
await this.branch.star();
void this.parent!.triggerChange();
}
@log()
async unstar() {
await this.branch.unstar();
void this.parent!.triggerChange();
}
@gate()
@debug()
refresh() {

+ 6
- 2
src/views/nodes/branchesNode.ts 查看文件

@ -30,7 +30,11 @@ export class BranchesNode extends ViewNode {
const branches = await this.repo.getBranches();
if (branches === undefined) return [];
branches.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }));
branches.sort(
(a, b) =>
(a.starred ? -1 : 1) - (b.starred ? -1 : 1) ||
a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' })
);
// filter local branches
const branchNodes = [
@ -42,7 +46,7 @@ export class BranchesNode extends ViewNode {
const hierarchy = Arrays.makeHierarchical(
branchNodes,
n => (n.branch.detached ? [n.branch.name] : n.branch.getName().split('/')),
n => n.treeHierarchy,
(...paths: string[]) => paths.join('/'),
this.view.config.files.compact
);

+ 6
- 2
src/views/nodes/remoteNode.ts 查看文件

@ -29,7 +29,11 @@ export class RemoteNode extends ViewNode {
const branches = await this.repo.getBranches();
if (branches === undefined) return [];
branches.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }));
branches.sort(
(a, b) =>
(a.starred ? -1 : 1) - (b.starred ? -1 : 1) ||
a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' })
);
// filter remote branches
const branchNodes = [
@ -43,7 +47,7 @@ export class RemoteNode extends ViewNode {
const hierarchy = Arrays.makeHierarchical(
branchNodes,
n => (n.branch.detached ? [n.branch.name] : n.branch.getName().split('/')),
n => n.treeHierarchy,
(...paths: string[]) => paths.join('/'),
this.view.config.files.compact
);

+ 18
- 3
src/views/nodes/repositoryNode.ts 查看文件

@ -41,7 +41,7 @@ export class RepositoryNode extends SubscribeableViewNode {
}
get id(): string {
return `gitlens:repository(${this.repo.path})`;
return `gitlens:repository(${this.repo.path})${this.repo.starred ? '+starred:' : ''}`;
}
async getChildren(): Promise<ViewNode[]> {
@ -158,15 +158,18 @@ export class RepositoryNode extends SubscribeableViewNode {
const item = new TreeItem(label, TreeItemCollapsibleState.Expanded);
item.contextValue = ResourceType.Repository;
if (this.repo.starred) {
item.contextValue += '+starred';
}
item.description = `${description || ''}${this.formatLastFetched({
prefix: `${Strings.pad(GlyphChars.Dot, 2, 2)}Last fetched `
})}`;
item.id = this.id;
item.tooltip = tooltip;
item.iconPath = {
dark: Container.context.asAbsolutePath(`images/dark/icon-repo${iconSuffix}.svg`),
light: Container.context.asAbsolutePath(`images/light/icon-repo${iconSuffix}.svg`)
};
item.id = this.id;
item.tooltip = tooltip;
void this.ensureSubscription();
@ -184,6 +187,18 @@ export class RepositoryNode extends SubscribeableViewNode {
}
@log()
async star() {
await this.repo.star();
void this.parent!.triggerChange();
}
@log()
async unstar() {
await this.repo.unstar();
void this.parent!.triggerChange();
}
@log()
push(options: { force?: boolean; progress?: boolean } = {}) {
return this.repo.push(options);
}

+ 13
- 0
src/views/viewCommands.ts 查看文件

@ -73,6 +73,9 @@ export class ViewCommands implements Disposable {
commands.registerCommand('gitlens.views.pushWithForce', n => this.push(n, true), this);
commands.registerCommand('gitlens.views.closeRepository', this.closeRepository, this);
commands.registerCommand('gitlens.views.star', this.star, this);
commands.registerCommand('gitlens.views.unstar', this.unstar, this);
commands.registerCommand('gitlens.views.exploreRepoRevision', this.exploreRepoRevision, this);
commands.registerCommand('gitlens.views.openChanges', this.openChanges, this);
@ -460,6 +463,16 @@ export class ViewCommands implements Disposable {
void (await Container.git.unStageFile(node.repoPath, node.file.fileName));
}
private star(node: BranchNode | RepositoryNode) {
if (node instanceof BranchNode || node instanceof RepositoryNode) return node.star();
return;
}
private unstar(node: BranchNode | RepositoryNode) {
if (node instanceof BranchNode || node instanceof RepositoryNode) return node.unstar();
return;
}
async terminalCheckoutBranch(node: BranchNode) {
if (!(node instanceof BranchNode)) return;

Loading…
取消
儲存