(no commit message)
This commit is contained in:
104
utils/file_ops.py
Normal file
104
utils/file_ops.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""File operation utilities for nanocode."""
|
||||
|
||||
import glob as globlib
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
def read_file(path: str, offset: int = 0, limit: int = None) -> str:
|
||||
"""Read file contents with line numbers.
|
||||
|
||||
Args:
|
||||
path: Path to the file to read
|
||||
offset: Line number to start from (0-indexed)
|
||||
limit: Maximum number of lines to read
|
||||
|
||||
Returns:
|
||||
File contents with line numbers
|
||||
"""
|
||||
lines = open(path).readlines()
|
||||
if limit is None:
|
||||
limit = len(lines)
|
||||
selected = lines[offset : offset + limit]
|
||||
return "".join(f"{offset + idx + 1:4}| {line}" for idx, line in enumerate(selected))
|
||||
|
||||
|
||||
def write_file(path: str, content: str) -> str:
|
||||
"""Write content to a file.
|
||||
|
||||
Args:
|
||||
path: Path to the file to write
|
||||
content: Content to write to the file
|
||||
|
||||
Returns:
|
||||
'ok' on success
|
||||
"""
|
||||
with open(path, "w") as f:
|
||||
f.write(content)
|
||||
return "ok"
|
||||
|
||||
|
||||
def edit_file(path: str, old: str, new: str, replace_all: bool = False) -> str:
|
||||
"""Replace text in a file.
|
||||
|
||||
Args:
|
||||
path: Path to the file to edit
|
||||
old: Text to find and replace
|
||||
new: Replacement text
|
||||
replace_all: If True, replace all occurrences; otherwise old must be unique
|
||||
|
||||
Returns:
|
||||
'ok' on success, error message on failure
|
||||
"""
|
||||
text = open(path).read()
|
||||
if old not in text:
|
||||
return "error: old_string not found"
|
||||
count = text.count(old)
|
||||
if not replace_all and count > 1:
|
||||
return f"error: old_string appears {count} times, must be unique (use replace_all=True)"
|
||||
replacement = text.replace(old, new) if replace_all else text.replace(old, new, 1)
|
||||
with open(path, "w") as f:
|
||||
f.write(replacement)
|
||||
return "ok"
|
||||
|
||||
|
||||
def glob_files(pattern: str, path: str = ".") -> str:
|
||||
"""Find files matching a glob pattern, sorted by modification time.
|
||||
|
||||
Args:
|
||||
pattern: Glob pattern to match (e.g., '**/*.py')
|
||||
path: Base directory to search in
|
||||
|
||||
Returns:
|
||||
Newline-separated list of matching files
|
||||
"""
|
||||
full_pattern = (path + "/" + pattern).replace("//", "/")
|
||||
files = globlib.glob(full_pattern, recursive=True)
|
||||
files = sorted(
|
||||
files,
|
||||
key=lambda f: os.path.getmtime(f) if os.path.isfile(f) else 0,
|
||||
reverse=True,
|
||||
)
|
||||
return "\n".join(files) or "no files found"
|
||||
|
||||
|
||||
def grep_files(pattern: str, path: str = ".") -> str:
|
||||
"""Search files for a regex pattern.
|
||||
|
||||
Args:
|
||||
pattern: Regular expression pattern to search for
|
||||
path: Base directory to search in
|
||||
|
||||
Returns:
|
||||
Matching lines in format 'filepath:line_num:content'
|
||||
"""
|
||||
regex = re.compile(pattern)
|
||||
hits = []
|
||||
for filepath in globlib.glob(path + "/**", recursive=True):
|
||||
try:
|
||||
for line_num, line in enumerate(open(filepath), 1):
|
||||
if regex.search(line):
|
||||
hits.append(f"{filepath}:{line_num}:{line.rstrip()}")
|
||||
except Exception:
|
||||
pass
|
||||
return "\n".join(hits[:50]) or "no matches found"
|
||||
Reference in New Issue
Block a user