Skip to content

Commit c6a7ce2

Browse files
committed
feat(rule): support "allows" options
1 parent 762c60a commit c6a7ce2

File tree

6 files changed

+77
-32
lines changed

6 files changed

+77
-32
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ textlint --rule @textlint-ja/no-synonyms README.md
3232

3333
```ts
3434
export interface Options {
35+
/**
36+
* 許可するワードの配列
37+
* ワードは完全一致で比較し、一致した場合は無視されます
38+
*/
39+
allows?: string[];
3540
/**
3641
* 同じ語形の語の中でのアルファベットの表記揺れを許可するかどうか
3742
* trueの場合はカタカナとアルファベットの表記ゆれを許可します
@@ -47,6 +52,7 @@ export interface Options {
4752
{
4853
"rules": {
4954
"@textlint-ja/no-synonyms": {
55+
"allows": ["ウェブアプリ", "ウェブアプリケーション"],
5056
"allowAlphabet": false
5157
}
5258
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"tabWidth": 4
5050
},
5151
"dependencies": {
52+
"textlint-rule-helper": "^2.1.1",
5253
"tiny-segmenter": "^0.2.0"
5354
},
5455
"devDependencies": {

src/create-index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@ export class ItemGroup {
1818

1919
}
2020

21-
usedItems(usedItemSet: Set<SudachiSynonyms>, { allowAlphabet }: { allowAlphabet: boolean }): SudachiSynonyms[] {
21+
usedItems(usedItemSet: Set<SudachiSynonyms>, { allowAlphabet, allows }: { allowAlphabet: boolean, allows: string[] }): SudachiSynonyms[] {
2222
// sort by used
2323
return Array.from(usedItemSet.values()).filter(item => {
2424
if (allowAlphabet && item.hyoukiYure === "アルファベット表記") {
2525
return false;
2626
}
27+
if (allows.includes(item.midashi)) {
28+
return false;
29+
}
2730
return this.items.includes(item);
2831
});
2932
}

src/textlint-rule-no-synonyms.ts

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import { TextlintRuleReporter } from "@textlint/types";
22
import { createIndex, ItemGroup, Midashi } from "./create-index";
33
import { SudachiSynonyms } from "sudachi-synonyms-dictionary";
4+
import { wrapReportHandler } from "textlint-rule-helper";
45

56
const TinySegmenter = require("tiny-segmenter");
67
const segmenter = new TinySegmenter(); // インスタンス生成
78

89
export interface Options {
10+
/**
11+
* 許可するワードの配列
12+
* ワードは完全一致で比較し、一致した場合は無視されます
13+
*/
14+
allows?: string[];
915
/**
1016
* 同じ語形の語の中でのアルファベットの表記揺れを許可するかどうか
1117
* trueの場合はカタカナとアルファベットの表記ゆれを許可します
@@ -16,12 +22,14 @@ export interface Options {
1622

1723

1824
export const DefaultOptions: Required<Options> = {
25+
allows: [],
1926
allowAlphabet: true
2027
};
2128

2229
const report: TextlintRuleReporter<Options> = (context, options = {}) => {
2330
const allowAlphabet = options.allowAlphabet !== undefined ? options.allowAlphabet : DefaultOptions.allowAlphabet;
24-
const { Syntax, getSource, report, RuleError } = context;
31+
const allows = options.allows !== undefined ? options.allows : DefaultOptions.allows;
32+
const { Syntax, getSource, RuleError } = context;
2533
const usedSudachiSynonyms: Set<SudachiSynonyms> = new Set();
2634
const locationMap: Map<SudachiSynonyms, { index: number }> = new Map();
2735
const usedItemGroup: Set<ItemGroup> = new Set();
@@ -44,36 +52,44 @@ const report: TextlintRuleReporter<Options> = (context, options = {}) => {
4452
});
4553
});
4654
};
47-
return {
48-
async [Syntax.Str](node) {
49-
const { keyItemGroupMap } = await indexPromise;
50-
const text = getSource(node);
51-
const segments: string[] = segmenter.segment(text);
52-
let absoluteIndex = node.range[0];
53-
segments.forEach((segement) => {
54-
matchSegment(segement, absoluteIndex, keyItemGroupMap);
55-
absoluteIndex += segement.length;
56-
});
55+
return wrapReportHandler(context,
56+
{
57+
ignoreNodeTypes: [Syntax.BlockQuote, Syntax.CodeBlock, Syntax.Code, Syntax.Html, Syntax.Link, Syntax.Image, Syntax.Comment]
5758
},
58-
async [Syntax.DocumentExit](node) {
59-
await indexPromise;
60-
for (const itemGroup of usedItemGroup.values()) {
61-
const items = itemGroup.usedItems(usedSudachiSynonyms, {
62-
allowAlphabet
63-
});
64-
if (items.length >= 2) {
65-
const 同義の見出しList = items.map(item => item.midashi);
66-
// select last used
67-
const matchSegment = locationMap.get(items[items.length - 1]);
68-
const index = matchSegment ? matchSegment.index : 0;
69-
const message = `同義語である「${同義の見出しList.join("」「")}」が利用されています`;
70-
report(node, new RuleError(message, {
71-
index
72-
}));
59+
(report) => {
60+
return {
61+
async [Syntax.Str](node) {
62+
const { keyItemGroupMap } = await indexPromise;
63+
const text = getSource(node);
64+
const segments: string[] = segmenter.segment(text);
65+
let absoluteIndex = node.range[0];
66+
segments.forEach((segement) => {
67+
matchSegment(segement, absoluteIndex, keyItemGroupMap);
68+
absoluteIndex += segement.length;
69+
});
70+
},
71+
async [Syntax.DocumentExit](node) {
72+
await indexPromise;
73+
for (const itemGroup of usedItemGroup.values()) {
74+
const items = itemGroup.usedItems(usedSudachiSynonyms, {
75+
allows,
76+
allowAlphabet
77+
});
78+
if (items.length >= 2) {
79+
const 同義の見出しList = items.map(item => item.midashi);
80+
// select last used
81+
const matchSegment = locationMap.get(items[items.length - 1]);
82+
const index = matchSegment ? matchSegment.index : 0;
83+
const message = `同義語である「${同義の見出しList.join("」「")}」が利用されています`;
84+
report(node, new RuleError(message, {
85+
index
86+
}));
87+
}
88+
}
7389
}
74-
}
90+
};
7591
}
76-
};
92+
);
7793
};
7894

7995
export default report;

test/textlint-rule-no-synonyms.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,16 @@ import rule from "../src/textlint-rule-no-synonyms";
77
tester.run("textlint-rule-no-synonyms", rule, {
88
valid: [
99
"新参入、借り入れ、問題のパスポート、マネー、雇入 片方のペアだけならOKです",
10-
"This is アーカイブ"
10+
"This is アーカイブ",
11+
// allow links
12+
`「[インターフェース](https://example.com)」と「[インタフェース](https://example.com)」`,
13+
// "allows
14+
{
15+
text: `ウェブアプリとウェブアプリケーションの違いは許容する`,
16+
options: {
17+
allows: ["ウェブアプリ"] // <= 片方が許可されていればOK
18+
}
19+
}
1120
],
1221
invalid: [{
1322
text: "この雇入と雇入れの違いは難しい問題だ",

yarn.lock

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,7 @@
752752
dependencies:
753753
any-observable "^0.3.0"
754754

755-
"@textlint/ast-node-types@^4.2.5":
755+
"@textlint/ast-node-types@^4.2.1", "@textlint/ast-node-types@^4.2.5":
756756
version "4.2.5"
757757
resolved "https://registry.yarnpkg.com/@textlint/ast-node-types/-/ast-node-types-4.2.5.tgz#ae13981bc8711c98313a6ac1c361194d6bf2d39b"
758758
integrity sha512-+rEx4jLOeZpUcdvll7jEg/7hNbwYvHWFy4IGW/tk2JdbyB3SJVyIP6arAwzTH/sp/pO9jftfyZnRj4//sLbLvQ==
@@ -864,7 +864,7 @@
864864
dependencies:
865865
"@textlint/text-to-ast" "^3.1.6"
866866

867-
"@textlint/types@^1.2.3":
867+
"@textlint/types@^1.1.2", "@textlint/types@^1.2.3":
868868
version "1.2.3"
869869
resolved "https://registry.yarnpkg.com/@textlint/types/-/types-1.2.3.tgz#6a27d6a2035873904d45c775b6205f162f169f91"
870870
integrity sha512-48wXioiKIKAWPkgTWf/KyvZpjGH5L2ohmrSVgtx/u8q+KcY+q+/dslKy7QN0H+Yeu5Vj4GZRHOzCYWtzHMQleQ==
@@ -4105,6 +4105,16 @@ text-table@^0.2.0:
41054105
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
41064106
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
41074107

4108+
textlint-rule-helper@^2.1.1:
4109+
version "2.1.1"
4110+
resolved "https://registry.yarnpkg.com/textlint-rule-helper/-/textlint-rule-helper-2.1.1.tgz#d572588685359134bc779939b217e61f087dab0f"
4111+
integrity sha512-6fxgHzoJVkjl3LaC1b2Egi+5wbhG4i0pU0knJmQujVhxIJ3D3AcQQZPs457xKAi5xKz1WayYeTeJ5jrD/hnO7g==
4112+
dependencies:
4113+
"@textlint/ast-node-types" "^4.2.1"
4114+
"@textlint/types" "^1.1.2"
4115+
structured-source "^3.0.2"
4116+
unist-util-visit "^1.1.0"
4117+
41084118
textlint-scripts@^3.0.0:
41094119
version "3.0.0"
41104120
resolved "https://registry.yarnpkg.com/textlint-scripts/-/textlint-scripts-3.0.0.tgz#4140a44c2cc77f9e22d99313ceb69661d5516d43"

0 commit comments

Comments
 (0)