delete_symbol
Remove a TypeScript/JavaScript symbol (variable, function, class) and optionally all its references across files for cleaner, refactored code.
Instructions
Delete a TypeScript/JavaScript symbol (variable, function, class, etc.) and all its references
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| filePath | Yes | File path containing the symbol (relative to root) | |
| line | Yes | Line number (1-based) or string to match in the line | |
| removeReferences | No | Also delete all references to the symbol | |
| root | Yes | Root directory for resolving relative paths | |
| symbolName | Yes | Name of the symbol to delete |
Implementation Reference
- src/tools/lsp/deleteSymbol.ts:41-203 (handler)The core handler function that implements the delete symbol logic using LSP. It finds the symbol position, locates references if requested, creates workspace edits to delete occurrences, and applies the edits.async function handleDeleteSymbol( { root, relativePath, line, textTarget, removeReferences = true, }: z.infer<typeof schema>, client: LSPClient, ): Promise<DeleteSymbolResult> { if (!client) { throw new Error("LSP client not initialized"); } // Convert to absolute path const absolutePath = path.isAbsolute(relativePath) ? relativePath : path.join(root, relativePath); // Convert to file URI const fileUri = pathToFileURL(absolutePath).toString(); // Read the file content const content = await fs.readFile(absolutePath, "utf-8"); // Open the document in LSP client.openDocument(fileUri, content); let locations: Location[] = []; try { // Resolve line parameter const lines = content.split("\n"); const lineIndex = resolveLineParameter(lines, line); const resolvedLine = lineIndex + 1; // Convert back to 1-based // Find the symbol position on the line const lineContent = lines[lineIndex]; const symbolIndex = lineContent.indexOf(textTarget); if (symbolIndex === -1) { throw new Error( `Symbol "${textTarget}" not found on line ${resolvedLine}: "${lineContent.trim()}"`, ); } // Create position for the symbol const position: Position = { line: lineIndex, // LSP uses 0-based line numbers character: symbolIndex, }; // First, find all references if removeReferences is true locations = removeReferences ? await client.findReferences(fileUri, position) : [{ uri: fileUri, range: { start: position, end: position } }]; if (locations.length === 0) { return { applied: false, deletedFromFiles: new Set(), totalDeleted: 0, failureReason: "No references found for the symbol", }; } // Create workspace edit const workspaceEdit: WorkspaceEdit = { changes: {}, }; // Group locations by file const fileChanges = new Map<string, Range[]>(); for (const location of locations) { const ranges = fileChanges.get(location.uri) || []; ranges.push(location.range); fileChanges.set(location.uri, ranges); } // Create text edits for each file for (const [uri, ranges] of fileChanges) { // Sort ranges in reverse order to avoid position shifts const sortedRanges = ranges.sort((a, b) => { if (a.start.line !== b.start.line) { return b.start.line - a.start.line; } return b.start.character - a.start.character; }); // Read file content if it's different from the current file let fileContent: string; let fileLines: string[]; if (uri === fileUri) { fileContent = content; fileLines = lines; } else { const absoluteFilePath = fileURLToPath(uri); fileContent = await fs.readFile(absoluteFilePath, "utf-8"); fileLines = fileContent.split("\n"); // Open document for editing client.openDocument(uri, fileContent); } const edits: TextEdit[] = []; for (const range of sortedRanges) { // Check if this is a complete line deletion const lineText = fileLines[range.start.line]; const beforeSymbol = lineText .substring(0, range.start.character) .trim(); const afterSymbol = lineText.substring(range.end.character).trim(); if (!beforeSymbol && !afterSymbol) { // Delete the entire line edits.push({ range: { start: { line: range.start.line, character: 0 }, end: { line: range.start.line + 1, character: 0 }, }, newText: "", }); } else { // Delete just the symbol edits.push({ range, newText: "", }); } } workspaceEdit.changes![uri] = edits; } // Apply the workspace edit const result = await client.applyEdit( workspaceEdit, `Delete symbol "${textTarget}"`, ); if (!result.applied) { return { applied: false, deletedFromFiles: new Set(), totalDeleted: 0, failureReason: result.failureReason || "Failed to apply workspace edit", }; } return { applied: true, deletedFromFiles: new Set(fileChanges.keys()), totalDeleted: locations.length, }; } finally { // Close all opened documents for (const uri of new Set([fileUri, ...locations.map((l) => l.uri)])) { client.closeDocument(uri); } } }
- src/tools/lsp/deleteSymbol.ts:16-32 (schema)Zod schema defining the input parameters for the delete symbol tool.const schemaShape = { root: z.string().describe("Root directory for resolving relative paths"), relativePath: z .string() .describe("File path containing the symbol (relative to root)"), line: z .union([z.number(), z.string()]) .describe("Line number (1-based) or string to match in the line"), textTarget: z.string().describe("Name of the symbol to delete"), removeReferences: z .boolean() .optional() .default(true) .describe("Also delete all references to the symbol"), }; const schema = z.object(schemaShape);
- src/tools/lsp/deleteSymbol.ts:224-237 (registration)Factory function creating the MCP tool definition named 'lsp_delete_symbol', which wraps the handler.export function createDeleteSymbolTool( client: LSPClient, ): McpToolDef<typeof schema> { return { name: "lsp_delete_symbol", description: "Delete a symbol and optionally all its references using LSP. Requires exact line:column position of the symbol.", schema, execute: async (args) => { const result = await handleDeleteSymbol(args, client); return formatDeleteSymbolResult(result); }, }; }
- src/tools/lsp/createLspTools.ts:40-40 (registration)The delete symbol tool is included in the list of all LSP tools returned by createLSPTools.createDeleteSymbolTool(client),
- src/tools/filterTools.ts:37-39 (helper)Helper function in capability checker that determines if the delete_symbol tool is supported based on codeActionProvider capability.delete_symbol: (checker) => { return checker.hasCapability("codeActionProvider"); },