Skip to content

Commit afb70c6

Browse files
committed
Merge pull request #29 from azu/fixer-support
Fixer support
2 parents cc2ca87 + ba7efd5 commit afb70c6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1078
-260
lines changed

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
# textlint-rule-preset-JTF-style [![Build Status](https://travis-ci.org/azu/textlint-rule-preset-JTF-style.svg?branch=master)](https://travis-ci.org/azu/textlint-rule-preset-JTF-style)
1+
# textlint-rule-preset-JTF-style [![textlint rule](https://img.shields.io/badge/textlint-fixable-green.svg?style=social)](https://textlint.github.io/) [![Build Status](https://travis-ci.org/azu/textlint-rule-preset-JTF-style.svg?branch=master)](https://travis-ci.org/azu/textlint-rule-preset-JTF-style)
22

33
[JTF日本語標準スタイルガイド(翻訳用)](https://www.jtf.jp/jp/style_guide/styleguide_top.html "JTF日本語標準スタイルガイド(翻訳用)") for [textlint](https://github.com/textlint/textlint "textlint").
44

5+
2016年2月22日改訂第2.2版を元にしています。
6+
57
## Installation
68

79
npm install textlint-rule-preset-jtf-style
@@ -52,6 +54,22 @@ npm run-script経由で実行すれば、`node_modules/.bin/`は省略出来ま
5254

5355
- [npm で依存もタスクも一元化する - Qiita](http://qiita.com/Jxck_/items/efaff21b977ddc782971#%E3%82%BF%E3%82%B9%E3%82%AF%E3%81%AE%E5%AE%9F%E8%A1%8C "npm で依存もタスクも一元化する - Qiita")
5456

57+
## 自動修正
58+
59+
使用するルールの設定が終わったら、`textlint`を実行してみてください。
60+
沢山のエラーが表示されると思います。
61+
62+
[![textlint rule](https://img.shields.io/badge/textlint-fixable-green.svg?style=social)](https://textlint.github.io/)
63+
64+
`textlint-rule-preset-JTF-style`の一部ルールは`textlint``--fix`にも対応しています。
65+
`--fix`を使うことで機械的に判断して修正出来る部分は自動修正します。
66+
67+
```sh
68+
textlint --fix /path/to/target.md
69+
```
70+
71+
実際にファイルを書き換えるので、必ずファイルをコピーしておくなどしてファイルを戻せるようにしてから実行してください。
72+
5573
### サンプル
5674

5775
[example/](example/) に実行できるサンプルプロジェクトがあります。

example/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
"scripts": {
77
"pretest": "npm rm textlint-rule-preset-jtf-style && npm i",
88
"textlint": "textlint",
9+
"textlint:fix": "textlint --fix ../README.md",
910
"test": "npm run textlint -- -f pretty-error ../README.md"
1011
},
1112
"author": "azu",
1213
"license": "MIT",
1314
"devDependencies": {
14-
"textlint": "^5.1.0",
15+
"textlint": "^5.5.3",
1516
"textlint-rule-preset-jtf-style": "file:.."
1617
}
1718
}

package.json

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"build": "babel src --out-dir lib --source-maps",
2727
"watch": "babel src --out-dir lib --watch --source-maps",
2828
"prepublish": "npm run --if-present build",
29+
"pretest": "node tool/create-fixtures.js",
2930
"test": "mocha && npm run test:textlint",
3031
"test:textlint": "(cd example && npm t)"
3132
},
@@ -37,16 +38,18 @@
3738
],
3839
"devDependencies": {
3940
"babel": "^5.8.23",
41+
"glob": "^7.0.0",
4042
"mocha": "^2.3.3",
41-
"textlint-tester": "^0.3.2"
43+
"textlint": "^5.5.2-0",
44+
"textlint-tester": "^0.5.1"
4245
},
4346
"dependencies": {
4447
"analyze-desumasu-dearu": "^2.1.2",
48+
"moji": "^0.5.1",
49+
"regexp.prototype.flags": "^1.1.1",
50+
"regx": "^1.0.4",
4551
"sorted-joyo-kanji": "^0.2.0",
4652
"textlint-rule-helper": "^1.1.3",
47-
"textlint-rule-prh": "^2.0.0"
48-
},
49-
"peerDependencies": {
50-
"textlint": ">= 5.1.0"
53+
"textlint-rule-prh": "^2.4.0"
5154
}
5255
}

