diff --git a/arduino-ide-extension/src/browser/contributions/sketch-files-tracker.ts b/arduino-ide-extension/src/browser/contributions/sketch-files-tracker.ts index ad7704ad..fcd07cb6 100644 --- a/arduino-ide-extension/src/browser/contributions/sketch-files-tracker.ts +++ b/arduino-ide-extension/src/browser/contributions/sketch-files-tracker.ts @@ -30,10 +30,7 @@ export class SketchFilesTracker extends SketchContribution { override onReady(): void { this.sketchServiceClient.currentSketch().then(async (sketch) => { - if ( - CurrentSketch.isValid(sketch) && - !(await this.sketchService.isTemp(sketch)) - ) { + if (CurrentSketch.isValid(sketch)) { this.toDisposeOnStop.push(this.fileService.watch(new URI(sketch.uri))); this.toDisposeOnStop.push( this.fileService.onDidFilesChange(async (event) => { diff --git a/arduino-ide-extension/src/browser/theia/core/widget-manager.ts b/arduino-ide-extension/src/browser/theia/core/widget-manager.ts index adc32860..4e8e1013 100644 --- a/arduino-ide-extension/src/browser/theia/core/widget-manager.ts +++ b/arduino-ide-extension/src/browser/theia/core/widget-manager.ts @@ -1,11 +1,41 @@ import type { MaybePromise } from '@theia/core'; import type { Widget } from '@theia/core/lib/browser'; import { WidgetManager as TheiaWidgetManager } from '@theia/core/lib/browser/widget-manager'; -import { injectable } from '@theia/core/shared/inversify'; +import { + inject, + injectable, + postConstruct, +} from '@theia/core/shared/inversify'; +import { EditorWidget } from '@theia/editor/lib/browser'; import deepEqual = require('deep-equal'); +import { + CurrentSketch, + SketchesServiceClientImpl, +} from '../../../common/protocol/sketches-service-client-impl'; +import { Sketch } from '../../contributions/contribution'; @injectable() export class WidgetManager extends TheiaWidgetManager { + @inject(SketchesServiceClientImpl) + private readonly sketchesServiceClient: SketchesServiceClientImpl; + + @postConstruct() + protected init(): void { + this.sketchesServiceClient.onCurrentSketchDidChange((currentSketch) => { + if (CurrentSketch.isValid(currentSketch)) { + const sketchFileUris = new Set(Sketch.uris(currentSketch)); + for (const widget of this.widgets.values()) { + if (widget instanceof EditorWidget) { + const uri = widget.editor.uri.toString(); + if (sketchFileUris.has(uri)) { + widget.title.closable = false; + } + } + } + } + }); + } + /** * Customized to find any existing widget based on `options` deepEquals instead of string equals. * See https://github.com/eclipse-theia/theia/issues/11309. diff --git a/arduino-ide-extension/src/common/protocol/sketches-service-client-impl.ts b/arduino-ide-extension/src/common/protocol/sketches-service-client-impl.ts index 7aea3367..63141ca9 100644 --- a/arduino-ide-extension/src/common/protocol/sketches-service-client-impl.ts +++ b/arduino-ide-extension/src/common/protocol/sketches-service-client-impl.ts @@ -10,7 +10,7 @@ import { DisposableCollection } from '@theia/core/lib/common/disposable'; import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application'; import { Sketch, SketchesService } from '../../common/protocol'; import { ConfigService } from './config-service'; -import { SketchContainer, SketchRef } from './sketches-service'; +import { SketchContainer, SketchesError, SketchRef } from './sketches-service'; import { ARDUINO_CLOUD_FOLDER, REMOTE_SKETCHBOOK_FOLDER, @@ -79,6 +79,7 @@ export class SketchesServiceClientImpl this.sketches.set(sketch.uri, sketch); } this.toDispose.push( + // Watch changes in the sketchbook to update `File` > `Sketchbook` menu items. this.fileService.watch(new URI(sketchDirUri), { recursive: true, excludes: [], @@ -87,6 +88,34 @@ export class SketchesServiceClientImpl this.toDispose.push( this.fileService.onDidFilesChange(async (event) => { for (const { type, resource } of event.changes) { + // The file change events have higher precedence in the current sketch over the sketchbook. + if ( + CurrentSketch.isValid(this._currentSketch) && + new URI(this._currentSketch.uri).isEqualOrParent(resource) + ) { + if (type === FileChangeType.UPDATED) { + return; + } + + let reloadedSketch: Sketch | undefined = undefined; + try { + reloadedSketch = await this.sketchService.loadSketch( + this._currentSketch.uri + ); + } catch (err) { + if (!SketchesError.NotFound.is(err)) { + throw err; + } + } + + if (!reloadedSketch) { + return; + } + + // TODO: check if current is the same as reloaded? + this.useCurrentSketch(reloadedSketch, true); + return; + } // We track main sketch files changes only. // TODO: check sketch folder changes. One can rename the folder without renaming the `.ino` file. if (sketchbookUri.isEqualOrParent(resource)) { if (Sketch.isSketchFile(resource)) { @@ -125,12 +154,31 @@ export class SketchesServiceClientImpl .reachedState('started_contributions') .then(async () => { const currentSketch = await this.loadCurrentSketch(); - this._currentSketch = currentSketch; - this.currentSketchDidChangeEmitter.fire(this._currentSketch); - this.currentSketchLoaded.resolve(this._currentSketch); + if (CurrentSketch.isValid(currentSketch)) { + this.toDispose.pushAll([ + // Watch the file changes of the current sketch + this.fileService.watch(new URI(currentSketch.uri), { + recursive: true, + excludes: [], + }), + ]); + } + this.useCurrentSketch(currentSketch); }); } + private useCurrentSketch( + currentSketch: CurrentSketch, + reassignPromise = false + ) { + this._currentSketch = currentSketch; + if (reassignPromise) { + this.currentSketchLoaded = new Deferred(); + } + this.currentSketchLoaded.resolve(this._currentSketch); + this.currentSketchDidChangeEmitter.fire(this._currentSketch); + } + onStop(): void { this.toDispose.dispose(); }