@opensumi/ide-ai-native 3.3.2 → 3.3.4-next-1725949351.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/lib/browser/ai-core.contribution.d.ts +2 -0
  2. package/lib/browser/ai-core.contribution.d.ts.map +1 -1
  3. package/lib/browser/ai-core.contribution.js +14 -1
  4. package/lib/browser/ai-core.contribution.js.map +1 -1
  5. package/lib/browser/ai-editor.contribution.d.ts +1 -0
  6. package/lib/browser/ai-editor.contribution.d.ts.map +1 -1
  7. package/lib/browser/ai-editor.contribution.js +8 -0
  8. package/lib/browser/ai-editor.contribution.js.map +1 -1
  9. package/lib/browser/contrib/problem-fix/problem-fix.component.d.ts +12 -0
  10. package/lib/browser/contrib/problem-fix/problem-fix.component.d.ts.map +1 -0
  11. package/lib/browser/contrib/problem-fix/problem-fix.component.js +32 -0
  12. package/lib/browser/contrib/problem-fix/problem-fix.component.js.map +1 -0
  13. package/lib/browser/contrib/problem-fix/problem-fix.feature.registry.d.ts +7 -0
  14. package/lib/browser/contrib/problem-fix/problem-fix.feature.registry.d.ts.map +1 -0
  15. package/lib/browser/contrib/problem-fix/problem-fix.feature.registry.js +18 -0
  16. package/lib/browser/contrib/problem-fix/problem-fix.feature.registry.js.map +1 -0
  17. package/lib/browser/contrib/problem-fix/problem-fix.handler.d.ts +17 -0
  18. package/lib/browser/contrib/problem-fix/problem-fix.handler.d.ts.map +1 -0
  19. package/lib/browser/contrib/problem-fix/problem-fix.handler.js +122 -0
  20. package/lib/browser/contrib/problem-fix/problem-fix.handler.js.map +1 -0
  21. package/lib/browser/contrib/problem-fix/problem-fix.module.less +5 -0
  22. package/lib/browser/contrib/problem-fix/problem-fix.service.d.ts +8 -0
  23. package/lib/browser/contrib/problem-fix/problem-fix.service.d.ts.map +1 -0
  24. package/lib/browser/contrib/problem-fix/problem-fix.service.js +20 -0
  25. package/lib/browser/contrib/problem-fix/problem-fix.service.js.map +1 -0
  26. package/lib/browser/index.d.ts.map +1 -1
  27. package/lib/browser/index.js +5 -0
  28. package/lib/browser/index.js.map +1 -1
  29. package/lib/browser/types.d.ts +16 -1
  30. package/lib/browser/types.d.ts.map +1 -1
  31. package/lib/browser/types.js.map +1 -1
  32. package/lib/browser/widget/inline-chat/inline-chat.handler.d.ts +17 -7
  33. package/lib/browser/widget/inline-chat/inline-chat.handler.d.ts.map +1 -1
  34. package/lib/browser/widget/inline-chat/inline-chat.handler.js +35 -23
  35. package/lib/browser/widget/inline-chat/inline-chat.handler.js.map +1 -1
  36. package/package.json +21 -21
  37. package/src/browser/ai-core.contribution.ts +13 -0
  38. package/src/browser/ai-editor.contribution.ts +7 -0
  39. package/src/browser/contrib/problem-fix/problem-fix.component.tsx +46 -0
  40. package/src/browser/contrib/problem-fix/problem-fix.feature.registry.ts +16 -0
  41. package/src/browser/contrib/problem-fix/problem-fix.handler.ts +160 -0
  42. package/src/browser/contrib/problem-fix/problem-fix.module.less +5 -0
  43. package/src/browser/contrib/problem-fix/problem-fix.service.ts +13 -0
  44. package/src/browser/index.ts +10 -1
  45. package/src/browser/types.ts +22 -0
  46. package/src/browser/widget/inline-chat/inline-chat.handler.ts +41 -26
