mirror of
https://github.com/EthanMarti/infio-copilot.git
synced 2026-01-17 16:37:52 +00:00
update edit aply file content
This commit is contained in:
parent
c415175de8
commit
4fb0bb22ac
@ -7,7 +7,7 @@ import { AppProvider } from './contexts/AppContext'
|
|||||||
|
|
||||||
export type ApplyViewState = {
|
export type ApplyViewState = {
|
||||||
file: TFile
|
file: TFile
|
||||||
originalContent: string
|
oldContent: string
|
||||||
newContent: string
|
newContent: string
|
||||||
onClose: (applied: boolean) => void
|
onClose: (applied: boolean) => void
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ export default function ApplyViewRoot({
|
|||||||
const app = useApp()
|
const app = useApp()
|
||||||
|
|
||||||
const [diff, setDiff] = useState<Change[]>(
|
const [diff, setDiff] = useState<Change[]>(
|
||||||
diffLines(state.originalContent, state.newContent),
|
diffLines(state.oldContent, state.newContent),
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleAccept = async () => {
|
const handleAccept = async () => {
|
||||||
|
|||||||
@ -48,7 +48,7 @@ import { PromptGenerator, addLineNumbers } from '../../utils/prompt-generator'
|
|||||||
import { fetchUrlsContent, webSearch } from '../../utils/web-search'
|
import { fetchUrlsContent, webSearch } from '../../utils/web-search'
|
||||||
|
|
||||||
// Simple file reading function that returns a placeholder content for testing
|
// Simple file reading function that returns a placeholder content for testing
|
||||||
const readFileContent = async (app: App, filePath: string): Promise<string> => {
|
const readFileContentByPath = async (app: App, filePath: string): Promise<string> => {
|
||||||
const file = app.vault.getFileByPath(filePath)
|
const file = app.vault.getFileByPath(filePath)
|
||||||
if (!file) {
|
if (!file) {
|
||||||
throw new Error(`File not found: ${filePath}`)
|
throw new Error(`File not found: ${filePath}`)
|
||||||
@ -367,24 +367,23 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
|||||||
>({
|
>({
|
||||||
mutationFn: async ({ applyMsgId, toolArgs }) => {
|
mutationFn: async ({ applyMsgId, toolArgs }) => {
|
||||||
try {
|
try {
|
||||||
const activeFile = app.workspace.getActiveFile()
|
let opFile = app.workspace.getActiveFile()
|
||||||
if (!activeFile) {
|
if ('filepath' in toolArgs && toolArgs.filepath) {
|
||||||
throw new Error(
|
opFile = app.vault.getFileByPath(toolArgs.filepath)
|
||||||
'No file is currently open to apply changes. Please open a file and try again.',
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeFileContent = await readTFileContent(activeFile, app.vault)
|
|
||||||
|
|
||||||
if (toolArgs.type === 'write_to_file' || toolArgs.type === 'insert_content') {
|
if (toolArgs.type === 'write_to_file' || toolArgs.type === 'insert_content') {
|
||||||
const applyRes = await ApplyEditToFile(
|
if (!opFile) {
|
||||||
activeFile,
|
throw new Error(`File not found: ${toolArgs.filepath}`)
|
||||||
activeFileContent,
|
}
|
||||||
|
const fileContent = await readTFileContent(opFile, app.vault)
|
||||||
|
const appliedFileContent = await ApplyEditToFile(
|
||||||
|
fileContent,
|
||||||
toolArgs.content,
|
toolArgs.content,
|
||||||
toolArgs.startLine,
|
toolArgs.startLine,
|
||||||
toolArgs.endLine
|
toolArgs.endLine
|
||||||
)
|
)
|
||||||
if (!applyRes) {
|
if (!appliedFileContent) {
|
||||||
throw new Error('Failed to apply edit changes')
|
throw new Error('Failed to apply edit changes')
|
||||||
}
|
}
|
||||||
// return a Promise, which will be resolved after user makes a choice
|
// return a Promise, which will be resolved after user makes a choice
|
||||||
@ -393,9 +392,9 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
|||||||
type: APPLY_VIEW_TYPE,
|
type: APPLY_VIEW_TYPE,
|
||||||
active: true,
|
active: true,
|
||||||
state: {
|
state: {
|
||||||
file: activeFile,
|
file: opFile,
|
||||||
originalContent: activeFileContent,
|
oldContent: fileContent,
|
||||||
newContent: applyRes,
|
newContent: appliedFileContent,
|
||||||
onClose: (applied: boolean) => {
|
onClose: (applied: boolean) => {
|
||||||
const applyStatus = applied ? ApplyStatus.Applied : ApplyStatus.Rejected
|
const applyStatus = applied ? ApplyStatus.Applied : ApplyStatus.Rejected
|
||||||
const applyEditContent = applied ? 'Changes successfully applied'
|
const applyEditContent = applied ? 'Changes successfully applied'
|
||||||
@ -418,13 +417,15 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else if (toolArgs.type === 'search_and_replace') {
|
} else if (toolArgs.type === 'search_and_replace') {
|
||||||
const fileContent = activeFile.path === toolArgs.filepath ? activeFileContent : await readFileContent(app, toolArgs.filepath)
|
if (!opFile) {
|
||||||
const applyRes = await SearchAndReplace(
|
throw new Error(`File not found: ${toolArgs.filepath}`)
|
||||||
activeFile,
|
}
|
||||||
|
const fileContent = await readTFileContent(opFile, app.vault)
|
||||||
|
const appliedFileContent = await SearchAndReplace(
|
||||||
fileContent,
|
fileContent,
|
||||||
toolArgs.operations
|
toolArgs.operations
|
||||||
)
|
)
|
||||||
if (!applyRes) {
|
if (!appliedFileContent) {
|
||||||
throw new Error('Failed to search_and_replace')
|
throw new Error('Failed to search_and_replace')
|
||||||
}
|
}
|
||||||
// return a Promise, which will be resolved after user makes a choice
|
// return a Promise, which will be resolved after user makes a choice
|
||||||
@ -433,9 +434,9 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
|||||||
type: APPLY_VIEW_TYPE,
|
type: APPLY_VIEW_TYPE,
|
||||||
active: true,
|
active: true,
|
||||||
state: {
|
state: {
|
||||||
file: activeFile,
|
file: opFile,
|
||||||
originalContent: activeFileContent,
|
oldContent: fileContent,
|
||||||
newContent: applyRes,
|
newContent: appliedFileContent,
|
||||||
onClose: (applied: boolean) => {
|
onClose: (applied: boolean) => {
|
||||||
const applyStatus = applied ? ApplyStatus.Applied : ApplyStatus.Rejected
|
const applyStatus = applied ? ApplyStatus.Applied : ApplyStatus.Rejected
|
||||||
const applyEditContent = applied ? 'Changes successfully applied'
|
const applyEditContent = applied ? 'Changes successfully applied'
|
||||||
@ -458,12 +459,15 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else if (toolArgs.type === 'apply_diff') {
|
} else if (toolArgs.type === 'apply_diff') {
|
||||||
const diffResult = await diffStrategy.applyDiff(
|
if (!opFile) {
|
||||||
activeFileContent,
|
throw new Error(`File not found: ${toolArgs.filepath}`)
|
||||||
|
}
|
||||||
|
const fileContent = await readTFileContent(opFile, app.vault)
|
||||||
|
const appliedResult = await diffStrategy.applyDiff(
|
||||||
|
fileContent,
|
||||||
toolArgs.diff
|
toolArgs.diff
|
||||||
)
|
)
|
||||||
if (!diffResult.success) {
|
if (!appliedResult || !appliedResult.success) {
|
||||||
console.log(diffResult)
|
|
||||||
throw new Error(`Failed to apply_diff`)
|
throw new Error(`Failed to apply_diff`)
|
||||||
}
|
}
|
||||||
// return a Promise, which will be resolved after user makes a choice
|
// return a Promise, which will be resolved after user makes a choice
|
||||||
@ -472,9 +476,9 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
|||||||
type: APPLY_VIEW_TYPE,
|
type: APPLY_VIEW_TYPE,
|
||||||
active: true,
|
active: true,
|
||||||
state: {
|
state: {
|
||||||
file: activeFile,
|
file: opFile,
|
||||||
originalContent: activeFileContent,
|
oldContent: fileContent,
|
||||||
newContent: diffResult.content,
|
newContent: appliedResult.content,
|
||||||
onClose: (applied: boolean) => {
|
onClose: (applied: boolean) => {
|
||||||
const applyStatus = applied ? ApplyStatus.Applied : ApplyStatus.Rejected
|
const applyStatus = applied ? ApplyStatus.Applied : ApplyStatus.Rejected
|
||||||
const applyEditContent = applied ? 'Changes successfully applied'
|
const applyEditContent = applied ? 'Changes successfully applied'
|
||||||
@ -497,7 +501,10 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else if (toolArgs.type === 'read_file') {
|
} else if (toolArgs.type === 'read_file') {
|
||||||
const fileContent = await readFileContent(app, toolArgs.filepath)
|
if (!opFile) {
|
||||||
|
throw new Error(`File not found: ${toolArgs.filepath}`)
|
||||||
|
}
|
||||||
|
const fileContent = await readTFileContent(opFile, app.vault)
|
||||||
const formattedContent = `[read_file for '${toolArgs.filepath}'] Result:\n${addLineNumbers(fileContent)}\n`;
|
const formattedContent = `[read_file for '${toolArgs.filepath}'] Result:\n${addLineNumbers(fileContent)}\n`;
|
||||||
return {
|
return {
|
||||||
type: 'read_file',
|
type: 'read_file',
|
||||||
|
|||||||
@ -241,8 +241,7 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
|||||||
const endLine = parsedBlock?.endLine || defaultEndLine;
|
const endLine = parsedBlock?.endLine || defaultEndLine;
|
||||||
|
|
||||||
const updatedContent = await ApplyEditToFile(
|
const updatedContent = await ApplyEditToFile(
|
||||||
activeFile,
|
await plugin.app.vault.cachedRead(activeFile),
|
||||||
await plugin.app.vault.read(activeFile),
|
|
||||||
finalContent,
|
finalContent,
|
||||||
startLine,
|
startLine,
|
||||||
endLine
|
endLine
|
||||||
|
|||||||
@ -4,32 +4,31 @@ import { SearchAndReplaceToolArgs } from '../types/apply';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies changes to a file by replacing content within specified line range
|
* Applies changes to a file by replacing content within specified line range
|
||||||
* @param content - The new content to insert
|
* @param updateContent - The new content to insert
|
||||||
* @param currentFile - The file being modified
|
* @param currentFile - The file being modified
|
||||||
* @param currentFileContent - The current content of the file
|
* @param rawContent - The current content of the file
|
||||||
* @param startLine - Starting line number (1-based indexing, optional)
|
* @param startLine - Starting line number (1-based indexing, optional)
|
||||||
* @param endLine - Ending line number (1-based indexing, optional)
|
* @param endLine - Ending line number (1-based indexing, optional)
|
||||||
* @returns Promise resolving to the modified content or null if operation fails
|
* @returns Promise resolving to the modified content or null if operation fails
|
||||||
*/
|
*/
|
||||||
export const ApplyEditToFile = async (
|
export const ApplyEditToFile = async (
|
||||||
currentFile: TFile,
|
rawContent: string,
|
||||||
currentFileContent: string,
|
updateContent: string,
|
||||||
content: string,
|
|
||||||
startLine?: number,
|
startLine?: number,
|
||||||
endLine?: number,
|
endLine?: number,
|
||||||
): Promise<string | null> => {
|
): Promise<string | null> => {
|
||||||
try {
|
try {
|
||||||
// 如果文件为空,直接返回新内容
|
// if file is empty, return new content
|
||||||
if (!currentFileContent || currentFileContent.trim() === '') {
|
if (!rawContent || rawContent.trim() === '') {
|
||||||
return content;
|
return updateContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果要清空文件,直接返回空字符串
|
// if content is empty, return empty string
|
||||||
if (content === '') {
|
if (updateContent === '') {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const lines = currentFileContent.split('\n')
|
const lines = rawContent.split('\n')
|
||||||
const effectiveStartLine = Math.max(1, startLine ?? 1)
|
const effectiveStartLine = Math.max(1, startLine ?? 1)
|
||||||
const effectiveEndLine = Math.min(endLine ?? lines.length, lines.length)
|
const effectiveEndLine = Math.min(endLine ?? lines.length, lines.length)
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ export const ApplyEditToFile = async (
|
|||||||
// Construct new content
|
// Construct new content
|
||||||
return [
|
return [
|
||||||
...lines.slice(0, effectiveStartLine - 1),
|
...lines.slice(0, effectiveStartLine - 1),
|
||||||
content,
|
updateContent,
|
||||||
...lines.slice(effectiveEndLine)
|
...lines.slice(effectiveEndLine)
|
||||||
].join('\n')
|
].join('\n')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -58,16 +57,15 @@ function escapeRegExp(string: string): string {
|
|||||||
/**
|
/**
|
||||||
* 搜索和替换文件内容
|
* 搜索和替换文件内容
|
||||||
* @param currentFile - 当前文件
|
* @param currentFile - 当前文件
|
||||||
* @param currentFileContent - 当前文件内容
|
* @param rawContent - 当前文件内容
|
||||||
* @param search - 搜索内容
|
* @param search - 搜索内容
|
||||||
* @param replace - 替换内容
|
* @param replace - 替换内容
|
||||||
*/
|
*/
|
||||||
export const SearchAndReplace = async (
|
export const SearchAndReplace = async (
|
||||||
currentFile: TFile,
|
rawContent: string,
|
||||||
currentFileContent: string,
|
|
||||||
operations: SearchAndReplaceToolArgs['operations']
|
operations: SearchAndReplaceToolArgs['operations']
|
||||||
) => {
|
) => {
|
||||||
let lines = currentFileContent.split("\n")
|
let lines = rawContent.split("\n")
|
||||||
|
|
||||||
for (const op of operations) {
|
for (const op of operations) {
|
||||||
const flags = op.regexFlags ?? (op.ignoreCase ? "gi" : "g")
|
const flags = op.regexFlags ?? (op.ignoreCase ? "gi" : "g")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user