@opensumi/ide-ai-native 3.7.1 → 3.7.2-next-1739859371.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 (162) hide show
  1. package/lib/browser/ai-core.contextkeys.d.ts +1 -0
  2. package/lib/browser/ai-core.contextkeys.d.ts.map +1 -1
  3. package/lib/browser/ai-core.contextkeys.js +1 -0
  4. package/lib/browser/ai-core.contextkeys.js.map +1 -1
  5. package/lib/browser/ai-core.contribution.d.ts +2 -1
  6. package/lib/browser/ai-core.contribution.d.ts.map +1 -1
  7. package/lib/browser/ai-core.contribution.js +60 -15
  8. package/lib/browser/ai-core.contribution.js.map +1 -1
  9. package/lib/browser/components/ChatMarkdown.d.ts.map +1 -1
  10. package/lib/browser/components/ChatMarkdown.js.map +1 -1
  11. package/lib/browser/components/WelcomeMsg.js.map +1 -1
  12. package/lib/browser/components/utils.d.ts +2 -2
  13. package/lib/browser/contrib/intelligent-completions/index.d.ts +14 -9
  14. package/lib/browser/contrib/intelligent-completions/index.d.ts.map +1 -1
  15. package/lib/browser/contrib/intelligent-completions/index.js +6 -1
  16. package/lib/browser/contrib/intelligent-completions/index.js.map +1 -1
  17. package/lib/browser/contrib/intelligent-completions/intelligent-completions.contribution.d.ts +1 -0
  18. package/lib/browser/contrib/intelligent-completions/intelligent-completions.contribution.d.ts.map +1 -1
  19. package/lib/browser/contrib/intelligent-completions/intelligent-completions.contribution.js +26 -4
  20. package/lib/browser/contrib/intelligent-completions/intelligent-completions.contribution.js.map +1 -1
  21. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.d.ts +5 -4
  22. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.d.ts.map +1 -1
  23. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js +50 -42
  24. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js.map +1 -1
  25. package/lib/browser/contrib/intelligent-completions/source/base.d.ts +9 -3
  26. package/lib/browser/contrib/intelligent-completions/source/base.d.ts.map +1 -1
  27. package/lib/browser/contrib/intelligent-completions/source/base.js +21 -3
  28. package/lib/browser/contrib/intelligent-completions/source/base.js.map +1 -1
  29. package/lib/browser/contrib/intelligent-completions/source/line-change.source.d.ts +10 -3
  30. package/lib/browser/contrib/intelligent-completions/source/line-change.source.d.ts.map +1 -1
  31. package/lib/browser/contrib/intelligent-completions/source/line-change.source.js +95 -22
  32. package/lib/browser/contrib/intelligent-completions/source/line-change.source.js.map +1 -1
  33. package/lib/browser/contrib/intelligent-completions/source/lint-error.source.d.ts +1 -3
  34. package/lib/browser/contrib/intelligent-completions/source/lint-error.source.d.ts.map +1 -1
  35. package/lib/browser/contrib/intelligent-completions/source/lint-error.source.js +13 -20
  36. package/lib/browser/contrib/intelligent-completions/source/lint-error.source.js.map +1 -1
  37. package/lib/browser/contrib/intelligent-completions/source/typing.source.d.ts +9 -0
  38. package/lib/browser/contrib/intelligent-completions/source/typing.source.d.ts.map +1 -0
  39. package/lib/browser/contrib/intelligent-completions/source/typing.source.js +36 -0
  40. package/lib/browser/contrib/intelligent-completions/source/typing.source.js.map +1 -0
  41. package/lib/browser/contrib/terminal/component/terminal-command-suggest-controller.js +2 -2
  42. package/lib/browser/contrib/terminal/component/terminal-command-suggest-controller.js.map +1 -1
  43. package/lib/browser/index.js +1 -1
  44. package/lib/browser/index.js.map +1 -1
  45. package/lib/browser/languages/tree-sitter/wasm-manager.d.ts.map +1 -1
  46. package/lib/browser/languages/tree-sitter/wasm-manager.js +14 -2
  47. package/lib/browser/languages/tree-sitter/wasm-manager.js.map +1 -1
  48. package/lib/browser/layout/ai-layout.d.ts.map +1 -1
  49. package/lib/browser/layout/ai-layout.js +2 -2
  50. package/lib/browser/layout/ai-layout.js.map +1 -1
  51. package/lib/browser/layout/layout.module.less +9 -9
  52. package/lib/browser/layout/tabbar.view.d.ts.map +1 -1
  53. package/lib/browser/layout/tabbar.view.js +5 -6
  54. package/lib/browser/layout/tabbar.view.js.map +1 -1
  55. package/lib/browser/model/enhanceDecorationsCollection.d.ts +14 -10
  56. package/lib/browser/model/enhanceDecorationsCollection.d.ts.map +1 -1
  57. package/lib/browser/model/enhanceDecorationsCollection.js +42 -53
  58. package/lib/browser/model/enhanceDecorationsCollection.js.map +1 -1
  59. package/lib/browser/preferences/schema.d.ts.map +1 -1
  60. package/lib/browser/preferences/schema.js +4 -0
  61. package/lib/browser/preferences/schema.js.map +1 -1
  62. package/lib/browser/types.d.ts +7 -2
  63. package/lib/browser/types.d.ts.map +1 -1
  64. package/lib/browser/types.js.map +1 -1
  65. package/lib/browser/widget/inline-chat/inline-chat-editor.controller.d.ts +2 -1
  66. package/lib/browser/widget/inline-chat/inline-chat-editor.controller.d.ts.map +1 -1
  67. package/lib/browser/widget/inline-chat/inline-chat-editor.controller.js +20 -48
  68. package/lib/browser/widget/inline-chat/inline-chat-editor.controller.js.map +1 -1
  69. package/lib/browser/widget/inline-chat/inline-chat.feature.registry.d.ts +3 -13
  70. package/lib/browser/widget/inline-chat/inline-chat.feature.registry.d.ts.map +1 -1
  71. package/lib/browser/widget/inline-chat/inline-chat.feature.registry.js +24 -72
  72. package/lib/browser/widget/inline-chat/inline-chat.feature.registry.js.map +1 -1
  73. package/lib/browser/widget/inline-chat/inline-chat.service.d.ts +1 -6
  74. package/lib/browser/widget/inline-chat/inline-chat.service.d.ts.map +1 -1
  75. package/lib/browser/widget/inline-chat/inline-chat.service.js +5 -17
  76. package/lib/browser/widget/inline-chat/inline-chat.service.js.map +1 -1
  77. package/lib/browser/widget/inline-chat/inline-content-widget.d.ts +2 -5
  78. package/lib/browser/widget/inline-chat/inline-content-widget.d.ts.map +1 -1
  79. package/lib/browser/widget/inline-chat/inline-content-widget.js +17 -42
  80. package/lib/browser/widget/inline-chat/inline-content-widget.js.map +1 -1
  81. package/lib/browser/widget/inline-diff/inline-diff-previewer.d.ts +22 -5
  82. package/lib/browser/widget/inline-diff/inline-diff-previewer.d.ts.map +1 -1
  83. package/lib/browser/widget/inline-diff/inline-diff-previewer.js +61 -30
  84. package/lib/browser/widget/inline-diff/inline-diff-previewer.js.map +1 -1
  85. package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts +8 -12
  86. package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts.map +1 -1
  87. package/lib/browser/widget/inline-diff/inline-diff.controller.js +68 -96
  88. package/lib/browser/widget/inline-diff/inline-diff.controller.js.map +1 -1
  89. package/lib/browser/widget/inline-hint/inline-hint.controller.d.ts +0 -1
  90. package/lib/browser/widget/inline-hint/inline-hint.controller.d.ts.map +1 -1
  91. package/lib/browser/widget/inline-hint/inline-hint.controller.js +0 -5
  92. package/lib/browser/widget/inline-hint/inline-hint.controller.js.map +1 -1
  93. package/lib/browser/widget/inline-input/inline-input-widget.d.ts +12 -2
  94. package/lib/browser/widget/inline-input/inline-input-widget.d.ts.map +1 -1
  95. package/lib/browser/widget/inline-input/inline-input-widget.js +26 -18
  96. package/lib/browser/widget/inline-input/inline-input-widget.js.map +1 -1
  97. package/lib/browser/widget/inline-input/inline-input.controller.d.ts +14 -5
  98. package/lib/browser/widget/inline-input/inline-input.controller.d.ts.map +1 -1
  99. package/lib/browser/widget/inline-input/inline-input.controller.js +321 -165
  100. package/lib/browser/widget/inline-input/inline-input.controller.js.map +1 -1
  101. package/lib/browser/widget/inline-input/inline-input.module.less +4 -0
  102. package/lib/browser/widget/inline-input/inline-input.service.d.ts +19 -7
  103. package/lib/browser/widget/inline-input/inline-input.service.d.ts.map +1 -1
  104. package/lib/browser/widget/inline-input/inline-input.service.js +72 -12
  105. package/lib/browser/widget/inline-input/inline-input.service.js.map +1 -1
  106. package/lib/browser/widget/inline-input/model.d.ts +34 -0
  107. package/lib/browser/widget/inline-input/model.d.ts.map +1 -0
  108. package/lib/browser/widget/inline-input/model.js +63 -0
  109. package/lib/browser/widget/inline-input/model.js.map +1 -0
  110. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts +8 -19
  111. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts.map +1 -1
  112. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js +44 -39
  113. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js.map +1 -1
  114. package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts +17 -4
  115. package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts.map +1 -1
  116. package/lib/browser/widget/inline-stream-diff/live-preview.component.js +37 -5
  117. package/lib/browser/widget/inline-stream-diff/live-preview.component.js.map +1 -1
  118. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts +7 -11
  119. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts.map +1 -1
  120. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js +33 -77
  121. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js.map +1 -1
  122. package/lib/common/utils.js +2 -2
  123. package/lib/common/utils.js.map +1 -1
  124. package/package.json +21 -21
  125. package/src/browser/ai-core.contextkeys.ts +3 -0
  126. package/src/browser/ai-core.contribution.ts +68 -17
  127. package/src/browser/components/ChatMarkdown.tsx +1 -1
  128. package/src/browser/components/WelcomeMsg.tsx +1 -1
  129. package/src/browser/contrib/intelligent-completions/index.ts +16 -4
  130. package/src/browser/contrib/intelligent-completions/intelligent-completions.contribution.ts +29 -8
  131. package/src/browser/contrib/intelligent-completions/intelligent-completions.controller.ts +86 -55
  132. package/src/browser/contrib/intelligent-completions/source/base.ts +28 -7
  133. package/src/browser/contrib/intelligent-completions/source/line-change.source.ts +129 -22
  134. package/src/browser/contrib/intelligent-completions/source/lint-error.source.ts +19 -31
  135. package/src/browser/contrib/intelligent-completions/source/typing.source.ts +34 -0
  136. package/src/browser/contrib/terminal/component/terminal-command-suggest-controller.tsx +1 -1
  137. package/src/browser/index.ts +2 -2
  138. package/src/browser/languages/tree-sitter/wasm-manager.ts +12 -2
  139. package/src/browser/layout/ai-layout.tsx +5 -2
  140. package/src/browser/layout/layout.module.less +9 -9
  141. package/src/browser/layout/tabbar.view.tsx +10 -8
  142. package/src/browser/model/enhanceDecorationsCollection.ts +62 -77
  143. package/src/browser/preferences/schema.ts +4 -0
  144. package/src/browser/types.ts +5 -3
  145. package/src/browser/widget/inline-chat/inline-chat-editor.controller.ts +29 -64
  146. package/src/browser/widget/inline-chat/inline-chat.feature.registry.ts +23 -90
  147. package/src/browser/widget/inline-chat/inline-chat.service.ts +2 -17
  148. package/src/browser/widget/inline-chat/inline-content-widget.tsx +14 -69
  149. package/src/browser/widget/inline-diff/inline-diff-previewer.ts +87 -32
  150. package/src/browser/widget/inline-diff/inline-diff.controller.ts +90 -114
  151. package/src/browser/widget/inline-hint/inline-hint.controller.ts +1 -7
  152. package/src/browser/widget/inline-input/inline-input-widget.tsx +34 -12
  153. package/src/browser/widget/inline-input/inline-input.controller.ts +454 -242
  154. package/src/browser/widget/inline-input/inline-input.module.less +4 -0
  155. package/src/browser/widget/inline-input/inline-input.service.ts +92 -13
  156. package/src/browser/widget/inline-input/model.ts +74 -0
  157. package/src/browser/widget/inline-stream-diff/inline-stream-diff.handler.tsx +54 -67
  158. package/src/browser/widget/inline-stream-diff/live-preview.component.tsx +45 -6
  159. package/src/browser/widget/inline-stream-diff/live-preview.decoration.tsx +40 -112
  160. package/src/common/utils.ts +2 -2
  161. package/lib/browser/model/styles.module.less +0 -7
  162. package/src/browser/model/styles.module.less +0 -7
