slopless 0.2.24 → 0.2.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/dist/rules/semantic-thinness/patterns/abstract-metaphor-claim.json +83 -1
- package/dist/rules/semantic-thinness/patterns/deictic-summary.json +31 -2
- package/dist/rules/semantic-thinness/patterns/empty-scene-transition.json +2 -1
- package/dist/rules/semantic-thinness/private/concrete-guards.js +2 -0
- package/dist/rules/semantic-thinness/private/pattern-matcher.js +22 -6
- package/dist/rules/syntactic-patterns/contrast/contrastive-aphorism.js +2 -0
- package/dist/rules/syntactic-patterns/contrast/negation-reframe.js +1 -1
- package/dist/rules/syntactic-patterns/contrast/private/action-reframe-vocabulary.js +47 -0
- package/dist/rules/syntactic-patterns/contrast/private/audience-replacement.js +267 -0
- package/dist/rules/syntactic-patterns/contrast/private/copular-reframe.js +39 -1
- package/dist/rules/syntactic-patterns/contrast/private/evidence-limitation-pair.js +307 -0
- package/dist/rules/syntactic-patterns/contrast/private/inline-semicolon-reframe.js +50 -0
- package/dist/rules/syntactic-patterns/contrast/private/inline-short-negation.js +1 -0
- package/dist/rules/syntactic-patterns/contrast/private/negation-reframe-matcher.js +64 -7
- package/dist/rules/syntactic-patterns/contrast/private/negation-reframe-parts.js +37 -25
- package/dist/rules/syntactic-patterns/contrast/private/negation-vocabulary.js +27 -0
- package/dist/rules/syntactic-patterns/contrast/private/negative-consequence.js +301 -0
- package/dist/rules/syntactic-patterns/contrast/private/negative-slop-frames.js +51 -38
- package/dist/rules/syntactic-patterns/contrast/private/reframe-classification.js +21 -0
- package/dist/rules/syntactic-patterns/contrast/private/sequence-reframes.js +9 -0
- package/dist/rules/syntactic-patterns/contrast/private/temporal-reframe.js +318 -0
- package/dist/rules/syntactic-patterns/lead-ins/generic-signposting.js +85 -22
- package/dist/rules/syntactic-patterns/lead-ins/private/component-assignment-frame.js +239 -0
- package/dist/rules/syntactic-patterns/lead-ins/private/discourse-evaluation.js +1 -0
- package/dist/rules/syntactic-patterns/lead-ins/private/evaluative-colon-frame.js +166 -0
- package/dist/rules/syntactic-patterns/lead-ins/private/reaction-frame.js +29 -0
- package/dist/rules/syntactic-patterns/lead-ins/private/relative-discourse-frame.js +119 -0
- package/package.json +5 -5
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { wordTokens } from "../../../../shared/text/tokens.js";
|
|
2
|
+
const ABSTRACT_CATEGORY_HEADS = new Set([
|
|
3
|
+
"acquisition",
|
|
4
|
+
"administration",
|
|
5
|
+
"advertising",
|
|
6
|
+
"analytics",
|
|
7
|
+
"answer",
|
|
8
|
+
"automation",
|
|
9
|
+
"awareness",
|
|
10
|
+
"brand",
|
|
11
|
+
"channel",
|
|
12
|
+
"commerce",
|
|
13
|
+
"compliance",
|
|
14
|
+
"content",
|
|
15
|
+
"control",
|
|
16
|
+
"conversion",
|
|
17
|
+
"copy",
|
|
18
|
+
"decision",
|
|
19
|
+
"delivery",
|
|
20
|
+
"demand",
|
|
21
|
+
"discoverability",
|
|
22
|
+
"discovery",
|
|
23
|
+
"distribution",
|
|
24
|
+
"documentation",
|
|
25
|
+
"editorial",
|
|
26
|
+
"enablement",
|
|
27
|
+
"engagement",
|
|
28
|
+
"execution",
|
|
29
|
+
"experience",
|
|
30
|
+
"governance",
|
|
31
|
+
"growth",
|
|
32
|
+
"hygiene",
|
|
33
|
+
"implementation",
|
|
34
|
+
"indexing",
|
|
35
|
+
"infrastructure",
|
|
36
|
+
"management",
|
|
37
|
+
"maintenance",
|
|
38
|
+
"market",
|
|
39
|
+
"marketing",
|
|
40
|
+
"media",
|
|
41
|
+
"merchandising",
|
|
42
|
+
"operations",
|
|
43
|
+
"optimization",
|
|
44
|
+
"page",
|
|
45
|
+
"plumbing",
|
|
46
|
+
"policy",
|
|
47
|
+
"positioning",
|
|
48
|
+
"procurement",
|
|
49
|
+
"product",
|
|
50
|
+
"production",
|
|
51
|
+
"publishing",
|
|
52
|
+
"ranking",
|
|
53
|
+
"recommendation",
|
|
54
|
+
"regulation",
|
|
55
|
+
"reporting",
|
|
56
|
+
"retrieval",
|
|
57
|
+
"retention",
|
|
58
|
+
"revenue",
|
|
59
|
+
"risk",
|
|
60
|
+
"sales",
|
|
61
|
+
"search",
|
|
62
|
+
"security",
|
|
63
|
+
"service",
|
|
64
|
+
"sourcing",
|
|
65
|
+
"storefront",
|
|
66
|
+
"strategy",
|
|
67
|
+
"support",
|
|
68
|
+
"trust",
|
|
69
|
+
"visibility",
|
|
70
|
+
"workflow",
|
|
71
|
+
"work"
|
|
72
|
+
]);
|
|
73
|
+
const ARTICLES = new Set(["a", "an", "the"]);
|
|
74
|
+
const PRESENT_COPULAS = new Set(["am", "are", "is"]);
|
|
75
|
+
const CONTRACTED_COPULAS = new Map([
|
|
76
|
+
["i'm", { copula: "am", subject: ["i"] }],
|
|
77
|
+
["you're", { copula: "are", subject: ["you"] }],
|
|
78
|
+
["we're", { copula: "are", subject: ["we"] }],
|
|
79
|
+
["they're", { copula: "are", subject: ["they"] }],
|
|
80
|
+
["he's", { copula: "is", subject: ["he"] }],
|
|
81
|
+
["she's", { copula: "is", subject: ["she"] }],
|
|
82
|
+
["it's", { copula: "is", subject: ["it"] }],
|
|
83
|
+
["that's", { copula: "is", subject: ["that"] }]
|
|
84
|
+
]);
|
|
85
|
+
const PRONOUNS = new Set([
|
|
86
|
+
"he",
|
|
87
|
+
"i",
|
|
88
|
+
"it",
|
|
89
|
+
"she",
|
|
90
|
+
"that",
|
|
91
|
+
"they",
|
|
92
|
+
"this",
|
|
93
|
+
"we",
|
|
94
|
+
"you"
|
|
95
|
+
]);
|
|
96
|
+
const PLURAL_PRONOUNS = new Set(["they", "we", "you"]);
|
|
97
|
+
const SINGULAR_S_SUBJECTS = new Set([
|
|
98
|
+
"analytics",
|
|
99
|
+
"business",
|
|
100
|
+
"economics",
|
|
101
|
+
"logistics",
|
|
102
|
+
"news",
|
|
103
|
+
"operations",
|
|
104
|
+
"sales"
|
|
105
|
+
]);
|
|
106
|
+
const FACTUAL_CONNECTORS = new Set([
|
|
107
|
+
"after",
|
|
108
|
+
"although",
|
|
109
|
+
"because",
|
|
110
|
+
"before",
|
|
111
|
+
"if",
|
|
112
|
+
"since",
|
|
113
|
+
"therefore",
|
|
114
|
+
"until",
|
|
115
|
+
"when",
|
|
116
|
+
"while"
|
|
117
|
+
]);
|
|
118
|
+
const DETAIL_PREPOSITIONS = new Set([
|
|
119
|
+
"above",
|
|
120
|
+
"at",
|
|
121
|
+
"behind",
|
|
122
|
+
"below",
|
|
123
|
+
"beside",
|
|
124
|
+
"between",
|
|
125
|
+
"by",
|
|
126
|
+
"from",
|
|
127
|
+
"in",
|
|
128
|
+
"inside",
|
|
129
|
+
"into",
|
|
130
|
+
"near",
|
|
131
|
+
"of",
|
|
132
|
+
"on",
|
|
133
|
+
"outside",
|
|
134
|
+
"through",
|
|
135
|
+
"to",
|
|
136
|
+
"under",
|
|
137
|
+
"with",
|
|
138
|
+
"within"
|
|
139
|
+
]);
|
|
140
|
+
const PHYSICAL_PROPERTY_WORDS = new Set([
|
|
141
|
+
"blue",
|
|
142
|
+
"cold",
|
|
143
|
+
"dry",
|
|
144
|
+
"empty",
|
|
145
|
+
"full",
|
|
146
|
+
"green",
|
|
147
|
+
"heavy",
|
|
148
|
+
"hot",
|
|
149
|
+
"large",
|
|
150
|
+
"light",
|
|
151
|
+
"local",
|
|
152
|
+
"long",
|
|
153
|
+
"optional",
|
|
154
|
+
"red",
|
|
155
|
+
"required",
|
|
156
|
+
"shared",
|
|
157
|
+
"short",
|
|
158
|
+
"small",
|
|
159
|
+
"tall",
|
|
160
|
+
"warm",
|
|
161
|
+
"weekly",
|
|
162
|
+
"wet",
|
|
163
|
+
"wide"
|
|
164
|
+
]);
|
|
165
|
+
const FRAME_SUBJECTS = new Set([
|
|
166
|
+
"audit",
|
|
167
|
+
"content",
|
|
168
|
+
"data",
|
|
169
|
+
"feed",
|
|
170
|
+
"index",
|
|
171
|
+
"indexing",
|
|
172
|
+
"metadata",
|
|
173
|
+
"page",
|
|
174
|
+
"product",
|
|
175
|
+
"search",
|
|
176
|
+
"seo",
|
|
177
|
+
"visibility"
|
|
178
|
+
]);
|
|
179
|
+
function isCompleteSentence(text) {
|
|
180
|
+
return [".", "!", "?"].includes(text.trim().at(-1) ?? "");
|
|
181
|
+
}
|
|
182
|
+
function isLetter(character) {
|
|
183
|
+
return (character.toLocaleLowerCase("en") !== character.toLocaleUpperCase("en"));
|
|
184
|
+
}
|
|
185
|
+
function isInteriorApostrophe(text, index) {
|
|
186
|
+
return isLetter(text[index - 1] ?? "") && isLetter(text[index + 1] ?? "");
|
|
187
|
+
}
|
|
188
|
+
function hasQuotedValueOrNumber(text) {
|
|
189
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
190
|
+
const character = text[index] ?? "";
|
|
191
|
+
if ((character >= "0" && character <= "9") ||
|
|
192
|
+
['"', "`", "\u201c", "\u201d"].includes(character)) {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
if (["'", "\u2018", "\u2019"].includes(character) &&
|
|
196
|
+
!isInteriorApostrophe(text, index)) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
function withoutArticle(words) {
|
|
203
|
+
return ARTICLES.has(words[0] ?? "") ? words.slice(1) : words;
|
|
204
|
+
}
|
|
205
|
+
function parsePastClause(text) {
|
|
206
|
+
const words = wordTokens(text).map((token) => token.normalized);
|
|
207
|
+
const usedIndex = words.findIndex((word, index) => word === "used" && words[index + 1] === "to" && words[index + 2] === "be");
|
|
208
|
+
const subject = words.slice(0, usedIndex);
|
|
209
|
+
const label = withoutArticle(words.slice(usedIndex + 3));
|
|
210
|
+
return usedIndex > 0 &&
|
|
211
|
+
subject.length <= 4 &&
|
|
212
|
+
label.length > 0 &&
|
|
213
|
+
label.length <= 4
|
|
214
|
+
? { label, subject }
|
|
215
|
+
: undefined;
|
|
216
|
+
}
|
|
217
|
+
function parsePresentClause(text) {
|
|
218
|
+
const words = wordTokens(text).map((token) => token.normalized);
|
|
219
|
+
const start = words[0] === "now" ? 1 : 0;
|
|
220
|
+
const contracted = CONTRACTED_COPULAS.get(words[start] ?? "");
|
|
221
|
+
if (contracted !== undefined) {
|
|
222
|
+
const label = withoutArticle(words.slice(start + 1));
|
|
223
|
+
return label.length > 0 && label.length <= 4
|
|
224
|
+
? { ...contracted, label }
|
|
225
|
+
: undefined;
|
|
226
|
+
}
|
|
227
|
+
const copulaIndex = words.findIndex((word, index) => index >= start && PRESENT_COPULAS.has(word));
|
|
228
|
+
const copula = words[copulaIndex];
|
|
229
|
+
const subject = words.slice(start, copulaIndex);
|
|
230
|
+
const label = withoutArticle(words.slice(copulaIndex + 1));
|
|
231
|
+
return copulaIndex > start &&
|
|
232
|
+
copula !== undefined &&
|
|
233
|
+
subject.length <= 4 &&
|
|
234
|
+
label.length > 0 &&
|
|
235
|
+
label.length <= 4
|
|
236
|
+
? { copula, label, subject }
|
|
237
|
+
: undefined;
|
|
238
|
+
}
|
|
239
|
+
function sameSubjectOrPronoun(first, second) {
|
|
240
|
+
if (first.length === second.length &&
|
|
241
|
+
first.every((word, index) => word === second[index])) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
if (second.length !== 1 || !PRONOUNS.has(second[0] ?? "")) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
const firstPronoun = first.length === 1 ? first[0] : undefined;
|
|
248
|
+
if (firstPronoun !== undefined && PRONOUNS.has(firstPronoun)) {
|
|
249
|
+
return firstPronoun === second[0];
|
|
250
|
+
}
|
|
251
|
+
const subjectHead = first.at(-1) ?? "";
|
|
252
|
+
const pluralSubject = isPluralSubjectHead(subjectHead);
|
|
253
|
+
return pluralSubject
|
|
254
|
+
? second[0] === "they"
|
|
255
|
+
: ["it", "that", "this"].includes(second[0] ?? "");
|
|
256
|
+
}
|
|
257
|
+
function isPluralSubjectHead(head) {
|
|
258
|
+
return (head.endsWith("s") && !head.endsWith("ss") && !SINGULAR_S_SUBJECTS.has(head));
|
|
259
|
+
}
|
|
260
|
+
function hasCopulaAgreement(clause) {
|
|
261
|
+
const subject = clause.subject;
|
|
262
|
+
const copula = clause.copula;
|
|
263
|
+
if (copula === undefined) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
if (subject.length === 1 && PRONOUNS.has(subject[0] ?? "")) {
|
|
267
|
+
if (subject[0] === "i") {
|
|
268
|
+
return copula === "am";
|
|
269
|
+
}
|
|
270
|
+
return PLURAL_PRONOUNS.has(subject[0] ?? "")
|
|
271
|
+
? copula === "are"
|
|
272
|
+
: copula === "is";
|
|
273
|
+
}
|
|
274
|
+
const head = subject.at(-1) ?? "";
|
|
275
|
+
const plural = isPluralSubjectHead(head);
|
|
276
|
+
return copula === (plural ? "are" : "is");
|
|
277
|
+
}
|
|
278
|
+
function isAbstractLabel(label) {
|
|
279
|
+
return ABSTRACT_CATEGORY_HEADS.has(label.at(-1) ?? "");
|
|
280
|
+
}
|
|
281
|
+
function hasBlockedDetail(words) {
|
|
282
|
+
return words.some((word) => FACTUAL_CONNECTORS.has(word) ||
|
|
283
|
+
DETAIL_PREPOSITIONS.has(word) ||
|
|
284
|
+
PHYSICAL_PROPERTY_WORDS.has(word));
|
|
285
|
+
}
|
|
286
|
+
function isPassiveLabel(label) {
|
|
287
|
+
const head = label[0] ?? "";
|
|
288
|
+
return head.endsWith("ed") || head.endsWith("en");
|
|
289
|
+
}
|
|
290
|
+
function isFrameSubject(subject) {
|
|
291
|
+
const head = subject.at(-1) ?? "";
|
|
292
|
+
return (FRAME_SUBJECTS.has(head) ||
|
|
293
|
+
(subject.length === 1 && ["it", "that", "this"].includes(head)));
|
|
294
|
+
}
|
|
295
|
+
export function matchTemporalReframe(first, second) {
|
|
296
|
+
if (!isCompleteSentence(first) ||
|
|
297
|
+
!isCompleteSentence(second) ||
|
|
298
|
+
hasQuotedValueOrNumber(first) ||
|
|
299
|
+
hasQuotedValueOrNumber(second)) {
|
|
300
|
+
return undefined;
|
|
301
|
+
}
|
|
302
|
+
const past = parsePastClause(first);
|
|
303
|
+
const present = parsePresentClause(second);
|
|
304
|
+
if (past === undefined || present === undefined) {
|
|
305
|
+
return undefined;
|
|
306
|
+
}
|
|
307
|
+
const allWords = [...past.subject, ...past.label, ...present.label];
|
|
308
|
+
return sameSubjectOrPronoun(past.subject, present.subject) &&
|
|
309
|
+
isFrameSubject(past.subject) &&
|
|
310
|
+
hasCopulaAgreement(present) &&
|
|
311
|
+
(isAbstractLabel(past.label) || isAbstractLabel(present.label)) &&
|
|
312
|
+
!hasBlockedDetail(allWords) &&
|
|
313
|
+
!isPassiveLabel(past.label) &&
|
|
314
|
+
!isPassiveLabel(present.label)
|
|
315
|
+
? `${first} ${second}`
|
|
316
|
+
: undefined;
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=temporal-reframe.js.map
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import { defineTextlintRule } from "../../../adapters/textlint/rule.js";
|
|
2
|
+
import { paragraphUnits, sentenceUnits } from "../../../adapters/textlint/units.js";
|
|
1
3
|
import { hasConcreteImplementationSummary } from "../../../shared/matchers/concrete-evidence.js";
|
|
2
4
|
import { cleanSentence, containsAny, tokens } from "../../../shared/matchers/prose-patterns.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
+
import { splitSentences } from "../../../shared/text/sentences.js";
|
|
6
|
+
import { matchComponentAssignmentFrame } from "./private/component-assignment-frame.js";
|
|
7
|
+
import { isAbstractAuditFrame, matchReactionFrame, matchDiscourseEvaluationFrame, matchExpandedDiscourseFrame } from "./private/discourse-evaluation.js";
|
|
8
|
+
import { matchEvaluativeColonFrame } from "./private/evaluative-colon-frame.js";
|
|
9
|
+
import { matchRelativeDiscourseFrame } from "./private/relative-discourse-frame.js";
|
|
5
10
|
const PREFIXES = ["however, ", "but ", "and ", "so "];
|
|
6
11
|
// "as such" was removed: it is a normal anaphoric connective ("a registered adviser; as
|
|
7
12
|
// such, it must...") and was the dominant false positive, not a signposting frame.
|
|
@@ -87,6 +92,19 @@ const WHAT_FRAME_TAIL_STARTERS = [
|
|
|
87
92
|
"true",
|
|
88
93
|
"usually"
|
|
89
94
|
];
|
|
95
|
+
const WHAT_MATTERS_CLAUSE_STARTERS = [
|
|
96
|
+
"how",
|
|
97
|
+
"if",
|
|
98
|
+
"that",
|
|
99
|
+
"what",
|
|
100
|
+
"when",
|
|
101
|
+
"where",
|
|
102
|
+
"whether",
|
|
103
|
+
"which",
|
|
104
|
+
"who",
|
|
105
|
+
"whose",
|
|
106
|
+
"why"
|
|
107
|
+
];
|
|
90
108
|
const POINT_NOUNS = ["goal", "job", "key", "point", "takeaway", "trick"];
|
|
91
109
|
function matchModifiedAbstractFrame(words) {
|
|
92
110
|
const [first, second, third, fourth] = words;
|
|
@@ -123,7 +141,8 @@ function matchWhatFrame(words) {
|
|
|
123
141
|
second === "changes") &&
|
|
124
142
|
third === "is" &&
|
|
125
143
|
fourth !== undefined &&
|
|
126
|
-
WHAT_FRAME_TAIL_STARTERS.includes(fourth)
|
|
144
|
+
(WHAT_FRAME_TAIL_STARTERS.includes(fourth) ||
|
|
145
|
+
(second === "matters" && !WHAT_MATTERS_CLAUSE_STARTERS.includes(fourth)))) {
|
|
127
146
|
return `what-${second}-is`;
|
|
128
147
|
}
|
|
129
148
|
if (first === "what" && second === "matters" && third === "most") {
|
|
@@ -139,11 +158,13 @@ function matchWhatFrame(words) {
|
|
|
139
158
|
}
|
|
140
159
|
function matchAbstractFrame(text) {
|
|
141
160
|
const words = tokens(text);
|
|
142
|
-
return (
|
|
161
|
+
return (matchRelativeDiscourseFrame(text, words) ??
|
|
162
|
+
matchExpandedDiscourseFrame(words) ??
|
|
143
163
|
matchModifiedAbstractFrame(words) ??
|
|
144
164
|
matchDiscourseEvaluationFrame(words) ??
|
|
145
165
|
matchPointIsToFrame(words) ??
|
|
146
|
-
matchWhatFrame(words)
|
|
166
|
+
matchWhatFrame(words) ??
|
|
167
|
+
matchReactionFrame(words));
|
|
147
168
|
}
|
|
148
169
|
function matchFormulaicContentSetup(text) {
|
|
149
170
|
if (text.startsWith("each ") &&
|
|
@@ -198,7 +219,10 @@ function matchSignposting(sentence) {
|
|
|
198
219
|
: stripped);
|
|
199
220
|
const formulaicSetup = matchFormulaicContentSetup(stripped);
|
|
200
221
|
const generatedFormula = matchGeneratedFormula(stripped);
|
|
201
|
-
if (abstract !== undefined &&
|
|
222
|
+
if (abstract !== undefined &&
|
|
223
|
+
(abstract === "what-matters-is" ||
|
|
224
|
+
abstract.startsWith("relative-") ||
|
|
225
|
+
!concreteImplementation)) {
|
|
202
226
|
return { kind: "abstract-evaluation-frame", signal: abstract };
|
|
203
227
|
}
|
|
204
228
|
if (generatedFormula !== undefined && !concreteImplementation) {
|
|
@@ -233,24 +257,63 @@ function matchSignposting(sentence) {
|
|
|
233
257
|
}
|
|
234
258
|
return undefined;
|
|
235
259
|
}
|
|
236
|
-
const rule =
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
260
|
+
const rule = defineTextlintRule({
|
|
261
|
+
detector: {
|
|
262
|
+
detect: ({ units }) => {
|
|
263
|
+
const detections = units.flatMap((unit) => {
|
|
264
|
+
if (unit.kind === "paragraph") {
|
|
265
|
+
const sentences = splitSentences(unit.text);
|
|
266
|
+
const pairDetections = [];
|
|
267
|
+
for (let index = 0; index < sentences.length - 1; index += 1) {
|
|
268
|
+
const current = sentences[index];
|
|
269
|
+
const next = sentences[index + 1];
|
|
270
|
+
if (current === undefined || next === undefined) {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
const signal = matchComponentAssignmentFrame(current.text, next.text);
|
|
274
|
+
if (signal !== undefined) {
|
|
275
|
+
pairDetections.push({
|
|
276
|
+
evidence: signal,
|
|
277
|
+
label: "component-assignment-frame",
|
|
278
|
+
range: { start: current.start, end: next.end },
|
|
279
|
+
ruleId: "syntactic-patterns:generic-signposting",
|
|
280
|
+
unitId: unit.id
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return pairDetections;
|
|
285
|
+
}
|
|
286
|
+
const componentSignal = matchComponentAssignmentFrame(unit.text);
|
|
287
|
+
const colonSignal = matchEvaluativeColonFrame(unit.text);
|
|
288
|
+
const matched = componentSignal === undefined && colonSignal === undefined
|
|
289
|
+
? matchSignposting(unit.text)
|
|
290
|
+
: undefined;
|
|
291
|
+
const signal = componentSignal ?? colonSignal ?? matched?.signal;
|
|
292
|
+
if (signal === undefined) {
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
return [
|
|
296
|
+
{
|
|
297
|
+
evidence: signal,
|
|
298
|
+
label: componentSignal !== undefined
|
|
299
|
+
? "component-assignment-frame"
|
|
300
|
+
: colonSignal !== undefined
|
|
301
|
+
? "evaluative-colon-frame"
|
|
302
|
+
: (matched?.kind ?? "generic-signposting"),
|
|
303
|
+
range: { start: 0, end: unit.text.length },
|
|
304
|
+
ruleId: "syntactic-patterns:generic-signposting",
|
|
305
|
+
unitId: unit.id
|
|
306
|
+
}
|
|
307
|
+
];
|
|
308
|
+
});
|
|
309
|
+
return detections;
|
|
310
|
+
},
|
|
311
|
+
family: "syntactic-patterns",
|
|
312
|
+
id: "syntactic-patterns:generic-signposting"
|
|
249
313
|
},
|
|
250
|
-
family: "syntactic-patterns",
|
|
251
314
|
formatMessage: (report) => `Generic signposting found: ${report.evidence}. Replace the frame with the concrete claim.`,
|
|
252
|
-
|
|
253
|
-
|
|
315
|
+
reportPolicy: { kind: "one-to-one" },
|
|
316
|
+
units: (document) => [...sentenceUnits(document), ...paragraphUnits(document)]
|
|
254
317
|
});
|
|
255
318
|
export default rule;
|
|
256
319
|
//# sourceMappingURL=generic-signposting.js.map
|