Apply ordered line edits with newline and encoding preservation.
(
path: Annotated[str, ParamMeta(description="Workspace-relative file to edit")],
*,
start_line: Annotated[int, ParamMeta(description="1-based line where the replacement should begin")],
end_line: Annotated[
Optional[int],
ParamMeta(description="Last line (>= start_line-1) to replace; defaults to start_line"),
] = None,
replacement: Annotated[
Optional[str],
ParamMeta(description="Text that should replace the selected line range"),
] = "",
encoding: Annotated[str, ParamMeta(description="Text encoding or 'auto'")]="auto",
newline: Annotated[
str,
ParamMeta(description="Newline style: 'preserve', 'lf', 'crlf', or 'cr'"),
]="preserve",
ensure_trailing_newline: Annotated[
Optional[bool],
ParamMeta(description="Force presence/absence of trailing newline; default preserves original"),
] = None,
_context: Dict[str, Any] | None = None,
)
| 536 | |
| 537 | |
| 538 | def apply_text_edits( |
| 539 | path: Annotated[str, ParamMeta(description="Workspace-relative file to edit")], |
| 540 | *, |
| 541 | start_line: Annotated[int, ParamMeta(description="1-based line where the replacement should begin")], |
| 542 | end_line: Annotated[ |
| 543 | Optional[int], |
| 544 | ParamMeta(description="Last line (>= start_line-1) to replace; defaults to start_line"), |
| 545 | ] = None, |
| 546 | replacement: Annotated[ |
| 547 | Optional[str], |
| 548 | ParamMeta(description="Text that should replace the selected line range"), |
| 549 | ] = "", |
| 550 | encoding: Annotated[str, ParamMeta(description="Text encoding or 'auto'")]="auto", |
| 551 | newline: Annotated[ |
| 552 | str, |
| 553 | ParamMeta(description="Newline style: 'preserve', 'lf', 'crlf', or 'cr'"), |
| 554 | ]="preserve", |
| 555 | ensure_trailing_newline: Annotated[ |
| 556 | Optional[bool], |
| 557 | ParamMeta(description="Force presence/absence of trailing newline; default preserves original"), |
| 558 | ] = None, |
| 559 | _context: Dict[str, Any] | None = None, |
| 560 | ) -> Dict[str, Any]: |
| 561 | """Apply ordered line edits with newline and encoding preservation.""" |
| 562 | |
| 563 | ctx = FileToolContext(_context) |
| 564 | target = ctx.resolve_under_workspace(path) |
| 565 | if not target.exists() or not target.is_file(): |
| 566 | raise FileNotFoundError(f"File not found: {path}") |
| 567 | |
| 568 | _check_attachments_not_modified(path) |
| 569 | |
| 570 | normalized = _normalize_edits(_build_single_edit(start_line, end_line, replacement)) |
| 571 | original_text, used_encoding = _read_text_content(target, encoding) |
| 572 | lines, had_trailing_newline = _split_lines(original_text) |
| 573 | newline_style = _resolve_newline_choice(newline, _detect_newline(original_text)) |
| 574 | |
| 575 | _apply_edits_in_place(lines, normalized) |
| 576 | |
| 577 | if ensure_trailing_newline is None: |
| 578 | final_trailing = had_trailing_newline |
| 579 | else: |
| 580 | final_trailing = ensure_trailing_newline |
| 581 | |
| 582 | rendered = newline_style.join(lines) |
| 583 | if final_trailing: |
| 584 | rendered += newline_style |
| 585 | |
| 586 | target.parent.mkdir(parents=True, exist_ok=True) |
| 587 | target.write_text(rendered, encoding=used_encoding) |
| 588 | stat = target.stat() |
| 589 | return { |
| 590 | "path": ctx.to_workspace_relative(target), |
| 591 | "encoding": used_encoding, |
| 592 | "newline": newline_style, |
| 593 | "line_count": len(lines), |
| 594 | "applied_edits": len(normalized), |
| 595 | "trailing_newline": final_trailing, |
nothing calls this directly
no test coverage detected