Skip to content

Commit b7f435c

Browse files
Akos Kittafrancescospissu
Akos Kitta
authored andcommitted
another approach
Signed-off-by: Akos Kitta <[email protected]>
1 parent a59f09c commit b7f435c

File tree

8 files changed

+111
-72
lines changed

8 files changed

+111
-72
lines changed

arduino-ide-extension/src/browser/contributions/examples.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ import {
1919
SketchContribution,
2020
CommandRegistry,
2121
MenuModelRegistry,
22+
URI,
2223
} from './contribution';
2324
import { NotificationCenter } from '../notification-center';
2425
import { Board, SketchRef, SketchContainer } from '../../common/protocol';
25-
import { nls } from '@theia/core/lib/common';
26+
import { nls } from '@theia/core/lib/common/nls';
2627

2728
@injectable()
2829
export abstract class Examples extends SketchContribution {
@@ -150,10 +151,13 @@ export abstract class Examples extends SketchContribution {
150151
return {
151152
execute: async () => {
152153
const sketch = await this.sketchService.cloneExample(uri);
153-
return this.commandService.executeCommand(
154-
OpenSketch.Commands.OPEN_SKETCH.id,
155-
sketch
156-
);
154+
return this.commandService
155+
.executeCommand(OpenSketch.Commands.OPEN_SKETCH.id, sketch)
156+
.then((result) => {
157+
const name = new URI(uri).path.base;
158+
this.sketchService.markAsRecentlyOpened({ name, sourceUri: uri }); // no await
159+
return result;
160+
});
157161
},
158162
};
159163
}

arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts

+12-7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { MainMenuManager } from '../../common/main-menu-manager';
1515
import { OpenSketch } from './open-sketch';
1616
import { NotificationCenter } from '../notification-center';
1717
import { nls } from '@theia/core/lib/common';
18+
import { ExampleRef } from '../../common/protocol';
1819

1920
@injectable()
2021
export class OpenRecentSketch extends SketchContribution {
@@ -55,26 +56,30 @@ export class OpenRecentSketch extends SketchContribution {
5556
);
5657
}
5758

58-
private refreshMenu(sketches: Sketch[]): void {
59+
private refreshMenu(sketches: (Sketch | ExampleRef)[]): void {
5960
this.register(sketches);
6061
this.mainMenuManager.update();
6162
}
6263

63-
protected register(sketches: Sketch[]): void {
64+
protected register(sketches: (Sketch | ExampleRef)[]): void {
6465
const order = 0;
6566
for (const sketch of sketches) {
66-
const { uri } = sketch;
67+
const uri = Sketch.is(sketch) ? sketch.uri : sketch.sourceUri;
6768
const toDispose = this.toDisposeBeforeRegister.get(uri);
6869
if (toDispose) {
6970
toDispose.dispose();
7071
}
7172
const command = { id: `arduino-open-recent--${uri}` };
7273
const handler = {
73-
execute: () =>
74+
execute: async () => {
75+
const toOpen = Sketch.is(sketch)
76+
? sketch
77+
: await this.sketchService.cloneExample(sketch.sourceUri);
7478
this.commandRegistry.executeCommand(
7579
OpenSketch.Commands.OPEN_SKETCH.id,
76-
sketch
77-
),
80+
toOpen
81+
);
82+
},
7883
};
7984
this.commandRegistry.registerCommand(command, handler);
8085
this.menuRegistry.registerMenuAction(
@@ -86,7 +91,7 @@ export class OpenRecentSketch extends SketchContribution {
8691
}
8792
);
8893
this.toDisposeBeforeRegister.set(
89-
sketch.uri,
94+
uri,
9095
new DisposableCollection(
9196
Disposable.create(() =>
9297
this.commandRegistry.unregisterCommand(command)

arduino-ide-extension/src/common/protocol/notification-service.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
Config,
66
ProgressMessage,
77
Sketch,
8+
ExampleRef,
89
} from '../protocol';
910
import type { LibraryPackage } from './library-service';
1011

@@ -27,7 +28,9 @@ export interface NotificationServiceClient {
2728
notifyLibraryDidInstall(event: { item: LibraryPackage }): void;
2829
notifyLibraryDidUninstall(event: { item: LibraryPackage }): void;
2930
notifyAttachedBoardsDidChange(event: AttachedBoardsChangeEvent): void;
30-
notifyRecentSketchesDidChange(event: { sketches: Sketch[] }): void;
31+
notifyRecentSketchesDidChange(event: {
32+
sketches: (Sketch | ExampleRef)[];
33+
}): void;
3134
}
3235

3336
export const NotificationServicePath = '/services/notification-service';

arduino-ide-extension/src/common/protocol/sketches-service.ts

+23-2
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,12 @@ export interface SketchesService {
7878
/**
7979
* Marks the sketch with the given URI as recently opened. It does nothing if the sketch is temp or not valid.
8080
*/
81-
markAsRecentlyOpened(uri: string): Promise<void>;
81+
markAsRecentlyOpened(uriOrRef: string | ExampleRef): Promise<void>;
8282

8383
/**
8484
* Resolves to an array of sketches in inverse chronological order. The newest is the first.
8585
*/
86-
recentlyOpenedSketches(): Promise<Sketch[]>;
86+
recentlyOpenedSketches(): Promise<(Sketch | ExampleRef)[]>;
8787

8888
/**
8989
* Archives the sketch, resolves to the archive URI.
@@ -102,6 +102,27 @@ export interface SketchesService {
102102
notifyDeleteSketch(sketch: Sketch): void;
103103
}
104104

105+
export interface ExampleRef {
106+
/**
107+
* Name of the example.
108+
*/
109+
readonly name: string;
110+
/**
111+
* This is the location where the example is. IDE2 will clone the sketch from this location.
112+
*/
113+
readonly sourceUri: string;
114+
}
115+
export namespace ExampleRef {
116+
export function is(arg: unknown): arg is ExampleRef {
117+
return (
118+
(arg as ExampleRef).name !== undefined &&
119+
typeof (arg as ExampleRef).name === 'string' &&
120+
(arg as ExampleRef).sourceUri !== undefined &&
121+
typeof (arg as ExampleRef).sourceUri === 'string'
122+
);
123+
}
124+
}
125+
105126
export interface SketchRef {
106127
readonly name: string;
107128
readonly uri: string; // `LocationPath`

arduino-ide-extension/src/electron-main/theia/electron-main-application.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
179179
);
180180
for (const workspace of workspaces) {
181181
if (await this.isValidSketchPath(workspace.file)) {
182-
if (
183-
this.isTempSketch.is(workspace.file) &&
184-
!this.isTempSketch.isExample(workspace.file)
185-
) {
182+
if (this.isTempSketch.is(workspace.file)) {
186183
console.info(
187184
`Skipped opening sketch. The sketch was detected as temporary. Workspace path: ${workspace.file}.`
188185
);
@@ -420,7 +417,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
420417
// Do not try to reopen the sketch if it was temp.
421418
// Unfortunately, IDE2 has two different logic of restoring recent sketches: the Theia default `recentworkspace.json` and there is the `recent-sketches.json`.
422419
const file = workspaceUri.fsPath;
423-
if (this.isTempSketch.is(file) && !this.isTempSketch.isExample(file)) {
420+
if (this.isTempSketch.is(file)) {
424421
console.info(
425422
`Ignored marking workspace as a closed sketch. The sketch was detected as temporary. Workspace URI: ${workspaceUri.toString()}.`
426423
);

arduino-ide-extension/src/node/is-temp-sketch.ts

-12
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ import * as tempDir from 'temp-dir';
33
import { isWindows, isOSX } from '@theia/core/lib/common/os';
44
import { injectable } from '@theia/core/shared/inversify';
55
import { firstToLowerCase } from '../common/utils';
6-
import { join } from 'path';
76

87
const Win32DriveRegex = /^[a-zA-Z]:\\/;
98
export const TempSketchPrefix = '.arduinoIDE-unsaved';
10-
export const ExampleTempSketchPrefix = `${TempSketchPrefix}-example`;
119

1210
@injectable()
1311
export class IsTempSketch {
@@ -35,16 +33,6 @@ export class IsTempSketch {
3533
console.debug(`isTempSketch: ${result}. Input was ${normalizedSketchPath}`);
3634
return result;
3735
}
38-
39-
isExample(sketchPath: string): boolean {
40-
const normalizedSketchPath = maybeNormalizeDrive(sketchPath);
41-
const result =
42-
normalizedSketchPath.startsWith(this.tempDirRealpath) &&
43-
normalizedSketchPath.includes(
44-
join(this.tempDirRealpath, ExampleTempSketchPrefix)
45-
);
46-
return result;
47-
}
4836
}
4937

5038
/**

arduino-ide-extension/src/node/notification-service-server.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
Config,
99
Sketch,
1010
ProgressMessage,
11+
ExampleRef,
1112
} from '../common/protocol';
1213

1314
@injectable()
@@ -76,7 +77,9 @@ export class NotificationServiceServerImpl
7677
this.clients.forEach((client) => client.notifyConfigDidChange(event));
7778
}
7879

79-
notifyRecentSketchesDidChange(event: { sketches: Sketch[] }): void {
80+
notifyRecentSketchesDidChange(event: {
81+
sketches: (Sketch | ExampleRef)[];
82+
}): void {
8083
this.clients.forEach((client) =>
8184
client.notifyRecentSketchesDidChange(event)
8285
);

arduino-ide-extension/src/node/sketches-service-impl.ts

+57-39
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
SketchRef,
1717
SketchContainer,
1818
SketchesError,
19+
ExampleRef,
1920
} from '../common/protocol/sketches-service';
2021
import { NotificationServiceServerImpl } from './notification-service-server';
2122
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
@@ -29,7 +30,6 @@ import * as glob from 'glob';
2930
import { Deferred } from '@theia/core/lib/common/promise-util';
3031
import { ServiceError } from './service-error';
3132
import {
32-
ExampleTempSketchPrefix,
3333
IsTempSketch,
3434
maybeNormalizeDrive,
3535
TempSketchPrefix,
@@ -258,9 +258,7 @@ export class SketchesServiceImpl
258258
.then((uri) => path.join(FileUri.fsPath(uri), 'recent-sketches.json'));
259259
}
260260

261-
private async loadRecentSketches(
262-
fsPath: string
263-
): Promise<Record<string, number>> {
261+
private async loadRecentSketches(fsPath: string): Promise<RecentSketches> {
264262
let data: Record<string, number> = {};
265263
try {
266264
const raw = await promisify(fs.readFile)(fsPath, {
@@ -271,32 +269,39 @@ export class SketchesServiceImpl
271269
return data;
272270
}
273271

274-
async markAsRecentlyOpened(uri: string): Promise<void> {
272+
async markAsRecentlyOpened(uriOrRef: string | ExampleRef): Promise<void> {
273+
const isExample = typeof uriOrRef !== 'string';
274+
const uri = isExample ? uriOrRef.sourceUri : uriOrRef;
275275
let sketch: Sketch | undefined = undefined;
276276
try {
277277
sketch = await this.loadSketch(uri);
278278
} catch {
279279
return;
280280
}
281-
if (
282-
(await this.isTemp(sketch)) &&
283-
!this.isTempSketch.isExample(FileUri.fsPath(sketch.uri))
284-
) {
281+
if (await this.isTemp(sketch)) {
285282
return;
286283
}
287284

288285
const fsPath = await this.recentSketchesFsPath;
289286
const data = await this.loadRecentSketches(fsPath);
290287
const now = Date.now();
291-
data[sketch.uri] = now;
288+
data[sketch.uri] = isExample ? { type: 'example', mtimeMs: now } : now;
292289

293290
let toDeleteUri: string | undefined = undefined;
294291
if (Object.keys(data).length > 10) {
295292
let min = Number.MAX_SAFE_INTEGER;
296293
for (const uri of Object.keys(data)) {
297-
if (min > data[uri]) {
298-
min = data[uri];
299-
toDeleteUri = uri;
294+
const value = data[uri];
295+
if (typeof value === 'number') {
296+
if (min > value) {
297+
min = value;
298+
toDeleteUri = uri;
299+
}
300+
} else {
301+
if (min > value.mtimeMs) {
302+
min = value.mtimeMs;
303+
toDeleteUri = uri;
304+
}
300305
}
301306
}
302307
}
@@ -311,36 +316,47 @@ export class SketchesServiceImpl
311316
);
312317
}
313318

314-
async recentlyOpenedSketches(): Promise<Sketch[]> {
319+
async recentlyOpenedSketches(): Promise<(Sketch | ExampleRef)[]> {
315320
const configDirUri = await this.envVariableServer.getConfigDirUri();
316321
const fsPath = path.join(
317322
FileUri.fsPath(configDirUri),
318323
'recent-sketches.json'
319324
);
320-
let data: Record<string, number> = {};
325+
let data: RecentSketches = {};
321326
try {
322327
const raw = await promisify(fs.readFile)(fsPath, {
323328
encoding: 'utf8',
324329
});
325330
data = JSON.parse(raw);
326331
} catch {}
327332

328-
const sketches: SketchWithDetails[] = [];
329-
for (const uri of Object.keys(data).sort(
330-
(left, right) => data[right] - data[left]
331-
)) {
332-
try {
333-
const sketch = await this.loadSketch(uri);
334-
sketches.push(sketch);
335-
} catch {}
333+
const sketches: (Sketch | ExampleRef)[] = [];
334+
for (const uri of Object.keys(data).sort((left, right) => {
335+
const leftValue = data[left];
336+
const rightValue = data[right];
337+
const leftMtimeMs =
338+
typeof leftValue === 'number' ? leftValue : leftValue.mtimeMs;
339+
const rightMtimeMs =
340+
typeof rightValue === 'number' ? rightValue : rightValue.mtimeMs;
341+
return leftMtimeMs - rightMtimeMs;
342+
})) {
343+
const value = data[uri];
344+
if (typeof value === 'number') {
345+
try {
346+
const sketch = await this.loadSketch(uri);
347+
sketches.push(sketch);
348+
} catch {}
349+
} else {
350+
sketches.push({ name: new URI(uri).path.base, sourceUri: uri });
351+
}
336352
}
337353

338354
return sketches;
339355
}
340356

341357
async cloneExample(uri: string): Promise<Sketch> {
342358
const sketch = await this.loadSketch(uri);
343-
const parentPath = await this.createTempFolder(false);
359+
const parentPath = await this.createTempFolder();
344360
const destinationUri = FileUri.create(
345361
path.join(parentPath, sketch.name)
346362
).toString();
@@ -421,24 +437,21 @@ void loop() {
421437
* For example, on Windows, instead of getting an [8.3 filename](https://en.wikipedia.org/wiki/8.3_filename), callers will get a fully resolved path.
422438
* `C:\\Users\\KITTAA~1\\AppData\\Local\\Temp\\.arduinoIDE-unsaved2022615-21100-iahybb.yyvh\\sketch_jul15a` will be `C:\\Users\\kittaakos\\AppData\\Local\\Temp\\.arduinoIDE-unsaved2022615-21100-iahybb.yyvh\\sketch_jul15a`
423439
*/
424-
private createTempFolder(isTemp = true): Promise<string> {
440+
private createTempFolder(prefix: string = TempSketchPrefix): Promise<string> {
425441
return new Promise<string>((resolve, reject) => {
426-
temp.mkdir(
427-
{ prefix: isTemp ? TempSketchPrefix : ExampleTempSketchPrefix },
428-
(createError, dirPath) => {
429-
if (createError) {
430-
reject(createError);
442+
temp.mkdir({ prefix }, (createError, dirPath) => {
443+
if (createError) {
444+
reject(createError);
445+
return;
446+
}
447+
fs.realpath.native(dirPath, (resolveError, resolvedDirPath) => {
448+
if (resolveError) {
449+
reject(resolveError);
431450
return;
432451
}
433-
fs.realpath.native(dirPath, (resolveError, resolvedDirPath) => {
434-
if (resolveError) {
435-
reject(resolveError);
436-
return;
437-
}
438-
resolve(resolvedDirPath);
439-
});
440-
}
441-
);
452+
resolve(resolvedDirPath);
453+
});
454+
});
442455
});
443456
}
444457

@@ -637,3 +650,8 @@ function sketchIndexToLetters(num: number): string {
637650
} while (pow > 0);
638651
return out;
639652
}
653+
654+
type RecentSketches = Record<
655+
string,
656+
number | { type: 'example'; mtimeMs: number }
657+
>;

0 commit comments

Comments
 (0)