Skip to content

Commit

Permalink
fix(dict2): 助詞が「が」の場合のみ自動修正する
Browse files Browse the repository at this point in the history
  • Loading branch information
azu committed Jan 5, 2019
1 parent 8ff730e commit 5fed776
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 38 deletions.
25 changes: 18 additions & 7 deletions src/dictionary.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
// MIT © 2016 azu
"use strict";
const punctuations = ["、", "、", ",", ","];

module.exports = [
/**
* _capture_to_expectedで返すことが特殊な値
* @type {{STOP_REPLACE: string}}
*/
const ExpectedType = {
// Expectedへの変換自体を取りやめる
"STOP_REPLACE": "STOP_REPLACE"
};
const Dictionary = [
{
// https://azu.github.io/morpheme-match/?text=省略(することが可能)。
id: "dict1",
Expand Down Expand Up @@ -80,13 +87,12 @@ module.exports = [
{
pos: "助詞",
_capture: "$3",
_capture_to_expected: function(actualToken) {
if (actualToken.surface_form === "も") {
return "";
} else if (actualToken.surface_form === "は") {
_capture_to_expected: (actualToken) => {
// 誤検知しにくい「が」のみ修正する
if (actualToken.surface_form === "が") {
return "";
}
return "";
return ExpectedType.STOP_REPLACE;
},
_readme: "[助詞]"
},
Expand Down Expand Up @@ -359,3 +365,8 @@ module.exports = [
]
}
];

export {
Dictionary,
ExpectedType
}
86 changes: 68 additions & 18 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// MIT © 2016 azu
"use strict";
import { matchPatterns } from "@textlint/regexp-string-matcher";
import { wrapReportHandler } from "textlint-rule-helper";
import StringSource from "textlint-util-to-string";
import { matchPatterns } from "@textlint/regexp-string-matcher";
import { Dictionary, ExpectedType } from "./dictionary.js"

const tokenize = require("kuromojin").tokenize;
const dictionaryList = require("./dictionary");

const createMatchAll = require("morpheme-match-all");

/**
Expand Down Expand Up @@ -49,22 +50,71 @@ const isTokensAllowed = (tokens, allows) => {
return allowsMatchResults.length > 0;
};

const createExpected = ({ text, matcherTokens, skipped, actualTokens }) => {
let resultText = text;
/**
* 置換できるかどうかを判定する
* @param matchResult
* @returns {boolean}
*/
const canReplaceToExpected = (matchResult) => {
const { dict, skipped } = matchResult;
const actualTokens = matchResult.tokens;
const matcherTokens = dict.tokens;
// expectedが定義されていない場合は置換できな
if (!dict.expected) {
return false;
}
let actualTokenIndex = 0;
matcherTokens.forEach((token, index) => {
for (let index = 0; index < matcherTokens.length; index++) {
const token = matcherTokens[index];
if (skipped[index]) {
continue;
}
if (token._capture) {
const to = replaceTokenWith(token, actualTokens[actualTokenIndex], "_capture_to_expected");
// _capture_to_expectedが"STOP_REPLACE"を返した場合は置換を取りやめる
if (to === ExpectedType.STOP_REPLACE) {
return false;
}
}
++actualTokenIndex;
}
return true;
};

/**
* マッチしたtokensを置換した結果の文字列を返す
* 置換できなかった場合はnullを返す
* @param {string} expected
* @param {*[]} matcherTokens
* @param {boolean[]} skipped
* @param {*[]} actualTokens
* @returns {null|string}
*/
const createExpected = ({ expected, matcherTokens, skipped, actualTokens }) => {
if (!expected) {
return null;
}
let resultText = expected;
let actualTokenIndex = 0;
for (let index = 0; index < matcherTokens.length; index++) {
const token = matcherTokens[index];
if (skipped[index]) {
resultText = replaceAll(resultText, token._capture, "");
return;
continue;
}
if (token._capture) {
const to = replaceTokenWith(token, actualTokens[actualTokenIndex], "_capture_to_expected");
// _capture_to_expectedが"STOP_REPLACE"を返した場合は置換を取りやめる
if (to === ExpectedType.STOP_REPLACE) {
return null;
}
resultText = replaceAll(resultText, token._capture, to);
}
++actualTokenIndex;
});
}
return resultText;
};

const createMessage = ({ id, text, matcherTokens, skipped, actualTokens }) => {
let resultText = text;
let actualTokenIndex = 0;
Expand Down Expand Up @@ -93,7 +143,7 @@ const reporter = (context, options = {}) => {
};
const dictOptions = options.dictOptions || DefaultOptions.dictOptions;
// "disabled": trueな辞書は取り除く
const enabledDictionaryList = dictionaryList.filter(dict => {
const enabledDictionaryList = Dictionary.filter(dict => {
const dictOption = dictOptions[dict.id] || {};
const disabled = typeof dictOption.disabled === "boolean" ? dictOption.disabled : dict.disabled;
return !disabled;
Expand Down Expand Up @@ -132,7 +182,7 @@ const reporter = (context, options = {}) => {
const lastWordIndex = source.originalIndexFromIndex(
Math.max(lastToken.word_position - 1, 0)
);
// replace $1
// エラーメッセージ
const message =
createMessage({
id: matchResult.dict.id,
Expand All @@ -141,15 +191,15 @@ const reporter = (context, options = {}) => {
skipped: matchResult.skipped,
actualTokens: matchResult.tokens
});
const expected = matchResult.dict.expected
? createExpected({
text: matchResult.dict.expected,
matcherTokens: matchResult.dict.tokens,
skipped: matchResult.skipped,
actualTokens: matchResult.tokens
})
: undefined;
if (expected) {
// 置換結果
const expected = createExpected({
expected: matchResult.dict.expected,
matcherTokens: matchResult.dict.tokens,
skipped: matchResult.skipped,
actualTokens: matchResult.tokens
});
const hasFixableResult = expected && tokensToString(matchResult.tokens) !== expected;
if (hasFixableResult) {
const wordLength = lastToken.surface_form.length;
report(
node,
Expand Down
12 changes: 5 additions & 7 deletions test/dictionary-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,22 @@
"use strict";
import assert from "assert";

import { Dictionary } from "../src/dictionary.js";
describe('dictionary', function() {
it("should not have duplicated id", () => {
const dictionary = require("../src/dictionary.js");
dictionary.forEach(item => {
Dictionary.forEach(item => {
assert.ok(typeof item.id === "string", "should have id property");
const sameIdItems = dictionary.filter(target => target.id === item.id);
const sameIdItems = Dictionary.filter(target => target.id === item.id);
assert.ok(sameIdItems.length === 1, "should not have duplicated id item");
});
});
it("should have disabled default value", () => {
const dictionary = require("../src/dictionary.js");
dictionary.forEach(item => {
Dictionary.forEach(item => {
assert.ok(typeof item.disabled === "boolean", `${item} should have disabled property`);
});
});
it("should have allows default value", () => {
const dictionary = require("../src/dictionary.js");
dictionary.forEach(item => {
Dictionary.forEach(item => {
assert.ok(Array.isArray(item.allows), `${item}: should have disabled property`);
});
});
Expand Down
18 changes: 12 additions & 6 deletions test/index-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ tester.run("textlint-rule-ja-no-redundant-expression", rule, {
}
]
},
// "が"は修正可能
{
text: "必要なら解析することができます。",
output: "必要なら解析できます。",
Expand All @@ -182,7 +183,6 @@ tester.run("textlint-rule-ja-no-redundant-expression", rule, {
},
{
text: "ES5では`class`は予約語であるため、構文エラーとなり実行することはできません。",
output: "ES5では`class`は予約語であるため、構文エラーとなり実行できません。",
errors: [
{
message: `【dict2】 "することはできませ"は冗長な表現です。"することは"を省き簡潔な表現にすると文章が明瞭になります。
Expand All @@ -193,7 +193,6 @@ tester.run("textlint-rule-ja-no-redundant-expression", rule, {
},
{
text: "たとえば`window.document`プロパティは、グローバル変数の`document`としてアクセスすることもできます。",
output: "たとえば`window.document`プロパティは、グローバル変数の`document`としてアクセスできます。",
errors: [
{
message: `【dict2】 "することもできます"は冗長な表現です。"することも"を省き簡潔な表現にすると文章が明瞭になります。
Expand All @@ -215,7 +214,6 @@ tester.run("textlint-rule-ja-no-redundant-expression", rule, {
},
{
text: "解析することもできますよ。",
output: "解析できますよ。",
errors: [
{
message: `【dict2】 "することもできます"は冗長な表現です。"することも"を省き簡潔な表現にすると文章が明瞭になります。
Expand All @@ -226,7 +224,6 @@ tester.run("textlint-rule-ja-no-redundant-expression", rule, {
},
{
text: "解析することはできますよ。",
output: "解析できますよ。",
errors: [
{
message: `【dict2】 "することはできます"は冗長な表現です。"することは"を省き簡潔な表現にすると文章が明瞭になります。
Expand All @@ -237,7 +234,6 @@ tester.run("textlint-rule-ja-no-redundant-expression", rule, {
},
{
text: "解析する事はできますよ。",
output: "解析できますよ。",
errors: [
{
message: `【dict2】 "する事はできます"は冗長な表現です。"する事は"を省き簡潔な表現にすると文章が明瞭になります。
Expand All @@ -249,7 +245,6 @@ tester.run("textlint-rule-ja-no-redundant-expression", rule, {

{
text: "解析することをできますよ。",
output: "解析できますよ。",
errors: [
{
message: `【dict2】 "することをできます"は冗長な表現です。"することを"を省き簡潔な表現にすると文章が明瞭になります。
Expand Down Expand Up @@ -309,6 +304,17 @@ tester.run("textlint-rule-ja-no-redundant-expression", rule, {
}
]
},
{
text: "この関数では、XHRを使ったデータの取得・HTML文字列の組み立て・組み立てたHTMLの表示を行っています。",
output: "この関数では、XHRを使ったデータの取得・HTML文字列の組み立て・組み立てたHTMLの表示を行っています。",
errors: [
{
message: `【dict5】 "表示を行う"は冗長な表現です。"表示する"など簡潔な表現にすると文章が明瞭になります。
解説: https://github.com/textlint-ja/textlint-rule-ja-no-redundant-expression#dict5`,
index: 44
}
]
},
{
text: "実験を行えば分かります。",
errors: [
Expand Down

0 comments on commit 5fed776

Please sign in to comment.