-
Notifications
You must be signed in to change notification settings - Fork 433
feat: support code reference and reference bar #4519
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
/next |
## Walkthrough
本次更改为 AI 聊天输入和上下文管理引入了“代码”引用的新占位符(`{{@code:...}}`),支持指定文件及其行范围,并在界面中集成了上下文文件/文件夹的管理功能。`LLMContextService` 现已作为新属性传递给相关组件,实现了上下文文件的动态添加、移除和清空。Mentions 输入组件与 OutlineTreeService 集成,支持代码符号的智能引用。样式文件也针对上下文展示做了增强,并新增了相关的本地化条目。
## Changes
| 文件或路径分组 | 变更摘要 |
|---|---|
| packages/ai-native/src/browser/chat/chat.view.tsx | 移除对 `llmContextService.addFileToContext` 和 `addFolderToContext` 的直接调用,新增 `{{@code:...}}` 占位符处理,并将 `contextService` 作为新属性传递。 |
| packages/ai-native/src/browser/components/ChatMentionInput.tsx | 集成 OutlineTreeService,支持“代码”类型 mention,缓存并筛选符号,完善最近文件夹筛选逻辑,新增 `contextService` 属性。 |
| packages/ai-native/src/browser/components/mention-input/mention-input.module.less | 调整 mention 输入组件的高度、图标、描述等样式,新增上下文相关样式类。 |
| packages/ai-native/src/browser/components/mention-input/mention-input.tsx | 增强 MentionInput,集成上下文文件管理,支持文件/文件夹/代码引用的动态添加、移除和清空,新增上下文清空 UI。 |
| packages/ai-native/src/browser/components/mention-input/types.ts | `MentionItem` 类型新增 `symbol`、`kind` 属性,`MentionInputProps` 新增 `contextService` 属性。 |
| packages/ai-native/src/browser/context/llm-context.service.ts | `onDidContextFilesChangeEmitter` 事件负载新增 `attachedFolders` 字段。 |
| packages/ai-native/src/browser/types.ts | `ChatInputRender` 类型新增可选属性 `contextService`。 |
| packages/ai-native/src/common/llm-context.ts | `LLMContextService` 的 `onDidContextFilesChangeEvent` 事件类型新增 `attachedFolders` 字段。 |
| packages/i18n/src/common/en-US.lang.ts<br>packages/i18n/src/common/zh-CN.lang.ts | 新增上下文管理相关的本地化字符串。 |
| configs/ts/tsconfig.build.json | 调整 `tsconfig.outline.json` 在引用列表中的顺序。 |
| packages/ai-native/package.json | 新增对 `@opensumi/ide-outline` 的 workspace 依赖。 |
## Sequence Diagram(s)
```mermaid
sequenceDiagram
participant User
participant ChatInput
participant MentionInput
participant LLMContextService
participant OutlineTreeService
User->>ChatInput: 输入内容(含@file/@folder/@code占位符)
ChatInput->>MentionInput: 渲染 mention 列表
MentionInput->>OutlineTreeService: 获取代码符号(@code)
OutlineTreeService-->>MentionInput: 返回符号列表
User->>MentionInput: 选择文件/文件夹/代码引用
MentionInput->>LLMContextService: addFileToContext / addFolderToContext
LLMContextService-->>MentionInput: 更新上下文文件事件
MentionInput->>ChatInput: 插入 mention 标签
User->>MentionInput: 按下清空上下文按钮
MentionInput->>LLMContextService: cleanFileContext
LLMContextService-->>MentionInput: 更新上下文文件事件 Possibly related PRs
Suggested labels
Suggested reviewers
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (2)
packages/ai-native/src/browser/components/ChatMentionInput.tsx (1)
71-73
: 符号缓存未随活动文件变化而刷新
prevOutlineItems
只在首次拉取时赋值,之后若用户切换到其他文件,其 outline 变化不会反映到缓存,可能出现符号过期。建议在currentUri
变化时清空缓存:useEffect(() => { prevOutlineItems.current = []; +}, [outlineTreeService.currentUri]);
packages/ai-native/src/browser/components/mention-input/mention-input.tsx (1)
1024-1091
: 清空上下文按钮应在无附件时隐藏或禁用
变量hasContext
已计算但未被使用,导致即使没有任何文件/文件夹也渲染“清空上下文”按钮且计数显示为 0。可以按如下方式改进:- <Popover + {hasContext && ( + <Popover @@ - </Popover> + </Popover> + )}这样可避免 UI 冗余并减少用户困惑。
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
packages/ai-native/src/browser/chat/chat.view.tsx
(2 hunks)packages/ai-native/src/browser/components/ChatMentionInput.tsx
(6 hunks)packages/ai-native/src/browser/components/mention-input/mention-input.module.less
(5 hunks)packages/ai-native/src/browser/components/mention-input/mention-input.tsx
(10 hunks)packages/ai-native/src/browser/components/mention-input/types.ts
(3 hunks)packages/ai-native/src/browser/context/llm-context.service.ts
(1 hunks)packages/ai-native/src/browser/types.ts
(2 hunks)packages/ai-native/src/common/llm-context.ts
(1 hunks)packages/i18n/src/common/en-US.lang.ts
(1 hunks)packages/i18n/src/common/zh-CN.lang.ts
(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
packages/ai-native/src/browser/context/llm-context.service.ts (1)
packages/ai-native/src/common/llm-context.ts (1)
FileContext
(49-52)
packages/ai-native/src/browser/types.ts (1)
packages/ai-native/src/common/llm-context.ts (1)
LLMContextService
(3-47)
packages/ai-native/src/browser/components/mention-input/types.ts (1)
packages/ai-native/src/common/llm-context.ts (1)
LLMContextService
(3-47)
⏰ Context from checks skipped due to timeout of 90000ms (5)
- GitHub Check: 🚀🚀🚀 Next Version for pull request
- GitHub Check: build-windows
- GitHub Check: build (ubuntu-latest, 20.x)
- GitHub Check: build (macos-latest, 20.x)
- GitHub Check: ubuntu-latest, Node.js 20.x
🔇 Additional comments (15)
packages/i18n/src/common/en-US.lang.ts (1)
1466-1468
: 添加关于 AI 聊天上下文的新本地化条目这些新增的本地化条目为 AI 聊天组件提供了上下文管理功能的文本,包括清除上下文的按钮标签、显示引用总数的描述以及清除引用的操作提示。这些文本将用于增强用户对聊天上下文和引用的管理体验。
packages/ai-native/src/browser/types.ts (1)
31-31
: 为 ChatInputRender 类型添加上下文服务支持这个变更导入并添加了可选的
contextService
属性到ChatInputRender
类型接口中,使聊天输入组件能够访问 LLM 上下文服务。这是实现上下文管理功能的必要类型支持,与新增加的本地化条目相对应。Also applies to: 170-170
packages/ai-native/src/browser/context/llm-context.service.ts (1)
48-48
: 扩展上下文变更事件以支持文件夹引用在事件负载中添加
attachedFolders
属性,使LLMContextService
能够跟踪和管理文件夹上下文。这与现有的文件上下文管理方式保持一致,增强了上下文服务的功能,允许用户在聊天中引用整个文件夹。packages/i18n/src/common/zh-CN.lang.ts (1)
1234-1236
: 添加 AI 聊天上下文的中文本地化条目新增三个中文本地化条目,对应英文版的上下文管理功能文本。这些文本很好地表达了清空上下文、显示引用计数以及清空引用的操作,翻译准确且符合中文使用习惯。
packages/ai-native/src/common/llm-context.ts (1)
32-37
: 扩展了上下文文件变化事件的数据结构事件接口中添加了
attachedFolders
属性,使上下文服务能够跟踪并通知已附加文件夹的变化。这个更改与addFolderToContext
方法配合,完善了上下文管理功能。packages/ai-native/src/browser/components/mention-input/types.ts (3)
1-4
: 添加了必要的符号和上下文服务导入这些导入支持代码符号引用功能,让组件能够处理和展示来自 Monaco 编辑器的文档符号信息。
15-17
: 增强了 MentionItem 支持符号信息为
MentionItem
接口添加的symbol
和kind
属性使其能够携带文档符号的元数据,这对于实现代码引用功能至关重要。
91-91
: 集成上下文服务到输入组件将
LLMContextService
作为可选属性添加到MentionInputProps
接口,使输入组件能够访问和管理上下文文件和文件夹,提升了组件的上下文感知能力。packages/ai-native/src/browser/chat/chat.view.tsx (1)
896-896
: 将上下文服务传递给聊天输入组件通过将
llmContextService
作为contextService
属性传递给ChatInputWrapperRender
组件,实现了聊天输入与上下文管理服务的集成。这种设计使相关组件能够共享上下文状态,提高了代码的可维护性。packages/ai-native/src/browser/components/mention-input/mention-input.module.less (6)
22-24
: 优化了编辑器区域的高度设置调整了行高和高度限制,为用户提供更舒适的输入体验。行高增加到24px,最小高度设为72px,最大高度为120px,使文本输入区域更加宽敞。
143-179
: 添加了上下文容器的样式新增的上下文容器样式支持显示已附加的文件和文件夹,包括悬停交互效果,提升了用户体验。当用户悬停时,将显示关闭图标替代外链图标,使界面操作更加直观。
227-230
: 优化了提及项左侧区域的显示添加了
max-width: 100%
限制,防止内容溢出导致布局问题,提高了组件在不同内容长度下的稳定性。
237-238
: 调整了图标的尺寸和行高图标尺寸增加到22px并强制设置行高,使图标与文本更好地对齐,提升了视觉上的一致性。
256-256
: 修改了描述文本的方向添加
direction: rtl
属性改变了文本方向,这样在文本过长需要截断时,会从右侧开始显示,保留了更重要的路径部分信息。
277-309
: 添加了上下文项的样式定义新增的上下文项样式提供了清晰的视觉展示,包括背景色、边框圆角和悬停效果。这些样式使上下文项在界面中易于识别和操作,特别是移除按钮的悬停透明度变化提供了良好的交互反馈。
const codePattern = /\{\{@code:(.*?)\}\}/g; | ||
const codeMatches = processedContent.match(codePattern); | ||
if (codeMatches) { | ||
for (const match of codeMatches) { | ||
const filePathWithLineRange = match.replace(/\{\{@code:(.*?)\}\}/, '$1'); | ||
const [filePath, lineRange] = filePathWithLineRange.split(':'); | ||
let range: [number, number] = [0, 0]; | ||
if (lineRange) { | ||
const [startLine, endLine] = lineRange.slice(1).split('-'); | ||
range = [parseInt(startLine, 10), parseInt(endLine, 10)]; | ||
} | ||
const fileUri = new URI(filePath); | ||
const relativePath = (await workspaceService.asRelativePath(fileUri))?.path || fileUri.displayName; | ||
processedContent = processedContent.replace( | ||
match, | ||
`\`<attached_file>${relativePath}:L${range[0]}-${range[1]}\``, | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
实现了代码引用的占位符处理
添加了对 {{@code:...}}
模式的支持,可以指定文件路径和行范围,格式转换为 `<attached_file>relativePath:Lstart-end`
。处理流程与文件和文件夹的处理保持一致。
有一点需要注意的是,当行范围格式不符合预期时,代码缺少健壮的错误处理。建议增加对格式错误的检查和处理。
const [filePath, lineRange] = filePathWithLineRange.split(':');
let range: [number, number] = [0, 0];
if (lineRange) {
- const [startLine, endLine] = lineRange.slice(1).split('-');
- range = [parseInt(startLine, 10), parseInt(endLine, 10)];
+ try {
+ if (lineRange.startsWith('L')) {
+ const [startLine, endLine] = lineRange.slice(1).split('-');
+ if (startLine && endLine) {
+ range = [parseInt(startLine, 10) || 0, parseInt(endLine, 10) || 0];
+ }
+ }
+ } catch (e) {
+ console.warn('解析行范围失败:', lineRange, e);
+ }
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const codePattern = /\{\{@code:(.*?)\}\}/g; | |
const codeMatches = processedContent.match(codePattern); | |
if (codeMatches) { | |
for (const match of codeMatches) { | |
const filePathWithLineRange = match.replace(/\{\{@code:(.*?)\}\}/, '$1'); | |
const [filePath, lineRange] = filePathWithLineRange.split(':'); | |
let range: [number, number] = [0, 0]; | |
if (lineRange) { | |
const [startLine, endLine] = lineRange.slice(1).split('-'); | |
range = [parseInt(startLine, 10), parseInt(endLine, 10)]; | |
} | |
const fileUri = new URI(filePath); | |
const relativePath = (await workspaceService.asRelativePath(fileUri))?.path || fileUri.displayName; | |
processedContent = processedContent.replace( | |
match, | |
`\`<attached_file>${relativePath}:L${range[0]}-${range[1]}\``, | |
); | |
} | |
} | |
const [filePath, lineRange] = filePathWithLineRange.split(':'); | |
let range: [number, number] = [0, 0]; | |
if (lineRange) { | |
try { | |
if (lineRange.startsWith('L')) { | |
const [startLine, endLine] = lineRange.slice(1).split('-'); | |
if (startLine && endLine) { | |
range = [parseInt(startLine, 10) || 0, parseInt(endLine, 10) || 0]; | |
} | |
} | |
} catch (e) { | |
console.warn('解析行范围失败:', lineRange, e); | |
} | |
} |
description: `${relativePath?.root ? relativePath.path : ''}:L${treeNode.raw.range.startLineNumber}-${ | ||
treeNode.raw.range.endLineNumber | ||
}`, | ||
kind: treeNode.raw.kind, | ||
contextId: `${outlineTreeService.currentUri?.codeUri.fsPath}:L${treeNode.raw.range.startLineNumber}-${treeNode.raw.range.endLineNumber}`, | ||
icon: getSymbolIcon(treeNode.raw.kind) + ' outline-icon', | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
contextId
包含行号信息导致 URI 无效
contextId
既包含 fsPath
又拼接了 :Lstart-end
,而 handleSelectItem
中又将同一字符串包进 new URI(...)
,这会生成非法 URI,并在 addFileToContext
时重复传递行号信息。
建议:
contextId
仅保存文件绝对路径;- 行号信息通过额外字段(如
range
)传递。
- contextId: `${outlineTreeService.currentUri?.codeUri.fsPath}:L${treeNode.raw.range.startLineNumber}-${treeNode.raw.range.endLineNumber}`,
+ contextId: outlineTreeService.currentUri?.codeUri.fsPath,
+ range: [treeNode.raw.range.startLineNumber, treeNode.raw.range.endLineNumber],
后续在 handleSelectItem
中读取 item.range
传入 addFileToContext
。
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
description: `${relativePath?.root ? relativePath.path : ''}:L${treeNode.raw.range.startLineNumber}-${ | |
treeNode.raw.range.endLineNumber | |
}`, | |
kind: treeNode.raw.kind, | |
contextId: `${outlineTreeService.currentUri?.codeUri.fsPath}:L${treeNode.raw.range.startLineNumber}-${treeNode.raw.range.endLineNumber}`, | |
icon: getSymbolIcon(treeNode.raw.kind) + ' outline-icon', | |
}; | |
description: `${relativePath?.root ? relativePath.path : ''}:L${treeNode.raw.range.startLineNumber}-${ | |
treeNode.raw.range.endLineNumber | |
}`, | |
kind: treeNode.raw.kind, | |
contextId: outlineTreeService.currentUri?.codeUri.fsPath, | |
range: [treeNode.raw.range.startLineNumber, treeNode.raw.range.endLineNumber], | |
icon: getSymbolIcon(treeNode.raw.kind) + ' outline-icon', |
packages/ai-native/src/browser/components/mention-input/mention-input.tsx
Outdated
Show resolved
Hide resolved
// 创建图标容器 | ||
const iconSpan = document.createElement('span'); | ||
iconSpan.className = cls( | ||
styles.mention_icon, | ||
item.type === 'file' ? labelService?.getIcon(new URI(item.text)) : getIcon('folder'), | ||
item.type === MentionType.FILE ? labelService?.getIcon(new URI(item.text)) : getIcon('folder'), | ||
); | ||
mentionTag.appendChild(iconSpan); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
文件图标解析使用了不完整路径
此处 new URI(item.text)
仅为文件名,缺少目录信息,labelService.getIcon
可能无法解析正确图标。建议改用 item.contextId
:
- item.type === MentionType.FILE ? labelService?.getIcon(new URI(item.text)) : getIcon('folder'),
+ item.type === MentionType.FILE ? labelService?.getIcon(new URI(item.contextId)) : getIcon('folder'),
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// 创建图标容器 | |
const iconSpan = document.createElement('span'); | |
iconSpan.className = cls( | |
styles.mention_icon, | |
item.type === 'file' ? labelService?.getIcon(new URI(item.text)) : getIcon('folder'), | |
item.type === MentionType.FILE ? labelService?.getIcon(new URI(item.text)) : getIcon('folder'), | |
); | |
mentionTag.appendChild(iconSpan); | |
// 创建图标容器 | |
const iconSpan = document.createElement('span'); | |
iconSpan.className = cls( | |
styles.mention_icon, | |
item.type === MentionType.FILE | |
? labelService?.getIcon(new URI(item.contextId)) | |
: getIcon('folder'), | |
); | |
mentionTag.appendChild(iconSpan); |
packages/ai-native/src/browser/components/mention-input/mention-input.tsx
Show resolved
Hide resolved
/next |
/next |
🎉 PR Next publish successful! 3.8.3-next-1745566910.0 |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #4519 +/- ##
=======================================
Coverage 53.11% 53.12%
=======================================
Files 1665 1665
Lines 102731 102731
Branches 22248 22248
=======================================
+ Hits 54566 54574 +8
+ Misses 40066 40059 -7
+ Partials 8099 8098 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
/next |
🎉 PR Next publish successful! 3.8.3-next-1745568063.0 |
Types
Background or solution
Changelog
support code reference and reference bar
Summary by CodeRabbit