1 {-# LANGUAGE FlexibleContexts #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE OverloadedStrings #-}
4 {-# LANGUAGE ScopedTypeVariables #-}
5 {-# LANGUAGE UndecidableInstances #-}
6 {-# OPTIONS_GHC -fno-warn-orphans #-}
7 module Textphile.DTC.Write.HTML5.Error where
9 import Control.Applicative (Applicative(..))
10 import Control.Monad (forM_, mapM_)
11 import Data.Default.Class (Default(..))
12 import Data.Either (Either(..))
13 import Data.Foldable (Foldable(..))
14 import Data.Function (($), (.))
15 import Data.Functor ((<$>))
17 import Data.Locale hiding (Index)
18 import Data.Map.Strict (Map)
19 import Data.Maybe (Maybe(..))
20 import Data.Ord (Ord(..))
21 import Data.Semigroup (Semigroup(..))
22 import Data.Sequence (Seq(..))
23 import Data.TreeSeq.Strict (tree0)
24 import Data.Tuple (fst, snd)
25 import Text.Blaze ((!))
26 import Text.Show (Show(..))
27 import qualified Control.Monad.Trans.RWS.Strict as RWS
28 import qualified Data.HashMap.Strict as HM
29 import qualified Data.HashSet as HS
30 import qualified Data.List as List
31 import qualified Data.Map.Strict as Map
32 import qualified Data.Text.Lazy as TL
33 import qualified Text.Blaze.Html5 as H
34 import qualified Text.Blaze.Html5.Attributes as HA
36 import Control.Monad.Utils
37 import Textphile.DTC.Document as DTC
38 import Textphile.DTC.Write.HTML5.Base
39 import Textphile.DTC.Write.HTML5.Ident
40 import Textphile.DTC.Write.XML ()
41 import Text.Blaze.Utils
42 import qualified Textphile.DTC.Analyze.Check as Analyze
43 import qualified Textphile.DTC.Analyze.Collect as Analyze
44 import qualified Textphile.DTC.Write.Plain as Plain
45 import qualified Textphile.TCT.Cell as TCT
46 import qualified Textphile.XML as XML
48 instance (Html5ify Plain, Html5ify TCT.Location) => Html5ify (Analyze.Errors (Seq Location)) where
49 html5ify Analyze.Errors{..} = do
51 { reader_all = Analyze.All{..}
52 , reader_l10n = Loqualization (l10n::FullLocale lang)
54 } <- composeLift RWS.ask
55 let errors :: [ ( Int{-errKind-}
56 , HTML5{-errKindDescr-}
57 , [(Plain{-errTypeKey-}, [(TCT.Location{-errPos-}, Ident{-errId-})])]
60 (\errKind (errKindDescr, errByPosByKey) ->
61 (errKind, errKindDescr l10n, errByPosByKey))
63 [ (l10n_Error_At_unknown , errorAt "-unknown" errors_at_unknown)
64 , (l10n_Error_At_ambiguous , errorAt "-ambiguous" errors_at_ambiguous)
65 , (l10n_Error_Rref_unknown , errorReference "-unknown" errors_ref_unknown)
66 , (l10n_Error_Reference_ambiguous , errorReference "-ambiguous" errors_reference_ambiguous)
67 , (l10n_Error_Judgment_Judges_unknown , errorIdent errors_judgment_judges_unknown)
68 , (l10n_Error_Judgment_Grades_unknown , errorIdent errors_judgment_grades_unknown)
69 , (l10n_Error_Judgment_Grades_duplicated, errorIdent errors_judgment_grades_duplicated)
70 , (l10n_Error_Judgment_Judge_unknown , errorName errors_judgment_judge_unknown)
71 , (l10n_Error_Judgment_Judge_duplicated , errorName errors_judgment_judge_duplicated)
72 , (l10n_Error_Judgment_Choice_duplicated, errorTitle errors_judgment_choice_duplicated)
73 , (l10n_Error_Judgment_Grade_unknown , errorName errors_judgment_grade_unknown)
75 let numErrors = Nat $ sum $ (<$> errors) $ \(_typ, _descr, errByPosByKey) ->
76 sum $ length . snd <$> errByPosByKey
77 when (numErrors > Nat 0) $ do
78 composeLift $ RWS.tell def
79 { writer_styles = HS.fromList
80 [ Left "dtc-errors.css"
82 -- NOTE: Implement a CSS-powered show/hide logic, using :target
83 "\n@media screen {" <>
84 "\n\t.error-filter:target .errors-list > li {display:none;}" <>
85 (`foldMap` errors) (\(num, _description, errs) ->
86 if null errs then "" else
87 let err = "error-type"<>TL.pack (show num)<>"\\." in
88 "\n\t.error-filter#"<>err<>":target .errors-list > li."<>err
89 <>" {display:list-item}" <>
90 "\n\t.error-filter#"<>err<>":target .errors-nav > ul > li."<>err
91 <>" {list-style-type:disc;}"
96 filterIds errors $ H.div ! HA.class_ "document-errors" ! HA.id "document-errors." $$ do
97 H.nav ! HA.class_ "errors-nav" $$ do
98 H.p ! HA.class_ "errors-all" $$
99 H.a ! HA.href (refIdent "document-errors.") $$ do
100 l10n_Errors_All l10n numErrors :: HTML5
103 \(errKind, errKindDescr, errs) -> do
104 unless (null errs) $ do
105 H.li ! HA.class_ (attrify $ errorType errKind) $$ do
106 H.a ! HA.href (refIdent $ errorType errKind) $$ do
109 html5ify $ sum $ length . snd <$> errs
111 H.ol ! HA.class_ "errors-list" $$ do
112 let errByPosByKind :: Map TCT.Location{-errPos-}
113 (Seq ( Int{-errKind-}
114 , HTML5{-errKindDescr-}
116 , [(TCT.Location{-errPos-}, Ident{-errId-})] )) =
117 Map.unionsWith (<>) $ (<$> errors) $ \(errKind, errKindDescr, errByKey) ->
118 Map.unionsWith (<>) $ (<$> errByKey) $ \(errKey, errByPos) ->
119 Map.singleton (fst $ List.head errByPos) $
120 -- NOTE: sort using the first position of this errKind with this errKey.
121 pure (errKind, errKindDescr, errKey, errByPos)
122 forM_ errByPosByKind $
123 mapM_ $ \(errKind, errKindDescr, errKey, errByPos) -> do
124 H.li ! HA.class_ (attrify $ errorType errKind) $$ do
125 H.span ! HA.class_ "error-message" $$ do
126 H.span ! HA.class_ "error-kind" $$ do
128 Plain.l10n_Colon l10n :: HTML5
130 H.ol ! HA.class_ "error-location" $$
131 forM_ errByPos $ \(errPos, errId) ->
133 H.a ! HA.href (refIdent errId) $$
136 errorType num = identify $ "error-type"<>show num<>"."
137 -- | Nest error id= to enable showing/hidding errors using :target pseudo-class.
139 filterIds ((num, _description, errs):es) h =
143 H.div ! HA.class_ "error-filter"
144 ! HA.id (attrify $ errorType num) $$
146 errorAt :: Ident -> HM.HashMap Ident (Seq (TCT.Location, XML.Pos)) -> [(Plain, [(TCT.Location,Ident)])]
147 errorAt suffix errs =
148 (<$> HM.toList errs) $ \(ref, errPositions) ->
149 ( pure $ tree0 $ PlainText $ unIdent ref
151 (\num (locTCT, _posXML) -> (locTCT, identifyAt suffix ref (Just $ Nat1 num)))
152 [1::Int ..] (toList errPositions)
154 errorReference :: Ident -> HM.HashMap Ident (Seq (TCT.Location, XML.Pos)) -> [(Plain, [(TCT.Location,Ident)])]
155 errorReference suffix errs =
156 (<$> HM.toList errs) $ \(id, errPositions) ->
157 ( pure $ tree0 $ PlainText $ unIdent id
159 (\num (locTCT, _posXML) -> (locTCT, identifyReference suffix id (Just $ Nat1 num)))
160 [1::Int ..] (toList errPositions)
162 errorIdent :: HM.HashMap Ident (Seq (TCT.Location, XML.Pos)) -> [(Plain, [(TCT.Location,Ident)])]
164 (<$> HM.toList errs) $ \(id, errPositions) ->
165 ( pure $ tree0 $ PlainText $ unIdent id
166 , (\(locTCT, posXML) ->
167 (locTCT, identify $ XML.pos_ancestorsWithFigureNames posXML))
168 <$> toList errPositions
170 errorName :: HM.HashMap Name (Seq (TCT.Location, XML.Pos)) -> [(Plain, [(TCT.Location,Ident)])]
172 (<$> HM.toList errs) $ \(name, errPositions) ->
173 ( pure $ tree0 $ PlainText $ unName name
174 , (\(locTCT, posXML) ->
175 (locTCT, identify $ XML.pos_ancestorsWithFigureNames posXML))
176 <$> toList errPositions
178 errorTitle :: HM.HashMap Title (Seq (TCT.Location, XML.Pos)) -> [(Plain, [(TCT.Location,Ident)])]
180 (<$> HM.toList errs) $ \(title, errPositions) ->
182 , (\(locTCT, posXML) ->
183 (locTCT, identify $ XML.pos_ancestorsWithFigureNames posXML))
184 <$> toList errPositions