From 4fb851c8ccc3eb26e14be894e82a1b46542c0a31 Mon Sep 17 00:00:00 2001 From: codetypes Date: Wed, 19 Mar 2025 08:23:27 +0800 Subject: [PATCH] cache loaded var --- sample/vars/declare-core.json | 5 +- src/components/editor.tsx | 42 +++++------- src/contexts/workspace-context.ts | 54 ++++++++++++--- src/misc/b3type.ts | 6 +- src/misc/b3util.ts | 107 +++++++++++++++++++----------- 5 files changed, 136 insertions(+), 78 deletions(-) diff --git a/sample/vars/declare-core.json b/sample/vars/declare-core.json index cd5290a..47f1701 100644 --- a/sample/vars/declare-core.json +++ b/sample/vars/declare-core.json @@ -1,9 +1,10 @@ { - "name": "def-core", + "name": "declare-core", "desc": "", "firstid": 1, "export": true, "import": [], + "group": [], "declvar": [ { "name": "context", @@ -18,4 +19,4 @@ "id": 1, "name": "Sequence" } -} +} \ No newline at end of file diff --git a/src/components/editor.tsx b/src/components/editor.tsx index 835a9c5..76ba806 100644 --- a/src/components/editor.tsx +++ b/src/components/editor.tsx @@ -23,7 +23,6 @@ import { } from "../contexts/workspace-context"; import { ImportDef, isExprType, NodeModel, TreeGraphData, TreeModel, VarDef } from "../misc/b3type"; import * as b3util from "../misc/b3util"; -import { loadSubtreeVarDef, loadVarDef } from "../misc/b3util"; import { message } from "../misc/hooks"; import i18n from "../misc/i18n"; import { Hotkey, isMacos, useKeyDown } from "../misc/keys"; @@ -48,13 +47,6 @@ interface FilterOption { isFocused: boolean; } -const collectVarDefs = (editor: EditorStore) => { - const vars: VarDef[] = editor.declvar.slice(); - loadVarDef(editor.import).forEach((v) => vars.push(v)); - loadSubtreeVarDef(editor.root).forEach((v) => vars.push(v)); - return vars; -}; - const createTreeData = (node: NodeModel, parent?: string) => { return b3util.createTreeData(node, parent, calcTreeDataSize); }; @@ -78,9 +70,9 @@ const isTreeUpdated = (editor: EditorStore, editTree: EditTree) => { return true; } - let max = Math.max(editor.declvar.length, editTree.data.declvar.length); + let max = Math.max(editor.declare.declvar.length, editTree.data.declvar.length); for (let i = 0; i < max; i++) { - const v1: VarDef | undefined = editor.declvar[i]; + const v1: VarDef | undefined = editor.declare.declvar[i]; const v2: VarDef | undefined = editTree.data.declvar[i]; if (v1?.name !== v2?.name || v1?.desc !== v2?.desc) { return true; @@ -94,9 +86,9 @@ const isTreeUpdated = (editor: EditorStore, editTree: EditTree) => { } } - max = Math.max(editor.import.length, editTree.data.import.length); + max = Math.max(editor.declare.import.length, editTree.data.import.length); for (let i = 0; i < max; i++) { - const v1: ImportDef | undefined = editor.import[i]; + const v1: ImportDef | undefined = editor.declare.import[i]; const v2: ImportDef | undefined = editTree.data.import[i]; if (v1?.path !== v2?.path) { return true; @@ -210,16 +202,17 @@ const createMenu = () => { export const Editor: FC = ({ onUpdate: updateState, data: editor, ...props }) => { const workspace = useWorkspace( useShallow((state) => ({ + refresh: state.refresh, editing: state.editing, + editingNode: state.editingNode, + editingTree: state.editingTree, + nodeDefs: state.nodeDefs, onEditingNode: state.onEditingNode, onEditingTree: state.onEditingTree, open: state.open, - workdir: state.workdir, relative: state.relative, updateFileMeta: state.updateFileMeta, - nodeDefs: state.nodeDefs, - editingTree: state.editingTree, - editingNode: state.editingNode, + workdir: state.workdir, })) ); @@ -544,14 +537,13 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . editor.data.export = editTree.data.export !== false; editor.data.group = editTree.data.group || []; editor.data.firstid = editTree.data.firstid ?? 1; - editor.declvar = editTree.data.declvar || []; - editor.import = editTree.data.import || []; - editor.data.import = editor.import.map((v) => v.path).sort(); - editor.data.declvar = editor.declvar + editor.declare.declvar = editTree.data.declvar || []; + editor.declare.import = editTree.data.import || []; + editor.data.import = editor.declare.import.map((v) => v.path).sort(); + editor.data.declvar = editor.declare.declvar .map((v) => ({ ...v })) .sort((a, b) => a.name.localeCompare(b.name)); - b3util.updateUsingGroups(editor.data.group); - b3util.updateUsingVars(collectVarDefs(editor)); + workspace.refresh(editor.path); pushHistory(); refresh(); onChange(); @@ -744,8 +736,8 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . const data = JSON.parse(str) as TreeModel; editor.data = data; editor.root = createTreeData(data.root); - editor.import = data.import.map((v) => ({ path: v, vars: [] })); - editor.declvar = data.declvar.map((v) => ({ ...v })); + editor.declare.import = data.import.map((v) => ({ path: v, vars: [], depends: [] })); + editor.declare.declvar = data.declvar.map((v) => ({ ...v })); editor.autoId = b3util.refreshTreeDataId(editor.root, editor.data.firstid); editor.graph.changeData(editor.root); editor.graph.layout(); @@ -1263,8 +1255,6 @@ export const Editor: FC = ({ onUpdate: updateState, data: editor, . useEffect(() => { if (workspace.editing === editor) { checkSubtree(); - b3util.updateUsingGroups(editor.data.group); - b3util.updateUsingVars(collectVarDefs(editor)); } }, [workspace.editing]); diff --git a/src/contexts/workspace-context.ts b/src/contexts/workspace-context.ts index 74b28fa..4123baf 100644 --- a/src/contexts/workspace-context.ts +++ b/src/contexts/workspace-context.ts @@ -7,7 +7,6 @@ import { create } from "zustand"; import { NodeDef } from "../behavior3/src/behavior3"; import { ImportDef, NodeModel, TreeGraphData, TreeModel, VarDef } from "../misc/b3type"; import * as b3util from "../misc/b3util"; -import { loadSubtreeVarDef } from "../misc/b3util"; import { message } from "../misc/hooks"; import i18n from "../misc/i18n"; import Path from "../misc/path"; @@ -47,8 +46,11 @@ export class EditorStore { data: TreeModel; root: TreeGraphData; - import: ImportDef[]; - declvar: VarDef[]; + declare: { + import: ImportDef[]; + subtree: ImportDef[]; + declvar: VarDef[]; + }; autoId: number = 1; dragSrcId?: string; @@ -76,8 +78,11 @@ export class EditorStore { this.data.name = Path.basenameWithoutExt(path); this.root = b3util.createTreeData(this.data.root); this.modifiedTime = fs.statSync(path).mtimeMs; - this.import = this.data.import.map((v) => ({ path: v, vars: [] })); - this.declvar = this.data.declvar.map((v) => ({ ...v })); + this.declare = { + import: this.data.import.map((v) => ({ path: v, vars: [], depends: [] })), + subtree: [], + declvar: this.data.declvar.map((v) => ({ ...v })), + }; this.autoId = b3util.refreshTreeDataId(this.root, this.data.firstid); this.historyStack.push( JSON.stringify( @@ -166,6 +171,7 @@ export type WorkspaceStore = { close: (path: string) => void; find: (path: string) => EditorStore | undefined; relative: (path: string) => string; + refresh: (path: string) => void; save: () => void; saveAs: () => void; @@ -526,6 +532,26 @@ export const useWorkspace = create((set, get) => ({ return Path.relative(workspace.workdir, path).replaceAll(Path.sep, "/"); }, + refresh: (path: string) => { + const workspace = get(); + const editor = workspace.editors.find((v) => v.path === path); + if (!editor) { + return; + } + const vars: Set = new Set(editor.declare.declvar.slice()); + editor.declare.subtree = b3util.findSubtrees(editor.root).map((v) => ({ + path: v, + vars: [], + depends: [], + })); + b3util.loadVarDef(editor.declare.import).forEach((v) => vars.add(v)); + b3util.loadVarDef(editor.declare.subtree).forEach((v) => { + vars.add(v); + }); + b3util.updateUsingGroups(editor.data.group); + b3util.updateUsingVars(Array.from(vars)); + }, + save: () => { const workspace = get(); saveFile(workspace.editing); @@ -560,7 +586,9 @@ export const useWorkspace = create((set, get) => ({ } else { const fullpath = Path.posixPath(`${workspace.workdir}/${filename}`); const editor = workspace.find(fullpath); - if (editor && editor.modifiedTime + 500 < fs.statSync(fullpath).mtimeMs) { + const modified = fs.statSync(fullpath).mtimeMs; + b3util.files[Path.posixPath(filename)] = modified; + if (editor && editor.modifiedTime + 500 < modified) { if (editor.unsave) { editor.alertReload = true; set({ modifiedTime: Date.now() }); @@ -595,7 +623,7 @@ export const useWorkspace = create((set, get) => ({ let fileMeta = allFiles.get(path); if (!fileMeta) { fileMeta = { path: fileNode.path }; - allFiles.set(fileNode.path, fileMeta); + allFiles.set(path, fileMeta); } else { fileMeta.path = fileNode.path; } @@ -610,8 +638,11 @@ export const useWorkspace = create((set, get) => ({ }; collect(data); allFiles.forEach((file, key) => { + const modified = fs.statSync(file.path).mtimeMs; + b3util.files[key] = modified; if (!file.exists) { allFiles.delete(key); + delete b3util.files[key]; updated = true; } }); @@ -653,13 +684,16 @@ export const useWorkspace = create((set, get) => ({ if (workspace.editing) { workspace.editing.editNode = null; } + workspace.refresh(editor.path); set({ editingTree: { data: { ...editor.data, - import: editor.import, - declvar: editor.declvar, - subtree: loadSubtreeVarDef(editor.root), + import: editor.declare.import, + declvar: editor.declare.declvar, + subtree: editor.declare.subtree + .map((def) => def.vars) + .reduce((acc, v) => [...acc, ...v], []), }, }, editingNodeDef: null, diff --git a/src/misc/b3type.ts b/src/misc/b3type.ts index bb6e7e6..76f9b6e 100644 --- a/src/misc/b3type.ts +++ b/src/misc/b3type.ts @@ -43,8 +43,12 @@ export interface GroupDef { export interface ImportDef { path: string; - date?: number; + modified?: number; vars: VarDef[]; + depends: { + path: string; + modified: number; + }[]; } export interface TreeModel { diff --git a/src/misc/b3util.ts b/src/misc/b3util.ts index 1ff4763..b7c7369 100644 --- a/src/misc/b3util.ts +++ b/src/misc/b3util.ts @@ -28,7 +28,9 @@ export let nodeDefs: NodeDefs = new NodeDefs(); export let groupDefs: string[] = []; export let usingGroups: Record = {}; export let usingVars: Record | null = null; +export const files: Record = {}; +const parsedVarDefs: Record = {}; const parsedExprs: Record = {}; let workdir: string = ""; let alertError: (msg: string, duration?: number) => void = () => {}; @@ -73,6 +75,18 @@ export const updateUsingVars = (vars: VarDef[]) => { } }; +export const parseExpr = (expr: string) => { + if (parsedExprs[expr]) { + return parsedExprs[expr]; + } + const result = expr + .split(/[^a-zA-Z0-9_.]/) + .map((v) => v.split(".")[0]) + .filter((v) => isValidVariableName(v)); + parsedExprs[expr] = result; + return result; +}; + export const isValidVariableName = (name: string) => { return /^[a-zA-Z_]/.test(name); }; @@ -716,67 +730,82 @@ export const isTreeFile = (path: string) => { }; export const loadVarDef = (list: ImportDef[]) => { - const all: Set = new Set(); for (const entry of list) { - // TODO: check file date - // TODO: use cache - if (entry.vars.length) { - entry.vars.forEach((v) => all.add(v)); + if (!files[entry.path]) { + console.warn(`file not found:${workdir}/${entry.path}`); continue; } - const filter: Map = new Map(); - const vars: VarDef[] = []; + + let changed = false; + if (!entry.modified || files[entry.path] > entry.modified) { + changed = true; + } + + if (!changed) { + changed = entry.depends.some((v) => files[v.path] && files[v.path] > v.modified); + } + + if (!changed) { + continue; + } + + entry.vars = []; + entry.depends = []; + entry.modified = files[entry.path]; + + const vars: Set = new Set(); + const depends: Set = new Set(); const load = (path: string) => { - if (filter.has(path)) { + if (parsingStack.includes(path)) { return; } - filter.set(path, true); + + const parsedEntry: ImportDef | undefined = parsedVarDefs[path]; + if (parsedEntry && files[path] === parsedEntry.modified) { + parsedEntry.depends.forEach((v) => depends.add(v.path)); + parsedEntry.vars.forEach((v) => vars.add(v)); + return; + } + + parsingStack.push(path); try { const model: TreeModel = readTree(`${workdir}/${path}`); - model.declvar.forEach((v) => vars.push(v)); - model.import.forEach((v) => load(v)); + model.declvar.forEach((v) => vars.add(v)); + model.import.forEach((v) => { + load(v); + depends.add(v); + }); + console.log(`load var: ${path}`); } catch (e) { alertError(`parsing error: ${path}`); } + parsingStack.pop(); }; load(entry.path); - filter.clear(); - entry.vars = vars - .filter((v) => { - if (!filter.has(v.name)) { - filter.set(v.name, true); - return true; - } - return false; - }) - .sort((a, b) => a.name.localeCompare(b.name)); - entry.vars.forEach((v) => all.add(v)); + entry.vars = Array.from(vars).sort((a, b) => a.name.localeCompare(b.name)); + entry.depends = Array.from(depends).map((v) => ({ path: v, modified: files[v] })); + parsedVarDefs[entry.path] = { + path: entry.path, + vars: entry.vars.map((v) => ({ name: v.name, desc: v.desc })), + depends: entry.depends.slice(), + modified: entry.modified, + }; } + const all: Set = new Set(); + list.forEach((entry) => entry.vars.forEach((v) => all.add(v))); return Array.from(all); }; -export const loadSubtreeVarDef = (data: TreeGraphData) => { - const list: ImportDef[] = []; +export const findSubtrees = (data: TreeGraphData) => { + const list: string[] = []; const traverse = (v: TreeGraphData) => { if (v.path) { - list.push({ path: v.path, vars: [] }); + list.push(v.path); } if (v.children) { - v.children.forEach(traverse); + v.children.forEach((child) => traverse(child)); } }; traverse(data); - return loadVarDef(list); -}; - -export const parseExpr = (expr: string) => { - if (parsedExprs[expr]) { - return parsedExprs[expr]; - } - const result = expr - .split(/[^a-zA-Z0-9_.]/) - .map((v) => v.split(".")[0]) - .filter((v) => isValidVariableName(v)); - parsedExprs[expr] = result; - return result; + return list; };