Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/test/simulation/workbench/stores/simulationBaseline.ts
13399 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 * as fs from 'fs';
7
import * as mobx from 'mobx';
8
import * as path from 'path';
9
import { RunOnceScheduler } from '../../../../src/util/vs/base/common/async';
10
import { Disposable, toDisposable } from '../../../../src/util/vs/base/common/lifecycle';
11
import { RUN_METADATA, SIMULATION_FOLDER_NAME } from '../../shared/sharedTypes';
12
import { REPO_ROOT, genericEquals } from '../utils/utils';
13
import { SimulationRunner } from './simulationRunner';
14
import { SimulationStorage, SimulationStorageValue } from './simulationStorage';
15
16
const SIMULATION_FOLDER_PATH = path.join(REPO_ROOT, SIMULATION_FOLDER_NAME);
17
18
class SimulationRun {
19
20
/** Shown in UI */
21
public readonly friendlyName: string;
22
23
/**
24
* @param name is the name of the run that is also the name of the directory that contains the run information (usually in `.simulation` directory)
25
*/
26
constructor(
27
public readonly name: string,
28
public readonly label?: string
29
) {
30
// example: out-20230804-105913 or out-external-20230804-105913
31
const m = name.match(/^out-(?:\w+-)?(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})(\d{2})$/);
32
if (m) {
33
const year = m ? parseInt(m[1], 10) : 0;
34
const month = m ? parseInt(m[2], 10) : 0;
35
const day = m ? parseInt(m[3], 10) : 0;
36
const hour = m ? parseInt(m[4], 10) : 0;
37
const minute = m ? parseInt(m[5], 10) : 0;
38
const second = m ? parseInt(m[6], 10) : 0;
39
const twodigits = (n: number) => String(n).padStart(2, '0');
40
this.friendlyName = `${twodigits(hour)}:${twodigits(minute)}:${twodigits(second)} -- ${twodigits(day)}/${twodigits(month)}/${year}`;
41
} else {
42
this.friendlyName = name;
43
}
44
if (this.label?.length) {
45
this.friendlyName = `${this.friendlyName} (${this.label})`;
46
}
47
}
48
}
49
50
51
/**
52
* Detects possible baseline runs
53
*/
54
export class SimulationRunsProvider extends Disposable {
55
private static readonly COMPARE_AGAINST_RUN_STORAGE_KEY = 'selectedBaseline';
56
57
private readonly _updateSoon = this._register(new RunOnceScheduler(() => this._update(), 50));
58
59
@mobx.observable
60
public runs: SimulationRun[] = [];
61
62
public selectedBaselineRunName: SimulationStorageValue<string>;
63
64
@mobx.computed
65
public get selectedBaselineRun(): SimulationRun | undefined {
66
return this.runs.find(r => r.name === this.selectedBaselineRunName.value);
67
}
68
69
constructor(
70
_storage: SimulationStorage,
71
private readonly _runner: SimulationRunner
72
) {
73
super();
74
mobx.makeObservable(this);
75
76
this.selectedBaselineRunName = new SimulationStorageValue(_storage, SimulationRunsProvider.COMPARE_AGAINST_RUN_STORAGE_KEY, '');
77
78
const listener = () => {
79
if (!this._updateSoon.isScheduled()) {
80
this._updateSoon.schedule();
81
}
82
};
83
84
fs.promises.mkdir(SIMULATION_FOLDER_PATH, { recursive: true }).then(() => {
85
fs.watch(SIMULATION_FOLDER_PATH, { recursive: false }, listener);
86
this._register(toDisposable(() => fs.unwatchFile(SIMULATION_FOLDER_PATH, listener)));
87
this._update();
88
});
89
}
90
91
private _excludedFiles = new Set(['cache.sqlite', 'cache.version', 'token_cache.json']);
92
93
private async _update(): Promise<void> {
94
let entries = (await fs.promises.readdir(SIMULATION_FOLDER_PATH)).map((e) => ({ timestamp: e, label: undefined }));
95
entries = entries.filter(entry => !entry.timestamp.startsWith('.') && !entry.timestamp.startsWith('tmp-') && !this._excludedFiles.has(entry.timestamp)); // ignore hidden & tmp- directories & cache-related files
96
// Sort descending
97
entries.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
98
99
if (genericEquals(entries, this.runs.map(r => r.name))) {
100
return;
101
}
102
103
for (const entry of entries) {
104
const runMetadata = path.join(SIMULATION_FOLDER_PATH, entry.timestamp, RUN_METADATA);
105
try {
106
const data = (await fs.promises.readFile(runMetadata, 'utf-8')).toString();
107
entry.label = JSON.parse(data).label;
108
} catch { }
109
}
110
111
mobx.runInAction(() => {
112
// Try to reuse the old objects
113
const existingRuns = new Map<string, SimulationRun>(this.runs.map(r => [r.name, r]));
114
this.runs = entries.map(entry => existingRuns.get(entry.timestamp) ?? new SimulationRun(entry.timestamp, entry.label));
115
});
116
}
117
118
public async renameRun(oldName: string, newName: string): Promise<boolean> {
119
if (!this._runner) {
120
console.log('Cannot rename: no runner available');
121
return false;
122
}
123
124
console.log('Attempting to rename run from', oldName, 'to', newName);
125
const success = await this._runner.renameRun(oldName, newName);
126
127
if (success) {
128
// Update selected baseline if it was renamed
129
if (this.selectedBaselineRunName.value === oldName) {
130
console.log('Updating selected baseline name from', oldName, 'to', newName);
131
mobx.runInAction(() => {
132
this.selectedBaselineRunName.value = newName;
133
});
134
}
135
} else {
136
console.log('Failed to rename run');
137
}
138
return success;
139
}
140
}
141
142