Skip to content

Commit 9f934ea

Browse files
Use SimpleMonacoEditor for most inline cases
1 parent fcb684a commit 9f934ea

File tree

14 files changed

+102
-84
lines changed

14 files changed

+102
-84
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
@@ -35,7 +35,7 @@ export function bindAskAndContinueChatAgentContribution(bind: interfaces.Bind):
3535
const systemPrompt: PromptTemplate = {
3636
id: 'askAndContinue-system',
3737
template: `
38-
You are an agent demonstrating on how to generate questions and continuing the conversation based on the user's answers.
38+
You are an agent demonstrating how to generate questions and continue the conversation based on the user's answers.
3939
4040
First answer the user's question or continue their story.
4141
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
@@ -74,6 +74,7 @@
7474
"license:check": "node scripts/check_3pp_licenses.js",
7575
"license:check:review": "node scripts/check_3pp_licenses.js --review",
7676
"lint": "lerna run lint",
77+
"lint:fix": "lerna run lint -- --fix",
7778
"lint:clean": "rimraf .eslintcache",
7879
"preinstall": "node-gyp install",
7980
"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';
@@ -69,7 +69,7 @@ export class AIChatInputWidget extends ReactWidget {
6969
@inject(ChangeSetActionService)
7070
protected readonly changeSetActionService: ChangeSetActionService;
7171

72-
protected editorRef: MonacoEditor | undefined = undefined;
72+
protected editorRef: SimpleMonacoEditor | undefined = undefined;
7373
private editorReady = new Deferred<void>();
7474

7575
protected isEnabled = false;
@@ -244,7 +244,7 @@ interface ChatInputProperties {
244244
editorProvider: MonacoEditorProvider;
245245
resources: InMemoryResources;
246246
contextMenuCallback: (event: IMouseEvent) => void;
247-
setEditorRef: (editor: MonacoEditor | undefined) => void;
247+
setEditorRef: (editor: SimpleMonacoEditor | undefined) => void;
248248
showContext?: boolean;
249249
showPinnedAgent?: boolean;
250250
labelProvider: LabelProvider;
@@ -273,7 +273,7 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
273273
const editorContainerRef = React.useRef<HTMLDivElement | null>(null);
274274
// eslint-disable-next-line no-null/no-null
275275
const placeholderRef = React.useRef<HTMLDivElement | null>(null);
276-
const editorRef = React.useRef<MonacoEditor | undefined>(undefined);
276+
const editorRef = React.useRef<SimpleMonacoEditor | undefined>(undefined);
277277

278278
React.useEffect(() => {
279279
const uri = new URI(`ai-chat:/input.${CHAT_VIEW_LANGUAGE_EXTENSION}`);
@@ -282,7 +282,7 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
282282
const paddingTop = 6;
283283
const lineHeight = 20;
284284
const maxHeight = 240;
285-
const editor = await props.editorProvider.createInline(uri, editorContainerRef.current!, {
285+
const editor = await props.editorProvider.createSimpleInline(uri, editorContainerRef.current!, {
286286
language: CHAT_VIEW_LANGUAGE_EXTENSION,
287287
// Disable code lens, inlay hints and hover support to avoid console errors from other contributions
288288
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 & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
1919
import { inject, injectable } from '@theia/core/shared/inversify';
2020
import { ChatViewTreeWidget, isRequestNode, isResponseNode, RequestNode, ResponseNode } from './chat-tree-view/chat-view-tree-widget';
2121
import { AIChatInputWidget } from './chat-input-widget';
22-
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
2322

2423
export namespace ChatViewCommands {
2524
export const COPY_MESSAGE = Command.toDefaultLocalizedCommand({
@@ -56,17 +55,6 @@ export class ChatViewMenuContribution implements MenuContribution, CommandContri
5655
},
5756
isEnabled: (...args: unknown[]) => containsRequestOrResponseNode(args)
5857
});
59-
commands.registerHandler(CommonCommands.PASTE.id, {
60-
execute: async (...args) => {
61-
if (hasEditorAsFirstArg(args)) {
62-
const editor = args[0];
63-
const range = editor.selection;
64-
const newText = await this.clipboardService.readText();
65-
editor.executeEdits([{ range, newText }]);
66-
}
67-
},
68-
isEnabled: (...args) => hasEditorAsFirstArg(args)
69-
});
7058
commands.registerCommand(ChatViewCommands.COPY_MESSAGE, {
7159
execute: (...args: unknown[]) => {
7260
if (containsRequestOrResponseNode(args)) {
@@ -142,11 +130,6 @@ export class ChatViewMenuContribution implements MenuContribution, CommandContri
142130
commandId: CommonCommands.PASTE.id
143131
});
144132
}
145-
146-
}
147-
148-
function hasEditorAsFirstArg(args: unknown[]): args is [MonacoEditor, ...unknown[]] {
149-
return args.length > 0 && args[0] instanceof MonacoEditor;
150133
}
151134

152135
function extractRequestOrResponseNodes(args: unknown[]): (RequestNode | ResponseNode)[] {

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)