{-# LANGUAGE OverloadedStrings, TypeSynonymInstances, MultiParamTypeClasses #-}-- Copyright (C) 2010 Petr Rockai---- Permission is hereby granted, free of charge, to any person-- obtaining a copy of this software and associated documentation-- files (the "Software"), to deal in the Software without-- restriction, including without limitation the rights to use, copy,-- modify, merge, publish, distribute, sublicense, and/or sell copies-- of the Software, and to permit persons to whom the Software is-- furnished to do so, subject to the following conditions:---- The above copyright notice and this permission notice shall be-- included in all copies or substantial portions of the Software.---- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS-- BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN-- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN-- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-- SOFTWARE.-- |-- Module : Darcs.Annotate-- Copyright : 2010 Petr Rockai-- License : MIT-- Maintainer : darcs-devel@darcs.net-- Stability : experimental-- Portability : portablemoduleDarcs.Annotate(annotate,annotateDirectory,format,machineFormat)whereimportPreludehiding(pi)importqualifiedData.ByteStringasBimportqualifiedData.ByteString.Char8asBCimportqualifiedData.MapasMimportqualifiedData.VectorasVimportData.List(nub,groupBy)importData.Maybe(isJust,catMaybes,isNothing)importControl.Monad.State(modify,when,gets,State,execState)importControl.Applicative((<$>))importDarcs.Patch.ApplyMonad(ApplyMonad(..))importDarcs.Patch.FileName(FileName,movedirfilename,fn2ps,ps2fn)importDarcs.Patch.Apply(Apply,apply,ApplyState)importDarcs.Patch.Info(PatchInfo(..),humanFriendly,piAuthor,makeFilename)importDarcs.Patch.PatchInfoAnd(info,PatchInfoAnd)importDarcs.Witnesses.OrderedimportStorage.Hashed.Tree(Tree)importLcs(getChanges)importPrinter(renderString)importByteStringUtils(linesPS,unlinesPS)#include "gadts.h"#include "impossible.h"dataFileOrDirectory=File|Directoryderiving(Show,Eq)dataAnnotated=Annotated{annotated::V.Vector(MaybePatchInfo,B.ByteString),current::[(Int,B.ByteString)],path::MaybeFileName,what::FileOrDirectory,currentInfo::PatchInfo}derivingShowtypeAnnotatedM=StateAnnotated-- XXX: No explicit method nor default method for 'editFile', 'editDirectory'instanceApplyMonadAnnotatedMTreewheretypeApplyMonadBaseAnnotatedM=AnnotatedMnestedApply__=undefinedFun"nestedApply"liftApply__=undefinedFun"liftApply"getApplyState=undefinedFun"getApplyState"putApplyState_=undefinedFun"putApplyState"mReadFilePS=undefinedFun"mReadFilePS"mDoesFileExist_=returnTruemDoesDirectoryExist_=returnTruemCreateDirectory_=return()mCreateFile_=return()mRemoveFilef=dop<-getspathwhen(p==Justf)$modify(\x->x{path=Nothing})updateDirectoryfmRemoveDirectory=mRemoveFilemRenameab=dop<-getspathw<-getswhatwhen(isJustp)$modify$\st->st{path=Just$movedirfilenameab(fromJustp)}when(w==Directory)$doletfix(i,x)=(i,fn2ps$movedirfilenameab(ps2fnx))modify$\st->st{current=mapfix$currentst}mModifyFilePSfjob=dop<-getspathwhen(p==Justf)$updateFile(fmaplinesPS.job.unlinesPS)mModifyFilePSsfjob=dop<-getspathwhen(p==Justf)$updateFilejobundefinedFun::Monadm=>String->maundefinedFunname=fail$name++" undefined for Annotated"updateFile::([B.ByteString]->AnnotatedM[B.ByteString])->AnnotatedM()updateFilejob=(==File)<$>getswhat>>=flipwhengowherego=dobefore<-mapsnd`fmap`getscurrentafter<-jobbeforereannotate$getChangesbeforeafterreannotate[]=return()reannotate((off,remove,add):rest)=doi<-getscurrentInfoc<-getscurrenta<-getsannotatedmodify$\s->s{current=takeoffc++[(-1,x)|x<-add]++drop(off+lengthremove)c,annotated=mergeia$take(lengthremove)$dropoffc}reannotaterestmergeial=aV.//[(line,(Justi,B.empty))|(line,_)<-l,line>=0&&line<V.lengtha]updateDirectory::FileName->AnnotatedM()updateDirectoryp=(==Directory)<$>getswhat>>=flipwhengowherego=doletline=fn2pspfiles<-getscurrentcasefilter((==line).snd)filesof[match@(ident,_)]->reannotateidentmatchline_->return()reannotateidentmatchline=modify$\x->x{annotated=annotatedxV.//[(ident,updateline$currentInfox)],current=filter(/=match)$currentx}updatelineinf=(Justinf,BC.concat[" -- created as: ",line])complete::Annotated->Boolcompletex=(V.all(isJust.fst)$annotatedx)||(isNothing$pathx)annotate'::(Applyp,ApplyStatep~Tree)=>FL(PatchInfoAndp)C(xy)->Annotated->Annotatedannotate'NilFLann=annannotate'(p:>:ps)ann|completeann=ann|otherwise=annotate'ps$execState(applyp)(ann{currentInfo=infop})annotate::(Applyp,ApplyStatep~Tree)=>FL(PatchInfoAndp)C(xy)->FileName->B.ByteString->Annotatedannotatepatchesinipathinicontent=annotate'patchesinitialwhereinitial=Annotated{path=Justinipath,currentInfo=error"There is no currentInfo.",current=zip[0..](linesPSinicontent),what=File,annotated=V.replicate(length$breakLinesinicontent)(Nothing,B.empty)}annotateDirectory::(Applyp,ApplyStatep~Tree)=>FL(PatchInfoAndp)C(xy)->FileName->[FileName]->AnnotatedannotateDirectorypatchesinipathinicontent=annotate'patchesinitialwhereinitial=Annotated{path=Justinipath,currentInfo=error"There is no currentInfo.",current=zip[0..](mapfn2psinicontent),what=Directory,annotated=V.replicate(lengthinicontent)(Nothing,B.empty)}machineFormat::B.ByteString->Annotated->StringmachineFormatda=unlines[caseiofJustinf->makeFilenameinfNothing->-- make unknowns uniform, for easier parsing"19700101000000-0000-0000000000000000000000000000000000000000.gz"++" | "++BC.unpackline++" "++BC.unpackadd|((i,add),line)<-zip(V.toList$annotateda)(breakLinesd)]format::B.ByteString->Annotated->Stringformatda=pi_list++"\n"++filewherepi_list=unlines$[shown++": "++renderString(humanFriendlyi)|(n::Int,i)<-zip[1..]pis]file=concat[annotation(fst$headchunk)++" | "++line(headchunk)++"\n"++unlines[indent25(" | "++linel)|l<-tailchunk]|chunk<-file_ann]pis=nub$catMaybes.mapfst$V.toList(annotateda)pi_map=M.fromList(zippis[1::Int..])file_ann=groupBy(\xy->fstx==fsty)$zip(V.toList$annotateda)(breakLinesd)line((_,add),l)=BC.unpack$BC.concat[l," ",add]annotation(Justi,_)|Justn<-M.lookupipi_map=pad20(piMaili)++" "++pad4("#"++shown)annotation_=pad25"unknown"padnstr=replicate(n-lengthstr)' '++(takenstr)indentnstr=replicaten' '++strpiMailpi|'<'`elem`piAuthorpi=takeWhile(/='>').drop1.dropWhile(/='<')$piAuthorpi|otherwise=piAuthorpibreakLines::BC.ByteString->[BC.ByteString]breakLiness=caseBC.split'\n'sof[]->[]split|BC.null(lastsplit)->initsplit|otherwise->split