@@ -27,6 +27,7 @@ import {
27
27
  } from '@opensumi/ide-core-browser';
28
28
  import {
29
29
  AI_CHAT_VISIBLE,
30
+ AI_INLINE_CHAT_INTERACTIVE_INPUT_CANCEL,
30
31
  AI_INLINE_CHAT_INTERACTIVE_INPUT_VISIBLE,
31
32
  AI_INLINE_CHAT_VISIBLE,
32
33
  AI_INLINE_COMPLETION_REPORTER,
@@ -37,6 +38,7 @@ import {
37
38
  InlineChatIsVisible,
38
39
  InlineDiffPartialEditsIsVisible,
39
40
  InlineHintWidgetIsVisible,
41
+ InlineInputWidgetIsStreaming,
40
42
  InlineInputWidgetIsVisible,
41
43
  } from '@opensumi/ide-core-browser/lib/contextkey/ai-native';
42
44
  import { DesignLayoutConfig } from '@opensumi/ide-core-browser/lib/layout/constants';
@@ -56,8 +58,9 @@ import {
56
58
  runWhenIdle,
57
59
  } from '@opensumi/ide-core-common';
58
60
  import { DESIGN_MENU_BAR_RIGHT } from '@opensumi/ide-design';
59
- import { IEditor } from '@opensumi/ide-editor';
61
+ import { IEditor, WorkbenchEditorService } from '@opensumi/ide-editor';
60
62
  import { BrowserEditorContribution, IEditorFeatureRegistry } from '@opensumi/ide-editor/lib/browser';
63
+ import { WorkbenchEditorServiceImpl } from '@opensumi/ide-editor/lib/browser/workbench-editor.service';
61
64
  import { IMainLayoutService } from '@opensumi/ide-main-layout';
62
65
  import { ISettingRegistry, SettingContribution } from '@opensumi/ide-preferences';
63
66
  import { EditorContributionInstantiation } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/editorExtensions';
@@ -101,11 +104,11 @@ import {
101
104
  } from './types';
102
105
  import { InlineChatEditorController } from './widget/inline-chat/inline-chat-editor.controller';
103
106
  import { InlineChatFeatureRegistry } from './widget/inline-chat/inline-chat.feature.registry';
104
- import { AIInlineChatService } from './widget/inline-chat/inline-chat.service';
107
+ import { InlineChatService } from './widget/inline-chat/inline-chat.service';
105
108
  import { InlineDiffController } from './widget/inline-diff/inline-diff.controller';
106
109
  import { InlineHintController } from './widget/inline-hint/inline-hint.controller';
107
110
  import { InlineInputController } from './widget/inline-input/inline-input.controller';
108
- import { InlineInputChatService } from './widget/inline-input/inline-input.service';
111
+ import { InlineInputService } from './widget/inline-input/inline-input.service';
109
112
  import { InlineStreamDiffService } from './widget/inline-stream-diff/inline-stream-diff.service';
110
113
  import { SumiLightBulbWidget } from './widget/light-bulb';
111
114
 
@@ -191,10 +194,10 @@ export class AINativeBrowserContribution
191
194
  private readonly chatProxyService: ChatProxyService;
192
195
 
193
196
  @Autowired(IAIInlineChatService)
194
- private readonly aiInlineChatService: AIInlineChatService;
197
+ private readonly aiInlineChatService: InlineChatService;
195
198
 
196
- @Autowired(InlineInputChatService)
197
- private readonly inlineInputChatService: InlineInputChatService;
199
+ @Autowired(InlineInputService)
200
+ private readonly inlineInputService: InlineInputService;
198
201
 
199
202
  @Autowired(InlineStreamDiffService)
200
203
  private readonly inlineStreamDiffService: InlineStreamDiffService;
@@ -205,6 +208,9 @@ export class AINativeBrowserContribution
205
208
  @Autowired(CodeActionSingleHandler)
206
209
  private readonly codeActionSingleHandler: CodeActionSingleHandler;
207
210
 
211
+ @Autowired(WorkbenchEditorService)
212
+ private readonly workbenchEditorService: WorkbenchEditorServiceImpl;
213
+
208
214
  constructor() {
209
215
  this.registerFeature();
210
216
  }
@@ -237,7 +243,7 @@ export class AINativeBrowserContribution
237
243
  EditorContributionInstantiation.BeforeFirstInteraction,
238
244
  );
