Skip to content

Commit 80bddc2

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
ATL-988: Aligned the hover size to the expression.
Signed-off-by: Akos Kitta <[email protected]>
1 parent 8a692d0 commit 80bddc2

File tree

6 files changed

+242
-0
lines changed

6 files changed

+242
-0
lines changed

Diff for: arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

+7
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ import { SearchInWorkspaceResultTreeWidget as TheiaSearchInWorkspaceResultTreeWi
156156
import { SearchInWorkspaceResultTreeWidget } from './theia/search-in-workspace/search-in-workspace-result-tree-widget';
157157
import { MonacoEditorProvider } from './theia/monaco/monaco-editor-provider';
158158
import { MonacoEditorProvider as TheiaMonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
159+
import { DebugEditorModel } from './theia/debug/debug-editor-model';
160+
import { DebugEditorModelFactory } from '@theia/debug/lib/browser/editor/debug-editor-model';
159161

160162
const ElementQueries = require('css-element-queries/src/ElementQueries');
161163

@@ -417,6 +419,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
417419
bind(DebugConfigurationManager).toSelf().inSingletonScope();
418420
rebind(TheiaDebugConfigurationManager).toService(DebugConfigurationManager);
419421

422+
// Patch for the debug hover: https://github.com/eclipse-theia/theia/pull/9256/
423+
rebind(DebugEditorModelFactory).toDynamicValue(({ container }) => <DebugEditorModelFactory>(editor =>
424+
DebugEditorModel.createModel(container, editor)
425+
)).inSingletonScope();
426+
420427
// Preferences
421428
bindArduinoPreferences(bind);
422429

Diff for: arduino-ide-extension/src/browser/style/debug.css

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* TODO: remove after https://github.com/eclipse-theia/theia/pull/9256/ */
2+
3+
/* To fix colors in Theia. */
4+
.theia-debug-hover-title.number,
5+
.theia-debug-console-variable.number {
6+
color: var(--theia-variable-number-variable-color);
7+
}
8+
.theia-debug-hover-title.boolean,
9+
.theia-debug-console-variable.boolean {
10+
color: var(--theia-variable-boolean-variable-color);
11+
}
12+
.theia-debug-hover-title.string,
13+
.theia-debug-console-variable.string {
14+
color: var(--theia-variable-string-variable-color);
15+
}
16+
17+
/* To unset the default debug hover dimension. */
18+
.theia-debug-hover {
19+
min-width: unset;
20+
min-height: unset;
21+
width: unset;
22+
height: unset;
23+
}
24+
25+
/* To adjust the left padding in the hover title. */
26+
.theia-debug-hover-title {
27+
padding-left: 5px;
28+
}
29+
30+
/* Use the default Theia dimensions only iff the expression is complex (`!!expression.hasChildren~) */
31+
.theia-debug-hover.complex-value {
32+
min-width: 324px;
33+
min-height: 324px;
34+
width: 324px;
35+
height: 324px;
36+
}

Diff for: arduino-ide-extension/src/browser/style/index.css

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
@import './terminal.css';
88
@import './editor.css';
99
@import './settings-dialog.css';
10+
@import './debug.css';
1011

1112
.theia-input.warning:focus {
1213
outline-width: 1px;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import debounce from 'p-debounce';
2+
import { inject, injectable, postConstruct, interfaces, Container } from 'inversify';
3+
import URI from '@theia/core/lib/common/uri';
4+
import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
5+
import { MonacoConfigurationService } from '@theia/monaco/lib/browser/monaco-frontend-module';
6+
import { INLINE_VALUE_DECORATION_KEY } from '@theia/debug/lib/browser/editor//debug-inline-value-decorator';
7+
import { DebugEditor } from '@theia/debug/lib/browser/editor/debug-editor';
8+
import { DebugExceptionWidget } from '@theia/debug/lib/browser/editor/debug-exception-widget';
9+
import { DebugBreakpointWidget } from '@theia/debug/lib/browser/editor/debug-breakpoint-widget';
10+
import { DebugEditorModel as TheiaDebugEditorModel } from '@theia/debug/lib/browser/editor/debug-editor-model';
11+
import { createDebugHoverWidgetContainer } from './debug-hover-widget'
12+
13+
// TODO: Remove after https://github.com/eclipse-theia/theia/pull/9256/
14+
@injectable()
15+
export class DebugEditorModel extends TheiaDebugEditorModel {
16+
17+
static createContainer(parent: interfaces.Container, editor: DebugEditor): Container {
18+
const child = createDebugHoverWidgetContainer(parent, editor);
19+
child.bind(DebugEditorModel).toSelf();
20+
child.bind(DebugBreakpointWidget).toSelf();
21+
child.bind(DebugExceptionWidget).toSelf();
22+
return child;
23+
}
24+
25+
static createModel(parent: interfaces.Container, editor: DebugEditor): DebugEditorModel {
26+
return DebugEditorModel.createContainer(parent, editor).get(DebugEditorModel);
27+
}
28+
29+
@inject(MonacoConfigurationService)
30+
readonly configurationService: monaco.services.IConfigurationService;
31+
32+
protected readonly toDisposeOnRenderFrames = new DisposableCollection();
33+
34+
@postConstruct()
35+
protected init(): void {
36+
this.toDispose.push(this.toDisposeOnRenderFrames);
37+
super.init();
38+
}
39+
40+
protected async updateEditorHover(): Promise<void> {
41+
if (this.isCurrentEditorFrame(this.uri)) {
42+
const codeEditor = this.editor.getControl();
43+
codeEditor.updateOptions({ hover: { enabled: false } });
44+
this.toDisposeOnRenderFrames.push(Disposable.create(() => {
45+
const model = codeEditor.getModel()!;
46+
const overrides = {
47+
resource: model.uri,
48+
overrideIdentifier: (model as any).getLanguageIdentifier().language,
49+
};
50+
const { enabled, delay, sticky } = this.configurationService._configuration.getValue('editor.hover', overrides, undefined);
51+
codeEditor.updateOptions({
52+
hover: {
53+
enabled,
54+
delay,
55+
sticky
56+
}
57+
});
58+
}));
59+
}
60+
}
61+
62+
private isCurrentEditorFrame(uri: URI): boolean {
63+
return this.sessions.currentFrame?.source?.uri.toString() === uri.toString();
64+
}
65+
66+
protected readonly renderFrames = debounce(async () => {
67+
if (this.toDispose.disposed) {
68+
return;
69+
}
70+
this.toDisposeOnRenderFrames.dispose();
71+
72+
this.toggleExceptionWidget();
73+
const [newFrameDecorations, inlineValueDecorations] = await Promise.all([
74+
this.createFrameDecorations(),
75+
this.createInlineValueDecorations()
76+
]);
77+
const codeEditor = this.editor.getControl();
78+
codeEditor.removeDecorations(INLINE_VALUE_DECORATION_KEY);
79+
codeEditor.setDecorations(INLINE_VALUE_DECORATION_KEY, inlineValueDecorations);
80+
this.frameDecorations = this.deltaDecorations(this.frameDecorations, newFrameDecorations);
81+
this.updateEditorHover();
82+
}, 100);
83+
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { injectable } from 'inversify';
2+
import { ExpressionItem, DebugVariable } from '@theia/debug/lib/browser/console/debug-console-items';
3+
import { DebugHoverSource as TheiaDebugHoverSource } from '@theia/debug/lib/browser/editor/debug-hover-source';
4+
5+
// TODO: remove after https://github.com/eclipse-theia/theia/pull/9256/.
6+
@injectable()
7+
export class DebugHoverSource extends TheiaDebugHoverSource {
8+
9+
async evaluate2(expression: string): Promise<ExpressionItem | DebugVariable | undefined> {
10+
const evaluated = await this.doEvaluate(expression);
11+
const elements = evaluated && await evaluated.getElements();
12+
this._expression = evaluated;
13+
this.elements = elements ? [...elements] : [];
14+
this.fireDidChange();
15+
return evaluated;
16+
}
17+
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { injectable, interfaces, Container } from 'inversify';
2+
import { Widget } from '@phosphor/widgets';
3+
import { SourceTreeWidget } from '@theia/core/lib/browser/source-tree';
4+
import { DisposableCollection } from '@theia/core/lib/common/disposable';
5+
import { DebugEditor } from '@theia/debug/lib/browser/editor/debug-editor';
6+
import { DebugVariable } from '@theia/debug/lib/browser/console/debug-console-items';
7+
import { DebugExpressionProvider } from '@theia/debug/lib/browser/editor/debug-expression-provider';
8+
import { DebugHoverSource as TheiaDebugHoverSource } from '@theia/debug/lib/browser/editor/debug-hover-source';
9+
import { DebugHoverWidget as TheiaDebugHoverWidget, ShowDebugHoverOptions } from '@theia/debug/lib/browser/editor/debug-hover-widget';
10+
import { DebugHoverSource } from './debug-hover-source';
11+
12+
export function createDebugHoverWidgetContainer(parent: interfaces.Container, editor: DebugEditor): Container {
13+
const child = SourceTreeWidget.createContainer(parent, {
14+
virtualized: false
15+
});
16+
child.bind(DebugEditor).toConstantValue(editor);
17+
child.bind(TheiaDebugHoverSource).toSelf();
18+
child.bind(DebugHoverSource).toSelf();
19+
child.rebind(TheiaDebugHoverSource).to(DebugHoverSource);
20+
child.unbind(SourceTreeWidget);
21+
child.bind(DebugExpressionProvider).toSelf();
22+
child.bind(TheiaDebugHoverWidget).toSelf();
23+
child.bind(DebugHoverWidget).toSelf();
24+
child.rebind(TheiaDebugHoverWidget).to(DebugHoverWidget);
25+
return child;
26+
}
27+
28+
// TODO: remove patch after https://github.com/eclipse-theia/theia/pull/9256/
29+
@injectable()
30+
export class DebugHoverWidget extends TheiaDebugHoverWidget {
31+
32+
protected async doShow(options: ShowDebugHoverOptions | undefined = this.options): Promise<void> {
33+
if (!this.isEditorFrame()) {
34+
this.hide();
35+
return;
36+
}
37+
if (!options) {
38+
this.hide();
39+
return;
40+
}
41+
if (this.options && this.options.selection.equalsRange(options.selection)) {
42+
return;
43+
}
44+
if (!this.isAttached) {
45+
Widget.attach(this, this.contentNode);
46+
}
47+
48+
this.options = options;
49+
const matchingExpression = this.expressionProvider.get(this.editor.getControl().getModel()!, options.selection);
50+
if (!matchingExpression) {
51+
this.hide();
52+
return;
53+
}
54+
const toFocus = new DisposableCollection();
55+
if (this.options.focus === true) {
56+
toFocus.push(this.model.onNodeRefreshed(() => {
57+
toFocus.dispose();
58+
this.activate();
59+
}));
60+
}
61+
const expression = await (this.hoverSource as DebugHoverSource).evaluate2(matchingExpression);
62+
if (!expression || !expression.value) {
63+
toFocus.dispose();
64+
this.hide();
65+
return;
66+
}
67+
68+
this.contentNode.hidden = false;
69+
['number', 'boolean', 'string'].forEach(token => this.titleNode.classList.remove(token));
70+
this.domNode.classList.remove('complex-value');
71+
if (expression.hasElements) {
72+
this.domNode.classList.add('complex-value');
73+
} else {
74+
this.contentNode.hidden = true;
75+
if (expression.type === 'number' || expression.type === 'boolean' || expression.type === 'string') {
76+
this.titleNode.classList.add(expression.type);
77+
} else if (!isNaN(+expression.value)) {
78+
this.titleNode.classList.add('number');
79+
} else if (DebugVariable.booleanRegex.test(expression.value)) {
80+
this.titleNode.classList.add('boolean');
81+
} else if (DebugVariable.stringRegex.test(expression.value)) {
82+
this.titleNode.classList.add('string');
83+
}
84+
}
85+
86+
// super.show(); // Here we cannot call `super.show()` but have to call `show` on the `Widget` prototype.
87+
Widget.prototype.show.call(this);
88+
await new Promise<void>(resolve => {
89+
setTimeout(() => window.requestAnimationFrame(() => {
90+
this.editor.getControl().layoutContentWidget(this);
91+
resolve();
92+
}), 0);
93+
});
94+
}
95+
96+
}

0 commit comments

Comments
 (0)