@@ -0,0 +1,160 @@
1
+ import { Autowired, INJECTOR_TOKEN, Injectable, Injector } from '@opensumi/di';
2
+ import { AppConfig } from '@opensumi/ide-core-browser';
3
+ import { InlineChatIsVisible } from '@opensumi/ide-core-browser/lib/contextkey/ai-native';
4
+ import {
5
+ AISerivceType,
6
+ ActionSourceEnum,
7
+ ActionTypeEnum,
8
+ Disposable,
9
+ IAIReporter,
10
+ ProblemFixRegistryToken,
11
+ } from '@opensumi/ide-core-common';
12
+ import { IEditor } from '@opensumi/ide-editor';
13
+ import { Range } from '@opensumi/ide-monaco';
14
+ import { HoverController } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/hover/browser/hover';
15
+ import {
16
+ HoverParticipantRegistry,
17
+ IEditorHoverRenderContext,
18
+ } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/hover/browser/hoverTypes';
19
+ import {
20
+ MarkerHover,
21
+ MarkerHoverParticipant,
22
+ } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/hover/browser/markerHoverParticipant';
23
+
24
+
25
+ import { AINativeContextKey } from '../../contextkey/ai-native.contextkey.service';
26
+ import { InlineChatHandler } from '../../widget/inline-chat/inline-chat.handler';
27
+ import { IAIMonacoContribHandler } from '../base';
28
+
29
+ import { MarkerHoverParticipantComponent } from './problem-fix.component';
30
+ import { ProblemFixProviderRegistry } from './problem-fix.feature.registry';
31
+ import { ProblemFixService } from './problem-fix.service';
32
+
33
+ class AIMonacoHoverParticipant extends MarkerHoverParticipant {
34
+ static injector: Injector;
35
+
36
+ override renderHoverParts(context: IEditorHoverRenderContext, hoverParts: MarkerHover[]) {
37
+ const disposable = super.renderHoverParts(context, hoverParts);
38
+
39
+ const { fragment } = context;
40
+ MarkerHoverParticipantComponent.mount(fragment, hoverParts, AIMonacoHoverParticipant.injector.get(AppConfig));
41
+
42
+ return disposable;
43
+ }
44
+ }
45
+
46
+ @Injectable()
47
+ export class ProblemFixHandler extends IAIMonacoContribHandler {
48
+ @Autowired(InlineChatHandler)
49
+ private readonly inlineChatHandler: InlineChatHandler;
50
+
51
+ @Autowired(INJECTOR_TOKEN)
52
+ private readonly injector: Injector;
53
+
54
+ @Autowired(ProblemFixService)
55
+ private readonly problemFixService: ProblemFixService;
56
+
57
+ @Autowired(ProblemFixRegistryToken)
58
+ private readonly problemFixProviderRegistry: ProblemFixProviderRegistry;
59
+
60
+ @Autowired(IAIReporter)
61
+ private readonly aiReporter: IAIReporter;
62
+
63
+ private aiNativeContextKey: AINativeContextKey;
64
+
65
+ mountEditor(editor: IEditor) {
66
+ this.aiNativeContextKey = this.injector.get(AINativeContextKey, [editor.monacoEditor.contextKeyService]);
67
+ return super.mountEditor(editor);
68
+ }
69
+
70
+ doContribute() {
71
+ const disposable = new Disposable();
72
+
73
+ // 先去掉 monaco 默认的 MarkerHoverParticipant
74
+ HoverParticipantRegistry._participants = HoverParticipantRegistry._participants.filter(
75
+ (participant) => participant !== MarkerHoverParticipant,
76
+ );
77
+
78
+ AIMonacoHoverParticipant.injector = this.injector;
79
+ HoverParticipantRegistry.register(AIMonacoHoverParticipant);
80
+
81
+ disposable.addDispose(
82
+ this.problemFixService.onHoverFixTrigger((part) => {
83
+ const hoverController = this.editor?.monacoEditor.getContribution<HoverController>(HoverController.ID);
84
+ if (hoverController) {
85
+ hoverController.hideContentHover();
86
+ }
87
+
88
+ this.handleHoverFix(part);
89
+ }),
90
+ );
91
+
92
+ return disposable;
93
+ }
94
+
95
+ private async handleHoverFix(part: MarkerHover) {
96
+ const provider = this.problemFixProviderRegistry.getHoverFixProvider();
97
+ if (!provider) {
98
+ return;
99
+ }
100
+
101
+ const monacoEditor = this.editor?.monacoEditor;
102
+
103
+ if (!monacoEditor) {
104
+ return;
105
+ }
106
+
107
+ const model = monacoEditor.getModel();
108
+
109
+ // 以 marker 的 range 为中心,向上取 2 行,向下取 3 行
110
+ const editRange = new Range(
111
+ Math.max(part.range.startLineNumber - 2, 0),
112
+ 1,
113
+ Math.min(part.range.endLineNumber + 3, model!.getLineCount() ?? 0),
114
+ model!.getLineMaxColumn(part.range.endLineNumber + 3) ?? 0,
115
+ );
116
+
117
+ const context = {
118
+ marker: part.marker,
119
+ editRange,
120
+ };
121
+
122
+ monacoEditor.setSelection(editRange);
123
+
124
+ const contextKeyDisposed = new Disposable();
125
+ const inlineChatIsVisible = new Set([InlineChatIsVisible.raw]);
126
+
127
+ contextKeyDisposed.addDispose(
128
+ this.aiNativeContextKey.contextKeyService!.onDidChangeContext((e) => {
129
+ if (e.payload.affectsSome(inlineChatIsVisible)) {
130
+ const isVisible = this.aiNativeContextKey.inlineChatIsVisible.get();
131
+ if (isVisible) {
132
+ this.inlineChatHandler.runAction({
133
+ monacoEditor,
134
+ reporterFn: (): string => {
135
+ const relationId = this.aiReporter.start(AISerivceType.ProblemFix, {
136
+ message: ActionTypeEnum.HoverFix,
137
+ type: AISerivceType.InlineChat,
138
+ source: ActionTypeEnum.HoverFix,
139
+ actionSource: ActionSourceEnum.Hover,
140
+ actionType: ActionTypeEnum.HoverFix,
141
+ });
142
+ return relationId;
143
+ },
144
+ crossSelection: monacoEditor.getSelection()!,
145
+ providerPreview: () => provider.provideFix(monacoEditor, context, this.inlineChatHandler.cancelIndicator.token),
146
+ extraData: {
147
+ actionSource: ActionSourceEnum.Hover,
148
+ actionType: ActionTypeEnum.HoverFix,
149
+ },
150
+ });
151
+ }
152
+
153
+ contextKeyDisposed.dispose();
154
+ }
155
+ }),
156
+ );
157
+
158
+ this.addDispose(contextKeyDisposed);
159
+ }
160
+ }
@@ -0,0 +1,5 @@
1
+ .problem_fix_btn_container {
2
+ margin-left: 8px;
3
+ margin-bottom: 8px;
4
+ height: 22px;
5
+ }
@@ -0,0 +1,13 @@
1
+ import { Injectable } from '@opensumi/di';
2
+ import { Emitter, Event } from '@opensumi/ide-core-common';
3
+ import { MarkerHover } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/hover/browser/markerHoverParticipant';
4
+
5
+ @Injectable()
6
+ export class ProblemFixService {
7
+ private readonly _onHoverFixTrigger = new Emitter<MarkerHover>();
8
+ public readonly onHoverFixTrigger: Event<MarkerHover> = this._onHoverFixTrigger.event;
9
+
10
+ public triggerHoverFix(isTrigger: MarkerHover) {
11
+ this._onHoverFixTrigger.fire(isTrigger);
12
+ }
13
+ }
@@ -13,7 +13,11 @@ import {
13
13
  RenameCandidatesProviderRegistryToken,
14
14
  ResolveConflictRegistryToken,
15
15
  } from '@opensumi/ide-core-browser';
