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

feat: support intelligent completions always visible preference #4043

Merged
merged 3 commits into from
Sep 26, 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
30 changes: 12 additions & 18 deletions packages/ai-native/src/browser/ai-core.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ 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 { InlineCompletionSingleHandler } from './contrib/inline-completions/inline-completions.handler';
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 @@ -203,9 +202,6 @@ export class AINativeBrowserContribution
@Autowired(RenameSingleHandler)
private readonly renameSingleHandler: RenameSingleHandler;

@Autowired(InlineCompletionSingleHandler)
private readonly inlineCompletionSingleHandler: InlineCompletionSingleHandler;

@Autowired(CodeActionSingleHandler)
private readonly codeActionSingleHandler: CodeActionSingleHandler;

Expand Down Expand Up @@ -272,8 +268,7 @@ export class AINativeBrowserContribution

onDidStart() {
runWhenIdle(() => {
const { supportsInlineCompletion, supportsRenameSuggestions, supportsInlineChat } =
this.aiNativeConfigService.capabilities;
const { supportsRenameSuggestions, supportsInlineChat } = this.aiNativeConfigService.capabilities;
const prefChatVisibleType = this.preferenceService.getValid(AINativeSettingSectionsId.ChatVisibleType);

if (prefChatVisibleType === 'always') {
Expand All @@ -286,10 +281,6 @@ export class AINativeBrowserContribution
this.renameSingleHandler.load();
}

if (supportsInlineCompletion) {
this.inlineCompletionSingleHandler.load();
}

if (supportsInlineChat) {
this.codeActionSingleHandler.load();
}
Expand Down Expand Up @@ -338,19 +329,23 @@ export class AINativeBrowserContribution

if (this.aiNativeConfigService.capabilities.supportsInlineCompletion) {
registry.registerSettingSection(AI_NATIVE_SETTING_GROUP_ID, {
title: localize('preference.ai.native.inlineCompletions.title'),
title: localize('preference.ai.native.intelligentCompletions.title'),
preferences: [
{
id: AINativeSettingSectionsId.InlineCompletionsCacheEnabled,
localized: 'preference.ai.native.inlineCompletions.cache.enabled',
id: AINativeSettingSectionsId.IntelligentCompletionsCacheEnabled,
localized: 'preference.ai.native.intelligentCompletions.cache.enabled',
},
{
id: AINativeSettingSectionsId.IntelligentCompletionsDebounceTime,
localized: 'preference.ai.native.intelligentCompletions.debounceTime',
},
{
id: AINativeSettingSectionsId.InlineCompletionsDebounceTime,
localized: 'preference.ai.native.inlineCompletions.debounceTime',
id: AINativeSettingSectionsId.IntelligentCompletionsPromptEngineeringEnabled,
localized: 'preference.ai.native.intelligentCompletions.promptEngineering.enabled',
},
{
id: AINativeSettingSectionsId.InlineCompletionsPromptEngineeringEnabled,
localized: 'preference.ai.native.inlineCompletions.promptEngineering.enabled',
id: AINativeSettingSectionsId.IntelligentCompletionsAlwaysVisible,
localized: 'preference.ai.native.intelligentCompletions.alwaysVisible',
},
],
});
Expand Down Expand Up @@ -383,7 +378,6 @@ export class AINativeBrowserContribution
const { monacoEditor } = editor;

this.codeActionSingleHandler.mountEditor(editor.monacoEditor);
this.inlineCompletionSingleHandler.mountEditor(editor.monacoEditor);

return monacoEditor.onDidScrollChange(() => {
if (this.ctxMenuRenderer.visible) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ export class AIInlineCompletionsProvider extends WithEventBus {
super();

this.inlineComletionsDebounceTime = this.preferenceService.getValid(
AINativeSettingSectionsId.InlineCompletionsDebounceTime,
AINativeSettingSectionsId.IntelligentCompletionsDebounceTime,
150,
);

this.addDispose(
this.preferenceService.onSpecificPreferenceChange(
AINativeSettingSectionsId.InlineCompletionsDebounceTime,
AINativeSettingSectionsId.IntelligentCompletionsDebounceTime,
({ newValue }) => {
this.inlineComletionsDebounceTime = newValue;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,13 @@ export class InlineCompletionRequestTask extends Disposable {
this.isCancelFlag = false;

this.isEnablePromptEngineering = this.preferenceService.getValid(
AINativeSettingSectionsId.InlineCompletionsPromptEngineeringEnabled,
AINativeSettingSectionsId.IntelligentCompletionsPromptEngineeringEnabled,
this.isEnablePromptEngineering,
);

this._disposables.add(
this.preferenceService.onSpecificPreferenceChange(
AINativeSettingSectionsId.InlineCompletionsPromptEngineeringEnabled,
AINativeSettingSectionsId.IntelligentCompletionsPromptEngineeringEnabled,
({ newValue }) => {
this.isEnablePromptEngineering = newValue;
},
Expand Down Expand Up @@ -143,7 +143,6 @@ export class InlineCompletionRequestTask extends Disposable {
return [];
}

this.aiCompletionsService.updateStatusBarItem('running', true);
const requestStartTime = Date.now();

let completeResult: IIntelligentCompletionsResult | undefined;
Expand All @@ -157,6 +156,7 @@ export class InlineCompletionRequestTask extends Disposable {
completeResult = cacheData;
} else {
try {
this.aiCompletionsService.updateStatusBarItem('running', true);
completeResult = await this.aiCompletionsService.complete(requestBean);
} catch (error) {
this.aiCompletionsService.reporterEnd(relationId, {
Expand All @@ -166,6 +166,8 @@ export class InlineCompletionRequestTask extends Disposable {
});
this.aiCompletionsService.hideStatusBarItem();
return [];
} finally {
this.aiCompletionsService.hideStatusBarItem();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ export class PromptCache implements IDisposable {
protected _isCacheEnabled = false;
constructor() {
this._isCacheEnabled = this.preferenceService.getValid(
AINativeSettingSectionsId.InlineCompletionsCacheEnabled,
AINativeSettingSectionsId.IntelligentCompletionsCacheEnabled,
true,
);

this._disposables.add(
this.preferenceService.onSpecificPreferenceChange(
AINativeSettingSectionsId.InlineCompletionsCacheEnabled,
AINativeSettingSectionsId.IntelligentCompletionsCacheEnabled,
(e) => {
this._isCacheEnabled = e.newValue;
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { PreferenceService } from '@opensumi/ide-core-browser';
import { MultiLineEditsIsVisible } from '@opensumi/ide-core-browser/lib/contextkey/ai-native';
import {
AINativeSettingSectionsId,
Disposable,
Event,
IAICompletionOption,
IDisposable,
IntelligentCompletionsRegistryToken,
isDefined,
runWhenIdle,
} from '@opensumi/ide-core-common';
import { ICodeEditor, ICursorPositionChangedEvent, IRange, ITextModel, Range } from '@opensumi/ide-monaco';
import { empty } from '@opensumi/ide-utils/lib/strings';
import { IObservable, autorun } from '@opensumi/monaco-editor-core/esm/vs/base/common/observable';
import { GhostTextWidget } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/inlineCompletions/browser/ghostTextWidget';
import { autorun, transaction } from '@opensumi/monaco-editor-core/esm/vs/base/common/observable';
import { ObservableValue } from '@opensumi/monaco-editor-core/esm/vs/base/common/observableInternal/base';
import { InlineCompletionsController } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController';
import {
SuggestItemInfo,
SuggestWidgetAdaptor,
} from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider';

import { AINativeContextKey } from '../../contextkey/ai-native.contextkey.service';
import { REWRITE_DECORATION_INLINE_ADD, RewriteWidget } from '../../widget/rewrite/rewrite-widget';
Expand All @@ -27,9 +32,9 @@ import {
} from './diff-computer';
import { IIntelligentCompletionsResult } from './intelligent-completions';
import { IntelligentCompletionsRegistry } from './intelligent-completions.feature.registry';
import { InlineCompletionsSource } from './intelligent-completions.source';
import { MultiLineDecorationModel } from './multi-line.decoration';


export class IntelligentCompletionsController extends BaseAIMonacoEditorController {
public static readonly ID = 'editor.contrib.ai.intelligent.completions';

Expand All @@ -45,19 +50,88 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
return this.monacoEditor.getModel()!;
}

private get preferenceService(): PreferenceService {
return this.injector.get(PreferenceService);
}

private multiLineDecorationModel: MultiLineDecorationModel;
private additionsDeletionsDecorationModel: AdditionsDeletionsDecorationModel;
private aiNativeContextKey: AINativeContextKey;
private rewriteWidget: RewriteWidget | null;
private whenMultiLineEditsVisibleDisposable: Disposable;
private inlineCompletionsSource: InlineCompletionsSource;

public mount(): IDisposable {
this.whenMultiLineEditsVisibleDisposable = new Disposable();
this.multiLineDecorationModel = new MultiLineDecorationModel(this.monacoEditor);
this.additionsDeletionsDecorationModel = new AdditionsDeletionsDecorationModel(this.monacoEditor);
this.aiNativeContextKey = this.injector.get(AINativeContextKey, [this.monacoEditor.contextKeyService]);
this.inlineCompletionsSource = this.injector.get(InlineCompletionsSource, [this.monacoEditor]);

return this.registerFeature(this.monacoEditor);
this.registerFeature(this.monacoEditor);
this.handlerAlwaysVisiblePreference();
return this;
}

private handlerAlwaysVisiblePreference(): void {
let observableDisposable = new Disposable();

const register = () => {
const inlineCompletionsController = InlineCompletionsController.get(this.monacoEditor);
if (inlineCompletionsController) {
observableDisposable.addDispose(
autorun((reader) => {
/**
* https://github.com/microsoft/vscode/blob/1.88.1/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts#L23
* SuggestWidgetAdaptor 是 inline completions 模块专门用来处理 suggest widget 的适配器
* 主要控制当下拉补全出现时阴影字符串的显示与隐藏
* 当 selectedItem 有值的时候(也就是选中下拉补全列表项时),会把原来的 inline completions 本身的阴影字符给屏蔽掉
* 所以可以利用这点,把 selectedItem 重新置为空即可
*/
const suggestWidgetAdaptor = inlineCompletionsController['_suggestWidgetAdaptor'] as SuggestWidgetAdaptor;
const selectedItemObservable = suggestWidgetAdaptor.selectedItem as ObservableValue<
SuggestItemInfo | undefined
>;
const selectedItem = selectedItemObservable.read(reader);

if (selectedItem) {
transaction((tx) => {
selectedItemObservable.set(undefined, tx);
});
}
}),
);
}
};

const unregister = () => {
if (observableDisposable) {
observableDisposable.dispose();
observableDisposable = new Disposable();
}
};

const isAlwaysVisible = this.preferenceService.getValid(
AINativeSettingSectionsId.IntelligentCompletionsAlwaysVisible,
true,
);

if (isAlwaysVisible) {
register();
}

this.addDispose(
this.preferenceService.onSpecificPreferenceChange(
AINativeSettingSectionsId.IntelligentCompletionsAlwaysVisible,
({ newValue }) => {
if (newValue) {
register();
} else {
unregister();
}
},
),
);
}

private destroyRewriteWidget() {
Expand Down Expand Up @@ -94,7 +168,7 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
}

private applyInlineDecorations(completionModel: IIntelligentCompletionsResult) {
const { items, alwaysVisible } = completionModel;
const { items } = completionModel;

const position = this.monacoEditor.getPosition()!;
const model = this.monacoEditor.getModel();
Expand Down Expand Up @@ -173,27 +247,6 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
}
}),
);

if (isDefined(alwaysVisible) && alwaysVisible) {
const inlineCompletionsController = InlineCompletionsController.get(this.monacoEditor);
if (inlineCompletionsController) {
/**
* https://github.com/microsoft/vscode/blob/1.88.1/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts#L67
* 当 _ghostTextWidgets 发生变化时,说明是由下拉补全引起的 “内联补全” 字符出现,此时需要将其销毁
*/
this.whenMultiLineEditsVisibleDisposable.addDispose(
autorun((reader) => {
const ghostTextWidgetsObservable = inlineCompletionsController['_ghostTextWidgets'] as IObservable<
readonly GhostTextWidget[]
>;
ghostTextWidgetsObservable.read(reader);

const ghostTextWidget = ghostTextWidgetsObservable.get()[0];
ghostTextWidget?.dispose();
}),
);
}
}
}

private async renderRewriteWidget(
Expand Down Expand Up @@ -274,7 +327,7 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
this.hide();
}

public registerFeature(monacoEditor: ICodeEditor): IDisposable {
private registerFeature(monacoEditor: ICodeEditor): void {
this.addDispose(
Event.any<any>(
monacoEditor.onDidChangeCursorPosition,
Expand Down Expand Up @@ -309,6 +362,6 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
}),
);

return this;
this.addDispose(this.inlineCompletionsSource.fetch());
}
}
Loading
Loading