Skip to content

feat(space-between-half-and-full-width): add an option to ignore numbers and apply only to alphabets #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,20 @@ textlint --rule ja-space-between-half-and-full-width README.md

## Options

- `space`: `"always"` || `"never"`
- `space`: `"always"` || `"never"` || Array
- デフォルト: `"never"`
- スペースを常に 入れる(`"always"`) or 入れない(`"never"`)
- Array 形式での指定も可能: `['alphabets', 'numbers', 'punctuation']`
- 対象としたい物のみ指定する
- 例えば、数値と句読点(、。)を例外として扱いたい場合は以下
- `['alphabets']`
- `exceptPunctuation`: `boolean`
- デフォルト: `true`
- 句読点(、。)を例外として扱うかどうか
- `lintStyledNode`: `boolean`
- デフォルト: `false`
- プレーンテキスト以外(リンクや画像のキャプションなど)を lint の対象とするかどうか (プレーンテキストの判断基準は [textlint/textlint-rule-helper: This is helper library for creating textlint rule](https://github.com/textlint/textlint-rule-helper#rulehelperisplainstrnodenode-boolean) を参照してください)

```json
{
"rules": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,46 @@ const assert = require("assert");
import {RuleHelper} from "textlint-rule-helper";
import {matchCaptureGroupAll} from "match-index";
const PunctuationRegExp = /[。、]/;
const ZenRegExpStr = '[、。]|[\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF]|[\uD840-\uD87F][\uDC00-\uDFFF]|[ぁ-んァ-ヶ]';
const defaultSpaceOptions = {
alphabets: false,
numbers: false,
punctuation: false
};
const defaultOptions = {
// スペースを入れるかどうか
// "never" or "always"
space: "never",
// [。、,.]を例外とするかどうか
exceptPunctuation: true,
// プレーンテキスト以外を対象とするか See https://github.com/textlint/textlint-rule-helper#rulehelperisplainstrnodenode-boolean
lintStyledNode: false,
};
function reporter(context, options = {}) {
/**
* 入力された `space` オプションを内部処理用に成形する
* @param {string|Array|undefined} opt `space` オプションのインプット
* @param {boolean|undefined} exceptPunctuation `exceptPunctuation` オプションのインプット
* @returns {Object}
*/
const parseSpaceOption = (opt, exceptPunctuation) => {
let option = {...defaultSpaceOptions};

if (typeof opt === 'string') {
assert(opt === "always" || opt === "never", `"space" options should be "always", "never" or an array.`);

if (opt === 'always') option = { ...option, alphabets: true, numbers: true };
if (exceptPunctuation === false) option = { ...option, punctuation: true };
} else if (Array.isArray(opt)) {
assert(opt.every((v) => Object.keys(option).includes(v)), `Only "alphabets", "numbers", "punctuation" can be included in the array.`);

opt.map(key => option[key] = true);
}

return option;
}

const {Syntax, RuleError, report, fixer, getSource} = context;
const helper = new RuleHelper();
const spaceOption = options.space || defaultOptions.space;
const exceptPunctuation = options.exceptPunctuation !== undefined
? options.exceptPunctuation
: defaultOptions.exceptPunctuation;
const spaceOption = parseSpaceOption(options.space, options.exceptPunctuation);
const lintStyledNode = options.lintStyledNode !== undefined
? options.lintStyledNode
: defaultOptions.lintStyledNode;
assert(spaceOption === "always" || spaceOption === "never", `"space" options should be "always" or "never".`);
/**
* `text`を対象に例外オプションを取り除くfilter関数を返す
* @param {string} text テスト対象のテキスト全体
Expand All @@ -35,7 +55,7 @@ function reporter(context, options = {}) {
*/
const createFilter = (text, padding) => {
/**
* `exceptPunctuation`で指定された例外を取り除く
* `PunctuationRegExp`で指定された例外を取り除く
* @param {Object} match
* @returns {boolean}
*/
Expand All @@ -44,16 +64,16 @@ function reporter(context, options = {}) {
if (!targetChar) {
return false;
}
if (exceptPunctuation && PunctuationRegExp.test(targetChar)) {
if (!spaceOption.punctuation && PunctuationRegExp.test(targetChar)) {
return false;
}
return true;
}
};
// Never: アルファベットと全角の間はスペースを入れない
const noSpaceBetween = (node, text) => {
const betweenHanAndZen = matchCaptureGroupAll(text, /[A-Za-z0-9]([  ])(?:[、。]|[\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF]|[\uD840-\uD87F][\uDC00-\uDFFF]|[ぁ-んァ-ヶ])/);
const betweenZenAndHan = matchCaptureGroupAll(text, /(?:[、。]|[\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF]|[\uD840-\uD87F][\uDC00-\uDFFF]|[ぁ-んァ-ヶ])([  ])[A-Za-z0-9]/);
const betweenHanAndZen = matchCaptureGroupAll(text, new RegExp(`[A-Za-z0-9]([  ])(?:${ZenRegExpStr})`));
const betweenZenAndHan = matchCaptureGroupAll(text, new RegExp(`(?:${ZenRegExpStr})([  ])[A-Za-z0-9]`));
const reportMatch = (match) => {
const {index} = match;
report(node, new RuleError("原則として、全角文字と半角文字の間にスペースを入れません。", {
Expand All @@ -66,12 +86,36 @@ function reporter(context, options = {}) {
};

// Always: アルファベットと全角の間はスペースを入れる
const needSpaceBetween = (node, text) => {
const betweenHanAndZen = matchCaptureGroupAll(text, /([A-Za-z0-9])(?:[、。]|[\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF]|[\uD840-\uD87F][\uDC00-\uDFFF]|[ぁ-んァ-ヶ])/);
const betweenZenAndHan = matchCaptureGroupAll(text, /([、。]|[\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF]|[\uD840-\uD87F][\uDC00-\uDFFF]|[ぁ-んァ-ヶ])[A-Za-z0-9]/);
const needSpaceBetween = (node, text, options) => {
/**
* オプションを元に正規表現オプジェクトを生成する
* @param {Array} opt `space` オプション
* @param {boolean} btwHanAndZen=true 半角全角の間か全角半角の間か
* @returns {Object}
*/
const generateRegExp = (opt, btwHanAndZen = true) => {
const alphabets = opt.alphabets ? 'A-Za-z' : '';
const numbers = opt.numbers ? '0-9' : '';

let expStr;
if (btwHanAndZen) {
expStr = `([${alphabets}${numbers}])(?:${ZenRegExpStr})`;
} else {
expStr = `(${ZenRegExpStr})[${alphabets}${numbers}]`;
}

return new RegExp(expStr);
};

const betweenHanAndZenRegExp = generateRegExp(options);
const betweenZenAndHanRegExp = generateRegExp(options, false);
const errorMsg = '原則として、全角文字と半角文字の間にスペースを入れます。';

const betweenHanAndZen = matchCaptureGroupAll(text, betweenHanAndZenRegExp);
const betweenZenAndHan = matchCaptureGroupAll(text, betweenZenAndHanRegExp);
const reportMatch = (match) => {
const {index} = match;
report(node, new RuleError("原則として、全角文字と半角文字の間にスペースを入れます。", {
report(node, new RuleError(errorMsg, {
index: match.index,
fix: fixer.replaceTextRange([index + 1, index + 1], " ")
}));
Expand All @@ -86,12 +130,12 @@ function reporter(context, options = {}) {
}
const text = getSource(node);

if (spaceOption === "always") {
needSpaceBetween(node, text)
} else if (spaceOption === "never") {
const noSpace = (key) => key === 'punctuation' ? true : !spaceOption[key];
if (Object.keys(spaceOption).every(noSpace)) {
noSpaceBetween(node, text);
} else {
needSpaceBetween(node, text, spaceOption);
}

}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ tester.run("全角文字と半角文字の間", rule, {
// デフォルト: never && exceptPunctuation
"JTF標準",
"これも、OK。",
"最新のversionは1.2.3です。",
{
text: "JTF標準",
options: {
Expand Down Expand Up @@ -37,13 +38,25 @@ Pull Request、コミットのやりかたなどが書かれています。`,
space: "always"
}
},
{
text: "最新の version は 1.2.3 です。",
options: {
space: ["alphabets", "numbers"]
}
},
// ignore
{
text: "# JTF 標準",
options: {
space: "never"
},
},
{
text: "# JTF 標準",
options: {
space: []
},
},
// except
{
text: "Always これは、Exception。",
Expand All @@ -52,6 +65,12 @@ Pull Request、コミットのやりかたなどが書かれています。`,
exceptPunctuation: true
},
},
{
text: "Always これは、Exception。",
options: {
space: ["alphabets", "numbers"]
},
},
// 入れても良い
{
text: "Always これは 、 Exception 。",
Expand All @@ -60,13 +79,25 @@ Pull Request、コミットのやりかたなどが書かれています。`,
exceptPunctuation: true
},
},
{
text: "Always これは 、 Exception 。",
options: {
space: ["alphabets", "numbers"]
},
},
{
text: "Never:これは、 Exception 。",
options: {
space: "never",
exceptPunctuation: true
}
},
{
text: "Never:これは、 Exception 。",
options: {
space: []
}
},
// 入れても良い
{
text: "Never:これは、Exception。",
Expand All @@ -75,6 +106,26 @@ Pull Request、コミットのやりかたなどが書かれています。`,
exceptPunctuation: true
}
},
{
text: "Never:これは、Exception。",
options: {
space: [],
}
},
// ignoreAlphabets
{
text: "最新のversionは 1.2.3 です。",
options: {
space: ["numbers", "punctuation"]
}
},
// ignoreNumbers
{
text: "最新の version は1.2.3です。",
options: {
space: ["alphabets", "punctuation"]
}
}
],
invalid: [
{
Expand All @@ -100,6 +151,19 @@ Pull Request、コミットのやりかたなどが書かれています。`,
}
]
},
{
text: "JTF 標準",
output: "JTF標準",
options: {
space: []
},
errors: [
{
message: "原則として、全角文字と半角文字の間にスペースを入れません。",
column: 4
}
]
},
{
text: "これは Unicode",
output: "これはUnicode",
Expand Down Expand Up @@ -155,6 +219,19 @@ Pull Request、コミットのやりかたなどが書かれています。`,
}
]
},
{
text: "JTF標準",
output: "JTF 標準",
options: {
space: ["alphabets", "numbers"]
},
errors: [
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 3
}
]
},
{
text: "aaa、bbb",
output: "aaa 、 bbb",
Expand Down Expand Up @@ -217,6 +294,31 @@ Pull Request、コミットのやりかたなどが書かれています。`,
}
]
},
{
text: "最新のversionは1.2.3です。",
output: "最新の version は 1.2.3 です。",
options: {
space: "always"
},
errors: [
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 3
},
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 10
},
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 11
},
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 16
}
]
},
// with option
{
text: "aaaとbbb、cccとddd",
Expand Down Expand Up @@ -244,5 +346,41 @@ Pull Request、コミットのやりかたなどが書かれています。`,
}
]
},
// ignoreAlphabets
{
text: "最新のversionは1.2.3です。",
output: "最新のversionは 1.2.3 です。",
options: {
space: ["numbers", "punctuation"]
},
errors: [
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 11
},
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 16
}
]
},
// ignoreNumbers
{
text: "最新のversionは1.2.3です。",
output: "最新の version は1.2.3です。",
options: {
space: ["alphabets", "punctuation"]
},
errors: [
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 3
},
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 10
}
]
}
]
});