Skip to content

feat: enhance edit_block with multi-block, flags, and error handling #81

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,7 @@ coverage/
.idea
.history

server.log
server.log-e
# Temporary and backup files
backup/
*.bak
152 changes: 152 additions & 0 deletions docs/enhanced-edit-block.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Enhanced Edit Block Functionality

The `edit_block` tool has been enhanced with several new features to make it more powerful and flexible. This document explains the new capabilities and how to use them.

## Core Enhancements

1. **Multiple Block Support**: You can now include multiple search/replace blocks in a single command
2. **Global Replacement**: Replace all occurrences of a pattern, not just the first one
3. **Counted Replacements**: Replace only a specific number of occurrences
4. **Case-Insensitive Matching**: Match patterns regardless of case
5. **Dry Run Mode**: Simulate replacements without actually modifying files
6. **Enhanced Error Handling**: Better error reporting with details

## Usage

### Basic Syntax with Flags

The `edit_block` command now supports optional flags in the search block delimiter:

```text
filepath
<<<<<<< SEARCH[:flags]
content to find
=======
new content
>>>>>>> REPLACE
```text

Where `flags` can be any combination of:
- `g`: Global replacement (replace all occurrences)
- `i`: Case-insensitive matching
- `d`: Dry run (don't actually modify the file)
- `n:X`: Replace only X occurrences (where X is a positive number)

For example:
```text
/path/to/file.txt
<<<<<<< SEARCH:gi
mixed case text
=======
REPLACED TEXT
>>>>>>> REPLACE
```text

### Multiple Blocks

You can include multiple search/replace blocks in a single edit_block command:

```text
/path/to/file.txt
<<<<<<< SEARCH
first pattern
=======
first replacement
>>>>>>> REPLACE
<<<<<<< SEARCH:g
second pattern
=======
second replacement (global)
>>>>>>> REPLACE
<<<<<<< SEARCH:i
THIRD pattern
=======
third replacement (case-insensitive)
>>>>>>> REPLACE
```text

## Examples

### Global Replacement

Replace all occurrences of a pattern:

```text
/path/to/file.txt
<<<<<<< SEARCH:g
function oldName
=======
function newName
>>>>>>> REPLACE
```text

### Counted Replacement

Replace only the first 3 occurrences of a pattern:

```text
/path/to/file.txt
<<<<<<< SEARCH:n:3
repeated text
=======
limited replacement
>>>>>>> REPLACE
```text

### Case-Insensitive Matching

Match a pattern regardless of case:

```text
/path/to/file.txt
<<<<<<< SEARCH:i
WARNING
=======
ERROR
>>>>>>> REPLACE
```text

This will match "WARNING", "Warning", "warning", etc.

### Combined Flags

Use multiple flags together:

```text
/path/to/file.txt
<<<<<<< SEARCH:n:2:i
error
=======
warning
>>>>>>> REPLACE
```text

This will replace the first 2 occurrences of "error", "Error", "ERROR", etc. with "warning".

### Dry Run

Test replacements without modifying the file:

```text
/path/to/file.txt
<<<<<<< SEARCH:d
sensitive change
=======
test replacement
>>>>>>> REPLACE
```text

The output will show what would be changed, but the file remains unmodified.

## Error Handling

The enhanced implementation provides better error messages for common issues:

1. **Malformed blocks**: Detailed information about syntax errors
2. **Pattern size limits**: Warnings for excessively large patterns
3. **Missing blocks**: Clear indication when no valid blocks are found
4. **Block-specific errors**: Each block's errors are reported separately

## Backward Compatibility

All existing `edit_block` usage patterns continue to work as before. The enhancements are fully backward compatible with the original implementation.
2 changes: 2 additions & 0 deletions fix_markdown.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
sed -i '' 's/^```$/```text/' docs/enhanced-edit-block.md
38 changes: 29 additions & 9 deletions src/handlers/edit-search-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,37 @@ import {capture, withTimeout} from '../utils.js';
import {createErrorResponse} from '../error-handlers.js';

/**
* Handle edit_block command
* Handle edit_block command with enhanced capabilities
*/
export async function handleEditBlock(args: unknown): Promise<ServerResult> {
const parsed = EditBlockArgsSchema.parse(args);
const {filePath, searchReplace, error} = await parseEditBlock(parsed.blockContent);

if (error) {
return createErrorResponse(error);
try {
// Parse input arguments
const parsed = EditBlockArgsSchema.parse(args);

// Parse the block content (enhanced implementation)
const { filePath, searchReplace, errors } = await parseEditBlock(parsed.blockContent);

// Handle parsing errors
if (errors?.global) {
return createErrorResponse(errors.global);
}

// Report block-specific errors if present
if (errors?.blocks && errors.blocks.length > 0) {
const errorMessages = errors.blocks.map(error =>
`Block ${error.index + 1}${error.lineNumber ? ` (line ${error.lineNumber})` : ''}: ${error.error}`
).join('\n');

return createErrorResponse(`Parse errors in edit blocks:\n${errorMessages}`);
}

// Use enhanced implementation for all replacements
return await performSearchReplace(filePath, searchReplace);
} catch (error) {
// Maintain existing error handling
const errorMessage = error instanceof Error ? error.message : String(error);
return createErrorResponse(errorMessage);
}

return performSearchReplace(filePath, searchReplace);
}

/**
Expand Down Expand Up @@ -98,4 +118,4 @@ export async function handleSearchCode(args: unknown): Promise<ServerResult> {
return {
content: [{type: "text", text: formattedResults.trim()}],
};
}
}
Loading