qwen-code/packages/cli/src/acp-integration/service/filesystem.ts
tanzhenxin 831d74dbfe feat: Preserve UTF-8 BOM when editing files (Fix #1672)
- Add FileEncoding constants (UTF8, UTF8_BOM)
- Add detectFileBOM() to detect existing file encoding
- Modify writeTextFile() to support BOM option
- Add defaultFileEncoding configuration option
- Preserve BOM when editing existing files
- Use configured encoding for new files
- Add comprehensive tests (unit, integration, e2e)
- Update documentation

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-02-01 10:32:20 +08:00

84 lines
2.2 KiB
TypeScript

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type { FileSystemService } from '@qwen-code/qwen-code-core';
import type * as acp from '../acp.js';
import { ACP_ERROR_CODES } from '../errorCodes.js';
/**
* ACP client-based implementation of FileSystemService
*/
export class AcpFileSystemService implements FileSystemService {
constructor(
private readonly client: acp.Client,
private readonly sessionId: string,
private readonly capabilities: acp.FileSystemCapability,
private readonly fallback: FileSystemService,
) {}
async readTextFile(filePath: string): Promise<string> {
if (!this.capabilities.readTextFile) {
return this.fallback.readTextFile(filePath);
}
let response: { content: string };
try {
response = await this.client.readTextFile({
path: filePath,
sessionId: this.sessionId,
line: null,
limit: null,
});
} catch (error) {
const errorCode =
typeof error === 'object' && error !== null && 'code' in error
? (error as { code?: unknown }).code
: undefined;
if (errorCode === ACP_ERROR_CODES.RESOURCE_NOT_FOUND) {
const err = new Error(
`File not found: ${filePath}`,
) as NodeJS.ErrnoException;
err.code = 'ENOENT';
err.errno = -2;
err.path = filePath;
throw err;
}
throw error;
}
return response.content;
}
async writeTextFile(
filePath: string,
content: string,
options?: { bom?: boolean },
): Promise<void> {
if (!this.capabilities.writeTextFile) {
return this.fallback.writeTextFile(filePath, content, options);
}
// Prepend BOM character if requested
const finalContent = options?.bom ? '\uFEFF' + content : content;
await this.client.writeTextFile({
path: filePath,
content: finalContent,
sessionId: this.sessionId,
});
}
async detectFileBOM(filePath: string): Promise<boolean> {
// Always use fallback for BOM detection
return this.fallback.detectFileBOM(filePath);
}
findFiles(fileName: string, searchPaths: readonly string[]): string[] {
return this.fallback.findFiles(fileName, searchPaths);
}
}