Skip to content
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
44 changes: 44 additions & 0 deletions src/Language/BlockString.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
use function mb_strlen;
use function mb_substr;
use function preg_split;
use function str_replace;
use function strpos;

class BlockString
{
Expand Down Expand Up @@ -103,4 +105,46 @@ public static function getIndentation(string $value): int

return $commonIndent ?? 0;
}

/**
* Print a block string in the indented block form by adding a leading and
* trailing blank line. However, if a block string starts with whitespace and is
* a single-line, adding a leading blank line would strip that whitespace.
*/
public static function print(
string $value,
string $indentation = '',
bool $preferMultipleLines = false
): string {
$valueLength = mb_strlen($value);
$isSingleLine = strpos($value, "\n") === false;
$hasLeadingSpace = $value[0] === ' ' || $value[0] === '\t';
$hasTrailingQuote = $value[$valueLength - 1] === '"';
$hasTrailingSlash = $value[$valueLength - 1] === '\\';
$printAsMultipleLines =
! $isSingleLine
|| $hasTrailingQuote
|| $hasTrailingSlash
|| $preferMultipleLines;

$result = '';
// Format a multi-line block quote to account for leading space.
if (
$printAsMultipleLines
&& ! ($isSingleLine && $hasLeadingSpace)
) {
$result .= "\n" . $indentation;
}

$result .= $indentation !== ''
? str_replace("\n", "\n" . $indentation, $value)
: $value;
if ($printAsMultipleLines) {
$result .= "\n";
}

return '"""'
. str_replace('"""', '\\"""', $result)
. '"""';
}
}
26 changes: 1 addition & 25 deletions src/Language/Printer.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
use function count;
use function implode;
use function json_encode;
use function preg_replace;
use function str_replace;
use function strlen;
use function strpos;
Expand Down Expand Up @@ -408,7 +407,7 @@ protected function p(?Node $node, bool $isDescription = false): string

case $node instanceof StringValueNode:
if ($node->block) {
return $this->printBlockString($node->value, $isDescription);
return BlockString::print($node->value, $isDescription ? '' : ' ');
}

return json_encode($node->value);
Expand Down Expand Up @@ -518,27 +517,4 @@ protected function join(array $parts, string $separator = ''): string
{
return implode($separator, array_filter($parts));
}

/**
* Print a block string in the indented block form by adding a leading and
* trailing blank line. However, if a block string starts with whitespace and is
* a single-line, adding a leading blank line would strip that whitespace.
*/
protected function printBlockString(string $value, bool $isDescription): string
{
$escaped = str_replace('"""', '\\"""', $value);

$startsWithWhitespace = $value[0] === ' ' || $value[0] === "\t";
$doesNotEndWithNewline = strpos($value, "\n") === false;

if ($startsWithWhitespace && $doesNotEndWithNewline) {
return '"""' . preg_replace('/"$/', "\"\n", $escaped) . '"""';
}

$content = $isDescription
? $escaped
: $this->indent($escaped);

return '"""' . "\n" . $content . "\n" . '"""';
}
}
48 changes: 7 additions & 41 deletions src/Utils/SchemaPrinter.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace GraphQL\Utils;

use GraphQL\Error\Error;
use GraphQL\Language\BlockString;
use GraphQL\Language\Printer;
use GraphQL\Type\Definition\Directive;
use GraphQL\Type\Definition\EnumType;
Expand Down Expand Up @@ -34,7 +35,6 @@
use function sprintf;
use function str_replace;
use function strlen;
use function substr;

/**
* Given an instance of Schema, prints it in schema definition language.
Expand Down Expand Up @@ -167,46 +167,17 @@ protected static function printDescription(array $options, $def, string $indenta
return '';
}

$lines = static::descriptionLines($def->description, 120 - strlen($indentation));
if (isset($options['commentDescriptions'])) {
return static::printDescriptionWithComments($lines, $indentation, $firstInBlock);
}

$description = $indentation !== '' && ! $firstInBlock
? "\n" . $indentation . '"""'
: $indentation . '"""';
$lines = static::descriptionLines($def->description, 120 - strlen($indentation));

// In some circumstances, a single line can be used for the description.
if (
count($lines) === 1 &&
mb_strlen($lines[0]) < 70 &&
substr($lines[0], -1) !== '"'
) {
return $description . static::escapeQuote($lines[0]) . "\"\"\"\n";
}

// Format a multi-line block quote to account for leading space.
$hasLeadingSpace = isset($lines[0]) &&
(
substr($lines[0], 0, 1) === ' ' ||
substr($lines[0], 0, 1) === '\t'
);
if (! $hasLeadingSpace) {
$description .= "\n";
}

$lineLength = count($lines);
for ($i = 0; $i < $lineLength; $i++) {
if ($i !== 0 || ! $hasLeadingSpace) {
$description .= $indentation;
}

$description .= static::escapeQuote($lines[$i]) . "\n";
return static::printDescriptionWithComments($lines, $indentation, $firstInBlock);
}

$description .= $indentation . "\"\"\"\n";
$preferMultipleLines = mb_strlen($def->description) > 70;
$blockString = BlockString::print($def->description, '', $preferMultipleLines);
$prefix = $indentation !== '' && ! $firstInBlock ? "\n" . $indentation : $indentation;

return $description;
return $prefix . str_replace("\n", "\n" . $indentation, $blockString) . "\n";
}

/**
Expand Down Expand Up @@ -264,11 +235,6 @@ protected static function printDescriptionWithComments(array $lines, string $ind
return $description;
}

protected static function escapeQuote(string $line): string
{
return str_replace('"""', '\\"""', $line);
}

/**
* @param array<string, bool> $options
* @param array<int, FieldArgument> $args
Expand Down
81 changes: 81 additions & 0 deletions tests/Language/BlockStringTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,85 @@ public function testDoNotTakeEmptyLinesIntoAccount(): void
self::assertEquals(1, BlockString::getIndentation("a\n\n b"));
self::assertEquals(2, BlockString::getIndentation("a\n \n b"));
}

// describe('printBlockString')

/**
* @see it('do not escape characters')
*/
public function testDoNotEscapeCharacters(): void
{
$str = "\" \\ / \u{8} \f \n \r \t"; // \u{8} === \b

self::assertEquals("\"\"\"\n" . $str . "\n\"\"\"", BlockString::print($str));
}