239
245
 
240
- if (this.inlineChatFeatureRegistry.getInteractiveInputHandler()) {
246
+ if (this.inlineInputService.getInteractiveInputHandler()) {
241
247
  register(
242
248
  InlineHintController.ID,
243
249
  new SyncDescriptor(InlineHintController, [this.injector]),
@@ -254,7 +260,7 @@ export class AINativeBrowserContribution
254
260
  register(
255
261
  IntelligentCompletionsController.ID,
256
262
  new SyncDescriptor(IntelligentCompletionsController, [this.injector]),
257
- EditorContributionInstantiation.AfterFirstRender,
263
+ EditorContributionInstantiation.Eager,
258
264
  );
259
265
  register(
260
266
  InlineCompletionsController.ID,
@@ -365,6 +371,10 @@ export class AINativeBrowserContribution
365
371
  id: AINativeSettingSectionsId.CodeEditsLineChange,
366
372
  localized: 'preference.ai.native.codeEdits.lineChange',
367
373
  },
374
+ {
375
+ id: AINativeSettingSectionsId.CodeEditsTyping,
376
+ localized: 'preference.ai.native.codeEdits.typing',
377
+ },
368
378
  ],
369
379
  });
370
380
  }
@@ -414,14 +424,48 @@ export class AINativeBrowserContribution
414
424
  });
