mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 07:23:06 +00:00
318 lines
7.7 KiB
TypeScript
318 lines
7.7 KiB
TypeScript
export type Language =
|
|
| "typescript"
|
|
| "javascript"
|
|
| "python"
|
|
| "rust"
|
|
| "go"
|
|
| "java"
|
|
| "c"
|
|
| "cpp"
|
|
| "unknown";
|
|
|
|
const LANGUAGE_EXTENSIONS: Record<string, Language> = {
|
|
".ts": "typescript",
|
|
".tsx": "typescript",
|
|
".js": "javascript",
|
|
".jsx": "javascript",
|
|
".mjs": "javascript",
|
|
".py": "python",
|
|
".pyw": "python",
|
|
".rs": "rust",
|
|
".go": "go",
|
|
".java": "java",
|
|
".c": "c",
|
|
".h": "c",
|
|
".cpp": "cpp",
|
|
".hpp": "cpp",
|
|
".cc": "cpp",
|
|
};
|
|
|
|
interface CommentPatterns {
|
|
line?: string;
|
|
blockStart?: string;
|
|
blockEnd?: string;
|
|
docLine?: string;
|
|
docBlockStart?: string;
|
|
}
|
|
|
|
const COMMENT_PATTERNS: Record<Language, CommentPatterns> = {
|
|
typescript: { line: "//", blockStart: "/*", blockEnd: "*/", docBlockStart: "/**" },
|
|
javascript: { line: "//", blockStart: "/*", blockEnd: "*/", docBlockStart: "/**" },
|
|
python: { line: "#", blockStart: '"""', blockEnd: '"""', docBlockStart: '"""' },
|
|
rust: { line: "//", blockStart: "/*", blockEnd: "*/", docLine: "///", docBlockStart: "/**" },
|
|
go: { line: "//", blockStart: "/*", blockEnd: "*/", docBlockStart: "/**" },
|
|
java: { line: "//", blockStart: "/*", blockEnd: "*/", docBlockStart: "/**" },
|
|
c: { line: "//", blockStart: "/*", blockEnd: "*/", docBlockStart: "/**" },
|
|
cpp: { line: "//", blockStart: "/*", blockEnd: "*/", docBlockStart: "/**" },
|
|
unknown: { line: "//", blockStart: "/*", blockEnd: "*/" },
|
|
};
|
|
|
|
const IMPORT_PATTERN = /^(use\s+|import\s+|from\s+|require\(|#include)/;
|
|
const SIGNATURE_PATTERN = /^(pub\s+)?(async\s+)?(fn|def|function|func|class|struct|enum|trait|interface|type)\s+\w+/;
|
|
const CONST_PATTERN = /^(const|static|let|pub\s+const|pub\s+static)\s+/;
|
|
|
|
function getCodePortion(line: string, language: Language): string {
|
|
const patterns = COMMENT_PATTERNS[language];
|
|
let quote: '"' | "'" | "`" | null = null;
|
|
let escaped = false;
|
|
let code = "";
|
|
|
|
for (let index = 0; index < line.length; index += 1) {
|
|
const character = line[index] ?? "";
|
|
|
|
if (escaped) {
|
|
escaped = false;
|
|
continue;
|
|
}
|
|
|
|
if (quote !== null) {
|
|
if (character === "\\") {
|
|
escaped = true;
|
|
continue;
|
|
}
|
|
if (character === quote) {
|
|
quote = null;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (patterns.line && line.startsWith(patterns.line, index)) {
|
|
break;
|
|
}
|
|
|
|
if (patterns.blockStart && patterns.blockEnd && line.startsWith(patterns.blockStart, index)) {
|
|
const blockEndIndex = line.indexOf(patterns.blockEnd, index + patterns.blockStart.length);
|
|
if (blockEndIndex === -1) {
|
|
break;
|
|
}
|
|
index = blockEndIndex + patterns.blockEnd.length - 1;
|
|
continue;
|
|
}
|
|
|
|
if (character === '"' || character === "'" || character === "`") {
|
|
quote = character;
|
|
continue;
|
|
}
|
|
|
|
code += character;
|
|
}
|
|
|
|
return code;
|
|
}
|
|
|
|
function countCodeBraces(line: string, language: Language): { open: number; close: number } {
|
|
let open = 0;
|
|
let close = 0;
|
|
|
|
for (const character of getCodePortion(line, language)) {
|
|
if (character === "{") {
|
|
open += 1;
|
|
} else if (character === "}") {
|
|
close += 1;
|
|
}
|
|
}
|
|
|
|
return { open, close };
|
|
}
|
|
|
|
export function detectLanguage(filePath: string): Language {
|
|
const lastDot = filePath.lastIndexOf(".");
|
|
if (lastDot === -1) {
|
|
return "unknown";
|
|
}
|
|
const extension = filePath.slice(lastDot).toLowerCase();
|
|
return LANGUAGE_EXTENSIONS[extension] ?? "unknown";
|
|
}
|
|
|
|
export function filterMinimal(content: string, language: Language): string {
|
|
const patterns = COMMENT_PATTERNS[language];
|
|
const lines = content.split("\n");
|
|
const result: string[] = [];
|
|
let inBlockComment = false;
|
|
let inDocstring = false;
|
|
let inUserscriptMetadataBlock = false;
|
|
const userscriptMetadataStartPattern = /^\/\/\s*==\s*userscript\s*==$/i;
|
|
const userscriptMetadataContentPattern = /^\/\/\s*@\w+/;
|
|
const userscriptMetadataEndPattern = /^\/\/\s*==\s*\/userscript\s*==$/i;
|
|
|
|
for (const line of lines) {
|
|
const trimmed = line.trim();
|
|
const isUserscriptMetadataStart = userscriptMetadataStartPattern.test(trimmed);
|
|
const isUserscriptMetadataContent = userscriptMetadataContentPattern.test(trimmed);
|
|
const isUserscriptMetadataEnd = userscriptMetadataEndPattern.test(trimmed);
|
|
|
|
if (isUserscriptMetadataStart) {
|
|
inUserscriptMetadataBlock = true;
|
|
result.push(line);
|
|
continue;
|
|
}
|
|
|
|
if (inUserscriptMetadataBlock) {
|
|
result.push(line);
|
|
if (isUserscriptMetadataEnd) {
|
|
inUserscriptMetadataBlock = false;
|
|
} else if (isUserscriptMetadataContent) {
|
|
// Preserve metadata key/value lines (e.g. // @name) within the userscript block.
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (patterns.blockStart && patterns.blockEnd) {
|
|
if (
|
|
!inDocstring &&
|
|
trimmed.includes(patterns.blockStart) &&
|
|
!(patterns.docBlockStart && trimmed.startsWith(patterns.docBlockStart))
|
|
) {
|
|
inBlockComment = true;
|
|
}
|
|
|
|
if (inBlockComment) {
|
|
if (trimmed.includes(patterns.blockEnd)) {
|
|
inBlockComment = false;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (language === "python" && trimmed.startsWith('"""')) {
|
|
inDocstring = !inDocstring;
|
|
result.push(line);
|
|
continue;
|
|
}
|
|
|
|
if (inDocstring) {
|
|
result.push(line);
|
|
continue;
|
|
}
|
|
|
|
if (patterns.line && trimmed.startsWith(patterns.line)) {
|
|
if (patterns.docLine && trimmed.startsWith(patterns.docLine)) {
|
|
result.push(line);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (trimmed.length === 0) {
|
|
result.push("");
|
|
continue;
|
|
}
|
|
|
|
result.push(line);
|
|
}
|
|
|
|
return result
|
|
.join("\n")
|
|
.replace(/\n{3,}/g, "\n\n")
|
|
.trim();
|
|
}
|
|
|
|
export function filterAggressive(content: string, language: Language): string {
|
|
const minimal = filterMinimal(content, language);
|
|
const lines = minimal.split("\n");
|
|
const result: string[] = [];
|
|
let braceDepth = 0;
|
|
let inImplementation = false;
|
|
|
|
for (const line of lines) {
|
|
const trimmed = line.trim();
|
|
|
|
if (IMPORT_PATTERN.test(trimmed)) {
|
|
result.push(line);
|
|
continue;
|
|
}
|
|
|
|
if (SIGNATURE_PATTERN.test(trimmed)) {
|
|
result.push(line);
|
|
inImplementation = true;
|
|
braceDepth = 0;
|
|
continue;
|
|
}
|
|
|
|
const braces = countCodeBraces(line, language);
|
|
const codeTrimmed = getCodePortion(line, language).trim();
|
|
|
|
if (inImplementation) {
|
|
braceDepth += braces.open;
|
|
braceDepth -= braces.close;
|
|
|
|
if (braceDepth <= 1 && (codeTrimmed === "{" || codeTrimmed === "}" || codeTrimmed.endsWith("{"))) {
|
|
result.push(line);
|
|
}
|
|
|
|
if (braceDepth <= 0) {
|
|
inImplementation = false;
|
|
if (trimmed.length > 0 && trimmed !== "}") {
|
|
result.push(" // ... implementation");
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (CONST_PATTERN.test(trimmed)) {
|
|
result.push(line);
|
|
}
|
|
}
|
|
|
|
return result.join("\n").trim();
|
|
}
|
|
|
|
export function smartTruncate(content: string, maxLines: number, _language: Language): string {
|
|
const lines = content.split("\n");
|
|
if (lines.length <= maxLines) {
|
|
return content;
|
|
}
|
|
|
|
const result: string[] = [];
|
|
let keptLines = 0;
|
|
let skippedSection = false;
|
|
|
|
for (const line of lines) {
|
|
const trimmed = line.trim();
|
|
const isImportant =
|
|
SIGNATURE_PATTERN.test(trimmed) ||
|
|
IMPORT_PATTERN.test(trimmed) ||
|
|
trimmed.startsWith("pub ") ||
|
|
trimmed.startsWith("export ") ||
|
|
trimmed === "}" ||
|
|
trimmed === "{";
|
|
|
|
if (isImportant || keptLines < maxLines / 2) {
|
|
if (skippedSection) {
|
|
result.push(` // ... ${lines.length - keptLines} lines omitted`);
|
|
skippedSection = false;
|
|
}
|
|
result.push(line);
|
|
keptLines += 1;
|
|
} else {
|
|
skippedSection = true;
|
|
}
|
|
|
|
if (keptLines >= maxLines - 1) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (skippedSection || keptLines < lines.length) {
|
|
result.push(`// ... ${lines.length - keptLines} more lines (total: ${lines.length})`);
|
|
}
|
|
|
|
return result.join("\n");
|
|
}
|
|
|
|
export function filterSourceCode(
|
|
content: string,
|
|
language: Language,
|
|
level: "none" | "minimal" | "aggressive",
|
|
): string {
|
|
switch (level) {
|
|
case "none":
|
|
return content;
|
|
case "minimal":
|
|
return filterMinimal(content, language);
|
|
case "aggressive":
|
|
return filterAggressive(content, language);
|
|
default:
|
|
return content;
|
|
}
|
|
}
|