diff --git a/CHANGELOG.md b/CHANGELOG.md index af5556b..9f10d3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Changed * [Add path to ASTCollecting](https://github.com/ionide/FSharp.Analyzers.SDK/pull/171) (thanks @nojaf!) * [Use Microsoft.Extensions.Logging instead of printf based logging infrastructure](https://github.com/ionide/FSharp.Analyzers.SDK/pull/175) (thanks @dawedawe!) +* [Console finer colorized pretty printing and link to analyzer URI](https://github.com/ionide/FSharp.Analyzers.SDK/pull/180) (thanks @smoothdeveloper!) ## [0.21.0] - 2023-11-22 diff --git a/src/FSharp.Analyzers.Cli/CustomLogging.fs b/src/FSharp.Analyzers.Cli/CustomLogging.fs index 60011d7..2be2d9e 100644 --- a/src/FSharp.Analyzers.Cli/CustomLogging.fs +++ b/src/FSharp.Analyzers.Cli/CustomLogging.fs @@ -8,20 +8,58 @@ open Microsoft.Extensions.Logging.Console open Microsoft.Extensions.Logging.Abstractions open Microsoft.Extensions.Options +module AnsiColorHelpers = + let private initialConsoleColor = Console.ForegroundColor + // see https://learn.microsoft.com/en-us/dotnet/core/extensions/console-log-formatter#implement-custom-color-formatting + let private ansiForegroundEscapeCodeOfColorConsole color = + match color with + | ConsoleColor.Black -> "\x1B[30m" + | ConsoleColor.DarkRed -> "\x1B[31m" + | ConsoleColor.DarkGreen -> "\x1B[32m" + | ConsoleColor.DarkYellow -> "\x1B[33m" + | ConsoleColor.DarkBlue -> "\x1B[34m" + | ConsoleColor.DarkMagenta -> "\x1B[35m" + | ConsoleColor.DarkCyan -> "\x1B[36m" + | ConsoleColor.Gray -> "\x1B[37m" + | ConsoleColor.Red -> "\x1B[1m\x1B[31m" + | ConsoleColor.Green -> "\x1B[1m\x1B[32m" + | ConsoleColor.Yellow -> "\x1B[1m\x1B[33m" + | ConsoleColor.Blue -> "\x1B[1m\x1B[34m" + | ConsoleColor.Magenta -> "\x1B[1m\x1B[35m" + | ConsoleColor.Cyan -> "\x1B[1m\x1B[36m" + | ConsoleColor.White -> "\x1B[1m\x1B[37m" + | _ -> +#if DEBUG + failwith $"didn't implement ansi code for color: {color}" +#else + // do not break code analyzis to wrong runtime color or such thing for release + "\x1B[37m" // ConsoleColor.Gray +#endif + + let consoleColorOfLogLevel logLevel = + match logLevel with + | LogLevel.Error -> ConsoleColor.Red + | LogLevel.Warning -> ConsoleColor.DarkYellow + | LogLevel.Information -> ConsoleColor.Blue + | LogLevel.Trace -> ConsoleColor.Cyan + | _ -> ConsoleColor.Gray + + let formatMessageAsAnsiColorizedString (color: ConsoleColor) (message: string) = + $"{ansiForegroundEscapeCodeOfColorConsole color}{message}{ansiForegroundEscapeCodeOfColorConsole initialConsoleColor}" + type CustomOptions() = inherit ConsoleFormatterOptions() /// if true: no LogLevel as prefix, colored output according to LogLevel /// if false: LogLevel as prefix, no colored output member val UseAnalyzersMsgStyle = false with get, set + member x.UseLogLevelAsPrefix = not x.UseAnalyzersMsgStyle type CustomFormatter(options: IOptionsMonitor) as this = inherit ConsoleFormatter("customName") let mutable optionsReloadToken: IDisposable = null let mutable formatterOptions = options.CurrentValue - let origColor = Console.ForegroundColor - do optionsReloadToken <- options.OnChange(fun x -> this.ReloadLoggerOptions(x)) member private _.ReloadLoggerOptions(opts: CustomOptions) = formatterOptions <- opts @@ -35,13 +73,10 @@ type CustomFormatter(options: IOptionsMonitor) as this = = let message = logEntry.Formatter.Invoke(logEntry.State, logEntry.Exception) - if formatterOptions.UseAnalyzersMsgStyle then - this.SetColor(textWriter, logEntry.LogLevel) - textWriter.WriteLine(message) - this.ResetColor() - else + if formatterOptions.UseLogLevelAsPrefix then this.WritePrefix(textWriter, logEntry.LogLevel) - textWriter.WriteLine(message) + + textWriter.WriteLine message member private _.WritePrefix(textWriter: TextWriter, logLevel: LogLevel) = match logLevel with @@ -53,20 +88,6 @@ type CustomFormatter(options: IOptionsMonitor) as this = | LogLevel.Critical -> textWriter.Write("critical: ") | _ -> () - // see https://learn.microsoft.com/en-us/dotnet/core/extensions/console-log-formatter - member private _.SetColor(textWriter: TextWriter, logLevel: LogLevel) = - let color = - match logLevel with - | LogLevel.Error -> "\x1B[1m\x1B[31m" // ConsoleColor.Red - | LogLevel.Warning -> "\x1B[33m" // ConsoleColor.DarkYellow - | LogLevel.Information -> "\x1B[1m\x1B[34m" // ConsoleColor.Blue - | LogLevel.Trace -> "\x1B[1m\x1B[36m" // ConsoleColor.Cyan - | _ -> "\x1B[37m" // ConsoleColor.Gray - - textWriter.Write(color) - - member private _.ResetColor() = Console.ForegroundColor <- origColor - interface IDisposable with member _.Dispose() = optionsReloadToken.Dispose() diff --git a/src/FSharp.Analyzers.Cli/Program.fs b/src/FSharp.Analyzers.Cli/Program.fs index 38e6705..8cc78cd 100644 --- a/src/FSharp.Analyzers.Cli/Program.fs +++ b/src/FSharp.Analyzers.Cli/Program.fs @@ -6,12 +6,13 @@ open System.Text.RegularExpressions open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Text open Argu -open FSharp.Analyzers.SDK open GlobExpressions open Microsoft.CodeAnalysis.Sarif open Microsoft.CodeAnalysis.Sarif.Writers open Microsoft.Extensions.Logging open Ionide.ProjInfo +open FSharp.Analyzers.SDK +open FSharp.Analyzers.Cli open FSharp.Analyzers.Cli.CustomLogging type Arguments = @@ -252,23 +253,31 @@ let printMessages (msgs: AnalyzerMessage list) = let msgLogger = factory.CreateLogger("") - msgs - |> Seq.iter (fun analyzerMessage -> - let m = analyzerMessage.Message + let colorFormat color message = + CustomLogging.AnsiColorHelpers.formatMessageAsAnsiColorizedString color message - msgLogger.Log( - severityToLogLevel[m.Severity], - "{0}({1},{2}): {3} {4} - {5}", - m.Range.FileName, - m.Range.StartLine, - m.Range.StartColumn, - (m.Severity.ToString()), - m.Code, - m.Message - ) - ) - - () + for analyzerMessage in msgs do + let m = analyzerMessage.Message + let logLevel = severityToLogLevel[m.Severity] + + let messageFormat, messageArgs = + let baseArgs: obj array = + [| + m.Range.FileName + m.Range.StartLine + m.Range.StartColumn + colorFormat (CustomLogging.AnsiColorHelpers.consoleColorOfLogLevel logLevel) (m.Severity.ToString()) + colorFormat (CustomLogging.AnsiColorHelpers.consoleColorOfLogLevel logLevel) m.Code + m.Message + |] + + let baseFormat = "{0}({1},{2}): {3} {4} - {5}" + + match analyzerMessage.HelpUri with + | None -> baseFormat, baseArgs + | Some uri -> baseFormat + " {6}", [| yield! baseArgs; colorFormat ConsoleColor.Cyan uri |] + + msgLogger.Log(logLevel, messageFormat, messageArgs) let writeReport (results: AnalyzerMessage list option) (codeRoot: string option) (report: string) = try