415
425
 
416
426
  commands.registerCommand(AI_INLINE_CHAT_INTERACTIVE_INPUT_VISIBLE, {
417
- execute: (isVisible: boolean) => {
418
- if (isVisible) {
419
- this.inlineInputChatService.visible();
420
- } else {
421
- this.inlineInputChatService.hide();
427
+ execute: async (isVisible: boolean) => {
428
+ if (!isVisible) {
429
+ this.inlineInputService.hide();
430
+ return;
422
431
  }
423
432
 
424
- this.aiInlineChatService._onInteractiveInputVisible.fire(isVisible);
433
+ // 每次在展示 inline input 的时候,先隐藏 inline chat
434
+ this.commandService.executeCommand(AI_INLINE_CHAT_VISIBLE.id, false);
435
+
436
+ const editor = this.workbenchEditorService.currentCodeEditor;
437
+ if (!editor) {
438
+ return;
439
+ }
440
+
441
+ const position = editor.monacoEditor.getPosition();
442
+ if (!position) {
443
+ return;
444
+ }
445
+
446
+ const selection = editor.monacoEditor.getSelection();
447
+ const isEmptyLine = position ? editor.monacoEditor.getModel()?.getLineLength(position.lineNumber) === 0 : false;
448
+
449
+ if (isEmptyLine) {
450
+ this.inlineInputService.visibleByPosition(position);
451
+ return;
452
+ }
453
+
454
+ if (selection && !selection.isEmpty()) {
455
+ this.inlineInputService.visibleBySelection(selection);
456
+ return;
457
+ }
458
+
459
+ this.inlineInputService.visibleByNearestCodeBlock(position, editor.monacoEditor);
460
+ },
461
+ });
462
+
463
+ commands.registerCommand(AI_INLINE_CHAT_INTERACTIVE_INPUT_CANCEL, {
464
+ execute: () => {
465
+ const editor = this.workbenchEditorService.currentCodeEditor;
466
+ if (editor) {
467
+ InlineInputController.get(editor.monacoEditor)?.cancelToken();
468
+ }
425
469
  },
426
470
  });
427
471
 
@@ -512,12 +556,12 @@ export class AINativeBrowserContribution
512
556
  when: `editorFocus && ${InlineChatIsVisible.raw}`,
513
557
  });
514
558
 
