{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}------------------------------------------------------------------------------- |-- Module : Text.Trifecta.Diagnostic.Rendering.Prim-- Copyright : (C) 2011 Edward Kmett-- License : BSD-style (see the file LICENSE)---- Maintainer : Edward Kmett <ekmett@gmail.com>-- Stability : experimental-- Portability : non-portable---- The type for Lines will very likely change over time, to enable drawing-- lit up multi-character versions of control characters for @^Z@, @^[@,-- @<0xff>@, etc. This will make for much nicer diagnostics when-- working with protocols.-------------------------------------------------------------------------------moduleText.Trifecta.Diagnostic.Rendering.Prim(Rendering(..),nullRendering,emptyRendering,Source(..),rendering,Renderable(..),Rendered(..)-- * Lower level drawing primitives,Lines,draw,ifNear,(.#))whereimportControl.ApplicativeimportControl.ComonadimportControl.Monad.StateimportData.ArrayimportData.ByteStringasBhiding(groupBy,empty,any)importData.FoldableimportData.Function(on)importData.Int(Int64)importData.Functor.BindimportData.List(groupBy)importData.SemigroupimportData.Semigroup.FoldableimportData.Semigroup.TraversableimportData.TraversableimportText.Trifecta.IntervalMapimportPreludeasPimportPreludehiding(span)importSystem.Console.Terminfo.ColorimportSystem.Console.Terminfo.PrettyPrintimportText.PrettyPrint.Freehiding(column)importText.Trifecta.Rope.BytesimportText.Trifecta.Rope.DeltaimportText.Trifecta.Highlight.ClassimportText.Trifecta.Highlight.EffectsimportqualifiedData.ByteString.UTF8asUTF8outOfRangeEffects::[ScopedEffect]->[ScopedEffect]outOfRangeEffectsxs=softBold:xstypeLines=Array(Int,Int64)([ScopedEffect],Char)(///)::Ixi=>Arrayie->[(i,e)]->Arrayiea///xs=a//P.filter(inRange(boundsa).fst)xsgrow::Int->Lines->Linesgrowya|inRange(t,b)y=a|otherwise=arraynew[(i,ifinRangeoldithena!ielse([],' '))|i<-rangenew]whereold@((t,lo),(b,hi))=boundsanew=((minty,lo),(maxby,hi))draw::[ScopedEffect]->Int->Int64->String->Lines->Linesdraweynxsa0|Prelude.nullxs=a0|otherwise=gt$lt(a///out)wherea=growya0((_,lo),(_,hi))=boundsaout=P.zipWith(\ic->((y,i),(e,c)))[n..]xslt|Prelude.any(\el->snd(fstel)<lo)out=(//[((y,lo),(outOfRangeEffectse,'<'))])|otherwise=idgt|Prelude.any(\el->snd(fstel)>hi)out=(//[((y,hi),(outOfRangeEffectse,'>'))])|otherwise=id-- | fill the interval from [n .. m) with a given effectrecolor::([ScopedEffect]->[ScopedEffect])->MaybeInt64->MaybeInt64->Lines->Linesrecolorfn0m0a0|m<=n=a0|otherwise=a///P.maprc[n..m-1]where((_,lo),(_,hi))=boundsan=maybeloidn0m=maybe(hi+1)idm0a=grow0a0rci=(yi,(fe,c))-- only if not isSpace?whereyi=(0,i)(e,c)=a!yidataRendering=Rendering{renderingDelta::!Delta-- focus, the render will keep this visible,renderingLineLen::{-# UNPACK #-}!Int64-- actual line length,renderingLineBytes::{-# UNPACK #-}!Int64-- line length in bytes,renderingLine::Lines->Lines,renderingOverlays::Delta->Lines->Lines}instanceHighlightableRenderingwhereaddHighlightsintervals(Renderingdlllblo)=Renderingdlllbl'owhered'=rewinddl'=Prelude.foldr(.)l[recolor(efftok)(columnlo<$guard(neardlo))(columnhi<$guard(neardhi))|(Intervallohi,tok)<-intersectionsd'(d'<>Columnslllb)intervals]efft_=highlightEffectstinstanceShowRenderingwhereshowsPrecd(Renderingplllb__)=showParen(d>10)$showString"Rendering ".showsPrec11p.showChar' '.showsPrec11ll.showChar' '.showsPrec11lb.showString" ... ..."nullRendering::Rendering->BoolnullRendering(Rendering(Columns00)00__)=TruenullRendering_=FalseemptyRendering::RenderingemptyRendering=rendering(Columns00)""instanceSemigroupRenderingwhere-- an unprincipled hackRendering(Columns00)00_f<>Renderingdellenlbdocg=Renderingdellenlbdoc$\dl->fd(gdl)Renderingdellenlbdocf<>Rendering____g=Renderingdellenlbdoc$\dl->fd(gdl)instanceMonoidRenderingwheremappend=(<>)mempty=emptyRenderingifNear::Delta->(Lines->Lines)->Delta->Lines->LinesifNeardfd'l|neardd'=fl|otherwise=linstanceHasDeltaRenderingwheredelta=renderingDeltaclassRenderabletwhererender::t->RenderinginstanceRenderableRenderingwhererender=idclassSourcetwheresource::t->(Int64,Int64,Lines->Lines){- the number of (padded) columns, number of bytes, and the the line -}instanceSourceStringwheresources|Prelude.elem'\n's=(ls,bs,draw[]00s')|otherwise=(ls+fromIntegral(Prelude.lengthend),bs,draw[soft(ForegroundBlue),softBold]0lsend.draw[]00s')whereend="<EOF>"s'=go0sbs=fromIntegral$B.length$UTF8.fromString$Prelude.takeWhile(/='\n')sls=fromIntegral$Prelude.lengths'gon('\t':xs)=lett=8-modn8inP.replicatet' '++go(n+t)xsgo_('\n':_)=[]gon(x:xs)=x:go(n+1)xsgo_[]=[]instanceSourceByteStringwheresource=source.UTF8.toString-- | create a drawing surfacerendering::Sources=>Delta->s->Renderingrenderingdels=casesourcesof(len,lb,doc)->Renderingdellenlbdoc(\_l->l)(.#)::(Delta->Lines->Lines)->Rendering->Renderingf.#Renderingdlllbsg=Renderingdlllbs$\el->fe$gelinstancePrettyRenderingwhereprettyr=prettyTermr>>=constemptyinstancePrettyTermRenderingwhereprettyTerm(Renderingdll_lf)=nesting$\k->columns$\n->go(fromIntegral(n-k))wheregocols=align(vsep(P.mapln[t..b]))where(lo,hi)=window(columnd)ll(min(max(cols-2)30)200)a=fd$l$array((0,lo),(-1,hi))[]((t,_),(b,_))=boundsalny=hcat$P.map(\g->P.foldrwith(pretty(P.mapsndg))(fst(P.headg)))$groupBy((==)`on`fst)[a!(y,i)|i<-[lo..hi]]window::Int64->Int64->Int64->(Int64,Int64)windowclw|c<=w2=(0,minwl)|c+w2>=l=ifl>wthen(l-w,l)else(0,w)|otherwise=(c-w2,c+w2)wherew2=divw2dataRendereda=a:@RenderingderivingShowinstanceFunctorRenderedwherefmapf(a:@s)=fa:@sinstanceHasDelta(Rendereda)wheredelta=delta.renderinstanceHasBytes(Rendereda)wherebytes=bytes.deltainstanceComonadRenderedwhereextendfas@(_:@s)=fas:@sextract(a:@_)=ainstanceApplyRenderedwhere(f:@s)<.>(a:@t)=fa:@(s<>t)instanceComonadApplyRenderedwhere(f:@s)<@>(a:@t)=fa:@(s<>t)instanceBindRenderedwhere(a:@s)>>-f=casefaofb:@t->b:@(s<>t)instanceFoldableRenderedwherefoldMapf(a:@_)=fainstanceTraversableRenderedwheretraversef(a:@s)=(:@s)<$>fainstanceFoldable1RenderedwherefoldMap1f(a:@_)=fainstanceTraversable1Renderedwheretraverse1f(a:@s)=(:@s)<$>fainstanceRenderable(Rendereda)whererender(_:@s)=s