src/1.1.2.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ Header
1717
は無視する
1818
*/
1919
import {isUserWrittenNode} from "./util/node-util";
20-
export default function (context) {
21-
let {Syntax, RuleError, report, getSource} = context;
20+
function mixer(context) {
21+
let {Syntax, RuleError, report, getSource, fixer} = context;
2222
return {
2323
[Syntax.Header](node){
2424
if (!isUserWrittenNode(node, context)) {
@@ -29,9 +29,17 @@ export default function (context) {
2929
let matchReg = /(\s*?)$/;
3030
let index = text.search(matchReg);
3131
if (index !== -1) {
32-
report(node, new RuleError("見出しの文末には、句点(。)を付けません。", index));
32+
report(node, {
33+
message: "見出しの文末には、句点(。)を付けません。",
34+
column: index,
35+
fix: fixer.removeRange([index, index + 1])
36+
});
3337
}
3438
// TODO: いずれの場合も、すべての見出しを通して複数の文体をできるだけ混在させないことが重要です。
3539
}
3640
}
41+
}
42+
export default {
43+
linter: mixer,
44+
fixer: mixer
3745
}

src/1.2.1.js

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,57 @@
11
// LICENSE : MIT
22
"use strict";
3+
import regx from 'regx';
4+
import {japaneseRegExp} from "./util/regexp";
5+
import {matchCaptureGroupAll} from "./util/match-index";
6+
import mergeMatches from "./util/merge-matches";
7+
const rx = regx("g");
38
/*
49
1.2.1. 句点(。)と読点(、)
510
句読点には全角の「、」と「。」を使います。和文の句読点としてピリオド(.)とカンマ(,)を使用しません。
611
「4.1.1 句点(。)」と「4.1.2 読点(、)」を参照してください。
712
*/
813
import {isUserWrittenNode} from "./util/node-util";
9-
export default function (context) {
10-
let {Syntax, RuleError, report, getSource} = context;
14+
15+
// [,.]{日本語}
16+
const leftTarget = rx`
17+
([,\.])
18+
${japaneseRegExp}
19+
`;
20+
// {日本語}[,.]
21+
const rightTarget = rx`
22+
${japaneseRegExp}
23+
([,\.])
24+
`;
25+
// . => 。 の置換マップ
26+
const replaceSymbol = {
27+
".": "。",
28+
",": "、"
29+
};
30+
31+
const reporter = (context) => {
32+
let {Syntax, report, fixer, getSource} = context;
1133
return {
1234
[Syntax.Str](node){
1335
if (!isUserWrittenNode(node, context)) {
1436
return;
1537
}
16-
let text = getSource(node);
17-
// 1.2.1. 句点(。)と読点(、)
18-
if (/[,\.]([\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF]|[\uD840-\uD87F][\uDC00-\uDFFF]|[--])/.test(text) || /([\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF]|[\uD840-\uD87F][\uDC00-\uDFFF]|[--])[,\.]/.test(text)) {
19-
report(node, new RuleError("句読点には全角の「、」と「。」を使います。和文の句読点としてピリオド(.)とカンマ(,)を使用しません。"));
20-
}
38+
const text = getSource(node);
39+
const leftMatches = matchCaptureGroupAll(text, leftTarget);
40+
const rightMatches = matchCaptureGroupAll(text, rightTarget);
41+
const matches = mergeMatches(leftMatches, rightMatches);
42+
matches.forEach(match => {
43+
const symbol = replaceSymbol[match.text];
44+
const indexOfSymbol = match.index;
45+
report(node, {
46+
message: "句読点には全角の「、」と「。」を使います。和文の句読点としてピリオド(.)とカンマ(,)を使用しません。",
47+
column: indexOfSymbol,
48+
fix: fixer.replaceTextRange([indexOfSymbol, indexOfSymbol + 1], symbol)
49+
});
50+
})
2151
}
2252
}
53+
};
54+
export default {
55+
linter: reporter,
56+
fixer: reporter
2357
}

src/1.2.2.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,17 @@
33
import {isUserWrittenNode} from "./util/node-util";
44
/*
55
1.2.2. ピリオド(.)とカンマ(,)
6-
欧文で表記する組織名などの固有名詞や数字にピリオド(.)やカンマ(,)が含まれる場合は、和文中でもピリオ ド(.)とカンマ(,)を使用します。いずれの場合も半角で表記します。「4.1.3 ピリオド(.)、カンマ(,)」を参照してく ださい。
6+
欧文で表記する組織名などの固有名詞や数字にピリオド(.)やカンマ(,)が含まれる場合は、和文中でもピリオド(.)とカンマ(,)を使用します。
7+
いずれの場合も半角で表記します。「4.1.3 ピリオド(.)、カンマ(,)」を参照してく ださい。
78
*/
8-
export default function (context) {
9-
let {Syntax, RuleError, report, getSource} = context;
9+
10+
// . => 。 の置換マップ
11+
const replaceSymbol = {
12+
".": ".",
13+
",": ","
14+
};
15+
function report(context) {
16+
let {Syntax, fixer, report, getSource} = context;
1017
return {
1118
[Syntax.Str](node){
1219
if (!isUserWrittenNode(node, context)) {
@@ -15,8 +22,18 @@ export default function (context) {
1522
let text = getSource(node);
1623
// 1.2.2. ピリオド(.)とカンマ(,)
1724
if (/[]/.test(text)) {
18-
report(node, new RuleError("全角のピリオドとカンマは使用しません。"));
25+
const index = text.search(/[]/);
26+
const symbol = replaceSymbol[text[index]];
27+
report(node, {
28+
message: "全角のピリオドとカンマは使用しません。",
29+
column: index,
30+
fix: fixer.replaceTextRange([index, index + 1], symbol)
31+
});
1932
}
2033
}
2134
}
35+
}
36+
export default {
37+
linter: report,
38+
fixer: report
2239
}

src/2.1.10.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
ただし桁区切りの「カンマ」は省略する場合があります。
77
*/
88
import {isUserWrittenNode} from "./util/node-util";
9-
export default function (context) {
10-
let {Syntax, RuleError, report, getSource} = context;
9+
import {matchCaptureGroupAll} from "./util/match-index";
10+
function reporter(context) {
11+
let {Syntax, RuleError, report, fixer, getSource} = context;
1112
return {
1213
[Syntax.Str](node){
1314
if (!isUserWrittenNode(node, context)) {
@@ -17,17 +18,24 @@ export default function (context) {
1718
// 数字,で絞って
1819
let numberWithComma = /([\d,]+)/g;
1920
// 0,xxx な文字列を検出する
20-
let strictMatchReg = /(^0+,\d+$)/;
21+
let strictMatchReg = /^0+(,)\d+$/;
2122
let match;
2223
while (match = numberWithComma.exec(text)) {
2324
// この段階では 10,000 も含まれている
2425
// ^0,xxx をだけを取り出す
2526
let matchedString = match[0];
26-
let index = matchedString.search(strictMatchReg);
27-
if (index !== -1) {
28-
report(node, new RuleError("小数点には「ピリオド」を使います。", index));
29-
}
27+
matchCaptureGroupAll(matchedString, strictMatchReg).forEach(subMatch => {
28+
const {index} = subMatch;
29+
report(node, new RuleError("小数点には「ピリオド」を使います。", {
30+
column: match.index + index,
31+
fix: fixer.replaceTextRange([match.index + index, match.index + index + 1], ".")
32+
}));
33+
});
3034
}
3135
}
3236
}
37+
}
38+
export default {
39+
linter: reporter,
40+
fixer: reporter
3341
}

src/2.1.2.js

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,24 @@
77
*/
88
import {isUserWrittenNode} from "./util/node-util";
99
import {isJoyo} from "sorted-joyo-kanji";
10-
10+
import {kanjiRegExp}from "./util/regexp";
1111
// http://qiita.com/YusukeHirao/items/2f0fb8d5bbb981101be0
12-
function stringToArray (value) {
12+
function stringToArray(value) {
1313
return value.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF]/g) || [];
1414
}
1515

16-
// http://tama-san.com/kanji-regex/
17-
const kanjiReg = /(?:[\u3400-\u9FFF\uF900-\uFAFF]|[\uD840-\uD87F][\uDC00-\uDFFF])/;
18-
1916
export default function (context) {
2017
let {Syntax, RuleError, report, getSource} = context;
2118
return {
2219
[Syntax.Str](node){
2320
if (!isUserWrittenNode(node, context)) {
2421
return;
2522
}
26-
let text = getSource(node);
27-
let strArray = stringToArray(text);
28-
for(let index = 0; index < strArray.length; index++) {
29-
let item = strArray[index];
30-
if(kanjiReg.test(item) && !isJoyo(item)) {
23+
const text = getSource(node);
24+
const strArray = stringToArray(text);
25+
for (let index = 0; index < strArray.length; index++) {
26+
const item = strArray[index];
27+
if (kanjiRegExp.test(item) && !isJoyo(item)) {
3128
report(node, new RuleError("「" + item + "」は「常用漢字表」外の漢字です。", index));
3229
}
3330
}

src/2.1.5.js

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,25 @@ Halfwidth Katakana variants(半角片仮名)
1010
http://www.asahi-net.or.jp/~ax2s-kmtn/ref/unicode/uff00.html
1111
*/
1212
import {isUserWrittenNode} from "./util/node-util";
13+
import {hanKarakanaRegExp} from "./util/regexp";
1314
import prh from "textlint-rule-prh";
1415
import path from "path";
15-
export default function (context) {
16-
let {Syntax, RuleError, report, getSource} = context;
16+
import {matchCaptureGroupAll} from "./util/match-index";
17+
import moji from "moji";
18+
/**
19+
* 半角カタカナを全角カタカナに変換
20+
*
21+
* @param {String} str 変換したい文字列
22+
*/
23+
function toZenkaku(string) {
24+
return moji(string).convert('HK', 'ZK').toString();
25+
}
26+
27+
28+
function reporter(context) {
29+
let {Syntax, fixer, report, getSource} = context;
1730
// 辞書ベースのカタカタ表記のチェックを行う
18-
let dictRule = prh(context, {
31+
let dictRule = prh.fixer(context, {
1932
rulePaths: [path.join(__dirname, "..", "dict", "2.1.5.yml")]
2033
});
2134
let originalStrRule = dictRule[Syntax.Str];
@@ -25,12 +38,21 @@ export default function (context) {
2538
if (!isUserWrittenNode(node, context)) {
2639
return;
2740
}
28-
let text = getSource(node);
29-
let matchReg = /[\uFF65-\uFF9F]/;
30-
let index = text.search(matchReg);
31-
if (index !== -1) {
32-
report(node, new RuleError("カタカナは「全角」で表記します。", index));
33-
}
41+
const text = getSource(node);
42+
const matches = matchCaptureGroupAll(text, /([\uFF65-\uFF9F]+)/g);
43+
matches.forEach(match => {
44+
const {index, text} = match;
45+
report(node, {
46+
message: "カタカナは「全角」で表記します。",
47+
column: index,
48+
fix: fixer.replaceTextRange([index, index + text.length], toZenkaku(text))
49+
});
50+
51+
});
3452
};
3553
return dictRule;
54+
}
55+
export default {
56+
linter: reporter,
57+
fixer: reporter
3658
}

src/2.1.6.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import prh from "textlint-rule-prh";
1111
import path from "path";
1212
export default function (context) {
1313
// 辞書ベースのカタカナ末尾の長音のチェックを行う
14-
return prh(context, {
14+
return prh.fixer(context, {
1515
rulePaths: [path.join(__dirname, "..", "dict", "2.1.6.yml")]
1616
});
1717
}

0 commit comments

Comments
 (0)