Skip to content

Commit e1b5417

Browse files
committed
Improve text truncation for descriptions and listings
1 parent 8b9a66f commit e1b5417

File tree

3 files changed

+98
-30
lines changed

3 files changed

+98
-30
lines changed

src/core/text.ts

Lines changed: 88 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,97 @@ export function formatLineRange(
4848
};
4949
}
5050

51-
export function truncateText(text: string, length: number) {
51+
const kLastPunctuationRegex = /([\S\s]*)[\.\?\!]/;
52+
function trimSentence(text: string) {
53+
const match = text.match(kLastPunctuationRegex);
54+
if (match) {
55+
return {
56+
text: match[0],
57+
trimmed: true,
58+
};
59+
} else {
60+
return {
61+
text,
62+
trimmed: false,
63+
};
64+
}
65+
}
66+
67+
function trimLength(text: string, length: number) {
5268
if (text.length < length) {
53-
return text;
69+
return {
70+
text,
71+
trimmed: false,
72+
};
73+
} else {
74+
return {
75+
text: text.substring(0, length),
76+
trimmed: true,
77+
};
78+
}
79+
}
80+
81+
function trimSpace(text: string) {
82+
const lastSpace = text.lastIndexOf(" ");
83+
if (lastSpace > 0) {
84+
return {
85+
text: text.substring(0, lastSpace),
86+
trimmed: true,
87+
};
88+
} else {
89+
return {
90+
text,
91+
trimmed: false,
92+
};
93+
}
94+
}
95+
96+
export function truncateText(
97+
text: string,
98+
length: number,
99+
breakAt: "space" | "punctuation",
100+
) {
101+
const trimEnd = (text: string) => {
102+
if ([",", "/", ":"].includes(text.charAt(text.length - 1))) {
103+
return text.substring(0, text.length - 1);
104+
} else {
105+
return text;
106+
}
107+
};
108+
109+
const trimAtSpace = (text: string) => {
110+
console.log(text);
111+
const spaceResult = trimSpace(
112+
text.substring(0, text.length - 1),
113+
);
114+
console.log(spaceResult.text);
115+
return trimEnd(spaceResult.text) + "…";
116+
};
117+
118+
const trimPunc = (text: string) => {
119+
const puncResult = trimSentence(text);
120+
if (puncResult.trimmed) {
121+
return puncResult.text;
122+
} else {
123+
return trimAtSpace(puncResult.text);
124+
}
125+
};
126+
127+
const lengthResult = trimLength(text, length);
128+
129+
if (lengthResult.trimmed) {
130+
// This was shortened
131+
if (breakAt === "punctuation") {
132+
return trimPunc(lengthResult.text);
133+
} else {
134+
return trimAtSpace(lengthResult.text);
135+
}
54136
} else {
55-
// Since we'll insert elips, trim an extra space
56-
const clipLength = length - 1;
57-
const clipped = text.substring(0, clipLength);
58-
const lastSpace = clipped.lastIndexOf(" ");
59-
if (lastSpace > 0) {
60-
return clipped.substring(0, lastSpace) + "…";
137+
// This wasn't shortened
138+
if (breakAt === "punctuation") {
139+
return trimPunc(lengthResult.text);
61140
} else {
62-
return clipped + "…";
141+
return trimEnd(lengthResult.text);
63142
}
64143
}
65144
}

src/project/types/website/listing/website-listing-template.ts

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
import { resourcePath } from "../../../../core/resources.ts";
4242
import { localizedString } from "../../../../config/localization.ts";
4343
import { formatDate, parsePandocDate } from "../../../../core/date.ts";
44+
import { truncateText } from "../../../../core/text.ts";
4445

4546
export const kDateFormat = "date-format";
4647

@@ -111,7 +112,11 @@ export function templateMarkdownHandler(
111112
const maxDescLength = listing[kMaxDescLength] as number ||
112113
-1;
113114
if (maxDescLength > 0) {
114-
record.description = truncateText(item.description, maxDescLength);
115+
record.description = truncateText(
116+
item.description,
117+
maxDescLength,
118+
"space",
119+
);
115120
}
116121
}
117122

@@ -198,14 +203,14 @@ export function templateMarkdownHandler(
198203
if (content) {
199204
content.appendChild(listingEl);
200205
} else {
201-
// Custom page layout doesn't have a main.content, so
206+
// Custom page layout doesn't have a main.content, so
202207
// just use the quarto-content div directly
203208
const customContent = doc.querySelector("#quarto-content");
204209
if (customContent) {
205210
customContent.appendChild(listingEl);
206211
} else {
207212
// Couldn't find anywhere to put the listing el, just
208-
// stick at the bottom of the body
213+
// stick at the bottom of the body
209214
doc.body.appendChild(listingEl);
210215
}
211216
}
@@ -513,19 +518,3 @@ export function templateJsScript(
513518
`;
514519
return jsScript;
515520
}
516-
517-
function truncateText(text: string, length: number) {
518-
if (text.length < length) {
519-
return text;
520-
} else {
521-
// Since we'll insert elips, trim an extra space
522-
const clipLength = length - 1;
523-
const clipped = text.substring(0, clipLength);
524-
const lastSpace = clipped.lastIndexOf(" ");
525-
if (lastSpace > 0) {
526-
return clipped.substring(0, lastSpace) + "…";
527-
} else {
528-
return clipped + "…";
529-
}
530-
}
531-
}

src/project/types/website/website-meta.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export function metadataHtmlPostProcessor(
106106
resolveValue: (key: string, value: string) => {
107107
// Limit to 300 chars for Open Graph
108108
if ([kDescription].includes(key)) {
109-
return truncateText(value, 300);
109+
return truncateText(value, 200, "punctuation");
110110
}
111111

112112
return value;
@@ -135,7 +135,7 @@ export function metadataHtmlPostProcessor(
135135
resolveValue: (key: string, value: string) => {
136136
// Limit to 200 chars for Twitter
137137
if ([kDescription].includes(key)) {
138-
return truncateText(value, 200);
138+
return truncateText(value, 200, "punctuation");
139139
}
140140

141141
return value;

0 commit comments

Comments
 (0)