WikiLinks
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
# racket-wiki
|
||||
|
||||
Version 0.2.30 tightens implicit wiki links to classic WikiWords: only letter-only CamelCase words with at least two capitalized word segments are treated as implicit links. Version-like identifiers and words containing digits or punctuation are not WikiWords.
|
||||
|
||||
Current development version: **0.2.29**.
|
||||
|
||||
A small self-hosted wiki with a Racket backend and an HTML5/CSS/JavaScript frontend.
|
||||
|
||||
+17
-53
@@ -158,31 +158,21 @@
|
||||
.replace(/[^\p{L}\p{N}]+/gu, "");
|
||||
}
|
||||
|
||||
function camelCaseParts(text) {
|
||||
return String(text || "").match(/[A-Z][a-z0-9]+(?:[A-Z][A-Za-z0-9]*)+/g) || [];
|
||||
}
|
||||
|
||||
function splitCamelCase(text) {
|
||||
function wikiWordParts(text) {
|
||||
const value = String(text || "");
|
||||
if (!/^[\p{L}\p{N}]+$/u.test(value)) return [];
|
||||
|
||||
const separated = value
|
||||
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
||||
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2");
|
||||
|
||||
const parts = separated.split(/\s+/).filter(Boolean);
|
||||
return parts.length >= 2 ? parts : [];
|
||||
if (!/^(?:\p{Lu}\p{Ll}+){2,}$/u.test(value)) return [];
|
||||
return value.match(/\p{Lu}\p{Ll}+/gu) || [];
|
||||
}
|
||||
|
||||
function camelCaseTarget(text, aliases) {
|
||||
const parts = wikiWordParts(text);
|
||||
if (parts.length === 0) return null;
|
||||
|
||||
const existing = aliases.get(normalizeMentionText(text));
|
||||
if (existing) {
|
||||
return { slug: existing.slug, title: existing.title };
|
||||
}
|
||||
|
||||
const parts = splitCamelCase(text);
|
||||
if (parts.length === 0) return null;
|
||||
|
||||
return {
|
||||
slug: parts.map((part) => part.toLocaleLowerCase()).join("-"),
|
||||
title: parts.join(" ")
|
||||
@@ -195,7 +185,9 @@
|
||||
for (const value of values) {
|
||||
if (!value) continue;
|
||||
aliases.add(value);
|
||||
for (const camel of camelCaseParts(value)) aliases.add(camel);
|
||||
for (const wikiWord of value.match(/(?:\p{Lu}\p{Ll}+){2,}/gu) || []) {
|
||||
aliases.add(wikiWord);
|
||||
}
|
||||
}
|
||||
return Array.from(aliases)
|
||||
.map((value) => ({ text: value, key: normalizeMentionText(value) }))
|
||||
@@ -268,45 +260,17 @@
|
||||
}
|
||||
|
||||
const protectedRanges = markdownProtectedRanges(line);
|
||||
const words = Array.from(line.matchAll(/[\p{L}\p{N}]+/gu));
|
||||
const words = Array.from(line.matchAll(/\p{L}+/gu));
|
||||
const replacements = [];
|
||||
let wordIndex = 0;
|
||||
|
||||
while (wordIndex < words.length) {
|
||||
let match = null;
|
||||
const maxWords = Math.min(5, words.length - wordIndex);
|
||||
for (const word of words) {
|
||||
const start = word.index;
|
||||
const end = start + word[0].length;
|
||||
if (positionIsProtected(start, end, protectedRanges)) continue;
|
||||
|
||||
for (let count = maxWords; count >= 1; count -= 1) {
|
||||
const first = words[wordIndex];
|
||||
const last = words[wordIndex + count - 1];
|
||||
const start = first.index;
|
||||
const end = last.index + last[0].length;
|
||||
|
||||
if (positionIsProtected(start, end, protectedRanges)) continue;
|
||||
|
||||
const candidate = line.slice(start, end);
|
||||
const page = aliases.get(normalizeMentionText(candidate));
|
||||
if (page) {
|
||||
match = { start, end, page };
|
||||
break;
|
||||
}
|
||||
|
||||
if (count === 1) {
|
||||
const target = camelCaseTarget(candidate, aliases);
|
||||
if (target) {
|
||||
match = { start, end, page: target };
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
replacements.push(match);
|
||||
while (wordIndex < words.length && words[wordIndex].index < match.end) {
|
||||
wordIndex += 1;
|
||||
}
|
||||
} else {
|
||||
wordIndex += 1;
|
||||
const target = camelCaseTarget(word[0], aliases);
|
||||
if (target) {
|
||||
replacements.push({ start, end, page: target });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user