fix: preserve placeholder case when restoring project text
This commit is contained in:
17
src/lib/restore-case.ts
Normal file
17
src/lib/restore-case.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/** Keep the saved spelling for the canonical placeholder and adapt edited case variants. */
|
||||
export function restoreOriginalCase(match: string, key: string, original: string): string {
|
||||
if (match === key) return original;
|
||||
const letters = match.replace(/[^\p{L}]/gu, "");
|
||||
if (!letters) return original;
|
||||
if (letters === letters.toLocaleLowerCase("de")) return original.toLocaleLowerCase("de");
|
||||
if (letters === letters.toLocaleUpperCase("de")) return original.toLocaleUpperCase("de");
|
||||
if (/^\p{Lu}\p{Ll}+$/u.test(letters)) {
|
||||
const originalLetters = original.replace(/[^\p{L}]/gu, "");
|
||||
if (originalLetters && (originalLetters === originalLetters.toLocaleLowerCase("de")
|
||||
|| originalLetters === originalLetters.toLocaleUpperCase("de"))) {
|
||||
return original.toLocaleLowerCase("de").replace(/(^|[^\p{L}])(\p{L})/gu,
|
||||
(_, prefix: string, letter: string) => prefix + letter.toLocaleUpperCase("de"));
|
||||
}
|
||||
}
|
||||
return original;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { PlaceholderMapping } from "../types";
|
||||
import type { ProjectRun } from "./velum-project";
|
||||
import { restoreOriginalCase } from "./restore-case";
|
||||
|
||||
function escapeRegExp(value: string): string {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
@@ -98,6 +99,13 @@ function replaceNode(
|
||||
session: boolean,
|
||||
): { value: string; count: number } {
|
||||
const candidates: Match[] = [];
|
||||
const foldedCounts = new Map<string, number>();
|
||||
if (!session) {
|
||||
for (const key of Object.keys(mapping)) {
|
||||
const folded = key.toUpperCase();
|
||||
foldedCounts.set(folded, (foldedCounts.get(folded) ?? 0) + 1);
|
||||
}
|
||||
}
|
||||
for (const [key, entry] of Object.entries(mapping)) {
|
||||
const stripped = key.replace(/[<>]/g, "");
|
||||
const bare = session ? stripped : key;
|
||||
@@ -105,7 +113,7 @@ function replaceNode(
|
||||
matchModes?.[key] === "anywhere"
|
||||
? escapeRegExp(bare)
|
||||
: `(?<![\\p{L}\\p{N}])${escapeRegExp(bare)}(?![\\p{L}\\p{N}])`;
|
||||
const patterns = [new RegExp(boundary, "gu")];
|
||||
const patterns = [new RegExp(boundary, session ? "gu" : "giu")];
|
||||
if (session) {
|
||||
patterns.unshift(
|
||||
new RegExp(
|
||||
@@ -116,10 +124,12 @@ function replaceNode(
|
||||
}
|
||||
for (const pattern of patterns) {
|
||||
for (const match of value.matchAll(pattern)) {
|
||||
if (!session && match[0] !== key && (Object.prototype.hasOwnProperty.call(mapping, match[0])
|
||||
|| foldedCounts.get(key.toUpperCase()) !== 1)) continue;
|
||||
candidates.push({
|
||||
start: match.index,
|
||||
end: match.index + match[0].length,
|
||||
replacement: escapeHtml(entry.original),
|
||||
replacement: escapeHtml(session ? entry.original : restoreOriginalCase(match[0], key, entry.original)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { environment } from "@raycast/api";
|
||||
import type { EntityType, PlaceholderMapping } from "../types";
|
||||
import { whitelistKey } from "./project-whitelist";
|
||||
import type { ProjectWhitelistEntry } from "./project-whitelist";
|
||||
import { restoreOriginalCase } from "./restore-case";
|
||||
|
||||
export { whitelistKey } from "./project-whitelist";
|
||||
export type { ProjectWhitelistEntry } from "./project-whitelist";
|
||||
@@ -265,7 +266,7 @@ function restorePattern(mapping: PlaceholderMapping, matchModes: ProjectRun["mat
|
||||
originalFor: (match) => {
|
||||
const key = Object.prototype.hasOwnProperty.call(mapping, match)
|
||||
? match : foldedKeys.get(match.toUpperCase());
|
||||
return key ? mapping[key].original : undefined;
|
||||
return key ? restoreOriginalCase(match, key, mapping[key].original) : undefined;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -74,4 +74,16 @@ describe("formatierte Textwiederherstellung", () => {
|
||||
),
|
||||
).toBe("<b>abcMax Mustermannxyz</b>");
|
||||
});
|
||||
|
||||
it("erhält HTML-Formatierung auch bei anders geschriebenen Platzhaltern", () => {
|
||||
const html = "<b>person_01</b> und <i>Person_01</i>";
|
||||
expect(restoreRichHtml(html, "person_01 und Person_01",
|
||||
"max mustermann und Max Mustermann", mapping))
|
||||
.toBe("<b>max mustermann</b> und <i>Max Mustermann</i>");
|
||||
expect(restoreRichHtml("<b>Person_01</b> / <i>PERSON_01</i>",
|
||||
"Person_01 / PERSON_01", "Person_01 / Eins", {
|
||||
PERSON_01: { original: "Eins", type: "PERSON" },
|
||||
person_01: { original: "Zwei", type: "PERSON" },
|
||||
})).toBe("<b>Person_01</b> / <i>Eins</i>");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -134,10 +134,19 @@ describe("Velum-Projektstände", () => {
|
||||
})).toBe("Archiv/BerichtMax Mustermann.docx");
|
||||
expect(project.restorePath("archiv/bericht_person_01.docx", {
|
||||
PERSON_01: { original: "Max Mustermann", type: "PERSON" },
|
||||
})).toBe("archiv/bericht_Max Mustermann.docx");
|
||||
})).toBe("archiv/bericht_max mustermann.docx");
|
||||
expect(project.restoreTextWithCount("person_01 und Person_01", {
|
||||
PERSON_01: { original: "Max Mustermann", type: "PERSON" },
|
||||
})).toEqual({ text: "Max Mustermann und Max Mustermann", replacements: 2 });
|
||||
})).toEqual({ text: "max mustermann und Max Mustermann", replacements: 2 });
|
||||
expect(project.restoreText("PERSON_01 / person_01 / Person_01", {
|
||||
PERSON_01: { original: "Max Mustermann", type: "PERSON" },
|
||||
})).toBe("Max Mustermann / max mustermann / Max Mustermann");
|
||||
expect(project.restoreText("KUNDE_01 / kunde_01 / Kunde_01", {
|
||||
KUNDE_01: { original: "ACME GMBH", type: "KUNDE" },
|
||||
})).toBe("ACME GMBH / acme gmbh / Acme Gmbh");
|
||||
expect(project.restoreText("person_01 / PERSON_01", {
|
||||
person_01: { original: "max mustermann", type: "PERSON" },
|
||||
})).toBe("max mustermann / MAX MUSTERMANN");
|
||||
expect(project.restoreText("BerichtPERSON_01", {
|
||||
PERSON_01: { original: "Max Mustermann", type: "PERSON" },
|
||||
})).toBe("BerichtPERSON_01");
|
||||
|
||||
Reference in New Issue
Block a user