{-# LANGUAGE CPP, RankNTypes, GADTs #-}{-# OPTIONS_GHC -fno-warn-unused-imports #-}-- Copyright (C) 2009 Ganesh Sittampalam---- 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.moduleDarcs.Patch.Split(Splitter(..),rawSplitter,noSplitter,primSplitter)whereimportData.List(intersperse)importDarcs.Witnesses.OrderedimportDarcs.Witnesses.SealedimportDarcs.Patch.Patchy(ReadPatch(..),ShowPatch(..),Invert(..))importDarcs.Patch.Prim(Prim(..),FilePatchType(..),canonize,canonizeFL)importDarcs.Patch.ReadMonads(parse_strictly)importDarcs.Patch.Read()importDarcs.Patch.Viewing()importPrinter(renderPS)importqualifiedData.ByteStringasBimportqualifiedData.ByteString.Char8asBC#include "gadts.h"-- |A splitter is something that can take a patch and (possibly) render it-- as text in some format of its own choosing.-- This text can then be presented to the user for editing, and the result-- given to the splitter for parsing.-- If the parse succeeds, the result is a list of patches that could replace-- the original patch in any context.-- Typically this list will contain the changed version of the patch, along-- with fixup pieces to ensure that the overall effect of the list is the same-- as the original patch.-- The individual elements of the list can then be offered separately to the-- user, allowing them to accept some and reject others.---- There's no immediate application for a splitter for anything other than-- Prim (you shouldn't go editing named patches, you'll break them!)-- However you might want to compose splitters for FilePatchType to make-- splitters for Prim etc, and the generality doesn't cost anything.dataSplitterp=Splitter{applySplitter::FORALL(xy)pC(xy)->Maybe(B.ByteString,B.ByteString->Maybe(FLpC(xy)))-- canonization is needed to undo the effects of splitting-- Typically, the list returned by applySplitter will not-- be in the simplest possible form (since the user will have-- deliberately added extra stuff). Once the user has selected-- the pieces they want, we need to make sure that we eliminate-- any remaining redundancy in the selected pieces, otherwise-- we might record (or whatever) a rather strange looking patch.-- This hook allows the splitter to provide an appropriate-- function for doing this.,canonizeSplit::FORALL(xy)FLpC(xy)->FLpC(xy)}{- Some facts that probably ought to be true about splitters: should make some QC
properties
applySplitter p = Just (bs, f) ==> f bs == Just (p :>: NilFL)
applySplitter p = Just (bs, f) ; f bs' = Just ps ==> canonizeSplit ps = p :>: NilFL
-}-- Does not canonize as there is no generic operation to do this.withEditedHead::Invertp=>pC(xy)->pC(xz)->FLpC(xy)withEditedHeadpres=res:>:invertres:>:p:>:NilFL-- |This generic splitter just lets the user edit the printed representation of the patch-- Should not be used expect for testing and experimentation.rawSplitter::(ShowPatchp,ReadPatchp,Invertp)=>SplitterprawSplitter=Splitter{applySplitter=\p->Just(renderPS.showPatch$p,\str->caseparse_strictly(readPatch'False)strofJust(Just(Sealedres),_)->Just(withEditedHeadpres)_->Nothing),canonizeSplit=id}-- |Never splits. In other code we normally pass around Maybe Splitter instead of using this-- as the default, because it saves clients that don't care about splitting from having to-- import this module just to get noSplitter.noSplitter::SplitterpnoSplitter=Splitter{applySplitter=constNothing,canonizeSplit=id}doPrimSplit::PrimC(xy)->Maybe(B.ByteString,B.ByteString->Maybe(FLPrimC(xy)))doPrimSplit(FPfn(Hunknbeforeafter))=Just(B.concat$intersperse(BC.pack"\n")$concat[helptext,[mkSep" BEFORE (reference) =========================="],before,[mkSep"=== AFTER (edit) ============================="],after,[mkSep"=== (edit above) ============================="]],\bs->doletls=BC.split'\n'bs(_,ls2)<-breakSepls-- before(before',ls3)<-breakSepls2-- after 1(after',_)<-breakSepls3-- after 2return(hunkbeforebefore'+>+hunkbefore'after'+>+hunkafter'after))wheresep=BC.pack"=========================="helptext=mapBC.pack["Interactive hunk edit:"," - Edit the section marked 'AFTER'"," - Arbitrary editing is supported"," - This will only affect the patch, not your working copy"," - Hints:"," - To split added text, delete the part you want to postpone"," - To split removed text, copy back the part you want to retain",""]hunkba=canonize(FPfn(Hunknba))mkSeps=BC.appendsep(BC.packs)breakSepxs=casebreak(sep`BC.isPrefixOf`)xsof(_,[])->Nothing(ys,_:zs)->Just(ys,zs)doPrimSplit_=Nothing-- |Split a primitive hunk patch up-- by allowing the user to edit both the before and after lines, then insert fixup patches-- to clean up the mess.primSplitter::SplitterPrimprimSplitter=Splitter{applySplitter=doPrimSplit,canonizeSplit=canonizeFL}