{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Hdoc.DTC.Write.HTML5.Error where import Control.Applicative (Applicative(..)) import Control.Monad (forM_, mapM_) import Data.Either (Either(..)) import Data.Foldable (Foldable(..)) import Data.Function (($), (.)) import Data.Functor ((<$>)) import Data.Int (Int) import Data.Locale hiding (Index) import Data.Map.Strict (Map) import Data.Maybe (Maybe(..)) import Data.Ord (Ord(..)) import Data.Semigroup (Semigroup(..)) import Data.Sequence (Seq(..)) import Data.TreeSeq.Strict (tree0) import Data.Tuple (fst, snd) import Text.Blaze ((!)) import Text.Show (Show(..)) import qualified Control.Monad.Trans.State as S import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HS import qualified Data.List as List import qualified Data.Map.Strict as Map import qualified Data.Text.Lazy as TL import qualified Text.Blaze.Html5 as H import qualified Text.Blaze.Html5.Attributes as HA import Control.Monad.Utils import Hdoc.DTC.Document as DTC import Hdoc.DTC.Write.HTML5.Base import Hdoc.DTC.Write.HTML5.Ident import Hdoc.DTC.Write.XML () import Text.Blaze.Utils import qualified Hdoc.DTC.Check as Check import qualified Hdoc.DTC.Collect as Collect import qualified Hdoc.DTC.Write.Plain as Plain import qualified Hdoc.TCT.Cell as TCT import qualified Hdoc.XML as XML instance (Html5ify Plain, Html5ify TCT.Location) => Html5ify Check.Errors where html5ify Check.Errors{..} = do st@State { state_collect = Collect.All{..} , state_l10n = Loqualization (l10n::FullLocale lang) , .. } <- liftComposeState S.get let errors :: [ ( Int{-errKind-} , HTML5{-errKindDescr-} , [(Plain{-errTypeKey-}, [(TCT.Location{-errPos-}, Ident{-errId-})])] ) ] = List.zipWith (\errKind (errKindDescr, errByPosByKey) -> (errKind, errKindDescr l10n, errByPosByKey)) [1::Int ..] [ (l10n_Error_Tag_unknown , errorTag st "-unknown" errors_tag_unknown) , (l10n_Error_Tag_ambiguous , errorTag st "-ambiguous" errors_tag_ambiguous) , (l10n_Error_Rref_unknown , errorReference "-unknown" errors_rref_unknown) , (l10n_Error_Reference_ambiguous , errorReference "-ambiguous" errors_reference_ambiguous) , (l10n_Error_Judgment_Judges_unknown , errorIdent errors_judgment_judges_unknown) , (l10n_Error_Judgment_Grades_unknown , errorIdent errors_judgment_grades_unknown) , (l10n_Error_Judgment_Grades_duplicated, errorIdent errors_judgment_grades_duplicated) , (l10n_Error_Judgment_Judge_unknown , errorName errors_judgment_judge_unknown) , (l10n_Error_Judgment_Judge_duplicated , errorName errors_judgment_judge_duplicated) , (l10n_Error_Judgment_Choice_duplicated, errorTitle errors_judgment_choice_duplicated) , (l10n_Error_Judgment_Grade_unknown , errorName errors_judgment_grade_unknown) ] let numErrors = Nat $ sum $ (<$> errors) $ \(_typ, _descr, errByPosByKey) -> sum $ length . snd <$> errByPosByKey when (numErrors > Nat 0) $ do liftComposeState $ S.put st { state_styles = HS.insert (Left "dtc-errors.css") $ HS.insert (Right $ -- NOTE: Implement a CSS-powered show/hide logic, using :target "\n@media screen {" <> "\n\t.error-filter:target .errors-list > li {display:none;}" <> (`foldMap` errors) (\(num, _description, errs) -> if null errs then "" else let err = "error-type"<>TL.pack (show num)<>"\\." in "\n\t.error-filter#"<>err<>":target .errors-list > li."<>err <>" {display:list-item}" <> "\n\t.error-filter#"<>err<>":target .errors-nav > ul > li."<>err <>" {list-style-type:disc;}" ) <> "\n}" ) state_styles } filterIds errors $ H.div ! HA.class_ "document-errors" ! HA.id "document-errors." $$ do H.nav ! HA.class_ "errors-nav" $$ do H.p ! HA.class_ "errors-all" $$ H.a ! HA.href (refIdent "document-errors.") $$ do l10n_Errors_All l10n numErrors :: HTML5 H.ul $$ forM_ errors $ \(errKind, errKindDescr, errs) -> do unless (null errs) $ do H.li ! HA.class_ (attrify $ errorType errKind) $$ do H.a ! HA.href (refIdent $ errorType errKind) $$ do errKindDescr " ("::HTML5 html5ify $ sum $ length . snd <$> errs ")" H.ol ! HA.class_ "errors-list" $$ do let errByPosByKind :: Map TCT.Location{-errPos-} (Seq ( Int{-errKind-} , HTML5{-errKindDescr-} , Plain{-errKey-} , [(TCT.Location{-errPos-}, Ident{-errId-})] )) = Map.unionsWith (<>) $ (<$> errors) $ \(errKind, errKindDescr, errByKey) -> Map.unionsWith (<>) $ (<$> errByKey) $ \(errKey, errByPos) -> Map.singleton (fst $ List.head errByPos) $ -- NOTE: sort using the first position of this errKind with this errKey. pure (errKind, errKindDescr, errKey, errByPos) forM_ errByPosByKind $ mapM_ $ \(errKind, errKindDescr, errKey, errByPos) -> do H.li ! HA.class_ (attrify $ errorType errKind) $$ do H.span ! HA.class_ "error-message" $$ do H.span ! HA.class_ "error-kind" $$ do errKindDescr Plain.l10n_Colon l10n :: HTML5 html5ify errKey H.ol ! HA.class_ "error-location" $$ forM_ errByPos $ \(errPos, errId) -> H.li $$ H.a ! HA.href (refIdent errId) $$ html5ify errPos where errorType num = identify $ "error-type"<>show num<>"." -- | Nest error id= to enable showing/hidding errors using :target pseudo-class. filterIds [] h = h filterIds ((num, _description, errs):es) h = if null errs then filterIds es h else do H.div ! HA.class_ "error-filter" ! HA.id (attrify $ errorType num) $$ filterIds es h errorTag :: State -> Ident -> HM.HashMap Title (Seq TCT.Location) -> [(Plain, [(TCT.Location,Ident)])] errorTag State{state_plainify=Plain.State{state_l10n}} suffix errs = (<$> HM.toList errs) $ \(Title tag, errPositions) -> ( tag , List.zipWith (\num -> (,identifyTag suffix state_l10n tag (Just $ Nat1 num))) [1::Int ..] (toList errPositions) ) errorReference :: Ident -> HM.HashMap Ident (Seq TCT.Location) -> [(Plain, [(TCT.Location,Ident)])] errorReference suffix errs = (<$> HM.toList errs) $ \(id, errPositions) -> ( pure $ tree0 $ PlainText $ unIdent id , List.zipWith (\num -> (,identifyReference suffix id (Just $ Nat1 num))) [1::Int ..] (toList errPositions) ) errorIdent :: HM.HashMap Ident (Seq (TCT.Location, XML.Pos)) -> [(Plain, [(TCT.Location,Ident)])] errorIdent errs = (<$> HM.toList errs) $ \(id, errPositions) -> ( pure $ tree0 $ PlainText $ unIdent id , (\(locTCT, posXML) -> (locTCT, identify $ XML.pos_ancestorsWithFigureNames posXML)) <$> toList errPositions ) errorName :: HM.HashMap Name (Seq (TCT.Location, XML.Pos)) -> [(Plain, [(TCT.Location,Ident)])] errorName errs = (<$> HM.toList errs) $ \(name, errPositions) -> ( pure $ tree0 $ PlainText $ unName name , (\(locTCT, posXML) -> (locTCT, identify $ XML.pos_ancestorsWithFigureNames posXML)) <$> toList errPositions ) errorTitle :: HM.HashMap Title (Seq (TCT.Location, XML.Pos)) -> [(Plain, [(TCT.Location,Ident)])] errorTitle errs = (<$> HM.toList errs) $ \(title, errPositions) -> ( unTitle title , (\(locTCT, posXML) -> (locTCT, identify $ XML.pos_ancestorsWithFigureNames posXML)) <$> toList errPositions )