Skip to content

Commit 075e156

Browse files
authored
feat: support code edits keybinding (#4362)
* feat: support code edits keybinding * fix: when * chore: improve command
1 parent 81c0f99 commit 075e156

5 files changed

Lines changed: 87 additions & 17 deletions

File tree

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
import { Autowired } from '@opensumi/di';
22
import {
3+
AINativeConfigService,
34
ClientAppContribution,
45
Key,
56
KeybindingContribution,
67
KeybindingRegistry,
78
KeybindingScope,
89
} from '@opensumi/ide-core-browser';
9-
import {
10-
AI_MULTI_LINE_COMPLETION_ACCEPT,
11-
AI_MULTI_LINE_COMPLETION_DISCARD,
12-
} from '@opensumi/ide-core-browser/lib/ai-native/command';
10+
import { AI_CODE_EDITS_COMMANDS } from '@opensumi/ide-core-browser/lib/ai-native/command';
1311
import { MultiLineEditsIsVisible } from '@opensumi/ide-core-browser/lib/contextkey/ai-native';
1412
import { CommandContribution, CommandRegistry, Domain } from '@opensumi/ide-core-common';
1513
import { WorkbenchEditorService } from '@opensumi/ide-editor';
1614
import { WorkbenchEditorServiceImpl } from '@opensumi/ide-editor/lib/browser/workbench-editor.service';
15+
import { transaction } from '@opensumi/ide-monaco/lib/common/observable';
1716

1817
import { IntelligentCompletionsController } from './intelligent-completions.controller';
1918

@@ -22,8 +21,11 @@ export class IntelligentCompletionsContribution implements KeybindingContributio
2221
@Autowired(WorkbenchEditorService)
2322
private readonly workbenchEditorService: WorkbenchEditorServiceImpl;
2423

24+
@Autowired(AINativeConfigService)
25+
private readonly aiNativeConfigService: AINativeConfigService;
26+
2527
registerCommands(commands: CommandRegistry): void {
26-
commands.registerCommand(AI_MULTI_LINE_COMPLETION_DISCARD, {
28+
commands.registerCommand(AI_CODE_EDITS_COMMANDS.DISCARD, {
2729
execute: () => {
2830
const editor = this.workbenchEditorService.currentCodeEditor;
2931
if (editor) {
@@ -32,31 +34,50 @@ export class IntelligentCompletionsContribution implements KeybindingContributio
3234
},
3335
});
3436

35-
commands.registerCommand(AI_MULTI_LINE_COMPLETION_ACCEPT, {
37+
commands.registerCommand(AI_CODE_EDITS_COMMANDS.ACCEPT, {
3638
execute: () => {
3739
const editor = this.workbenchEditorService.currentCodeEditor;
3840
if (editor) {
3941
IntelligentCompletionsController.get(editor.monacoEditor)?.accept.get();
4042
}
4143
},
4244
});
45+
46+
commands.registerCommand(AI_CODE_EDITS_COMMANDS.TRIGGER, {
47+
execute: () => {
48+
const editor = this.workbenchEditorService.currentCodeEditor;
49+
if (editor) {
50+
transaction((tx) => {
51+
IntelligentCompletionsController.get(editor.monacoEditor)?.trigger(tx);
52+
});
53+
}
54+
},
55+
});
4356
}
4457

4558
registerKeybindings(keybindings: KeybindingRegistry): void {
59+
const { codeEdits } = this.aiNativeConfigService;
60+
4661
keybindings.registerKeybinding({
47-
command: AI_MULTI_LINE_COMPLETION_DISCARD.id,
62+
command: AI_CODE_EDITS_COMMANDS.DISCARD.id,
4863
keybinding: Key.ESCAPE.code,
4964
when: MultiLineEditsIsVisible.raw,
5065
priority: 100,
5166
});
5267

5368
keybindings.registerKeybinding(
5469
{
55-
command: AI_MULTI_LINE_COMPLETION_ACCEPT.id,
70+
command: AI_CODE_EDITS_COMMANDS.ACCEPT.id,
5671
keybinding: Key.TAB.code,
5772
when: MultiLineEditsIsVisible.raw,
5873
},
5974
KeybindingScope.USER,
6075
);
76+
77+
keybindings.registerKeybinding({
78+
command: AI_CODE_EDITS_COMMANDS.TRIGGER.id,
79+
keybinding: codeEdits.triggerKeybinding,
80+
when: 'editorFocus',
81+
});
6182
}
6283
}

packages/ai-native/src/browser/contrib/intelligent-completions/intelligent-completions.controller.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ import {
1313
import { Emitter, ICodeEditor, ICursorPositionChangedEvent, IRange, ITextModel, Range } from '@opensumi/ide-monaco';
1414
import {
1515
IObservable,
16+
IObservableSignal,
1617
ISettableObservable,
18+
ITransaction,
1719
autorun,
1820
autorunWithStoreHandleChanges,
1921
derived,
22+
observableSignal,
2023
observableValue,
2124
transaction,
2225
} from '@opensumi/ide-monaco/lib/common/observable';
@@ -85,11 +88,13 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
8588
private aiNativeContextKey: AINativeContextKey;
8689
private rewriteWidget: RewriteWidget | null;
8790
private whenMultiLineEditsVisibleDisposable: Disposable;
91+
private codeEditsTriggerSignal: IObservableSignal<void>;
8892

8993
public mount(): IDisposable {
9094
this.handlerAlwaysVisiblePreference();
9195

9296
this.codeEditsResult = observableValue<CodeEditsResultValue | undefined>(this, undefined);
97+
this.codeEditsTriggerSignal = observableSignal(this);
9398

9499
this.whenMultiLineEditsVisibleDisposable = new Disposable();
95100
this.multiLineDecorationModel = new MultiLineDecorationModel(this.monacoEditor);
@@ -393,6 +398,10 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
393398
this.hide();
394399
});
395400

401+
public trigger(tx: ITransaction): void {
402+
this.codeEditsTriggerSignal.trigger(tx);
403+
}
404+
396405
private registerFeature(monacoEditor: ICodeEditor): void {
397406
this.featureDisposable.addDispose(
398407
Event.any<any>(
@@ -432,16 +441,19 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
432441
autorunWithStoreHandleChanges(
433442
{
434443
createEmptyChangeSummary: () => ({}),
435-
handleChange: (context, changeSummary) => {
444+
handleChange: (context) => {
436445
if (context.didChange(this.codeEditsSourceCollection.codeEditsContextBean)) {
437446
// 如果上一次补全结果还在,则不重复请求
438447
const isVisible = this.aiNativeContextKey.multiLineEditsIsVisible.get();
439448
return !isVisible;
449+
} else if (context.didChange(this.codeEditsTriggerSignal)) {
450+
return true;
440451
}
441452
return false;
442453
},
443454
},
444455
async (reader, _, store) => {
456+
this.codeEditsTriggerSignal.read(reader);
445457
const context = this.codeEditsSourceCollection.codeEditsContextBean.read(reader);
446458

447459
const provider = this.intelligentCompletionsRegistry.getCodeEditsProvider();

packages/core-browser/src/ai-native/ai-config.service.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { Autowired, Injectable } from '@opensumi/di';
2-
import { IAINativeCapabilities, IAINativeConfig, IAINativeInlineChatConfig } from '@opensumi/ide-core-common';
2+
import {
3+
IAINativeCapabilities,
4+
IAINativeCodeEditsConfig,
5+
IAINativeConfig,
6+
IAINativeInlineChatConfig,
7+
} from '@opensumi/ide-core-common';
38

49
import { AILogoAvatar } from '../components/ai-native';
510
import { LayoutViewSizeConfig } from '../layout/constants';
@@ -28,6 +33,10 @@ const DEFAULT_INLINE_CHAT_CONFIG: Required<IAINativeInlineChatConfig> = {
2833
logo: AILogoAvatar,
2934
};
3035

36+
const DEFAULT_CODE_EDITS_CONFIG: Required<IAINativeCodeEditsConfig> = {
37+
triggerKeybinding: 'alt+\\',
38+
};
39+
3140
@Injectable()
3241
export class AINativeConfigService implements IAINativeConfig {
3342
@Autowired(AppConfig)
@@ -40,6 +49,7 @@ export class AINativeConfigService implements IAINativeConfig {
4049

4150
private internalCapabilities = DEFAULT_CAPABILITIES;
4251
private internalInlineChat = DEFAULT_INLINE_CHAT_CONFIG;
52+
private internalCodeEdits = DEFAULT_CODE_EDITS_CONFIG;
4353

4454
public get capabilities(): Required<IAINativeCapabilities> {
4555
if (!this.aiModuleLoaded) {
@@ -65,6 +75,16 @@ export class AINativeConfigService implements IAINativeConfig {
6575
return this.internalInlineChat;
6676
}
6777

78+
public get codeEdits(): Required<IAINativeCodeEditsConfig> {
79+
const { AINativeConfig } = this.appConfig;
80+
81+
if (AINativeConfig?.codeEdits) {
82+
return { ...this.internalCodeEdits, ...AINativeConfig.codeEdits };
83+
}
84+
85+
return this.internalCodeEdits;
86+
}
87+
6888
setAINativeModuleLoaded(value: boolean): void {
6989
this.aiModuleLoaded = value;
7090
}

packages/core-browser/src/ai-native/command.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,16 @@ export const AI_CODE_ACTION = {
3030
id: 'ai.code.action',
3131
};
3232

33-
export const AI_MULTI_LINE_COMPLETION_DISCARD = {
34-
id: 'ai.multiLine.completion.discard',
35-
};
36-
37-
export const AI_MULTI_LINE_COMPLETION_ACCEPT = {
38-
id: 'ai.multiLine.completion.accept',
39-
};
33+
export namespace AI_CODE_EDITS_COMMANDS {
34+
export const DISCARD = {
35+
id: 'ai.codeEdits.discard',
36+
};
37+
38+
export const ACCEPT = {
39+
id: 'ai.codeEdits.accept',
40+
};
41+
42+
export const TRIGGER = {
43+
id: 'ai.codeEdits.trigger',
44+
};
45+
}

packages/core-common/src/types/ai-native/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ export interface IAINativeInlineChatConfig {
8181
logo?: string | React.ReactNode | React.ComponentType<any>;
8282
}
8383

84+
export interface IAINativeCodeEditsConfig {
85+
/**
86+
* 触发 code edits 的快捷键
87+
*/
88+
triggerKeybinding?: string;
89+
}
90+
8491
export interface IAINativeConfig {
8592
capabilities?: IAINativeCapabilities;
8693
/**
@@ -91,6 +98,10 @@ export interface IAINativeConfig {
9198
* inline chat 配置
9299
*/
93100
inlineChat?: IAINativeInlineChatConfig;
101+
/**
102+
* code edits 配置
103+
*/
104+
codeEdits?: IAINativeCodeEditsConfig;
94105
}
95106

96107
export enum ECompletionType {

0 commit comments

Comments
 (0)