Skip to content

feat(textlint-rule-spacing): add exceptPunctuation option #12

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 1 commit into from
Oct 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"babel-preset-es2015": "^6.9.0",
"babel-register": "^6.9.0",
"lerna": "2.0.0-beta.24",
"mocha": "^2.5.3"
"mocha": "^3.1.0"
},
"scripts": {
"bootstrap": "lerna bootstrap",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
OK: これはUnicode
NG: これは Unicode

全角文字には、句読点(、。)も含まれていますがデフォルトでは、`exceptPunctuation: true`であるため無視されます。

OK: これも、Unicode。

## Install

Install with [npm](https://www.npmjs.com/):
Expand Down Expand Up @@ -41,6 +45,9 @@ textlint --rule ja-space-between-half-and-full-width README.md
- `space`: `"always"` || `"never"`
- デフォルト: `"never"`
- スペースを常に 入れる(`"always"`) or 入れない(`"never"`)
- `exceptPunctuation`: `boolean`
- デフォルト: `true`
- 句読点(、。)を例外として扱うかどうか

```json
{
Expand All @@ -52,6 +59,25 @@ textlint --rule ja-space-between-half-and-full-width README.md
}
```

`exceptPunctuation: true`とした場合は、句読点に関しては無視されるようになります。

スペースは必須だが、`日本語、[alphabet]。`は許可する

text: "これは、Exception。",
options: {
space: "always",
exceptPunctuation: true
}

スペースは不要だが、`日本語、 [alphabet] 。`は許可する。

text: "これは、 Exception 。",
options: {
space: "never",
exceptPunctuation: true
}


## Changelog

See [Releases page](https://github.com/textlint-ja/textlint-rule-spacing/releases).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,80 @@ const assert = require("assert");
*/
import {RuleHelper} from "textlint-rule-helper";
import {matchCaptureGroupAll} from "match-index";
const PunctuationRegExp = /[。、]/;
const defaultOptions = {
// スペースを入れるかどうか
// "never" or "always"
"space": "never"
space: "never",
// [。、,.]を例外とするかどうか
exceptPunctuation: true
};
function reporter(context, options = {}) {
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;
assert(spaceOption === "always" || spaceOption === "never", `"space" options should be "always" or "never".`);
// アルファベットと全角の間はスペースを入れない
/**
* `text`を対象に例外オプションを取り除くfilter関数を返す
* @param {string} text テスト対象のテキスト全体
* @param {number} padding +1 or -1
* @returns {function(*, *)}
*/
const createFilter = (text, padding) => {
/**
* `exceptPunctuation`で指定された例外を取り除く
* @param {Object} match
* @returns {boolean}
*/
return (match) => {
const targetChar = text[match.index + padding];
if (!targetChar) {
return false;
}
if (exceptPunctuation && 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, /[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 reportMatch = (match) => {
const {index} = match;
report(node, new RuleError("原則として、全角文字と半角文字の間にスペースを入れません。", {
index: match.index,
fix: fixer.replaceTextRange([index, index + 1], "")
}));
};
betweenHanAndZen.forEach(reportMatch);
betweenZenAndHan.forEach(reportMatch);
betweenHanAndZen.filter(createFilter(text, 1)).forEach(reportMatch);
betweenZenAndHan.filter(createFilter(text, -1)).forEach(reportMatch);
};

// アルファベットと全角の間はスペースを入れる
// 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 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 reportMatch = (match) => {
const {index} = match;
report(node, new RuleError("原則として、全角文字と半角文字の間にスペースを入れます。", {
index: match.index,
fix: fixer.replaceTextRange([index + 1, index + 1], " ")
}));
};
betweenHanAndZen.forEach(reportMatch);
betweenZenAndHan.forEach(reportMatch);
betweenHanAndZen.filter(createFilter(text, 1)).forEach(reportMatch);
betweenZenAndHan.filter(createFilter(text, 0)).forEach(reportMatch);
};
return {
[Syntax.Str](node){
if (helper.isChildNode(node, [
Syntax.Header, Syntax.Link, Syntax.Image, Syntax.BlockQuote, Syntax.Emphasis
])) {
const isIgnoredParentNode = helper.isChildNode(node, [
Syntax.Header, Syntax.Link, Syntax.Image, Syntax.BlockQuote, Syntax.Emphasis
]);
if (isIgnoredParentNode) {
return;
}
const text = getSource(node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import rule from "../src/index";
var tester = new TextLintTester();
tester.run("全角文字と半角文字の間", rule, {
valid: [
// never
// デフォルト: never && exceptPunctuation
"JTF標準",
"これも、OK。",
{
text: "JTF標準",
options: {
Expand Down Expand Up @@ -43,6 +44,37 @@ Pull Request、コミットのやりかたなどが書かれています。`,
space: "never"
},
},
// except
{
text: "Always これは、Exception。",
options: {
space: "always",
exceptPunctuation: true
},
},
// 入れても良い
{
text: "Always これは 、 Exception 。",
options: {
space: "always",
exceptPunctuation: true
},
},
{
text: "Never:これは、 Exception 。",
options: {
space: "never",
exceptPunctuation: true
}
},
// 入れても良い
{
text: "Never:これは、Exception。",
options: {
space: "never",
exceptPunctuation: true
}
},
],
invalid: [
{
Expand Down Expand Up @@ -82,6 +114,33 @@ Pull Request、コミットのやりかたなどが書かれています。`,
{message: "原則として、全角文字と半角文字の間にスペースを入れません。"}
]
},
{

text: "aaa と bbb 、 ccc と ddd",
output: "aaaとbbb 、 cccとddd",
options: {
space: "never",
exceptPunctuation: true
},
errors: [
{
message: "原則として、全角文字と半角文字の間にスペースを入れません。",
column: 4
},
{
message: "原則として、全角文字と半角文字の間にスペースを入れません。",
column: 6
},
{
message: "原則として、全角文字と半角文字の間にスペースを入れません。",
column: 16
},
{
message: "原則として、全角文字と半角文字の間にスペースを入れません。",
column: 18
}
]
},
// need space
{
text: "JTF標準",
Expand All @@ -96,6 +155,24 @@ Pull Request、コミットのやりかたなどが書かれています。`,
}
]
},
{
text: "aaa、bbb",
output: "aaa 、 bbb",
options: {
space: "always",
exceptPunctuation: false
},
errors: [
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 3
},
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 4
}
]
},
{
text: "これはUnicode",
output: "これは Unicode",
Expand Down Expand Up @@ -126,5 +203,32 @@ Pull Request、コミットのやりかたなどが書かれています。`,
}
]
},
// with option
{
text: "aaaとbbb、cccとddd",
output: "aaa と bbb、ccc と ddd",
options: {
space: "always",
exceptPunctuation: true
},
errors: [
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 3
},
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 4
},
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 11
},
{
message: "原則として、全角文字と半角文字の間にスペースを入れます。",
column: 12
}
]
},
]
});