@@ -6,50 +6,80 @@ const assert = require("assert");
66 */
77import { RuleHelper } from "textlint-rule-helper" ;
88import { matchCaptureGroupAll } from "match-index" ;
9+ const PunctuationRegExp = / [ 。 、 ] / ;
910const defaultOptions = {
1011 // スペースを入れるかどうか
1112 // "never" or "always"
12- "space" : "never"
13+ space : "never" ,
14+ // [。、,.]を例外とするかどうか
15+ exceptPunctuation : true
1316} ;
1417function reporter ( context , options = { } ) {
1518 const { Syntax, RuleError, report, fixer, getSource} = context ;
1619 const helper = new RuleHelper ( ) ;
1720 const spaceOption = options . space || defaultOptions . space ;
21+ const exceptPunctuation = options . exceptPunctuation !== undefined
22+ ? options . exceptPunctuation
23+ : defaultOptions . exceptPunctuation ;
1824 assert ( spaceOption === "always" || spaceOption === "never" , `"space" options should be "always" or "never".` ) ;
19- // アルファベットと全角の間はスペースを入れない
25+ /**
26+ * `text`を対象に例外オプションを取り除くfilter関数を返す
27+ * @param {string } text テスト対象のテキスト全体
28+ * @param {number } padding +1 or -1
29+ * @returns {function(*, *) }
30+ */
31+ const createFilter = ( text , padding ) => {
32+ /**
33+ * `exceptPunctuation`で指定された例外を取り除く
34+ * @param {Object } match
35+ * @returns {boolean }
36+ */
37+ return ( match ) => {
38+ const targetChar = text [ match . index + padding ] ;
39+ if ( ! targetChar ) {
40+ return false ;
41+ }
42+ if ( exceptPunctuation && PunctuationRegExp . test ( targetChar ) ) {
43+ return false ;
44+ }
45+ return true ;
46+ }
47+ } ;
48+ // Never: アルファベットと全角の間はスペースを入れない
2049 const noSpaceBetween = ( node , text ) => {
21- const betweenHanAndZen = matchCaptureGroupAll ( text , / [ A - Z a - z 0 - 9 ] ( [ ] ) (?: [ \u3400 - \u4DBF \u4E00 - \u9FFF \uF900 - \uFAFF ] | [ \uD840 - \uD87F ] [ \uDC00 - \uDFFF ] | [ ぁ - ん ァ - ヶ ] ) / ) ;
22- const betweenZenAndHan = matchCaptureGroupAll ( text , / (?: [ \u3400 - \u4DBF \u4E00 - \u9FFF \uF900 - \uFAFF ] | [ \uD840 - \uD87F ] [ \uDC00 - \uDFFF ] | [ ぁ - ん ァ - ヶ ] ) ( [ ] ) [ A - Z a - z 0 - 9 ] / ) ;
50+ const betweenHanAndZen = matchCaptureGroupAll ( text , / [ A - Z a - z 0 - 9 ] ( [ ] ) (?: [ 、 。 ] | [ \u3400 - \u4DBF \u4E00 - \u9FFF \uF900 - \uFAFF ] | [ \uD840 - \uD87F ] [ \uDC00 - \uDFFF ] | [ ぁ - ん ァ - ヶ ] ) / ) ;
51+ const betweenZenAndHan = matchCaptureGroupAll ( text , / (?: [ 、 。 ] | [ \u3400 - \u4DBF \u4E00 - \u9FFF \uF900 - \uFAFF ] | [ \uD840 - \uD87F ] [ \uDC00 - \uDFFF ] | [ ぁ - ん ァ - ヶ ] ) ( [ ] ) [ A - Z a - z 0 - 9 ] / ) ;
2352 const reportMatch = ( match ) => {
2453 const { index} = match ;
2554 report ( node , new RuleError ( "原則として、全角文字と半角文字の間にスペースを入れません。" , {
2655 index : match . index ,
2756 fix : fixer . replaceTextRange ( [ index , index + 1 ] , "" )
2857 } ) ) ;
2958 } ;
30- betweenHanAndZen . forEach ( reportMatch ) ;
31- betweenZenAndHan . forEach ( reportMatch ) ;
59+ betweenHanAndZen . filter ( createFilter ( text , 1 ) ) . forEach ( reportMatch ) ;
60+ betweenZenAndHan . filter ( createFilter ( text , - 1 ) ) . forEach ( reportMatch ) ;
3261 } ;
3362
34- // アルファベットと全角の間はスペースを入れる
63+ // Always: アルファベットと全角の間はスペースを入れる
3564 const needSpaceBetween = ( node , text ) => {
36- const betweenHanAndZen = matchCaptureGroupAll ( text , / [ A - Z a - z 0 - 9 ] ( [ ^ ] ) (?: [ \u3400 - \u4DBF \u4E00 - \u9FFF \uF900 - \uFAFF ] | [ \uD840 - \uD87F ] [ \uDC00 - \uDFFF ] | [ ぁ - ん ァ - ヶ ] ) / ) ;
37- const betweenZenAndHan = matchCaptureGroupAll ( text , / (?: [ \u3400 - \u4DBF \u4E00 - \u9FFF \uF900 - \uFAFF ] | [ \uD840 - \uD87F ] [ \uDC00 - \uDFFF ] | [ ぁ - ん ァ - ヶ ] ) ( [ ^ ] ) [ A - Z a - z 0 - 9 ] / ) ;
65+ const betweenHanAndZen = matchCaptureGroupAll ( text , / ( [ A - Z a - z 0 - 9 ] ) (?: [ 、 。 ] | [ \u3400 - \u4DBF \u4E00 - \u9FFF \uF900 - \uFAFF ] | [ \uD840 - \uD87F ] [ \uDC00 - \uDFFF ] | [ ぁ - ん ァ - ヶ ] ) / ) ;
66+ const betweenZenAndHan = matchCaptureGroupAll ( text , / ( [ 、 。 ] | [ \u3400 - \u4DBF \u4E00 - \u9FFF \uF900 - \uFAFF ] | [ \uD840 - \uD87F ] [ \uDC00 - \uDFFF ] | [ ぁ - ん ァ - ヶ ] ) [ A - Z a - z 0 - 9 ] / ) ;
3867 const reportMatch = ( match ) => {
3968 const { index} = match ;
4069 report ( node , new RuleError ( "原則として、全角文字と半角文字の間にスペースを入れます。" , {
4170 index : match . index ,
4271 fix : fixer . replaceTextRange ( [ index + 1 , index + 1 ] , " " )
4372 } ) ) ;
4473 } ;
45- betweenHanAndZen . forEach ( reportMatch ) ;
46- betweenZenAndHan . forEach ( reportMatch ) ;
74+ betweenHanAndZen . filter ( createFilter ( text , 1 ) ) . forEach ( reportMatch ) ;
75+ betweenZenAndHan . filter ( createFilter ( text , 0 ) ) . forEach ( reportMatch ) ;
4776 } ;
4877 return {
4978 [ Syntax . Str ] ( node ) {
50- if ( helper . isChildNode ( node , [
51- Syntax . Header , Syntax . Link , Syntax . Image , Syntax . BlockQuote , Syntax . Emphasis
52- ] ) ) {
79+ const isIgnoredParentNode = helper . isChildNode ( node , [
80+ Syntax . Header , Syntax . Link , Syntax . Image , Syntax . BlockQuote , Syntax . Emphasis
81+ ] ) ;
82+ if ( isIgnoredParentNode ) {
5383 return ;
5484 }
5585 const text = getSource ( node ) ;
0 commit comments