Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit cc57dec

Browse files
author
Akos Kitta
committed
fix: flawed timing issue when opening editors
From now on, the editor widget open promise resolution does not rely on internal Theia events but solely on @phosphor's `isAttached`/`isVisible` properties. The editor widget promise resolves with the next task after a navigation frame so the browser can render the widget. Closes #1612 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
1 parent f6d112e commit cc57dec

File tree

1 file changed

+51
-47
lines changed

1 file changed

+51
-47
lines changed

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

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { nls } from '@theia/core/lib/common/nls';
2-
import { inject,injectable } from '@theia/core/shared/inversify';
2+
import { injectable } from '@theia/core/shared/inversify';
33
import type { EditorOpenerOptions } from '@theia/editor/lib/browser/editor-manager';
44
import { Later } from '../../common/nls';
55
import { Sketch, SketchesError } from '../../common/protocol';
@@ -15,14 +15,9 @@ import { ApplicationError } from '@theia/core/lib/common/application-error';
1515
import { Deferred, wait } from '@theia/core/lib/common/promise-util';
1616
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
1717
import { DisposableCollection } from '@theia/core/lib/common/disposable';
18-
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
19-
import { ContextKeyService as VSCodeContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/browser/contextKeyService';
2018

2119
@injectable()
2220
export class OpenSketchFiles extends SketchContribution {
23-
@inject(VSCodeContextKeyService)
24-
private readonly contextKeyService: VSCodeContextKeyService;
25-
2621
override registerCommands(registry: CommandRegistry): void {
2722
registry.registerCommand(OpenSketchFiles.Commands.OPEN_SKETCH_FILES, {
2823
execute: (uri: URI) => this.openSketchFiles(uri),
@@ -135,39 +130,36 @@ export class OpenSketchFiles extends SketchContribution {
135130
const widget = this.editorManager.all.find(
136131
(widget) => widget.editor.uri.toString() === uri
137132
);
133+
if (widget && !forceOpen) {
134+
return widget;
135+
}
136+
138137
const disposables = new DisposableCollection();
139-
if (!widget || forceOpen) {
140-
const deferred = new Deferred<EditorWidget>();
138+
const deferred = new Deferred<EditorWidget>();
139+
// An editor can be in two primary states:
140+
// - The editor is not yet opened. The `widget` is `undefined`. With `editorManager#open`, Theia will create an editor and fire an `editorManager#onCreated` event.
141+
// - The editor is opened. Can be active, current, or open.
142+
// - If the editor has the focus (the cursor blinks in the editor): it's the active editor.
143+
// - If the editor does not have the focus (the focus is on a different widget or the context menu is opened in the editor): it's the current editor.
144+
// - If the editor is not the top editor in the main area, it's opened.
145+
if (!widget) {
146+
// If the widget is `undefined`, IDE2 expects one `onCreate` event. Subscribe to the `onCreated` event
147+
// and resolve the promise with the editor only when the new editor's visibility changes.
141148
disposables.push(
142149
this.editorManager.onCreated((editor) => {
143150
if (editor.editor.uri.toString() === uri) {
144-
if (editor.isVisible) {
145-
disposables.dispose();
151+
if (editor.isAttached && editor.isVisible) {
146152
deferred.resolve(editor);
147153
} else {
148-
// In Theia, the promise resolves after opening the editor, but the editor is neither attached to the DOM, nor visible.
149-
// This is a hack to first get an event from monaco after the widget update request, then IDE2 waits for the next monaco context key event.
150-
// Here, the monaco context key event is not used, but this is the first event after the editor is visible in the UI.
151154
disposables.push(
152-
(editor.editor as MonacoEditor).onDidResize((dimension) => {
153-
if (dimension) {
154-
const isKeyOwner = (
155-
arg: unknown
156-
): arg is { key: string } => {
157-
if (typeof arg === 'object') {
158-
const object = arg as Record<string, unknown>;
159-
return typeof object['key'] === 'string';
160-
}
161-
return false;
162-
};
163-
disposables.push(
164-
this.contextKeyService.onDidChangeContext((e) => {
165-
// `commentIsEmpty` is the first context key change event received from monaco after the editor is for real visible in the UI.
166-
if (isKeyOwner(e) && e.key === 'commentIsEmpty') {
167-
deferred.resolve(editor);
168-
disposables.dispose();
169-
}
170-
})
155+
editor.onDidChangeVisibility((visible) => {
156+
if (visible) {
157+
// wait an animation frame. although the visible and attached props are true the editor is not there.
158+
// let the browser render the widget
159+
setTimeout(
160+
() =>
161+
requestAnimationFrame(() => deferred.resolve(editor)),
162+
0
171163
);
172164
}
173165
})
@@ -176,29 +168,41 @@ export class OpenSketchFiles extends SketchContribution {
176168
}
177169
})
178170
);
179-
this.editorManager.open(
171+
}
172+
173+
this.editorManager
174+
.open(
180175
new URI(uri),
181176
options ?? {
182177
mode: 'reveal',
183178
preview: false,
184179
counter: 0,
185180
}
181+
)
182+
.then((editorWidget) => {
183+
// If the widget was defined, it was already opened.
184+
// The editor is expected to be attached to the shell and visible in the UI.
185+
// The deferred promise does not have to wait for the `editorManager#onCreated` event.
186+
// It can resolve earlier.
187+
if (!widget) {
188+
deferred.resolve(editorWidget);
189+
}
190+
});
191+
192+
const timeout = 5_000; // number of ms IDE2 waits for the editor to show up in the UI
193+
const result = await Promise.race([
194+
deferred.promise,
195+
wait(timeout).then(() => {
196+
disposables.dispose();
197+
return 'timeout';
198+
}),
199+
]);
200+
if (result === 'timeout') {
201+
console.warn(
202+
`Timeout after ${timeout} millis. The editor has not shown up in time. URI: ${uri}`
186203
);
187-
const timeout = 5_000; // number of ms IDE2 waits for the editor to show up in the UI
188-
const result = await Promise.race([
189-
deferred.promise,
190-
wait(timeout).then(() => {
191-
disposables.dispose();
192-
return 'timeout';
193-
}),
194-
]);
195-
if (result === 'timeout') {
196-
console.warn(
197-
`Timeout after ${timeout} millis. The editor has not shown up in time. URI: ${uri}`
198-
);
199-
}
200-
return result;
201204
}
205+
return result;
202206
}
203207
}
204208
export namespace OpenSketchFiles {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /