fix(teacher): 改善只读工具分页与读取预算

This commit is contained in:
2026-09-27 10:15:17 +08:00
parent 17c7e5c495
commit 56cefe84e7
8 changed files with 297 additions and 56 deletions

View File

@@ -277,6 +277,32 @@ export class CodingProjectFileService {
}));
}
async *textChunks(projectPath: string, requestedPath: string, signal: AbortSignal): AsyncGenerator<string> {
const relativePath = normalizeRelativePath(requestedPath);
const target = await containedExistingTarget(projectPath, relativePath);
if (!(await lstat(target)).isFile()) throw new Error('Project file path is not a file');
const handle = await open(target, 'r');
const stream = handle.createReadStream({ autoClose: false });
const decoder = new TextDecoder('utf-8', { fatal: true });
let pendingCR = '';
try {
for await (const chunk of stream) {
signal.throwIfAborted();
let text = pendingCR + decoder.decode(chunk as Buffer, { stream: true });
if (text.includes('\0')) throw new Error('Project file is not UTF-8 text');
// A Windows newline can straddle two filesystem chunks.
pendingCR = text.endsWith('\r') ? '\r' : '';
if (pendingCR) text = text.slice(0, -1);
if (text) yield text.replaceAll('\r\n', '\n');
}
const final = pendingCR + decoder.decode();
if (final) yield final;
} finally {
stream.destroy();
await handle.close();
}
}
async content(projectPath: string, requestedPath: string): Promise<CodingProjectFileContent> {
const relativePath = normalizeRelativePath(requestedPath);
const result = await readBoundedFile(projectPath, relativePath, MAX_CONTENT_BYTES);