Skip to content

Commit

Permalink
feat: support ai inline completions (#3400)
Browse files Browse the repository at this point in the history
* feat: support ai inline completions

---------

Co-authored-by: John <qingyi.xjh@antgroup.com>
  • Loading branch information
bytemain and Ricbet authored Mar 14, 2024
1 parent 56d97f5 commit 0cc59cd
Show file tree
Hide file tree
Showing 16 changed files with 1,131 additions and 10 deletions.
37 changes: 35 additions & 2 deletions packages/ai-native/src/browser/ai-core.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ import {
SlotRendererRegistry,
getIcon,
} from '@opensumi/ide-core-browser';
import { AI_INLINE_CHAT_VISIBLE } from '@opensumi/ide-core-browser/lib/ai-native/command';
import {
AI_INLINE_CHAT_VISIBLE,
AI_INLINE_COMPLETION_REPORTER,
AI_INLINE_COMPLETION_VISIBLE,
} from '@opensumi/ide-core-browser/lib/ai-native/command';
import { InlineChatIsVisible } from '@opensumi/ide-core-browser/lib/contextkey/ai-native';
import { localize } from '@opensumi/ide-core-common';
import { CommandService, localize } from '@opensumi/ide-core-common';
import { AI_NATIVE_SETTING_GROUP_ID } from '@opensumi/ide-core-common/src/settings/ai-native';
import { IEditor } from '@opensumi/ide-editor';
import { BrowserEditorContribution, IEditorFeatureRegistry } from '@opensumi/ide-editor/lib/browser';
Expand All @@ -30,6 +34,8 @@ import { AI_CHAT_CONTAINER_VIEW_ID } from '../common';
import { AIEditorContribution } from './ai-editor.contribution';
import { AINativeService } from './ai-native.service';
import { AIChatView } from './chat/chat.view';
import { AIInlineCompletionsProvider } from './inline-completions/completeProvider';
import { AICompletionsService } from './inline-completions/service/ai-completions.service';
import { AIChatLayoutConfig } from './layout/layout-config';
import { AIChatTabRenderer } from './layout/tabbar.view';
import { AINativeCoreContribution, IChatFeatureRegistry, IInlineChatFeatureRegistry } from './types';
Expand Down Expand Up @@ -74,6 +80,15 @@ export class AINativeBrowserContribution
@Autowired(AINativeConfigService)
private readonly aiNativeConfigService: AINativeConfigService;

@Autowired(AICompletionsService)
private aiCompletionsService: AICompletionsService;

@Autowired(AIInlineCompletionsProvider)
private readonly aiInlineCompletionsProvider: AIInlineCompletionsProvider;

@Autowired(CommandService)
private readonly commandService: CommandService;

constructor() {
this.registerFeature();
}
Expand Down Expand Up @@ -141,6 +156,24 @@ export class AINativeBrowserContribution
this.aiNativeService.launchInlineChatVisible(value);
},
});

commands.registerCommand(AI_INLINE_COMPLETION_REPORTER, {
execute: (relationId: string, sessionId: string, accept: boolean) => {
this.aiCompletionsService.report({ sessionId, accept, relationId });
},
});

commands.registerCommand(AI_INLINE_COMPLETION_VISIBLE, {
execute: async (visible: boolean) => {
if (!visible) {
await this.commandService.executeCommand('editor.action.inlineSuggest.hide');
this.aiCompletionsService.hideStatusBarItem();
this.aiInlineCompletionsProvider.resetContextKey();
this.aiInlineCompletionsProvider.cancelRequest();
this.aiCompletionsService.setVisibleCompletion(false);
}
},
});
}

registerRenderer(registry: SlotRendererRegistry): void {
Expand Down
118 changes: 117 additions & 1 deletion packages/ai-native/src/browser/ai-editor.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import { IBrowserCtxMenu } from '@opensumi/ide-core-browser/lib/menu/next/render
import {
AINativeSettingSectionsId,
CancellationToken,
ContributionProvider,
Disposable,
Event,
IDisposable,
ILogServiceClient,
ILoggerManagerClient,
MaybePromise,
Schemes,
SupportLogNamespace,
runWhenIdle,
} from '@opensumi/ide-core-common';
import { DesignBrowserCtxMenuService } from '@opensumi/ide-design/lib/browser/override/menu.service';
Expand All @@ -20,7 +23,16 @@ import * as monaco from '@opensumi/monaco-editor-core/esm/vs/editor/editor.api';
import { AIInlineChatContentWidget } from '../common';

import { AINativeService } from './ai-native.service';
import { CancelResponse, ErrorResponse, IInlineChatFeatureRegistry, ReplyResponse } from './types';
import { AIInlineCompletionsProvider } from './inline-completions/completeProvider';
import { AICompletionsService } from './inline-completions/service/ai-completions.service';
import {
AINativeCoreContribution,
CancelResponse,
ErrorResponse,
IAIMiddleware,
IInlineChatFeatureRegistry,
ReplyResponse,
} from './types';
import { InlineChatFeatureRegistry } from './widget/inline-chat/inline-chat.feature.registry';
import { AIInlineChatService, EInlineChatStatus } from './widget/inline-chat/inline-chat.service';
import { AIInlineContentWidget } from './widget/inline-chat/inline-content-widget';
Expand Down Expand Up @@ -52,6 +64,25 @@ export class AIEditorContribution extends Disposable implements IEditorFeatureCo
@Autowired(IInlineChatFeatureRegistry)
private readonly inlineChatFeatureRegistry: InlineChatFeatureRegistry;

@Autowired(AINativeCoreContribution)
private readonly contributions: ContributionProvider<AINativeCoreContribution>;

@Autowired(AIInlineCompletionsProvider)
private readonly aiInlineCompletionsProvider: AIInlineCompletionsProvider;

@Autowired(AICompletionsService)
private aiCompletionsService: AICompletionsService;

private latestMiddlewareCollector: IAIMiddleware;

private logger: ILogServiceClient;

constructor() {
super();

this.logger = this.loggerManagerClient.getLogger(SupportLogNamespace.Browser);
}

private aiDiffWidget: AIDiffWidget;
private aiInlineContentWidget: AIInlineContentWidget;
private aiInlineChatDisposed: Disposable = new Disposable();
Expand Down Expand Up @@ -80,6 +111,15 @@ export class AIEditorContribution extends Disposable implements IEditorFeatureCo
return this;
}

