📜
editor_index_copy.js
Back
📝 Javascript ⚡ Executable Ctrl+S: Save • Ctrl+R: Run • Ctrl+F: Find
/** * Editor Index (CodeMirror-Powered Version) * ------------------------------------------ * - Uses CodeMirror's folding system for accurate block detection * - Leverages CodeMirror's language-aware parsing * - Detects functions, classes, blocks properly per language */ (function () { 'use strict'; console.log("[editor_index.js] Loading CodeMirror-powered index module..."); const ACTIVE_FILES_KEY = "sftp_active_files"; // ========================================================================= // LOCALSTORAGE HELPERS // ========================================================================= function getActiveFileContent() { try { const files = JSON.parse(localStorage.getItem(ACTIVE_FILES_KEY) || "[]"); const active = files.find(f => f.active); return { content: active?.content || "", name: active?.name || "Untitled", path: active?.path || "" }; } catch (err) { console.error("[editor_index.js] Failed to load file:", err); return { content: "", name: "Untitled", path: "" }; } } function getLines() { const { content } = getActiveFileContent(); return content.split("\n"); } function getLine(index) { const lines = getLines(); return lines[index] || ""; } function getLineCount() { return getLines().length; } // ========================================================================= // CODEMIRROR SETUP // ========================================================================= let cmDoc = null; function initCodeMirrorDoc() { if (typeof CodeMirror === 'undefined') { console.warn("[editor_index.js] CodeMirror not loaded, falling back to simple parsing"); return null; } const { content, name } = getActiveFileContent(); const mode = detectCodeMirrorMode(name); // Create a CodeMirror document cmDoc = CodeMirror.Doc(content, mode); console.log(`[editor_index.js] CodeMirror doc initialized with mode: ${mode}`); return cmDoc; } function detectCodeMirrorMode(fileName) { const ext = (fileName.split(".").pop() || "").toLowerCase(); if (["js", "jsx"].includes(ext)) return "javascript"; if (["ts", "tsx"].includes(ext)) return "text/typescript"; if (["html", "htm"].includes(ext)) return "htmlmixed"; if (["php"].includes(ext)) return "php"; if (["css"].includes(ext)) return "css"; if (["json"].includes(ext)) return "application/json"; if (["py"].includes(ext)) return "python"; if (["java"].includes(ext)) return "text/x-java"; if (["cpp", "cc", "cxx"].includes(ext)) return "text/x-c++src"; if (["c"].includes(ext)) return "text/x-csrc"; return "text/plain"; } function getLanguageFromMode(mode) { if (!mode) return "text"; if (mode.includes("javascript")) return "javascript"; if (mode.includes("typescript")) return "typescript"; if (mode.includes("html")) return "html"; if (mode.includes("php")) return "php"; if (mode.includes("css")) return "css"; if (mode.includes("json")) return "json"; if (mode.includes("python")) return "python"; if (mode.includes("java")) return "java"; if (mode.includes("c++") || mode.includes("csrc")) return "cpp"; return "text"; } // ========================================================================= // CODEMIRROR FOLD-BASED BLOCK DETECTION // ========================================================================= function getFoldableRanges(doc) { if (!doc || !CodeMirror.fold) { console.warn("[editor_index.js] CodeMirror fold addon not loaded"); return []; } const ranges = []; const lineCount = doc.lineCount(); // Try to use fold helpers const mode = doc.getMode(); let foldFunc = null; // Load appropriate fold function if (CodeMirror.fold.brace) foldFunc = CodeMirror.fold.brace; else if (CodeMirror.fold.indent) foldFunc = CodeMirror.fold.indent; if (!foldFunc) { console.warn("[editor_index.js] No fold function available"); return []; } for (let line = 0; line < lineCount; line++) { try { const range = foldFunc(doc, { line, ch: 0 }); if (range && range.from && range.to) { ranges.push({ startRow: range.from.line, endRow: range.to.line, startCol: range.from.ch, endCol: range.to.ch }); } } catch (e) { // Skip lines that can't fold } } // Remove duplicate ranges const unique = []; const seen = new Set(); for (const range of ranges) { const key = `${range.startRow}-${range.endRow}`; if (!seen.has(key)) { seen.add(key); unique.push(range); } } return unique; } function detectBlockType(doc, startRow, endRow) { const line = doc.getLine(startRow).trim(); const mode = doc.getMode(); const lang = getLanguageFromMode(mode.name || mode); let type = "block"; let label = `Block ${startRow + 1}`; let icon = "📦"; // JavaScript/TypeScript if (lang === "javascript" || lang === "typescript") { if (line.match(/^\s*function\s+(\w+)/)) { const match = line.match(/function\s+(\w+)/); label = match[1] + "()"; type = "function"; icon = "⚙️"; } else if (line.match(/^\s*async\s+function\s+(\w+)/)) { const match = line.match(/function\s+(\w+)/); label = match[1] + "()"; type = "function"; icon = "⚙️"; } else if (line.match(/^\s*(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>/)) { const match = line.match(/(?:const|let|var)\s+(\w+)/); label = match[1] + "()"; type = "function"; icon = "⚙️"; } else if (line.match(/^\s*(?:class|export\s+class)\s+(\w+)/)) { const match = line.match(/class\s+(\w+)/); label = match[1]; type = "class"; icon = "📦"; } else if (line.match(/^\s*(?:if|for|while|switch|try)\s*\(/)) { label = line.substring(0, 30) + "..."; type = "control"; icon = "🔀"; } else if (line.match(/^\s*\{/)) { label = "{ block }"; type = "block"; icon = "📦"; } } // PHP else if (lang === "php") { if (line.match(/^\s*function\s+(\w+)/)) { const match = line.match(/function\s+(\w+)/); label = match[1] + "()"; type = "function"; icon = "⚙️"; } else if (line.match(/^\s*(?:class|abstract\s+class|final\s+class)\s+(\w+)/)) { const match = line.match(/class\s+(\w+)/); label = match[1]; type = "class"; icon = "📦"; } } // CSS else if (lang === "css") { if (line.match(/^\s*\.([\w-]+)\s*\{/)) { const match = line.match(/\.([\w-]+)/); label = "." + match[1]; type = "css-class"; icon = "🎨"; } else if (line.match(/^\s*#([\w-]+)\s*\{/)) { const match = line.match(/#([\w-]+)/); label = "#" + match[1]; type = "css-id"; icon = "🎨"; } else if (line.match(/^\s*([\w-]+)\s*\{/)) { const match = line.match(/([\w-]+)\s*\{/); label = match[1]; type = "css-tag"; icon = "🎨"; } } // HTML else if (lang === "html") { if (line.match(/^\s*<([\w-]+)/)) { const match = line.match(/<([\w-]+)/); label = "<" + match[1] + ">"; type = "html-tag"; icon = "📄"; } else if (line.match(/^\s*<script/i)) { label = "<script>"; type = "script"; icon = "⚙️"; } else if (line.match(/^\s*<style/i)) { label = "<style>"; type = "style"; icon = "🎨"; } } return { type, label, icon, lang }; } function extractBlocksFromCodeMirror() { const doc = initCodeMirrorDoc(); if (!doc) return fallbackSimpleParsing(); const foldRanges = getFoldableRanges(doc); const blocks = []; console.log(`[editor_index.js] Found ${foldRanges.length} foldable ranges`); foldRanges.forEach((range) => { const { startRow, endRow } = range; const blockInfo = detectBlockType(doc, startRow, endRow); const code = doc.getRange( { line: startRow, ch: 0 }, { line: endRow, ch: doc.getLine(endRow).length } ); blocks.push({ row: startRow, endRow: endRow, label: blockInfo.label, type: blockInfo.type, icon: blockInfo.icon, lang: blockInfo.lang, code: code }); }); return blocks; } // ========================================================================= // FALLBACK SIMPLE PARSING // ========================================================================= function fallbackSimpleParsing() { console.log("[editor_index.js] Using fallback simple parsing"); const lines = getLines(); const blocks = []; const CHUNK_SIZE = 30; for (let i = 0; i < lines.length; i += CHUNK_SIZE) { const endRow = Math.min(i + CHUNK_SIZE - 1, lines.length - 1); blocks.push({ row: i, endRow: endRow, label: `Lines ${i + 1}-${endRow + 1}`, type: "chunk", icon: "📄", lang: "text", code: lines.slice(i, endRow + 1).join("\n") }); } return blocks; } // ========================================================================= // MARKER PARSING // ========================================================================= function parseMarkerName(name) { const cleaned = name.replace(/[\[\]]/g, "").trim(); const parts = cleaned.split("_"); if (parts.length === 1) return { component: parts[0], language: null, number: null, fullName: cleaned }; if (parts.length === 2) return { component: parts[0], language: parts[1], number: null, fullName: cleaned }; const num = parseInt(parts[2]); return { component: parts[0], language: parts[1], number: isNaN(num) ? null : num, fullName: cleaned }; } function findMarkerEnd(startRow, markerName) { const lineCount = getLineCount(); for (let row = startRow + 1; row < lineCount; row++) { const line = getLine(row).trim(); if (line.includes(">")) { const m = line.match(/(?:<!--|\/\*|\/\/\/|\/\/)\s*([\w\-\[\]_]+)\s*>/); if (m && m[1].trim() === markerName) return row; } } return startRow; } function extractMarkersAndBlocks() { const lines = getLines(); const components = {}; const unmarked = []; const markerRanges = []; // Find markers for (let row = 0; row < lines.length; row++) { const line = lines[row].trim(); const open = line.match(/(?:<!--|\/\*|\/\/\/|\/\/)\s*([\w\-\[\]_]+)\s*</); if (open) { const markerName = open[1].trim(); const parsed = parseMarkerName(markerName); const endRow = findMarkerEnd(row, markerName); const code = lines.slice(row, endRow + 1).join("\n"); const markerItem = { type: "marker", row, endRow, label: markerName, parsed, lang: parsed.language || "text", code, children: [] }; markerRanges.push({ startRow: row, endRow, markerItem }); if (parsed.component) { if (!components[parsed.component]) components[parsed.component] = {}; if (!components[parsed.component][parsed.language || "text"]) components[parsed.component][parsed.language || "text"] = []; components[parsed.component][parsed.language || "text"].push(markerItem); } else { unmarked.push(markerItem); } } } // Get CodeMirror blocks const cmBlocks = extractBlocksFromCodeMirror(); // Assign blocks for (const block of cmBlocks) { let belongsTo = null; for (const range of markerRanges) { if (block.row > range.startRow && block.row < range.endRow) { belongsTo = range.markerItem; break; } } if (belongsTo) { belongsTo.children.push(block); } else { unmarked.push(block); } } return { components, unmarked }; } // ========================================================================= // MAIN INDEX GENERATION // ========================================================================= function generateDocumentIndex() { const { components, unmarked } = extractMarkersAndBlocks(); if (Object.keys(components).length === 0 && unmarked.length === 0) { const fallbackBlocks = fallbackSimpleParsing(); return { components: {}, unmarked: fallbackBlocks }; } return { components, unmarked }; } // ========================================================================= // PUBLIC API // ========================================================================= window.EditorIndex = { generateDocumentIndex, initCodeMirrorDoc, getFoldableRanges, extractBlocksFromCodeMirror, parseMarkerName, getActiveFileContent, getLines, getLine, getLineCount }; console.log("[editor_index.js] CodeMirror-powered indexer ready."); })();