16
- import { IntelligentCompletionsRegistryToken, TerminalRegistryToken } from '@opensumi/ide-core-common';
16
+ import {
17
+ IntelligentCompletionsRegistryToken,
18
+ ProblemFixRegistryToken,
19
+ TerminalRegistryToken,
20
+ } from '@opensumi/ide-core-common';
17
21
 
18
22
  import { ChatProxyServiceToken, IChatAgentService, IChatInternalService, IChatManagerService } from '../common';
19
23
 
@@ -32,6 +36,7 @@ import { IntelligentCompletionsRegistry } from './contrib/intelligent-completion
32
36
  import { InterfaceNavigationContribution } from './contrib/interface-navigation/interface-navigation.contribution';
33
37
  import { MergeConflictContribution } from './contrib/merge-conflict';
34
38
  import { ResolveConflictRegistry } from './contrib/merge-conflict/merge-conflict.feature.registry';
39
+ import { ProblemFixProviderRegistry } from './contrib/problem-fix/problem-fix.feature.registry';
35
40
  import { RenameCandidatesProviderRegistry } from './contrib/rename/rename.feature.registry';
36
41
  import { TerminalAIContribution } from './contrib/terminal/terminal-ai.contributon';
37
42
  import { TerminalFeatureRegistry } from './contrib/terminal/terminal.feature.registry';