515
- if (this.inlineChatFeatureRegistry.getInteractiveInputHandler()) {
559
+ if (this.inlineInputService.getInteractiveInputHandler()) {
516
560
  // 当 Inline Chat (浮动组件)展示时,通过 CMD K 唤起 Inline Input
517
561
  keybindings.registerKeybinding(
518
562
  {
519
563
  command: AI_INLINE_CHAT_INTERACTIVE_INPUT_VISIBLE.id,
520
- keybinding: 'ctrlcmd+k',
564
+ keybinding: this.aiNativeConfigService.inlineChat.inputKeybinding,
521
565
  args: true,
522
566
  priority: 0,
523
567
  when: `editorFocus && (${InlineChatIsVisible.raw} || inlineSuggestionVisible)`,
@@ -532,11 +576,18 @@ export class AINativeBrowserContribution
532
576
  priority: 0,
533
577
  when: `editorFocus && ${InlineInputWidgetIsVisible.raw}`,
534
578
  });
579
+ // 当 Inline Input 流式编辑时,通过 ESC 退出
580
+ keybindings.registerKeybinding({
581
+ command: AI_INLINE_CHAT_INTERACTIVE_INPUT_CANCEL.id,
582
+ keybinding: 'esc',
583
+ priority: 1,
584
+ when: `editorFocus && ${InlineInputWidgetIsStreaming.raw}`,
585
+ });
535
586
  // 当出现 CMD K 展示信息时,通过快捷键快速唤起 Inline Input
536
587
  keybindings.registerKeybinding(
537
588
  {
538
589
  command: AI_INLINE_CHAT_INTERACTIVE_INPUT_VISIBLE.id,
539
- keybinding: 'ctrlcmd+k',
590
+ keybinding: this.aiNativeConfigService.inlineChat.inputKeybinding,
540
591
  args: true,
541
592
  priority: 0,
542
593
  when: `editorFocus && ${InlineHintWidgetIsVisible.raw} && ${InlineChatIsVisible.not}`,
@@ -1,5 +1,5 @@
1
1
  import cls from 'classnames';
2
- import React, { ReactNode, useEffect, useRef, useState } from 'react';
2
+ import React, { useEffect, useRef, useState } from 'react';
3
3
 
4
4
  import { MarkdownReactParser, MarkdownReactRenderer } from '@opensumi/ide-components/lib/markdown-react';
5
5
  import { IMarkedOptions, marked } from '@opensumi/ide-components/lib/utils';
@@ -14,7 +14,7 @@ import {
14
14
  import { isMarkdownString } from '@opensumi/monaco-editor-core/esm/vs/base/common/htmlContent';
15
15
 
16
16
  import 'react-chat-elements/dist/main.css';
17
- import { IChatAgentService, IChatReplyFollowup, ISampleQuestions } from '../../common';
17
+ import { IChatAgentService, ISampleQuestions } from '../../common';
18
18
  import { ChatService } from '../chat/chat.api.service';
19
19
  import { ChatFeatureRegistry } from '../chat/chat.feature.registry';
20
20
  import { ChatRenderRegistry } from '../chat/chat.render.registry';
@@ -1,9 +1,15 @@
1
1
  import { Disposable, ECodeEditsSourceTyping } from '@opensumi/ide-core-common';
2
- import { IPosition, IRange, InlineCompletion } from '@opensumi/ide-monaco';
2
+ import { IModelContentChangedEvent, IPosition, IRange, InlineCompletion } from '@opensumi/ide-monaco';
3
3
 
4
4
  import type { ILineChangeData } from './source/line-change.source';
5
5
  import type { ILinterErrorData } from './source/lint-error.source';
6
6
 
7
+ /**
8
+ * 有效弃用时间(毫秒)
9
+ * 在可见的情况下超过 750ms 弃用才算有效数据,否则视为无效数据
10
+ */
11
+ export const VALID_TIME = 750;
12
+
7
13
  export interface IIntelligentCompletionsResult<T = any> {
8
14
  readonly items: InlineCompletion[];
9
15
  /**
@@ -12,9 +18,15 @@ export interface IIntelligentCompletionsResult<T = any> {
12
18
  extra?: T;
13
19
  }
14
20
 
15
- export type ICodeEditsContextBean =
16
- | { typing: ECodeEditsSourceTyping.LinterErrors; position: IPosition; data: ILinterErrorData }
17
- | { typing: ECodeEditsSourceTyping.LineChange; position: IPosition; data: ILineChangeData };
21
+ export interface ICodeEditsContextBean {
22
+ typing: ECodeEditsSourceTyping;
23
+ position: IPosition;
24
+ data: {
25
+ [ECodeEditsSourceTyping.LinterErrors]?: ILinterErrorData;
26
+ [ECodeEditsSourceTyping.LineChange]?: ILineChangeData;
27
+ [ECodeEditsSourceTyping.Typing]?: IModelContentChangedEvent;
28
+ };
29
+ }
18
30
 
19
31
  export interface ICodeEdit {
20
32
  /**
@@ -1,19 +1,18 @@
1
1
  import { Autowired } from '@opensumi/di';
2
2
  import {
3
+ AINativeConfigService,
3
4
  ClientAppContribution,
4
5
  Key,
5
6
  KeybindingContribution,
6
7
  KeybindingRegistry,
7
8
  KeybindingScope,
8
9
  } 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';
13
11
  import { MultiLineEditsIsVisible } from '@opensumi/ide-core-browser/lib/contextkey/ai-native';
14
12
  import { CommandContribution, CommandRegistry, Domain } from '@opensumi/ide-core-common';
15
13
  import { WorkbenchEditorService } from '@opensumi/ide-editor';
16
14
  import { WorkbenchEditorServiceImpl } from '@opensumi/ide-editor/lib/browser/workbench-editor.service';
15
+ import { transaction } from '@opensumi/ide-monaco/lib/common/observable';
17
16
 
18
17
  import { IntelligentCompletionsController } from './intelligent-completions.controller';
19
18
 
@@ -22,8 +21,11 @@ export class IntelligentCompletionsContribution implements KeybindingContributio
22
21
  @Autowired(WorkbenchEditorService)
23
22
  private readonly workbenchEditorService: WorkbenchEditorServiceImpl;
24
23
 
24
+ @Autowired(AINativeConfigService)
25
+ private readonly aiNativeConfigService: AINativeConfigService;
26
+
25
27
  registerCommands(commands: CommandRegistry): void {
26
- commands.registerCommand(AI_MULTI_LINE_COMPLETION_DISCARD, {
28
+ commands.registerCommand(AI_CODE_EDITS_COMMANDS.DISCARD, {
27
29
  execute: () => {
28
30
  const editor = this.workbenchEditorService.currentCodeEditor;
29
31
  if (editor) {
@@ -32,7 +34,7 @@ export class IntelligentCompletionsContribution implements KeybindingContributio
32
34
  },
33
35
  });
34
36
 
35
- commands.registerCommand(AI_MULTI_LINE_COMPLETION_ACCEPT, {
37
+ commands.registerCommand(AI_CODE_EDITS_COMMANDS.ACCEPT, {
36
38
  execute: () => {
37
39
  const editor = this.workbenchEditorService.currentCodeEditor;
38
40
  if (editor) {
@@ -40,11 +42,24 @@ export class IntelligentCompletionsContribution implements KeybindingContributio
40
42
  }
41
43
  },
42
44
  });
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
+ });
43
56
  }
44
57
 
45
58
  registerKeybindings(keybindings: KeybindingRegistry): void {
59
+ const { codeEdits } = this.aiNativeConfigService;
60
+
46
61
  keybindings.registerKeybinding({
47
- command: AI_MULTI_LINE_COMPLETION_DISCARD.id,
62
+ command: AI_CODE_EDITS_COMMANDS.DISCARD.id,
48
63
  keybinding: Key.ESCAPE.code,
49
64
  when: MultiLineEditsIsVisible.raw,
50
65
  priority: 100,
@@ -52,11 +67,17 @@ export class IntelligentCompletionsContribution implements KeybindingContributio
52
67
 
53
68
  keybindings.registerKeybinding(
54
69
  {
55
- command: AI_MULTI_LINE_COMPLETION_ACCEPT.id,
70
+ command: AI_CODE_EDITS_COMMANDS.ACCEPT.id,
56
71
  keybinding: Key.TAB.code,
57
72
  when: MultiLineEditsIsVisible.raw,
58
73
  },
59
74
  KeybindingScope.USER,
60
75
  );
76
+
77
+ keybindings.registerKeybinding({
78
+ command: AI_CODE_EDITS_COMMANDS.TRIGGER.id,
79
+ keybinding: codeEdits.triggerKeybinding,
80
+ when: 'editorFocus',
81
+ });
61
82
  }
62
83
  }
@@ -1,4 +1,10 @@
1
- import { Key, KeybindingRegistry, KeybindingScope, PreferenceService } from '@opensumi/ide-core-browser';
1
+ import {
2
+ ContextKeyChangeEvent,
3
+ Key,
4
+ KeybindingRegistry,
5
+ KeybindingScope,
6
+ PreferenceService,
7
+ } from '@opensumi/ide-core-browser';
2
8
  import { MultiLineEditsIsVisible } from '@opensumi/ide-core-browser/lib/contextkey/ai-native';
3
9
  import {
4
10
  AINativeSettingSectionsId,
@@ -13,10 +19,16 @@ import {
13
19
  import { Emitter, ICodeEditor, ICursorPositionChangedEvent, IRange, ITextModel, Range } from '@opensumi/ide-monaco';
14
20
  import {
15
21
  IObservable,
22
+ IObservableSignal,
16
23
  ISettableObservable,
24
+ ITransaction,
17
25
  autorun,
18
26
  autorunWithStoreHandleChanges,
19
27
  derived,
28
+ derivedHandleChanges,
29
+ derivedOpts,
30
+ observableFromEvent,
31
+ observableSignal,
20
32
  observableValue,
21
33
  transaction,
22
34
  } from '@opensumi/ide-monaco/lib/common/observable';
@@ -47,8 +59,9 @@ import { IntelligentCompletionsRegistry } from './intelligent-completions.featur
47
59
  import { CodeEditsSourceCollection } from './source/base';
48
60
  import { LineChangeCodeEditsSource } from './source/line-change.source';
49
61
  import { LintErrorCodeEditsSource } from './source/lint-error.source';
62
+ import { TypingCodeEditsSource } from './source/typing.source';
50
63
 
51
- import { CodeEditsResultValue } from './index';
64
+ import { CodeEditsResultValue, VALID_TIME } from './index';
52
65
 
53
66
  export class IntelligentCompletionsController extends BaseAIMonacoEditorController {
54
67
  public static readonly ID = 'editor.contrib.ai.intelligent.completions';
@@ -83,22 +96,32 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
83
96
  private codeEditsSourceCollection: CodeEditsSourceCollection;
84
97
  private aiNativeContextKey: AINativeContextKey;
85
98
  private rewriteWidget: RewriteWidget | null;
86
- private whenMultiLineEditsVisibleDisposable: Disposable;
99
+ private codeEditsTriggerSignal: IObservableSignal<void>;
100
+ private multiLineEditsIsVisibleObs: IObservable<boolean>;
87
101
 
88
102
  public mount(): IDisposable {
89
103
  this.handlerAlwaysVisiblePreference();
90
104
 
91
105
  this.codeEditsResult = observableValue<CodeEditsResultValue | undefined>(this, undefined);
106
+ this.codeEditsTriggerSignal = observableSignal(this);
92
107
 
93
- this.whenMultiLineEditsVisibleDisposable = new Disposable();
94
108
  this.multiLineDecorationModel = new MultiLineDecorationModel(this.monacoEditor);
95
109
  this.additionsDeletionsDecorationModel = new AdditionsDeletionsDecorationModel(this.monacoEditor);
96
110
  this.aiNativeContextKey = this.injector.get(AINativeContextKey, [this.monacoEditor.contextKeyService]);
97
111
  this.codeEditsSourceCollection = this.injector.get(CodeEditsSourceCollection, [
98
- [LintErrorCodeEditsSource, LineChangeCodeEditsSource],
112
+ [LintErrorCodeEditsSource, LineChangeCodeEditsSource, TypingCodeEditsSource],
99
113
  this.monacoEditor,
100
114
  ]);
101
115
 
116
+ const multiLineEditsIsVisibleKey = new Set([MultiLineEditsIsVisible.raw]);
117
+ this.multiLineEditsIsVisibleObs = observableFromEvent(
118
+ this,
119
+ Event.filter(this.aiNativeContextKey.contextKeyService!.onDidChangeContext, (e: ContextKeyChangeEvent) =>
120
+ e.payload.affectsSome(multiLineEditsIsVisibleKey),
121
+ ),
122
+ () => !!this.aiNativeContextKey.multiLineEditsIsVisible.get(),
123
+ );
124
+
102
125
  this.registerFeature(this.monacoEditor);
103
126
  return this.featureDisposable;
104
127
  }
@@ -253,26 +276,6 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
253
276
  this.additionsDeletionsDecorationModel.updateDeletionsDecoration(wordChanges, range, eol);
254
277
  this.renderRewriteWidget(wordChanges, model, range, insertTextString);
255
278
  }
256
-
257
- if (this.whenMultiLineEditsVisibleDisposable.disposed) {
258
- this.whenMultiLineEditsVisibleDisposable = new Disposable();
259
- }
260
- // 监听当前光标位置的变化,如果超出 range 区域则表示弃用
261
- this.whenMultiLineEditsVisibleDisposable.addDispose(
262
- this.monacoEditor.onDidChangeCursorPosition((event: ICursorPositionChangedEvent) => {
263
- const isVisible = this.aiNativeContextKey.multiLineEditsIsVisible.get();
264
- if (isVisible) {
265
- const position = event.position;
266
- if (position.lineNumber < range.startLineNumber || position.lineNumber > range.endLineNumber) {
267
- runWhenIdle(() => {
268
- this.discard.get();
269
- });
270
- }
271
- } else {
272
- this.whenMultiLineEditsVisibleDisposable.dispose();
273
- }
274
- }),
275
- );
276
279
  }
277
280
 
278
281
  private async renderRewriteWidget(
@@ -335,36 +338,52 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
335
338
  const { range, insertText } = codeEditsResult.items[0];
336
339
  const newCode = insertText;
337
340
  const originCode = this.model.getValueInRange(range);
338
- return (type: keyof Pick<CodeEditsRT, 'isReceive' | 'isDrop' | 'isValid'>) => {
339
- contextBean.reporterEnd({
340
- [type]: true,
341
+ return (type: keyof Pick<CodeEditsRT, 'isReceive' | 'isDrop' | 'isValid'>, defaultValue: boolean = true) => {
342
+ const data = {
343
+ [type]: defaultValue,
341
344
  code: newCode,
342
345
  originCode,
343
- });
346
+ };
347
+
348
+ contextBean.reporterEnd(data);
344
349
  };
345
350
  }
346
351
  });
347
352
 
348
- private lastVisibleTime = derived(this, (reader) => {
349
- const isVisible = this.aiNativeContextKey.multiLineEditsIsVisible.get();
350
- return isVisible ? Date.now() : undefined;
351
- });
352
-
353
- public discard = derived(this, (reader) => {
354
- const lastVisibleTime = this.lastVisibleTime.read(reader);
355
- const report = this.reportData.read(reader);
356
-
357
- // 在可见的情况下超过 750ms 弃用才算有效数据,否则视为取消
358
- if (lastVisibleTime && Date.now() - lastVisibleTime > 750) {
359
- report?.('isDrop');
360
- } else {
361
- report?.('isValid');
362
- }
353
+ public discard = derivedHandleChanges(
354
+ {
355
+ owner: this,
356
+ createEmptyChangeSummary: () => ({ lastVisibleTime: Date.now() }),
357
+ handleChange: (context, changeSummary) => {
358
+ if (context.didChange(this.multiLineEditsIsVisibleObs)) {
359
+ changeSummary.lastVisibleTime = Date.now();
360
+ return this.multiLineEditsIsVisibleObs.get();
361
+ }
362
+ return false;
363
+ },
364
+ equalityComparer: () => false,
365
+ },
366
+ (reader, changeSummary) => {
367
+ this.multiLineEditsIsVisibleObs.read(reader);
368
+
369
+ const lastVisibleTime = changeSummary.lastVisibleTime;
370
+ const report = this.reportData.read(reader);
371
+ let isValid = false;
372
+
373
+ if (lastVisibleTime && Date.now() - lastVisibleTime > VALID_TIME) {
374
+ isValid = true;
375
+ report?.('isDrop');
376
+ } else {
377
+ isValid = false;
378
+ report?.('isValid', false);
379
+ }
363
380
 
364
- this.hide();
365
- });
381
+ this.hide();
382
+ return isValid;
383
+ },
384
+ );
366
385
 
367
- public accept = derived(this, (reader) => {
386
+ public accept = derivedOpts({ owner: this, equalsFn: () => false }, (reader) => {
368
387
  const report = this.reportData.read(reader);
369
388
  report?.('isReceive');
370
389
 
@@ -392,6 +411,10 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
392
411
  this.hide();
393
412
  });
394
413
 
414
+ public trigger(tx: ITransaction): void {
415
+ this.codeEditsTriggerSignal.trigger(tx);
416
+ }
417
+
395
418
  private registerFeature(monacoEditor: ICodeEditor): void {
396
419
  this.featureDisposable.addDispose(
397
420
  Event.any<any>(
@@ -403,14 +426,19 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
403
426
  }),
404
427
  );
405
428
 
406
- const multiLineEditsIsVisibleKey = new Set([MultiLineEditsIsVisible.raw]);
407
- this.featureDisposable.addDispose(this.whenMultiLineEditsVisibleDisposable);
429
+ // 监听当前光标位置的变化,如果超出 range 区域则表示弃用
408
430
  this.featureDisposable.addDispose(
409
- this.aiNativeContextKey.contextKeyService!.onDidChangeContext((e) => {
410
- if (e.payload.affectsSome(multiLineEditsIsVisibleKey)) {
411
- const isVisible = this.aiNativeContextKey.multiLineEditsIsVisible.get();
412
- if (!isVisible) {
413
- this.whenMultiLineEditsVisibleDisposable.dispose();
431
+ this.monacoEditor.onDidChangeCursorPosition((event: ICursorPositionChangedEvent) => {
432
+ const isVisible = this.multiLineEditsIsVisibleObs.get();
433
+ const completionModel = this.codeEditsResult.get();
434
+
435
+ if (isVisible && completionModel) {
436
+ const position = event.position;
437
+ const range = completionModel.items[0].range;
438
+ if (position.lineNumber < range.startLineNumber || position.lineNumber > range.endLineNumber) {
439
+ runWhenIdle(() => {
440
+ this.discard.get();
441
+ });
414
442
  }
415
443
  }
416
444
  }),
@@ -431,16 +459,19 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
431
459
  autorunWithStoreHandleChanges(
432
460
  {
433
461
  createEmptyChangeSummary: () => ({}),
434
- handleChange: (context, changeSummary) => {
462
+ handleChange: (context) => {
435
463
  if (context.didChange(this.codeEditsSourceCollection.codeEditsContextBean)) {
436
464
  // 如果上一次补全结果还在,则不重复请求
437
465
  const isVisible = this.aiNativeContextKey.multiLineEditsIsVisible.get();
438
466
  return !isVisible;
467
+ } else if (context.didChange(this.codeEditsTriggerSignal)) {
468
+ return true;
439
469
  }
440
470
  return false;
441
471
  },
442
472
  },
443
473
  async (reader, _, store) => {
474
+ this.codeEditsTriggerSignal.read(reader);
444
475
  const context = this.codeEditsSourceCollection.codeEditsContextBean.read(reader);
445
476
 
446
477
  const provider = this.intelligentCompletionsRegistry.getCodeEditsProvider();
@@ -7,7 +7,6 @@ import {
7
7
  Disposable,
8
8
  IAIReporter,
9
9
  IDisposable,
10
- MaybePromise,
11
10
  uuid,
12
11
  } from '@opensumi/ide-core-common';
13
12
  import { CancellationTokenSource, ICodeEditor } from '@opensumi/ide-monaco';
@@ -37,14 +36,26 @@ export class CodeEditsContextBean extends Disposable {
37
36
  return this.raw;
38
37
  }
39
38
 
39
+ public get typing() {
40
+ return this.raw.typing;
41
+ }
42
+
40
43
  public get position() {
41
44
  return this.raw.position;
42
45
  }
43
46
 
47
+ public get data() {
48
+ return this.raw.data;
49
+ }
50
+
44
51
  public get token() {
45
52
  return this.source.token;
46
53
  }
47
54
 
55
+ public joinData(data: ICodeEditsContextBean['data']) {
56
+ this.raw.data = { ...this.data, ...data };
57
+ }
58
+
48
59
  public reporterStart() {
49
60
  return this.source.reporterStart();
50
61
  }
@@ -69,8 +80,6 @@ export abstract class BaseCodeEditsSource extends Disposable {
69
80
  private cancellationTokenSource = new CancellationTokenSource();
70
81
  private readonly relationID = observableValue<string | undefined>(this, undefined);
71
82
 
72
- protected abstract doTrigger(...args: any[]): MaybePromise<void>;
73
-
74
83
  public readonly codeEditsContextBean = disposableObservableValue<CodeEditsContextBean | undefined>(this, undefined);
75
84
  public abstract priority: number;
76
85
  public abstract mount(): IDisposable;
@@ -100,9 +109,9 @@ export abstract class BaseCodeEditsSource extends Disposable {
100
109
  });
101
110
  }
102
111
 
103
- protected setBean(bean: ICodeEditsContextBean) {
112
+ protected setBean(bean: Omit<ICodeEditsContextBean, 'position'>) {
104
113
  transaction((tx) => {
105
- const context = new CodeEditsContextBean(bean, this);
114
+ const context = new CodeEditsContextBean({ ...bean, position: this.monacoEditor.getPosition()! }, this);
106
115
  this.codeEditsContextBean.set(context, tx);
107
116
  });
108
117
  }
@@ -112,7 +121,7 @@ export abstract class BaseCodeEditsSource extends Disposable {
112
121
  if (context) {
113
122
  const relationID = this.aiReporter.start(AIServiceType.CodeEdits, {
114
123
  type: AIServiceType.CodeEdits,
115
- actionSource: context?.bean.typing,
124
+ actionSource: context?.typing,
116
125
  });
117
126
 
118
127
  transaction((tx) => {
@@ -168,11 +177,23 @@ export class CodeEditsSourceCollection extends Disposable {
168
177
 
169
178
  for (const source of lastSources) {
170
179
  const value = source.codeEditsContextBean.get();
180
+ if (!value) {
181
+ return;
182
+ }
183
+
184
+ if (!contextBean) {
185
+ contextBean = value;
186
+ }
171
187
 
172
- if (value && value.priority >= highestPriority) {
188
+ if (value.priority >= highestPriority) {
173
189
  highestPriority = value.priority;
190
+
191
+ value.joinData(contextBean.data);
174
192
  contextBean = value;
175
193
  }
194
+
195
+ // 将多个 source 的 data 合并到一起
196
+ contextBean.joinData(value.data);
176
197
  }
177
198
 
178
199
  transaction((tx) => {