@opensumi/ide-ai-native 3.7.1-next-1739439717.0 → 3.7.1-next-1739448958.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 (119) 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 +55 -14
  8. package/lib/browser/ai-core.contribution.js.map +1 -1
  9. package/lib/browser/contrib/intelligent-completions/intelligent-completions.contribution.d.ts +1 -0
  10. package/lib/browser/contrib/intelligent-completions/intelligent-completions.contribution.d.ts.map +1 -1
  11. package/lib/browser/contrib/intelligent-completions/intelligent-completions.contribution.js +26 -4
  12. package/lib/browser/contrib/intelligent-completions/intelligent-completions.contribution.js.map +1 -1
  13. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.d.ts +3 -1
  14. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.d.ts.map +1 -1
  15. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js +9 -1
  16. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js.map +1 -1
  17. package/lib/browser/contrib/intelligent-completions/source/base.d.ts +1 -2
  18. package/lib/browser/contrib/intelligent-completions/source/base.d.ts.map +1 -1
  19. package/lib/browser/contrib/intelligent-completions/source/base.js.map +1 -1
  20. package/lib/browser/contrib/intelligent-completions/source/line-change.source.d.ts +2 -3
  21. package/lib/browser/contrib/intelligent-completions/source/line-change.source.d.ts.map +1 -1
  22. package/lib/browser/contrib/intelligent-completions/source/line-change.source.js +51 -21
  23. package/lib/browser/contrib/intelligent-completions/source/line-change.source.js.map +1 -1
  24. package/lib/browser/index.js +1 -1
  25. package/lib/browser/index.js.map +1 -1
  26. package/lib/browser/layout/ai-layout.d.ts.map +1 -1
  27. package/lib/browser/layout/ai-layout.js +2 -2
  28. package/lib/browser/layout/ai-layout.js.map +1 -1
  29. package/lib/browser/model/enhanceDecorationsCollection.d.ts +14 -10
  30. package/lib/browser/model/enhanceDecorationsCollection.d.ts.map +1 -1
  31. package/lib/browser/model/enhanceDecorationsCollection.js +42 -53
  32. package/lib/browser/model/enhanceDecorationsCollection.js.map +1 -1
  33. package/lib/browser/types.d.ts +2 -1
  34. package/lib/browser/types.d.ts.map +1 -1
  35. package/lib/browser/widget/inline-chat/inline-chat-editor.controller.d.ts +2 -1
  36. package/lib/browser/widget/inline-chat/inline-chat-editor.controller.d.ts.map +1 -1
  37. package/lib/browser/widget/inline-chat/inline-chat-editor.controller.js +13 -41
  38. package/lib/browser/widget/inline-chat/inline-chat-editor.controller.js.map +1 -1
  39. package/lib/browser/widget/inline-chat/inline-chat.feature.registry.d.ts +3 -13
  40. package/lib/browser/widget/inline-chat/inline-chat.feature.registry.d.ts.map +1 -1
  41. package/lib/browser/widget/inline-chat/inline-chat.feature.registry.js +24 -72
  42. package/lib/browser/widget/inline-chat/inline-chat.feature.registry.js.map +1 -1
  43. package/lib/browser/widget/inline-chat/inline-chat.service.d.ts +1 -6
  44. package/lib/browser/widget/inline-chat/inline-chat.service.d.ts.map +1 -1
  45. package/lib/browser/widget/inline-chat/inline-chat.service.js +5 -19
  46. package/lib/browser/widget/inline-chat/inline-chat.service.js.map +1 -1
  47. package/lib/browser/widget/inline-chat/inline-content-widget.d.ts +2 -5
  48. package/lib/browser/widget/inline-chat/inline-content-widget.d.ts.map +1 -1
  49. package/lib/browser/widget/inline-chat/inline-content-widget.js +17 -42
  50. package/lib/browser/widget/inline-chat/inline-content-widget.js.map +1 -1
  51. package/lib/browser/widget/inline-diff/inline-diff-previewer.d.ts +22 -5
  52. package/lib/browser/widget/inline-diff/inline-diff-previewer.d.ts.map +1 -1
  53. package/lib/browser/widget/inline-diff/inline-diff-previewer.js +61 -30
  54. package/lib/browser/widget/inline-diff/inline-diff-previewer.js.map +1 -1
  55. package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts +8 -12
  56. package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts.map +1 -1
  57. package/lib/browser/widget/inline-diff/inline-diff.controller.js +68 -96
  58. package/lib/browser/widget/inline-diff/inline-diff.controller.js.map +1 -1
  59. package/lib/browser/widget/inline-hint/inline-hint.controller.d.ts +0 -1
  60. package/lib/browser/widget/inline-hint/inline-hint.controller.d.ts.map +1 -1
  61. package/lib/browser/widget/inline-hint/inline-hint.controller.js +0 -5
  62. package/lib/browser/widget/inline-hint/inline-hint.controller.js.map +1 -1
  63. package/lib/browser/widget/inline-input/inline-input-widget.d.ts +12 -2
  64. package/lib/browser/widget/inline-input/inline-input-widget.d.ts.map +1 -1
  65. package/lib/browser/widget/inline-input/inline-input-widget.js +26 -18
  66. package/lib/browser/widget/inline-input/inline-input-widget.js.map +1 -1
  67. package/lib/browser/widget/inline-input/inline-input.controller.d.ts +14 -6
  68. package/lib/browser/widget/inline-input/inline-input.controller.d.ts.map +1 -1
  69. package/lib/browser/widget/inline-input/inline-input.controller.js +320 -169
  70. package/lib/browser/widget/inline-input/inline-input.controller.js.map +1 -1
  71. package/lib/browser/widget/inline-input/inline-input.module.less +4 -0
  72. package/lib/browser/widget/inline-input/inline-input.service.d.ts +19 -7
  73. package/lib/browser/widget/inline-input/inline-input.service.d.ts.map +1 -1
  74. package/lib/browser/widget/inline-input/inline-input.service.js +72 -12
  75. package/lib/browser/widget/inline-input/inline-input.service.js.map +1 -1
  76. package/lib/browser/widget/inline-input/model.d.ts +34 -0
  77. package/lib/browser/widget/inline-input/model.d.ts.map +1 -0
  78. package/lib/browser/widget/inline-input/model.js +63 -0
  79. package/lib/browser/widget/inline-input/model.js.map +1 -0
  80. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts +8 -19
  81. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts.map +1 -1
  82. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js +48 -41
  83. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js.map +1 -1
  84. package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts +17 -4
  85. package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts.map +1 -1
  86. package/lib/browser/widget/inline-stream-diff/live-preview.component.js +37 -5
  87. package/lib/browser/widget/inline-stream-diff/live-preview.component.js.map +1 -1
  88. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts +7 -11
  89. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts.map +1 -1
  90. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js +33 -77
  91. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js.map +1 -1
  92. package/package.json +21 -21
  93. package/src/browser/ai-core.contextkeys.ts +3 -0
  94. package/src/browser/ai-core.contribution.ts +63 -16
  95. package/src/browser/contrib/intelligent-completions/intelligent-completions.contribution.ts +29 -8
  96. package/src/browser/contrib/intelligent-completions/intelligent-completions.controller.ts +13 -1
  97. package/src/browser/contrib/intelligent-completions/source/base.ts +0 -2
  98. package/src/browser/contrib/intelligent-completions/source/line-change.source.ts +79 -24
  99. package/src/browser/index.ts +2 -2
  100. package/src/browser/layout/ai-layout.tsx +5 -2
  101. package/src/browser/model/enhanceDecorationsCollection.ts +62 -77
  102. package/src/browser/types.ts +2 -2
  103. package/src/browser/widget/inline-chat/inline-chat-editor.controller.ts +21 -56
  104. package/src/browser/widget/inline-chat/inline-chat.feature.registry.ts +23 -90
  105. package/src/browser/widget/inline-chat/inline-chat.service.ts +2 -19
  106. package/src/browser/widget/inline-chat/inline-content-widget.tsx +14 -71
  107. package/src/browser/widget/inline-diff/inline-diff-previewer.ts +87 -32
  108. package/src/browser/widget/inline-diff/inline-diff.controller.ts +90 -114
  109. package/src/browser/widget/inline-hint/inline-hint.controller.ts +1 -7
  110. package/src/browser/widget/inline-input/inline-input-widget.tsx +34 -12
  111. package/src/browser/widget/inline-input/inline-input.controller.ts +453 -247
  112. package/src/browser/widget/inline-input/inline-input.module.less +4 -0
  113. package/src/browser/widget/inline-input/inline-input.service.ts +92 -13
  114. package/src/browser/widget/inline-input/model.ts +74 -0
  115. package/src/browser/widget/inline-stream-diff/inline-stream-diff.handler.tsx +58 -69
  116. package/src/browser/widget/inline-stream-diff/live-preview.component.tsx +45 -6
  117. package/src/browser/widget/inline-stream-diff/live-preview.decoration.tsx +40 -112
  118. package/lib/browser/model/styles.module.less +0 -7
  119. package/src/browser/model/styles.module.less +0 -7
