def search_file()

in project/paperbench/paperbench/agents/aisi-basic-agent/_file_reader.py [0:0]


def search_file():
    async def execute(
        file: str, query: str, context_lines: int = 2, max_matches: int = 5, page: int = 1
    ) -> str:
        """Search for a keyword or phrase in a file and return matching lines with context.

        Args:
            file (str): Path to the file to search
            query (str): Text to search for (case-insensitive)
            context_lines (int): Number of lines of context to show before and after each match (default: 2)
            max_matches (int): Maximum number of matches to return per page (default: 5)
            page (int): Which page of results to return (1-indexed, default: 1)

        Returns:
            str: Matching lines with their line numbers and context, paginated

        Raises:
            ToolError: If the file cannot be read or if invalid parameters are provided
        """
        if not query:
            raise ToolError("Query cannot be empty.")
        if context_lines < 0:
            raise ToolError("context_lines must be >= 0")
        if max_matches < 1:
            raise ToolError("max_matches must be >= 1")
        if page < 1:
            raise ToolError("page must be >= 1")

        try:
            # Read the file
            content = await sandbox().read_file(file)

            # Split into lines
            lines = content.splitlines()

            # Find all matches (case-insensitive)
            all_matches = []
            query = query.lower()

            for i, line in enumerate(lines):
                if query in line.lower():
                    # Calculate context range
                    start = max(0, i - context_lines)
                    end = min(len(lines), i + context_lines + 1)

                    # Get context lines
                    context = []
                    for j in range(start, end):
                        prefix = ">>> " if j == i else "    "  # Highlight matching line
                        context.append(f"{prefix}{j+1}: {lines[j]}")

                    all_matches.append("\n".join(context))

            if not all_matches:
                return f"No matches found for '{query}' in {file}"

            # Calculate pagination
            total_matches = len(all_matches)
            total_pages = (total_matches + max_matches - 1) // max_matches

            if page > total_pages:
                return f"Invalid page number. There are only {total_pages} pages of results."

            start_idx = (page - 1) * max_matches
            end_idx = min(start_idx + max_matches, total_matches)

            # Get matches for this page
            matches = all_matches[start_idx:end_idx]

            # Build summary with pagination info
            summary = [
                f"Found {total_matches} matches for '{query}' in {file}",
                f"Showing matches {start_idx + 1}-{end_idx} (Page {page} of {total_pages})",
                "",  # Empty line for spacing
            ]

            # Add match index to each result
            numbered_matches = []
            for i, match in enumerate(matches, start=start_idx + 1):
                numbered_matches.append(f"[Match {i} of {total_matches}]\n{match}")

            return "\n\n".join(summary + numbered_matches)

        except FileNotFoundError:
            raise ToolError(f"File '{file}' not found")
        except Exception as e:
            raise ToolError(f"Error searching file: {str(e)}")

    return execute