diff --git a/src/Components/TrailGrid.svelte b/src/Components/TrailGrid.svelte index fa6c545e..56ad8b01 100644 --- a/src/Components/TrailGrid.svelte +++ b/src/Components/TrailGrid.svelte @@ -4,6 +4,8 @@ import type BreadcrumbsPlugin from "src/main"; import { closeImpliedLinks, + getAllXGs, + mergeGs, normalise, openOrSwitch, padArray, @@ -52,14 +54,21 @@ // const data: {[cell: string]: number} = {} // allCells.forEach(cell => data[cell] = app.metadataCache.getFileCache(app.metadataCache.getFirstLinkpathDest(cell, currFile.path))?.links.length ?? 0); + const allUps = getAllXGs(plugin, "up"); + const allDowns = getAllXGs(plugin, "down"); + console.log({ allUps, allDowns }); + + const upG = mergeGs(...Object.values(allUps)); + const downG = mergeGs(...Object.values(allDowns)); + console.log({ upG, downG }); + + const closedParents = closeImpliedLinks(upG, downG); + const children: { [cell: string]: number } = {}; allCells.forEach( (cell) => (children[cell] = ( - closeImpliedLinks( - plugin.currGraphs.gChildren, - plugin.currGraphs.gParents - ).successors(cell) ?? [] + closeImpliedLinks(downG, upG).successors(cell) ?? [] ).length) ); diff --git a/src/MatrixView.ts b/src/MatrixView.ts index 432eb555..89b9c0cf 100644 --- a/src/MatrixView.ts +++ b/src/MatrixView.ts @@ -17,7 +17,13 @@ import type { d3Graph, } from "src/interfaces"; import type BreadcrumbsPlugin from "src/main"; -import { closeImpliedLinks, copy, debug } from "src/sharedFunctions"; +import { + closeImpliedLinks, + copy, + debug, + getAllXGs, + mergeGs, +} from "src/sharedFunctions"; import Lists from "./Components/Lists.svelte"; import Matrix from "./Components/Matrix.svelte"; @@ -46,13 +52,18 @@ export default class MatrixView extends ItemView { callback: async () => { const settings = this.plugin.settings; const currFile = this.app.workspace.getActiveFile().basename; - const allPaths = this.dfsAllPaths( - closeImpliedLinks( - this.plugin.currGraphs.gChildren, - this.plugin.currGraphs.gParents - ), - currFile - ); + + const allUps = getAllXGs(this.plugin, "up"); + const allDowns = getAllXGs(this.plugin, "down"); + console.log({ allUps, allDowns }); + + const upG = mergeGs(...Object.values(allUps)); + const downG = mergeGs(...Object.values(allDowns)); + console.log({ upG, downG }); + + const closedParents = closeImpliedLinks(upG, downG); + + const allPaths = this.dfsAllPaths(closedParents, currFile); const index = this.createIndex(currFile + "\n", allPaths, settings); debug(settings, { index }); await copy(index); @@ -63,17 +74,23 @@ export default class MatrixView extends ItemView { id: "global-index", name: "Copy a Global Index to the clipboard", callback: async () => { - const { gParents, gChildren } = this.plugin.currGraphs; - const terminals = gParents.sinks(); + const allUps = getAllXGs(this.plugin, "up"); + const allDowns = getAllXGs(this.plugin, "down"); + console.log({ allUps, allDowns }); + + const upG = mergeGs(...Object.values(allUps)); + const downG = mergeGs(...Object.values(allDowns)); + console.log({ upG, downG }); + + const closedParents = closeImpliedLinks(upG, downG); + + const terminals = upG.sinks(); const settings = this.plugin.settings; let globalIndex = ""; terminals.forEach((terminal) => { globalIndex += terminal + "\n"; - const allPaths = this.dfsAllPaths( - closeImpliedLinks(gChildren, gParents), - terminal - ); + const allPaths = this.dfsAllPaths(closedParents, terminal); globalIndex = this.createIndex(globalIndex, allPaths, settings); }); @@ -239,7 +256,18 @@ export default class MatrixView extends ItemView { async draw(): Promise { this.contentEl.empty(); - const { gParents, gSiblings, gChildren } = this.plugin.currGraphs; + + const allUps = getAllXGs(this.plugin, "up"); + const allSames = getAllXGs(this.plugin, "same"); + + const allDowns = getAllXGs(this.plugin, "down"); + console.log({ allUps, allDowns }); + + const upG = mergeGs(...Object.values(allUps)); + const sameG = mergeGs(...Object.values(allSames)); + const downG = mergeGs(...Object.values(allDowns)); + console.log({ upG, sameG, downG }); + const currFile = this.app.workspace.getActiveFile(); const settings = this.plugin.settings; @@ -253,9 +281,9 @@ export default class MatrixView extends ItemView { }); const [parentFieldName, siblingFieldName, childFieldName] = [ - settings.showNameOrType ? settings.parentFieldName : "Parent", - settings.showNameOrType ? settings.siblingFieldName : "Sibling", - settings.showNameOrType ? settings.childFieldName : "Child", + "up", + "same", + "down", ]; let [ @@ -265,23 +293,21 @@ export default class MatrixView extends ItemView { impliedParents, impliedChildren, ] = [ - this.squareItems(gParents, currFile), - this.squareItems(gSiblings, currFile), - this.squareItems(gChildren, currFile), - this.squareItems(gChildren, currFile, false), - this.squareItems(gParents, currFile, false), + this.squareItems(upG, currFile), + this.squareItems(sameG, currFile), + this.squareItems(downG, currFile), + this.squareItems(downG, currFile, false), + this.squareItems(upG, currFile, false), ]; // SECTION Implied Siblings /// Notes with the same parents - const currParents = (gParents.successors(currFile.basename) ?? - []) as string[]; + const currParents = (upG.successors(currFile.basename) ?? []) as string[]; let impliedSiblingsArr: internalLinkObj[] = []; if (currParents.length) { currParents.forEach((parent) => { - const impliedSiblings = (gParents.predecessors(parent) ?? - []) as string[]; + const impliedSiblings = (upG.predecessors(parent) ?? []) as string[]; // The current note is always it's own implied sibling, so remove it from the list const indexCurrNote = impliedSiblings.indexOf(currFile.basename); @@ -302,7 +328,7 @@ export default class MatrixView extends ItemView { } /// A real sibling implies the reverse sibling - impliedSiblingsArr.push(...this.squareItems(gSiblings, currFile, false)); + impliedSiblingsArr.push(...this.squareItems(sameG, currFile, false)); // !SECTION diff --git a/src/main.ts b/src/main.ts index b83cda73..944759b4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,30 +9,31 @@ import { VIEW_TYPE_BREADCRUMBS_STATS, } from "src/constants"; import type { - allGraphs, BreadcrumbsSettings, dvFrontmatterCache, - neighbourObj, relObj, } from "src/interfaces"; import MatrixView from "src/MatrixView"; -import StatsView from "src/StatsView"; import { closeImpliedLinks, debug, + getAllXGs, getDVMetadataCache, getNeighbourObjArr, getObsMetadataCache, + mergeGs, splitAndTrim, } from "src/sharedFunctions"; +import StatsView from "src/StatsView"; +import { VisModal } from "src/VisModal"; import TrailGrid from "./Components/TrailGrid.svelte"; import TrailPath from "./Components/TrailPath.svelte"; -import { VisModal } from "src/VisModal"; const DEFAULT_SETTINGS: BreadcrumbsSettings = { - parentFieldName: "parent", - siblingFieldName: "sibling", - childFieldName: "child", + userHierarchies: [], + // parentFieldName: "parent", + // siblingFieldName: "sibling", + // childFieldName: "child", indexNote: [""], refreshIntervalTime: 0, defaultView: true, @@ -76,7 +77,7 @@ export default class BreadcrumbsPlugin extends Plugin { settings: BreadcrumbsSettings; visited: [string, HTMLDivElement][]; refreshIntervalID: number; - currGraphs: { [graph: string]: Graph }; + currGraphs: { [field: string]: Graph }[]; async onload(): Promise { console.log("loading breadcrumbs plugin"); @@ -192,18 +193,17 @@ export default class BreadcrumbsPlugin extends Plugin { populateGraph( g: Graph, currFileName: string, - relObj: relObj, - relationship: keyof relObj + fields: string[], + relationship: string ): void { g.setNode(currFileName, relationship); - relObj[relationship].forEach((node: string) => { - g.setEdge(currFileName, node, relationship); + if (relationship === "") return; + fields.forEach((field) => { + g.setEdge(currFileName, field, relationship); }); } - async initGraphs(): Promise<{ - [graph: string]: Graph; - }> { + async initGraphs(): Promise<{ [field: string]: Graph }[]> { debug(this.settings, "initialising graphs"); const files = this.app.vault.getMarkdownFiles(); @@ -217,46 +217,36 @@ export default class BreadcrumbsPlugin extends Plugin { const relObjArr = await getNeighbourObjArr(this, fileFrontmatterArr); - const { parentFieldName, siblingFieldName, childFieldName } = this.settings; - const [parentFields, siblingFields, childFields] = [ - splitAndTrim(parentFieldName), - splitAndTrim(siblingFieldName), - splitAndTrim(childFieldName), - ]; - const allFields = [parentFields, siblingFields, childFields].flat(1); + const { userHierarchies } = this.settings; + const allFields: string[] = userHierarchies + .map((hier) => Object.values(hier)) + .flat() + .filter((field: string) => field !== ""); - const graphs: { [graph: string]: Graph } = {}; + const graphs: { [field: string]: Graph }[] = []; - allFields.forEach((field) => { - graphs[field] = new Graph(); + userHierarchies.forEach((hier, i) => { + const newGraphs: { [field: string]: Graph } = {}; + newGraphs[hier.up] = new Graph(); + newGraphs[hier.same] = new Graph(); + newGraphs[hier.down] = new Graph(); + graphs.push(newGraphs); }); relObjArr.forEach((relObj) => { const currFileName = relObj.current.basename || relObj.current.name; - Object.keys(relObj).forEach((rel) => { - if (rel === "current") return; - this.populateGraph(graphs[rel], currFileName, relObj, rel); + relObj.hierarchies.forEach((hier, i) => { + Object.keys(hier).forEach((key) => { + const fields = hier[key]; + this.populateGraph(graphs[i][key], currFileName, fields, key); + }); }); }); - // const [gParents, gSiblings, gChildren] = [ - // new Graph(), - // new Graph(), - // new Graph(), - // ]; - - // neighbourArr.forEach((neighbourObj) => { - // const currFileName = - // neighbourObj.current.basename || neighbourObj.current.name; - - // this.populateGraph(gParents, currFileName, neighbourObj, "parents"); - // this.populateGraph(gSiblings, currFileName, neighbourObj, "siblings"); - // this.populateGraph(gChildren, currFileName, neighbourObj, "children"); - // }); debug(this.settings, "graphs inited"); console.log({ graphs }); - return { ...graphs }; + return graphs; } // !SECTION OneSource @@ -373,8 +363,15 @@ export default class BreadcrumbsPlugin extends Plugin { const settings = this.settings; - const { gParents, gChildren } = this.currGraphs; - const closedParents = closeImpliedLinks(gParents, gChildren); + const allUps = getAllXGs(this, "up"); + const allDowns = getAllXGs(this, "down"); + console.log({ allUps, allDowns }); + + const upG = mergeGs(...Object.values(allUps)); + const downG = mergeGs(...Object.values(allDowns)); + console.log({ upG, downG }); + + const closedParents = closeImpliedLinks(upG, downG); const sortedTrails = this.getBreadcrumbs(closedParents); debug(settings, { sortedTrails }); diff --git a/src/sharedFunctions.ts b/src/sharedFunctions.ts index 9b9a16a7..083dd830 100644 --- a/src/sharedFunctions.ts +++ b/src/sharedFunctions.ts @@ -142,12 +142,8 @@ export async function getJugglLinks( debug(settings, { typedLinksArr }); - const allFields: string[] = [ - settings.parentFieldName, - settings.siblingFieldName, - settings.childFieldName, - ] - .map(splitAndTrim) + const allFields: string[] = settings.userHierarchies + .map((hier) => Object.values(hier)) .flat() .filter((field: string) => field !== ""); @@ -213,58 +209,52 @@ export const splitAndTrim = (fields: string): string[] => export async function getNeighbourObjArr( plugin: BreadcrumbsPlugin, fileFrontmatterArr: dvFrontmatterCache[] -): Promise { - const { parentFieldName, siblingFieldName, childFieldName } = plugin.settings; - - const [parentFields, siblingFields, childFields] = [ - splitAndTrim(parentFieldName), - splitAndTrim(siblingFieldName), - splitAndTrim(childFieldName), - ]; - - const allFields = [parentFields, siblingFields, childFields].flat(1); +): Promise< + { + current: TFile; + hierarchies: { [field: string]: string[] }[]; + }[] +> { + const { userHierarchies } = plugin.settings; + const allFields: string[] = userHierarchies + .map((hier) => Object.values(hier)) + .flat() + .filter((field: string) => field !== ""); let jugglLinks: JugglLink[] = []; if (plugin.app.plugins.plugins.juggl !== undefined) { jugglLinks = await getJugglLinks(plugin.app, plugin.settings); } - const neighbourObjArr: relObj[] = fileFrontmatterArr.map( - (fileFrontmatter) => { - const relObj: relObj = { - current: fileFrontmatter.file, - }; - - allFields.forEach( - (field) => - (relObj[field] = getFieldValues( - fileFrontmatter, - field, - plugin.settings - )) - ); - - console.log({ relObj }); - - if (jugglLinks.length) { - const currFileJugglLinks = jugglLinks.filter( - (link) => - link.note === - (fileFrontmatter.file.basename || fileFrontmatter.file.name) + const neighbourObjArr: { + current: TFile; + hierarchies: { [field: string]: string[] }[]; + }[] = fileFrontmatterArr.map((fileFrontmatter) => { + const hierFields: { + current: TFile; + hierarchies: { [field: string]: string[] }[]; + } = { + current: fileFrontmatter.file, + hierarchies: [], + }; + + userHierarchies.forEach((hier, i) => { + const fields: string[] = Object.values(hier); + const newHier: { [field: string]: string[] } = {}; + fields.forEach((field) => { + const fieldValues = getFieldValues( + fileFrontmatter, + field, + plugin.settings ); + newHier[field] = fieldValues; + }); + hierFields.hierarchies.push(newHier); + }); - currFileJugglLinks.forEach((jugglLink) => { - jugglLink.links.forEach((link) => { - if (allFields.includes(link.type)) { - relObj[link.type].push(...link.linksInLine); - } - }); - }); - } + return hierFields; + }); - return { ...relObj }; - } - ); console.log({ neighbourObjArr }); debug(plugin.settings, { neighbourObjArr }); return neighbourObjArr; @@ -273,6 +263,7 @@ export async function getNeighbourObjArr( // This function takes the real & implied graphs for a given relation, and returns a new graphs with both. // It makes implied relations real export function closeImpliedLinks(real: Graph, implied: Graph): Graph { + console.log({ real, implied }); const closedG = graphlib.json.read(graphlib.json.write(real)); implied.edges().forEach((impliedEdge) => { closedG.setEdge(impliedEdge.w, impliedEdge.v); @@ -432,6 +423,18 @@ export function mergeGraphs(g1: Graph, g2: Graph) { return copy1; } +export function mergeGs(...graphs: Graph[]) { + const copy = graphlib.json.read(graphlib.json.write(graphs[0])); + graphs.forEach((graph, i) => { + if (i > 0) { + graph.edges().forEach((edge) => { + copy.setEdge(edge); + }); + } + }); + return copy; +} + export function removeUnlinkedNodes(g: Graph) { const copy = graphlib.json.read(graphlib.json.write(g)); const nodes = copy.nodes(); @@ -441,3 +444,24 @@ export function removeUnlinkedNodes(g: Graph) { unlinkedNodes.forEach((node) => copy.removeNode(node)); return copy; } + +export function getAllXGs( + plugin: BreadcrumbsPlugin, + rel: "up" | "same" | "down" +) { + const { userHierarchies } = plugin.settings; + const fieldNamesInXDir = userHierarchies + .map((hier) => hier[rel]) + .filter((field) => field !== ""); + const currHiers = plugin.currGraphs; + const allXGs: { [rel: string]: Graph } = {}; + currHiers.forEach((hier) => { + fieldNamesInXDir.forEach((field) => { + const graph = hier[field]; + if (hier[field]) { + allXGs[field] = graph; + } + }); + }); + return allXGs; +}