Skip to content

Commit 5db9a93

Browse files
committed
Figure out the issue with indentation of the “first line” in blockstrings
subtle: swift .split omits blank lines by default. But other issues combined with that.
1 parent 0f3614a commit 5db9a93

File tree

2 files changed

+59
-19
lines changed

2 files changed

+59
-19
lines changed

Sources/GraphQL/Language/Lexer.swift

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -586,14 +586,7 @@ func readRawString(source: Source, start: Int, line: Int, col: Int, prev: Token)
586586
body.charCode(at: body.utf8.index(after: positionIndex)) == 34 {
587587
blockString = true
588588
positionIndex = body.utf8.index(positionIndex, offsetBy: 2)
589-
590-
// if the first character after the """ is a newline, then it is not included in the value
591-
if let code = body.charCode(at: positionIndex),
592-
(code == 0x000A || code == 0x000D) {
593-
positionIndex = body.utf8.index(after: positionIndex)
594-
}
595-
596-
chunkStartIndex = positionIndex
589+
chunkStartIndex = positionIndex
597590
}
598591
}
599592

@@ -619,7 +612,7 @@ func readRawString(source: Source, start: Int, line: Int, col: Int, prev: Token)
619612
codeNext == 34,
620613
let codeNextNext = body.charCode(at: body.utf8.index(after: body.utf8.index(after: positionIndex))),
621614
codeNextNext == 34 {
622-
positionIndex = body.utf8.index(after: body.utf8.index(after: positionIndex)) // so we clean up quotes on exit
615+
positionIndex = body.utf8.index(after: body.utf8.index(after: positionIndex)) // position after quotes
623616
break
624617
}
625618

@@ -749,11 +742,12 @@ func readRawString(source: Source, start: Int, line: Int, col: Int, prev: Token)
749742
*/
750743

751744
func blockStringValue(rawValue: String) -> String {
752-
var commonIndent: Int = 0
753-
var lines = rawValue.utf8.split { (code) -> Bool in
745+
var lines = rawValue.utf8.split(omittingEmptySubsequences: false) { (code) -> Bool in
754746
return code == 0x000A || code == 0x000D
755747
}
756-
748+
749+
var commonIndent: Int = 0
750+
757751
for idx in lines.indices {
758752
let line = lines[idx]
759753
if idx == lines.startIndex { continue }
@@ -809,7 +803,7 @@ func blockStringValue(rawValue: String) -> String {
809803
result.append(contentsOf: Substring(lines[idx]))
810804
}
811805
}
812-
806+
813807
return String(result)
814808
}
815809

Tests/GraphQLTests/LanguageTests/LexerTests.swift

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ class LexerTests : XCTestCase {
763763
end: 66,
764764
line: 1,
765765
column: 1,
766-
value: " TopLevel {\n indented\n alsoIndented\n }\n",
766+
value: "\n TopLevel {\n indented\n alsoIndented\n }\n",
767767
prev: nil, next: nil)
768768

769769
let source = Source(body: sourceStr, name: "TestSource")
@@ -777,25 +777,43 @@ class LexerTests : XCTestCase {
777777
}
778778

779779
func testBlockStringIndentationAndBlankLine() throws {
780-
let rawString = "\n\n\n TopLevel {\n indented\n alsoIndented\n }\n\n\n\t\t\n" // from testReadRawString() above
780+
let rawString = "\n\n\n TopLevel {\n indented\n alsoIndented\n }\n\n\n\t\t\n"
781781
let cleanedString = blockStringValue(rawValue: rawString)
782782

783-
XCTAssertEqual(cleanedString, " TopLevel {\n indented\n alsoIndented\n}")
783+
XCTAssertEqual(cleanedString, "TopLevel {\n indented\n alsoIndented\n}")
784784
}
785785

786786
func testBlockStringDoubleIndentationAndBlankLine() throws {
787-
let rawString = "\n\n\n TopLevel {\n indented: {\n foo: String\n }\n alsoIndented\n }\n\n\n\t\t\n" // from testReadRawString() above
787+
let rawString = "\n\n\n TopLevel {\n indented: {\n foo: String\n }\n alsoIndented\n }\n\n\n\t\t\n"
788788
let cleanedString = blockStringValue(rawValue: rawString)
789789

790-
XCTAssertEqual(cleanedString, " TopLevel {\n indented: {\n foo: String\n }\n alsoIndented\n}")
790+
XCTAssertEqual(cleanedString, "TopLevel {\n indented: {\n foo: String\n }\n alsoIndented\n}")
791791
}
792792

793793
func testBlockStringIndentationAndBlankLineFirstLineNotIndentedWeird() throws {
794-
let rawString = "\n\n\nTopLevel {\n indented\n alsoIndented\n }\n\n\n\t\t\n" // from testReadRawString() above
794+
let rawString = "\n\n\nTopLevel {\n indented\n alsoIndented\n}\n\n\n\t\t\n"
795+
let cleanedString = blockStringValue(rawValue: rawString)
796+
797+
XCTAssertEqual(cleanedString, "TopLevel {\n indented\n alsoIndented\n}")
798+
}
799+
800+
func testBlockStringIndentationMultilineAndBlankLineFirstLineNotIndentedWeird() throws {
801+
let rawString = """
802+
803+
804+
TopLevel {
805+
indented
806+
alsoIndented
807+
}
808+
809+
810+
\t
811+
"""
795812
let cleanedString = blockStringValue(rawValue: rawString)
796813

797814
XCTAssertEqual(cleanedString, "TopLevel {\n indented\n alsoIndented\n}")
798815
}
816+
799817

800818
// Lexer tests for multi-line string token parsing
801819

@@ -869,6 +887,34 @@ class LexerTests : XCTestCase {
869887

870888
XCTAssertEqual(token, expected, "expected: \n \(dump(expected))\ngot: \n\(dump(token))\n")
871889
}
890+
891+
func testMultilineStrings_stringIndentedInStream() throws {
892+
let sourceStr =
893+
#"""
894+
"""
895+
Multi-line string {
896+
with Inner "foo"
897+
should be valid indented
898+
}
899+
"""
900+
"""#
901+
902+
let token = try lexOne(sourceStr)
903+
904+
let expected = Token(
905+
kind: .string,
906+
start: 4,
907+
end: 103,
908+
line: 1,
909+
column: 5,
910+
value: "Multi-line string {\n with Inner \"foo\"\n should be valid indented\n}"
911+
)
912+
913+
print(sourceStr)
914+
915+
XCTAssertEqual(token, expected, "expected: \n \(dump(expected))\ngot: \n\(dump(token))\n")
916+
}
917+
872918

873919
// Test empty strings & multi-line string lexer token parsing
874920

0 commit comments

Comments
 (0)