Skip to content
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

Add stripIgnoredCharacters utility function #1802

Merged
merged 3 commits into from Apr 3, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/index.js
Expand Up @@ -386,6 +386,9 @@ export {
concatAST,
// Separates an AST into an AST per Operation.
separateOperations,
// Strips characters that are not significant to the validity or execution
// of a GraphQL document.
stripIgnoredCharacters,
// Comparators for types
isEqualType,
isTypeSubTypeOf,
Expand Down
37 changes: 36 additions & 1 deletion src/language/__tests__/blockString-test.js
Expand Up @@ -9,7 +9,11 @@

import { expect } from 'chai';
import { describe, it } from 'mocha';
import { dedentBlockStringValue, printBlockString } from '../blockString';
import {
dedentBlockStringValue,
getBlockStringIndentation,
printBlockString,
} from '../blockString';

function joinLines(...args) {
return args.join('\n');
Expand Down Expand Up @@ -99,6 +103,37 @@ describe('dedentBlockStringValue', () => {
});
});

describe('getBlockStringIndentation', () => {
it('returns zero for an empty array', () => {
expect(getBlockStringIndentation([])).to.equal(0);
});

it('do not take first line into account', () => {
expect(getBlockStringIndentation([' a'])).to.equal(0);
expect(getBlockStringIndentation([' a', ' b'])).to.equal(2);
});

it('returns minimal indentation length', () => {
expect(getBlockStringIndentation(['', ' a', ' b'])).to.equal(1);
expect(getBlockStringIndentation(['', ' a', ' b'])).to.equal(1);
expect(getBlockStringIndentation(['', ' a', ' b', 'c'])).to.equal(0);
});

it('count both tab and space as single character', () => {
expect(getBlockStringIndentation(['', '\ta', ' b'])).to.equal(1);
expect(getBlockStringIndentation(['', '\t a', ' b'])).to.equal(2);
expect(getBlockStringIndentation(['', ' \t a', ' b'])).to.equal(3);
});

it('do not take empty lines into account', () => {
expect(getBlockStringIndentation(['a', '\t'])).to.equal(0);
expect(getBlockStringIndentation(['a', ' '])).to.equal(0);
expect(getBlockStringIndentation(['a', ' ', ' b'])).to.equal(2);
expect(getBlockStringIndentation(['a', ' ', ' b'])).to.equal(2);
expect(getBlockStringIndentation(['a', '', ' b'])).to.equal(1);
});
});

describe('printBlockString', () => {
it('by default print block strings as single line', () => {
const str = 'one liner';
Expand Down
30 changes: 29 additions & 1 deletion src/language/__tests__/lexer-test.js
Expand Up @@ -15,7 +15,7 @@ import dedent from '../../jsutils/dedent';
import inspect from '../../jsutils/inspect';
import { GraphQLError } from '../../error';
import { Source } from '../source';
import { createLexer, TokenKind } from '../lexer';
import { createLexer, TokenKind, isPunctuatorToken } from '../lexer';

function lexOne(str) {
const lexer = createLexer(new Source(str));
Expand Down Expand Up @@ -766,3 +766,31 @@ describe('Lexer', () => {
]);
});
});

describe('isPunctuatorToken', () => {
it('returns true for punctuator tokens', () => {
expect(isPunctuatorToken(lexOne('!'))).to.equal(true);
expect(isPunctuatorToken(lexOne('$'))).to.equal(true);
expect(isPunctuatorToken(lexOne('&'))).to.equal(true);
expect(isPunctuatorToken(lexOne('('))).to.equal(true);
expect(isPunctuatorToken(lexOne(')'))).to.equal(true);
expect(isPunctuatorToken(lexOne('...'))).to.equal(true);
expect(isPunctuatorToken(lexOne(':'))).to.equal(true);
expect(isPunctuatorToken(lexOne('='))).to.equal(true);
expect(isPunctuatorToken(lexOne('@'))).to.equal(true);
expect(isPunctuatorToken(lexOne('['))).to.equal(true);
expect(isPunctuatorToken(lexOne(']'))).to.equal(true);
expect(isPunctuatorToken(lexOne('{'))).to.equal(true);
expect(isPunctuatorToken(lexOne('|'))).to.equal(true);
expect(isPunctuatorToken(lexOne('}'))).to.equal(true);
});

it('returns false for non-punctuator tokens', () => {
expect(isPunctuatorToken(lexOne(''))).to.equal(false);
expect(isPunctuatorToken(lexOne('name'))).to.equal(false);
expect(isPunctuatorToken(lexOne('1'))).to.equal(false);
expect(isPunctuatorToken(lexOne('3.14'))).to.equal(false);
expect(isPunctuatorToken(lexOne('"str"'))).to.equal(false);
expect(isPunctuatorToken(lexOne('"""str"""'))).to.equal(false);
});
});
39 changes: 24 additions & 15 deletions src/language/blockString.js
Expand Up @@ -18,22 +18,9 @@ export function dedentBlockStringValue(rawString: string): string {
const lines = rawString.split(/\r\n|[\n\r]/g);

// Remove common indentation from all lines but first.
let commonIndent = null;
for (let i = 1; i < lines.length; i++) {
const line = lines[i];
const indent = leadingWhitespace(line);
if (
indent < line.length &&
(commonIndent === null || indent < commonIndent)
) {
commonIndent = indent;
if (commonIndent === 0) {
break;
}
}
}
const commonIndent = getBlockStringIndentation(lines);

if (commonIndent) {
if (commonIndent !== 0) {
for (let i = 1; i < lines.length; i++) {
lines[i] = lines[i].slice(commonIndent);
}
Expand All @@ -51,6 +38,28 @@ export function dedentBlockStringValue(rawString: string): string {
return lines.join('\n');
}

// @internal
export function getBlockStringIndentation(lines: Array<string>): number {
let commonIndent = null;

for (let i = 1; i < lines.length; i++) {
const line = lines[i];
const indent = leadingWhitespace(line);
if (indent === line.length) {
continue; // skip empty lines
}

if (commonIndent === null || indent < commonIndent) {
commonIndent = indent;
if (commonIndent === 0) {
break;
}
}
}

return commonIndent === null ? 0 : commonIndent;
}

function leadingWhitespace(str) {
let i = 0;
while (i < str.length && (str[i] === ' ' || str[i] === '\t')) {
Expand Down
21 changes: 21 additions & 0 deletions src/language/lexer.js
Expand Up @@ -129,6 +129,27 @@ export const TokenKind = Object.freeze({
*/
export type TokenKindEnum = $Values<typeof TokenKind>;

// @internal
export function isPunctuatorToken(token: Token) {
const kind = token.kind;
return (
kind === TokenKind.BANG ||
kind === TokenKind.DOLLAR ||
kind === TokenKind.AMP ||
kind === TokenKind.PAREN_L ||
kind === TokenKind.PAREN_R ||
kind === TokenKind.SPREAD ||
kind === TokenKind.COLON ||
kind === TokenKind.EQUALS ||
kind === TokenKind.AT ||
kind === TokenKind.BRACKET_L ||
kind === TokenKind.BRACKET_R ||
kind === TokenKind.BRACE_L ||
kind === TokenKind.PIPE ||
kind === TokenKind.BRACE_R
);
}

/**
* A helper function to describe a token as a string for debugging
*/
Expand Down