Skip to content

Commit

Permalink
refactor: intelligent completion & implement code edits api (#4105)
Browse files Browse the repository at this point in the history
* refactor: inline-completion

* feat: implement lint error code edits api

* chore: improve code

* chore: improve code

* chore: improve code
  • Loading branch information
Ricbet authored Oct 18, 2024
1 parent f112878 commit 06b3924
Show file tree
Hide file tree
Showing 16 changed files with 323 additions and 186 deletions.
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);
}

@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);
}

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

0 comments on commit 06b3924

Please sign in to comment.