Skip to content

Commit

Permalink
feat: support intelligent completions always visible preference
Browse files Browse the repository at this point in the history
  • Loading branch information
Ricbet committed Sep 25, 2024
1 parent b4dd6d7 commit 2ffea83
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 152 deletions.
31 changes: 12 additions & 19 deletions packages/ai-native/src/browser/ai-core.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,10 @@ import {
ChatProxyServiceToken,
} from '../common';


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 @@ -204,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 @@ -273,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 @@ -287,10 +281,6 @@ export class AINativeBrowserContribution
this.renameSingleHandler.load();
}

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

if (supportsInlineChat) {
this.codeActionSingleHandler.load();
}
Expand Down Expand Up @@ -339,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.always.visible',
},
],
});
Expand Down Expand Up @@ -384,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,7 +156,9 @@ export class InlineCompletionRequestTask extends Disposable {
completeResult = cacheData;
} else {
try {
this.aiCompletionsService.updateStatusBarItem('running', true);
completeResult = await this.aiCompletionsService.complete(requestBean);
this.aiCompletionsService.hideStatusBarItem();
} catch (error) {
this.aiCompletionsService.reporterEnd(relationId, {
success: false,
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

0 comments on commit 2ffea83

Please sign in to comment.