Browse Source

Adds configurable delay to repo file watching

- Subscribers can specify the delay and we use the min delay of current subscriptions
 - Defaults Graph & Commit Details to 1s delay (rather than 2.5s)
main
Eric Amodio 1 year ago
parent
commit
300cb231f0
7 changed files with 108 additions and 39 deletions
  1. +61
    -34
      src/git/models/repository.ts
  2. +1
    -1
      src/plus/webviews/graph/graphWebview.ts
  3. +42
    -0
      src/system/iterable.ts
  4. +1
    -1
      src/views/nodes/fileHistoryNode.ts
  5. +1
    -1
      src/views/nodes/lineHistoryNode.ts
  6. +1
    -1
      src/views/nodes/repositoryNode.ts
  7. +1
    -1
      src/webviews/commitDetails/commitDetailsWebview.ts

+ 61
- 34
src/git/models/repository.ts View File

@ -1,6 +1,6 @@
import type { CancellationToken, ConfigurationChangeEvent, Event, Uri, WorkspaceFolder } from 'vscode';
import { Disposable, EventEmitter, ProgressLocation, RelativePattern, window, workspace } from 'vscode';
import { md5 } from '@env/crypto';
import { md5, uuid } from '@env/crypto';
import { ForcePushMode } from '../../@types/vscode.git.enums';
import type { CreatePullRequestActionContext } from '../../api/gitlens';
import type { RepositoriesSorting } from '../../config';
@ -16,8 +16,9 @@ import { configuration } from '../../system/configuration';
import { formatDate, fromNow } from '../../system/date';
import { gate } from '../../system/decorators/gate';
import { debug, log, logName } from '../../system/decorators/log';
import type { Deferrable } from '../../system/function';
import { debounce } from '../../system/function';
import { filter, join, some } from '../../system/iterable';
import { filter, join, min, some } from '../../system/iterable';
import { getLoggableName, Logger } from '../../system/logger';
import { getLogScope } from '../../system/logger.scope';
import { updateRecordValue } from '../../system/object';
@ -91,6 +92,9 @@ export const enum RepositoryChangeComparisonMode {
Exclusive,
}
const defaultFileSystemChangeDelay = 2500;
const defaultRepositoryChangeDelay = 250;
export class RepositoryChangeEvent {
private readonly _changes: Set<RepositoryChange>;
@ -235,10 +239,8 @@ export class Repository implements Disposable {
private _branch: Promise<GitBranch | undefined> | undefined;
private readonly _disposable: Disposable;
private _fireChangeDebounced: (() => void) | undefined = undefined;
private _fireFileSystemChangeDebounced: (() => void) | undefined = undefined;
private _fsWatchCounter = 0;
private _fsWatcherDisposable: Disposable | undefined;
private _fireChangeDebounced: Deferrable<() => void> | undefined = undefined;
private _fireFileSystemChangeDebounced: Deferrable<() => void> | undefined = undefined;
private _pendingFileSystemChange?: RepositoryFileSystemChangeEvent;
private _pendingRepoChange?: RepositoryChangeEvent;
private _suspended: boolean;
@ -365,7 +367,7 @@ export class Repository implements Disposable {
}
dispose() {
this.stopWatchingFileSystem();
this.unWatchFileSystem(true);
this._disposable.dispose();
}
@ -1002,7 +1004,7 @@ export class Repository implements Disposable {
}
if (this._pendingFileSystemChange != null) {
this._fireFileSystemChangeDebounced!();
this._fireFileSystemChangeDebounced?.();
}
}
@ -1138,8 +1140,32 @@ export class Repository implements Disposable {
return this._etagFileSystem;
}
startWatchingFileSystem(): Disposable {
this._fsWatchCounter++;
suspend() {
this._suspended = true;
}
@log()
tag(...args: string[]) {
void this.runTerminalCommand('tag', ...args);
}
@log()
tagDelete(tags: GitTagReference | GitTagReference[]) {
if (!Array.isArray(tags)) {
tags = [tags];
}
const args = ['--delete'];
void this.runTerminalCommand('tag', ...args, ...tags.map(t => t.ref));
}
private _fsWatcherDisposable: Disposable | undefined;
private _fsWatchers = new Map<string, number>();
private _fsChangeDelay: number = defaultFileSystemChangeDelay;
watchFileSystem(delay: number = defaultFileSystemChangeDelay): Disposable {
const id = uuid();
this._fsWatchers.set(id, delay);
if (this._fsWatcherDisposable == null) {
const watcher = workspace.createFileSystemWatcher(new RelativePattern(this.uri, '**'));
this._fsWatcherDisposable = Disposable.from(
@ -1152,36 +1178,34 @@ export class Repository implements Disposable {
this._etagFileSystem = Date.now();
}
return { dispose: () => this.stopWatchingFileSystem() };
this.ensureMinFileSystemChangeDelay();
return { dispose: () => this.unWatchFileSystem(id) };
}
stopWatchingFileSystem(force: boolean = false) {
if (this._fsWatcherDisposable == null) return;
if (--this._fsWatchCounter > 0 && !force) return;
private unWatchFileSystem(forceOrId: true | string) {
if (typeof forceOrId !== 'boolean') {
this._fsWatchers.delete(forceOrId);
if (this._fsWatchers.size !== 0) {
this.ensureMinFileSystemChangeDelay();
return;
}
}
this._etagFileSystem = undefined;
this._fsWatchCounter = 0;
this._fsWatcherDisposable.dispose();
this._fsChangeDelay = defaultFileSystemChangeDelay;
this._fsWatchers.clear();
this._fsWatcherDisposable?.dispose();
this._fsWatcherDisposable = undefined;
}
suspend() {
this._suspended = true;
}
private ensureMinFileSystemChangeDelay() {
const minDelay = min(this._fsWatchers.values());
if (minDelay === this._fsChangeDelay) return;
@log()
tag(...args: string[]) {
void this.runTerminalCommand('tag', ...args);
}
@log()
tagDelete(tags: GitTagReference | GitTagReference[]) {
if (!Array.isArray(tags)) {
tags = [tags];
}
const args = ['--delete'];
void this.runTerminalCommand('tag', ...args, ...tags.map(t => t.ref));
this._fsChangeDelay = minDelay;
this._fireFileSystemChangeDebounced?.flush();
this._fireFileSystemChangeDebounced = undefined;
}
@debug()
@ -1191,7 +1215,7 @@ export class Repository implements Disposable {
this._updatedAt = Date.now();
if (this._fireChangeDebounced == null) {
this._fireChangeDebounced = debounce(this.fireChangeCore.bind(this), 250);
this._fireChangeDebounced = debounce(this.fireChangeCore.bind(this), defaultRepositoryChangeDelay);
}
this._pendingRepoChange = this._pendingRepoChange?.with(changes) ?? new RepositoryChangeEvent(this, changes);
@ -1224,7 +1248,10 @@ export class Repository implements Disposable {
this._updatedAt = Date.now();
if (this._fireFileSystemChangeDebounced == null) {
this._fireFileSystemChangeDebounced = debounce(this.fireFileSystemChangeCore.bind(this), 2500);
this._fireFileSystemChangeDebounced = debounce(
this.fireFileSystemChangeCore.bind(this),
this._fsChangeDelay,
);
}
if (this._pendingFileSystemChange == null) {

+ 1
- 1
src/plus/webviews/graph/graphWebview.ts View File

@ -1544,7 +1544,7 @@ export class GraphWebviewProvider implements WebviewProvider
this._repositoryEventsDisposable = Disposable.from(
repo.onDidChange(this.onRepositoryChanged, this),
repo.startWatchingFileSystem(),
repo.watchFileSystem(1000),
repo.onDidChangeFileSystem(this.onRepositoryFileSystemChanged, this),
onDidChangeContext(key => {
if (key !== 'gitlens:hasConnectedRemotes') return;

+ 42
- 0
src/system/iterable.ts View File

@ -170,6 +170,48 @@ export function* map(
}
}
export function max(source: Iterable<number> | IterableIterator<number>): number;
export function max<T>(source: Iterable<T> | IterableIterator<T>, selector: (item: T) => number): number;
export function max<T>(source: Iterable<T> | IterableIterator<T>, selector?: (item: T) => number): number {
let max = Number.NEGATIVE_INFINITY;
if (selector == null) {
for (const item of source as Iterable<number> | IterableIterator<number>) {
if (item > max) {
max = item;
}
}
} else {
for (const item of source) {
const value = selector(item);
if (value > max) {
max = value;
}
}
}
return max;
}
export function min(source: Iterable<number> | IterableIterator<number>): number;
export function min<T>(source: Iterable<T> | IterableIterator<T>, selector: (item: T) => number): number;
export function min<T>(source: Iterable<T> | IterableIterator<T>, selector?: (item: T) => number): number {
let min = Number.POSITIVE_INFINITY;
if (selector == null) {
for (const item of source as Iterable<number> | IterableIterator<number>) {
if (item < min) {
min = item;
}
}
} else {
for (const item of source) {
const value = selector(item);
if (value < min) {
min = value;
}
}
}
return min;
}
export function next<T>(source: IterableIterator<T>): T {
return source.next().value as T;
}

+ 1
- 1
src/views/nodes/fileHistoryNode.ts View File

@ -189,7 +189,7 @@ export class FileHistoryNode
const subscription = Disposable.from(
weakEvent(repo.onDidChange, this.onRepositoryChanged, this),
weakEvent(repo.onDidChangeFileSystem, this.onFileSystemChanged, this, [repo.startWatchingFileSystem()]),
weakEvent(repo.onDidChangeFileSystem, this.onFileSystemChanged, this, [repo.watchFileSystem()]),
weakEvent(
configuration.onDidChange,
e => {

+ 1
- 1
src/views/nodes/lineHistoryNode.ts View File

@ -202,7 +202,7 @@ export class LineHistoryNode
const subscription = Disposable.from(
weakEvent(repo.onDidChange, this.onRepositoryChanged, this),
weakEvent(repo.onDidChangeFileSystem, this.onFileSystemChanged, this, [repo.startWatchingFileSystem()]),
weakEvent(repo.onDidChangeFileSystem, this.onFileSystemChanged, this, [repo.watchFileSystem()]),
);
return subscription;

+ 1
- 1
src/views/nodes/repositoryNode.ts View File

@ -398,7 +398,7 @@ export class RepositoryNode extends SubscribeableViewNode<'repository', ViewsWit
if (this.view.config.includeWorkingTree) {
disposables.push(
weakEvent(this.repo.onDidChangeFileSystem, this.onFileSystemChanged, this, [
this.repo.startWatchingFileSystem(),
this.repo.watchFileSystem(),
]),
);
}

+ 1
- 1
src/webviews/commitDetails/commitDetailsWebview.ts View File

@ -740,7 +740,7 @@ export class CommitDetailsWebviewProvider
private subscribeToRepositoryWip(repo: Repository) {
return Disposable.from(
repo.startWatchingFileSystem(),
repo.watchFileSystem(1000),
repo.onDidChangeFileSystem(() => this.onWipChanged(repo)),
repo.onDidChange(e => {
if (e.changed(RepositoryChange.Index, RepositoryChangeComparisonMode.Any)) {

Loading…
Cancel
Save