/**
* @see it('by default print block strings as single line')
*/
public function testByDefaultPrintBlockStringsAsSingleLine(): void
{
$str = 'one liner';

self::assertEquals('"""one liner"""', BlockString::print($str));
self::assertEquals("\"\"\"\none liner\n\"\"\"", BlockString::print($str, '', true));
}

/**
* @see it('correctly prints single-line with leading space')
*/
public function testCorrectlyPrintsSingleLineWithLeadingSpace(): void
{
$str = ' space-led string';

self::assertEquals('""" space-led string"""', BlockString::print($str));
self::assertEquals("\"\"\" space-led string\n\"\"\"", BlockString::print($str, '', true));
}

/**
* @see it('correctly prints single-line with leading space and quotation')
*/
public function testCorrectlyPrintsSingleLineWithLeadingSpaceAndQuotation(): void
{
$str = ' space-led value "quoted string"';

self::assertEquals("\"\"\" space-led value \"quoted string\"\n\"\"\"", BlockString::print($str));
self::assertEquals("\"\"\" space-led value \"quoted string\"\n\"\"\"", BlockString::print($str));
}

/**
* @see it('correctly prints single-line with trailing backslash')
*/
public function testCorrectlyPrintsSingleLineWithTrailingBackslash(): void
{
$str = 'backslash \\';

self::assertEquals("\"\"\"\nbackslash \\\n\"\"\"", BlockString::print($str));
self::assertEquals("\"\"\"\nbackslash \\\n\"\"\"", BlockString::print($str, '', true));
}

/**
* @see it('correctly prints string with a first line indentation')
*/
public function testCorrectlyPrintsStringWithAFirstLineIndentation(): void
{
$str = self::joinLines(
' first ',
' line ',
'indentation',
' string',
);

self::assertEquals(
self::joinLines(
'"""',
' first ',
' line ',
'indentation',
' string',
'"""',
),
BlockString::print($str)
);
}
}
80 changes: 0 additions & 80 deletions tests/Language/PrinterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,67 +119,6 @@ public function testPrintsFragmentWithVariableDirectives(): void
self::assertEquals($expected, Printer::doPrint($queryAstWithVariableDirective));
}

/**
* @see it('correctly prints single-line with leading space')
*/
public function testCorrectlyPrintsSingleLineBlockStringsWithLeadingSpace(): void
{
$mutationAstWithArtifacts = Parser::parse(
'{ field(arg: """ space-led value""") }'
);
$expected = '{
field(arg: """ space-led value""")
}
';
self::assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
}

/**
* @see it('correctly prints string with a first line indentation')
*/
public function testCorrectlyPrintsBlockStringsWithAFirstLineIndentation(): void
{
$mutationAstWithArtifacts = Parser::parse(
'{
field(arg: """
first
line
indentation
""")
}'
);
$expected = '{
field(arg: """
first
line
indentation
""")
}
';
self::assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
}

/**
* @see it('correctly prints single-line with leading space and quotation')
*/
public function testCorrectlyPrintsSingleLineWithLeadingSpaceAndQuotation(): void
{
$mutationAstWithArtifacts = Parser::parse('
{
field(arg: """ space-led value "quoted string"
""")
}
');
$expected = <<<END
{
field(arg: """ space-led value "quoted string"
""")
}

END;
self::assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
}

/**
* @see it('Experimental: correctly prints fragment defined variables')
*/
Expand All @@ -203,25 +142,6 @@ public function testExperimentalCorrectlyPrintsFragmentDefinedVariables(): void
);
}

/**
* @see it('correctly prints single-line with leading space and quotation')
*/
public function testCorrectlyPrintsSingleLineStringsWithLeadingSpaceAndQuotation(): void
{
$mutationAstWithArtifacts = Parser::parse(
'{
field(arg: """ space-led value "quoted string"
""")
}'
);
$expected = '{
field(arg: """ space-led value "quoted string"
""")
}
';
self::assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
}

/**
* @see it('prints kitchen sink')
*/
Expand Down
8 changes: 2 additions & 6 deletions tests/Language/SchemaPrinterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,9 @@ public function testPrintsKitchenSink(): void
"""
type Foo implements Bar & Baz & Two {
one: Type
"""
This is a description of the `two` field.
"""
"""This is a description of the `two` field."""
two(
"""
This is a description of the `argument` argument.
"""
"""This is a description of the `argument` argument."""
argument: InputType!
): Type
three(argument: InputType, other: String): Int
Expand Down
Loading