@@ -390,6 +390,108 @@ class DiagnosticConsoleWriterDefaultFormattingTest: XCTestCase {
390390 }
391391 }
392392
393+ func testClampsDiagnosticRangeToSourceRange( ) throws {
394+ let fs = try TestFileSystem ( folders: [
395+ Folder ( name: " Something.docc " , content: [
396+ TextFile ( name: " Article.md " , utf8Content: """
397+ # Title
398+
399+ A very short article with only an abstract.
400+ """ )
401+ ] )
402+ ] )
403+
404+ let summary = " Test diagnostic summary "
405+ let explanation = " Test diagnostic explanation. "
406+
407+ let bundle = try XCTUnwrap ( fs. bundles ( ) . first)
408+ let baseURL = bundle. baseURL
409+ let source = try XCTUnwrap ( bundle. markupURLs. first)
410+
411+ typealias Location = ( line: Int , column: Int )
412+ func logMessageFor( start: Location , end: Location ) throws -> String {
413+ let range = SourceLocation ( line: start. line, column: start. column, source: source) ..< SourceLocation ( line: end. line, column: end. column, source: source)
414+
415+ let logStorage = LogHandle . LogStorage ( )
416+ let consumer = DiagnosticConsoleWriter ( LogHandle . memory ( logStorage) , baseURL: baseURL, highlight: true , fileManager: fs)
417+
418+ let diagnostic = Diagnostic ( source: source, severity: . warning, range: range, identifier: " org.swift.docc.test-identifier " , summary: summary, explanation: explanation)
419+ consumer. receive ( [ Problem ( diagnostic: diagnostic, possibleSolutions: [ ] ) ] )
420+ try consumer. flush ( )
421+
422+ // There are no lines before line 1
423+ return logStorage. text
424+ }
425+
426+ // Highlight the "Title" word on line 1
427+ XCTAssertEqual ( try logMessageFor ( start: ( line: 1 , column: 3 ) , end: ( line: 1 , column: 8 ) ) , """
428+ \u{001B} [1;33mwarning: \( summary) \u{001B} [0;0m
429+ \( explanation)
430+ --> Something.docc/Article.md:1:3-1:8
431+ 1 + # \u{001B} [1;32mTitle \u{001B} [0;0m
432+ 2 |
433+ 3 | A very short article with only an abstract.
434+ """ )
435+
436+ // Highlight the "short" word on line 3
437+ XCTAssertEqual ( try logMessageFor ( start: ( line: 3 , column: 8 ) , end: ( line: 3 , column: 13 ) ) , """
438+ \u{001B} [1;33mwarning: \( summary) \u{001B} [0;0m
439+ \( explanation)
440+ --> Something.docc/Article.md:3:8-3:13
441+ 1 | # Title
442+ 2 |
443+ 3 + A very \u{001B} [1;32mshort \u{001B} [0;0m article with only an abstract.
444+ """ )
445+
446+ // Extend the highlight beyond the end of that line
447+ XCTAssertEqual ( try logMessageFor ( start: ( line: 3 , column: 8 ) , end: ( line: 3 , column: 100 ) ) , """
448+ \u{001B} [1;33mwarning: \( summary) \u{001B} [0;0m
449+ \( explanation)
450+ --> Something.docc/Article.md:3:8-3:100
451+ 1 | # Title
452+ 2 |
453+ 3 + A very \u{001B} [1;32mshort article with only an abstract. \u{001B} [0;0m
454+ """ )
455+
456+ // Extend the highlight beyond the start of that line
457+ XCTAssertEqual ( try logMessageFor ( start: ( line: 3 , column: - 4 ) , end: ( line: 3 , column: 13 ) ) , """
458+ \u{001B} [1;33mwarning: \( summary) \u{001B} [0;0m
459+ \( explanation)
460+ --> Something.docc/Article.md:3:1-3:13
461+ 1 | # Title
462+ 2 |
463+ 3 + \u{001B} [1;32mA very short \u{001B} [0;0m article with only an abstract.
464+ """ )
465+
466+ // Highlight a line before the start of the file
467+ XCTAssertEqual ( try logMessageFor ( start: ( line: - 4 , column: 1 ) , end: ( line: - 4 , column: 5 ) ) , """
468+ \u{001B} [1;33mwarning: \( summary) \u{001B} [0;0m
469+ \( explanation)
470+ --> Something.docc/Article.md:1:1-1:5
471+ """ )
472+
473+ // Highlight a line after the end of the file
474+ XCTAssertEqual ( try logMessageFor ( start: ( line: 100 , column: 1 ) , end: ( line: 100 , column: 5 ) ) , """
475+ \u{001B} [1;33mwarning: \( summary) \u{001B} [0;0m
476+ \( explanation)
477+ --> Something.docc/Article.md:100:1-100:5
478+ """ )
479+
480+ // Extended the highlighted lines before the start of the file
481+ XCTAssertEqual ( try logMessageFor ( start: ( line: - 4 , column: 1 ) , end: ( line: 1 , column: 5 ) ) , """
482+ \u{001B} [1;33mwarning: \( summary) \u{001B} [0;0m
483+ \( explanation)
484+ --> Something.docc/Article.md:1:1-1:5
485+ """ )
486+
487+ // Extended the highlighted lines after the end of the file
488+ XCTAssertEqual ( try logMessageFor ( start: ( line: 1 , column: 1 ) , end: ( line: 100 , column: 5 ) ) , """
489+ \u{001B} [1;33mwarning: \( summary) \u{001B} [0;0m
490+ \( explanation)
491+ --> Something.docc/Article.md:1:1-100:5
492+ """ )
493+ }
494+
393495 func testEmitAdditionReplacementSolution( ) throws {
394496 func problemsLoggerOutput( possibleSolutions: [ Solution ] ) -> String {
395497 let logger = Logger ( )
@@ -399,8 +501,8 @@ class DiagnosticConsoleWriterDefaultFormattingTest: XCTestCase {
399501 try ? consumer. flush ( )
400502 return logger. output
401503 }
402- let sourcelocation = SourceLocation ( line: 1 , column: 1 , source: nil )
403- let range = sourcelocation ..< sourcelocation
504+ let sourceLocation = SourceLocation ( line: 1 , column: 1 , source: nil )
505+ let range = sourceLocation ..< sourceLocation
404506 XCTAssertEqual (
405507 problemsLoggerOutput ( possibleSolutions: [
406508 Solution ( summary: " Create a sloth. " , replacements: [
0 commit comments