packages feed

tricorder-0.2.0.0: src/Tricorder/CLI/Render.hs

module Tricorder.CLI.Render
    ( -- * Plain-text formatting
      diagnosticLine
    , diagnosticLineIndexed
    , diagnosticBlock
    , formatDuration
    , renderSourceResults
    ) where

import Atelier.Effects.Console (Console)
import Atelier.Time (Millisecond, toMicroseconds)

import Atelier.Effects.Console qualified as Console

import Tricorder.Build (Diagnostic (..), Severity (..))
import Tricorder.Module (ModuleName (..), PackageId (..))
import Tricorder.SourceLookup (ModuleSourceResult (..), SourceQuery (..))


formatDuration :: Millisecond -> Text
formatDuration d =
    let ms = toMicroseconds d `div` 1000
    in  if ms < 1000 then
            show ms <> "ms"
        else
            show (ms `div` 1000) <> "." <> show ((ms `mod` 1000) `div` 100) <> "s"


-- | Single-line diagnostic for plain-text / shell output.
--
-- Format: @E src\/Foo\/Bar.hs:42 \`something\` not in scope@
diagnosticLine :: Diagnostic -> Text
diagnosticLine d =
    prefix d.severity <> " " <> toText d.file <> ":" <> show d.line <> " " <> d.title
  where
    prefix SError = "E"
    prefix SWarning = "W"


-- | Like 'diagnosticLine' but prefixed with a 1-based index.
--
-- Format: @[N] E src\/Foo\/Bar.hs:42 \`something\` not in scope@
diagnosticLineIndexed :: Int -> Diagnostic -> Text
diagnosticLineIndexed n d = "[" <> show n <> "] " <> diagnosticLine d


-- | One-liner followed by the full GHC message body (verbose mode).
diagnosticBlock :: Diagnostic -> Text
diagnosticBlock d = diagnosticLine d <> "\n" <> d.text


renderSourceResults :: (Console :> es) => [ModuleSourceResult] -> Eff es ()
renderSourceResults results = mapM_ renderOne results
  where
    renderOne (SourceFound query src) = do
        when (length results > 1) $ Console.putTextLn $ header query
        Console.putText src
        when (length results > 1) $ Console.putStrLn ""
    renderOne (SourceNotFound query) =
        Console.putTextLn
            $ "Not found: "
                <> unModuleName query.moduleName
                <> " (module not in any installed package)"
    renderOne (SourceUnavailable query pkgId) =
        Console.putTextLn
            $ "No source available: "
                <> unModuleName query.moduleName
                <> " (could not locate or fetch a source tarball for "
                <> unPackageId pkgId
                <> ")"
    renderOne (FunctionNotFound query) =
        Console.putTextLn
            $ "tricorder: "
                <> unModuleName query.moduleName
                <> "#"
                <> fromMaybe "" query.function
                <> ": function not found in module source"

    header query =
        "-- "
            <> unModuleName query.moduleName
            <> maybe "" ("#" <>) query.function