@@ -68,6 +68,22 @@ public struct DiagnosticsFormatter {
6868 var isFreeOfAnnotations : Bool {
6969 return diagnostics. isEmpty && suffixText. isEmpty
7070 }
71+
72+ /// Converts a UTF-8 column index into an index that considers each character as a single column, not each UTF-8
73+ /// byte.
74+ ///
75+ /// For example the 👨👩👧👦 character is considered as a single character, not 25 bytes.
76+ ///
77+ /// Both the input and the output column are 1-based.
78+ func characterColumn( ofUtf8Column utf8Column: Int ) -> Int {
79+ let index =
80+ sourceString. utf8. index (
81+ sourceString. utf8. startIndex,
82+ offsetBy: utf8Column - 1 ,
83+ limitedBy: sourceString. utf8. endIndex
84+ ) ?? sourceString. utf8. endIndex
85+ return sourceString. distance ( from: sourceString. startIndex, to: index) + 1
86+ }
7187 }
7288
7389 /// Number of lines which should be printed before and after the diagnostic message
@@ -139,7 +155,7 @@ public struct DiagnosticsFormatter {
139155
140156 let endColumn : Int
141157 if endLine > lineNumber {
142- endColumn = annotatedLine. sourceString. count
158+ endColumn = annotatedLine. sourceString. utf8 . count
143159 } else if endLine == lineNumber {
144160 endColumn = endLoc. column
145161 } else {
@@ -274,9 +290,13 @@ public struct DiagnosticsFormatter {
274290 annotatedSource. append ( " \n " )
275291 }
276292
277- let columnsWithDiagnostics = Set ( annotatedLine. diagnostics. map { $0. location ( converter: slc) . column } )
293+ let columnsWithDiagnostics = Set (
294+ annotatedLine. diagnostics. map {
295+ annotatedLine. characterColumn ( ofUtf8Column: $0. location ( converter: slc) . column)
296+ }
297+ )
278298 let diagsPerColumn = Dictionary ( grouping: annotatedLine. diagnostics) { diag in
279- diag. location ( converter: slc) . column
299+ annotatedLine . characterColumn ( ofUtf8Column : diag. location ( converter: slc) . column)
280300 } . sorted { lhs, rhs in
281301 lhs. key > rhs. key
282302 }
0 commit comments