📜
fold-finder.js
Back
📝 Javascript ⚡ Executable Ctrl+S: Save • Ctrl+R: Run • Ctrl+F: Find
// fold-finder.js - Intelligent code fold detection for multiple languages const FoldFinder = (() => { // Utility functions function normalize(str) { return str.replace(/\s+/g, ' ').trim(); } function findMatchingPair(text, open, close, start) { let depth = 1; for (let i = start + 1; i < text.length; i++) { if (text[i] === open) depth++; if (text[i] === close) { depth--; if (depth === 0) return i; } } return -1; } function calculateMatchRatio(foldText, searchText) { const normFold = normalize(foldText); const normSearch = normalize(searchText); const searchTokens = normSearch.split(' ').filter(t => t.length > 2); const matchCount = searchTokens.filter(tok => normFold.includes(tok)).length; return searchTokens.length > 0 ? matchCount / searchTokens.length : 0; } // Language detection function detectLanguage(text) { text = text.trim(); if (/^\s*<\?php/i.test(text) || (/function\s+\w+\s*\([^)]*\)\s*\{/i.test(text) && /\$/i.test(text))) { return 'php'; } if (/^\s*<[a-z]+[\s>]/i.test(text) || /<\/[a-z]+>/i.test(text)) { return 'html'; } if (/^\s*def\s+\w+/i.test(text)) { return 'python'; } if (/^\s*(function|const|let|var|class)\s+/i.test(text)) { return 'js'; } return 'js'; // default } // JavaScript fold finder function findJsFold(text, pastedText) { const normSearch = normalize(pastedText); const sigMatch = pastedText.match(/(function\s+\w+|class\s+\w+|const\s+\w+\s*=|let\s+\w+\s*=|var\s+\w+\s*=|if\s*\(|for\s*\(|while\s*\()/i); if (!sigMatch) return null; const signature = sigMatch[0]; const lines = text.split('\n'); const results = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.includes(signature)) { const lineStart = lines.slice(0, i).join('\n').length + (i > 0 ? 1 : 0); const braceIdx = text.indexOf('{', lineStart); if (braceIdx !== -1) { const braceEnd = findMatchingPair(text, '{', '}', braceIdx); if (braceEnd !== -1) { const foldText = text.slice(lineStart, braceEnd + 1); const matchRatio = calculateMatchRatio(foldText, pastedText); if (matchRatio > 0.5) { results.push({ start: lineStart, end: braceEnd + 1, row: i, matchRatio: matchRatio }); } } } } } results.sort((a, b) => b.matchRatio - a.matchRatio); return results[0] || null; } // PHP fold finder function findPhpFold(text, pastedText) { const normSearch = normalize(pastedText); const hasPhpTag = /^\s*<\?php/i.test(pastedText); const sigMatch = pastedText.match(/(function\s+\w+|class\s+\w+)/i); if (sigMatch) { const signature = sigMatch[0]; const lines = text.split('\n'); const results = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.includes(signature)) { const lineStart = lines.slice(0, i).join('\n').length + (i > 0 ? 1 : 0); const braceIdx = text.indexOf('{', lineStart); if (braceIdx !== -1) { const braceEnd = findMatchingPair(text, '{', '}', braceIdx); if (braceEnd !== -1) { const foldText = text.slice(lineStart, braceEnd + 1); const matchRatio = calculateMatchRatio(foldText, pastedText); if (matchRatio > 0.5) { results.push({ start: lineStart, end: braceEnd + 1, row: i, matchRatio: matchRatio }); } } } } } results.sort((a, b) => b.matchRatio - a.matchRatio); if (results[0]) return results[0]; } // Fall back to PHP block if (hasPhpTag) { const phpOpenIdx = text.indexOf('<?php'); if (phpOpenIdx !== -1) { let phpCloseIdx = text.indexOf('?>', phpOpenIdx); if (phpCloseIdx === -1) phpCloseIdx = text.length; else phpCloseIdx += 2; const beforeStart = text.slice(0, phpOpenIdx); const row = (beforeStart.match(/\n/g) || []).length; return { start: phpOpenIdx, end: phpCloseIdx, row: row, matchRatio: 0.7 }; } } return null; } // HTML fold finder function findHtmlFold(text, pastedText) { const normSearch = normalize(pastedText); const tagMatch = pastedText.match(/<([a-zA-Z0-9-]+)[\s>]/i); if (!tagMatch) return null; const tagName = tagMatch[1]; const lines = text.split('\n'); const results = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; const openPattern = new RegExp('<' + tagName + '[\\s>]', 'i'); if (openPattern.test(line)) { const lineStart = lines.slice(0, i).join('\n').length + (i > 0 ? 1 : 0); const tagStart = text.indexOf('<' + tagName, lineStart); // Find closing tag with depth tracking let depth = 1; let searchPos = tagStart + tagName.length + 1; while (depth > 0 && searchPos < text.length) { const nextOpen = text.indexOf('<' + tagName, searchPos); const nextClose = text.indexOf('</' + tagName + '>', searchPos); if (nextClose === -1) break; if (nextOpen !== -1 && nextOpen < nextClose) { depth++; searchPos = nextOpen + tagName.length + 1; } else { depth--; if (depth === 0) { const closeEnd = nextClose + tagName.length + 3; const foldText = text.slice(tagStart, closeEnd); const matchRatio = calculateMatchRatio(foldText, pastedText); if (matchRatio > 0.4) { results.push({ start: tagStart, end: closeEnd, row: i, matchRatio: matchRatio }); } break; } searchPos = nextClose + tagName.length + 3; } } } } results.sort((a, b) => b.matchRatio - a.matchRatio); return results[0] || null; } // Python fold finder function findPythonFold(text, pastedText) { const normSearch = normalize(pastedText); const sigMatch = pastedText.match(/(def\s+\w+|class\s+\w+)/i); if (!sigMatch) return null; const signature = sigMatch[0]; const lines = text.split('\n'); const results = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.includes(signature)) { const baseIndent = line.match(/^\s*/)[0].length; let endRow = i; // Find end of function by indentation for (let j = i + 1; j < lines.length; j++) { const jLine = lines[j]; if (jLine.trim() === '') continue; const jIndent = jLine.match(/^\s*/)[0].length; if (jIndent <= baseIndent) { endRow = j - 1; break; } endRow = j; } const startPos = lines.slice(0, i).join('\n').length + (i > 0 ? 1 : 0); const endPos = lines.slice(0, endRow + 1).join('\n').length; const foldText = text.slice(startPos, endPos); const matchRatio = calculateMatchRatio(foldText, pastedText); if (matchRatio > 0.5) { results.push({ start: startPos, end: endPos, row: i, matchRatio: matchRatio }); } } } results.sort((a, b) => b.matchRatio - a.matchRatio); return results[0] || null; } // Main find function function findFold(text, pastedText, lang) { switch (lang) { case 'php': return findPhpFold(text, pastedText); case 'html': return findHtmlFold(text, pastedText); case 'python': return findPythonFold(text, pastedText); case 'js': default: return findJsFold(text, pastedText); } } // Public API return { detectLanguage, findFold, findJsFold, findPhpFold, findHtmlFold, findPythonFold }; })();