if (this.aiNativeConfigService.capabilities.supportsInlineCompletion) {
this.contributions.getContributions().forEach((contribution) => {
if (contribution.middleware) {
this.latestMiddlewareCollector = contribution.middleware;
}
});
this.registerCompletion(editor);
}

this.disposables.push(
monacoEditor.onDidChangeModel(() => {
this.disposeAllWidget();
Expand Down Expand Up @@ -316,4 +356,80 @@ export class AIEditorContribution extends Disposable implements IEditorFeatureCo
});
this.aiInlineContentWidget?.layoutContentWidget();
}

/**
* 代码补全
*/
private async registerCompletion(editor: IEditor): Promise<void> {
const { monacoEditor, currentUri } = editor;

if (currentUri && currentUri.codeUri.scheme !== Schemes.file) {
return;
}

let dispose: IDisposable | undefined;

this.disposables.push(
Event.debounce(
monacoEditor.onDidChangeModel,
(_, e) => e,
300,
)(async (event) => {
if (dispose) {
dispose.dispose();
this.aiInlineCompletionsProvider.dispose();
}

const { monacoEditor, currentUri } = editor;
if (currentUri && currentUri.codeUri.scheme !== Schemes.file) {
return this;
}

const model = monacoEditor.getModel();
if (!model) {
return;
}

// 取光标的当前位置
const position = monacoEditor.getPosition();
if (!position) {
return;
}

this.aiInlineCompletionsProvider.registerEditor(editor);

dispose = monaco.languages.registerInlineCompletionsProvider(model.getLanguageId(), {
provideInlineCompletions: async (model, position, context, token) => {
if (this.latestMiddlewareCollector?.language?.provideInlineCompletions) {
this.aiCompletionsService.setMiddlewareComplete(
this.latestMiddlewareCollector?.language?.provideInlineCompletions,
);
}

const list = await this.aiInlineCompletionsProvider.provideInlineCompletionItems(
model,
position,
context,
token,
);

this.logger.log(
'provideInlineCompletions: ',
list.items.map((data) => data.insertText),
);

return list;
},
freeInlineCompletions(completions: monaco.languages.InlineCompletions<monaco.languages.InlineCompletion>) {},
handleItemDidShow: (completions, item) => {
if (completions.items.length > 0) {
this.aiCompletionsService.setVisibleCompletion(true);
}
},
});
this.disposables.push(dispose);
}),
);
return;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Autowired, Injectable, Optional } from '@opensumi/di';
import { IContextKey, IContextKeyService, IScopedContextKeyService } from '@opensumi/ide-core-browser';
import { InlineChatIsVisible } from '@opensumi/ide-core-browser/lib/contextkey/ai-native';
import { InlineChatIsVisible, InlineCompletionIsTrigger } from '@opensumi/ide-core-browser/lib/contextkey/ai-native';
import { ContextKeyService } from '@opensumi/monaco-editor-core/esm/vs/platform/contextkey/browser/contextKeyService';
import { IContextKeyServiceTarget } from '@opensumi/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey';

Expand All @@ -12,9 +12,11 @@ export class AINativeContextKey {
private _contextKeyService: IScopedContextKeyService | undefined;

public readonly inlineChatIsVisible: IContextKey<boolean>;
public readonly inlineCompletionIsTrigger: IContextKey<boolean>;

constructor(@Optional() dom?: HTMLElement | IContextKeyServiceTarget | ContextKeyService) {
this._contextKeyService = this.globalContextKeyService.createScoped(dom);
this.inlineChatIsVisible = InlineChatIsVisible.bind(this._contextKeyService);
this.inlineCompletionIsTrigger = InlineCompletionIsTrigger.bind(this._contextKeyService);
}
}
Loading

0 comments on commit 0cc59cd

Please sign in to comment.