chaffjs 0.16.0 → 0.17.0
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/dist/calendar-number.d.ts +7 -2
- package/dist/calendar-number.d.ts.map +1 -1
- package/dist/calendar-number.js +10 -3
- package/dist/calendar-number.js.map +1 -1
- package/dist/closing-numbers.d.ts +20 -0
- package/dist/closing-numbers.d.ts.map +1 -0
- package/dist/closing-numbers.js +56 -0
- package/dist/closing-numbers.js.map +1 -0
- package/dist/commands/test.d.ts.map +1 -1
- package/dist/commands/test.js +11 -9
- package/dist/commands/test.js.map +1 -1
- package/dist/detectors/acronym-context.d.ts +2 -0
- package/dist/detectors/acronym-context.d.ts.map +1 -1
- package/dist/detectors/acronym-context.js +13 -0
- package/dist/detectors/acronym-context.js.map +1 -1
- package/dist/detectors/acronym-expansion.d.ts +2 -0
- package/dist/detectors/acronym-expansion.d.ts.map +1 -1
- package/dist/detectors/acronym-expansion.js +104 -1
- package/dist/detectors/acronym-expansion.js.map +1 -1
- package/dist/detectors/agreement-slip.d.ts +32 -0
- package/dist/detectors/agreement-slip.d.ts.map +1 -0
- package/dist/detectors/agreement-slip.js +137 -0
- package/dist/detectors/agreement-slip.js.map +1 -0
- package/dist/detectors/content-morphemes.d.ts +4 -0
- package/dist/detectors/content-morphemes.d.ts.map +1 -0
- package/dist/detectors/content-morphemes.js +12 -0
- package/dist/detectors/content-morphemes.js.map +1 -0
- package/dist/detectors/en-shape.d.ts +0 -1
- package/dist/detectors/en-shape.d.ts.map +1 -1
- package/dist/detectors/en-shape.js +0 -235
- package/dist/detectors/en-shape.js.map +1 -1
- package/dist/detectors/heading-echo.d.ts.map +1 -1
- package/dist/detectors/heading-echo.js +12 -18
- package/dist/detectors/heading-echo.js.map +1 -1
- package/dist/detectors/heading-overlap.d.ts +13 -0
- package/dist/detectors/heading-overlap.d.ts.map +1 -0
- package/dist/detectors/heading-overlap.js +52 -0
- package/dist/detectors/heading-overlap.js.map +1 -0
- package/dist/detectors/index.d.ts.map +1 -1
- package/dist/detectors/index.js +6 -1
- package/dist/detectors/index.js.map +1 -1
- package/dist/detectors/letter-joined-name.d.ts +13 -0
- package/dist/detectors/letter-joined-name.d.ts.map +1 -0
- package/dist/detectors/letter-joined-name.js +11 -0
- package/dist/detectors/letter-joined-name.js.map +1 -0
- package/dist/detectors/lexicon.d.ts.map +1 -1
- package/dist/detectors/lexicon.js +5 -2
- package/dist/detectors/lexicon.js.map +1 -1
- package/dist/detectors/orthography.js +2 -2
- package/dist/detectors/orthography.js.map +1 -1
- package/dist/detectors/oxford-comma.d.ts +3 -0
- package/dist/detectors/oxford-comma.d.ts.map +1 -0
- package/dist/detectors/oxford-comma.js +340 -0
- package/dist/detectors/oxford-comma.js.map +1 -0
- package/dist/detectors/signals.d.ts.map +1 -1
- package/dist/detectors/signals.js +29 -10
- package/dist/detectors/signals.js.map +1 -1
- package/dist/detectors/stacked-hedge.d.ts +0 -7
- package/dist/detectors/stacked-hedge.d.ts.map +1 -1
- package/dist/detectors/stacked-hedge.js +6 -3
- package/dist/detectors/stacked-hedge.js.map +1 -1
- package/dist/detectors/stray-space.d.ts +7 -0
- package/dist/detectors/stray-space.d.ts.map +1 -0
- package/dist/detectors/stray-space.js +34 -0
- package/dist/detectors/stray-space.js.map +1 -0
- package/dist/detectors/structure.d.ts +3 -1
- package/dist/detectors/structure.d.ts.map +1 -1
- package/dist/detectors/structure.js +4 -2
- package/dist/detectors/structure.js.map +1 -1
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +15 -3
- package/dist/document.js.map +1 -1
- package/dist/genre-parse.d.ts +5 -1
- package/dist/genre-parse.d.ts.map +1 -1
- package/dist/genre-parse.js +16 -3
- package/dist/genre-parse.js.map +1 -1
- package/dist/genre-suggest.d.ts.map +1 -1
- package/dist/genre-suggest.js +15 -1
- package/dist/genre-suggest.js.map +1 -1
- package/dist/line-paragraphs.d.ts.map +1 -1
- package/dist/line-paragraphs.js +3 -1
- package/dist/line-paragraphs.js.map +1 -1
- package/dist/mask.d.ts.map +1 -1
- package/dist/mask.js +2 -11
- package/dist/mask.js.map +1 -1
- package/dist/number-name.js +1 -1
- package/dist/number-name.js.map +1 -1
- package/dist/opening-line.d.ts +3 -0
- package/dist/opening-line.d.ts.map +1 -0
- package/dist/opening-line.js +14 -0
- package/dist/opening-line.js.map +1 -0
- package/dist/orthography.d.ts +5 -1
- package/dist/orthography.d.ts.map +1 -1
- package/dist/orthography.js +7 -1
- package/dist/orthography.js.map +1 -1
- package/dist/paragraph-length.d.ts +7 -0
- package/dist/paragraph-length.d.ts.map +1 -0
- package/dist/paragraph-length.js +10 -0
- package/dist/paragraph-length.js.map +1 -0
- package/dist/plugin.d.ts +6 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/quoted-span.d.ts +0 -2
- package/dist/quoted-span.d.ts.map +1 -1
- package/dist/quoted-span.js +1 -1
- package/dist/quoted-span.js.map +1 -1
- package/dist/rule-load.d.ts.map +1 -1
- package/dist/rule-load.js +8 -0
- package/dist/rule-load.js.map +1 -1
- package/dist/run-semantic.d.ts +3 -1
- package/dist/run-semantic.d.ts.map +1 -1
- package/dist/run-semantic.js +8 -3
- package/dist/run-semantic.js.map +1 -1
- package/dist/run.d.ts +2 -0
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +4 -1
- package/dist/run.js.map +1 -1
- package/dist/semantic.d.ts +1 -1
- package/dist/semantic.d.ts.map +1 -1
- package/dist/semantic.js +10 -3
- package/dist/semantic.js.map +1 -1
- package/dist/span-merge.d.ts +4 -0
- package/dist/span-merge.d.ts.map +1 -0
- package/dist/span-merge.js +12 -0
- package/dist/span-merge.js.map +1 -0
- package/dist/speaker-labels.d.ts +4 -0
- package/dist/speaker-labels.d.ts.map +1 -0
- package/dist/speaker-labels.js +106 -0
- package/dist/speaker-labels.js.map +1 -0
- package/dist/stray-space.d.ts +17 -0
- package/dist/stray-space.d.ts.map +1 -0
- package/dist/stray-space.js +66 -0
- package/dist/stray-space.js.map +1 -0
- package/dist/structure/relative-resolve.d.ts.map +1 -1
- package/dist/structure/relative-resolve.js +26 -20
- package/dist/structure/relative-resolve.js.map +1 -1
- package/dist/team-names.d.ts.map +1 -1
- package/dist/team-names.js +11 -14
- package/dist/team-names.js.map +1 -1
- package/genres.yaml +34 -2
- package/package.json +3 -3
- package/rules/agreement-slip.yaml +36 -0
- package/rules/empty-conclusion.yaml +1 -0
- package/rules/heading-echo.yaml +1 -0
- package/rules/max-paragraph-length.yaml +9 -2
- package/rules/max-sentence-length.yaml +4 -0
- package/rules/oxford-comma-consistency.yaml +1 -1
- package/rules/stray-space.yaml +38 -0
- package/rules/undefined-acronym.yaml +1 -0
- package/src/calendar-number.ts +16 -7
- package/src/closing-numbers.ts +72 -0
- package/src/commands/test.ts +19 -14
- package/src/detectors/acronym-context.ts +16 -0
- package/src/detectors/acronym-expansion.ts +132 -1
- package/src/detectors/agreement-slip.ts +191 -0
- package/src/detectors/content-morphemes.ts +19 -0
- package/src/detectors/en-shape.ts +0 -269
- package/src/detectors/heading-echo.ts +12 -20
- package/src/detectors/heading-overlap.ts +65 -0
- package/src/detectors/index.ts +6 -1
- package/src/detectors/letter-joined-name.ts +15 -0
- package/src/detectors/lexicon.ts +6 -2
- package/src/detectors/orthography.ts +2 -2
- package/src/detectors/oxford-comma.ts +402 -0
- package/src/detectors/signals.ts +32 -9
- package/src/detectors/stacked-hedge.ts +6 -3
- package/src/detectors/stray-space.ts +40 -0
- package/src/detectors/structure.ts +10 -2
- package/src/document.ts +25 -4
- package/src/genre-parse.ts +30 -5
- package/src/genre-suggest.ts +15 -2
- package/src/line-paragraphs.ts +4 -1
- package/src/mask.ts +2 -11
- package/src/number-name.ts +1 -1
- package/src/opening-line.ts +17 -0
- package/src/orthography.ts +10 -2
- package/src/paragraph-length.ts +9 -0
- package/src/plugin.ts +6 -0
- package/src/quoted-span.ts +1 -1
- package/src/rule-load.ts +8 -0
- package/src/run-semantic.ts +28 -20
- package/src/run.ts +5 -1
- package/src/semantic.ts +11 -3
- package/src/span-merge.ts +13 -0
- package/src/speaker-labels.ts +129 -0
- package/src/stray-space.ts +81 -0
- package/src/structure/relative-resolve.ts +34 -19
- package/src/team-names.ts +13 -13
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// 日本語の語と語のあいだの空白。どの境目を数えるかを品詞で決める。
|
|
2
|
+
import type { Sentence, Span, Token } from "./plugin.ts";
|
|
3
|
+
import { isJapanese } from "./orthography.ts";
|
|
4
|
+
import { isWithinAny, quotedIn } from "./quoted-span.ts";
|
|
5
|
+
|
|
6
|
+
/** between-phrases は助詞の後ろ(文節の切れ目)、inside-phrase は文節を始められない語の前(助詞・助動詞・名詞に続く「する」)。 */
|
|
7
|
+
export type JointKind = "between-phrases" | "inside-phrase";
|
|
8
|
+
|
|
9
|
+
/** offset は前の語の終わり(文書の座標)。before / after は境目の両側の語。 */
|
|
10
|
+
export type Joint = { readonly offset: number; readonly kind: JointKind; readonly spaced: boolean; readonly before: string; readonly after: string };
|
|
11
|
+
|
|
12
|
+
const SPACES = /^[ \u3000]+$/u;
|
|
13
|
+
const PARTICLE = new Set(["ADP", "SCONJ"]);
|
|
14
|
+
// 文節の頭に立てない語。「確認 しました」の「する」は名詞の後ろのときだけ(「し始める」の「し」は動詞の頭に立つ)。
|
|
15
|
+
const BOUND = new Set(["ADP", "SCONJ", "PART", "AUX"]);
|
|
16
|
+
const LIGHT_VERB = "する";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 空白の後ろの「が」を、解析器は文頭の接続詞(「が、」)と読む。一字の接続詞(が・と・や・か)は助詞として扱う。
|
|
20
|
+
* 二字以上(また・なお)は文節を始める接続詞。
|
|
21
|
+
*/
|
|
22
|
+
const isBound = (token: Token): boolean => BOUND.has(token.pos) || (token.pos === "CCONJ" && [...token.surface].length === 1);
|
|
23
|
+
|
|
24
|
+
const kindOf = (left: Token, right: Token): JointKind | undefined => {
|
|
25
|
+
if (isBound(right) || (right.lemma === LIGHT_VERB && left.pos === "NOUN")) return "inside-phrase";
|
|
26
|
+
// 名詞どうし(担当 田中、赤坂 文弥、フルデスクトップ モード)は名札・名前・複合語の書き方で、数えない。
|
|
27
|
+
return PARTICLE.has(left.pos) ? "between-phrases" : undefined;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 行(か文)の頭の語か。助詞は前の語に付くので、行頭の「助詞」(「ヘ」)は項目の記号を読み違えたもので、後ろの空白は記号と本文の区切り。
|
|
32
|
+
* 行頭の内容語(折り返した「確認 しました」の「確認」)は語句の頭なので、ここでは外さない。
|
|
33
|
+
*/
|
|
34
|
+
const isLineHead = (text: string, at: number): boolean => text.slice(text.lastIndexOf("\n", at - 1) + 1, at).trim() === "";
|
|
35
|
+
|
|
36
|
+
const isWord = (token: Token): boolean => token.surface.trim() !== "";
|
|
37
|
+
|
|
38
|
+
type Pair = { readonly left: Token; readonly right: Token };
|
|
39
|
+
|
|
40
|
+
const pairsOf = (tokens: readonly Token[]): Pair[] =>
|
|
41
|
+
tokens.filter(isWord).flatMap((right, index, words) => {
|
|
42
|
+
const left = words[index - 1];
|
|
43
|
+
return left === undefined ? [] : [{ left, right }];
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// 強調とリンクの括弧。読み手には見えない。
|
|
47
|
+
const CHROME = /[*_~[\]]/gu;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 空けたか詰めたか。間が何も無いか強調・リンクの括弧だけなら詰めた、書き手が書いた空白(半角・全角)だけなら空けた。
|
|
51
|
+
* 括弧の隣の空白(「**…こと** とする」「手順は [README]」)は、日本語の隣で強調が効かない Markdown のための空白なので数えない。
|
|
52
|
+
* 改行(Markdown の改行や、行の折り返し)や、見える字を覆った空白(コード)をまたぐ語も、境目に数えない。
|
|
53
|
+
*/
|
|
54
|
+
const spacing = (sentence: Sentence, written: string, pair: Pair): boolean | undefined => {
|
|
55
|
+
const [from, to] = [pair.left.span.end - sentence.span.start, pair.right.span.start - sentence.span.start];
|
|
56
|
+
const gap = written.slice(from, to);
|
|
57
|
+
if (gap.replace(CHROME, "") === "") return false;
|
|
58
|
+
return SPACES.test(gap) ? true : undefined;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const jointOf = (sentence: Sentence, written: string, quoted: readonly Span[], pair: Pair): Joint | undefined => {
|
|
62
|
+
const { left, right } = pair;
|
|
63
|
+
if (!isJapanese(left.surface.at(-1)) || !isJapanese(right.surface.at(0))) return undefined;
|
|
64
|
+
const spaced = spacing(sentence, written, pair);
|
|
65
|
+
if (spaced === undefined || isWithinAny(quoted, { start: left.span.start, end: right.span.end })) return undefined;
|
|
66
|
+
const kind = kindOf(left, right);
|
|
67
|
+
if (kind === undefined || (kind === "between-phrases" && isLineHead(sentence.text, left.span.start - sentence.span.start))) return undefined;
|
|
68
|
+
return { offset: left.span.end, kind, spaced, before: left.surface, after: right.surface };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 文の中の、日本語の語と語の境目のうち、空けるか詰めるかを数えるもの。written は本文でないものを覆う前の、文と同じ範囲。
|
|
73
|
+
* 英字・数字との境目は latin-spacing が見るので入れない。
|
|
74
|
+
*/
|
|
75
|
+
export const phraseJoints = (sentence: Sentence, written: string): Joint[] => {
|
|
76
|
+
const quoted = quotedIn(sentence);
|
|
77
|
+
return pairsOf(sentence.tokens ?? []).flatMap((pair) => {
|
|
78
|
+
const joint = jointOf(sentence, written, quoted, pair);
|
|
79
|
+
return joint === undefined ? [] : [joint];
|
|
80
|
+
});
|
|
81
|
+
};
|
|
@@ -10,7 +10,13 @@ import type { StructureNode } from "../plugin.ts";
|
|
|
10
10
|
/** range は「第四十三条から第五十五条まで 削除」のように、一行で並びをまとめたもの。番地は最初のものしか持たない。 */
|
|
11
11
|
type Place = { readonly address: string; readonly level: number; readonly range?: boolean };
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* 参照が指した文書: 他の文書の名前と、番号を名指した単位の語。
|
|
15
|
+
* 単位の語は、見出しに「1 目的」と番号を振った指針の「法第16条」が、条で数える別の文書の条だと決めるのに使う(unit-word.ts)。
|
|
16
|
+
*/
|
|
17
|
+
type Cited = { readonly document: string | undefined; readonly unitWord: string | undefined };
|
|
18
|
+
|
|
19
|
+
type Last = Cited & { readonly target: string };
|
|
14
20
|
|
|
15
21
|
const NUMBERED = new Set(["chapter", "article", "item"]);
|
|
16
22
|
|
|
@@ -100,21 +106,21 @@ const continued = (level: number, node: StructureNode, context: Context): Last |
|
|
|
100
106
|
if (origin === undefined || context.articleLevel === undefined || level < context.articleLevel) return undefined;
|
|
101
107
|
const parts = origin.target.split(".");
|
|
102
108
|
const kept = level - context.articleLevel + 1;
|
|
103
|
-
return parts.length < kept ? undefined : { target: parts.slice(0, kept).join(".")
|
|
109
|
+
return parts.length < kept ? undefined : { ...origin, target: parts.slice(0, kept).join(".") };
|
|
104
110
|
};
|
|
105
111
|
|
|
106
112
|
/**
|
|
107
113
|
* 参照が名指しした深さを覚える。名指しは、書き出しの深さ(第〇条なら条、前項なら項)から番地の深さまで。
|
|
108
114
|
* 番地を深さで切る起点は条。条より外のまとまりは番地の形が違うので覚えない。
|
|
109
115
|
*/
|
|
110
|
-
const remember = (context: Context,
|
|
116
|
+
const remember = (context: Context, reference: Last, from: number): void => {
|
|
111
117
|
const articleLevel = context.articleLevel;
|
|
112
118
|
// 章・節は番地の形(ch1)が条から始まらないので覚えない。覚えると「同条」が章を指してしまう。
|
|
113
119
|
if (articleLevel === undefined || from < articleLevel) return;
|
|
114
|
-
const parts = target.split(".");
|
|
120
|
+
const parts = reference.target.split(".");
|
|
115
121
|
parts.forEach((_, index) => {
|
|
116
122
|
const level = articleLevel + index;
|
|
117
|
-
if (level >= from) context.last.set(level, { target: parts.slice(0, index + 1).join(".")
|
|
123
|
+
if (level >= from) context.last.set(level, { ...reference, target: parts.slice(0, index + 1).join(".") });
|
|
118
124
|
});
|
|
119
125
|
};
|
|
120
126
|
|
|
@@ -145,12 +151,17 @@ const baseOf = (way: string, node: StructureNode, anchor: Anchor | undefined, la
|
|
|
145
151
|
return anchor === undefined ? undefined : byPosition(way, Number(node.attrs["count"]), anchor);
|
|
146
152
|
};
|
|
147
153
|
|
|
148
|
-
/**
|
|
149
|
-
const
|
|
150
|
-
if (way === "same") return last
|
|
151
|
-
return way === "continue" ? continued(Number(node.attrs["level"]), node, context)
|
|
154
|
+
/** 同と並びの続きは、元の参照が指した文書を引き継ぐ。前・次・本はこの文書の中。 */
|
|
155
|
+
const citedOf = (way: string, node: StructureNode, last: Last | undefined, context: Context): Cited | undefined => {
|
|
156
|
+
if (way === "same") return last;
|
|
157
|
+
return way === "continue" ? continued(Number(node.attrs["level"]), node, context) : undefined;
|
|
152
158
|
};
|
|
153
159
|
|
|
160
|
+
const citedAttrs = (cited: Cited | undefined): Record<string, string> => ({
|
|
161
|
+
...(cited?.document === undefined ? {} : { document: cited.document }),
|
|
162
|
+
...(cited?.unitWord === undefined ? {} : { unitWord: cited.unitWord }),
|
|
163
|
+
});
|
|
164
|
+
|
|
154
165
|
const resolveOne = (node: StructureNode, path: readonly StructureNode[], context: Context): StructureNode | undefined => {
|
|
155
166
|
const way = String(node.attrs["relative"]);
|
|
156
167
|
const level = Number(node.attrs["level"]);
|
|
@@ -160,12 +171,11 @@ const resolveOne = (node: StructureNode, path: readonly StructureNode[], context
|
|
|
160
171
|
const place = base === undefined ? undefined : withSuffix(base, String(node.attrs["suffix"] ?? ""), context.implicitLevel);
|
|
161
172
|
if (place === undefined) return undefined;
|
|
162
173
|
const fallback = fallbackOf(place, context.implicitLevel);
|
|
163
|
-
const document = documentOf(way, node, last, context);
|
|
164
174
|
const attrs = {
|
|
165
175
|
target: place.address,
|
|
166
176
|
label: String(node.attrs["label"]),
|
|
167
177
|
...(fallback === undefined ? {} : { fallback }),
|
|
168
|
-
...(
|
|
178
|
+
...citedAttrs(citedOf(way, node, last, context)),
|
|
169
179
|
};
|
|
170
180
|
return { ...node, attrs };
|
|
171
181
|
};
|
|
@@ -176,17 +186,16 @@ const isRelative = (node: StructureNode): boolean => node.kind === "reference" &
|
|
|
176
186
|
const walk = (node: StructureNode, path: readonly StructureNode[], context: Context): StructureNode | undefined => {
|
|
177
187
|
if (isRelative(node)) {
|
|
178
188
|
const resolved = resolveOne(node, path, context);
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
189
|
+
if (resolved === undefined) return undefined;
|
|
190
|
+
const reference = lastOf(resolved);
|
|
191
|
+
context.byStart.set(node.span.start, reference);
|
|
192
|
+
remember(context, reference, Number(node.attrs["names"] ?? node.attrs["level"]));
|
|
183
193
|
return resolved;
|
|
184
194
|
}
|
|
185
195
|
if (node.kind === "reference") {
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
context
|
|
189
|
-
remember(context, target, document, context.articleLevel ?? 0);
|
|
196
|
+
const reference = lastOf(node);
|
|
197
|
+
context.byStart.set(node.span.start, reference);
|
|
198
|
+
remember(context, reference, context.articleLevel ?? 0);
|
|
190
199
|
}
|
|
191
200
|
const inside = [...path, node];
|
|
192
201
|
const children = node.children.flatMap((child) => {
|
|
@@ -198,6 +207,12 @@ const walk = (node: StructureNode, path: readonly StructureNode[], context: Cont
|
|
|
198
207
|
|
|
199
208
|
const optional = (value: string | number | undefined): string | undefined => (value === undefined ? undefined : String(value));
|
|
200
209
|
|
|
210
|
+
const lastOf = (reference: StructureNode): Last => ({
|
|
211
|
+
target: String(reference.attrs["target"]),
|
|
212
|
+
document: optional(reference.attrs["document"]),
|
|
213
|
+
unitWord: optional(reference.attrs["unitWord"]),
|
|
214
|
+
});
|
|
215
|
+
|
|
201
216
|
const articlesOf = (node: StructureNode): Place[] => {
|
|
202
217
|
const place = node.kind === "article" ? placeOf(node) : undefined;
|
|
203
218
|
return [...(place === undefined ? [] : [place]), ...node.children.flatMap(articlesOf)];
|
package/src/team-names.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Span } from "./plugin.ts";
|
|
2
|
+
import { mergeSpans } from "./span-merge.ts";
|
|
2
3
|
|
|
3
4
|
const SPACE = /\s/u;
|
|
4
5
|
const ESCAPE = /[.*+?^${}()|[\]\\]/gu;
|
|
@@ -9,9 +10,14 @@ const WRAP = String.raw`(?:\r?\n[ \t]*)?`;
|
|
|
9
10
|
const patternOf = (name: string): RegExp => {
|
|
10
11
|
const pieces = Array.from(name.trim()).reduce<string[]>((acc, char) => {
|
|
11
12
|
const previous = acc.at(-1);
|
|
12
|
-
if (SPACE.test(char))
|
|
13
|
+
if (SPACE.test(char)) {
|
|
14
|
+
if (previous !== WHITESPACE_RUN) acc.push(WHITESPACE_RUN);
|
|
15
|
+
return acc;
|
|
16
|
+
}
|
|
13
17
|
const escaped = char.replace(ESCAPE, "\\$&");
|
|
14
|
-
|
|
18
|
+
if (previous !== undefined && previous !== WHITESPACE_RUN) acc.push(WRAP);
|
|
19
|
+
acc.push(escaped);
|
|
20
|
+
return acc;
|
|
15
21
|
}, []);
|
|
16
22
|
return new RegExp(pieces.join(""), "gu");
|
|
17
23
|
};
|
|
@@ -19,21 +25,15 @@ const patternOf = (name: string): RegExp => {
|
|
|
19
25
|
const occurrences = (text: string, name: string): Span[] =>
|
|
20
26
|
name.trim().length === 0 ? [] : [...text.matchAll(patternOf(name))].map((match) => ({ start: match.index, end: match.index + match[0].length }));
|
|
21
27
|
|
|
22
|
-
const merged = (spans: readonly Span[]): Span[] =>
|
|
23
|
-
spans
|
|
24
|
-
.toSorted((left, right) => left.start - right.start)
|
|
25
|
-
.reduce<Span[]>((acc, span) => {
|
|
26
|
-
const last = acc.at(-1);
|
|
27
|
-
if (last !== undefined && span.start < last.end) acc[acc.length - 1] = { start: last.start, end: Math.max(last.end, span.end) };
|
|
28
|
-
else acc.push(span);
|
|
29
|
-
return acc;
|
|
30
|
-
}, []);
|
|
31
|
-
|
|
32
28
|
/**
|
|
33
29
|
* チームが chaff.yaml の names に並べた名前が、text のどこにあるか。綴りは書かれたとおり(Apple と apple は別)。
|
|
34
30
|
* 重なる出現は 1 つの範囲にまとめる。接するだけなら別の名前(並んだ 2 つの名前は 2 つと数える)。位置は text の先頭を 0 とする。
|
|
35
31
|
*/
|
|
36
|
-
export const nameSpans = (text: string, names: readonly string[]): Span[] =>
|
|
32
|
+
export const nameSpans = (text: string, names: readonly string[]): Span[] =>
|
|
33
|
+
mergeSpans(
|
|
34
|
+
names.flatMap((name) => occurrences(text, name)),
|
|
35
|
+
false,
|
|
36
|
+
);
|
|
37
37
|
|
|
38
38
|
/** [start, end) が names の範囲のどれかと重なるか。 */
|
|
39
39
|
export const touchesAny = (spans: readonly Span[], start: number, end: number): boolean => spans.some((span) => start < span.end && span.start < end);
|