@opensumi/ide-ai-native 3.8.1-next-1741180778.0 → 3.8.1-next-1741224137.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.
- package/lib/browser/ai-core.contribution.d.ts.map +1 -1
- package/lib/browser/ai-core.contribution.js +3 -1
- package/lib/browser/ai-core.contribution.js.map +1 -1
- package/lib/browser/chat/chat-model.js +1 -1
- package/lib/browser/chat/chat-model.js.map +1 -1
- package/lib/browser/chat/chat.internal.service.d.ts +2 -0
- package/lib/browser/chat/chat.internal.service.d.ts.map +1 -1
- package/lib/browser/chat/chat.internal.service.js +3 -0
- package/lib/browser/chat/chat.internal.service.js.map +1 -1
- package/lib/browser/components/ChatInput.d.ts.map +1 -1
- package/lib/browser/components/ChatInput.js +5 -1
- package/lib/browser/components/ChatInput.js.map +1 -1
- package/lib/browser/components/ChatReply.d.ts.map +1 -1
- package/lib/browser/components/ChatReply.js +1 -1
- package/lib/browser/components/ChatReply.js.map +1 -1
- package/lib/browser/components/ChatThinking.d.ts +0 -2
- package/lib/browser/components/ChatThinking.d.ts.map +1 -1
- package/lib/browser/components/ChatThinking.js +2 -10
- package/lib/browser/components/ChatThinking.js.map +1 -1
- package/lib/browser/components/WelcomeMsg.js +1 -1
- package/lib/browser/components/WelcomeMsg.js.map +1 -1
- package/lib/browser/components/components.module.less +2 -0
- package/lib/browser/mcp/base-apply.service.d.ts +8 -5
- package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
- package/lib/browser/mcp/base-apply.service.js +83 -78
- package/lib/browser/mcp/base-apply.service.js.map +1 -1
- package/lib/browser/widget/inline-chat/inline-chat-controller.d.ts.map +1 -1
- package/lib/browser/widget/inline-chat/inline-chat-controller.js +6 -1
- package/lib/browser/widget/inline-chat/inline-chat-controller.js.map +1 -1
- package/lib/browser/widget/inline-diff/inline-diff-manager.js +1 -1
- package/lib/browser/widget/inline-diff/inline-diff-manager.js.map +1 -1
- package/lib/browser/widget/inline-diff/inline-diff-widget.module.less +3 -3
- package/lib/common/types.d.ts +1 -0
- package/lib/common/types.d.ts.map +1 -1
- package/package.json +23 -23
- package/src/browser/ai-core.contribution.ts +4 -1
- package/src/browser/chat/chat-model.ts +1 -1
- package/src/browser/chat/chat.internal.service.ts +4 -0
- package/src/browser/components/ChatInput.tsx +14 -2
- package/src/browser/components/ChatReply.tsx +1 -5
- package/src/browser/components/ChatThinking.tsx +3 -9
- package/src/browser/components/WelcomeMsg.tsx +1 -1
- package/src/browser/components/components.module.less +2 -0
- package/src/browser/mcp/base-apply.service.ts +91 -86
- package/src/browser/widget/inline-chat/inline-chat-controller.ts +5 -1
- package/src/browser/widget/inline-diff/inline-diff-manager.tsx +1 -1
- package/src/browser/widget/inline-diff/inline-diff-widget.module.less +3 -3
- package/src/common/types.ts +1 -0
|
@@ -99,7 +99,7 @@ export class ChatResponseModel extends Disposable {
|
|
|
99
99
|
this.#requestId = requestId;
|
|
100
100
|
if (initParams) {
|
|
101
101
|
this.#responseContents = initParams.responseContents;
|
|
102
|
-
this.#responseParts = initParams.responseParts;
|
|
102
|
+
this.#responseParts = initParams.responseParts || [];
|
|
103
103
|
this.#responseText = initParams.responseText;
|
|
104
104
|
this.#isComplete = initParams.isComplete;
|
|
105
105
|
this.#isCanceled = initParams.isCanceled;
|
|
@@ -30,6 +30,9 @@ export class ChatInternalService extends Disposable {
|
|
|
30
30
|
private readonly _onCancelRequest = new Emitter<void>();
|
|
31
31
|
public readonly onCancelRequest: Event<void> = this._onCancelRequest.event;
|
|
32
32
|
|
|
33
|
+
private readonly _onWillClearSession = new Emitter<string>();
|
|
34
|
+
public readonly onWillClearSession: Event<string> = this._onWillClearSession.event;
|
|
35
|
+
|
|
33
36
|
private readonly _onRegenerateRequest = new Emitter<void>();
|
|
34
37
|
public readonly onRegenerateRequest: Event<void> = this._onRegenerateRequest.event;
|
|
35
38
|
|
|
@@ -83,6 +86,7 @@ export class ChatInternalService extends Disposable {
|
|
|
83
86
|
|
|
84
87
|
clearSessionModel(sessionId?: string) {
|
|
85
88
|
sessionId = sessionId || this.#sessionModel.sessionId;
|
|
89
|
+
this._onWillClearSession.fire(sessionId);
|
|
86
90
|
this.chatManagerService.clearSession(sessionId);
|
|
87
91
|
if (sessionId === this.#sessionModel.sessionId) {
|
|
88
92
|
this.#sessionModel = this.chatManagerService.startSession();
|
|
@@ -16,11 +16,18 @@ import { CommandService } from '@opensumi/ide-core-common/lib/command';
|
|
|
16
16
|
import { MonacoCommandRegistry } from '@opensumi/ide-editor/lib/browser/monaco-contrib/command/command.service';
|
|
17
17
|
import { IDialogService } from '@opensumi/ide-overlay';
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
AT_SIGN_SYMBOL,
|
|
21
|
+
IChatAgentService,
|
|
22
|
+
IChatInternalService,
|
|
23
|
+
SLASH_SYMBOL,
|
|
24
|
+
TokenMCPServerProxyService,
|
|
25
|
+
} from '../../common';
|
|
20
26
|
import { ChatAgentViewService } from '../chat/chat-agent.view.service';
|
|
21
27
|
import { ChatSlashCommandItemModel } from '../chat/chat-model';
|
|
22
28
|
import { ChatProxyService } from '../chat/chat-proxy.service';
|
|
23
29
|
import { ChatFeatureRegistry } from '../chat/chat.feature.registry';
|
|
30
|
+
import { ChatInternalService } from '../chat/chat.internal.service';
|
|
24
31
|
import { OPEN_MCP_CONFIG_COMMAND } from '../mcp/config/mcp-config.commands';
|
|
25
32
|
import { MCPServerProxyService } from '../mcp/mcp-server-proxy.service';
|
|
26
33
|
import { MCPToolsDialog } from '../mcp/mcp-tools-dialog.view';
|
|
@@ -204,7 +211,7 @@ export const ChatInput = React.forwardRef((props: IChatInputProps, ref) => {
|
|
|
204
211
|
const [showExpand, setShowExpand] = useState(false);
|
|
205
212
|
const [isExpand, setIsExpand] = useState(false);
|
|
206
213
|
const [placeholder, setPlaceHolder] = useState(localize('aiNative.chat.input.placeholder.default'));
|
|
207
|
-
|
|
214
|
+
const aiChatService = useInjectable<ChatInternalService>(IChatInternalService);
|
|
208
215
|
const dialogService = useInjectable<IDialogService>(IDialogService);
|
|
209
216
|
const aiNativeConfigService = useInjectable<AINativeConfigService>(AINativeConfigService);
|
|
210
217
|
const mcpServerProxyService = useInjectable<MCPServerProxyService>(TokenMCPServerProxyService);
|
|
@@ -324,6 +331,10 @@ export const ChatInput = React.forwardRef((props: IChatInputProps, ref) => {
|
|
|
324
331
|
}
|
|
325
332
|
}, []);
|
|
326
333
|
|
|
334
|
+
const handleStop = useCallback(() => {
|
|
335
|
+
aiChatService.cancelRequest();
|
|
336
|
+
}, []);
|
|
337
|
+
|
|
327
338
|
const handleSend = useCallback(async () => {
|
|
328
339
|
if (disabled) {
|
|
329
340
|
return;
|
|
@@ -484,6 +495,7 @@ export const ChatInput = React.forwardRef((props: IChatInputProps, ref) => {
|
|
|
484
495
|
disabled={disabled}
|
|
485
496
|
className={styles.input_wrapper}
|
|
486
497
|
onSend={handleSend}
|
|
498
|
+
onStop={handleStop}
|
|
487
499
|
sendBtnClassName={sendBtnClassName}
|
|
488
500
|
onHeightChange={handleHeightChange}
|
|
489
501
|
height={inputHeight}
|
|
@@ -355,11 +355,7 @@ export const ChatReply = (props: IChatReplyProps) => {
|
|
|
355
355
|
}, [request.response.followups]);
|
|
356
356
|
|
|
357
357
|
if (!request.response.isComplete) {
|
|
358
|
-
return
|
|
359
|
-
<ChatThinking message={request.response.responseText} onStop={onStop}>
|
|
360
|
-
{contentNode}
|
|
361
|
-
</ChatThinking>
|
|
362
|
-
);
|
|
358
|
+
return <ChatThinking message={request.response.responseText}>{contentNode}</ChatThinking>;
|
|
363
359
|
}
|
|
364
360
|
|
|
365
361
|
return (
|
|
@@ -18,14 +18,12 @@ interface ITinkingProps {
|
|
|
18
18
|
message?: string;
|
|
19
19
|
onRegenerate?: () => void;
|
|
20
20
|
requestId?: string;
|
|
21
|
-
onStop?: () => void;
|
|
22
21
|
thinkingText?: string;
|
|
23
|
-
showStop?: boolean;
|
|
24
22
|
showRegenerate?: boolean;
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
export const ChatThinking = (props: ITinkingProps) => {
|
|
28
|
-
const { children, message,
|
|
26
|
+
const { children, message, thinkingText } = props;
|
|
29
27
|
|
|
30
28
|
const chatRenderRegistry = useInjectable<ChatRenderRegistry>(ChatRenderRegistryToken);
|
|
31
29
|
|
|
@@ -34,10 +32,6 @@ export const ChatThinking = (props: ITinkingProps) => {
|
|
|
34
32
|
[chatRenderRegistry, chatRenderRegistry.chatThinkingRender],
|
|
35
33
|
);
|
|
36
34
|
|
|
37
|
-
const handlePause = useCallback(async () => {
|
|
38
|
-
onStop && onStop();
|
|
39
|
-
}, []);
|
|
40
|
-
|
|
41
35
|
const renderContent = useCallback(() => {
|
|
42
36
|
if (!children || !message?.trim()) {
|
|
43
37
|
if (CustomThinkingRender) {
|
|
@@ -61,12 +55,12 @@ export const ChatThinking = (props: ITinkingProps) => {
|
|
|
61
55
|
{!children && <Progress loading={true} wrapperClassName={styles.ai_native_progress_wrapper} />}
|
|
62
56
|
</span>
|
|
63
57
|
)}
|
|
64
|
-
{showStop && (
|
|
58
|
+
{/* {showStop && (
|
|
65
59
|
<div className={styles.block} onClick={handlePause} tabIndex={0} role='button'>
|
|
66
60
|
<Icon className={getIcon('circle-pause')}></Icon>
|
|
67
61
|
<span>{localize('aiNative.operate.stop.title')}</span>
|
|
68
62
|
</div>
|
|
69
|
-
)}
|
|
63
|
+
)} */}
|
|
70
64
|
</div>
|
|
71
65
|
</div>
|
|
72
66
|
</>
|
|
@@ -62,7 +62,7 @@ export const WelcomeMessage = () => {
|
|
|
62
62
|
}, []);
|
|
63
63
|
|
|
64
64
|
if (!welcomeMessage) {
|
|
65
|
-
return <ChatThinking
|
|
65
|
+
return <ChatThinking thinkingText={localize('aiNative.chat.welcome.loading.text')} />;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
const allSampleQuestions = React.useMemo(
|
|
@@ -69,7 +69,17 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
69
69
|
super();
|
|
70
70
|
this.addDispose(
|
|
71
71
|
this.chatInternalService.onCancelRequest(() => {
|
|
72
|
-
this.
|
|
72
|
+
const currentMessageId = this.chatInternalService.sessionModel.history.lastMessageId;
|
|
73
|
+
if (!currentMessageId) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const codeBlockMap = this.getMessageCodeBlocks(currentMessageId);
|
|
77
|
+
if (!codeBlockMap) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
Object.values(codeBlockMap).forEach((blockData) => {
|
|
81
|
+
this.cancelApply(blockData);
|
|
82
|
+
});
|
|
73
83
|
}),
|
|
74
84
|
);
|
|
75
85
|
this.currentSessionId = this.chatInternalService.sessionModel.sessionId;
|
|
@@ -94,6 +104,11 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
94
104
|
});
|
|
95
105
|
}),
|
|
96
106
|
);
|
|
107
|
+
this.addDispose(
|
|
108
|
+
this.chatInternalService.onWillClearSession((sessionId) => {
|
|
109
|
+
this.cancelAllApply(sessionId);
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
97
112
|
}
|
|
98
113
|
|
|
99
114
|
private getMessageCodeBlocks(
|
|
@@ -109,27 +124,6 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
109
124
|
return message?.codeBlockMap;
|
|
110
125
|
}
|
|
111
126
|
|
|
112
|
-
private getSessionCodeBlocksForPath(relativePath: string, sessionId?: string) {
|
|
113
|
-
sessionId = sessionId || this.chatInternalService.sessionModel.sessionId;
|
|
114
|
-
const sessionModel = this.chatInternalService.getSession(sessionId);
|
|
115
|
-
if (!sessionModel) {
|
|
116
|
-
throw new Error(`Session ${sessionId} not found`);
|
|
117
|
-
}
|
|
118
|
-
const sessionAdditionals = sessionModel.history.sessionAdditionals;
|
|
119
|
-
const codeBlocks: CodeBlockData[] = Array.from(sessionAdditionals.values())
|
|
120
|
-
.map((additional) => additional.codeBlockMap as { [toolCallId: string]: CodeBlockData })
|
|
121
|
-
.reduce((acc, cur) => {
|
|
122
|
-
Object.values(cur).forEach((block) => {
|
|
123
|
-
if (block.relativePath === relativePath) {
|
|
124
|
-
acc.push(block);
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
return acc;
|
|
128
|
-
}, [] as CodeBlockData[])
|
|
129
|
-
.sort((a, b) => b.version - a.version);
|
|
130
|
-
return codeBlocks;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
127
|
private activePreviewerMap = this.registerDispose(
|
|
134
128
|
new DisposableMap<string, BaseInlineDiffPreviewer<BaseInlineStreamDiffHandler>>(),
|
|
135
129
|
);
|
|
@@ -156,10 +150,9 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
156
150
|
) {
|
|
157
151
|
return;
|
|
158
152
|
}
|
|
159
|
-
const filePendingApplies =
|
|
160
|
-
this.
|
|
161
|
-
|
|
162
|
-
// TODO: 刷新后重新应用,事件无法恢复 & 恢复继续请求,需要改造成批量apply形式
|
|
153
|
+
const filePendingApplies =
|
|
154
|
+
this.getUriCodeBlocks(event.payload.resource.uri)?.filter((block) => block.status === 'pending') || [];
|
|
155
|
+
// 使用最后一个版本内容渲染 apply 内容
|
|
163
156
|
if (filePendingApplies.length > 0 && filePendingApplies[0].updatedCode) {
|
|
164
157
|
const editor = event.payload.group.codeEditor.monacoEditor;
|
|
165
158
|
this.renderApplyResult(editor, filePendingApplies[0], filePendingApplies[0].updatedCode);
|
|
@@ -174,23 +167,23 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
174
167
|
return this.activePreviewerMap.get(path.relative(this.appConfig.workspaceDir, currentUri.path.toString()));
|
|
175
168
|
}
|
|
176
169
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
return Object.values(codeBlockMap).find(
|
|
187
|
-
(block) =>
|
|
188
|
-
block.relativePath === path.relative(this.appConfig.workspaceDir, uri.path.toString()) &&
|
|
189
|
-
block.status === 'pending',
|
|
190
|
-
);
|
|
170
|
+
/**
|
|
171
|
+
* 获取指定uri的 code block,按version降序排序
|
|
172
|
+
*/
|
|
173
|
+
getUriCodeBlocks(uri: URI): CodeBlockData[] | undefined {
|
|
174
|
+
const sessionCodeBlocks = this.getSessionCodeBlocks();
|
|
175
|
+
const relativePath = path.relative(this.appConfig.workspaceDir, uri.path.toString());
|
|
176
|
+
return sessionCodeBlocks
|
|
177
|
+
.filter((block) => block.relativePath === relativePath)
|
|
178
|
+
.sort((a, b) => b.version - a.version);
|
|
191
179
|
}
|
|
192
180
|
|
|
193
181
|
getPendingPaths(sessionId?: string): string[] {
|
|
182
|
+
const sessionCodeBlocks = this.getSessionCodeBlocks(sessionId);
|
|
183
|
+
return sessionCodeBlocks.filter((block) => block.status === 'pending').map((block) => block.relativePath);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
protected getSessionCodeBlocks(sessionId?: string) {
|
|
194
187
|
sessionId = sessionId || this.chatInternalService.sessionModel.sessionId;
|
|
195
188
|
const sessionModel = this.chatInternalService.getSession(sessionId);
|
|
196
189
|
if (!sessionModel) {
|
|
@@ -198,15 +191,13 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
198
191
|
}
|
|
199
192
|
const sessionAdditionals = sessionModel.history.sessionAdditionals;
|
|
200
193
|
return Array.from(sessionAdditionals.values())
|
|
201
|
-
.map((additional) => additional.codeBlockMap as { [toolCallId: string]: CodeBlockData })
|
|
194
|
+
.map((additional) => (additional.codeBlockMap || {}) as { [toolCallId: string]: CodeBlockData })
|
|
202
195
|
.reduce((acc, cur) => {
|
|
203
196
|
Object.values(cur).forEach((block) => {
|
|
204
|
-
|
|
205
|
-
acc.push(block.relativePath);
|
|
206
|
-
}
|
|
197
|
+
acc.push(block);
|
|
207
198
|
});
|
|
208
199
|
return acc;
|
|
209
|
-
}, [] as
|
|
200
|
+
}, [] as CodeBlockData[]);
|
|
210
201
|
}
|
|
211
202
|
|
|
212
203
|
getCodeBlock(toolCallId: string, messageId?: string): CodeBlockData | undefined {
|
|
@@ -221,11 +212,8 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
221
212
|
return codeBlockMap[toolCallId];
|
|
222
213
|
}
|
|
223
214
|
|
|
224
|
-
protected updateCodeBlock(codeBlock: CodeBlockData
|
|
225
|
-
messageId = messageId
|
|
226
|
-
if (!messageId) {
|
|
227
|
-
throw new Error('Message ID is required');
|
|
228
|
-
}
|
|
215
|
+
protected updateCodeBlock(codeBlock: CodeBlockData) {
|
|
216
|
+
const messageId = codeBlock.messageId;
|
|
229
217
|
const codeBlockMap = this.getMessageCodeBlocks(messageId);
|
|
230
218
|
if (!codeBlockMap) {
|
|
231
219
|
throw new Error('Code block not found');
|
|
@@ -239,7 +227,7 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
239
227
|
|
|
240
228
|
async registerCodeBlock(relativePath: string, content: string, toolCallId: string): Promise<CodeBlockData> {
|
|
241
229
|
const lastMessageId = this.chatInternalService.sessionModel.history.lastMessageId!;
|
|
242
|
-
const
|
|
230
|
+
const uriCodeBlocks = this.getUriCodeBlocks(URI.file(path.join(this.appConfig.workspaceDir, relativePath)));
|
|
243
231
|
const originalModelRef = await this.editorDocumentModelService.createModelReference(
|
|
244
232
|
URI.file(path.join(this.appConfig.workspaceDir, relativePath)),
|
|
245
233
|
);
|
|
@@ -251,13 +239,13 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
251
239
|
version: 1,
|
|
252
240
|
createdAt: Date.now(),
|
|
253
241
|
toolCallId,
|
|
242
|
+
messageId: lastMessageId,
|
|
254
243
|
// TODO: 支持range
|
|
255
244
|
originalCode: originalModelRef.instance.getText(),
|
|
256
245
|
};
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
for (const block of samePathCodeBlocks.sort((a, b) => a.version - b.version)) {
|
|
246
|
+
if (uriCodeBlocks?.length) {
|
|
247
|
+
newBlock.version = uriCodeBlocks.length;
|
|
248
|
+
for (const block of uriCodeBlocks) {
|
|
261
249
|
// 如果连续的上一个同文件apply结果存在LintError,则iterationCount++
|
|
262
250
|
if (block.relativePath === relativePath && block.applyResult?.diagnosticInfos?.length) {
|
|
263
251
|
newBlock.iterationCount++;
|
|
@@ -266,6 +254,7 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
266
254
|
}
|
|
267
255
|
}
|
|
268
256
|
}
|
|
257
|
+
const savedCodeBlockMap = this.getMessageCodeBlocks(lastMessageId) || {};
|
|
269
258
|
savedCodeBlockMap[toolCallId] = newBlock;
|
|
270
259
|
this.chatInternalService.sessionModel.history.setMessageAdditional(lastMessageId, {
|
|
271
260
|
codeBlockMap: savedCodeBlockMap,
|
|
@@ -296,9 +285,9 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
296
285
|
}
|
|
297
286
|
|
|
298
287
|
if (this.activePreviewerMap.has(codeBlock.relativePath)) {
|
|
299
|
-
|
|
288
|
+
// 有正在进行的 apply,则取消(但不更新block状态,只清理副作用)
|
|
289
|
+
this.cancelApply(codeBlock, true);
|
|
300
290
|
}
|
|
301
|
-
// FIXME: 同一个bubble单个文件多次写入(如迭代)兼容
|
|
302
291
|
// trigger diffPreivewer & return expected diff result directly
|
|
303
292
|
const result = await this.editorService.open(
|
|
304
293
|
URI.file(path.join(this.appConfig.workspaceDir, codeBlock.relativePath)),
|
|
@@ -306,6 +295,11 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
306
295
|
if (!result) {
|
|
307
296
|
throw new Error('Failed to open file');
|
|
308
297
|
}
|
|
298
|
+
if (typeof fastApplyFileResult.result === 'string') {
|
|
299
|
+
codeBlock.updatedCode = fastApplyFileResult.result;
|
|
300
|
+
codeBlock.status = 'pending';
|
|
301
|
+
this.updateCodeBlock(codeBlock);
|
|
302
|
+
}
|
|
309
303
|
const applyResult = await this.renderApplyResult(
|
|
310
304
|
result.group.codeEditor.monacoEditor,
|
|
311
305
|
codeBlock,
|
|
@@ -340,16 +334,14 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
340
334
|
|
|
341
335
|
if (typeof updatedContentOrStream === 'string') {
|
|
342
336
|
const editorCurrentContent = editor.getModel()!.getValue();
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
);
|
|
337
|
+
const uri = URI.file(path.join(this.appConfig.workspaceDir, codeBlock.relativePath));
|
|
338
|
+
const document = this.editorDocumentModelService.getModelReference(uri);
|
|
346
339
|
if (editorCurrentContent !== updatedContentOrStream || document?.instance.dirty) {
|
|
347
340
|
editor.getModel()?.pushEditOperations([], [EditOperation.replace(range, updatedContentOrStream)], () => null);
|
|
348
|
-
await this.editorService.save(
|
|
341
|
+
await this.editorService.save(uri);
|
|
349
342
|
}
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
);
|
|
343
|
+
const uriPendingCodeBlocks = this.getUriCodeBlocks(uri)?.filter((block) => block.status === 'pending');
|
|
344
|
+
const earlistPendingCodeBlock = uriPendingCodeBlocks?.[uriPendingCodeBlocks.length - 1];
|
|
353
345
|
if ((earlistPendingCodeBlock?.originalCode || codeBlock.originalCode) === updatedContentOrStream) {
|
|
354
346
|
codeBlock.status = 'cancelled';
|
|
355
347
|
this.updateCodeBlock(codeBlock);
|
|
@@ -367,9 +359,6 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
367
359
|
},
|
|
368
360
|
) as LiveInlineDiffPreviewer;
|
|
369
361
|
this.activePreviewerMap.set(codeBlock.relativePath, previewer);
|
|
370
|
-
codeBlock.updatedCode = updatedContentOrStream;
|
|
371
|
-
codeBlock.status = 'pending';
|
|
372
|
-
this.updateCodeBlock(codeBlock);
|
|
373
362
|
// 新建文件场景,为避免model为空,加一个空行
|
|
374
363
|
previewer.setValue(earlistPendingCodeBlock?.originalCode || codeBlock.originalCode || '\n');
|
|
375
364
|
// 强刷展示 manager 视图
|
|
@@ -385,7 +374,7 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
385
374
|
|
|
386
375
|
const { diff, rangesFromDiffHunk } = this.getDiffResult(
|
|
387
376
|
codeBlock.originalCode,
|
|
388
|
-
codeBlock.updatedCode,
|
|
377
|
+
codeBlock.updatedCode || updatedContentOrStream,
|
|
389
378
|
codeBlock.relativePath,
|
|
390
379
|
);
|
|
391
380
|
const diagnosticInfos = this.getDiagnosticInfos(editor.getModel()!.uri.toString(), rangesFromDiffHunk);
|
|
@@ -405,6 +394,17 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
405
394
|
renderRemovedWidgetImmediately: false,
|
|
406
395
|
},
|
|
407
396
|
}) as LiveInlineDiffPreviewer;
|
|
397
|
+
|
|
398
|
+
this.addDispose(
|
|
399
|
+
controller.onError((err) => {
|
|
400
|
+
deferred.reject(err);
|
|
401
|
+
}),
|
|
402
|
+
);
|
|
403
|
+
this.addDispose(
|
|
404
|
+
controller.onAbort(() => {
|
|
405
|
+
deferred.reject(new Error('Apply aborted'));
|
|
406
|
+
}),
|
|
407
|
+
);
|
|
408
408
|
this.addDispose(
|
|
409
409
|
// 流式输出结束后,转为直接输出逻辑
|
|
410
410
|
previewer.getNode()!.onDiffFinished(async (diffModel) => {
|
|
@@ -412,12 +412,13 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
412
412
|
// TODO: 添加 reapply
|
|
413
413
|
// 实际应用结果为空,则取消
|
|
414
414
|
if (codeBlock.updatedCode === codeBlock.originalCode) {
|
|
415
|
-
codeBlock.status = '
|
|
415
|
+
codeBlock.status = 'failed';
|
|
416
416
|
this.updateCodeBlock(codeBlock);
|
|
417
417
|
previewer.dispose();
|
|
418
|
-
deferred.
|
|
418
|
+
deferred.reject(new Error('no changes applied'));
|
|
419
419
|
return;
|
|
420
420
|
}
|
|
421
|
+
codeBlock.status = 'pending';
|
|
421
422
|
this.updateCodeBlock(codeBlock);
|
|
422
423
|
previewer.dispose();
|
|
423
424
|
const result = await this.renderApplyResult(editor, codeBlock, codeBlock.updatedCode);
|
|
@@ -432,8 +433,12 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
432
433
|
/**
|
|
433
434
|
* Cancel an ongoing apply operation
|
|
434
435
|
*/
|
|
435
|
-
cancelApply(blockData: CodeBlockData): void {
|
|
436
|
+
cancelApply(blockData: CodeBlockData, keepStatus?: boolean): void {
|
|
436
437
|
if (blockData.status === 'generating' || blockData.status === 'pending') {
|
|
438
|
+
// 先取消掉相关的监听器
|
|
439
|
+
this.editorListenerMap.disposeKey(
|
|
440
|
+
URI.file(path.join(this.appConfig.workspaceDir, blockData.relativePath)).toString(),
|
|
441
|
+
);
|
|
437
442
|
if (this.activePreviewerMap.has(blockData.relativePath)) {
|
|
438
443
|
this.activePreviewerMap
|
|
439
444
|
.get(blockData.relativePath)
|
|
@@ -441,19 +446,16 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
441
446
|
?.livePreviewDiffDecorationModel.discardUnProcessed();
|
|
442
447
|
this.activePreviewerMap.disposeKey(blockData.relativePath);
|
|
443
448
|
}
|
|
444
|
-
|
|
445
|
-
|
|
449
|
+
if (!keepStatus) {
|
|
450
|
+
blockData.status = 'cancelled';
|
|
451
|
+
this.updateCodeBlock(blockData);
|
|
452
|
+
}
|
|
446
453
|
}
|
|
447
454
|
}
|
|
448
455
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
const codeBlockMap = this.getMessageCodeBlocks(messageId);
|
|
453
|
-
if (!codeBlockMap) {
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
Object.values(codeBlockMap).forEach((blockData) => {
|
|
456
|
+
cancelAllApply(sessionId?: string): void {
|
|
457
|
+
const sessionCodeBlocks = this.getSessionCodeBlocks(sessionId);
|
|
458
|
+
sessionCodeBlocks.forEach((blockData) => {
|
|
457
459
|
this.cancelApply(blockData);
|
|
458
460
|
});
|
|
459
461
|
}
|
|
@@ -476,12 +478,12 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
476
478
|
}
|
|
477
479
|
|
|
478
480
|
processAll(uri: URI, type: 'accept' | 'reject'): void {
|
|
479
|
-
const
|
|
480
|
-
if (!
|
|
481
|
+
const codeBlocks = this.getUriCodeBlocks(uri)?.filter((block) => block.status === 'pending');
|
|
482
|
+
if (!codeBlocks?.length) {
|
|
481
483
|
throw new Error('No pending code block found');
|
|
482
484
|
}
|
|
483
485
|
const decorationModel = this.activePreviewerMap
|
|
484
|
-
.get(
|
|
486
|
+
.get(codeBlocks[0].relativePath)
|
|
485
487
|
?.getNode()?.livePreviewDiffDecorationModel;
|
|
486
488
|
if (!decorationModel) {
|
|
487
489
|
throw new Error('No active previewer found');
|
|
@@ -492,8 +494,11 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
492
494
|
decorationModel.discardUnProcessed();
|
|
493
495
|
}
|
|
494
496
|
this.editorService.save(uri);
|
|
495
|
-
codeBlock
|
|
496
|
-
|
|
497
|
+
codeBlocks.forEach((codeBlock) => {
|
|
498
|
+
codeBlock.status = type === 'accept' ? 'success' : 'cancelled';
|
|
499
|
+
// TODO: 批量更新
|
|
500
|
+
this.updateCodeBlock(codeBlock);
|
|
501
|
+
});
|
|
497
502
|
}
|
|
498
503
|
|
|
499
504
|
protected listenPartialEdit(model: ITextModel, codeBlock: CodeBlockData) {
|
|
@@ -83,7 +83,11 @@ export class InlineChatController {
|
|
|
83
83
|
this._onData.fire(reply);
|
|
84
84
|
},
|
|
85
85
|
onEnd: () => {
|
|
86
|
-
|
|
86
|
+
if (!wholeContent) {
|
|
87
|
+
this._onError.fire(new ErrorResponse(new Error('No content')));
|
|
88
|
+
} else {
|
|
89
|
+
this._onEnd.fire();
|
|
90
|
+
}
|
|
87
91
|
},
|
|
88
92
|
onError: (error) => {
|
|
89
93
|
if (AbortError.is(error)) {
|
|
@@ -29,7 +29,7 @@ export const InlineDiffManager: React.FC<{ resource: IResource }> = (props) => {
|
|
|
29
29
|
const [show, setShow] = useState(true);
|
|
30
30
|
const [changesCount, setChangesCount] = useState(0);
|
|
31
31
|
const [currentChangeIndex, setCurrentChangeIndex] = useState(0);
|
|
32
|
-
const [filePaths, setFilePaths] = useState<string[]>(
|
|
32
|
+
const [filePaths, setFilePaths] = useState<string[]>(applyService.getPendingPaths());
|
|
33
33
|
|
|
34
34
|
const currentFilePath = useMemo(
|
|
35
35
|
() => path.relative(appConfig.workspaceDir, resource.uri.path.toString()),
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
.inlineDiffManager {
|
|
24
24
|
display: flex;
|
|
25
|
-
border-radius:
|
|
25
|
+
border-radius: 14px;
|
|
26
26
|
align-items: center;
|
|
27
27
|
position: absolute;
|
|
28
28
|
bottom: 18px;
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
border-right: 1px solid var(--design-borderColor);
|
|
39
39
|
}
|
|
40
40
|
:global(.codicon) {
|
|
41
|
-
font-size:
|
|
41
|
+
font-size: 16px;
|
|
42
42
|
padding: 6px;
|
|
43
43
|
color: var(--kt-primaryButton-foreground);
|
|
44
44
|
background-color: var(--kt-primaryButton-hoverBackground);
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
.disabled {
|
|
51
|
-
|
|
51
|
+
color: var(--kt-button-disableForeground) !important;
|
|
52
52
|
background-color: var(--kt-button-disableBackground) !important;
|
|
53
53
|
cursor: not-allowed;
|
|
54
54
|
}
|