-- Copyright (C) 2002-2003 David Roundy---- This program is free software; you can redistribute it and/or modify-- it under the terms of the GNU General Public License as published by-- the Free Software Foundation; either version 2, or (at your option)-- any later version.---- This program is distributed in the hope that it will be useful,-- but WITHOUT ANY WARRANTY; without even the implied warranty of-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the-- GNU General Public License for more details.---- You should have received a copy of the GNU General Public License-- along with this program; see the file COPYING. If not, write to-- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,-- Boston, MA 02110-1301, USA.{-# OPTIONS_GHC -fglasgow-exts #-}{-# LANGUAGE CPP, ForeignFunctionInterface #-}-- , ScopedTypeVariables, TypeOperators, PatternGuards #-}#include "gadts.h"moduleDarcs.SelectChanges(with_selected_changes',with_selected_changes_to_files',with_selected_last_changes_to_files',with_selected_last_changes_reversed',with_selected_changes,with_selected_changes_to_files,with_selected_changes_reversed,with_selected_last_changes_to_files,with_selected_last_changes_to_files_reversed,with_selected_last_changes_reversed,view_changes,with_selected_patch_from_repo,)whereimportSystem.IOimportData.List(intersperse)importData.Maybe(catMaybes)importData.Char(toUpper)importControl.Monad(when)importSystem.Exit(exitWith,ExitCode(ExitSuccess))importEnglish(Noun(..),englishNum)importDarcs.Hopefully(PatchInfoAnd,hopefully)importDarcs.Repository(Repository,read_repo)importDarcs.Patch(RepoPatch,Patchy,Prim,summary,invert,list_touched_files,commuteFL)importDarcs.Patch.Patchy(showPatch)importqualifiedDarcs.Patch(thing,things)importDarcs.Ordered(FL(..),RL(..),(:>)(..),(+>+),lengthFL,concatRL,mapFL_FL,spanFL,reverseFL,(+<+),mapFL,unsafeCoerceP)importDarcs.Patch.Choices(PatchChoices,patch_choices,patch_choices_tps,force_first,force_last,make_uncertain,tag,get_choices,separate_first_middle_from_last,separate_first_from_middle_last,patch_slot,select_all_middles,force_matching_last,force_matching_first,make_everything_later,TaggedPatch,tp_patch,Slot(..),)importDarcs.Patch.TouchesFiles(deselect_not_touching,select_not_touching)importDarcs.PrintPatch(printFriendly,printPatch,printPatchPager)importDarcs.SlurpDirectory(Slurpy)importDarcs.Match(have_nonrange_match,match_a_patch,match_a_patchread)importDarcs.Flags(DarcsFlag(Summary,DontGrabDeps,Verbose,DontPromptForDependencies),isInteractive)importDarcs.Sealed(FlippedSeal(..),flipSeal,seal2,unseal2)importDarcs.Utils(askUser,promptCharFancy,without_buffering)importPrinter(prefix,putDocLn)#include "impossible.h"dataWhichChanges=Last|LastReversed|First|FirstReversedderiving(Eq,Show)typeMatchCriterionp=FORALL(uv)WhichChanges->[DarcsFlag]->(pC(uv))->BooltypeWithPatchespaC(xy)=String-- jobname->[DarcsFlag]-- opts->Slurpy-- directory->FLpC(xy)-- patches to select among->((FLp:>FLp)C(xy)->IOa)-- job->IOa-- result of running job-- | The only difference with 'WithPatches' is the [FilePath] argumenttypeWithPatchesToFilespaC(xy)=String-- jobname->[DarcsFlag]-- opts->Slurpy-- directory->[FilePath]-- files->FLpC(xy)-- patches to select among->((FLp:>FLp)C(xy)->IOa)-- job->IOa-- result of running jobwith_selected_changes'::WithPatchesPrimaC(xy)with_selected_changes_to_files'::WithPatchesToFilesPrimaC(xy)with_selected_last_changes_to_files'::WithPatchesToFilesPrimaC(xy)with_selected_last_changes_reversed'::WithPatchesPrimaC(xy)-- Common match criteriatriv::MatchCriterionptriv___=Trueiswanted::Patchyp=>MatchCriterion(PatchInfoAndp)iswantedFirstoptsp=match_a_patchopts.hopefully$piswantedLastReversedoptsp=match_a_patchopts.hopefully.invert$piswantedLast__=bug"don't support patch matching with Last in wasp"iswantedFirstReversed__=bug"don't support patch matching with FirstReversed in wasp"with_selected_changes'=wascFirsttrivwith_selected_changes_to_files'=wasc_Firsttrivwith_selected_last_changes_to_files'=wasc_Lasttrivwith_selected_last_changes_reversed'=wascLastReversedtrivwith_selected_changes::RepoPatchp=>WithPatches(PatchInfoAndp)aC(xy)with_selected_changes_to_files::RepoPatchp=>WithPatchesToFiles(PatchInfoAndp)aC(xy)with_selected_changes_reversed::RepoPatchp=>WithPatches(PatchInfoAndp)aC(xy)with_selected_last_changes_to_files::RepoPatchp=>WithPatchesToFiles(PatchInfoAndp)aC(xy)with_selected_last_changes_to_files_reversed::RepoPatchp=>WithPatchesToFiles(PatchInfoAndp)aC(xy)with_selected_last_changes_reversed::RepoPatchp=>WithPatches(PatchInfoAndp)aC(xy)with_selected_changes=wascFirstiswantedwith_selected_changes_to_files=wasc_Firstiswantedwith_selected_changes_reversed=wascFirstReversediswantedwith_selected_last_changes_to_files=wasc_Lastiswantedwith_selected_last_changes_to_files_reversed=wasc_LastReversediswantedwith_selected_last_changes_reversed=wascLastReversediswanted-- | wasc and wasc_ are just shorthand for with_any_selected_changeswasc::Patchyp=>WhichChanges->MatchCriterionp->WithPatchespaC(xy)wascmwchcritjos=wasc_mwchcritjos[]wasc_::Patchyp=>WhichChanges->MatchCriterionp->WithPatchesToFilespaC(xy)wasc_=with_any_selected_changeswith_any_selected_changes::Patchyp=>WhichChanges->MatchCriterionp->WithPatchesToFilespaC(xy)with_any_selected_changesLastcritjnoptssfs=with_any_selected_changes_last(patches_to_consider_last'fsoptscrit)critjnoptssfswith_any_selected_changesFirstcritjnoptssfs=with_any_selected_changes_first(patches_to_consider_first'fsoptscrit)critjnoptssfswith_any_selected_changesFirstReversedcritjnoptssfs=with_any_selected_changes_first_reversed(patches_to_consider_first_reversed'fsoptscrit)critjnoptssfswith_any_selected_changesLastReversedcritjnoptssfs=with_any_selected_changes_last_reversed(patches_to_consider_last_reversed'fsoptscrit)critjnoptssfsview_changes::RepoPatchp=>[DarcsFlag]->Slurpy->[FilePath]->FL(PatchInfoAndp)C(xy)->IO()view_changesopts_fpps=casepatches_to_consider_nothing'fpoptsiswantedpsofps_to_consider:>_->vcps_to_considerwherevc::RepoPatchp=>FL(PatchInfoAndp)C(xy)->IO()vcp=without_buffering$dotext_viewoptsps_len0NilRLinit_tpsinit_pcreturn()where(init_pc,init_tps)=patch_choices_tpspps_len=lengthFLinit_tpsdataKeyPressa=KeyPress{kp::Char,kpHelp::String}helpFor::String->[[KeyPressa]]->StringhelpForjobnameoptions=unlines$["How to use "++jobname++":"]++(concat$intersperse[""]$map(maphelp)options)++["","?: show this help","","<Space>: accept the current default (which is capitalized)"]wherehelpi=kpi:(": "++kpHelpi)keysFor::[[KeyPressa]]->[Char]keysFor=concatMap(mapkp)with_selected_patch_from_repo::forallpC(rut).RepoPatchp=>String->RepositorypC(rut)->[DarcsFlag]->(FORALL(a)(FL(PatchInfoAndp):>PatchInfoAndp)C(ar)->IO())->IO()with_selected_patch_from_repojnrepositoryoptsjob=dop_s<-read_reporepositorysp<-without_buffering$wspfrjn(match_a_patchreadopts)(concatRLp_s)NilFLcasespofJust(FlippedSeal(skipped:>selected))->job(skipped:>selected)Nothing->doputStrLn$"Cancelling "++jn++" since no patch was selected."-- | This ensures that the selected patch commutes freely with the skipped patches, including pending-- and also that the skipped sequences has an ending context that matches the recorded state, z,-- of the repository.wspfr::RepoPatchp=>String->(FORALL(ab)(PatchInfoAndp)C(ab)->Bool)->RL(PatchInfoAndp)C(xy)->FL(PatchInfoAndp)C(yu)->IO(Maybe(FlippedSeal(FL(PatchInfoAndp):>(PatchInfoAndp))C(u)))wspfr__NilRL_=returnNothingwspfrjnmatches(p:<:pps)skipped|not$matchesp=wspfrjnmatchespps(p:>:skipped)|otherwise=casecommuteFL(p:>skipped)ofLeft_->doputStrLn"\nSkipping depended-upon patch:"printFriendly[]pwspfrjnmatchespps(p:>:skipped)Right(skipped':>p')->doprintFriendly[]pletrepeat_this=wspfrjnmatches(p:<:pps)skippedoptions=[[KeyPress'y'(jn++" this patch"),KeyPress'n'("don't "++jn++" it"),KeyPress'v'"view this patch in full",KeyPress'p'"view this patch in full with pager",KeyPress'x'"view a summary of this patch",KeyPress'q'("cancel "++jn)]]letprompt="Shall I "++jn++" this patch?"yorn<-promptCharFancyprompt(keysForoptions)(Just'n')"?h"caseyornof'y'->return$Just$flipSeal$skipped':>p''n'->wspfrjnmatchespps(p:>:skipped)'v'->printPatchp>>repeat_this'p'->printPatchPagerp>>repeat_this'x'->doputDocLn$prefix" "$summaryprepeat_this'q'->doputStrLn$jn_cap++" cancelled."exitWith$ExitSuccess_->doputStrLn$helpForjnoptionsrepeat_thiswherejn_cap=(toUpper$headjn):tailjnwith_any_selected_changes_last::forallpaC(xy).Patchyp=>(FLpC(xy)->(FLp:>FLp)C(xy))->MatchCriterionp->WithPatchesToFilespaC(xy)with_any_selected_changes_lastp2ccritjobnameopts__psjob=casep2cpsofps_to_consider:>other_ps->ifnot$isInteractiveoptsthenjob$ps_to_consider:>other_pselsedopc<-without_buffering$tentatively_text_select""jobname(Noun"patch")Lastcritoptsps_len0NilRLinit_tpsinit_pcjob$selected_patches_lastrejected_pspcwhererejected_ps=ps_to_considerps_len=lengthFLinit_tps(init_pc,init_tps)=patch_choices_tps$other_pswith_any_selected_changes_first::forallpaC(xy).Patchyp=>(FLpC(xy)->(FLp:>FLp)C(xy))->MatchCriterionp->WithPatchesToFilespaC(xy)with_any_selected_changes_firstp2ccritjobnameopts__psjob=casep2cpsofps_to_consider:>other_ps->ifnot$isInteractiveoptsthenjob$ps_to_consider:>other_pselsedopc<-without_buffering$tentatively_text_select""jobname(Noun"patch")Firstcritoptsps_len0NilRLinit_tpsinit_pcjob$selected_patches_firstrejected_pspcwhererejected_ps=other_psps_len=lengthFLinit_tps(init_pc,init_tps)=patch_choices_tps$ps_to_considerwith_any_selected_changes_first_reversed::forallpaC(xy).Patchyp=>(FLpC(xy)->(FLp:>FLp)C(yx))->MatchCriterionp->WithPatchesToFilespaC(xy)with_any_selected_changes_first_reversedp2ccritjobnameopts__psjob=casep2cpsofps_to_consider:>other_ps->ifnot$isInteractiveoptsthenjob$invertother_ps:>invertps_to_considerelsedopc<-without_buffering$tentatively_text_select""jobname(Noun"patch")FirstReversedcritoptsps_len0NilRLinit_tpsinit_pcjob$selected_patches_first_reversedrejected_pspcwhererejected_ps=ps_to_considerps_len=lengthFLinit_tps(init_pc,init_tps)=patch_choices_tpsother_pswith_any_selected_changes_last_reversed::forallpaC(xy).Patchyp=>(FLpC(xy)->(FLp:>FLp)C(yx))->MatchCriterionp->WithPatchesToFilespaC(xy)with_any_selected_changes_last_reversedp2ccritjobnameopts__psjob=casep2cpsofps_to_consider:>other_ps->ifnot$isInteractiveoptsthenjob$invertother_ps:>invertps_to_considerelsedopc<-without_buffering$tentatively_text_select""jobname(Noun"patch")LastReversedcritoptsps_len0NilRLinit_tpsinit_pcjob$selected_patches_last_reversedrejected_pspcwhererejected_ps=other_psps_len=lengthFLinit_tps(init_pc,init_tps)=patch_choices_tpsps_to_considerpatches_to_consider_first'::Patchyp=>[FilePath]-- ^ files->[DarcsFlag]-- ^ opts->MatchCriterionp->FLpC(xy)-- ^ patches->(FLp:>FLp)C(xy)patches_to_consider_first'fsoptscritps=letdeselect_unwantedpc=ifhave_nonrange_matchoptsthenifDontGrabDeps`elem`optsthenforce_matching_last(not.iswanted_)pcelsemake_everything_later$force_matching_firstiswanted_pcelsepciswanted_=critFirstopts.tp_patchinifnullfs&&not(have_nonrange_matchopts)thenps:>NilFLelsetp_patches$separate_first_middle_from_last$deselect_not_touchingfs$deselect_unwanted$patch_choicespspatches_to_consider_last'::Patchyp=>[FilePath]-- ^ files->[DarcsFlag]-- ^ opts->MatchCriterionp->FLpC(xy)-- ^ patches->(FLp:>FLp)C(xy)patches_to_consider_last'fsoptscritps=letdeselect_unwantedpc=ifhave_nonrange_matchoptsthenifDontGrabDeps`elem`optsthenforce_matching_last(not.iswanted_)pcelsemake_everything_later$force_matching_firstiswanted_pcelsepciswanted_=critLastopts.tp_patchinifnullfs&&not(have_nonrange_matchopts)thenNilFL:>pselsecaseget_choices$select_not_touchingfs$deselect_unwanted$patch_choicespsoffc:>mc:>lc->tp_patches$fc:>mc+>+lcpatches_to_consider_first_reversed'::Patchyp=>[FilePath]-- ^ files->[DarcsFlag]-- ^ opts->MatchCriterionp->FLpC(xy)-- ^ patches->(FLp:>FLp)C(yx)patches_to_consider_first_reversed'fsoptscritps=letdeselect_unwantedpc=ifhave_nonrange_matchoptsthenifDontGrabDeps`elem`optsthenforce_matching_last(not.iswanted_)pcelsemake_everything_later$force_matching_firstiswanted_pcelsepciswanted_=critFirstReversedopts.tp_patchinifnullfs&&not(have_nonrange_matchopts)thenNilFL:>(invertps)elsecaseget_choices$select_not_touchingfs$deselect_unwanted$patch_choices$invertpsoffc:>mc:>lc->tp_patches$fc:>mc+>+lcpatches_to_consider_last_reversed'::Patchyp=>[FilePath]-- ^ files->[DarcsFlag]-- ^ opts->MatchCriterionp->FLpC(xy)-- ^ patches->(FLp:>FLp)C(yx)patches_to_consider_last_reversed'fsoptscritps=letdeselect_unwantedpc=ifhave_nonrange_matchoptsthenifDontGrabDeps`elem`optsthenforce_matching_last(not.iswanted_)pcelsemake_everything_later$force_matching_firstiswanted_pcelsepciswanted_=critLastReversedopts.tp_patchinifnullfs&&not(have_nonrange_matchopts)then(invertps):>NilFLelsetp_patches$separate_first_middle_from_last$deselect_not_touchingfs$deselect_unwanted$patch_choices$invertpspatches_to_consider_nothing'::RepoPatchp=>[FilePath]-- ^ files->[DarcsFlag]-- ^ opts->MatchCriterion(PatchInfoAndp)->FL(PatchInfoAndp)C(xy)-- ^ patches->(FL(PatchInfoAndp):>FL(PatchInfoAndp))C(xy)patches_to_consider_nothing'fsoptscritps=letdeselect_unwantedpc=ifhave_nonrange_matchoptsthenifDontGrabDeps`elem`optsthenforce_matching_last(not.iswanted_)pcelsemake_everything_later$force_matching_firstiswanted_pcelsepciswanted_=critFirstopts.tp_patchinifnullfs&&not(have_nonrange_matchopts)thenps:>NilFLelsetp_patches$separate_first_middle_from_last$deselect_not_touchingfs$deselect_unwanted$patch_choicesps-- | Returns the results of a patch selection user interactionselected_patches_last::Patchyp=>FLpC(xy)->PatchChoicespC(yz)->(FLp:>FLp)C(xz)selected_patches_lastother_pspc=caseget_choicespcoffc:>mc:>lc->other_ps+>+mapFL_FLtp_patch(fc+>+mc):>mapFL_FLtp_patchlcselected_patches_first::Patchyp=>FLpC(yz)->PatchChoicespC(xy)->(FLp:>FLp)C(xz)selected_patches_firstother_pspc=caseseparate_first_from_middle_lastpcofxs:>ys->mapFL_FLtp_patchxs:>mapFL_FLtp_patchys+>+other_psselected_patches_last_reversed::Patchyp=>FLpC(yx)->PatchChoicespC(zy)->(FLp:>FLp)C(xz)selected_patches_last_reversedother_pspc=caseseparate_first_from_middle_lastpcofxs:>ys->invert(mapFL_FLtp_patchys+>+other_ps):>invert(mapFL_FLtp_patchxs)selected_patches_first_reversed::Patchyp=>FLpC(zy)->PatchChoicespC(yx)->(FLp:>FLp)C(xz)selected_patches_first_reversedother_pspc=caseget_choicespcoffc:>mc:>lc->invert(mapFL_FLtp_patchlc):>invert(other_ps+>+mapFL_FLtp_patch(fc+>+mc))text_select::forallpC(xyz).Patchyp=>String->WhichChanges->MatchCriterionp->[DarcsFlag]->Int->Int->RL(TaggedPatchp)C(xy)->FL(TaggedPatchp)C(yz)->PatchChoicespC(xz)->IO((PatchChoicesp)C(xz))text_select_______NilFLpc=returnpctext_selectjnwhichchcritoptsn_maxntps_donetps_todo@(tp:>:tps_todo')pc=do(printFriendlyopts)`unseal2`viewprepeat_this-- prompt the userwheredo_next_actionjaje=tentatively_text_selectjajnjewhichchcritoptsn_max(n+1)(tp:<:tps_done)tps_todo'do_next=do_next_action""(Noun"patch")helper::PatchChoicespC(ab)->pC(ab)helper=undefinedthing=Darcs.Patch.thing(helperpc)things=Darcs.Patch.things(helperpc)options_basic=[KeyPress'y'(jn++" this "++thing),KeyPress'n'("don't "++jn++" it"),KeyPress'w'("wait and decide later, defaulting to no")]options_file=[KeyPress's'("don't "++jn++" the rest of the changes to this file"),KeyPress'f'(jn++" the rest of the changes to this file")]options_view=[KeyPress'v'("view this "++thing++" in full"),KeyPress'p'("view this "++thing++" in full with pager"),KeyPress'l'("list all selected "++things)]options_summary=[KeyPress'x'("view a summary of this "++thing)]options_quit=[KeyPress'd'(jn++" selected "++things++", skipping all the remaining "++things),KeyPress'a'(jn++" all the remaining "++things),KeyPress'q'("cancel "++jn)]options_nav=[KeyPress'j'("skip to next "++thing),KeyPress'k'("back up to previous "++thing)]options=[options_basic]++(ifis_single_file_patchthen[options_file]else[])++[options_view++ifSummary`elem`optsthen[]elseoptions_summary]++[options_quit]++[options_nav]prompt="Shall I "++jn++" this "++thing++"? "++"("++show(n+1)++"/"++shown_max++") "repeat_this::IO((PatchChoicesp)C(xz))repeat_this=doyorn<-promptCharFancyprompt(keysForoptions)(Justthe_default)"?h"caseyornof'y'->do_next$force_yes(tagtp)pc'n'->do_next$force_no(tagtp)pc'w'->do_next$make_uncertain(tagtp)pc's'->do_next_action"Skipped"(Noun"change")$skip_file'f'->do_next_action"Included"(Noun"change")$do_file'v'->printPatch`unseal2`viewp>>repeat_this'p'->printPatchPager`unseal2`viewp>>repeat_this'l'->doletselected=caseget_choicespcof(first_chs:>_:>last_chs)->ifwhichch==Last||whichch==FirstReversedthenmap_patcheslast_chselsemap_patchesfirst_chsmap_patches=mapFL(\a->showPatch`unseal2`(seal2$tp_patcha))putStrLn$"---- Already selected "++things++" ----"mapM_putDocLn$selectedputStrLn$"---- end of already selected "++things++" ----"(printFriendlyopts)`unseal2`viewprepeat_this'x'->do(putDocLn.prefix" ".summary)`unseal2`viewprepeat_this'd'->returnpc'a'->doask_confirmationreturn$select_all_middles(whichch==Last||whichch==FirstReversed)pc'q'->doputStrLn$jn_cap++" cancelled."exitWith$ExitSuccess'j'->casetps_todo'ofNilFL->-- May as well work out the length now we have all-- the patches in memorytext_selectjnwhichchcritoptsn_maxntps_donetps_todopc_->text_selectjnwhichchcritoptsn_max(n+1)(tp:<:tps_done)tps_todo'pc'k'->casetps_doneofNilRL->repeat_this(tp':<:tps_done')->text_selectjnwhichchcritoptsn_max(n-1)tps_done'(tp':>:tps_todo)pc'c'->text_selectjnwhichchcritoptsn_maxntps_donetps_todopc_->doputStrLn$helpForjnoptionsrepeat_thisforce_yes=ifwhichch==Last||whichch==FirstReversedthenforce_lastelseforce_firstforce_no=ifwhichch==Last||whichch==FirstReversedthenforce_firstelseforce_lastpatches_to_skip=(tagtp:)$catMaybes$mapFL(\tp'->iflist_touched_filestp'==touched_filesthenJust(tagtp')elseNothing)tps_todo'skip_file=foldrforce_nopcpatches_to_skipdo_file=foldrforce_yespcpatches_to_skipthe_default=get_default(whichch==Last||whichch==FirstReversed)$patch_slottppcjn_cap=(toUpper$headjn):tailjntouched_files=list_touched_files$tp_patchtpis_single_file_patch=lengthtouched_files==1viewp=ifwhichch==LastReversed||whichch==FirstReversedthenseal2$invert(tp_patchtp)elseseal2$tp_patchtpask_confirmation=ifjn`elem`["unpull","unrecord","obliterate"]thendoyorn<-askUser$"Really "++jn++" all undecided patches? "caseyornof('y':_)->return()_->exitWith$ExitSuccesselsereturn()text_view::forallpC(xyurs).Patchyp=>[DarcsFlag]->Int->Int->RL(TaggedPatchp)C(xy)->FL(TaggedPatchp)C(yu)->PatchChoicespC(rs)->IO((PatchChoicesp)C(rs))text_view____NilFL_=return$patch_choices$unsafeCoercePNilFL--return pctext_viewoptsn_maxntps_donetps_todo@(tp:>:tps_todo')pc=doprintFriendlyopts(tp_patchtp)putStr"\n"repeat_this-- prompt the userwhereprev_patch::IO((PatchChoicesp)C(rs))prev_patch=casetps_doneofNilRL->repeat_this(tp':<:tps_done')->text_viewoptsn_max(n-1)tps_done'(tp':>:tps_todo)pcnext_patch::IO((PatchChoicesp)C(rs))next_patch=casetps_todo'ofNilFL->-- May as well work out the length now we have all-- the patches in memorytext_viewoptsn_maxntps_doneNilFLpc_->text_viewoptsn_max(n+1)(tp:<:tps_done)tps_todo'pcoptions_yn=[KeyPress'y'"view this patch and go to the next",KeyPress'n'"skip to the next patch"]options_view=[KeyPress'v'"view this patch in full",KeyPress'p'"view this patch in full with pager"]options_summary=[KeyPress'x'"view a summary of this patch"]options_nav=[KeyPress'q'("quit view changes"),KeyPress'k'"back up to previous patch",KeyPress'j'"skip to next patch"]options=[options_yn]++[options_view++ifSummary`elem`optsthen[]elseoptions_summary]++[options_nav]prompt="Shall I view this patch? "++"("++show(n+1)++"/"++shown_max++")"repeat_this::IO((PatchChoicesp)C(rs))repeat_this=doyorn<-promptCharFancyprompt(keysForoptions)(Just'n')"?h"caseyornof'y'->printPatch(tp_patchtp)>>next_patch'n'->next_patch'v'->printPatch(tp_patchtp)>>repeat_this'p'->printPatchPager(tp_patchtp)>>repeat_this'x'->doputDocLn$prefix" "$summary(tp_patchtp)repeat_this'q'->exitWithExitSuccess'k'->prev_patch'j'->next_patch'c'->text_viewoptsn_maxntps_donetps_todopc_->doputStrLn$helpFor"view changes"optionsrepeat_thistentatively_text_select::Patchyp=>String->String->Noun->WhichChanges->MatchCriterionp->[DarcsFlag]->Int->Int->RL(TaggedPatchp)C(xy)->FL(TaggedPatchp)C(yz)->PatchChoicespC(xz)->IO((PatchChoicesp)C(xz))tentatively_text_select_________NilFLpc=returnpctentatively_text_selectjobactionjobnamejobelementwhichchcritoptsn_maxnps_doneps_todopc=casespanFL(\p->decided$patch_slotppc)ps_todoofskipped:>unskipped->dowhen(numSkipped>0)show_skippedlet(boringThenInteresting)=ifDontPromptForDependencies`elem`optsthenspanFL(not.(critwhichchopts).tp_patch)unskippedelseNilFL:>unskippedcaseboringThenInterestingofboring:>interesting->doletnumNotConsidered=lengthFLboring+numSkippedtext_selectjobnamewhichchcritoptsn_max(n+numNotConsidered)(reverseFLboring+<+reverseFLskipped+<+ps_done)interestingpcwherenumSkipped=lengthFLskippedshow_skipped=doputStrLn$_doing_++_with_++"."when(Verbose`elem`opts)$showskippedpatchskippedwhere_doing_=_action_++" "++jobname_with_=" of "++shownumSkipped++" "++_elem_""_action_=if(lengthjobaction)==0then"Skipped"elsejobaction_elem_=englishNumnumSkippedjobelementshowskippedpatch::Patchyp=>FL(TaggedPatchp)C(yt)->IO()showskippedpatch(tp:>:tps)=(putDocLn$prefix" "$summary(tp_patchtp))>>showskippedpatchtpsshowskippedpatchNilFL=return()decided::Slot->BooldecidedInMiddle=Falsedecided_=Trueget_default::Bool->Slot->Charget_default_InMiddle='w'get_defaultTrueInFirst='n'get_defaultTrueInLast='y'get_defaultFalseInFirst='y'get_defaultFalseInLast='n'tp_patches::(FL(TaggedPatchp):>FL(TaggedPatchp))C(xy)->(FLp:>FLp)C(xy)tp_patches(x:>y)=mapFL_FLtp_patchx:>mapFL_FLtp_patchy