Skip to content

Commit 13a1f15

Browse files
colin-grant-worklaemmleint
authored andcommitted
Use SimpleMonacoEditor for most inline cases (eclipse-theia#15389)
1 parent 41be92e commit 13a1f15

File tree

14 files changed

+102
-83
lines changed

14 files changed

+102
-83
lines changed

examples/api-samples/src/browser/chat/ask-and-continue-chat-agent-contribution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export function bindAskAndContinueChatAgentContribution(bind: interfaces.Bind):
3636
const systemPrompt: PromptTemplate = {
3737
id: 'askAndContinue-system',
3838
template: `
39-
You are an agent demonstrating on how to generate questions and continuing the conversation based on the user's answers.
39+
You are an agent demonstrating how to generate questions and continue the conversation based on the user's answers.
4040
4141
First answer the user's question or continue their story.
4242
Then come up with an interesting question and 2-3 answers which will be presented to the user as multiple choice.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
"license:check": "node scripts/check_3pp_licenses.js",
7474
"license:check:review": "node scripts/check_3pp_licenses.js --review",
7575
"lint": "lerna run lint",
76+
"lint:fix": "lerna run lint -- --fix",
7677
"lint:clean": "rimraf .eslintcache",
7778
"preinstall": "node-gyp install",
7879
"postinstall": "theia-patch && npm run -s compute-references && lerna run afterInstall",

packages/ai-chat-ui/src/browser/chat-input-widget.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { Deferred } from '@theia/core/lib/common/promise-util';
2020
import { inject, injectable, optional, postConstruct } from '@theia/core/shared/inversify';
2121
import * as React from '@theia/core/shared/react';
2222
import { IMouseEvent } from '@theia/monaco-editor-core';
23-
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
23+
import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor';
2424
import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
2525
import { CHAT_VIEW_LANGUAGE_EXTENSION } from './chat-view-language-contribution';
2626
import { AIVariableResolutionRequest } from '@theia/ai-core';
@@ -74,7 +74,7 @@ export class AIChatInputWidget extends ReactWidget {
7474
@inject(ChangeSetDecoratorService)
7575
protected readonly changeSetDecoratorService: ChangeSetDecoratorService;
7676

77-
protected editorRef: MonacoEditor | undefined = undefined;
77+
protected editorRef: SimpleMonacoEditor | undefined = undefined;
7878
protected readonly editorReady = new Deferred<void>();
7979

8080
protected isEnabled = false;
@@ -267,7 +267,7 @@ interface ChatInputProperties {
267267
resources: InMemoryResources;
268268
resourceUriProvider: () => URI;
269269
contextMenuCallback: (event: IMouseEvent) => void;
270-
setEditorRef: (editor: MonacoEditor | undefined) => void;
270+
setEditorRef: (editor: SimpleMonacoEditor | undefined) => void;
271271
showContext?: boolean;
272272
showPinnedAgent?: boolean;
273273
showChangeSet?: boolean;
@@ -300,7 +300,7 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
300300
const editorContainerRef = React.useRef<HTMLDivElement | null>(null);
301301
// eslint-disable-next-line no-null/no-null
302302
const placeholderRef = React.useRef<HTMLDivElement | null>(null);
303-
const editorRef = React.useRef<MonacoEditor | undefined>(undefined);
303+
const editorRef = React.useRef<SimpleMonacoEditor | undefined>(undefined);
304304

305305
React.useEffect(() => {
306306
const uri = props.resourceUriProvider();
@@ -309,7 +309,7 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
309309
const paddingTop = 6;
310310
const lineHeight = 20;
311311
const maxHeight = 240;
312-
const editor = await props.editorProvider.createInline(uri, editorContainerRef.current!, {
312+
const editor = await props.editorProvider.createSimpleInline(uri, editorContainerRef.current!, {
313313
language: CHAT_VIEW_LANGUAGE_EXTENSION,
314314
// Disable code lens, inlay hints and hover support to avoid console errors from other contributions
315315
codeLens: false,

packages/ai-chat-ui/src/browser/chat-response-renderer/code-part-renderer.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { ReactNode } from '@theia/core/shared/react';
2626
import { nls } from '@theia/core/lib/common/nls';
2727
import { Position } from '@theia/core/shared/vscode-languageserver-protocol';
2828
import { EditorManager, EditorWidget } from '@theia/editor/lib/browser';
29-
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
29+
import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor';
3030
import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
3131
import { MonacoLanguages } from '@theia/monaco/lib/browser/monaco-languages';
3232
import { ChatResponsePartRenderer } from '../chat-response-part-renderer';
@@ -206,11 +206,11 @@ export const CodeWrapper = (props: {
206206
}) => {
207207
// eslint-disable-next-line no-null/no-null
208208
const ref = React.useRef<HTMLDivElement | null>(null);
209-
const editorRef = React.useRef<MonacoEditor | undefined>(undefined);
209+
const editorRef = React.useRef<SimpleMonacoEditor | undefined>(undefined);
210210

211211
const createInputElement = async () => {
212212
const resource = await props.untitledResourceResolver.createUntitledResource(undefined, props.language);
213-
const editor = await props.editorProvider.createInline(resource.uri, ref.current!, {
213+
const editor = await props.editorProvider.createSimpleInline(resource.uri, ref.current!, {
214214
readOnly: true,
215215
autoSizing: true,
216216
scrollBeyondLastLine: false,

packages/ai-chat-ui/src/browser/chat-view-contribution.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import {
2222
isResponseNode, RequestNode, ResponseNode, type EditableRequestNode
2323
} from './chat-tree-view/chat-view-tree-widget';
2424
import { AIChatInputWidget } from './chat-input-widget';
25-
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
2625

2726
export namespace ChatViewCommands {
2827
export const COPY_MESSAGE = Command.toDefaultLocalizedCommand({
@@ -63,17 +62,6 @@ export class ChatViewMenuContribution implements MenuContribution, CommandContri
6362
},
6463
isEnabled: (...args: unknown[]) => containsRequestOrResponseNode(args)
6564
});
66-
commands.registerHandler(CommonCommands.PASTE.id, {
67-
execute: async (...args) => {
68-
if (hasEditorAsFirstArg(args)) {
69-
const editor = args[0];
70-
const range = editor.selection;
71-
const newText = await this.clipboardService.readText();
72-
editor.executeEdits([{ range, newText }]);
73-
}
74-
},
75-
isEnabled: (...args) => hasEditorAsFirstArg(args)
76-
});
7765
commands.registerCommand(ChatViewCommands.COPY_MESSAGE, {
7866
execute: (...args: unknown[]) => {
7967
if (containsRequestOrResponseNode(args)) {
@@ -162,10 +150,6 @@ export class ChatViewMenuContribution implements MenuContribution, CommandContri
162150

163151
}
164152

165-
function hasEditorAsFirstArg(args: unknown[]): args is [MonacoEditor, ...unknown[]] {
166-
return hasAsFirstArg(args, (arg): arg is MonacoEditor => arg instanceof MonacoEditor);
167-
}
168-
169153
function hasAsFirstArg<T>(args: unknown[], guard: (arg: unknown) => arg is T): args is [T, ...unknown[]] {
170154
return args.length > 0 && guard(args[0]);
171155
}

packages/debug/src/browser/debug-configuration-manager.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -342,12 +342,8 @@ export class DebugConfigurationManager {
342342
if (!model) {
343343
return;
344344
}
345-
const widget = await this.doOpen(model);
346-
if (!(widget.editor instanceof MonacoEditor)) {
347-
return;
348-
}
349-
const editor = widget.editor.getControl();
350-
const editorModel = editor.getModel();
345+
const editor = MonacoEditor.get(await this.doOpen(model))?.getControl();
346+
const editorModel = editor && editor.getModel();
351347
if (!editorModel) {
352348
return;
353349
}

packages/debug/src/browser/editor/debug-breakpoint-widget.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,14 @@ import { Disposable, DisposableCollection, InMemoryResources, nls } from '@theia
2222
import URI from '@theia/core/lib/common/uri';
2323
import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
2424
import { MonacoEditorZoneWidget } from '@theia/monaco/lib/browser/monaco-editor-zone-widget';
25-
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
25+
import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor';
2626
import { DebugEditor } from './debug-editor';
2727
import { DebugSourceBreakpoint } from '../model/debug-source-breakpoint';
2828
import { Dimension } from '@theia/editor/lib/browser';
2929
import * as monaco from '@theia/monaco-editor-core';
3030
import { LanguageSelector } from '@theia/monaco-editor-core/esm/vs/editor/common/languageSelector';
3131
import { provideSuggestionItems, CompletionOptions } from '@theia/monaco-editor-core/esm/vs/editor/contrib/suggest/browser/suggest';
3232
import { IDecorationOptions } from '@theia/monaco-editor-core/esm/vs/editor/common/editorCommon';
33-
import { StandaloneCodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor';
3433
import { CompletionItemKind, CompletionContext } from '@theia/monaco-editor-core/esm/vs/editor/common/languages';
3534
import { ILanguageFeaturesService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/languageFeatures';
3635
import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices';
@@ -83,8 +82,8 @@ export class DebugBreakpointWidget implements Disposable {
8382
};
8483
}
8584

86-
protected _input: MonacoEditor | undefined;
87-
get input(): MonacoEditor | undefined {
85+
protected _input: SimpleMonacoEditor | undefined;
86+
get input(): SimpleMonacoEditor | undefined {
8887
return this._input;
8988
}
9089
// eslint-disable-next-line no-null/no-null
@@ -162,11 +161,11 @@ export class DebugBreakpointWidget implements Disposable {
162161
}));
163162
this.toDispose.push(this.zone.onDidLayoutChange(dimension => this.layout(dimension)));
164163
this.toDispose.push(input.getControl().onDidChangeModelContent(() => {
165-
const heightInLines = input.getControl().getModel()?.getLineCount() || 0 + 1;
166-
this.zone.layout(heightInLines);
164+
const heightInLines = input.getControl().getModel()?.getLineCount();
165+
this.zone.layout(Math.max(heightInLines ?? 0, 2));
167166
this.updatePlaceholder();
168167
}));
169-
this._input.getControl().createContextKey<boolean>('breakpointWidgetFocus', true);
168+
this._input.getControl().contextKeyService.createKey<boolean>('breakpointWidgetFocus', true);
170169
}
171170

172171
dispose(): void {
@@ -226,8 +225,8 @@ export class DebugBreakpointWidget implements Disposable {
226225
}
227226
}
228227

229-
protected createInput(node: HTMLElement): Promise<MonacoEditor> {
230-
return this.editorProvider.createInline(this.uri, node, {
228+
protected createInput(node: HTMLElement): Promise<SimpleMonacoEditor> {
229+
return this.editorProvider.createSimpleInline(this.uri, node, {
231230
autoSizing: false
232231
});
233232
}
@@ -284,8 +283,7 @@ export class DebugBreakpointWidget implements Disposable {
284283
}
285284
}
286285
}];
287-
(this._input.getControl() as unknown as StandaloneCodeEditor)
288-
.setDecorationsByType('Debug breakpoint placeholder', DebugBreakpointWidget.PLACEHOLDER_DECORATION, decorations);
286+
this._input.getControl().setDecorationsByType('Debug breakpoint placeholder', DebugBreakpointWidget.PLACEHOLDER_DECORATION, decorations);
289287
}
290288
protected get placeholder(): string {
291289
const acceptString = 'Enter';

packages/debug/src/browser/editor/debug-editor-service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ export class DebugEditorService {
5353
}
5454

5555
protected push(widget: EditorWidget): void {
56-
const { editor } = widget;
57-
if (!(editor instanceof MonacoEditor)) {
56+
const editor = MonacoEditor.get(widget);
57+
if (!editor) {
5858
return;
5959
}
6060
const uri = editor.getResourceUri().toString();

packages/debug/src/browser/style/index.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,10 @@
414414
margin-bottom: var(--theia-ui-padding);
415415
}
416416

417+
.theia-debug-breakpoint-input .monaco-editor {
418+
outline: none;
419+
}
420+
417421
/* Status Bar */
418422
.theia-mod-debugging #theia-statusBar {
419423
background: var(--theia-statusBar-debuggingBackground);

packages/monaco/src/browser/monaco-editor-provider.ts

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ import { IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/co
4444
import { ITextModelService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/resolverService';
4545
import { IReference } from '@theia/monaco-editor-core/esm/vs/base/common/lifecycle';
4646
import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
47+
import { SimpleMonacoEditor } from './simple-monaco-editor';
48+
import { ICodeEditorWidgetOptions } from '@theia/monaco-editor-core/esm/vs/editor/browser/widget/codeEditor/codeEditorWidget';
4749

4850
export const MonacoEditorFactory = Symbol('MonacoEditorFactory');
4951
export interface MonacoEditorFactory {
@@ -108,9 +110,9 @@ export class MonacoEditorProvider {
108110
return this.doCreateEditor(uri, (override, toDispose) => this.createEditor(uri, override, toDispose));
109111
}
110112

111-
protected async doCreateEditor(uri: URI, factory: (
112-
override: EditorServiceOverrides, toDispose: DisposableCollection) => Promise<MonacoEditor>
113-
): Promise<MonacoEditor> {
113+
protected async doCreateEditor<T>(uri: URI, factory: (
114+
override: EditorServiceOverrides, toDispose: DisposableCollection) => Promise<T>
115+
): Promise<T> {
114116
const domNode = document.createElement('div');
115117
const contextKeyService = StandaloneServices.get(IContextKeyService).createScoped(domNode);
116118
StandaloneServices.get(IOpenerService).registerOpener({
@@ -121,21 +123,22 @@ export class MonacoEditorProvider {
121123
];
122124
const toDispose = new DisposableCollection();
123125
const editor = await factory(overrides, toDispose);
124-
editor.onDispose(() => toDispose.dispose());
125-
126-
this.injectKeybindingResolver(editor);
127-
128-
toDispose.push(editor.onFocusChanged(focused => {
129-
if (focused) {
130-
this._current = editor;
131-
}
132-
}));
133-
toDispose.push(Disposable.create(() => {
134-
if (this._current === editor) {
135-
this._current = undefined;
136-
}
137-
}));
138-
126+
if (editor instanceof MonacoEditor) {
127+
editor.onDispose(() => toDispose.dispose());
128+
129+
this.injectKeybindingResolver(editor);
130+
131+
toDispose.push(editor.onFocusChanged(focused => {
132+
if (focused) {
133+
this._current = editor;
134+
}
135+
}));
136+
toDispose.push(Disposable.create(() => {
137+
if (this._current === editor) {
138+
this._current = undefined;
139+
}
140+
}));
141+
}
139142
return editor;
140143
}
141144

@@ -369,6 +372,11 @@ export class MonacoEditorProvider {
369372
return MonacoDiffNavigatorFactory.nullNavigator;
370373
}
371374

375+
/**
376+
* Creates an instance of the standard MonacoEditor with a StandaloneCodeEditor as its Monaco delegeate.
377+
* Among other differences, these editors execute basic actions like typing or deletion via commands that may be overridden by extensions.
378+
* @deprecated Most use cases for inline editors should be served by `createSimpleInline` instead.
379+
*/
372380
async createInline(uri: URI, node: HTMLElement, options?: MonacoEditor.IOptions): Promise<MonacoEditor> {
373381
return this.doCreateEditor(uri, async (override, toDispose) => {
374382
const overrides = override ? Array.from(override) : [];
@@ -383,7 +391,6 @@ export class MonacoEditorProvider {
383391
this.services,
384392
Object.assign({
385393
model,
386-
isSimpleWidget: true,
387394
autoSizing: false,
388395
minHeight: 1,
389396
maxHeight: 1
@@ -393,6 +400,42 @@ export class MonacoEditorProvider {
393400
});
394401
}
395402

403+
/**
404+
* Creates an instance of the standard MonacoEditor with a CodeEditorWidget as its Monaco delegeate.
405+
* In addition to the service customizability of the StandaloneCodeEditor,This editor allows greater customization the editor contributions active in the widget.
406+
* See {@link ICodeEditorWidgetOptions.contributions}.
407+
* @deprecated Most use cases for inline editors should be served by `createSimpleInline` instead.
408+
*/
409+
async createSimpleInline(uri: URI, node: HTMLElement, options?: MonacoEditor.IOptions, widgetOptions?: ICodeEditorWidgetOptions): Promise<SimpleMonacoEditor> {
410+
return this.doCreateEditor(uri, async (override, toDispose) => {
411+
const overrides = override ? Array.from(override) : [];
412+
overrides.push([IContextMenuService, { showContextMenu: () => { /** no op! */ } }]);
413+
const document = await this.getModel(uri, toDispose);
414+
document.suppressOpenEditorWhenDirty = true;
415+
const model = (await document.load()).textEditorModel;
416+
const baseOptions: Partial<MonacoEditor.IOptions> = {
417+
model,
418+
autoSizing: false,
419+
minHeight: 1,
420+
maxHeight: 1
421+
};
422+
const editorOptions = {
423+
...baseOptions,
424+
...MonacoEditorProvider.inlineOptions,
425+
...options
426+
};
427+
return new SimpleMonacoEditor(
428+
uri,
429+
document,
430+
node,
431+
this.services,
432+
editorOptions,
433+
overrides,
434+
{ isSimpleWidget: true, ...widgetOptions }
435+
);
436+
});
437+
}
438+
396439
static inlineOptions: monaco.editor.IEditorConstructionOptions = {
397440
wordWrap: 'on',
398441
overviewRulerLanes: 0,
@@ -451,7 +494,7 @@ export class MonacoEditorProvider {
451494
override,
452495
parentEditor
453496
)
454-
) as MonacoDiffEditor;
497+
);
455498
}
456499

457500
protected insertFinalNewline(editor: MonacoEditor): monaco.editor.IIdentifiedSingleEditOperation[] {

0 commit comments

Comments
 (0)