chore: remove all cn comments (#277)
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* 中文字符的Unicode范围正则表达式
|
||||
*/
|
||||
const CHINESE_REGEX = /[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff]/;
|
||||
const CHINESE_EXTRACT_REGEX = /[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff\u3000-\u303f\uff00-\uffef]+/g;
|
||||
|
||||
/**
|
||||
* 检测文本是否包含中文字符
|
||||
*/
|
||||
export const containsChinese = (text: string): boolean => {
|
||||
return CHINESE_REGEX.test(text);
|
||||
};
|
||||
|
||||
/**
|
||||
* 提取文本中的中文部分
|
||||
*/
|
||||
export const extractChineseParts = (text: string): string[] => {
|
||||
return text.match(CHINESE_EXTRACT_REGEX) || [];
|
||||
};
|
||||
|
||||
/**
|
||||
* 计算文本中中文字符的数量
|
||||
*/
|
||||
export const countChineseCharacters = (text: string): number => {
|
||||
const matches = text.match(CHINESE_EXTRACT_REGEX);
|
||||
if (!matches) return 0;
|
||||
|
||||
return matches.reduce((count, match) => count + match.length, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* 检测文本是否主要由中文组成
|
||||
*/
|
||||
export const isPrimarilyChinese = (text: string, threshold: number = 0.5): boolean => {
|
||||
const totalLength = text.length;
|
||||
if (totalLength === 0) return false;
|
||||
|
||||
const chineseLength = countChineseCharacters(text);
|
||||
return chineseLength / totalLength >= threshold;
|
||||
};
|
||||
|
||||
/**
|
||||
* 清理注释文本,移除注释符号和多余空格
|
||||
*/
|
||||
export const cleanCommentText = (
|
||||
text: string,
|
||||
commentType: 'single-line' | 'multi-line',
|
||||
language?: string
|
||||
): string => {
|
||||
let cleaned = text;
|
||||
|
||||
if (commentType === 'single-line') {
|
||||
// 根据语言类型移除不同的单行注释符号
|
||||
switch (language) {
|
||||
case 'yaml':
|
||||
case 'toml':
|
||||
case 'shell':
|
||||
case 'python':
|
||||
case 'ruby':
|
||||
cleaned = cleaned.replace(/^#\s*/, '');
|
||||
break;
|
||||
case 'ini':
|
||||
cleaned = cleaned.replace(/^[;#]\s*/, '');
|
||||
break;
|
||||
case 'php':
|
||||
cleaned = cleaned.replace(/^(?:\/\/|#)\s*/, '');
|
||||
break;
|
||||
default:
|
||||
// JavaScript/TypeScript/Go/Java/C/C++/C# style
|
||||
cleaned = cleaned.replace(/^\/\/\s*/, '');
|
||||
}
|
||||
} else if (commentType === 'multi-line') {
|
||||
// 根据语言类型移除不同的多行注释符号
|
||||
switch (language) {
|
||||
case 'html':
|
||||
case 'xml':
|
||||
case 'markdown':
|
||||
cleaned = cleaned.replace(/^<!--\s*/, '').replace(/\s*-->$/, '');
|
||||
break;
|
||||
case 'python':
|
||||
cleaned = cleaned.replace(/^"""\s*/, '').replace(/\s*"""$/, '');
|
||||
break;
|
||||
case 'ruby':
|
||||
cleaned = cleaned.replace(/^=begin\s*/, '').replace(/\s*=end$/, '');
|
||||
break;
|
||||
default:
|
||||
// JavaScript/TypeScript/Go/Java/C/C++/C#/CSS style
|
||||
cleaned = cleaned.replace(/^\/\*\s*/, '').replace(/\s*\*\/$/, '');
|
||||
// 移除每行开头的 * 符号
|
||||
cleaned = cleaned.replace(/^\s*\*\s?/gm, '');
|
||||
}
|
||||
}
|
||||
|
||||
// 移除多余的空格和换行
|
||||
cleaned = cleaned.trim();
|
||||
|
||||
return cleaned;
|
||||
};
|
||||
|
||||
/**
|
||||
* 验证翻译结果是否有效
|
||||
*/
|
||||
export const isValidTranslation = (original: string, translated: string): boolean => {
|
||||
// 基本验证
|
||||
if (!translated || translated.trim().length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否还包含中文(可能翻译失败)
|
||||
if (containsChinese(translated)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查长度是否合理(翻译后的文本不应该比原文长太多)
|
||||
if (translated.length > original.length * 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -0,0 +1,173 @@
|
||||
import { Result } from '../types/index';
|
||||
|
||||
/**
|
||||
* 函数组合 - 从左到右执行
|
||||
*/
|
||||
export const pipe =
|
||||
<T>(...fns: Function[]) =>
|
||||
(value: T) =>
|
||||
fns.reduce((acc, fn) => fn(acc), value);
|
||||
|
||||
/**
|
||||
* 函数组合 - 从右到左执行
|
||||
*/
|
||||
export const compose =
|
||||
<T>(...fns: Function[]) =>
|
||||
(value: T) =>
|
||||
fns.reduceRight((acc, fn) => fn(acc), value);
|
||||
|
||||
/**
|
||||
* 柯里化函数
|
||||
*/
|
||||
export const curry =
|
||||
(fn: Function) =>
|
||||
(...args: any[]) =>
|
||||
args.length >= fn.length
|
||||
? fn(...args)
|
||||
: (...more: any[]) => curry(fn)(...args, ...more);
|
||||
|
||||
/**
|
||||
* 异步映射
|
||||
*/
|
||||
export const asyncMap = curry(
|
||||
async <T, U>(fn: (item: T) => Promise<U>, items: T[]): Promise<U[]> =>
|
||||
Promise.all(items.map(fn)),
|
||||
);
|
||||
|
||||
/**
|
||||
* 异步过滤
|
||||
*/
|
||||
export const asyncFilter = curry(
|
||||
async <T>(
|
||||
predicate: (item: T) => Promise<boolean>,
|
||||
items: T[],
|
||||
): Promise<T[]> => {
|
||||
const results = await Promise.all(items.map(predicate));
|
||||
return items.filter((_, index) => results[index]);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 异步归约
|
||||
*/
|
||||
export const asyncReduce = curry(
|
||||
async <T, U>(
|
||||
fn: (acc: U, item: T) => Promise<U>,
|
||||
initial: U,
|
||||
items: T[],
|
||||
): Promise<U> => {
|
||||
let result = initial;
|
||||
for (const item of items) {
|
||||
result = await fn(result, item);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 创建成功结果
|
||||
*/
|
||||
export const success = <T>(data: T): Result<T> => ({ success: true, data });
|
||||
|
||||
/**
|
||||
* 创建失败结果
|
||||
*/
|
||||
export const failure = <E>(error: E): Result<never, E> => ({
|
||||
success: false,
|
||||
error,
|
||||
});
|
||||
|
||||
/**
|
||||
* 安全的异步操作包装
|
||||
*/
|
||||
export const tryCatch = async <T>(fn: () => Promise<T>): Promise<Result<T>> => {
|
||||
try {
|
||||
const data = await fn();
|
||||
return success(data);
|
||||
} catch (error) {
|
||||
return failure(error instanceof Error ? error : new Error(String(error)));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 同步版本的安全操作包装
|
||||
*/
|
||||
export const tryCatchSync = <T>(fn: () => T): Result<T> => {
|
||||
try {
|
||||
const data = fn();
|
||||
return success(data);
|
||||
} catch (error) {
|
||||
return failure(error instanceof Error ? error : new Error(String(error)));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 数组分块
|
||||
*/
|
||||
export const chunk = <T>(array: T[], size: number): T[][] => {
|
||||
const chunks: T[][] = [];
|
||||
for (let i = 0; i < array.length; i += size) {
|
||||
chunks.push(array.slice(i, i + size));
|
||||
}
|
||||
return chunks;
|
||||
};
|
||||
|
||||
/**
|
||||
* 延迟执行
|
||||
*/
|
||||
export const delay = (ms: number): Promise<void> =>
|
||||
new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* 重试机制
|
||||
*/
|
||||
export const retry = async <T>(
|
||||
fn: () => Promise<T>,
|
||||
maxAttempts: number = 3,
|
||||
delayMs: number = 1000,
|
||||
): Promise<T> => {
|
||||
let lastError: Error;
|
||||
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
try {
|
||||
return await fn();
|
||||
} catch (error) {
|
||||
lastError = error instanceof Error ? error : new Error(String(error));
|
||||
|
||||
if (attempt < maxAttempts) {
|
||||
await delay(delayMs * attempt); // 指数退避
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError!;
|
||||
};
|
||||
|
||||
/**
|
||||
* 深度合并对象
|
||||
*/
|
||||
export const deepMerge = <T extends Record<string, any>>(
|
||||
target: T,
|
||||
source: Partial<T>,
|
||||
): T => {
|
||||
const result = { ...target } as T;
|
||||
|
||||
for (const key in source) {
|
||||
if (source[key] !== undefined) {
|
||||
if (
|
||||
typeof source[key] === 'object' &&
|
||||
source[key] !== null &&
|
||||
!Array.isArray(source[key]) &&
|
||||
typeof target[key] === 'object' &&
|
||||
target[key] !== null &&
|
||||
!Array.isArray(target[key])
|
||||
) {
|
||||
(result as any)[key] = deepMerge(target[key], source[key]!);
|
||||
} else {
|
||||
(result as any)[key] = source[key]!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
import { simpleGit } from 'simple-git';
|
||||
import * as path from 'path';
|
||||
import { tryCatch } from './fp';
|
||||
import { Result } from '../types/index';
|
||||
|
||||
/**
|
||||
* 获取Git仓库中的所有已跟踪文件
|
||||
*/
|
||||
export const getGitTrackedFiles = async (
|
||||
root: string,
|
||||
): Promise<Result<string[]>> => {
|
||||
return tryCatch(async () => {
|
||||
const git = simpleGit(root);
|
||||
const files = await git.raw(['ls-files']);
|
||||
|
||||
return files
|
||||
.split('\n')
|
||||
.filter(Boolean)
|
||||
.map(file => path.resolve(root, file));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取Git仓库中的所有文件(包括未跟踪的)
|
||||
*/
|
||||
export const getAllGitFiles = async (
|
||||
root: string,
|
||||
): Promise<Result<string[]>> => {
|
||||
return tryCatch(async () => {
|
||||
const git = simpleGit(root);
|
||||
|
||||
// 获取已跟踪的文件
|
||||
const trackedFiles = await git.raw(['ls-files']);
|
||||
const trackedFilesArray = trackedFiles
|
||||
.split('\n')
|
||||
.filter(Boolean)
|
||||
.map(file => path.resolve(root, file));
|
||||
|
||||
// 获取未跟踪的文件
|
||||
const status = await git.status();
|
||||
const untrackedFiles = status.not_added.map(file =>
|
||||
path.resolve(root, file),
|
||||
);
|
||||
|
||||
// 合并并去重
|
||||
const allFiles = [...new Set([...trackedFilesArray, ...untrackedFiles])];
|
||||
|
||||
return allFiles;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查目录是否是Git仓库
|
||||
*/
|
||||
export const isGitRepository = async (
|
||||
root: string,
|
||||
): Promise<Result<boolean>> => {
|
||||
return tryCatch(async () => {
|
||||
const git = simpleGit(root);
|
||||
await git.status();
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取Git仓库的根目录
|
||||
*/
|
||||
export const getGitRoot = async (cwd: string): Promise<Result<string>> => {
|
||||
return tryCatch(async () => {
|
||||
const git = simpleGit(cwd);
|
||||
const root = await git.revparse(['--show-toplevel']);
|
||||
return root.trim();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查文件是否被Git忽略
|
||||
*/
|
||||
export const isIgnoredByGit = async (
|
||||
root: string,
|
||||
filePath: string,
|
||||
): Promise<Result<boolean>> => {
|
||||
return tryCatch(async () => {
|
||||
const git = simpleGit(root);
|
||||
const relativePath = path.relative(root, filePath);
|
||||
|
||||
try {
|
||||
await git.raw(['check-ignore', relativePath]);
|
||||
return true; // 文件被忽略
|
||||
} catch {
|
||||
return false; // 文件未被忽略
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,227 @@
|
||||
import { SourceFileLanguage, CommentPattern } from '../types/index';
|
||||
|
||||
/**
|
||||
* 根据文件扩展名识别编程语言
|
||||
*/
|
||||
export const detectLanguage = (filePath: string): SourceFileLanguage => {
|
||||
const ext = filePath.toLowerCase().split('.').pop();
|
||||
|
||||
const languageMap: Record<string, SourceFileLanguage> = {
|
||||
'ts': 'typescript',
|
||||
'tsx': 'typescript',
|
||||
'js': 'javascript',
|
||||
'jsx': 'javascript',
|
||||
'go': 'go',
|
||||
'md': 'markdown',
|
||||
'txt': 'text',
|
||||
'json': 'json',
|
||||
'yaml': 'yaml',
|
||||
'yml': 'yaml',
|
||||
'toml': 'toml',
|
||||
'ini': 'ini',
|
||||
'conf': 'ini',
|
||||
'config': 'ini',
|
||||
'sh': 'shell',
|
||||
'bash': 'shell',
|
||||
'zsh': 'shell',
|
||||
'fish': 'shell',
|
||||
'py': 'python',
|
||||
'css': 'css',
|
||||
'scss': 'css',
|
||||
'sass': 'css',
|
||||
'less': 'css',
|
||||
'html': 'html',
|
||||
'htm': 'html',
|
||||
'xml': 'xml',
|
||||
'php': 'php',
|
||||
'rb': 'ruby',
|
||||
'rs': 'rust',
|
||||
'java': 'java',
|
||||
'c': 'c',
|
||||
'h': 'c',
|
||||
'cpp': 'cpp',
|
||||
'cxx': 'cpp',
|
||||
'cc': 'cpp',
|
||||
'hpp': 'cpp',
|
||||
'cs': 'csharp'
|
||||
};
|
||||
|
||||
return languageMap[ext || ''] || 'other';
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据文件扩展名过滤文件
|
||||
*/
|
||||
export const filterFilesByExtensions = (
|
||||
files: string[],
|
||||
extensions: string[]
|
||||
): string[] => {
|
||||
if (extensions.length === 0) {
|
||||
// 默认支持的文本文件扩展名
|
||||
const defaultExtensions = [
|
||||
'.ts', '.tsx', '.js', '.jsx', '.go', '.md', '.txt', '.json',
|
||||
'.yaml', '.yml', '.toml', '.ini', '.conf', '.config',
|
||||
'.sh', '.bash', '.zsh', '.fish', '.py', '.css', '.scss', '.sass', '.less',
|
||||
'.html', '.htm', '.xml', '.php', '.rb', '.rs', '.java', '.c', '.h',
|
||||
'.cpp', '.cxx', '.cc', '.hpp', '.cs'
|
||||
];
|
||||
return files.filter(file =>
|
||||
defaultExtensions.some(ext => file.toLowerCase().endsWith(ext))
|
||||
);
|
||||
}
|
||||
|
||||
return files.filter(file => {
|
||||
const lowerFile = file.toLowerCase();
|
||||
return extensions.some(ext => {
|
||||
const lowerExt = ext.toLowerCase();
|
||||
// 如果扩展名已经有点号,直接使用;否则添加点号
|
||||
const extWithDot = lowerExt.startsWith('.') ? lowerExt : `.${lowerExt}`;
|
||||
return lowerFile.endsWith(extWithDot);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取不同编程语言的注释模式
|
||||
*/
|
||||
export const getCommentPatterns = (language: SourceFileLanguage): CommentPattern | null => {
|
||||
const commentPatterns: Record<SourceFileLanguage, CommentPattern> = {
|
||||
typescript: {
|
||||
single: /(?:^|[^:])\s*\/\/(.*)$/gm,
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
},
|
||||
javascript: {
|
||||
single: /(?:^|[^:])\s*\/\/(.*)$/gm,
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
},
|
||||
go: {
|
||||
single: /\/\/(.*)$/gm,
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
},
|
||||
markdown: {
|
||||
single: /<!--(.*)-->/g,
|
||||
multiStart: /<!--/g,
|
||||
multiEnd: /-->/g
|
||||
},
|
||||
text: {
|
||||
single: /^(.*)$/gm, // 文本文件每行都可能是注释
|
||||
multiStart: /^/g,
|
||||
multiEnd: /$/g
|
||||
},
|
||||
json: {
|
||||
single: /\/\/(.*)$/gm, // JSON通常不支持注释,但一些工具支持
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
},
|
||||
yaml: {
|
||||
single: /#(.*)$/gm,
|
||||
multiStart: /^$/g, // YAML不支持多行注释
|
||||
multiEnd: /^$/g
|
||||
},
|
||||
toml: {
|
||||
single: /#(.*)$/gm,
|
||||
multiStart: /^$/g, // TOML不支持多行注释
|
||||
multiEnd: /^$/g
|
||||
},
|
||||
ini: {
|
||||
single: /[;#](.*)$/gm, // INI文件支持 ; 和 # 作为注释
|
||||
multiStart: /^$/g, // INI不支持多行注释
|
||||
multiEnd: /^$/g
|
||||
},
|
||||
shell: {
|
||||
single: /#(.*)$/gm,
|
||||
multiStart: /^$/g, // Shell脚本不支持多行注释
|
||||
multiEnd: /^$/g
|
||||
},
|
||||
python: {
|
||||
single: /#(.*)$/gm,
|
||||
multiStart: /"""[\s\S]*?$/gm, // Python docstring
|
||||
multiEnd: /[\s\S]*?"""/gm
|
||||
},
|
||||
css: {
|
||||
single: /^$/g, // CSS不支持单行注释
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
},
|
||||
html: {
|
||||
single: /^$/g, // HTML不支持单行注释
|
||||
multiStart: /<!--/g,
|
||||
multiEnd: /-->/g
|
||||
},
|
||||
xml: {
|
||||
single: /^$/g, // XML不支持单行注释
|
||||
multiStart: /<!--/g,
|
||||
multiEnd: /-->/g
|
||||
},
|
||||
php: {
|
||||
single: /(?:\/\/|#)(.*)$/gm, // PHP支持 // 和 # 作为单行注释
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
},
|
||||
ruby: {
|
||||
single: /#(.*)$/gm,
|
||||
multiStart: /=begin/g,
|
||||
multiEnd: /=end/g
|
||||
},
|
||||
rust: {
|
||||
single: /\/\/(.*)$/gm,
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
},
|
||||
java: {
|
||||
single: /\/\/(.*)$/gm,
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
},
|
||||
c: {
|
||||
single: /\/\/(.*)$/gm,
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
},
|
||||
cpp: {
|
||||
single: /\/\/(.*)$/gm,
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
},
|
||||
csharp: {
|
||||
single: /\/\/(.*)$/gm,
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
},
|
||||
other: {
|
||||
single: /\/\/(.*)$/gm,
|
||||
multiStart: /\/\*/g,
|
||||
multiEnd: /\*\//g
|
||||
}
|
||||
};
|
||||
|
||||
return commentPatterns[language] || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查文件是否支持处理
|
||||
*/
|
||||
export const isSupportedFile = (filePath: string): boolean => {
|
||||
const language = detectLanguage(filePath);
|
||||
return language !== 'other';
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取文件的MIME类型(用于判断是否为文本文件)
|
||||
*/
|
||||
export const isTextFile = (filePath: string): boolean => {
|
||||
const textExtensions = [
|
||||
'.ts', '.tsx', '.js', '.jsx', '.go', '.md', '.txt', '.json',
|
||||
'.css', '.scss', '.sass', '.less', '.html', '.htm', '.xml',
|
||||
'.yaml', '.yml', '.toml', '.ini', '.conf', '.config',
|
||||
'.sh', '.bash', '.zsh', '.fish', '.py', '.java', '.c', '.cpp', '.h', '.hpp', '.cs',
|
||||
'.php', '.rb', '.rs', '.kt', '.swift', '.dart', '.scala'
|
||||
];
|
||||
|
||||
return textExtensions.some(ext =>
|
||||
filePath.toLowerCase().endsWith(ext)
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 信号量并发控制类
|
||||
*/
|
||||
export class Semaphore {
|
||||
private permits: number;
|
||||
private waiting: (() => void)[] = [];
|
||||
|
||||
constructor(permits: number) {
|
||||
this.permits = permits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取许可
|
||||
*/
|
||||
async acquire(): Promise<void> {
|
||||
if (this.permits > 0) {
|
||||
this.permits--;
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
this.waiting.push(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放许可
|
||||
*/
|
||||
release(): void {
|
||||
this.permits++;
|
||||
const next = this.waiting.shift();
|
||||
if (next) {
|
||||
this.permits--;
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前可用许可数
|
||||
*/
|
||||
available(): number {
|
||||
return this.permits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取等待队列长度
|
||||
*/
|
||||
waitingCount(): number {
|
||||
return this.waiting.length;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user