@@ -1,34 +1,43 @@
1
1
  import { Injectable } from '@opensumi/di';
2
- import { IAIInlineChatService } from '@opensumi/ide-core-browser';
3
2
  import {
4
3
  CancelResponse,
5
4
  Disposable,
6
5
  Event,
6
+ FRAME_FIVE,
7
7
  FRAME_THREE,
8
+ IAIReporter,
8
9
  IDisposable,
9
- InlineChatFeatureRegistryToken,
10
+ IEventBus,
10
11
  ReplyResponse,
11
12
  RunOnceScheduler,
13
+ localize,
12
14
  } from '@opensumi/ide-core-common';
13
- import { ICodeEditor } from '@opensumi/ide-monaco';
15
+ import { EditorGroupCloseEvent } from '@opensumi/ide-editor/lib/browser';
14
16
  import * as monaco from '@opensumi/ide-monaco';
17
+ import { ICodeEditor } from '@opensumi/ide-monaco';
18
+ import {
19
+ IObservable,
20
+ ISettableObservable,
21
+ observableFromEvent,
22
+ observableValue,
23
+ } from '@opensumi/ide-monaco/lib/common/observable';
24
+ import { MessageService } from '@opensumi/ide-overlay/lib/browser/message.service';
15
25
  import { EditOperation } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/editOperation';