@@ -112,6 +117,10 @@ export class AINativeModule extends BrowserModule {
112
117
  token: RenameCandidatesProviderRegistryToken,
113
118
  useClass: RenameCandidatesProviderRegistry,
114
119
  },
120
+ {
121
+ token: ProblemFixRegistryToken,
122
+ useClass: ProblemFixProviderRegistry,
123
+ },
115
124
  {
116
125
  token: TerminalRegistryToken,
117
126
  useClass: TerminalFeatureRegistry,
@@ -15,6 +15,7 @@ import {
15
15
  } from '@opensumi/ide-core-common';
16
16
  import { ICodeEditor, IRange, ITextModel, NewSymbolNamesProvider, Position } from '@opensumi/ide-monaco';
17
17
  import { SumiReadableStream } from '@opensumi/ide-utils/lib/stream';
18
+ import { IMarker } from '@opensumi/monaco-editor-core/esm/vs/platform/markers/common/markers';
18
19
 
19
20
  import { IChatWelcomeMessageContent, ISampleQuestions, ITerminalCommandSuggestionDesc } from '../common';
20
21
 
@@ -206,6 +207,23 @@ export interface IIntelligentCompletionsRegistry {
206
207
  registerIntelligentCompletionProvider(provider: IIntelligentCompletionProvider): void;
207
208
  }
208
209
 
210
+ export interface IProblemFixContext {
211
+ marker: IMarker;
212
+ editRange: IRange;
213
+ }
214
+
215
+ export interface IHoverFixHandler {
216
+ provideFix: (
217
+ editor: ICodeEditor,
218
+ context: IProblemFixContext,
219
+ token: CancellationToken,
220
+ ) => MaybePromise<ChatResponse | InlineChatController>;
221
+ }
222
+
223
+ export interface IProblemFixProviderRegistry {
224
+ registerHoverFixProvider(handler: IHoverFixHandler): void;
225
+ }
226
+
209
227
  export const AINativeCoreContribution = Symbol('AINativeCoreContribution');
210
228
 
211
229
  export interface AINativeCoreContribution {
@@ -230,6 +248,10 @@ export interface AINativeCoreContribution {
230
248
  * 注册智能重命名相关功能
231
249
  */
232
250
  registerRenameProvider?(registry: IRenameCandidatesProviderRegistry): void;
251
+ /**
252
+ * 注册智能修复相关功能
253
+ */
254
+ registerProblemFixFeature?(registry: IProblemFixProviderRegistry): void;
233
255
  /**
234
256
  * 注册智能终端相关功能
235
257
  */
@@ -70,11 +70,14 @@ export class InlineChatHandler extends Disposable {
70
70
  private aiInlineContentWidget: AIInlineContentWidget;
71
71
  private aiInlineChatDisposable: Disposable = new Disposable();
72
72
  private aiInlineChatOperationDisposable: Disposable = new Disposable();
73
- private cancelIndicator = new CancellationTokenSource();
73
+ private _cancelIndicator = new CancellationTokenSource();
74
+ get cancelIndicator() {
75
+ return this._cancelIndicator;
76
+ }
74
77
 
75
- private cancelToken() {
76
- this.cancelIndicator.cancel();
77
- this.cancelIndicator = new CancellationTokenSource();
78
+ public cancelToken() {
79
+ this._cancelIndicator.cancel();
80
+ this._cancelIndicator = new CancellationTokenSource();
78
81
  }
79
82
 
80
83
  private disposeAllWidget() {
@@ -228,6 +231,11 @@ export class InlineChatHandler extends Disposable {
228
231
  return;
229
232
  }
230
233
 
234
+ const crossSelection = this.getCrossSelection(monacoEditor);
235
+ if (!crossSelection) {
236
+ return;
237
+ }
238
+
231
239
  const previewer = () => {
232
240
  // 兼容 providerDiffPreviewStrategy api
233
241
  const strategy = handler.providerDiffPreviewStrategy
@@ -237,11 +245,12 @@ export class InlineChatHandler extends Disposable {
237
245
  return undefined;
238
246
  }
239
247
 
240
- return strategy.bind(this, monacoEditor, this.cancelIndicator.token);
248
+ return strategy.bind(this, monacoEditor, this._cancelIndicator.token);
241
249
  };
242
250
 
243
- this.runInlineChatAction({
251
+ this.runAction({
244
252
  monacoEditor,
253
+ crossSelection,
245
254
  reporterFn: () => {
246
255
  const relationId = this.aiReporter.start(action.name, {
247
256
  message: action.name,
@@ -253,7 +262,7 @@ export class InlineChatHandler extends Disposable {
253
262
  });
254
263
  return relationId;
255
264
  },
256
- execute: handler.execute ? handler.execute!.bind(this, monacoEditor, this.cancelIndicator.token) : undefined,
265
+ execute: handler.execute ? handler.execute!.bind(this, monacoEditor, this._cancelIndicator.token) : undefined,
257
266
  providerPreview: previewer(),
258
267
  extraData: {
259
268
  actionSource: source === 'codeAction' ? ActionSourceEnum.CodeAction : ActionSourceEnum.InlineChat,
@@ -273,8 +282,14 @@ export class InlineChatHandler extends Disposable {
273
282
 
274
283
  const strategy = await this.inlineChatFeatureRegistry.getInteractiveInputStrategyHandler()(monacoEditor, value);
275
284
 
276
- this.runInlineChatAction({
285
+ const crossSelection = this.getCrossSelection(monacoEditor);
286
+ if (!crossSelection) {
287
+ return;
288
+ }
289
+
290
+ this.runAction({
277
291
  monacoEditor,
292
+ crossSelection,
278
293
  reporterFn: () => {
279
294
  const relationId = this.aiReporter.start(AISerivceType.InlineChatInput, {
280
295
  message: value,
@@ -286,11 +301,11 @@ export class InlineChatHandler extends Disposable {
286
301
  },
287
302
  execute:
288
303
  handler.execute && strategy === ERunStrategy.EXECUTE
289
- ? handler.execute!.bind(this, monacoEditor, value, this.cancelIndicator.token)
304
+ ? handler.execute!.bind(this, monacoEditor, value, this._cancelIndicator.token)
290
305
  : undefined,
291
306
  providerPreview:
292
307
  handler.providePreviewStrategy && strategy === ERunStrategy.PREVIEW
293
- ? handler.providePreviewStrategy.bind(this, monacoEditor, value, this.cancelIndicator.token)
308
+ ? handler.providePreviewStrategy.bind(this, monacoEditor, value, this._cancelIndicator.token)
294
309
  : undefined,
295
310
  extraData: {
296
311
  actionSource: ActionSourceEnum.InlineChatInput,
@@ -301,6 +316,18 @@ export class InlineChatHandler extends Disposable {
301
316
  );
302
317
  }
303
318
 
319
+ private getCrossSelection(monacoEditor: monaco.ICodeEditor) {
320
+ const selection = monacoEditor.getSelection();
321
+ if (!selection) {
322
+ this.logger.error('No selection found, aborting inline chat action.');
323
+ return;
324
+ }
325
+
326
+ return selection
327
+ .setStartPosition(selection.startLineNumber, 1)
328
+ .setEndPosition(selection.endLineNumber, monacoEditor.getModel()!.getLineMaxColumn(selection.endLineNumber));
329
+ }
330
+
304
331
  private convertInlineChatStatus(
305
332
  status: EInlineChatStatus,
306
333
  reportInfo: {
@@ -438,9 +465,6 @@ export class InlineChatHandler extends Disposable {
438
465
  }
439
466
  }
440
467
 
441
- /**
442
- * 重新生成代码
443
- */
444
468
  private async handleDiffPreviewStrategy(params: {
445
469
  monacoEditor: monaco.ICodeEditor;
446
470
  strategy: (...arg: any[]) => MaybePromise<ChatResponse | InlineChatController>;
@@ -460,12 +484,11 @@ export class InlineChatHandler extends Disposable {
460
484
  this.aiInlineChatOperationDisposable.dispose();
461
485
 
462
486
  this.ensureInlineChatVisible(monacoEditor, crossSelection);
463
-
464
487
  this.aiInlineChatDisposable.addDispose(this.aiInlineContentWidget.launchChatStatus(EInlineChatStatus.THINKING));
465
488
 
466
489
  const startTime = Date.now();
467
490
 
468
- if (this.cancelIndicator.token.isCancellationRequested) {
491
+ if (this._cancelIndicator.token.isCancellationRequested) {
469
492
  this.convertInlineChatStatus(EInlineChatStatus.READY, {
470
493
  relationId,
471
494
  message: 'abort',
@@ -567,9 +590,10 @@ export class InlineChatHandler extends Disposable {
567
590
  ]);
568
591
  }
569
592
 
570
- private async runInlineChatAction(params: {
593
+ public async runAction(params: {
571
594
  monacoEditor: monaco.ICodeEditor;
572
595
  reporterFn: () => string;
596
+ crossSelection: monaco.Selection;
573
597
  execute?: () => MaybePromise<void>;
574
598
  providerPreview?: () => MaybePromise<ChatResponse | InlineChatController>;
575
599
  extraData?: {
@@ -577,12 +601,7 @@ export class InlineChatHandler extends Disposable {
577
601
  actionType: string;
578
602
  };
579
603
  }) {
580
- const { monacoEditor, reporterFn, execute, providerPreview, extraData } = params;
581
- const selection = monacoEditor.getSelection();
582
- if (!selection) {
583
- this.logger.error('No selection found, aborting inline chat action.');
584
- return;
585
- }
604
+ const { monacoEditor, crossSelection, reporterFn, execute, providerPreview, extraData } = params;
586
605
 
587
606
  if (execute) {
588
607
  await execute();
@@ -590,10 +609,6 @@ export class InlineChatHandler extends Disposable {
590
609
  }
591
610
 
592
611
  if (providerPreview) {
593
- const crossSelection = selection
594
- .setStartPosition(selection.startLineNumber, 1)
595
- .setEndPosition(selection.endLineNumber, Number.MAX_SAFE_INTEGER);
596
-
597
612
  const relationId = reporterFn();
598
613
 
599
614
  await this.handleDiffPreviewStrategy({