refactor: consolidate block processing logic
Introduce a centralized `block-processor` and `block-scanner` to handle markdown fenced code blocks. This replaces the previous `inline-blocks.ts` and individual completion sources with a unified approach that handles block stripping, directive replacement, and metadata extraction (stats, templates, and spark tables) in a single pass.
This commit is contained in:
+43
-25
@@ -11,7 +11,10 @@ import {
|
||||
scanCompletions,
|
||||
type CompletionsPayload,
|
||||
} from "../completions/index.js";
|
||||
import { extractInlineBlocks } from "../inline-blocks.js";
|
||||
import {
|
||||
processBlocks,
|
||||
type ProcessedBlocks,
|
||||
} from "../completions/block-processor.js";
|
||||
|
||||
interface ContentIndex {
|
||||
[path: string]: string;
|
||||
@@ -83,8 +86,16 @@ function getBestIP(): string {
|
||||
/**
|
||||
* 扫描目录内的 .md 文件,生成索引
|
||||
*/
|
||||
export function scanDirectory(dir: string): ContentIndex {
|
||||
export function scanDirectory(dir: string): {
|
||||
index: ContentIndex;
|
||||
blocks: ProcessedBlocks;
|
||||
} {
|
||||
const index: ContentIndex = {};
|
||||
const blocks: ProcessedBlocks = {
|
||||
stats: [],
|
||||
statTemplates: [],
|
||||
sparkTables: [],
|
||||
};
|
||||
|
||||
function scan(currentPath: string, relativePath: string) {
|
||||
const entries = readdirSync(currentPath);
|
||||
@@ -107,12 +118,11 @@ export function scanDirectory(dir: string): ContentIndex {
|
||||
try {
|
||||
const content = readFileSync(fullPath, "utf-8");
|
||||
if (entry.endsWith(".md")) {
|
||||
const stripped = extractInlineBlocks(
|
||||
content,
|
||||
normalizedRelPath,
|
||||
index,
|
||||
);
|
||||
index[normalizedRelPath] = stripped;
|
||||
const result = processBlocks(content, normalizedRelPath, index);
|
||||
index[normalizedRelPath] = result.stripped;
|
||||
blocks.stats.push(...result.blocks.stats);
|
||||
blocks.statTemplates.push(...result.blocks.statTemplates);
|
||||
blocks.sparkTables.push(...result.blocks.sparkTables);
|
||||
} else {
|
||||
index[normalizedRelPath] = content;
|
||||
}
|
||||
@@ -124,7 +134,7 @@ export function scanDirectory(dir: string): ContentIndex {
|
||||
}
|
||||
|
||||
scan(dir, "");
|
||||
return index;
|
||||
return { index, blocks };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,6 +277,11 @@ export function createContentServer(
|
||||
host: string = "0.0.0.0",
|
||||
): ContentServer {
|
||||
let contentIndex: ContentIndex = {};
|
||||
let collectedBlocks: ProcessedBlocks = {
|
||||
stats: [],
|
||||
statTemplates: [],
|
||||
sparkTables: [],
|
||||
};
|
||||
let completionsIndex: CompletionsPayload = {
|
||||
dice: [],
|
||||
links: [],
|
||||
@@ -275,9 +290,9 @@ export function createContentServer(
|
||||
statTemplates: [],
|
||||
};
|
||||
|
||||
/** Re-scan completions from current content index (cached) */
|
||||
/** Re-scan completions from current content index and collected blocks */
|
||||
function recomputeCompletions(): void {
|
||||
completionsIndex = scanCompletions(contentIndex);
|
||||
completionsIndex = scanCompletions(contentIndex, collectedBlocks);
|
||||
console.log(
|
||||
`[completions] dice=${completionsIndex.dice.length} links=${completionsIndex.links.length} sparkTables=${completionsIndex.sparkTables.length} stats=${completionsIndex.stats.length} templates=${completionsIndex.statTemplates.length}`,
|
||||
);
|
||||
@@ -285,7 +300,9 @@ export function createContentServer(
|
||||
|
||||
// 扫描内容目录生成索引
|
||||
console.log("扫描内容目录...");
|
||||
contentIndex = scanDirectory(contentDir);
|
||||
const scanResult = scanDirectory(contentDir);
|
||||
contentIndex = scanResult.index;
|
||||
collectedBlocks = scanResult.blocks;
|
||||
console.log(`已索引 ${Object.keys(contentIndex).length} 个文件`);
|
||||
recomputeCompletions();
|
||||
|
||||
@@ -308,12 +325,11 @@ export function createContentServer(
|
||||
const content = readFileSync(path, "utf-8");
|
||||
const relPath = "/" + relative(contentDir, path).split(sep).join("/");
|
||||
if (relPath.endsWith(".md")) {
|
||||
const stripped = extractInlineBlocks(
|
||||
content,
|
||||
relPath,
|
||||
contentIndex,
|
||||
);
|
||||
contentIndex[relPath] = stripped;
|
||||
const result = processBlocks(content, relPath, contentIndex);
|
||||
contentIndex[relPath] = result.stripped;
|
||||
// Re-scan to get fresh blocks (simpler than per-file merge)
|
||||
const rescan = scanDirectory(contentDir);
|
||||
collectedBlocks = rescan.blocks;
|
||||
recomputeCompletions();
|
||||
} else {
|
||||
contentIndex[relPath] = content;
|
||||
@@ -334,12 +350,10 @@ export function createContentServer(
|
||||
const content = readFileSync(path, "utf-8");
|
||||
const relPath = "/" + relative(contentDir, path).split(sep).join("/");
|
||||
if (relPath.endsWith(".md")) {
|
||||
const stripped = extractInlineBlocks(
|
||||
content,
|
||||
relPath,
|
||||
contentIndex,
|
||||
);
|
||||
contentIndex[relPath] = stripped;
|
||||
const result = processBlocks(content, relPath, contentIndex);
|
||||
contentIndex[relPath] = result.stripped;
|
||||
const rescan = scanDirectory(contentDir);
|
||||
collectedBlocks = rescan.blocks;
|
||||
recomputeCompletions();
|
||||
} else {
|
||||
contentIndex[relPath] = content;
|
||||
@@ -359,7 +373,11 @@ export function createContentServer(
|
||||
const relPath = "/" + relative(contentDir, path).split(sep).join("/");
|
||||
delete contentIndex[relPath];
|
||||
console.log(`[删除] ${relPath}`);
|
||||
if (relPath.endsWith(".md")) recomputeCompletions();
|
||||
if (relPath.endsWith(".md")) {
|
||||
const rescan = scanDirectory(contentDir);
|
||||
collectedBlocks = rescan.blocks;
|
||||
recomputeCompletions();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user