16
26
  import { LineRange } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/lineRange';
17
27
  import { ModelDecorationOptions } from '@opensumi/monaco-editor-core/esm/vs/editor/common/model/textModel';
18
28
 
19
29
  import { AINativeContextKey } from '../../ai-core.contextkeys';
20
30
  import { BaseAIMonacoEditorController } from '../../contrib/base';
21
- import { LanguageParserService } from '../../languages/service';
22
31
  import { ERunStrategy } from '../../types';
23
32
  import { InlineChatController } from '../inline-chat/inline-chat-controller';
24
- import { InlineChatFeatureRegistry } from '../inline-chat/inline-chat.feature.registry';
25
- import { AIInlineChatService, EInlineChatStatus, EResultKind } from '../inline-chat/inline-chat.service';
33
+ import { EInlineChatStatus, EResultKind } from '../inline-chat/inline-chat.service';
34
+ import { InlineDiffController } from '../inline-diff';
26
35
  import { InlineInputPreviewDecorationID } from '../internal.type';
27
36
 
28
- import { InlineInputChatWidget } from './inline-input-widget';
37
+ import { InlineInputWidget } from './inline-input-widget';
29
38
  import styles from './inline-input.module.less';
30
- import { InlineInputChatService } from './inline-input.service';
31
-
39
+ import { InlineInputService } from './inline-input.service';
40
+ import { InlineInputWidgetStoreInEmptyLine, InlineInputWidgetStoreInSelection } from './model';
32
41
  @Injectable()
