- Split 25+ index.ts files into hook.ts + extracted modules - Rename all catch-all utils.ts/helpers.ts to domain-specific names - Split src/tools/lsp/ into ~15 focused modules - Split src/tools/delegate-task/ into ~18 focused modules - Separate shared types from implementation - 155 files changed, 60+ new files created - All typecheck clean, 61 tests pass
122 lines
4.1 KiB
TypeScript
122 lines
4.1 KiB
TypeScript
import { readFileSync, writeFileSync } from "fs"
|
|
|
|
import { uriToPath } from "./lsp-client-wrapper"
|
|
import type { TextEdit, WorkspaceEdit } from "./types"
|
|
|
|
export interface ApplyResult {
|
|
success: boolean
|
|
filesModified: string[]
|
|
totalEdits: number
|
|
errors: string[]
|
|
}
|
|
|
|
function applyTextEditsToFile(filePath: string, edits: TextEdit[]): { success: boolean; editCount: number; error?: string } {
|
|
try {
|
|
let content = readFileSync(filePath, "utf-8")
|
|
const lines = content.split("\n")
|
|
|
|
const sortedEdits = [...edits].sort((a, b) => {
|
|
if (b.range.start.line !== a.range.start.line) {
|
|
return b.range.start.line - a.range.start.line
|
|
}
|
|
return b.range.start.character - a.range.start.character
|
|
})
|
|
|
|
for (const edit of sortedEdits) {
|
|
const startLine = edit.range.start.line
|
|
const startChar = edit.range.start.character
|
|
const endLine = edit.range.end.line
|
|
const endChar = edit.range.end.character
|
|
|
|
if (startLine === endLine) {
|
|
const line = lines[startLine] || ""
|
|
lines[startLine] = line.substring(0, startChar) + edit.newText + line.substring(endChar)
|
|
} else {
|
|
const firstLine = lines[startLine] || ""
|
|
const lastLine = lines[endLine] || ""
|
|
const newContent = firstLine.substring(0, startChar) + edit.newText + lastLine.substring(endChar)
|
|
lines.splice(startLine, endLine - startLine + 1, ...newContent.split("\n"))
|
|
}
|
|
}
|
|
|
|
writeFileSync(filePath, lines.join("\n"), "utf-8")
|
|
return { success: true, editCount: edits.length }
|
|
} catch (err) {
|
|
return { success: false, editCount: 0, error: err instanceof Error ? err.message : String(err) }
|
|
}
|
|
}
|
|
|
|
export function applyWorkspaceEdit(edit: WorkspaceEdit | null): ApplyResult {
|
|
if (!edit) {
|
|
return { success: false, filesModified: [], totalEdits: 0, errors: ["No edit provided"] }
|
|
}
|
|
|
|
const result: ApplyResult = { success: true, filesModified: [], totalEdits: 0, errors: [] }
|
|
|
|
if (edit.changes) {
|
|
for (const [uri, edits] of Object.entries(edit.changes)) {
|
|
const filePath = uriToPath(uri)
|
|
const applyResult = applyTextEditsToFile(filePath, edits)
|
|
|
|
if (applyResult.success) {
|
|
result.filesModified.push(filePath)
|
|
result.totalEdits += applyResult.editCount
|
|
} else {
|
|
result.success = false
|
|
result.errors.push(`${filePath}: ${applyResult.error}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
if (edit.documentChanges) {
|
|
for (const change of edit.documentChanges) {
|
|
if ("kind" in change) {
|
|
if (change.kind === "create") {
|
|
try {
|
|
const filePath = uriToPath(change.uri)
|
|
writeFileSync(filePath, "", "utf-8")
|
|
result.filesModified.push(filePath)
|
|
} catch (err) {
|
|
result.success = false
|
|
result.errors.push(`Create ${change.uri}: ${err}`)
|
|
}
|
|
} else if (change.kind === "rename") {
|
|
try {
|
|
const oldPath = uriToPath(change.oldUri)
|
|
const newPath = uriToPath(change.newUri)
|
|
const content = readFileSync(oldPath, "utf-8")
|
|
writeFileSync(newPath, content, "utf-8")
|
|
require("fs").unlinkSync(oldPath)
|
|
result.filesModified.push(newPath)
|
|
} catch (err) {
|
|
result.success = false
|
|
result.errors.push(`Rename ${change.oldUri}: ${err}`)
|
|
}
|
|
} else if (change.kind === "delete") {
|
|
try {
|
|
const filePath = uriToPath(change.uri)
|
|
require("fs").unlinkSync(filePath)
|
|
result.filesModified.push(filePath)
|
|
} catch (err) {
|
|
result.success = false
|
|
result.errors.push(`Delete ${change.uri}: ${err}`)
|
|
}
|
|
}
|
|
} else {
|
|
const filePath = uriToPath(change.textDocument.uri)
|
|
const applyResult = applyTextEditsToFile(filePath, change.edits)
|
|
|
|
if (applyResult.success) {
|
|
result.filesModified.push(filePath)
|
|
result.totalEdits += applyResult.editCount
|
|
} else {
|
|
result.success = false
|
|
result.errors.push(`${filePath}: ${applyResult.error}`)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|