UNPKG

@vrcd-community/zhlint

Version:

A linting tool for Chinese language.

190 lines (189 loc) 9.11 kB
/** * @fileoverview * * This rule is checking spaces besides brackets. * * Options * - noSpaceInsideBracket: boolean | undefined * - spaceOutsideHalfBracket: boolean | undefined * - nospaceOutsideFullBracket: boolean | undefined * * Details: * - noSpaceInsideBracket: * - left-bracket x anything * - non-left-bracket x right-bracket * - spaceOutsideHalfBracket: * - right-half-bracket x left-half-bracket * - right-half-bracket x content/left-quotation/code * - content/right-quotation/code x left-half-bracket * - noSpaceOutsideFullBracket: * - right-full-bracket x left-full-bracket * - right-full-bracket x content/left-quotation/code * - content/right-quotation/code x left-full-bracket */ import { CharType, GroupTokenType, isLetterType, isFullwidthPair, MarkSideType, HyperTokenType } from '../parser/index.js'; import { checkSpaceAfter, findVisibleTokenAfter, findVisibleTokenBefore, findWrappersBetween, findTokenAfter, findTokenBefore } from './util.js'; import { BRACKET_NOSPACE_INSIDE, BRACKET_NOSPACE_OUTSIDE, BRACKET_SPACE_OUTSIDE } from './messages.js'; const isFullWidth = (char, adjusted) => { return isFullwidthPair(char) && adjusted.indexOf(char) === -1; }; const shouldSkip = (before, beforeTokenSeq, token, afterTokenSeq, after) => { if (!before || !after) { return false; } if (isFullwidthPair(token.value) || isFullwidthPair(token.modifiedValue)) { return false; } if (beforeTokenSeq.filter((x) => x.spaceAfter).length || afterTokenSeq.filter((x) => x.spaceAfter).length) { return false; } return ( // x(x // ^ (before.type === CharType.WESTERN_LETTER || // x() // ^ (before.value === '(' && token.value === ')')) && // x)x // ^ (after.type === CharType.WESTERN_LETTER || // ()x // ^ (token.value === '(' && after.value === ')'))); }; const generateHandler = (options) => { const noInsideBracketOption = options.noSpaceInsideBracket; const spaceOutsideHalfBracketOption = options.spaceOutsideHalfwidthBracket; const noSpaceOutsideFullBracketOption = options.noSpaceOutsideFullwidthBracket; const adjustedFullWidthOption = options.adjustedFullwidthPunctuation || ''; return (token, _, group) => { // skip non-bracket tokens if (token.type !== HyperTokenType.BRACKET_MARK) { return; } // 1. no space inside bracket if (noInsideBracketOption) { if (token.markSide === MarkSideType.LEFT) { // no space after const tokenAfter = findTokenAfter(group, token); if (tokenAfter) { checkSpaceAfter(token, '', BRACKET_NOSPACE_INSIDE); } } else { // no space before const tokenBefore = findTokenBefore(group, token); if (tokenBefore && // dedupe tokenBefore.markSide !== MarkSideType.LEFT) { checkSpaceAfter(tokenBefore, '', BRACKET_NOSPACE_INSIDE); } } } // skip bracket between half-width content without spaces // or empty brackets beside half-width content without spaces const contentTokenBefore = findVisibleTokenBefore(group, token); const contentTokenAfter = findVisibleTokenAfter(group, token); const { spaceHost: beforeSpaceHost, tokens: beforeTokenSeq } = findWrappersBetween(group, contentTokenBefore, token); const { spaceHost: afterSpaceHost, tokens: afterTokenSeq } = findWrappersBetween(group, token, contentTokenAfter); if (shouldSkip(contentTokenBefore, beforeTokenSeq, token, afterTokenSeq, contentTokenAfter)) { return; } // 2. spaces outside half/full bracket if (typeof spaceOutsideHalfBracketOption !== 'undefined' || noSpaceOutsideFullBracketOption) { const fullWidth = isFullWidth(token.modifiedValue, adjustedFullWidthOption); // 2.1 right-bracket x left-bracket if (contentTokenAfter) { if (token.markSide === MarkSideType.RIGHT && contentTokenAfter.markSide === MarkSideType.LEFT) { if (afterSpaceHost) { const hasFullWidth = fullWidth || isFullWidth(contentTokenAfter.modifiedValue, adjustedFullWidthOption); // 2.1.1 any-full-bracket // 2.1.2 right-half-bracket x left-half-bracket if (hasFullWidth) { if (noSpaceOutsideFullBracketOption) { checkSpaceAfter(token, '', BRACKET_NOSPACE_OUTSIDE); } } else { // skip no spaces between if (afterTokenSeq.filter((x) => x.spaceAfter).length > 0) { if (typeof spaceOutsideHalfBracketOption !== 'undefined') { const spaceAfter = spaceOutsideHalfBracketOption ? ' ' : ''; const message = spaceOutsideHalfBracketOption ? BRACKET_SPACE_OUTSIDE : BRACKET_NOSPACE_OUTSIDE; checkSpaceAfter(token, spaceAfter, message); } } } } } } // 2.2 content/right-quotation/code x left-bracket // 2.3 right-racket x content/left-quotation/code if (token.markSide === MarkSideType.LEFT) { if (contentTokenBefore && (isLetterType(contentTokenBefore.type) || contentTokenBefore.type === GroupTokenType.GROUP || contentTokenBefore.type === HyperTokenType.CODE_CONTENT)) { if (beforeSpaceHost) { // 2.2.1 content/right-quotation/code x left-full-bracket // 2.2.2 content/right-quotation/code x left-half-bracket if (fullWidth || (contentTokenBefore.type === GroupTokenType.GROUP && isFullWidth(contentTokenBefore.modifiedEndValue, adjustedFullWidthOption))) { if (noSpaceOutsideFullBracketOption) { checkSpaceAfter(beforeSpaceHost, '', BRACKET_NOSPACE_OUTSIDE); } } else { if (typeof spaceOutsideHalfBracketOption !== 'undefined') { const spaceAfter = spaceOutsideHalfBracketOption ? ' ' : ''; const message = spaceOutsideHalfBracketOption ? BRACKET_SPACE_OUTSIDE : BRACKET_NOSPACE_OUTSIDE; checkSpaceAfter(beforeSpaceHost, spaceAfter, message); } } } } } else { if (contentTokenAfter && (isLetterType(contentTokenAfter.type) || contentTokenAfter.type === GroupTokenType.GROUP || contentTokenAfter.type === HyperTokenType.CODE_CONTENT)) { if (afterSpaceHost) { // 2.3.1 right-full-bracket x content/left-quotation/code // 2.4.2 right-half-bracket x content/left-quotation/code if (fullWidth || (contentTokenAfter.type === GroupTokenType.GROUP && isFullWidth(contentTokenAfter.modifiedStartValue, adjustedFullWidthOption))) { if (noSpaceOutsideFullBracketOption) { checkSpaceAfter(afterSpaceHost, '', BRACKET_NOSPACE_OUTSIDE); } } else { if (typeof spaceOutsideHalfBracketOption !== 'undefined') { const spaceAfter = spaceOutsideHalfBracketOption ? ' ' : ''; const message = spaceOutsideHalfBracketOption ? BRACKET_SPACE_OUTSIDE : BRACKET_NOSPACE_OUTSIDE; checkSpaceAfter(afterSpaceHost, spaceAfter, message); } } } } } } }; }; export const defaultConfig = { spaceOutsideHalfBracket: true, noSpaceInsideBracket: true }; export default generateHandler;