Bläddra i källkod

Adds ability to close (hide) repositories in the GitLens explorer

main
Eric Amodio 6 år sedan
förälder
incheckning
fe52312d4a
11 ändrade filer med 186 tillägg och 114 borttagningar
  1. +1
    -0
      CHANGELOG.md
  2. +19
    -0
      package.json
  3. +69
    -50
      src/git/models/repository.ts
  4. +17
    -4
      src/gitService.ts
  5. +4
    -2
      src/system/iterable.ts
  6. +57
    -52
      src/system/searchTree.ts
  7. +1
    -1
      src/views/activeRepositoryNode.ts
  8. +9
    -0
      src/views/explorerCommands.ts
  9. +7
    -4
      src/views/gitExplorer.ts
  10. +1
    -0
      src/views/repositoriesNode.ts
  11. +1
    -1
      src/views/statusNode.ts

+ 1
- 0
CHANGELOG.md Visa fil

@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
## [Unreleased] ## [Unreleased]
### Added ### Added
- Adds (re-adds) support for handling single files — closes [#321](https://github.com/eamodio/vscode-gitlens/issues/321) - Adds (re-adds) support for handling single files — closes [#321](https://github.com/eamodio/vscode-gitlens/issues/321)
- Adds *Close Repository* (`gitlens.explorers.closeRepository`) command to repository and repository status nodes in the *GitLens* explorer — closes (hides) the repository in the *GitLens* explorer
### Fixed ### Fixed
- Fixes [#384](https://github.com/eamodio/vscode-gitlens/issues/384) - Absolute dates not always honored in GitLens Results explorer - Fixes [#384](https://github.com/eamodio/vscode-gitlens/issues/384) - Absolute dates not always honored in GitLens Results explorer

+ 19
- 0
package.json Visa fil

@ -1595,6 +1595,11 @@
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.explorers.closeRepository",
"title": "Close Repository",
"category": "GitLens"
},
{
"command": "gitlens.explorers.compareAncestryWithWorking", "command": "gitlens.explorers.compareAncestryWithWorking",
"title": "Compare Ancestry with Working Tree", "title": "Compare Ancestry with Working Tree",
"category": "GitLens" "category": "GitLens"
@ -2151,6 +2156,10 @@
"when": "false" "when": "false"
}, },
{ {
"command": "gitlens.explorers.closeRepository",
"when": "false"
},
{
"command": "gitlens.explorers.compareAncestryWithWorking", "command": "gitlens.explorers.compareAncestryWithWorking",
"when": "false" "when": "false"
}, },
@ -2889,6 +2898,11 @@
"group": "1_gitlens@1" "group": "1_gitlens@1"
}, },
{ {
"command": "gitlens.explorers.closeRepository",
"when": "viewItem == gitlens:status",
"group": "8_gitlens@1"
},
{
"command": "gitlens.openBranchesInRemote", "command": "gitlens.openBranchesInRemote",
"when": "viewItem == gitlens:remote", "when": "viewItem == gitlens:remote",
"group": "1_gitlens@1" "group": "1_gitlens@1"
@ -2909,6 +2923,11 @@
"group": "1_gitlens@1" "group": "1_gitlens@1"
}, },
{ {
"command": "gitlens.explorers.closeRepository",
"when": "viewItem == gitlens:repository",
"group": "8_gitlens@1"
},
{
"command": "gitlens.resultsExplorer.swapComparision", "command": "gitlens.resultsExplorer.swapComparision",
"when": "viewItem == gitlens:results:comparison", "when": "viewItem == gitlens:results:comparison",
"group": "inline@1" "group": "inline@1"

+ 69
- 50
src/git/models/repository.ts Visa fil

@ -10,6 +10,7 @@ import * as _path from 'path';
export enum RepositoryChange { export enum RepositoryChange {
Config = 'config', Config = 'config',
Closed = 'closed',
// FileSystem = 'file-system', // FileSystem = 'file-system',
Remotes = 'remotes', Remotes = 'remotes',
Repository = 'repository', Repository = 'repository',
@ -83,8 +84,9 @@ export class Repository extends Disposable {
public readonly folder: WorkspaceFolder, public readonly folder: WorkspaceFolder,
public readonly path: string, public readonly path: string,
public readonly root: boolean, public readonly root: boolean,
private readonly onAnyRepositoryChanged: (repo: Repository) => void,
suspended: boolean
private readonly onAnyRepositoryChanged: (repo: Repository, reason: RepositoryChange) => void,
suspended: boolean,
closed: boolean = false
) { ) {
super(() => this.dispose()); super(() => this.dispose());
@ -103,7 +105,9 @@ export class Repository extends Disposable {
this.normalizedPath = (this.path.endsWith('/') ? this.path : `${this.path}/`).toLowerCase(); this.normalizedPath = (this.path.endsWith('/') ? this.path : `${this.path}/`).toLowerCase();
this._suspended = suspended; this._suspended = suspended;
this._closed = closed;
// TODO: createFileSystemWatcher doesn't work unless the folder is part of the workspaceFolders
const watcher = workspace.createFileSystemWatcher(new RelativePattern(folder, '{**/.git/config,**/.git/index,**/.git/HEAD,**/.git/refs/stash,**/.git/refs/heads/**,**/.git/refs/remotes/**,**/.git/refs/tags/**,**/.gitignore}')); const watcher = workspace.createFileSystemWatcher(new RelativePattern(folder, '{**/.git/config,**/.git/index,**/.git/HEAD,**/.git/refs/stash,**/.git/refs/heads/**,**/.git/refs/remotes/**,**/.git/refs/tags/**,**/.gitignore}'));
this._disposable = Disposable.from( this._disposable = Disposable.from(
watcher, watcher,
@ -178,59 +182,21 @@ export class Repository extends Disposable {
return; return;
} }
this.onAnyRepositoryChanged(this);
this.onAnyRepositoryChanged(this, RepositoryChange.Repository);
this.fireChange(RepositoryChange.Repository); this.fireChange(RepositoryChange.Repository);
} }
private fireChange(...reasons: RepositoryChange[]) {
if (this._fireChangeDebounced === undefined) {
this._fireChangeDebounced = Functions.debounce(this.fireChangeCore, 250);
}
if (this._pendingChanges.repo === undefined) {
this._pendingChanges.repo = new RepositoryChangeEvent(this);
}
const e = this._pendingChanges.repo;
for (const reason of reasons) {
if (!e.changes.includes(reason)) {
e.changes.push(reason);
}
}
if (this._suspended) return;
this._fireChangeDebounced(e);
}
private fireChangeCore(e: RepositoryChangeEvent) {
this._pendingChanges.repo = undefined;
this._onDidChange.fire(e);
private _closed: boolean = false;
get closed(): boolean {
return this._closed;
} }
private fireFileSystemChange(uri: Uri) {
if (this._fireFileSystemChangeDebounced === undefined) {
this._fireFileSystemChangeDebounced = Functions.debounce(this.fireFileSystemChangeCore, 2500);
set closed(value: boolean) {
const changed = this._closed !== value;
this._closed = value;
if (changed) {
this.onAnyRepositoryChanged(this, RepositoryChange.Closed);
this.fireChange(RepositoryChange.Closed);
} }
if (this._pendingChanges.fs === undefined) {
this._pendingChanges.fs = { repository: this, uris: [] };
}
const e = this._pendingChanges.fs;
e.uris.push(uri);
if (this._suspended) return;
this._fireFileSystemChangeDebounced(e);
}
private fireFileSystemChangeCore(e: RepositoryFileSystemChangeEvent) {
this._pendingChanges.fs = undefined;
this._onDidChangeFileSystem.fire(e);
} }
containsUri(uri: Uri) { containsUri(uri: Uri) {
@ -313,6 +279,7 @@ export class Repository extends Disposable {
this._fsWatchCounter++; this._fsWatchCounter++;
if (this._fsWatcherDisposable !== undefined) return; if (this._fsWatcherDisposable !== undefined) return;
// TODO: createFileSystemWatcher doesn't work unless the folder is part of the workspaceFolders
const watcher = workspace.createFileSystemWatcher(new RelativePattern(this.folder, `**`)); const watcher = workspace.createFileSystemWatcher(new RelativePattern(this.folder, `**`));
this._fsWatcherDisposable = Disposable.from( this._fsWatcherDisposable = Disposable.from(
watcher, watcher,
@ -333,4 +300,56 @@ export class Repository extends Disposable {
suspend() { suspend() {
this._suspended = true; this._suspended = true;
} }
private fireChange(...reasons: RepositoryChange[]) {
if (this._fireChangeDebounced === undefined) {
this._fireChangeDebounced = Functions.debounce(this.fireChangeCore, 250);
}
if (this._pendingChanges.repo === undefined) {
this._pendingChanges.repo = new RepositoryChangeEvent(this);
}
const e = this._pendingChanges.repo;
for (const reason of reasons) {
if (!e.changes.includes(reason)) {
e.changes.push(reason);
}
}
if (this._suspended) return;
this._fireChangeDebounced(e);
}
private fireChangeCore(e: RepositoryChangeEvent) {
this._pendingChanges.repo = undefined;
this._onDidChange.fire(e);
}
private fireFileSystemChange(uri: Uri) {
if (this._fireFileSystemChangeDebounced === undefined) {
this._fireFileSystemChangeDebounced = Functions.debounce(this.fireFileSystemChangeCore, 2500);
}
if (this._pendingChanges.fs === undefined) {
this._pendingChanges.fs = { repository: this, uris: [] };
}
const e = this._pendingChanges.fs;
e.uris.push(uri);
if (this._suspended) return;
this._fireFileSystemChangeDebounced(e);
}
private fireFileSystemChangeCore(e: RepositoryFileSystemChangeEvent) {
this._pendingChanges.fs = undefined;
this._onDidChangeFileSystem.fire(e);
}
} }

+ 17
- 4
src/gitService.ts Visa fil

@ -5,7 +5,7 @@ import { configuration, IRemotesConfig } from './configuration';
import { CommandContext, DocumentSchemes, setCommandContext } from './constants'; import { CommandContext, DocumentSchemes, setCommandContext } from './constants';
import { Container } from './container'; import { Container } from './container';
import { RemoteProviderFactory, RemoteProviderMap } from './git/remotes/factory'; import { RemoteProviderFactory, RemoteProviderMap } from './git/remotes/factory';
import { CommitFormatting, Git, GitAuthor, GitBlame, GitBlameCommit, GitBlameLine, GitBlameLines, GitBlameParser, GitBranch, GitBranchParser, GitCommit, GitCommitType, GitDiff, GitDiffChunkLine, GitDiffParser, GitDiffShortStat, GitLog, GitLogCommit, GitLogParser, GitRemote, GitRemoteParser, GitStash, GitStashParser, GitStatus, GitStatusFile, GitStatusParser, GitTag, GitTagParser, IGit, Repository } from './git/git';
import { CommitFormatting, Git, GitAuthor, GitBlame, GitBlameCommit, GitBlameLine, GitBlameLines, GitBlameParser, GitBranch, GitBranchParser, GitCommit, GitCommitType, GitDiff, GitDiffChunkLine, GitDiffParser, GitDiffShortStat, GitLog, GitLogCommit, GitLogParser, GitRemote, GitRemoteParser, GitStash, GitStashParser, GitStatus, GitStatusFile, GitStatusParser, GitTag, GitTagParser, IGit, Repository, RepositoryChange } from './git/git';
import { CachedBlame, CachedDiff, CachedLog, GitDocumentState, TrackedDocument } from './trackers/gitDocumentTracker'; import { CachedBlame, CachedDiff, CachedLog, GitDocumentState, TrackedDocument } from './trackers/gitDocumentTracker';
import { GitUri, IGitCommitInfo } from './git/gitUri'; import { GitUri, IGitCommitInfo } from './git/gitUri';
import { Logger } from './logger'; import { Logger } from './logger';
@ -75,8 +75,17 @@ export class GitService extends Disposable {
return Container.config.advanced.caching.enabled; return Container.config.advanced.caching.enabled;
} }
private onAnyRepositoryChanged(repo: Repository) {
private onAnyRepositoryChanged(repo: Repository, reason: RepositoryChange) {
this._trackedCache.clear(); this._trackedCache.clear();
if (reason === RepositoryChange.Closed) {
// Send a notification that the repositories changed
setImmediate(async () => {
await this.updateContext(this._repositoryTree);
this.fireRepositoriesChanged();
});
}
} }
private onConfigurationChanged(e: ConfigurationChangeEvent) { private onConfigurationChanged(e: ConfigurationChangeEvent) {
@ -1248,9 +1257,13 @@ export class GitService extends Disposable {
return Container.git.getActiveRepoPath(editor); return Container.git.getActiveRepoPath(editor);
} }
async getRepositories(): Promise<Iterable<Repository>> {
async getRepositories(predicate?: (repo: Repository) => boolean): Promise<Iterable<Repository>> {
const repositoryTree = await this.getRepositoryTree(); const repositoryTree = await this.getRepositoryTree();
return repositoryTree.values();
const values = repositoryTree.values();
return predicate !== undefined
? Iterables.filter(values, predicate)
: values;
} }
private async getRepositoryTree(): Promise<TernarySearchTree<Repository>> { private async getRepositoryTree(): Promise<TernarySearchTree<Repository>> {

+ 4
- 2
src/system/iterable.ts Visa fil

@ -1,7 +1,7 @@
'use strict'; 'use strict';
export namespace Iterables { export namespace Iterables {
export function count<T>(source: Iterable<T> | IterableIterator<T>): number {
export function count<T>(source: Iterable<T> | IterableIterator<T>, predicate?: (item: T) => boolean): number {
let count = 0; let count = 0;
let next: IteratorResult<T>; let next: IteratorResult<T>;
@ -9,7 +9,9 @@ export namespace Iterables {
next = (source as IterableIterator<T>).next(); next = (source as IterableIterator<T>).next();
if (next.done) break; if (next.done) break;
count++;
if (predicate === undefined || predicate(next.value)) {
count++;
}
} }
return count; return count;

+ 57
- 52
src/system/searchTree.ts Visa fil

@ -6,7 +6,6 @@ import { Iterables } from '../system/iterable';
export interface IKeyIterator { export interface IKeyIterator {
reset(key: string): this; reset(key: string): this;
next(): this; next(): this;
join(parts: string[]): string;
hasNext(): boolean; hasNext(): boolean;
cmp(a: string): number; cmp(a: string): number;
@ -29,10 +28,6 @@ export class StringIterator implements IKeyIterator {
return this; return this;
} }
join(parts: string[]): string {
return parts.join('');
}
hasNext(): boolean { hasNext(): boolean {
return this._pos < this._value.length - 1; return this._pos < this._value.length - 1;
} }
@ -48,10 +43,18 @@ export class StringIterator implements IKeyIterator {
} }
} }
export class PathIterator implements IKeyIterator {
const enum CharCode {
/**
* The `/` character.
*/
Slash = 47,
/**
* The `\` character.
*/
Backslash = 92
}
private static readonly _fwd = '/'.charCodeAt(0);
private static readonly _bwd = '\\'.charCodeAt(0);
export class PathIterator implements IKeyIterator {
private _value!: string; private _value!: string;
private _from!: number; private _from!: number;
@ -68,17 +71,13 @@ export class PathIterator implements IKeyIterator {
return this._to < this._value.length; return this._to < this._value.length;
} }
join(parts: string[]): string {
return parts.join('/');
}
next(): this { next(): this {
// this._data = key.split(/[\\/]/).filter(s => !!s); // this._data = key.split(/[\\/]/).filter(s => !!s);
this._from = this._to; this._from = this._to;
let justSeps = true; let justSeps = true;
for (; this._to < this._value.length; this._to++) { for (; this._to < this._value.length; this._to++) {
const ch = this._value.charCodeAt(this._to); const ch = this._value.charCodeAt(this._to);
if (ch === PathIterator._fwd || ch === PathIterator._bwd) {
if (ch === CharCode.Slash || ch === CharCode.Backslash) {
if (justSeps) { if (justSeps) {
this._from++; this._from++;
} else { } else {
@ -121,14 +120,15 @@ export class PathIterator implements IKeyIterator {
} }
class TernarySearchTreeNode<E> { class TernarySearchTreeNode<E> {
str!: string;
element: E | undefined;
segment!: string;
value: E | undefined;
key!: string;
left: TernarySearchTreeNode<E> | undefined; left: TernarySearchTreeNode<E> | undefined;
mid: TernarySearchTreeNode<E> | undefined; mid: TernarySearchTreeNode<E> | undefined;
right: TernarySearchTreeNode<E> | undefined; right: TernarySearchTreeNode<E> | undefined;
isEmpty(): boolean { isEmpty(): boolean {
return this.left === undefined && this.mid === undefined && this.right === undefined && this.element === undefined;
return !this.left && !this.mid && !this.right && !this.value;
} }
} }
@ -159,17 +159,17 @@ export class TernarySearchTree {
if (!this._root) { if (!this._root) {
this._root = new TernarySearchTreeNode<E>(); this._root = new TernarySearchTreeNode<E>();
this._root.str = iter.value();
this._root.segment = iter.value();
} }
node = this._root; node = this._root;
while (true) { while (true) {
const val = iter.cmp(node.str);
const val = iter.cmp(node.segment);
if (val > 0) { if (val > 0) {
// left // left
if (!node.left) { if (!node.left) {
node.left = new TernarySearchTreeNode<E>(); node.left = new TernarySearchTreeNode<E>();
node.left.str = iter.value();
node.left.segment = iter.value();
} }
node = node.left; node = node.left;
@ -177,7 +177,7 @@ export class TernarySearchTree {
// right // right
if (!node.right) { if (!node.right) {
node.right = new TernarySearchTreeNode<E>(); node.right = new TernarySearchTreeNode<E>();
node.right.str = iter.value();
node.right.segment = iter.value();
} }
node = node.right; node = node.right;
@ -186,15 +186,16 @@ export class TernarySearchTree {
iter.next(); iter.next();
if (!node.mid) { if (!node.mid) {
node.mid = new TernarySearchTreeNode<E>(); node.mid = new TernarySearchTreeNode<E>();
node.mid.str = iter.value();
node.mid.segment = iter.value();
} }
node = node.mid; node = node.mid;
} else { } else {
break; break;
} }
} }
const oldElement = node.element;
node.element = element;
const oldElement = node.value;
node.value = element;
node.key = key;
return oldElement; return oldElement;
} }
@ -202,7 +203,7 @@ export class TernarySearchTree {
const iter = this._iter.reset(key); const iter = this._iter.reset(key);
let node = this._root; let node = this._root;
while (node) { while (node) {
const val = iter.cmp(node.str);
const val = iter.cmp(node.segment);
if (val > 0) { if (val > 0) {
// left // left
node = node.left; node = node.left;
@ -217,7 +218,7 @@ export class TernarySearchTree {
break; break;
} }
} }
return node ? node.element : undefined;
return node ? node.value : undefined;
} }
delete(key: string): void { delete(key: string): void {
@ -227,7 +228,7 @@ export class TernarySearchTree {
// find and unset node // find and unset node
while (node) { while (node) {
const val = iter.cmp(node.str);
const val = iter.cmp(node.segment);
if (val > 0) { if (val > 0) {
// left // left
stack.push([1, node]); stack.push([1, node]);
@ -243,7 +244,7 @@ export class TernarySearchTree {
node = node.mid; node = node.mid;
} else { } else {
// remove element // remove element
node.element = undefined;
node.value = undefined;
// clean up empty nodes // clean up empty nodes
while (stack.length > 0 && node.isEmpty()) { while (stack.length > 0 && node.isEmpty()) {
@ -265,7 +266,7 @@ export class TernarySearchTree {
let node = this._root; let node = this._root;
let candidate: E | undefined; let candidate: E | undefined;
while (node) { while (node) {
const val = iter.cmp(node.str);
const val = iter.cmp(node.segment);
if (val > 0) { if (val > 0) {
// left // left
node = node.left; node = node.left;
@ -275,20 +276,20 @@ export class TernarySearchTree {
} else if (iter.hasNext()) { } else if (iter.hasNext()) {
// mid // mid
iter.next(); iter.next();
candidate = node.element || candidate;
candidate = node.value || candidate;
node = node.mid; node = node.mid;
} else { } else {
break; break;
} }
} }
return node && node.element || candidate;
return node && node.value || candidate;
} }
findSuperstr(key: string): TernarySearchTree<E> | undefined { findSuperstr(key: string): TernarySearchTree<E> | undefined {
const iter = this._iter.reset(key); const iter = this._iter.reset(key);
let node = this._root; let node = this._root;
while (node) { while (node) {
const val = iter.cmp(node.str);
const val = iter.cmp(node.segment);
if (val > 0) { if (val > 0) {
// left // left
node = node.left; node = node.left;
@ -313,44 +314,43 @@ export class TernarySearchTree {
} }
forEach(callback: (value: E, index: string) => any) { forEach(callback: (value: E, index: string) => any) {
this._forEach(this._root!, [], callback);
this._forEach(this._root!, callback);
} }
private _forEach(node: TernarySearchTreeNode<E>, parts: string[], callback: (value: E, index: string) => any) {
private _forEach(node: TernarySearchTreeNode<E>, callback: (value: E, index: string) => any) {
if (node === undefined) return; if (node === undefined) return;
// left // left
this._forEach(node.left!, parts, callback);
this._forEach(node.left!, callback);
// node // node
parts.push(node.str);
if (node.element) {
callback(node.element, this._iter.join(parts));
if (node.value) {
callback(node.value, node.key);
} }
// mid // mid
this._forEach(node.mid!, parts, callback);
parts.pop();
this._forEach(node.mid!, callback);
// right // right
this._forEach(node.right!, parts, callback);
this._forEach(node.right!, callback);
} }
any(): boolean { any(): boolean {
return this._root !== undefined && !this._root.isEmpty(); return this._root !== undefined && !this._root.isEmpty();
} }
count(): number {
count(predicate?: (entry: E) => boolean): number {
if (this._root === undefined || this._root.isEmpty()) return 0; if (this._root === undefined || this._root.isEmpty()) return 0;
return Iterables.count(this.entries());
return Iterables.count(this.entries(), predicate === undefined ? undefined : ([e]) => predicate(e));
} }
entries(): Iterable<[E, string]> { entries(): Iterable<[E, string]> {
return this._iterator(this._root!, []);
return this._iterator(this._root!);
} }
values(): Iterable<E> { values(): Iterable<E> {
return Iterables.map(this.entries(), e => e[0]);
return Iterables.map(this.entries(), ([e]) => e);
} }
highlander(): [E, string] | undefined { highlander(): [E, string] | undefined {
@ -375,22 +375,27 @@ export class TernarySearchTree {
return value; return value;
} }
private *_iterator(node: TernarySearchTreeNode<E> | undefined, parts: string[]): IterableIterator<[E, string]> {
some(predicate: (entry: E) => boolean): boolean {
if (this._root === undefined || this._root.isEmpty()) return false;
return Iterables.some(this.entries(), ([e]) => predicate(e));
}
private *_iterator(node: TernarySearchTreeNode<E> | undefined): IterableIterator<[E, string]> {
if (node !== undefined) { if (node !== undefined) {
// left // left
yield* this._iterator(node.left!, parts);
yield* this._iterator(node.left!);
// node // node
parts.push(node.str);
if (node.element) {
yield [node.element, this._iter.join(parts)];
if (node.value) {
yield [node.value, node.key];
} }
// mid // mid
yield* this._iterator(node.mid!, parts);
parts.pop();
yield* this._iterator(node.mid!);
// right // right
yield* this._iterator(node.right!, parts);
yield* this._iterator(node.right!);
} }
} }
} }

+ 1
- 1
src/views/activeRepositoryNode.ts Visa fil

@ -58,7 +58,7 @@ export class ActiveRepositoryNode extends ExplorerNode {
if (this._repositoryNode !== undefined && this._repositoryNode.repo.path === repoPath) return; if (this._repositoryNode !== undefined && this._repositoryNode.repo.path === repoPath) return;
const repo = await Container.git.getRepository(repoPath); const repo = await Container.git.getRepository(repoPath);
if (repo === undefined) {
if (repo === undefined || repo.closed) {
if (this._repositoryNode !== undefined) { if (this._repositoryNode !== undefined) {
changed = true; changed = true;

+ 9
- 0
src/views/explorerCommands.ts Visa fil

@ -6,6 +6,8 @@ import { BranchNode, ExplorerNode, TagNode } from '../views/gitExplorer';
import { CommitFileNode, CommitNode, ExplorerRefNode, RemoteNode, StashFileNode, StashNode, StatusFileCommitsNode, StatusUpstreamNode } from './explorerNodes'; import { CommitFileNode, CommitNode, ExplorerRefNode, RemoteNode, StashFileNode, StashNode, StatusFileCommitsNode, StatusUpstreamNode } from './explorerNodes';
import { Commands, DiffWithCommandArgs, DiffWithCommandArgsRevision, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, openEditor, OpenFileInRemoteCommandArgs, OpenFileRevisionCommandArgs } from '../commands'; import { Commands, DiffWithCommandArgs, DiffWithCommandArgsRevision, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, openEditor, OpenFileInRemoteCommandArgs, OpenFileRevisionCommandArgs } from '../commands';
import { GitService, GitUri } from '../gitService'; import { GitService, GitUri } from '../gitService';
import { RepositoryNode } from './repositoryNode';
import { StatusNode } from './statusNode';
export interface RefreshNodeCommandArgs { export interface RefreshNodeCommandArgs {
maxCount?: number; maxCount?: number;
@ -36,6 +38,7 @@ export class ExplorerCommands extends Disposable {
commands.registerCommand('gitlens.explorers.openChangedFileChangesWithWorking', this.openChangedFileChangesWithWorking, this); commands.registerCommand('gitlens.explorers.openChangedFileChangesWithWorking', this.openChangedFileChangesWithWorking, this);
commands.registerCommand('gitlens.explorers.openChangedFileRevisions', this.openChangedFileRevisions, this); commands.registerCommand('gitlens.explorers.openChangedFileRevisions', this.openChangedFileRevisions, this);
commands.registerCommand('gitlens.explorers.applyChanges', this.applyChanges, this); commands.registerCommand('gitlens.explorers.applyChanges', this.applyChanges, this);
commands.registerCommand('gitlens.explorers.closeRepository', this.closeRepository, this);
commands.registerCommand('gitlens.explorers.compareAncestryWithWorking', this.compareAncestryWithWorking, this); commands.registerCommand('gitlens.explorers.compareAncestryWithWorking', this.compareAncestryWithWorking, this);
commands.registerCommand('gitlens.explorers.compareWithHead', this.compareWithHead, this); commands.registerCommand('gitlens.explorers.compareWithHead', this.compareWithHead, this);
commands.registerCommand('gitlens.explorers.compareWithRemote', this.compareWithRemote, this); commands.registerCommand('gitlens.explorers.compareWithRemote', this.compareWithRemote, this);
@ -68,6 +71,12 @@ export class ExplorerCommands extends Disposable {
return this.openFile(node); return this.openFile(node);
} }
private closeRepository(node: RepositoryNode | StatusNode) {
if (!(node instanceof RepositoryNode) && !(node instanceof StatusNode)) return;
node.repo.closed = true;
}
private compareWithHead(node: ExplorerNode) { private compareWithHead(node: ExplorerNode) {
if (!(node instanceof ExplorerRefNode)) return; if (!(node instanceof ExplorerRefNode)) return;

+ 7
- 4
src/views/gitExplorer.ts Visa fil

@ -330,14 +330,17 @@ export class GitExplorer extends Disposable implements TreeDataProvider
this._loading = promise.then(_ => Functions.wait(0)); this._loading = promise.then(_ => Functions.wait(0));
const repositories = [...await promise]; const repositories = [...await promise];
if (repositories.length === 0) return undefined; // new MessageNode('No repositories found');
if (repositories.length === 0) return undefined;
if (repositories.length === 1) {
const repo = repositories[0];
const openedRepos = repositories.filter(r => !r.closed);
if (openedRepos.length === 0) return undefined;
if (openedRepos.length === 1) {
const repo = openedRepos[0];
return new RepositoryNode(GitUri.fromRepoPath(repo.path), repo, this, true); return new RepositoryNode(GitUri.fromRepoPath(repo.path), repo, this, true);
} }
return new RepositoriesNode(repositories, this);
return new RepositoriesNode(openedRepos, this);
} }
} }
} }

+ 1
- 0
src/views/repositoriesNode.ts Visa fil

@ -19,6 +19,7 @@ export class RepositoriesNode extends ExplorerNode {
if (this.children === undefined) { if (this.children === undefined) {
this.children = this.repositories this.children = this.repositories
.sort((a, b) => a.index - b.index) .sort((a, b) => a.index - b.index)
.filter(repo => !repo.closed)
.map(repo => new RepositoryNode(GitUri.fromRepoPath(repo.path), repo, this.explorer)); .map(repo => new RepositoryNode(GitUri.fromRepoPath(repo.path), repo, this.explorer));
if (this.children.length > 1) { if (this.children.length > 1) {

+ 1
- 1
src/views/statusNode.ts Visa fil

@ -11,7 +11,7 @@ export class StatusNode extends ExplorerNode {
constructor( constructor(
uri: GitUri, uri: GitUri,
private readonly repo: Repository,
public readonly repo: Repository,
private readonly explorer: GitExplorer, private readonly explorer: GitExplorer,
private readonly active: boolean = false private readonly active: boolean = false
) { ) {

Laddar…
Avbryt
Spara