-
Notifications
You must be signed in to change notification settings - Fork 834
Further optimize the rich style of diagnostic message #19141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
muqiuhan
wants to merge
6
commits into
dotnet:main
Choose a base branch
from
muqiuhan:feat/enhance-diagnostic
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
78c170e
Feat Further optimize the rich style of diagnostic message
muqiuhan 0b202e3
Merge branch 'main' into feat/enhance-diagnostic
muqiuhan 57c1bfd
Use seq instead of resize array
muqiuhan bd92cbf
Add Release notes
muqiuhan c6fe524
reformat
muqiuhan ae8de2a
Merge branch 'dotnet:main' into feat/enhance-diagnostic
muqiuhan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2246,11 +2246,103 @@ type PhasedDiagnostic with | |
|
|
||
| /// used by fsc.exe and fsi.exe, but not by VS | ||
| /// prints error and related errors to the specified StringBuilder | ||
| member diagnostic.Output(buf, tcConfig: TcConfig, severity) = | ||
| member diagnostic.Output(buf: StringBuilder, tcConfig: TcConfig, severity: FSharpDiagnosticSeverity) = | ||
|
|
||
| // '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. | ||
| let diagnostics = CollectFormattedDiagnostics(tcConfig, severity, diagnostic, true) | ||
|
|
||
| let renderRich (details: FormattedDiagnosticDetailedInfo) = | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this does not need any environmental information and it only takes the details object, you can also consider extracting this to a separate standalone function and test it in isolation. |
||
| let severityText = | ||
| match severity with | ||
| | FSharpDiagnosticSeverity.Error -> "Error" | ||
| | FSharpDiagnosticSeverity.Warning -> "Warning" | ||
| | FSharpDiagnosticSeverity.Info | ||
| | FSharpDiagnosticSeverity.Hidden -> "Info" | ||
|
|
||
| let codeText = sprintf "FS%04d" details.Canonical.ErrorNumber | ||
|
|
||
| let messageSentences = | ||
| details.Message.Split([| '\n' |], StringSplitOptions.None) | ||
| |> Seq.collect (fun line -> | ||
| let parts = line.Split([| '.' |], StringSplitOptions.None) | ||
|
|
||
| parts | ||
| |> Seq.mapi (fun idx part -> | ||
| let trimmed = part.Trim() | ||
|
|
||
| if trimmed = "" then | ||
| None | ||
| else | ||
| let hasDot = idx < parts.Length - 1 || line.EndsWith(".") | ||
| Some(if hasDot then trimmed + "." else trimmed))) | ||
| |> Seq.choose id | ||
| |> Seq.toList | ||
|
|
||
| let messageLines = | ||
| match messageSentences with | ||
| | head :: tail -> | ||
| seq { | ||
| yield sprintf "[%s] %s: %s" codeText severityText head | ||
| yield! tail |> Seq.map (fun s -> sprintf "│ %s" s) | ||
| } | ||
| | [] -> seq { yield sprintf "[%s] %s" codeText details.Message } | ||
|
|
||
| let locationAndSnippet = | ||
| match details.Location with | ||
| | Some l when not l.IsEmpty -> | ||
| seq { | ||
| let range = l.Range | ||
|
|
||
| let fileDisplay = | ||
| if String.IsNullOrWhiteSpace l.File then | ||
| "unknown" | ||
| else | ||
| l.File | ||
|
|
||
| yield sprintf "└─ [%s:(%d,%d)]" fileDisplay range.StartLine range.StartColumn | ||
| yield "" | ||
|
|
||
| try | ||
| let fullPath = | ||
| l.File |> FileSystem.GetFullFilePathInDirectoryShim tcConfig.implicitIncludeDir | ||
|
|
||
| if FileSystem.FileExistsShim fullPath then | ||
| let content = File.ReadAllLines fullPath | ||
|
|
||
| if content.Length > 0 then | ||
| let startLine = max 1 (range.StartLine - 1) | ||
| let endLine = min content.Length range.StartLine | ||
|
|
||
| let snippetLines = | ||
| [ | ||
| for ln in startLine .. min content.Length (endLine + 1) -> ln, content[ln - 1] | ||
| ] | ||
|
|
||
| let lineNoWidth = | ||
| snippetLines | ||
| |> List.map fst | ||
| |> List.map string | ||
| |> List.map String.length | ||
| |> List.max | ||
|
|
||
| let caretStart = max 0 (range.StartColumn - 1) | ||
| let caretWidth = max 1 (max 0 (range.EndColumn - range.StartColumn)) | ||
| let caretLine = String.make caretStart ' ' + String.make caretWidth '^' | ||
|
|
||
| for (ln, text) in snippetLines do | ||
| yield sprintf " %*d | %s" lineNoWidth ln text | ||
|
|
||
| if ln = range.StartLine then | ||
| yield sprintf " %s | %s" (String.make lineNoWidth ' ') caretLine | ||
| with _ -> | ||
| () | ||
| } | ||
| | _ -> Seq.empty | ||
|
|
||
| Seq.append messageLines locationAndSnippet | ||
| |> String.concat "\n" | ||
| |> fun rendered -> buf.Append(rendered) |> ignore | ||
|
|
||
| for e in diagnostics do | ||
| Printf.bprintf buf "\n" | ||
|
|
||
|
|
@@ -2273,17 +2365,7 @@ type PhasedDiagnostic with | |
|
|
||
| buf.AppendString details.Canonical.TextRepresentation | ||
| buf.AppendString details.Message | ||
| | DiagnosticStyle.Rich -> | ||
| buf.AppendString details.Canonical.TextRepresentation | ||
| buf.AppendString details.Message | ||
|
|
||
| match details.Location with | ||
| | Some l when not l.IsEmpty -> | ||
| buf.AppendString l.TextRepresentation | ||
|
|
||
| if details.Context.IsSome then | ||
| buf.AppendString details.Context.Value | ||
| | _ -> () | ||
| | DiagnosticStyle.Rich -> renderRich details | ||
|
|
||
| member diagnostic.OutputContext(buf, prefix, fileLineFunction) = | ||
| match diagnostic.Range with | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have grown to a non-trivial amount of logic now.
This should now have dedicated tests showing the various aspects of the feature in place.
(as an inspiration, look how existing "help" function exercise the compiler to print help and then assert on exact matching compiler output - that might work well. Other test suites typically do not focus on the formatting of diagnostics and instead use F# data to assert them, so e.g. ComponentTests style will not be much applicable for this feature)