Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: intelligent completion & implement code edits api #4105

Merged
merged 5 commits into from
Oct 18, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import {
GHOST_TEXT,
GHOST_TEXT_DESCRIPTION,
MultiLineDecorationModel,
} from '@opensumi/ide-ai-native/lib/browser/contrib/intelligent-completions/multi-line.decoration';
} from '@opensumi/ide-ai-native/lib/browser/contrib/intelligent-completions/decoration/multi-line.decoration';
import { IMultiLineDiffChangeResult } from '@opensumi/ide-ai-native/lib/browser/contrib/intelligent-completions/diff-computer';
import { EnhanceDecorationsCollection } from '@opensumi/ide-ai-native/lib/browser/model/enhanceDecorationsCollection';
import { ICodeEditor, IPosition } from '@opensumi/ide-monaco';
import { monacoApi } from '@opensumi/ide-monaco/lib/browser/monaco-api';

import { IMultiLineDiffChangeResult } from '../../../../src/browser/contrib/intelligent-completions/diff-computer';
import { EnhanceDecorationsCollection } from '../../../../src/browser/model/enhanceDecorationsCollection';

describe('MultiLineDecorationModel', () => {
let editor: ICodeEditor;
let decorationsCollection: EnhanceDecorationsCollection;
Expand Down
6 changes: 6 additions & 0 deletions packages/ai-native/src/browser/ai-core.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ import { ChatProxyService } from './chat/chat-proxy.service';
import { AIChatView } from './chat/chat.view';
import { CodeActionSingleHandler } from './contrib/code-action/code-action.handler';
import { AIInlineCompletionsProvider } from './contrib/inline-completions/completeProvider';
import { InlineCompletionsController } from './contrib/inline-completions/inline-completions.controller';
import { AICompletionsService } from './contrib/inline-completions/service/ai-completions.service';
import { IntelligentCompletionsController } from './contrib/intelligent-completions/intelligent-completions.controller';
import { ProblemFixController } from './contrib/problem-fix/problem-fix.controller';
Expand Down Expand Up @@ -255,6 +256,11 @@ export class AINativeBrowserContribution
new SyncDescriptor(IntelligentCompletionsController, [this.injector]),
EditorContributionInstantiation.AfterFirstRender,
);
register(
InlineCompletionsController.ID,
new SyncDescriptor(InlineCompletionsController, [this.injector]),
EditorContributionInstantiation.AfterFirstRender,
);
}
if (supportsProblemFix) {
register(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import debounce from 'lodash/debounce';

import { Autowired, INJECTOR_TOKEN, Injectable, Injector, Optional } from '@opensumi/di';
import { AI_INLINE_COMPLETION_VISIBLE } from '@opensumi/ide-core-browser/lib/ai-native/command';
import {
CommandService,
CommandServiceImpl,
Disposable,
IAICompletionOption,
IDisposable,
IEventBus,
IntelligentCompletionsRegistryToken,
Sequencer,
runWhenIdle,
} from '@opensumi/ide-core-common';
Expand All @@ -18,28 +18,40 @@ import { empty } from '@opensumi/ide-utils/lib/strings';
import { InlineCompletionContextKeys } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys';

import { IAIInlineCompletionsProvider } from '../../../common';
import { AINativeContextKey } from '../../contextkey/ai-native.contextkey.service';
import { BaseAIMonacoEditorController } from '../base';
import { IIntelligentCompletionsResult } from '../intelligent-completions';
import { IntelligentCompletionsRegistry } from '../intelligent-completions/intelligent-completions.feature.registry';

import { IIntelligentCompletionsResult } from './intelligent-completions';
export class InlineCompletionsController extends BaseAIMonacoEditorController {
public static readonly ID = 'editor.contrib.ai.inline.completions';

@Injectable({ multiple: true })
export class InlineCompletionsSource extends Disposable {
@Autowired(INJECTOR_TOKEN)
protected readonly injector: Injector;
public static get(editor: ICodeEditor): InlineCompletionsController | null {
return editor.getContribution<InlineCompletionsController>(InlineCompletionsController.ID);
}

private get eventBus(): IEventBus {
return this.injector.get(IEventBus);
}

@Autowired(IEventBus)
private eventBus: IEventBus;
private get commandService(): CommandServiceImpl {
return this.injector.get(CommandService);
}
Ricbet marked this conversation as resolved.
Show resolved Hide resolved

@Autowired(CommandService)
private commandService: CommandServiceImpl;
private get aiInlineCompletionsProvider(): IAIInlineCompletionsProvider {
return this.injector.get(IAIInlineCompletionsProvider);
}

@Autowired(IAIInlineCompletionsProvider)
private readonly aiInlineCompletionsProvider: IAIInlineCompletionsProvider;
private get intelligentCompletionsRegistry(): IntelligentCompletionsRegistry {
return this.injector.get(IntelligentCompletionsRegistryToken);
}
Ricbet marked this conversation as resolved.
Show resolved Hide resolved

private aiNativeContextKey: AINativeContextKey;
private sequencer = new Sequencer();
private preDidShowItems: InlineCompletions | undefined;

constructor(@Optional() private readonly monacoEditor: ICodeEditor) {
super();
public mount(): IDisposable {
this.aiNativeContextKey = this.injector.get(AINativeContextKey, [this.monacoEditor.contextKeyService]);

// 判断用户是否选择了一块区域或者移动光标 取消掉请补全请求
const selectionChange = () => {
Expand All @@ -62,7 +74,7 @@ export class InlineCompletionsSource extends Disposable {
});

const inlineVisibleKey = new Set([InlineCompletionContextKeys.inlineSuggestionVisible.key]);
this.addDispose(
this.featureDisposable.addDispose(
this.monacoEditor.contextKeyService.onDidChangeContext((e) => {
// inline completion 真正消失时
if (e.affectsSome(inlineVisibleKey)) {
Expand All @@ -79,7 +91,7 @@ export class InlineCompletionsSource extends Disposable {
}),
);

this.addDispose(
this.featureDisposable.addDispose(
this.eventBus.on(EditorSelectionChangeEvent, (e) => {
if (e.payload.source === 'mouse') {
debouncedSelectionChange();
Expand All @@ -90,7 +102,7 @@ export class InlineCompletionsSource extends Disposable {
}),
);

this.addDispose(
this.featureDisposable.addDispose(
this.monacoEditor.onDidChangeModelContent((e) => {
const changes = e.changes;
for (const change of changes) {
Expand All @@ -104,17 +116,15 @@ export class InlineCompletionsSource extends Disposable {
}),
);

this.addDispose(
this.featureDisposable.addDispose(
this.monacoEditor.onDidBlurEditorText(() => {
this.commandService.executeCommand(AI_INLINE_COMPLETION_VISIBLE.id, false);
}),
);
}

public fetch(): IDisposable {
let prePosition: Position | undefined;

this.addDispose(
this.featureDisposable.addDispose(
monacoApi.languages.registerInlineCompletionsProvider('*', {
groupId: 'ai-native-intelligent-completions',
provideInlineCompletions: async (model, position, context, token) => {
Expand Down Expand Up @@ -150,6 +160,24 @@ export class InlineCompletionsSource extends Disposable {
}),
);

return this;
return this.featureDisposable;
}

public async fetchProvider(bean: IAICompletionOption): Promise<IIntelligentCompletionsResult | undefined> {
const provider = this.intelligentCompletionsRegistry.getInlineCompletionsProvider();
if (!provider) {
return;
}

// 如果上一次补全结果还在,则不重复请求
const isVisible = this.aiNativeContextKey.multiLineEditsIsVisible.get();
if (isVisible) {
return;
}

const position = this.monacoEditor.getPosition()!;
const inlineCompletionModel = await provider(this.monacoEditor, position, bean, this.token);

return inlineCompletionModel;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as monaco from '@opensumi/ide-monaco';

import { IIntelligentCompletionsResult } from '../../intelligent-completions/intelligent-completions';
import { IIntelligentCompletionsResult } from '../../intelligent-completions';

/**
* 缓存的结果
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import { WorkbenchEditorService } from '@opensumi/ide-editor';
import { WorkbenchEditorServiceImpl } from '@opensumi/ide-editor/lib/browser/workbench-editor.service';
import * as monaco from '@opensumi/ide-monaco';

import { IIntelligentCompletionsResult } from '../../intelligent-completions/intelligent-completions';
import { IntelligentCompletionsController } from '../../intelligent-completions/intelligent-completions.controller';
import { IIntelligentCompletionsResult } from '../../intelligent-completions';
import { IntelligentCompletionsRegistry } from '../../intelligent-completions/intelligent-completions.feature.registry';
import { InlineCompletionsController } from '../inline-completions.controller';
import { InlineCompletionItem } from '../model/competionModel';
import { PromptCache } from '../promptCache';
import { lineBasedPromptProcessor } from '../provider';
Expand Down Expand Up @@ -174,14 +174,14 @@ export class InlineCompletionRequestTask extends Disposable {
} else {
try {
this.aiCompletionsService.updateStatusBarItem('running', true);
const provider = this.intelligentCompletionsRegistry.getProvider();
const provider = this.intelligentCompletionsRegistry.getInlineCompletionsProvider();
if (provider) {
const editor = this.workbenchEditorService.currentCodeEditor;
if (!editor) {
return [];
}
const intelligentCompletionsHandler = IntelligentCompletionsController.get(editor.monacoEditor);
completeResult = await intelligentCompletionsHandler?.fetchProvider(requestBean);
const inlineCompletionsHandler = InlineCompletionsController.get(editor.monacoEditor);
completeResult = await inlineCompletionsHandler?.fetchProvider(requestBean);
} else {
completeResult = await this.aiCompletionsService.complete(requestBean);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from '@opensumi/ide-core-browser';
import { IHashCalculateService } from '@opensumi/ide-core-common/lib/hash-calculate/hash-calculate';

import { IIntelligentCompletionsResult } from '../intelligent-completions/intelligent-completions';
import { IIntelligentCompletionsResult } from '../intelligent-completions';

/**
* 缓存服务
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from '@opensumi/ide-core-common';
import { CompletionRT, IAIReporter } from '@opensumi/ide-core-common/lib/types/ai-native/reporter';

import { IIntelligentCompletionsResult } from '../../intelligent-completions/intelligent-completions';
import { IIntelligentCompletionsResult } from '../../intelligent-completions';

@Injectable()
export class AICompletionsService extends Disposable {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { ICodeEditor, IModelDeltaDecoration, IRange, TrackedRangeStickiness } from '@opensumi/ide-monaco';

import { EnhanceDecorationsCollection } from '../../model/enhanceDecorationsCollection';
import { REWRITE_DECORATION_INLINE_ADD } from '../../widget/rewrite/rewrite-widget';

import { IMultiLineDiffChangeResult } from './diff-computer';
import styles from './intelligent-completions.module.less';
import { EnhanceDecorationsCollection } from '../../../model/enhanceDecorationsCollection';
import { REWRITE_DECORATION_INLINE_ADD } from '../../../widget/rewrite/rewrite-widget';
import { IMultiLineDiffChangeResult } from '../diff-computer';
import styles from '../intelligent-completions.module.less';

export class AdditionsDeletionsDecorationModel {
private deletionsDecorations: EnhanceDecorationsCollection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import { empty } from '@opensumi/ide-utils/lib/strings';
import { EditOperation } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/editOperation';
import { LineDecoration } from '@opensumi/monaco-editor-core/esm/vs/editor/common/viewLayout/lineDecorations';

import { EnhanceDecorationsCollection } from '../../model/enhanceDecorationsCollection';

import { IMultiLineDiffChangeResult } from './diff-computer';
import { EnhanceDecorationsCollection } from '../../../model/enhanceDecorationsCollection';
import { IMultiLineDiffChangeResult } from '../diff-computer';

export interface IModificationsInline {
newValue: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { IRange, InlineCompletion } from '@opensumi/ide-monaco';

import type { ILinterErrorData } from './lint-error.source';

export interface IIntelligentCompletionsResult<T = any> {
readonly items: InlineCompletion[];
/**
* 定义的额外信息
*/
extra?: T;
}

export enum ECodeEditsSource {
LinterErrors = 'lint_errors',
}

export interface ICodeEditsContextBean {
typing: ECodeEditsSource.LinterErrors;
data: ILinterErrorData;
}

export interface ICodeEdit {
/**
* 插入的文本
*/
readonly insertText: string;
/**
* 替换的文本范围
*/
readonly range: IRange;
}
export interface ICodeEditsResult {
readonly items: ICodeEdit[];
}
Loading
Loading