Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Use SimpleMonacoEditor for most inline cases
  • Loading branch information
colin-grant-work committed Apr 25, 2025
commit 34dea8a9c027d23ed9874502c5fb855cc7392ecb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function bindAskAndContinueChatAgentContribution(bind: interfaces.Bind):
const systemPrompt: PromptTemplate = {
id: 'askAndContinue-system',
template: `
You are an agent demonstrating on how to generate questions and continuing the conversation based on the user's answers.
You are an agent demonstrating how to generate questions and continue the conversation based on the user's answers.
First answer the user's question or continue their story.
Then come up with an interesting question and 2-3 answers which will be presented to the user as multiple choice.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"license:check": "node scripts/check_3pp_licenses.js",
"license:check:review": "node scripts/check_3pp_licenses.js --review",
"lint": "lerna run lint",
"lint:fix": "lerna run lint -- --fix",
"lint:clean": "rimraf .eslintcache",
"preinstall": "node-gyp install",
"postinstall": "theia-patch && npm run -s compute-references && lerna run afterInstall",
Expand Down
10 changes: 5 additions & 5 deletions packages/ai-chat-ui/src/browser/chat-input-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { Deferred } from '@theia/core/lib/common/promise-util';
import { inject, injectable, optional, postConstruct } from '@theia/core/shared/inversify';
import * as React from '@theia/core/shared/react';
import { IMouseEvent } from '@theia/monaco-editor-core';
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor';
import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
import { CHAT_VIEW_LANGUAGE_EXTENSION } from './chat-view-language-contribution';
import { AIVariableResolutionRequest } from '@theia/ai-core';
Expand Down Expand Up @@ -74,7 +74,7 @@ export class AIChatInputWidget extends ReactWidget {
@inject(ChangeSetDecoratorService)
protected readonly changeSetDecoratorService: ChangeSetDecoratorService;

protected editorRef: MonacoEditor | undefined = undefined;
protected editorRef: SimpleMonacoEditor | undefined = undefined;
protected readonly editorReady = new Deferred<void>();

protected isEnabled = false;
Expand Down Expand Up @@ -267,7 +267,7 @@ interface ChatInputProperties {
resources: InMemoryResources;
resourceUriProvider: () => URI;
contextMenuCallback: (event: IMouseEvent) => void;
setEditorRef: (editor: MonacoEditor | undefined) => void;
setEditorRef: (editor: SimpleMonacoEditor | undefined) => void;
showContext?: boolean;
showPinnedAgent?: boolean;
showChangeSet?: boolean;
Expand Down Expand Up @@ -300,7 +300,7 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
const editorContainerRef = React.useRef<HTMLDivElement | null>(null);
// eslint-disable-next-line no-null/no-null
const placeholderRef = React.useRef<HTMLDivElement | null>(null);
const editorRef = React.useRef<MonacoEditor | undefined>(undefined);
const editorRef = React.useRef<SimpleMonacoEditor | undefined>(undefined);

React.useEffect(() => {
const uri = props.resourceUriProvider();
Expand All @@ -309,7 +309,7 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
const paddingTop = 6;
const lineHeight = 20;
const maxHeight = 240;
const editor = await props.editorProvider.createInline(uri, editorContainerRef.current!, {
const editor = await props.editorProvider.createSimpleInline(uri, editorContainerRef.current!, {
language: CHAT_VIEW_LANGUAGE_EXTENSION,
// Disable code lens, inlay hints and hover support to avoid console errors from other contributions
codeLens: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { ReactNode } from '@theia/core/shared/react';
import { nls } from '@theia/core/lib/common/nls';
import { Position } from '@theia/core/shared/vscode-languageserver-protocol';
import { EditorManager, EditorWidget } from '@theia/editor/lib/browser';
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor';
import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
import { MonacoLanguages } from '@theia/monaco/lib/browser/monaco-languages';
import { ChatResponsePartRenderer } from '../chat-response-part-renderer';
Expand Down Expand Up @@ -206,11 +206,11 @@ export const CodeWrapper = (props: {
}) => {
// eslint-disable-next-line no-null/no-null
const ref = React.useRef<HTMLDivElement | null>(null);
const editorRef = React.useRef<MonacoEditor | undefined>(undefined);
const editorRef = React.useRef<SimpleMonacoEditor | undefined>(undefined);

const createInputElement = async () => {
const resource = await props.untitledResourceResolver.createUntitledResource(undefined, props.language);
const editor = await props.editorProvider.createInline(resource.uri, ref.current!, {
const editor = await props.editorProvider.createSimpleInline(resource.uri, ref.current!, {
readOnly: true,
autoSizing: true,
scrollBeyondLastLine: false,
Expand Down
16 changes: 0 additions & 16 deletions packages/ai-chat-ui/src/browser/chat-view-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
isResponseNode, RequestNode, ResponseNode, type EditableRequestNode
} from './chat-tree-view/chat-view-tree-widget';
import { AIChatInputWidget } from './chat-input-widget';
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';

export namespace ChatViewCommands {
export const COPY_MESSAGE = Command.toDefaultLocalizedCommand({
Expand Down Expand Up @@ -63,17 +62,6 @@ export class ChatViewMenuContribution implements MenuContribution, CommandContri
},
isEnabled: (...args: unknown[]) => containsRequestOrResponseNode(args)
});
commands.registerHandler(CommonCommands.PASTE.id, {
execute: async (...args) => {
if (hasEditorAsFirstArg(args)) {
const editor = args[0];
const range = editor.selection;
const newText = await this.clipboardService.readText();
editor.executeEdits([{ range, newText }]);
}
},
isEnabled: (...args) => hasEditorAsFirstArg(args)
});
commands.registerCommand(ChatViewCommands.COPY_MESSAGE, {
execute: (...args: unknown[]) => {
if (containsRequestOrResponseNode(args)) {
Expand Down Expand Up @@ -162,10 +150,6 @@ export class ChatViewMenuContribution implements MenuContribution, CommandContri

}

function hasEditorAsFirstArg(args: unknown[]): args is [MonacoEditor, ...unknown[]] {
return hasAsFirstArg(args, (arg): arg is MonacoEditor => arg instanceof MonacoEditor);
}

function hasAsFirstArg<T>(args: unknown[], guard: (arg: unknown) => arg is T): args is [T, ...unknown[]] {
return args.length > 0 && guard(args[0]);
}
Expand Down
8 changes: 2 additions & 6 deletions packages/debug/src/browser/debug-configuration-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,12 +342,8 @@ export class DebugConfigurationManager {
if (!model) {
return;
}
const widget = await this.doOpen(model);
if (!(widget.editor instanceof MonacoEditor)) {
return;
}
const editor = widget.editor.getControl();
const editorModel = editor.getModel();
const editor = MonacoEditor.get(await this.doOpen(model))?.getControl();
const editorModel = editor && editor.getModel();
if (!editorModel) {
return;
}
Expand Down
20 changes: 9 additions & 11 deletions packages/debug/src/browser/editor/debug-breakpoint-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,14 @@ import { Disposable, DisposableCollection, InMemoryResources, nls } from '@theia
import URI from '@theia/core/lib/common/uri';
import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
import { MonacoEditorZoneWidget } from '@theia/monaco/lib/browser/monaco-editor-zone-widget';
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor';
import { DebugEditor } from './debug-editor';
import { DebugSourceBreakpoint } from '../model/debug-source-breakpoint';
import { Dimension } from '@theia/editor/lib/browser';
import * as monaco from '@theia/monaco-editor-core';
import { LanguageSelector } from '@theia/monaco-editor-core/esm/vs/editor/common/languageSelector';
import { provideSuggestionItems, CompletionOptions } from '@theia/monaco-editor-core/esm/vs/editor/contrib/suggest/browser/suggest';
import { IDecorationOptions } from '@theia/monaco-editor-core/esm/vs/editor/common/editorCommon';
import { StandaloneCodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor';
import { CompletionItemKind, CompletionContext } from '@theia/monaco-editor-core/esm/vs/editor/common/languages';
import { ILanguageFeaturesService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/languageFeatures';
import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices';
Expand Down Expand Up @@ -83,8 +82,8 @@ export class DebugBreakpointWidget implements Disposable {
};
}

protected _input: MonacoEditor | undefined;
get input(): MonacoEditor | undefined {
protected _input: SimpleMonacoEditor | undefined;
get input(): SimpleMonacoEditor | undefined {
return this._input;
}
// eslint-disable-next-line no-null/no-null
Expand Down Expand Up @@ -162,11 +161,11 @@ export class DebugBreakpointWidget implements Disposable {
}));
this.toDispose.push(this.zone.onDidLayoutChange(dimension => this.layout(dimension)));
this.toDispose.push(input.getControl().onDidChangeModelContent(() => {
const heightInLines = input.getControl().getModel()?.getLineCount() || 0 + 1;
this.zone.layout(heightInLines);
const heightInLines = input.getControl().getModel()?.getLineCount();
this.zone.layout(Math.max(heightInLines ?? 0, 2));
this.updatePlaceholder();
}));
this._input.getControl().createContextKey<boolean>('breakpointWidgetFocus', true);
this._input.getControl().contextKeyService.createKey<boolean>('breakpointWidgetFocus', true);
}

dispose(): void {
Expand Down Expand Up @@ -226,8 +225,8 @@ export class DebugBreakpointWidget implements Disposable {
}
}

protected createInput(node: HTMLElement): Promise<MonacoEditor> {
return this.editorProvider.createInline(this.uri, node, {
protected createInput(node: HTMLElement): Promise<SimpleMonacoEditor> {
return this.editorProvider.createSimpleInline(this.uri, node, {
autoSizing: false
});
}
Expand Down Expand Up @@ -284,8 +283,7 @@ export class DebugBreakpointWidget implements Disposable {
}
}
}];
(this._input.getControl() as unknown as StandaloneCodeEditor)
.setDecorationsByType('Debug breakpoint placeholder', DebugBreakpointWidget.PLACEHOLDER_DECORATION, decorations);
this._input.getControl().setDecorationsByType('Debug breakpoint placeholder', DebugBreakpointWidget.PLACEHOLDER_DECORATION, decorations);
}
protected get placeholder(): string {
const acceptString = 'Enter';
Expand Down
4 changes: 2 additions & 2 deletions packages/debug/src/browser/editor/debug-editor-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ export class DebugEditorService {
}

protected push(widget: EditorWidget): void {
const { editor } = widget;
if (!(editor instanceof MonacoEditor)) {
const editor = MonacoEditor.get(widget);
if (!editor) {
return;
}
const uri = editor.getResourceUri().toString();
Expand Down
4 changes: 4 additions & 0 deletions packages/debug/src/browser/style/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,10 @@
margin-bottom: var(--theia-ui-padding);
}

.theia-debug-breakpoint-input .monaco-editor {
outline: none;
}

/* Status Bar */
.theia-mod-debugging #theia-statusBar {
background: var(--theia-statusBar-debuggingBackground);
Expand Down
83 changes: 63 additions & 20 deletions packages/monaco/src/browser/monaco-editor-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import { IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/co
import { ITextModelService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/resolverService';
import { IReference } from '@theia/monaco-editor-core/esm/vs/base/common/lifecycle';
import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
import { SimpleMonacoEditor } from './simple-monaco-editor';
import { ICodeEditorWidgetOptions } from '@theia/monaco-editor-core/esm/vs/editor/browser/widget/codeEditor/codeEditorWidget';

export const MonacoEditorFactory = Symbol('MonacoEditorFactory');
export interface MonacoEditorFactory {
Expand Down Expand Up @@ -108,9 +110,9 @@ export class MonacoEditorProvider {
return this.doCreateEditor(uri, (override, toDispose) => this.createEditor(uri, override, toDispose));
}

protected async doCreateEditor(uri: URI, factory: (
override: EditorServiceOverrides, toDispose: DisposableCollection) => Promise<MonacoEditor>
): Promise<MonacoEditor> {
protected async doCreateEditor<T>(uri: URI, factory: (
override: EditorServiceOverrides, toDispose: DisposableCollection) => Promise<T>
): Promise<T> {
const domNode = document.createElement('div');
const contextKeyService = StandaloneServices.get(IContextKeyService).createScoped(domNode);
StandaloneServices.get(IOpenerService).registerOpener({
Expand All @@ -121,21 +123,22 @@ export class MonacoEditorProvider {
];
const toDispose = new DisposableCollection();
const editor = await factory(overrides, toDispose);
editor.onDispose(() => toDispose.dispose());

this.injectKeybindingResolver(editor);

toDispose.push(editor.onFocusChanged(focused => {
if (focused) {
this._current = editor;
}
}));
toDispose.push(Disposable.create(() => {
if (this._current === editor) {
this._current = undefined;
}
}));

if (editor instanceof MonacoEditor) {
editor.onDispose(() => toDispose.dispose());

this.injectKeybindingResolver(editor);

toDispose.push(editor.onFocusChanged(focused => {
if (focused) {
this._current = editor;
}
}));
toDispose.push(Disposable.create(() => {
if (this._current === editor) {
this._current = undefined;
}
}));
}
return editor;
}

Expand Down Expand Up @@ -369,6 +372,11 @@ export class MonacoEditorProvider {
return MonacoDiffNavigatorFactory.nullNavigator;
}

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

/**
* Creates an instance of the standard MonacoEditor with a CodeEditorWidget as its Monaco delegeate.
* In addition to the service customizability of the StandaloneCodeEditor,This editor allows greater customization the editor contributions active in the widget.
* See {@link ICodeEditorWidgetOptions.contributions}.
* @deprecated Most use cases for inline editors should be served by `createSimpleInline` instead.
*/
async createSimpleInline(uri: URI, node: HTMLElement, options?: MonacoEditor.IOptions, widgetOptions?: ICodeEditorWidgetOptions): Promise<SimpleMonacoEditor> {
return this.doCreateEditor(uri, async (override, toDispose) => {
const overrides = override ? Array.from(override) : [];
overrides.push([IContextMenuService, { showContextMenu: () => { /** no op! */ } }]);
const document = await this.getModel(uri, toDispose);
document.suppressOpenEditorWhenDirty = true;
const model = (await document.load()).textEditorModel;
const baseOptions: Partial<MonacoEditor.IOptions> = {
model,
autoSizing: false,
minHeight: 1,
maxHeight: 1
};
const editorOptions = {
...baseOptions,
...MonacoEditorProvider.inlineOptions,
...options
};
return new SimpleMonacoEditor(
uri,
document,
node,
this.services,
editorOptions,
overrides,
{ isSimpleWidget: true, ...widgetOptions }
);
});
}

static inlineOptions: monaco.editor.IEditorConstructionOptions = {
wordWrap: 'on',
overviewRulerLanes: 0,
Expand Down Expand Up @@ -451,7 +494,7 @@ export class MonacoEditorProvider {
override,
parentEditor
)
) as MonacoDiffEditor;
);
}

protected insertFinalNewline(editor: MonacoEditor): monaco.editor.IIdentifiedSingleEditOperation[] {
Expand Down
10 changes: 3 additions & 7 deletions packages/monaco/src/browser/monaco-editor-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,10 @@ export class MonacoEditorService extends StandaloneCodeEditorService {
const openerOptions = this.createEditorOpenerOptions(input, source, sideBySide);
const widget = await open(this.openerService, uri, openerOptions);
const editorWidget = await this.findEditorWidgetByUri(widget, uri.toString());
if (editorWidget && editorWidget.editor instanceof MonacoEditor) {
const candidate = editorWidget.editor.getControl();
// Since we extend a private super class, we have to check that the thing that matches the public interface also matches the private expectations the superclass.
// eslint-disable-next-line no-null/no-null
return candidate instanceof StandaloneCodeEditor ? candidate : null;
}
const candidate = MonacoEditor.get(editorWidget)?.getControl();
// Since we extend a private super class, we have to check that the thing that matches the public interface also matches the private expectations the superclass.
// eslint-disable-next-line no-null/no-null
return null;
return candidate instanceof StandaloneCodeEditor ? candidate : null;
}

protected async findEditorWidgetByUri(widget: object | undefined, uriAsString: string): Promise<EditorWidget | undefined> {
Expand Down
4 changes: 2 additions & 2 deletions packages/monaco/src/browser/monaco-editor-zone-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ export class MonacoEditorZoneWidget implements Disposable {
this.toHide
);

editor: monaco.editor.IStandaloneCodeEditor;
editor: monaco.editor.ICodeEditor;

constructor(
editorInstance: monaco.editor.IStandaloneCodeEditor, readonly showArrow: boolean = true
editorInstance: monaco.editor.ICodeEditor, readonly showArrow: boolean = true
) {
this.editor = editorInstance;
this.zoneNode.classList.add('zone-widget');
Expand Down
Loading
Loading