2
2
"use strict" ;
3
3
import { wrapReportHandler } from "textlint-rule-helper" ;
4
4
import StringSource from "textlint-util-to-string" ;
5
+ import { matchPatterns } from "@textlint/regexp-string-matcher" ;
5
6
6
7
const tokenize = require ( "kuromojin" ) . tokenize ;
7
8
const dictionaryList = require ( "./dictionary" ) ;
8
9
const createMatchAll = require ( "morpheme-match-all" ) ;
9
10
11
+ /**
12
+ * textの中身をすべて置換する
13
+ * @param {string } text
14
+ * @param {string|undefined } from
15
+ * @param {string } to
16
+ * @returns {string }
17
+ */
10
18
const replaceAll = ( text , from , to ) => {
11
19
return text . split ( from ) . join ( to ) ;
12
20
} ;
@@ -17,6 +25,30 @@ const replaceTokenWith = (matcherToken, actualToken, specialTo) => {
17
25
}
18
26
return actualToken . surface_form ;
19
27
} ;
28
+
29
+ /**
30
+ * tokensのsurface_formをつなげた文字列を返す
31
+ * @param tokens
32
+ * @returns {string }
33
+ */
34
+ const tokensToString = tokens => {
35
+ return tokens . map ( token => token . surface_form ) . join ( "" ) ;
36
+ } ;
37
+
38
+ /**
39
+ * "allows" オプションで許可されているかどうか
40
+ * @param {*[] } tokens
41
+ * @param {string[] } allows
42
+ */
43
+ const isTokensAllowed = ( tokens , allows ) => {
44
+ if ( allows . length === 0 ) {
45
+ return false ;
46
+ }
47
+ const matchedText = tokensToString ( tokens ) ;
48
+ const allowsMatchResults = matchPatterns ( matchedText , allows ) ;
49
+ return allowsMatchResults . length > 0 ;
50
+ } ;
51
+
20
52
const createExpected = ( { text, matcherTokens, skipped, actualTokens } ) => {
21
53
let resultText = text ;
22
54
let actualTokenIndex = 0 ;
@@ -33,7 +65,7 @@ const createExpected = ({ text, matcherTokens, skipped, actualTokens }) => {
33
65
} ) ;
34
66
return resultText ;
35
67
} ;
36
- const createMessage = ( { text, matcherTokens, skipped, actualTokens } ) => {
68
+ const createMessage = ( { id , text, matcherTokens, skipped, actualTokens } ) => {
37
69
let resultText = text ;
38
70
let actualTokenIndex = 0 ;
39
71
matcherTokens . forEach ( ( token , index ) => {
@@ -48,16 +80,25 @@ const createMessage = ({ text, matcherTokens, skipped, actualTokens }) => {
48
80
}
49
81
++ actualTokenIndex ;
50
82
} ) ;
51
- return resultText ;
83
+ return `【${ id } 】 ${ resultText }
84
+ 解説: https://github.com/textlint-ja/textlint-rule-ja-no-redundant-expression#${ id } ` ;
52
85
} ;
53
86
54
87
const reporter = ( context , options = { } ) => {
55
88
const { Syntax, RuleError, fixer } = context ;
56
89
const DefaultOptions = {
57
90
// https://textlint.github.io/docs/txtnode.html#type
58
- allowNodeTypes : [ Syntax . BlockQuote , Syntax . Link , Syntax . ReferenceDef ]
91
+ allowNodeTypes : [ Syntax . BlockQuote , Syntax . Link , Syntax . ReferenceDef ] ,
92
+ dictOptions : { }
59
93
} ;
60
- const matchAll = createMatchAll ( dictionaryList ) ;
94
+ const dictOptions = options . dictOptions || DefaultOptions . dictOptions ;
95
+ // "disabled": trueな辞書は取り除く
96
+ const enabledDictionaryList = dictionaryList . filter ( dict => {
97
+ const dictOption = dictOptions [ dict . id ] || { } ;
98
+ const disabled = typeof dictOption . disabled === "boolean" ? dictOption . disabled : dict . disabled ;
99
+ return ! disabled ;
100
+ } ) ;
101
+ const matchAll = createMatchAll ( enabledDictionaryList ) ;
61
102
const skipNodeTypes = options . allowNodeTypes || DefaultOptions . allowNodeTypes ;
62
103
return wrapReportHandler (
63
104
context ,
@@ -75,6 +116,14 @@ const reporter = (context, options = {}) => {
75
116
*/
76
117
const matchResults = matchAll ( currentTokens ) ;
77
118
matchResults . forEach ( matchResult => {
119
+ const dictOption = dictOptions [ matchResult . dict . id ] || { } ;
120
+ // "allows" オプションにマッチした場合はエラーを報告しない
121
+ const allows = dictOption . allows || matchResult . dict . allows ;
122
+ const isAllowed = isTokensAllowed ( matchResult . tokens , allows ) ;
123
+ if ( isAllowed ) {
124
+ return ;
125
+ }
126
+ // エラー報告
78
127
const firstToken = matchResult . tokens [ 0 ] ;
79
128
const lastToken = matchResult . tokens [ matchResult . tokens . length - 1 ] ;
80
129
const firstWordIndex = source . originalIndexFromIndex (
@@ -86,11 +135,12 @@ const reporter = (context, options = {}) => {
86
135
// replace $1
87
136
const message =
88
137
createMessage ( {
138
+ id : matchResult . dict . id ,
89
139
text : matchResult . dict . message ,
90
140
matcherTokens : matchResult . dict . tokens ,
91
141
skipped : matchResult . skipped ,
92
142
actualTokens : matchResult . tokens
93
- } ) + ( matchResult . dict . url ? `参考: ${ matchResult . dict . url } ` : "" ) ;
143
+ } ) ;
94
144
const expected = matchResult . dict . expected
95
145
? createExpected ( {
96
146
text : matchResult . dict . expected ,
0 commit comments