Skip to content

feat(options): add 読点 and 句点 as options #10

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 3 commits into from
Apr 8, 2021
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: 2 additions & 0 deletions .githook/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
npx --no-install lint-staged
38 changes: 30 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

一文に利用できる`、`の数を制限する[textlint](https://github.com/textlint/textlint "textlint")ルール

一文の読点の数が多いと冗長で読みにくい文章となるため、読点の数を一定数以下にするルールです。
読点の数を減らすためには、句点(。)で文を区切る必要があります。
一文の読点の数が多いと冗長で読みにくい文章となるため、読点の数を一定数以下にするルールです。 読点の数を減らすためには、句点(。)で文を区切る必要があります。

## Installation

Expand All @@ -17,26 +16,49 @@
$ textlint --rule max-ten README.md
# 11:0 error 一つの文で"、"を3つ以上使用しています max-ten

## Setting
## Options

- `max`: number
- デフォルト: 3
- 一文に許可される読点の数 + 1
- 一文に許可される読点の数 + 1となった場合にエラーとします。デフォルトでは4つの"、"が一文にあるとエラーとなります。

```json
```json5
{
"rules": {
"max-ten": {
"max" : 3
// 1文に利用できる最大の、の数
"max": 3,
// 例外ルールを適応するかどうか,
"strict": false,
// 読点として扱う文字
// https://ja.wikipedia.org/wiki/%E8%AA%AD%E7%82%B9
"touten": "、",
// 句点として扱う文字
// https://ja.wikipedia.org/wiki/%E5%8F%A5%E7%82%B9
"kuten": "。"
}
}
}
```

読点に「,」句点に「.」を使う場合は、次のように設定します。

```json5
{
"rules": {
"max-ten": {
// 読点として扱う文字
"touten": ",",
// 句点として扱う文字
"kuten": "."
}
}
}
```

## 例外

`<名詞>`、`<名詞>` のように名詞に挟まれた読点はカウントしません。
箇条書きとしての区切り文字として使われているため無視します。
`<名詞>`、`<名詞>` のように名詞に挟まれた読点はカウントしません。 箇条書きとしての区切り文字として使われているため無視します。

## Tests

Expand Down
17 changes: 16 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@
"build": "textlint-scripts build",
"watch": "textlint-scripts build --watch",
"prepublish": "npm run --if-present build",
"test": "textlint-scripts test"
"test": "textlint-scripts test",
"prettier": "prettier --write \"**/*.{js,jsx,ts,tsx,css}\"",
"prepare": "git config --local core.hooksPath .githook"
},
"devDependencies": {
"lint-staged": "^10.5.4",
"prettier": "^2.2.1",
"textlint-scripts": "^3.0.0"
},
"dependencies": {
Expand All @@ -41,5 +45,16 @@
"structured-source": "^3.0.2",
"textlint-rule-helper": "^2.0.0",
"textlint-util-to-string": "^3.1.1"
},
"prettier": {
"singleQuote": false,
"printWidth": 120,
"tabWidth": 4,
"trailingComma": "none"
},
"lint-staged": {
"*.{js,jsx,ts,tsx,css}": [
"prettier --write"
]
}
}
64 changes: 41 additions & 23 deletions src/max-ten.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
// LICENSE : MIT
"use strict";
import { RuleHelper } from "textlint-rule-helper"
import { RuleHelper } from "textlint-rule-helper";
import { getTokenizer } from "kuromojin";
import { splitAST, Syntax as SentenceSyntax } from "sentence-splitter";
import { StringSource } from "textlint-util-to-string";

const defaultOptions = {
max: 3, // 1文に利用できる最大の、の数
strict: false // 例外ルールを適応するかどうか
// 1文に利用できる最大の、の数
max: 3,
// 例外ルールを適応するかどうか,
strict: false,
// 読点として扱う文字
// https://ja.wikipedia.org/wiki/%E8%AA%AD%E7%82%B9
touten: "、",
// 句点として扱う文字
// https://ja.wikipedia.org/wiki/%E5%8F%A5%E7%82%B9
kuten: "。"
};

function isSandwichedMeishi({
before,
token,
after
}) {
function isSandwichedMeishi({ before, token, after }) {
if (before === undefined || after === undefined || token === undefined) {
return false;
}
Expand All @@ -23,20 +27,31 @@ function isSandwichedMeishi({

/**
* @param {RuleContext} context
* @param {object} [options]
* @param {typeof defaultOptions} [options]
*/
module.exports = function (context, options = {}) {
const maxLen = options.max || defaultOptions.max;
const isStrict = options.strict || defaultOptions.strict;
const maxLen = options.max ?? defaultOptions.max;
const isStrict = options.strict ?? defaultOptions.strict;
const touten = options.touten ?? defaultOptions.touten;
const kuten = options.kuten ?? defaultOptions.kuten;
const helper = new RuleHelper(context);
const { Syntax, RuleError, report, getSource } = context;
return {
[Syntax.Paragraph](node) {
if (helper.isChildNode(node, [Syntax.BlockQuote])) {
return;
}
const resultNode = splitAST(node);
const sentences = resultNode.children.filter(childNode => childNode.type === SentenceSyntax.Sentence);
const resultNode = splitAST(node, {
SeparatorParser: {
separatorCharacters: [
"?", // question mark
"!", // exclamation mark
"?", // (ja) zenkaku question mark
"!" // (ja) zenkaku exclamation mark
].concat(kuten)
}
});
const sentences = resultNode.children.filter((childNode) => childNode.type === SentenceSyntax.Sentence);
/*
<p>
<str><code><img><str>
Expand All @@ -49,18 +64,18 @@ module.exports = function (context, options = {}) {
2. sentence to tokens
3. check tokens
*/
return getTokenizer().then(tokenizer => {
sentences.forEach(sentence => {
return getTokenizer().then((tokenizer) => {
sentences.forEach((sentence) => {
const source = new StringSource(sentence);
const text = source.toString();
const tokens = tokenizer.tokenizeForSentence(text);
let currentTenCount = 0;
let lastToken = null;
tokens.forEach((token, index) => {
let surface = token.surface_form;
if (surface === "、") {
const surface = token.surface_form;
if (surface === touten) {
// 名詞に囲まわれている場合は例外とする
let isSandwiched = isSandwichedMeishi({
const isSandwiched = isSandwichedMeishi({
before: tokens[index - 1],
token: token,
after: tokens[index + 1]
Expand All @@ -80,15 +95,18 @@ module.exports = function (context, options = {}) {
if (currentTenCount >= maxLen) {
const positionInSentence = source.originalIndexFromIndex(lastToken.word_position - 1);
const index = sentence.range[0] + positionInSentence;
const ruleError = new context.RuleError(`一つの文で"、"を${maxLen}つ以上使用しています`, {
index
});
const ruleError = new context.RuleError(
`一つの文で"${touten}"を${maxLen}つ以上使用しています`,
{
index
}
);
report(node, ruleError);
currentTenCount = 0;
}
});
});
});
}
}
}
};
};
68 changes: 54 additions & 14 deletions test/max-ten-test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
const rule = require("../src/max-ten");
import TextLintTester from "textlint-tester";
import rule from "../src/max-ten";

function textIncludeTen(count) {
return (new Array(count + 1)).join("テスト文章において、") + "です";
return new Array(count + 1).join("テスト文章において、") + "です";
}
var TextLintTester = require("textlint-tester");
var tester = new TextLintTester();

const tester = new TextLintTester();
// ruleName, rule, expected[]
tester.run("max-ten", rule, {
// default max:3
Expand All @@ -17,19 +19,31 @@ tester.run("max-ten", rule, {
{
text: textIncludeTen(5 - 1),
options: {
"max": 5
max: 5
}
},
{
text: "これは、テストです。"
},
{
text: "これは、これは、これは、これは、オプションでカウントされないのでOK",
options: {
touten: ",",
kuten: "."
}
},
{
text: `これは,これは.これは,これは.`,
options: {
touten: ",",
kuten: "."
}
}

],
invalid: [
{
text: `これは、これは、これは
、d`
,
、d`,
errors: [
{
message: `一つの文で"、"を3つ以上使用しています`,
Expand All @@ -38,10 +52,36 @@ tester.run("max-ten", rule, {
}
]
},
{
text: `これは,これは,これは,これは。`,
errors: [
{
message: `一つの文で","を3つ以上使用しています`,
index: 11
}
],
options: {
touten: ",",
kuten: "."
}
},
{
text: `これは,これは,これは。これは,これは,これは,`,
errors: [
{
message: `一つの文で","を3つ以上使用しています`,
index: 23
}
],
options: {
touten: ",",
kuten: "."
}
},
{
text: textIncludeTen(5),
options: {
"max": 5
max: 5
},
errors: [
{
Expand All @@ -52,7 +92,7 @@ tester.run("max-ten", rule, {
{
text: `これは、長文の例ですが、columnがちゃんと計算、されてるはずです。`,
options: {
"max": 3
max: 3
},
errors: [
{
Expand All @@ -65,7 +105,7 @@ tester.run("max-ten", rule, {
{
text: "間に、Str以外の`code`Nodeが、あっても、OK",
options: {
"max": 3
max: 3
},
errors: [
{
Expand All @@ -78,7 +118,7 @@ tester.run("max-ten", rule, {
{
text: `複数のセンテンスがある場合。これでも、columnが、ちゃんと計算、されているはずです。`,
options: {
"max": 3
max: 3
},
errors: [
{
Expand All @@ -91,7 +131,7 @@ tester.run("max-ten", rule, {
{
text: `複数のセンテンスがあって、改行されている場合でも\n大丈夫です。これでも、lineとcolumnが、ちゃんと計算、されているはずです。`,
options: {
"max": 3
max: 3
},
errors: [
{
Expand All @@ -102,4 +142,4 @@ tester.run("max-ten", rule, {
]
}
]
});
});
Loading