-
-
Save kuuote/52a88f809d9e36890dd99e776967e92f to your computer and use it in GitHub Desktop.
Scrapbox風リンク解決
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type PageRelation = { | |
relate: string; | |
pages: Page[]; | |
}; | |
const getOrCreate = <T>(map: Map<string, T>, key: string, func: () => T) => { | |
const result = map.get(key); | |
if (!result) { | |
const newValue = func(); | |
map.set(key, newValue); | |
return newValue; | |
} | |
return result; | |
}; | |
class Page { | |
static comparator = (a: Page, b: Page) => | |
a.mtime === b.mtime ? (a.name < b.name ? -1 : 1) : b.mtime - a.mtime; | |
name: string; | |
links = new Map<string, Page>(); | |
fromLinks = new Map<string, Page>(); | |
mtime = 0; | |
constructor(name: string) { | |
this.name = name; | |
} | |
getLinks(): Array<PageRelation> { | |
const links = { | |
relate: "links", | |
pages: Array.from(this.links.values()).sort(Page.comparator), | |
}; | |
const fromLinks = { | |
relate: "fromLinks", | |
pages: Array.from(this.fromLinks.values()).sort(Page.comparator), | |
}; | |
const twoHop = links.pages.map(( | |
p, | |
) => ({ | |
relate: p.name, | |
pages: Array.from(p.fromLinks.values()).sort(Page.comparator), | |
})); | |
const visit = new Map<string, void>(); | |
// ignore this at link destination | |
visit.set(this.name); | |
return [links, fromLinks, ...twoHop].map(({ relate, pages }) => ({ | |
relate, | |
pages: pages.filter((p) => { | |
if (visit.has(p.name)) { | |
return false; | |
} | |
visit.set(p.name); | |
return true; | |
}), | |
})).filter(({ pages }) => pages.length !== 0); | |
} | |
} | |
class Project { | |
path: string; | |
pages = new Map<string, Page>(); | |
constructor(path: string) { | |
this.path = path; | |
} | |
getOrCreatePage(name: string) { | |
return getOrCreate(this.pages, name, () => new Page(name)); | |
} | |
readAll() { | |
for (const entry of Deno.readDirSync(this.path)) { | |
if (entry.name.endsWith(".scp")) { | |
this.resolve(entry.name.slice(0, -4), `${this.path}/${entry.name}`); | |
} | |
} | |
} | |
resolve(name: string, path: string) { | |
const page = this.getOrCreatePage(name); | |
// 既存のリンクの削除 | |
for (const linkedPage of page.links.values()) { | |
linkedPage.fromLinks.delete(name); | |
} | |
// テキストを読んで `[link]` 形式のリンクを抽出し | |
const match = (Deno.readTextFileSync(path).match(/\[[^\]]+\]/g) ?? []).map(( | |
l, | |
) => l.slice(1, -1)); | |
// ページを関連付けする | |
for (const link of match) { | |
const linkToPage = this.getOrCreatePage(link); | |
page.links.set(link, linkToPage); | |
linkToPage.fromLinks.set(name, page); | |
} | |
} | |
} | |
const projects = new Map<string, Project>(); | |
export const getOrCreateProject = (path: string): Project => | |
getOrCreate(projects, path, () => new Project(path)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
assertEquals, | |
} from "https://deno.land/std@0.87.0/testing/asserts.ts"; | |
import { getOrCreateProject } from "./scrap.ts"; | |
const hoge = getOrCreateProject("./test"); | |
hoge.readAll(); | |
const a = hoge.getOrCreatePage('a'); | |
const b = hoge.getOrCreatePage('b'); | |
const c = hoge.getOrCreatePage('c'); | |
// resolve link | |
console.log('# resolve links'); | |
console.log('a -> b'); | |
assertEquals(Array.from(a.links.keys()).sort(), ["b"]); | |
console.log('b <- a, c'); | |
assertEquals(Array.from(b.fromLinks.keys()).sort(), ["a", "c"]); | |
console.log('c -> b'); | |
assertEquals(Array.from(c.links.keys()).sort(), ["b"]); | |
// get links, backlinks, and two hop links | |
console.log('# get links'); | |
console.log('a'); | |
console.log('links -> b | b -> c') | |
const aLinks = a.getLinks(); | |
assertEquals(aLinks.length, 2); // links, b | |
assertEquals(aLinks[0].relate, "links"); | |
assertEquals(aLinks[0].pages.map((p) => p.name), ["b"]); | |
assertEquals(aLinks[1].relate, "b"); | |
assertEquals(aLinks[1].pages.map((p) => p.name), ["c"]); // ignore self | |
console.log('b'); | |
console.log('fromLinks -> a, c') | |
const bLinks = b.getLinks(); | |
assertEquals(bLinks.length, 1); // fromLinks | |
assertEquals(bLinks[0].relate, "fromLinks"); | |
assertEquals(bLinks[0].pages.map((p) => p.name).sort(), ["a", "c"]); | |
console.log('c'); | |
console.log('links -> b | b -> a') | |
const cLinks = c.getLinks(); | |
assertEquals(cLinks.length, 2); // links, b | |
assertEquals(cLinks[0].relate, "links"); | |
assertEquals(cLinks[0].pages.map((p) => p.name), ["b"]); | |
assertEquals(cLinks[1].relate, "b"); | |
assertEquals(cLinks[1].pages.map((p) => p.name), ["a"]); // ignore self |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment