Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/git/src/autofetch.ts
5221 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { workspace, Disposable, EventEmitter, Memento, window, MessageItem, ConfigurationTarget, Uri, ConfigurationChangeEvent, l10n, env } from 'vscode';
7
import { Repository } from './repository';
8
import { eventToPromise, filterEvent, onceEvent } from './util';
9
import { GitErrorCodes } from './api/git';
10
11
export class AutoFetcher {
12
13
private static DidInformUser = 'autofetch.didInformUser';
14
15
private _onDidChange = new EventEmitter<boolean>();
16
private onDidChange = this._onDidChange.event;
17
18
private _enabled: boolean = false;
19
private _fetchAll: boolean = false;
20
get enabled(): boolean { return this._enabled; }
21
set enabled(enabled: boolean) { this._enabled = enabled; this._onDidChange.fire(enabled); }
22
23
private disposables: Disposable[] = [];
24
25
constructor(private repository: Repository, private globalState: Memento) {
26
workspace.onDidChangeConfiguration(this.onConfiguration, this, this.disposables);
27
this.onConfiguration();
28
29
const onGoodRemoteOperation = filterEvent(repository.onDidRunOperation, ({ operation, error }) => !error && operation.remote);
30
const onFirstGoodRemoteOperation = onceEvent(onGoodRemoteOperation);
31
onFirstGoodRemoteOperation(this.onFirstGoodRemoteOperation, this, this.disposables);
32
33
env.onDidChangeMeteredConnection(() => this.onConfiguration(), this, this.disposables);
34
}
35
36
private async onFirstGoodRemoteOperation(): Promise<void> {
37
const didInformUser = !this.globalState.get<boolean>(AutoFetcher.DidInformUser);
38
39
if (this.enabled && !didInformUser) {
40
this.globalState.update(AutoFetcher.DidInformUser, true);
41
}
42
43
const shouldInformUser = !this.enabled && didInformUser;
44
45
if (!shouldInformUser) {
46
return;
47
}
48
49
const yes: MessageItem = { title: l10n.t('Yes') };
50
const no: MessageItem = { isCloseAffordance: true, title: l10n.t('No') };
51
const askLater: MessageItem = { title: l10n.t('Ask Me Later') };
52
const result = await window.showInformationMessage(l10n.t('Would you like {0} to [periodically run "git fetch"]({1})?', env.appName, 'https://go.microsoft.com/fwlink/?linkid=865294'), yes, no, askLater);
53
54
if (result === askLater) {
55
return;
56
}
57
58
if (result === yes) {
59
const gitConfig = workspace.getConfiguration('git', Uri.file(this.repository.root));
60
gitConfig.update('autofetch', true, ConfigurationTarget.Global);
61
}
62
63
this.globalState.update(AutoFetcher.DidInformUser, true);
64
}
65
66
private onConfiguration(e?: ConfigurationChangeEvent): void {
67
if (e !== undefined && !e.affectsConfiguration('git.autofetch')) {
68
return;
69
}
70
71
if (env.isMeteredConnection) {
72
this.disable();
73
return;
74
}
75
76
const gitConfig = workspace.getConfiguration('git', Uri.file(this.repository.root));
77
switch (gitConfig.get<boolean | 'all'>('autofetch')) {
78
case true:
79
this._fetchAll = false;
80
this.enable();
81
break;
82
case 'all':
83
this._fetchAll = true;
84
this.enable();
85
break;
86
case false:
87
default:
88
this._fetchAll = false;
89
this.disable();
90
break;
91
}
92
}
93
94
enable(): void {
95
if (this.enabled) {
96
return;
97
}
98
99
this.enabled = true;
100
this.run();
101
}
102
103
disable(): void {
104
this.enabled = false;
105
}
106
107
private async run(): Promise<void> {
108
while (this.enabled) {
109
await this.repository.whenIdleAndFocused();
110
111
if (!this.enabled) {
112
return;
113
}
114
115
try {
116
if (this._fetchAll) {
117
await this.repository.fetchAll({ silent: true });
118
} else {
119
await this.repository.fetchDefault({ silent: true });
120
}
121
} catch (err) {
122
if (err.gitErrorCode === GitErrorCodes.AuthenticationFailed) {
123
this.disable();
124
}
125
}
126
127
if (!this.enabled) {
128
return;
129
}
130
131
const period = workspace.getConfiguration('git', Uri.file(this.repository.root)).get<number>('autofetchPeriod', 180) * 1000;
132
const timeout = new Promise(c => setTimeout(c, period));
133
const whenDisabled = eventToPromise(filterEvent(this.onDidChange, enabled => !enabled));
134
135
await Promise.race([timeout, whenDisabled]);
136
}
137
}
138
139
dispose(): void {
140
this.disable();
141
this.disposables.forEach(d => d.dispose());
142
}
143
}
144
145