aboutsummaryrefslogtreecommitdiff
path: root/src/HSVIS/Diagnostic.hs
blob: 116e4cd11404d8445b0d766e879e13729d903e26 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
module HSVIS.Diagnostic where

import Data.List (intercalate)

import HSVIS.Pretty


data Pos = Pos
  { posLine :: Int  -- ^ zero-based
  , posCol :: Int   -- ^ zero-based
  }
  deriving (Show, Eq, Ord)

instance Pretty Pos where
  prettysPrec _ (Pos y x) = showString (show (y + 1) ++ ":" ++ show (x + 1))

-- | Inclusive-exclusive range of positions in a file.
data Range = Range Pos Pos
  deriving (Show)

instance Semigroup Range where
  Range a b <> Range c d = Range (min a c) (max b d)

instance Pretty Range where
  prettysPrec _ (Range (Pos y1 x1) (Pos y2 x2))
    | y2 <= y1 + 1, x2 <= x1 + 1 = showString (show (y1 + 1) ++ ":" ++ show (x1 + 1))
    | y2 <= y1 + 1               = showString (show (y1 + 1) ++ ":" ++ show (x1 + 1) ++ "-" ++ show x2)
    | otherwise                  =
        showString ("(" ++ show (y1 + 1) ++ ":" ++ show (x1 + 1) ++ ")-(" ++ show (y2 + 1) ++ ":" ++ show x2 ++ ")") 

data Severity = SError | SWarning
  deriving (Show)
                                                                           
data Diagnostic = Diagnostic                                               
  { dSeverity :: Severity  -- ^ Error level
  , dFile :: FilePath      -- ^ The file for which the diagnostic was raised
  , dRange :: Range        -- ^ Where in the file
  , dStk :: [String]       -- ^ Stack of contexts (innermost at head) of the diagnostic
  , dSourceLine :: String  -- ^ The line in the source file of the start of the range
  , dMsg :: String         -- ^ The error message
  }
  deriving (Show)

printDiagnostic :: Diagnostic -> String
printDiagnostic (Diagnostic sev fp rng@(Range (Pos y1 x1) (Pos y2 x2)) stk srcline msg) =
  let linenum = show (y1 + 1)
      locstr = pretty rng
      ncarets | y1 == y2  = max 1 (x2 - x1)
              | otherwise = length srcline - x1
      caretsuffix | y1 == y2  = ""
                  | otherwise = "..."

      mainLine =
        (case sev of SError -> "Error: "
                     SWarning -> "Warning: ")
        ++ fp ++ ":" ++ locstr ++ ": " ++ msg
      revCtxTrace = reverse $ map (\(i, descr) -> "in " ++ descr ++ (if i == 0 then "" else ","))
                                  (zip [0::Int ..] (reverse stk))
      srcPointer =
         [map (\_ -> ' ') linenum ++ " |"
         ,linenum ++ " | " ++ srcline
         ,map (\_ -> ' ') linenum ++ " | " ++ replicate x1 ' ' ++
            replicate ncarets '^' ++ caretsuffix]
  in intercalate "\n" $ [mainLine] ++ srcPointer ++ revCtxTrace