src/vs/platform/userDataSync/common/userDataSyncMachines.ts

219 LOC · 184 covered · 35 uncovered · 37 ranges · 585 concepts · 8 introducers · 348 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- abstractSynchronizer.ts ×49
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 { Emitter, Event } from '../../../base/common/event.js';
7 > import { Disposable } from '../../../base/common/lifecycle.js';
8 > import { isAndroid, isChrome, isEdge, isFirefox, isSafari, isWeb, Platform, platform, PlatformToString } from '../../../base/common/platform.js';
9 > import { escapeRegExpCharacters } from '../../../base/common/strings.js';
10 > import { localize } from '../../../nls.js';
11 > import { IEnvironmentService } from '../../environment/common/environment.js';
12 > import { IFileService } from '../../files/common/files.js';
13 > import { createDecorator } from '../../instantiation/common/instantiation.js';
14 > import { IProductService } from '../../product/common/productService.js';
15 > import { getServiceMachineId } from '../../externalServices/common/serviceMachineId.js';
16 > import { IStorageService, StorageScope, StorageTarget } from '../../storage/common/storage.js';
17 > import { IUserData, IUserDataManifest, IUserDataSyncLogService, IUserDataSyncStoreService } from './userDataSync.js';
18 >
19 > export interface IMachineData {
20 > id: string;
21 > name: string;
22 > disabled?: boolean;
23 > platform?: string;
24 > }
25 >
26 > export interface IMachinesData {
27 > version: number;
28 > machines: IMachineData[];
29 > }
30 >
31 > export type IUserDataSyncMachine = Readonly<IMachineData> & { readonly isCurrent: boolean };
32 >
33 > export const IUserDataSyncMachinesService = createDecorator<IUserDataSyncMachinesService>('IUserDataSyncMachinesService');
34 > export interface IUserDataSyncMachinesService {
35 > _serviceBrand: undefined;
36 >
37 > readonly onDidChange: Event<void>;
38 >
39 > getMachines(manifest?: IUserDataManifest): Promise<IUserDataSyncMachine[]>;
40 >
41 > addCurrentMachine(manifest?: IUserDataManifest): Promise<void>;
42 > removeCurrentMachine(manifest?: IUserDataManifest): Promise<void>;
43 > renameMachine(machineId: string, name: string): Promise<void>;
44 > setEnablements(enbalements: [string, boolean][]): Promise<void>;
45 > }
46 >
47 > const currentMachineNameKey = 'sync.currentMachineName';
48 >
49 > const Safari = 'Safari';
50 > const Chrome = 'Chrome';
51 > const Edge = 'Edge';
52 > const Firefox = 'Firefox';
53 > const Android = 'Android';
54 >
55 > export function isWebPlatform(platform: string) {
56 switch (platform) {
57 case Safari:
58 case Chrome:
59 case Edge:
60 case Firefox:
61 case Android:
62 case PlatformToString(Platform.Web):
63 return true;
64 }
65 return false;
66 }
68 > function getPlatformName(): string { userDataSyncMachines.ts ×8
69 > if (isSafari) { return Safari; }
70 > if (isChrome) { return Chrome; }
71 > if (isEdge) { return Edge; }
72 > if (isFirefox) { return Firefox; }
73 > if (isAndroid) { return Android; }
74 > return PlatformToString(isWeb ? Platform.Web : platform);
75 > }
77 > export class UserDataSyncMachinesService extends Disposable implements IUserDataSyncMachinesService {
78 >
79 > private static readonly VERSION = 1;
80 > private static readonly RESOURCE = 'machines';
81 >
82 > _serviceBrand: undefined;
83 >
84 > private readonly _onDidChange = this._register(new Emitter<void>());
85 > readonly onDidChange = this._onDidChange.event;
86 >
87 > private readonly currentMachineIdPromise: Promise<string>;
88 > private userData: IUserData | null = null;
89 >
90 > constructor(
91 > @IEnvironmentService environmentService: IEnvironmentService, userDataSyncStoreService.ts ×36
92 > @IFileService fileService: IFileService,
93 > @IStorageService private readonly storageService: IStorageService,
94 > @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
95 > @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
96 > @IProductService private readonly productService: IProductService,
97 > ) {
98 > super();
99 > this.currentMachineIdPromise = getServiceMachineId(environmentService, fileService, storageService);
100 > }
102 > async getMachines(manifest?: IUserDataManifest): Promise<IUserDataSyncMachine[]> {
103 > const currentMachineId = await this.currentMachineIdPromise; userDataAutoSyncService.ts ×37
104 > const machineData = await this.readMachinesData(manifest);
105 > return machineData.machines.map<IUserDataSyncMachine>(machine => ({ ...machine, ...{ isCurrent: machine.id === currentMachineId } }));
106 > }
108 > async addCurrentMachine(manifest?: IUserDataManifest): Promise<void> {
109 > const currentMachineId = await this.currentMachineIdPromise; userDataSyncMachines.ts ×8
110 > const machineData = await this.readMachinesData(manifest);
111 > if (!machineData.machines.some(({ id }) => id === currentMachineId)) {
112 > machineData.machines.push({ id: currentMachineId, name: this.computeCurrentMachineName(machineData.machines), platform: getPlatformName() });
113 > await this.writeMachinesData(machineData);
114 > }
115 > }
117 > async removeCurrentMachine(manifest?: IUserDataManifest): Promise<void> {
118 > const currentMachineId = await this.currentMachineIdPromise; userDataSyncMachines.ts ×2
119 > const machineData = await this.readMachinesData(manifest);
120 > const updatedMachines = machineData.machines.filter(({ id }) => id !== currentMachineId);
121 > if (updatedMachines.length !== machineData.machines.length) {
122 > machineData.machines = updatedMachines; userDataSyncMachines.ts ×1
123 > await this.writeMachinesData(machineData);
124 > }
127 > async renameMachine(machineId: string, name: string, manifest?: IUserDataManifest): Promise<void> {
128 const machineData = await this.readMachinesData(manifest);
129 const machine = machineData.machines.find(({ id }) => id === machineId);
130 if (machine) {
131 machine.name = name;
132 await this.writeMachinesData(machineData);
133 const currentMachineId = await this.currentMachineIdPromise;
134 if (machineId === currentMachineId) {
135 this.storageService.store(currentMachineNameKey, name, StorageScope.APPLICATION, StorageTarget.MACHINE);
136 }
137 }
138 }
140 > async setEnablements(enablements: [string, boolean][]): Promise<void> {
141 > const machineData = await this.readMachinesData(); userDataAutoSyncService.ts ×1
142 > for (const [machineId, enabled] of enablements) {
143 > const machine = machineData.machines.find(machine => machine.id === machineId);
144 > if (machine) {
145 > machine.disabled = enabled ? undefined : true;
146 > }
147 > }
148 > await this.writeMachinesData(machineData);
149 > }
151 > private computeCurrentMachineName(machines: IMachineData[]): string {
152 > const previousName = this.storageService.get(currentMachineNameKey, StorageScope.APPLICATION); userDataSyncMachines.ts ×8
153 > if (previousName) {
154 if (!machines.some(machine => machine.name === previousName)) {
155 return previousName;
156 }
157 this.storageService.remove(currentMachineNameKey, StorageScope.APPLICATION);
158 }
160 > const namePrefix = `${this.productService.embedderIdentifier ? `${this.productService.embedderIdentifier} - ` : ''}${getPlatformName()} (${this.productService.nameShort})`;
161 > const nameRegEx = new RegExp(`${escapeRegExpCharacters(namePrefix)}\\s#(\\d+)`);
162 > let nameIndex = 0;
163 > for (const machine of machines) {
164 const matches = nameRegEx.exec(machine.name);
165 const index = matches ? parseInt(matches[1]) : 0;
166 nameIndex = index > nameIndex ? index : nameIndex;
167 }
168 > return `${namePrefix} #${nameIndex + 1}`; userDataSyncMachines.ts ×8
169 > }
171 > private async readMachinesData(manifest?: IUserDataManifest): Promise<IMachinesData> {
172 > this.userData = await this.readUserData(manifest); userDataAutoSyncService.ts ×37
173 > const machinesData = this.parse(this.userData);
174 > if (machinesData.version !== UserDataSyncMachinesService.VERSION) {
175 throw new Error(localize('error incompatible', "Cannot read machines data as the current version is incompatible. Please update {0} and try again.", this.productService.nameLong));
176 }
177 > return machinesData; userDataAutoSyncService.ts ×37
178 > }
180 > private async writeMachinesData(machinesData: IMachinesData): Promise<void> {
181 > const content = JSON.stringify(machinesData); userDataSyncMachines.ts ×8
182 > const ref = await this.userDataSyncStoreService.writeResource(UserDataSyncMachinesService.RESOURCE, content, this.userData?.ref || null);
183 > this.userData = { ref, content };
184 > this._onDidChange.fire();
185 > }
187 > private async readUserData(manifest?: IUserDataManifest): Promise<IUserData> {
188 > if (this.userData) { userDataAutoSyncService.ts ×37
190 > const latestRef = manifest && manifest.latest ? manifest.latest[UserDataSyncMachinesService.RESOURCE] : undefined;
191 >
192 > // Last time synced resource and latest resource on server are same
193 > if (this.userData.ref === latestRef) {
194 > return this.userData; userDataSyncMachines.ts ×3
195 > }
197 > // There is no resource on server and last time it was synced with no resource
198 > if (latestRef === undefined && this.userData.content === null) {
199 > return this.userData;
200 > }
201 > }
203 > return this.userDataSyncStoreService.readResource(UserDataSyncMachinesService.RESOURCE, this.userData);
204 > }
206 > private parse(userData: IUserData): IMachinesData {
207 > if (userData.content !== null) { userDataAutoSyncService.ts ×37
209 > return JSON.parse(userData.content);
210 > } catch (e) {
211 this.logService.error(e);
212 }
215 > version: UserDataSyncMachinesService.VERSION,
216 > machines: []
217 > };
218 > }