33
42
  export class InlineInputController extends BaseAIMonacoEditorController {
34
43
  public static readonly ID = 'editor.contrib.ai.inline.input';
@@ -37,286 +46,483 @@ export class InlineInputController extends BaseAIMonacoEditorController {
37
46
  return editor.getContribution<InlineInputController>(InlineInputController.ID);
38
47
  }
39
48
 
40
- private get inlineInputChatService(): InlineInputChatService {
41
- return this.injector.get(InlineInputChatService);
49
+ private get inlineInputService(): InlineInputService {
50
+ return this.injector.get(InlineInputService);
42
51
  }
43
52
 
44
- private get inlineChatFeatureRegistry(): InlineChatFeatureRegistry {
45
- return this.injector.get(InlineChatFeatureRegistryToken);
53
+ private get aiReporter(): IAIReporter {
54
+ return this.injector.get(IAIReporter);
46
55
  }
47
56
 
48
- private get languageParserService(): LanguageParserService {
49
- return this.injector.get(LanguageParserService);
57
+ private get eventBus(): IEventBus {
58
+ return this.injector.get(IEventBus);
50
59
  }
51
60
 
52
- private get inlineChatService(): AIInlineChatService {
53
- return this.injector.get(IAIInlineChatService);
61
+ private get messageService(): MessageService {
62
+ return this.injector.get(MessageService);
54
63
  }
55
64
 
65
+ private inlineDiffController: InlineDiffController;
66
+ private inputDisposable: Disposable;
67
+ private aiNativeContextKey: AINativeContextKey;
68
+
69
+ private inputValue: ISettableObservable<string>;
70
+ private modelChangeObs: IObservable<monaco.editor.ITextModel, unknown>;
71
+ private inlineInputWidgetStore: Map<
72
+ string,
73
+ InlineInputWidgetStoreInEmptyLine | InlineInputWidgetStoreInSelection | null
74
+ >;
75
+
56
76
  mount(): IDisposable {
57
- return this.registerInlineInputFeature(this.monacoEditor);
77
+ this.inputDisposable = new Disposable();
78
+ this.aiNativeContextKey = this.injector.get(AINativeContextKey, [this.monacoEditor.contextKeyService]);
79
+ this.inlineDiffController = InlineDiffController.get(this.monacoEditor)!;
80
+
81
+ this.inputValue = observableValue(this, '');
82
+ this.modelChangeObs = observableFromEvent<monaco.editor.ITextModel>(
83
+ this,
84
+ this.monacoEditor.onDidChangeModel,
85
+ () => this.monacoEditor.getModel()!,
86
+ );
87
+ this.inlineInputWidgetStore = new Map();
88
+
89
+ this.featureDisposable.addDispose(
90
+ /**
91
+ * 如果在流式过程中,直接关闭了当前文件,则需要销毁 diff previewer 并隐藏 input,恢复原始代码
92
+ */
93
+ this.eventBus.on(EditorGroupCloseEvent, (e: EditorGroupCloseEvent) => {
94
+ const isStreaming = this.aiNativeContextKey.inlineInputWidgetIsStreaming.get();
95
+ if (!isStreaming) {
96
+ return;
97
+ }
98
+
99
+ const resource = e.payload.resource.uri.toString();
100
+ const currentUri = this.monacoEditor.getModel()?.uri.toString();
101
+
102
+ if (currentUri === resource) {
103
+ this.hideInput();
104
+
105
+ const message = localize('aiNative.inline.chat.generating.canceled');
106
+ if (message) {
107
+ this.messageService.info(message);
108
+ }
109
+ }
110
+ }),
111
+ );
112
+
113
+ this.featureDisposable.addDispose(
114
+ this.inlineInputService.onHidden(() => {
115
+ this.hideInput();
116
+ }),
117
+ );
118
+
119
+ this.featureDisposable.addDispose(
120
+ this.inlineInputService.onInteractiveInputVisibleInPosition(async (position) => {
121
+ if (position) {
122
+ this.showInputInEmptyLine(position, this.monacoEditor);
123
+ } else {
124
+ setTimeout(() => this.monacoEditor.focus(), 0);
125
+ }
126
+ }),
127
+ );
128
+
129
+ this.featureDisposable.addDispose(
130
+ this.inlineInputService.onInteractiveInputVisibleInSelection((selection) => {
131
+ if (!selection) {
132
+ return;
133
+ }
134
+
135
+ this.showInputInSelection(selection, this.monacoEditor);
136
+ }),
137
+ );
138
+
139
+ this.featureDisposable.addDispose(this.inputDisposable);
140
+
141
+ return this.featureDisposable;
142
+ }
143
+
144
+ override cancelToken() {
145
+ super.cancelToken();
146
+ this.aiNativeContextKey.inlineInputWidgetIsStreaming.set(false);
58
147
  }
59
148
 
60
- private async doRequestReadable(
61
- value: string,
62
- widget: InlineInputChatWidget,
63
- monacoEditor: ICodeEditor,
64
- inputDisposable: Disposable,
65
- decoration: monaco.editor.IEditorDecorationsCollection,
66
- hideInput: () => void,
67
- ): Promise<void> {
68
- const handler = this.inlineChatFeatureRegistry.getInteractiveInputHandler();
149
+ private hideInput() {
150
+ this.inlineInputWidgetStore.delete(this.monacoEditor.getModel()!.id);
151
+ this.inputDisposable.dispose();
152
+ }
153
+
154
+ private async showInputInEmptyLine(position: monaco.IPosition, monacoEditor: ICodeEditor, defaultValue?: string) {
69
155
  const model = monacoEditor.getModel();
70
156
 
71
- if (!handler || !model) {
157
+ if (!model) {
72
158
  return;
73
159
  }
74
160
 
75
- widget.launchChatStatus(EInlineChatStatus.THINKING);
161
+ const selection = monacoEditor.getSelection();
162
+ if (selection && !selection.isEmpty()) {
163
+ return;
164
+ }
76
165
 
77
- const strategy = await this.inlineChatFeatureRegistry.getInteractiveInputStrategyHandler()(monacoEditor, value);
166
+ this.inputValue.set(defaultValue || '', undefined);
167
+ this.inlineInputWidgetStore.set(model.id, new InlineInputWidgetStoreInEmptyLine(position, defaultValue));
78
168
 
79
- if (strategy === ERunStrategy.EXECUTE && handler.execute) {
80
- handler.execute(monacoEditor, value, this.token);
81
- widget.launchChatStatus(EInlineChatStatus.DONE);
82
- hideInput();
83
- return;
169
+ if (this.inputDisposable) {
170
+ this.inputDisposable.dispose();
171
+ this.inputDisposable = new Disposable();
84
172
  }
85
173
 
86
- if (strategy === ERunStrategy.PREVIEW && handler.providePreviewStrategy) {
87
- const previewResponse = await handler.providePreviewStrategy(monacoEditor, value, this.token);
88
-
89
- if (CancelResponse.is(previewResponse)) {
90
- widget.launchChatStatus(EInlineChatStatus.READY);
91
- hideInput();
92
- return;
93
- }
94
-
95
- if (InlineChatController.is(previewResponse)) {
96
- const controller = previewResponse as InlineChatController;
97
-
98
- let latestContent: string | undefined;
99
- const schedulerEdit: RunOnceScheduler = this.registerDispose(
100
- new RunOnceScheduler(() => {
101
- const range = decoration.getRange(0);
102
- if (range && latestContent) {
103
- model.pushEditOperations(null, [EditOperation.replace(range, latestContent)], () => null);
104
- }
105
- }, 16 * 12.5),
106
- );
107
-
108
- inputDisposable.addDispose([
109
- controller.onData(async (data) => {
110
- if (!ReplyResponse.is(data)) {
111
- return;
112
- }
113
-
114
- latestContent = data.message;
115
-
116
- if (!schedulerEdit.isScheduled()) {
117
- schedulerEdit.schedule();
118
- }
119
- }),
120
- controller.onError((error) => {
121
- widget.launchChatStatus(EInlineChatStatus.ERROR);
122
- }),
123
- controller.onAbort(() => {
124
- widget.launchChatStatus(EInlineChatStatus.READY);
125
- }),
126
- controller.onEnd(() => {
127
- model.pushStackElement();
128
- widget.launchChatStatus(EInlineChatStatus.DONE);
129
- }),
130
- ]);
131
-
132
- controller.listen();
133
- }
174
+ const collection = monacoEditor.createDecorationsCollection();
175
+ const inlineInputWidget = this.injector.get(InlineInputWidget, [monacoEditor, this.inputValue.get()]);
176
+
177
+ // 仅在空行情况下增加装饰逻辑
178
+ collection.append([
179
+ {
180
+ range: monaco.Range.fromPositions(position),
181
+ options: ModelDecorationOptions.register({
182
+ description: InlineInputPreviewDecorationID,
183
+ isWholeLine: true,
184
+ className: styles.input_decoration_readable_container,
185
+ inlineClassName: styles.inline_chat_inserted_range,
186
+ }),
187
+ },
188
+ ]);
189
+
190
+ const decorationRange = collection.getRange(0);
191
+ let preLineRange: LineRange;
192
+ if (decorationRange) {
193
+ preLineRange = LineRange.fromRange(decorationRange);
194
+ inlineInputWidget.show({ position: decorationRange.getStartPosition() });
195
+ this.aiNativeContextKey.inlineInputWidgetIsVisible.set(true);
134
196
  }
135
- }
136
197
 
137
- private registerInlineInputFeature(monacoEditor: ICodeEditor): IDisposable {
138
- const inputDisposable = new Disposable();
139
- const aiNativeContextKey = this.injector.get(AINativeContextKey, [monacoEditor.contextKeyService]);
198
+ this.inputDisposable.addDispose(
199
+ inlineInputWidget.onDispose(() => {
200
+ this.cancelToken();
201
+ collection.clear();
202
+ this.aiNativeContextKey.inlineInputWidgetIsVisible.set(false);
203
+ }),
204
+ );
140
205
 
141
- const hideInput = () => {
142
- inputDisposable.dispose();
143
- };
206
+ this.inputDisposable.addDispose(
207
+ inlineInputWidget.onValueChange((value) => {
208
+ this.inputValue.set(value, undefined);
144
209
 
145
- this.featureDisposable.addDispose(
146
- this.inlineInputChatService.onInteractiveInputVisibleInPosition((position) => {
147
- hideInput();
148
- if (position) {
149
- showInput(position, monacoEditor);
150
- } else {
151
- setTimeout(() => {
152
- monacoEditor.focus();
153
- }, 0);
210
+ const storeData = this.inlineInputWidgetStore.get(model.id);
211
+ if (storeData instanceof InlineInputWidgetStoreInEmptyLine) {
212
+ storeData.setValue(value);
154
213
  }
155
214
  }),
156
215
  );
157
216
 
158
- const showInput = async (position: monaco.Position, monacoEditor: ICodeEditor) => {
159
- this.featureDisposable.addDispose(
160
- monacoEditor.onWillChangeModel(() => {
161
- hideInput();
162
- }),
163
- );
217
+ this.inputDisposable.addDispose(
218
+ inlineInputWidget.onResultClick(async (kind: EResultKind) => {
219
+ const clear = () => {
220
+ const curPosi = collection.getRange(0)!;
221
+
222
+ model.pushStackElement();
223
+ model.pushEditOperations(null, [EditOperation.delete(curPosi)], () => null);
224
+ model.pushStackElement();
225
+ };
226
+
227
+ switch (kind) {
228
+ case EResultKind.ACCEPT:
229
+ this.hideInput();
230
+ break;
231
+ case EResultKind.DISCARD:
232
+ clear();
233
+ this.hideInput();
234
+ break;
235
+ case EResultKind.REGENERATE:
236
+ clear();
237
+ requestAnimationFrame(() => {
238
+ /**
239
+ * 避免在重新生成的时候,因为光标移动,导致 input 的位置不正确
240
+ */
241
+ const curPosi = collection.getRange(0)!;
242
+ const curPosition = curPosi.getStartPosition();
243
+ this.showInputInEmptyLine(curPosition, monacoEditor, this.inputValue.get());
244
+ });
245
+ break;
164
246
 
165
- const model = monacoEditor.getModel();
166
- if (!model) {
167
- return;
168
- }
247
+ default:
248
+ break;
249
+ }
250
+ }),
251
+ );
169
252
 
170
- /**
171
- * 只有当前编辑器的光标聚焦,才会展示
172
- * 用于解决多栏的情况下,同时打开多个 input 的问题
173
- */
174
- const hasFocus = monacoEditor.hasTextFocus();
175
- if (!hasFocus) {
176
- return;
177
- }
178
-
179
- const selection = monacoEditor.getSelection();
180
- if (selection && selection.startLineNumber !== selection.endLineNumber) {
181
- return;
182
- }
183
-
184
- const inlineInputChatWidget = this.injector.get(InlineInputChatWidget, [monacoEditor]);
185
-
186
- const collection = monacoEditor.createDecorationsCollection();
187
- const isEmptyLine = !monacoEditor.getModel()?.getLineContent(position.lineNumber).trim();
188
-
189
- if (!isEmptyLine) {
190
- // 根据光标位置自动检测并选中临近的代码块
191
- const cursorPosition = monacoEditor.getPosition();
192
- const editorModel = monacoEditor.getModel();
193
- const cursor = editorModel?.getOffsetAt(cursorPosition!);
194
- const language = editorModel?.getLanguageId();
195
- const parser = this.languageParserService.createParser(language!);
196
- const codeBlock = await parser?.findNearestCodeBlockWithPosition(editorModel?.getValue() || '', cursor!);
197
-
198
- if (codeBlock) {
199
- const selection = new monaco.Selection(
200
- codeBlock.range.start.line + 1,
201
- codeBlock.range.start.character,
202
- codeBlock.range.end.line + 1,
203
- codeBlock.range.end.character,
204
- );
205
- monacoEditor.setSelection(selection);
206
- } else {
207
- // 选中当前行
208
- monacoEditor.setSelection(new monaco.Selection(position.lineNumber, 1, position.lineNumber, Infinity));
253
+ this.inputDisposable.addDispose(
254
+ Event.debounce(
255
+ collection.onDidChange.bind(collection),
256
+ () => {},
257
+ FRAME_THREE,
258
+ )(() => {
259
+ if (!collection.getRange(0)) {
260
+ return;
209
261
  }
210
- this.inlineChatService.launchInputVisible(true);
211
- return;
212
- }
213
-
214
- // 仅在空行情况下增加装饰逻辑
215
- collection.append([
216
- {
217
- range: monaco.Range.fromPositions(position),
218
- options: ModelDecorationOptions.register({
219
- description: InlineInputPreviewDecorationID,
220
- isWholeLine: true,
221
- className: styles.input_decoration_readable_container,
222
- inlineClassName: styles.inline_chat_inserted_range,
223
- }),
224
- },
225
- ]);
226
-
227
- const decorationRange = collection.getRange(0);
228
- let preLineRange: LineRange;
229
- if (decorationRange) {
230
- preLineRange = LineRange.fromRange(decorationRange);
231
- inlineInputChatWidget.show({ position: decorationRange.getStartPosition() });
232
- }
233
- aiNativeContextKey.inlineInputWidgetIsVisible.set(true);
234
-
235
- inputDisposable.addDispose(
236
- inlineInputChatWidget.onDispose(() => {
262
+
263
+ const range = collection.getRange(0)!;
264
+ const curLineRange = LineRange.fromRange(range);
265
+ if (!preLineRange.equals(curLineRange)) {
266
+ inlineInputWidget.setOptions({
267
+ position: range.getStartPosition(),
268
+ });
269
+
270
+ inlineInputWidget.layoutContentWidget();
271
+ }
272
+ preLineRange = curLineRange;
273
+ }),
274
+ );
275
+
276
+ this.inputDisposable.addDispose(
277
+ inlineInputWidget.onClose(() => {
278
+ const isStreaming = this.aiNativeContextKey.inlineInputWidgetIsStreaming.get();
279
+ if (isStreaming) {
237
280
  this.cancelToken();
238
- collection.clear();
239
- aiNativeContextKey.inlineInputWidgetIsVisible.set(false);
240
- }),
241
- );
242
-
243
- inputDisposable.addDispose(
244
- inlineInputChatWidget.onResultClick(async (kind: EResultKind) => {
245
- const clear = () => {
246
- const curPosi = collection.getRange(0)!;
247
-
248
- model.pushStackElement();
249
- model.pushEditOperations(null, [EditOperation.delete(curPosi)], () => null);
250
- model.pushStackElement();
251
- };
252
-
253
- switch (kind) {
254
- case EResultKind.ACCEPT:
255
- hideInput();
256
- break;
257
- case EResultKind.DISCARD:
258
- clear();
259
- hideInput();
260
- break;
261
- case EResultKind.REGENERATE:
262
- clear();
263
- await this.doRequestReadable(
264
- inlineInputChatWidget.interactiveInputValue,
265
- inlineInputChatWidget,
266
- monacoEditor,
267
- inputDisposable,
268
- collection,
269
- hideInput,
270
- );
271
- break;
272
-
273
- default:
274
- break;
275
- }
276
- }),
277
- );
278
-
279
- inputDisposable.addDispose(
280
- Event.debounce(
281
- collection.onDidChange.bind(collection),
282
- () => {},
283
- FRAME_THREE,
284
- )(() => {
285
- if (!collection.getRange(0)) {
281
+ } else {
282
+ this.hideInput();
283
+ }
284
+ }),
285
+ );
286
+
287
+ this.inputDisposable.addDispose(
288
+ inlineInputWidget.onSend(async (value) => {
289
+ monacoEditor.focus();
290
+
291
+ const handler = this.inlineInputService.getInteractiveInputHandler();
292
+ const model = monacoEditor.getModel();
293
+
294
+ if (!handler || !model) {
295
+ return;
296
+ }
297
+
298
+ inlineInputWidget.launchChatStatus(EInlineChatStatus.THINKING);
299
+
300
+ const strategy = await this.inlineInputService.getInteractiveInputStrategyHandler()(monacoEditor, value);
301
+ const selection = monaco.Selection.fromPositions(position);
302
+
303
+ if (strategy === ERunStrategy.EXECUTE && handler.execute) {
304
+ handler.execute(monacoEditor, selection, value, this.token);
305
+ inlineInputWidget.launchChatStatus(EInlineChatStatus.DONE);
306
+ this.hideInput();
307
+ return;
308
+ }
309
+
310
+ if (strategy === ERunStrategy.PREVIEW && handler.providePreviewStrategy) {
311
+ const previewResponse = await handler.providePreviewStrategy(monacoEditor, selection, value, this.token);
312
+
313
+ if (CancelResponse.is(previewResponse)) {
314
+ inlineInputWidget.launchChatStatus(EInlineChatStatus.READY);
315
+ this.hideInput();
286
316
  return;
287
317
  }
288
318
 
289
- const range = collection.getRange(0)!;
290
- const curLineRange = LineRange.fromRange(range);
291
- if (!preLineRange.equals(curLineRange)) {
292
- inlineInputChatWidget.setOptions({
293
- position: range.getStartPosition(),
294
- });
295
-
296
- inlineInputChatWidget.layoutContentWidget();
319
+ if (InlineChatController.is(previewResponse)) {
320
+ const controller = previewResponse as InlineChatController;
321
+
322
+ let latestContent: string | undefined;
323
+ const schedulerEdit: RunOnceScheduler = this.registerDispose(
324
+ new RunOnceScheduler(() => {
325
+ const range = collection.getRange(0);
326
+ if (range && latestContent) {
327
+ model.pushEditOperations(null, [EditOperation.replace(range, latestContent)], () => null);
328
+ }
329
+ }, FRAME_FIVE),
330
+ );
331
+
332
+ this.inputDisposable.addDispose([
333
+ controller.onData(async (data) => {
334
+ if (!ReplyResponse.is(data)) {
335
+ return;
336
+ }
337
+
338
+ this.aiNativeContextKey.inlineInputWidgetIsStreaming.set(true);
339
+ latestContent = data.message;
340
+
341
+ if (!schedulerEdit.isScheduled()) {
342
+ schedulerEdit.schedule();
343
+ }
344
+ }),
345
+ controller.onError((error) => {
346
+ this.aiNativeContextKey.inlineInputWidgetIsStreaming.set(false);
347
+ inlineInputWidget.launchChatStatus(EInlineChatStatus.READY);
348
+ }),
349
+ controller.onAbort(() => {
350
+ this.aiNativeContextKey.inlineInputWidgetIsStreaming.set(false);
351
+ model.pushStackElement();
352
+ inlineInputWidget.launchChatStatus(EInlineChatStatus.DONE);
353
+ }),
354
+ controller.onEnd(() => {
355
+ this.aiNativeContextKey.inlineInputWidgetIsStreaming.set(false);
356
+ model.pushStackElement();
357
+ inlineInputWidget.launchChatStatus(EInlineChatStatus.DONE);
358
+ }),
359
+ ]);
360
+
361
+ controller.listen();
297
362
  }
298
- preLineRange = curLineRange;
363
+ }
364
+ }),
365
+ );
366
+
367
+ this.inputDisposable.addDispose(inlineInputWidget);
368
+ }
369
+
370
+ private async showInputInSelection(selection: monaco.Selection, monacoEditor: ICodeEditor, defaultValue?: string) {
371
+ if (this.inputDisposable) {
372
+ this.inputDisposable.dispose();
373
+ this.inputDisposable = new Disposable();
374
+ }
375
+
376
+ const model = monacoEditor.getModel();
377
+ if (!model) {
378
+ return;
379
+ }
380
+
381
+ const decorationsCollection = monacoEditor.createDecorationsCollection();
382
+ decorationsCollection.set([
383
+ {
384
+ range: monaco.Range.fromPositions(
385
+ { lineNumber: selection.startLineNumber, column: 1 },
386
+ {
387
+ lineNumber: selection.endLineNumber,
388
+ column: monacoEditor.getModel()!.getLineMaxColumn(selection.endLineNumber),
389
+ },
390
+ ),
391
+ options: ModelDecorationOptions.register({
392
+ description: InlineInputPreviewDecorationID,
393
+ isWholeLine: true,
394
+ className: styles.input_decoration_pending_container,
299
395
  }),
300
- );
396
+ },
397
+ ]);
398
+ const decorationSelection = monaco.Selection.fromRange(
399
+ decorationsCollection.getRange(0)!,
400
+ selection.getDirection(),
401
+ );
301
402
 
302
- inputDisposable.addDispose(
303
- inlineInputChatWidget.onInteractiveInputValue(async (value) => {
304
- await this.doRequestReadable(
305
- value,
306
- inlineInputChatWidget,
403
+ this.inputValue.set(defaultValue || '', undefined);
404
+ this.inlineInputWidgetStore.set(model.id, new InlineInputWidgetStoreInSelection(decorationSelection, defaultValue));
405
+
406
+ const inlineInputWidget = this.injector.get(InlineInputWidget, [monacoEditor, this.inputValue.get()]);
407
+ inlineInputWidget.show({ selection: decorationSelection });
408
+
409
+ this.aiNativeContextKey.inlineInputWidgetIsVisible.set(true);
410
+
411
+ this.inputDisposable.addDispose(
412
+ inlineInputWidget.onDispose(() => {
413
+ this.cancelToken();
414
+ decorationsCollection.clear();
415
+ this.aiNativeContextKey.inlineInputWidgetIsVisible.set(false);
416
+ }),
417
+ );
418
+
419
+ this.inputDisposable.addDispose(
420
+ inlineInputWidget.onValueChange((value) => {
421
+ this.inputValue.set(value, undefined);
422
+
423
+ const storeData = this.inlineInputWidgetStore.get(model.id);
424
+ if (storeData instanceof InlineInputWidgetStoreInSelection) {
425
+ storeData.setValue(value);
426
+ }
427
+ }),
428
+ );
429
+
430
+ this.inputDisposable.addDispose(
431
+ inlineInputWidget.onClose(() => {
432
+ const isStreaming = this.aiNativeContextKey.inlineInputWidgetIsStreaming.get();
433
+ if (isStreaming) {
434
+ this.cancelToken();
435
+ } else {
436
+ this.hideInput();
437
+ }
438
+ }),
439
+ );
440
+
441
+ this.inputDisposable.addDispose(
442
+ inlineInputWidget.onSend(async (value) => {
443
+ monacoEditor.focus();
444
+
445
+ const handler = this.inlineInputService.getInteractiveInputHandler();
446
+
447
+ if (!handler) {
448
+ return;
449
+ }
450
+
451
+ inlineInputWidget.launchChatStatus(EInlineChatStatus.THINKING);
452
+
453
+ const strategy = await this.inlineInputService.getInteractiveInputStrategyHandler()(monacoEditor, value);
454
+
455
+ if (strategy === ERunStrategy.PREVIEW && handler.providePreviewStrategy) {
456
+ const previewResponse = await handler.providePreviewStrategy(
307
457
  monacoEditor,
308
- inputDisposable,
309
- collection,
310
- hideInput,
458
+ decorationSelection,
459
+ value,
460
+ this.token,
311
461
  );
312
- }),
313
- );
314
462
 
315
- inputDisposable.addDispose(inlineInputChatWidget);
316
- };
463
+ if (CancelResponse.is(previewResponse)) {
464
+ decorationsCollection.clear();
465
+ this.aiNativeContextKey.inlineInputWidgetIsStreaming.set(false);
466
+ inlineInputWidget.launchChatStatus(EInlineChatStatus.DONE);
467
+ return;
468
+ }
317
469
 
318
- this.featureDisposable.addDispose(inputDisposable);
470
+ if (InlineChatController.is(previewResponse)) {
471
+ const chatResponse = previewResponse;
472
+
473
+ this.inputDisposable.addDispose([
474
+ chatResponse.onData((data) => {
475
+ decorationsCollection.clear();
476
+ if (ReplyResponse.is(data)) {
477
+ this.aiNativeContextKey.inlineInputWidgetIsStreaming.set(true);
478
+ }
479
+ }),
480
+ chatResponse.onError((error) => {
481
+ this.aiNativeContextKey.inlineInputWidgetIsStreaming.set(false);
482
+ inlineInputWidget.launchChatStatus(EInlineChatStatus.READY);
483
+ }),
484
+ chatResponse.onAbort(() => {
485
+ decorationsCollection.clear();
486
+ this.aiNativeContextKey.inlineInputWidgetIsStreaming.set(false);
487
+ inlineInputWidget.launchChatStatus(EInlineChatStatus.DONE);
488
+ }),
489
+ chatResponse.onEnd(() => {
490
+ decorationsCollection.clear();
491
+ this.aiNativeContextKey.inlineInputWidgetIsStreaming.set(false);
492
+ inlineInputWidget.launchChatStatus(EInlineChatStatus.DONE);
493
+ }),
494
+ ]);
495
+
496
+ const diffPreviewer = this.inlineDiffController.showPreviewerByStream(monacoEditor, {
497
+ crossSelection: decorationSelection,
498
+ chatResponse,
499
+ });
319
500
 
320
- return this.featureDisposable;
501
+ diffPreviewer.mountWidget(inlineInputWidget);
502
+
503
+ chatResponse.listen();
504
+ }
505
+ } else {
506
+ decorationsCollection.clear();
507
+ inlineInputWidget.launchChatStatus(EInlineChatStatus.READY);
508
+ this.hideInput();
509
+ }
510
+ }),
511
+ );
512
+
513
+ this.inputDisposable.addDispose(
514
+ inlineInputWidget.onResultClick((kind: EResultKind) => {
515
+ this.inlineDiffController.handleAction(kind);
516
+ this.hideInput();
517
+
518
+ if (kind === EResultKind.REGENERATE) {
519
+ requestAnimationFrame(() => {
520
+ this.showInputInSelection(decorationSelection, monacoEditor, this.inputValue.get());
521
+ });
522
+ }
523
+ }),
524
+ );
525
+
526
+ this.inputDisposable.addDispose(inlineInputWidget);
321
527
  }
322
528
  }