@vrcd-community/zhlint
Version:
A linting tool for Chinese language.
148 lines (147 loc) • 7.55 kB
JavaScript
/**
* @fileoverview
*
* This rule is checking spaces besides quotations.
*
* Options
* - noSpaceInsideQuotation: boolean | undefined
* - spaceOutsideHalfwidthQuotation: boolean | undefined
* - noSpaceOutsideFullwidthQuotation: boolean | undefined
*
* Details:
* - noSpaceInsideQuotation:
* - left-quotation x right-quotation
* - content/punctuation/right-quotation/right-bracket/code/unknown/container x right-quotation
* - left-quotation x content/punctuation/left-quotation/left-bracket/code/unknown/container
* - spaceOutsideHalfwidthQuotation:
* - right-half-quotation x left-half-quotation
* - content/code x left-half-quotation
* - right-half-quotation x content/code
* - noSpaceOutsideFullwidthQuotation:
* - right-full-quotation x left-full-quotation
* - content/code x left-full-quotation
* - right-full-quotation x content/code
*/
import { GroupTokenType, isLetterType, isFullwidthPair, MarkSideType, HyperTokenType } from '../parser/index.js';
import { checkInnerSpaceBefore, checkSpaceAfter, findWrappersBetween, findNonCodeVisibleTokenAfter, findNonCodeVisibleTokenBefore } from './util.js';
import { QUOTATION_NOSPACE_INSIDE, QUOTATION_NOSPACE_OUTSIDE, QUOTATION_SPACE_OUTSIDE } from './messages.js';
const isFullWidth = (char, adjusted) => {
return isFullwidthPair(char) && adjusted.indexOf(char) === -1;
};
const generateHandler = (options) => {
const noSpaceInsideQuotationOption = options.noSpaceInsideQuotation;
const spaceOutsideHalfQuotationOption = options.spaceOutsideHalfwidthQuotation;
const noSpaceOutsideFullQuotationOption = options.noSpaceOutsideFullwidthQuotation;
const adjustedFullWidthOption = options.adjustedFullwidthPunctuation || '';
return (token, _, group) => {
// skip non-group tokens
if (token.type !== GroupTokenType.GROUP) {
return;
}
// 1. no space inside quotation
if (noSpaceInsideQuotationOption) {
// 1.1 left-quotation x content/punctuation/left-quotation/left-bracket/code/unknown/container
const firstInsdieToken = token[0];
if (firstInsdieToken &&
firstInsdieToken.markSide !== MarkSideType.RIGHT) {
checkInnerSpaceBefore(token, '', QUOTATION_NOSPACE_INSIDE);
}
// 1.2 content/punctuation/right-quotation/right-bracket/code/unknown/container x right-quotation
const lastInsideToken = token[token.length - 1];
if (lastInsideToken && lastInsideToken.markSide !== MarkSideType.LEFT) {
checkSpaceAfter(lastInsideToken, '', QUOTATION_NOSPACE_INSIDE);
}
// 1.3 left-quotation x right-quotation
if (!firstInsdieToken) {
checkInnerSpaceBefore(token, '', QUOTATION_NOSPACE_INSIDE);
}
}
// 2. space outside half/full quotation
if (typeof spaceOutsideHalfQuotationOption !== 'undefined' ||
noSpaceOutsideFullQuotationOption) {
// 2.1 right-quotation x left-quotation
const contentTokenAfter = findNonCodeVisibleTokenAfter(group, token);
if (contentTokenAfter &&
contentTokenAfter.type === GroupTokenType.GROUP) {
const { spaceHost } = findWrappersBetween(group, token, contentTokenAfter);
if (spaceHost) {
const fullWidth = isFullWidth(token.modifiedEndValue, adjustedFullWidthOption) ||
isFullWidth(contentTokenAfter.modifiedStartValue, adjustedFullWidthOption);
// 2.1.1 right-full-quotation x left-full-quotation
// 2.1.2 right-half-quotation x left-half-quotation
if (fullWidth) {
if (noSpaceOutsideFullQuotationOption) {
checkSpaceAfter(spaceHost, '', QUOTATION_SPACE_OUTSIDE);
}
}
else {
if (typeof spaceOutsideHalfQuotationOption !== 'undefined') {
const spaceAfter = spaceOutsideHalfQuotationOption ? ' ' : '';
const message = spaceOutsideHalfQuotationOption
? QUOTATION_SPACE_OUTSIDE
: QUOTATION_NOSPACE_OUTSIDE;
checkSpaceAfter(spaceHost, spaceAfter, message);
}
}
}
}
// 2.2 content/code x left-quotation
const contentTokenBefore = findNonCodeVisibleTokenBefore(group, token);
if (contentTokenBefore &&
(isLetterType(contentTokenBefore.type) ||
contentTokenBefore.type === HyperTokenType.CODE_CONTENT)) {
const { spaceHost } = findWrappersBetween(group, contentTokenBefore, token);
if (spaceHost) {
const fullWidth = isFullWidth(token.modifiedStartValue, adjustedFullWidthOption);
// 2.2.1 content/code x left-full-quotation
// 2.2.2 content/code x left-half-quotation
if (fullWidth) {
if (noSpaceOutsideFullQuotationOption) {
checkSpaceAfter(spaceHost, '', QUOTATION_NOSPACE_OUTSIDE);
}
}
else {
if (typeof spaceOutsideHalfQuotationOption !== 'undefined') {
const spaceAfter = spaceOutsideHalfQuotationOption ? ' ' : '';
const message = spaceOutsideHalfQuotationOption
? QUOTATION_SPACE_OUTSIDE
: QUOTATION_NOSPACE_OUTSIDE;
checkSpaceAfter(spaceHost, spaceAfter, message);
}
}
}
}
// 2.3 right-quotation x content/code
if (contentTokenAfter &&
(isLetterType(contentTokenAfter.type) ||
contentTokenAfter.type === HyperTokenType.CODE_CONTENT)) {
const { spaceHost } = findWrappersBetween(group, token, contentTokenAfter);
if (spaceHost) {
const fullWidth = isFullWidth(token.modifiedEndValue, adjustedFullWidthOption);
// 2.3.1 right-full-quotation x content/code
// 2.3.2 right-half-quotation x content/code
if (fullWidth) {
if (noSpaceOutsideFullQuotationOption) {
checkSpaceAfter(spaceHost, '', QUOTATION_NOSPACE_OUTSIDE);
}
}
else {
if (typeof spaceOutsideHalfQuotationOption !== 'undefined') {
const spaceAfter = spaceOutsideHalfQuotationOption ? ' ' : '';
const message = spaceOutsideHalfQuotationOption
? QUOTATION_SPACE_OUTSIDE
: QUOTATION_NOSPACE_OUTSIDE;
checkSpaceAfter(spaceHost, spaceAfter, message);
}
}
}
}
}
};
};
export const defaultConfig = {
spaceOutsideHalfwidthQuotation: true,
noSpaceInsideQuotation: true,
noSpaceOutsideFullwidthQuotation: true
};
export default generateHandler;