3
3
import { RuleHelper } from "textlint-rule-helper" ;
4
4
import { getTokenizer } from "kuromojin" ;
5
5
import splitSentences , { Syntax as SentenceSyntax } from "sentence-splitter" ;
6
+ import StringSource from "textlint-util-to-string" ;
6
7
/**
7
8
* create a object that
8
9
* map ={
@@ -34,31 +35,49 @@ function matchExceptionRule(tokens) {
34
35
}
35
36
return false ;
36
37
}
38
+ /*
39
+ default options
40
+ */
37
41
const defaultOptions = {
38
42
min_interval : 1 ,
39
43
strict : false
40
44
} ;
45
+
46
+
47
+ /*
48
+ 1. Paragraph Node -> text
49
+ 2. text -> sentences
50
+ 3. tokenize sentence
51
+ 4. report error if found word that match the rule.
52
+
53
+ TODO: need abstraction
54
+ */
41
55
export default function ( context , options = { } ) {
42
56
const helper = new RuleHelper ( context ) ;
43
57
// 最低間隔値
44
- let minInterval = options . min_interval || defaultOptions . min_interval ;
45
- let isStrict = options . strict || defaultOptions . strict ;
46
- let { Syntax, report, getSource, RuleError} = context ;
58
+ const minInterval = options . min_interval || defaultOptions . min_interval ;
59
+ const isStrict = options . strict || defaultOptions . strict ;
60
+ const { Syntax, report, getSource, RuleError} = context ;
47
61
return {
48
- [ Syntax . Str ] ( node ) {
62
+ [ Syntax . Paragraph ] ( node ) {
49
63
if ( helper . isChildNode ( node , [ Syntax . Link , Syntax . Image , Syntax . BlockQuote , Syntax . Emphasis ] ) ) {
50
64
return ;
51
65
}
52
- let text = getSource ( node ) ;
53
- let sentences = splitSentences ( text ) . filter ( node => {
66
+ const source = new StringSource ( node ) ;
67
+ const text = source . toString ( ) ;
68
+ const isSentenceNode = node => {
54
69
return node . type === SentenceSyntax . Sentence ;
55
- } ) ;
70
+ } ;
71
+ let sentences = splitSentences ( text , {
72
+ charRegExp : / [ 。 \? \! ? ! ] /
73
+ } ) . filter ( isSentenceNode ) ;
56
74
return getTokenizer ( ) . then ( tokenizer => {
57
75
const checkSentence = ( sentence ) => {
58
76
let tokens = tokenizer . tokenizeForSentence ( sentence . raw ) ;
59
- let joshiTokens = tokens . filter ( token => {
77
+ const isJoshiToken = token => {
60
78
return token . pos === "助詞" ;
61
- } ) ;
79
+ } ;
80
+ let joshiTokens = tokens . filter ( isJoshiToken ) ;
62
81
let joshiTokenSurfaceKeyMap = createSurfaceKeyMap ( joshiTokens ) ;
63
82
/*
64
83
# Data Structure
@@ -73,26 +92,33 @@ export default function (context, options = {}) {
73
92
let tokens = joshiTokenSurfaceKeyMap [ key ] ;
74
93
// strict mode ではない時例外を除去する
75
94
if ( ! isStrict ) {
76
- if ( matchExceptionRule ( tokens ) ) {
95
+ if ( matchExceptionRule ( tokens ) ) {
77
96
return ;
78
97
}
79
98
}
80
99
if ( tokens . length <= 1 ) {
81
100
return ; // no duplicated token
82
101
}
83
102
// if found differenceIndex less than
103
+ // tokes are sorted ascending order
84
104
tokens . reduce ( ( prev , current ) => {
85
105
let startPosition = joshiTokens . indexOf ( prev ) ;
86
106
let otherPosition = joshiTokens . indexOf ( current ) ;
87
107
// if difference
88
108
let differenceIndex = otherPosition - startPosition ;
89
109
if ( differenceIndex <= minInterval ) {
90
- report ( node , new RuleError ( `一文に二回以上利用されている助詞 "${ key } " がみつかりました。` , {
91
- line : sentence . loc . start . line - 1 ,
110
+ let originalPosition = source . originalPositionFor ( {
111
+ line : sentence . loc . start . line ,
112
+ column : sentence . loc . start . column + ( current . word_position - 1 )
113
+ } ) ;
114
+ // padding position
115
+ var padding = {
116
+ line : originalPosition . line - 1 ,
92
117
// matchLastToken.word_position start with 1
93
118
// this is padding column start with 0 (== -1)
94
- column : sentence . loc . start . column + ( current . word_position - 1 )
95
- } ) ) ;
119
+ column : originalPosition . column
120
+ } ;
121
+ report ( node , new RuleError ( `一文に二回以上利用されている助詞 "${ key } " がみつかりました。` , padding ) ) ;
96
122
}
97
123
return current ;
98
124
} ) ;
0 commit comments