11// LICENSE : MIT
22"use strict" ;
33import { RuleHelper } from "textlint-rule-helper" ;
4- import StructuredSource from "structured-source" ;
54const prh = require ( "prh" ) ;
65const path = require ( "path" ) ;
76const untildify = require ( 'untildify' ) ;
@@ -50,6 +49,47 @@ Please set .textlinrc:
5049` ) ;
5150 }
5251} ;
52+
53+ /**
54+ * for each diff of changeSet
55+ * @param {ChangeSet } changeSet
56+ * @param {string } str
57+ * @param {function({
58+ matchStartIndex: number,
59+ matchEndIndex: number,
60+ actual: string
61+ expected: string
62+ })}onChangeOfMatch
63+ */
64+ const forEachChange = ( changeSet , str , onChangeOfMatch ) => {
65+ const sortedDiffs = changeSet . diffs . sort ( function ( a , b ) {
66+ return a . index - b . index ;
67+ } ) ;
68+ let delta = 0 ;
69+ sortedDiffs . forEach ( function ( diff ) {
70+ const result = diff . expected . replace ( / \$ ( [ 0 - 9 ] { 1 , 2 } ) / g, function ( match , g1 ) {
71+ const index = parseInt ( g1 ) ;
72+ if ( index === 0 || ( diff . matches . length - 1 ) < index ) {
73+ return match ;
74+ }
75+ return diff . matches [ index ] || "" ;
76+ } ) ;
77+ // matchStartIndex/matchEndIndex value is original position, not replaced position
78+ // textlint use original position
79+ const matchStartIndex = diff . index ;
80+ const matchEndIndex = matchStartIndex + diff . matches [ 0 ] . length ;
81+ // actual => expected
82+ const actual = str . slice ( diff . index + delta , diff . index + delta + diff . matches [ 0 ] . length ) ;
83+ onChangeOfMatch ( {
84+ matchStartIndex,
85+ matchEndIndex,
86+ actual : actual ,
87+ expected : result
88+ } ) ;
89+ str = str . slice ( 0 , diff . index + delta ) + result + str . slice ( diff . index + delta + diff . matches [ 0 ] . length ) ;
90+ delta += result . length - diff . matches [ 0 ] . length ;
91+ } ) ;
92+ } ;
5393function reporter ( context , options = { } ) {
5494 assertOptions ( options ) ;
5595 const textlintRcFilePath = context . config ? context . config . configFile : null ;
@@ -69,33 +109,17 @@ function reporter(context, options = {}) {
69109 if ( helper . isChildNode ( node , [ Syntax . Link , Syntax . Image , Syntax . BlockQuote , Syntax . Emphasis ] ) ) {
70110 return ;
71111 }
72- let text = getSource ( node ) ;
112+ const text = getSource ( node ) ;
73113 // to get position from index
74- let src = new StructuredSource ( text ) ;
75- let makeChangeSet = prhEngine . makeChangeSet ( null , text ) ;
76- makeChangeSet . diffs . forEach ( function ( changeSet ) {
77- // | ----[match]------
78- var slicedText = text . slice ( changeSet . index ) ;
79- // | ----[match------|
80- var matchedText = slicedText . slice ( 0 , changeSet . matches [ 0 ] . length ) ;
81- var expected = matchedText . replace ( changeSet . pattern , changeSet . expected ) ;
82- // Avoid accidental match(ignore case)
83- if ( matchedText === expected ) {
114+ const makeChangeSet = prhEngine . makeChangeSet ( null , text ) ;
115+ forEachChange ( makeChangeSet , text , ( { matchStartIndex, matchEndIndex, actual, expected} ) => {
116+ // If result is not changed, should not report
117+ if ( actual === expected ) {
84118 return ;
85119 }
86- /*
87- line start with 1
88- column start with 0
89-
90- adjust position => line -1, column +0
91- */
92- var position = src . indexToPosition ( changeSet . index ) ;
93-
94- // line, column
95- report ( node , new RuleError ( matchedText + " => " + expected , {
96- line : position . line - 1 ,
97- column : position . column ,
98- fix : fixer . replaceTextRange ( [ changeSet . index , changeSet . index + matchedText . length ] , expected )
120+ report ( node , new RuleError ( actual + " => " + expected , {
121+ index : matchStartIndex ,
122+ fix : fixer . replaceTextRange ( [ matchStartIndex , matchEndIndex ] , expected )
99123 } ) ) ;
100124 } ) ;
101125 }
0 commit comments