@@ -2246,7 +2246,7 @@ type PhasedDiagnostic with
22462246
22472247 /// used by fsc.exe and fsi.exe, but not by VS
22482248 /// prints error and related errors to the specified StringBuilder
2249- member diagnostic.Output ( buf , tcConfig : TcConfig , severity ) =
2249+ member diagnostic.Output ( buf : StringBuilder , tcConfig : TcConfig , severity : FSharpDiagnosticSeverity ) =
22502250
22512251 // 'true' for "canSuggestNames" is passed last here because we want to report suggestions in fsc.exe and fsi.exe, just not in regular IDE usage.
22522252 let diagnostics = CollectFormattedDiagnostics( tcConfig, severity, diagnostic, true )
@@ -2260,66 +2260,75 @@ type PhasedDiagnostic with
22602260 | FSharpDiagnosticSeverity.Hidden -> " Info"
22612261
22622262 let codeText = sprintf " FS%04d " details.Canonical.ErrorNumber
2263- let lines = ResizeArray< string>()
2264-
2265- // For multi-line messages, the first line with number and severity, the rest are indented
2266- let messageLines = details.Message.Split([| '\n' |], StringSplitOptions.None)
2267- if messageLines.Length > 0 then
2268- lines.Add( sprintf " [%s ] %s : %s " codeText severityText messageLines[ 0 ])
2269- for i in 1 .. messageLines.Length - 1 do
2270- lines.Add( sprintf " │ %s " messageLines[ i])
2271- else
2272- lines.Add( sprintf " [%s ] %s " codeText details.Message)
2273-
2274- let addLocationAndSnippet ( l : FormattedDiagnosticLocation ) =
2275- let range = l.Range
2276- let fileDisplay = if String.IsNullOrWhiteSpace l.File then " unknown" else l.File
2277- lines.Add( sprintf " └─ [%s :(%d ,%d )]" fileDisplay range.StartLine range.StartColumn)
2278- lines.Add( " " )
2279-
2280- let tryRenderSnippet () =
2281- try
2282- let fullPath =
2283- l.File
2284- |> FileSystem.GetFullFilePathInDirectoryShim tcConfig.implicitIncludeDir
2285-
2286- if FileSystem.FileExistsShim fullPath then
2287- let content = File.ReadAllLines fullPath
2288-
2289- if content.Length > 0 then
2290- let startLine = max 1 ( range.StartLine - 1 )
2291- let endLine = min content.Length ( range.StartLine)
2292- let snippetLines =
2293- [ for ln in startLine .. min content.Length ( endLine + 1 ) do
2294- yield ln, content[ ln - 1 ] ]
2295-
2296- let lineNoWidth =
2297- snippetLines
2298- |> List.map fst
2299- |> List.map string
2300- |> List.map String.length
2301- |> List.max
2302-
2303- let caretStart = max 0 ( range.StartColumn - 1 )
2304- let caretWidth = max 1 ( max 0 ( range.EndColumn - range.StartColumn))
2305- let caretLine = String.make caretStart ' ' + String.make caretWidth '^'
2263+ let messageSentences =
2264+ details.Message.Split([| '\n' |], StringSplitOptions.None)
2265+ |> Seq.collect ( fun line ->
2266+ let parts = line.Split([| '.' |], StringSplitOptions.None)
23062267
2307- for ( ln, text) in snippetLines do
2308- lines.Add( sprintf " %*d | %s " lineNoWidth ln text)
2268+ parts
2269+ |> Seq.mapi ( fun idx part ->
2270+ let trimmed = part.Trim()
23092271
2310- lines.Add( sprintf " %s | %s " ( String.make lineNoWidth ' ' ) caretLine)
2311- with _ ->
2312- ()
2313-
2314- tryRenderSnippet ()
2315-
2316- match details.Location with
2317- | Some l when not l.IsEmpty -> addLocationAndSnippet l
2318- | _ -> ()
2319-
2320- for i = 0 to lines.Count - 1 do
2321- buf.AppendString lines[ i]
2322- if i < lines.Count - 1 then buf.AppendString " \n "
2272+ if trimmed = " " then
2273+ None
2274+ else
2275+ let hasDot = idx < parts.Length - 1 || line.EndsWith( " ." )
2276+ Some( if hasDot then trimmed + " ." else trimmed)))
2277+ |> Seq.choose id
2278+ |> Seq.toList
2279+
2280+ let messageLines =
2281+ match messageSentences with
2282+ | head :: tail ->
2283+ seq {
2284+ yield sprintf " [%s ] %s : %s " codeText severityText head
2285+ yield ! tail |> Seq.map ( fun s -> sprintf " │ %s " s)
2286+ }
2287+ | [] -> seq { yield sprintf " [%s ] %s " codeText details.Message }
2288+
2289+ let locationAndSnippet =
2290+ match details.Location with
2291+ | Some l when not l.IsEmpty ->
2292+ seq {
2293+ let range = l.Range
2294+ let fileDisplay = if String.IsNullOrWhiteSpace l.File then " unknown" else l.File
2295+ yield sprintf " └─ [%s :(%d ,%d )]" fileDisplay range.StartLine range.StartColumn
2296+ yield " "
2297+
2298+ try
2299+ let fullPath = l.File |> FileSystem.GetFullFilePathInDirectoryShim tcConfig.implicitIncludeDir
2300+
2301+ if FileSystem.FileExistsShim fullPath then
2302+ let content = File.ReadAllLines fullPath
2303+
2304+ if content.Length > 0 then
2305+ let startLine = max 1 ( range.StartLine - 1 )
2306+ let endLine = min content.Length range.StartLine
2307+ let snippetLines =
2308+ [ for ln in startLine .. min content.Length ( endLine + 1 ) -> ln, content[ ln - 1 ] ]
2309+
2310+ let lineNoWidth =
2311+ snippetLines
2312+ |> List.map fst
2313+ |> List.map string
2314+ |> List.map String.length
2315+ |> List.max
2316+
2317+ let caretStart = max 0 ( range.StartColumn - 1 )
2318+ let caretWidth = max 1 ( max 0 ( range.EndColumn - range.StartColumn))
2319+ let caretLine = String.make caretStart ' ' + String.make caretWidth '^'
2320+
2321+ for ( ln, text) in snippetLines do
2322+ yield sprintf " %*d | %s " lineNoWidth ln text
2323+ if ln = range.StartLine then
2324+ yield sprintf " %s | %s " ( String.make lineNoWidth ' ' ) caretLine
2325+ with _ -> ()
2326+ }
2327+ | _ -> Seq.empty
2328+
2329+ Seq.append messageLines locationAndSnippet
2330+ |> String.concat " \n "
2331+ |> fun rendered -> buf.Append( rendered) |> ignore
23232332
23242333 for e in diagnostics do
23252334 Printf.bprintf buf " \n "
0 commit comments