diff --git a/README.md b/README.md index b0b137e..3db9e12 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ textlint rule that check unmatched pairs like `(` and `]` - 隅付き括弧【】: `【` and `】` - double guillemet: `«` and `»` - single guillemet: `‹` and `›` +- ornate parenthesis `﴾` and `﴿` ## Examples @@ -53,6 +54,20 @@ Via `.textlintrc`(Recommended) } ``` +Some pair characters like the ornate parenthesis `﴾﴿` are strictly left or right oriented in every context. +To use such characters in rtl context, you need to reverse their usage. +You can use this rule by turning on the rtl option: + +```json +{ + "rules": { + "@textlint-rule/no-unmatched-pair": { + "rtl": true + } + } +} +``` + Via CLI ``` diff --git a/src/parser/PairMaker.js b/src/parser/PairMaker.js index 720d264..bc6ca4f 100644 --- a/src/parser/PairMaker.js +++ b/src/parser/PairMaker.js @@ -9,7 +9,7 @@ * https://ja.wikipedia.org/wiki/%E6%8B%AC%E5%BC%A7 */ /** - * @typedef {{key:string,start:string,end:string}[]} PairMark + * @typedef {{key:string,start:string,end:string,reverseInRtl:boolean}[]} PairMark */ const PAIR_MARKS = [ { @@ -76,25 +76,38 @@ const PAIR_MARKS = [ key: "single guillemet ‹›", start: "‹", end: "›" + }, + { + key: "ornate parenthesis ﴾﴿", + start: "﴾", + end: "﴿", + reverseInRtl: true } ]; // create entries // [start.key, mark] // [end.key, mark] -const PAIR_MARKS_ENTRIES = PAIR_MARKS.map((mark) => { - return [ - [mark.start, mark], - [mark.end, mark] - ]; -}).flat(1); +const PAIR_MARKS_ENTRIES = (rtl) => + PAIR_MARKS.map((mark) => { + const newMark = Object.assign({}, mark); + if (rtl && newMark.reverseInRtl) { + [newMark.start, newMark.end] = [newMark.end, newMark.start]; + } + + return [ + [newMark.start, newMark], + [newMark.end, newMark] + ]; + }).flat(1); /** * Optimized Map - * @type Map + * @type Map */ -const PAIR_MARKS_KEY_Map = new Map(PAIR_MARKS_ENTRIES); -const matchPair = (string) => { +const createPairMarksKeyMap = (rtl) => new Map(PAIR_MARKS_ENTRIES(rtl)); +const matchPair = (string, rtl) => { + const PAIR_MARKS_KEY_Map = createPairMarksKeyMap(rtl); return PAIR_MARKS_KEY_Map.get(string); }; // For readme @@ -102,15 +115,16 @@ const matchPair = (string) => { export class PairMaker { /** * @param {import("./SourceCode").SourceCode} sourceCode + * @param {boolean} rtl * @returns */ - mark(sourceCode) { + mark(sourceCode, rtl) { const string = sourceCode.read(); if (!string) { return; } - const matchedPair = matchPair(string); + const matchedPair = matchPair(string, rtl); if (!matchedPair) { return; } @@ -118,12 +132,12 @@ export class PairMaker { // {"{test}"} if (sourceCode.isInContext(matchedPair)) { // check that string is end mark? - const pair = PAIR_MARKS.find((pair) => pair.end === string); + const pair = PAIR_MARKS.find((pair) => (rtl && pair.reverseInRtl ? pair.start : pair.end === string)); if (pair) { sourceCode.leaveContext(pair); } } else { - const pair = PAIR_MARKS.find((pair) => pair.start === string); + const pair = PAIR_MARKS.find((pair) => (rtl && pair.reverseInRtl ? pair.end : pair.start === string)); if (pair) { sourceCode.enterContext(pair); } diff --git a/src/textlint-rule-no-unmatched-pair.js b/src/textlint-rule-no-unmatched-pair.js index 8930f56..0b66fa4 100644 --- a/src/textlint-rule-no-unmatched-pair.js +++ b/src/textlint-rule-no-unmatched-pair.js @@ -4,7 +4,7 @@ import { PairMaker } from "./parser/PairMaker.js"; import { SourceCode } from "./parser/SourceCode.js"; import { IgnoreNodeManager } from "textlint-rule-helper"; -const report = (context) => { +const report = (context, options) => { const { Syntax, report, RuleError } = context; const ignoreNodeManager = new IgnoreNodeManager(); return { @@ -30,7 +30,7 @@ const report = (context) => { const characterIndex = sentenceIndex + source.index; // console.log(characterIndex, source.text[source.index], ignoreNodeManager.isIgnoredIndex(characterIndex)); if (!ignoreNodeManager.isIgnoredIndex(characterIndex)) { - pairMaker.mark(source); + pairMaker.mark(source, options.rtl); } source.peek(); } diff --git a/test/textlint-rule-no-unmatched-pair-test.js b/test/textlint-rule-no-unmatched-pair-test.js index 7f6f045..dca2b40 100644 --- a/test/textlint-rule-no-unmatched-pair-test.js +++ b/test/textlint-rule-no-unmatched-pair-test.js @@ -39,7 +39,14 @@ tester.run("textlint-rule-no-unmatched-pair", rule, { `> [!NOTE] > some content`, `Paul a dit : « Je viendrai demain » .`, - `Elle a écrit: « L’article est intitulé ‹ La technologie aujourd’hui › » .` + `Elle a écrit: « L’article est intitulé ‹ La technologie aujourd’hui › » .`, + `a test for ﴾ornate﴿ parenthesis`, + { + text: `كانت آية ﴿اقرأ﴾ أول آية نزلت من القرآن`, + options: { + rtl: true + } + } ], invalid: [ { @@ -115,6 +122,27 @@ This pair mark is called double quote.` column: 41 } ] + }, + { + text: `a test for ﴾ornate parenthesis`, + errors: [ + { + line: 1, + column: 13 + } + ] + }, + { + text: `كانت آية ﴿اقرأ أول آية نزلت من القرآن`, + options: { + rtl: true + }, + errors: [ + { + line: 1, + column: 11 + } + ] } ] });