| 1
2
3
4
| -- Copyright 2013 Evan Laforge
-- This program is distributed under the terms of the GNU General Public
-- License 3.0, see COPYING or http://www.gnu.org/licenses/gpl-3.0.txt
|
|
| 5
| {-# LANGUAGE LambdaCase #-}
|
|
| 6
| {- | Functions to save and restore state to and from files.
|
|
| 7
8
9
10
11
12
|
The naming convention is that @load@ and @save@ functions either load
the given file, replace the state with it, and set the SaveFile, or save
the current state and set the SaveFile. @read@ and @write@ functions
are lower level and either read the file and return the state, or write the
given state, without messing with the SaveFile.
|
|
| |
| 14
15
| module Cmd.Save (
-- * universal
|
|
| 16
| save, load, read, read_, load_template
|
|
| 17
18
19
20
21
| , infer_save_type
-- * state
, save_state, save_state_as, load_state
, read_state, read_state_, write_state
, write_current_state
|
|
| 22
23
| -- ** path
, get_state_path, state_path_for_repo, infer_state_path
|
|
| 24
25
26
27
| -- * git
, save_git, save_git_as, load_git, revert
, get_git_path
-- * config
|
|
| 28
| , save_allocations, load_allocations
|
|
| 29
30
31
| -- * misc
, save_views
) where
|
|
| 32
| import Prelude hiding (read)
|
|
| 33
| import qualified Control.Exception as Exception
|
|
| 34
| import qualified Control.Monad.Identity as Identity
|
|
| 35
| import qualified Data.List as List
|
|
| 36
| import qualified Data.Map as Map
|
|
| 37
| import qualified Data.Text as Text
|
|
| 38
| import qualified Data.Time as Time
|
|
| |
| 40
| import qualified System.Directory as Directory
|
|
| 41
| import qualified System.FilePath as FilePath
|
|
| 42
| import System.FilePath ((</>))
|
|
| |
| 44
| import qualified Util.File as File
|
|
| 45
| import qualified Util.Git as Git
|
|
| 46
| import qualified Util.Locale as Locale
|
|
| 47
| import qualified Util.Log as Log
|
|
| 48
| import qualified Util.Seq as Seq
|
|
| 49
| import qualified Util.Serialize as Serialize
|
|
| 50
| import qualified Util.TextUtil as TextUtil
|
|
| |
| 52
| import qualified Ui.Id as Id
|
|
| 53
| import qualified Ui.Ui as Ui
|
|
| 54
| import qualified Ui.UiConfig as UiConfig
|
|
| 55
56
| import qualified Ui.Transform as Transform
|
|
| 57
| import qualified Cmd.Cmd as Cmd
|
|
| 58
| import qualified Cmd.Instrument.MidiInst as MidiInst
|
|
| 59
| import qualified Cmd.Play as Play
|
|
| 60
| import qualified Cmd.SaveGit as SaveGit
|
|
| 61
| import qualified Cmd.SaveGitTypes as SaveGitTypes
|
|
| 62
| import qualified Cmd.Serialize
|
|
| |
| 64
65
| import qualified Perform.Midi.Patch as Patch
import qualified Instrument.Inst as Inst
|
|
| 66
| import qualified App.Config as Config
|
|
| |
| |
| |
| 70
71
72
73
| -- * universal
-- | Save to the current 'Cmd.state_save_file', or create a new git repo if
-- there is none.
|
|
| 74
| save :: Cmd.CmdT IO ()
|
|
| 75
| save = Cmd.gets Cmd.state_save_file >>= \x -> case x of
|
|
| |
| 77
78
79
80
| -- Try to override Cmd.Writable on an explicit save. If it's still
-- read only, this should throw an exception.
Just (_, Cmd.SaveRepo repo) -> save_git_as repo
Just (_, Cmd.SaveState fn) -> save_state_as fn
|
|
| |
| 82
83
84
85
86
87
| -- | Like 'read', but replace the current state and set 'Cmd.state_save_file'.
load :: FilePath -> Cmd.CmdT IO ()
load path = do
(state, save_file) <- read path
set_state save_file True state
|
|
| 88
89
| -- | Try to guess whether the given path is a git save or state save. If it's
-- a directory, look inside for a .git or .state save.
|
|
| 90
| read :: FilePath -> Cmd.CmdT IO (Ui.State, StateSaveFile)
|
|
| |
| 92
| path <- expand_filename path
|
|
| 93
94
| save <- Cmd.require_right ("read: "<>) =<< liftIO (infer_save_type path)
case save of
|
|
| 95
| Cmd.SaveRepo repo -> read_git repo Nothing
|
|
| 96
| Cmd.SaveState fn -> read_state fn
|
|
| |
| 98
| -- | Low level 'read'.
|
|
| 99
| read_ :: Cmd.InstrumentDb -> FilePath -> IO (Either Text Ui.State)
|
|
| 100
101
102
103
104
105
106
| read_ db path = infer_save_type path >>= \case
Left err -> return $ Left $ "read " <> showt path <> ": " <> err
Right save -> case save of
Cmd.SaveState fname -> first pretty <$> read_state_ db fname
Cmd.SaveRepo repo -> fmap extract <$> read_git_ db repo Nothing
where extract (state, _, _) = state
|
|
| 107
108
| -- | Like 'load', but don't set SaveFile, so you can't overwrite the loaded
-- file when you save.
|
|
| 109
110
111
| load_template :: FilePath -> Cmd.CmdT IO ()
load_template fn = do
(state, _) <- read fn
|
|
| 112
| set_state Nothing True state
|
|
| 113
| now <- liftIO $ Time.getCurrentTime
|
|
| 114
| Ui.modify_config $ Ui.meta#Ui.creation #= now
|
|
| 115
116
117
118
119
|
-- | Given a path, which is either a file or a directory, try to figure out
-- what to load. Saves can be either a plain saved state, or a directory
-- containing either a git repo @save.git@, or a state @save.state@. If
-- both are present, the git repo is preferred.
|
|
| 120
| infer_save_type :: FilePath -> IO (Either Text Cmd.SaveFile)
|
|
| 121
| infer_save_type path = fmap prepend $ cond
|
|
| 122
| [ (return $ SaveGit.is_git path, ok $ Cmd.SaveRepo path)
|
|
| |
| 124
125
| [ (is_dir git_fn, ok $ Cmd.SaveRepo git_fn)
, (is_file state_fn, ok $ Cmd.SaveState state_fn)
|
|
| 126
127
| ] $ return $ Left $ "directory contains neither " <> txt git_fn
<> " nor " <> txt state_fn)
|
|
| 128
| , (is_file path, ok $ Cmd.SaveState path)
|
|
| 129
| ] $ return $ Left "file not found"
|
|
| |
| 131
| prepend (Left err) = Left $ txt path <> ": " <> err
|
|
| 132
133
134
135
136
137
| prepend (Right val) = Right val
ok = return . Right
git_fn = path </> default_git
state_fn = path </> default_state
is_dir = Directory.doesDirectoryExist
is_file = Directory.doesFileExist
|
|
| |
| 139
140
141
142
143
144
145
146
147
148
| -- | Like guard cases but with monadic conditions.
cond :: Monad m => [(m Bool, m a)] -> m a -> m a
cond [] consequent = consequent
cond ((condition, result) : rest) consequent =
ifM condition result (cond rest consequent)
-- * expand path
-- | Expand `-delimited macros to make a filepath.
expand_filename :: FilePath -> Cmd.CmdT IO FilePath
|
|
| 149
| expand_filename = fmap untxt . TextUtil.mapDelimitedM False '`' expand . txt
|
|
| |
| 151
152
| expand text = case lookup text filename_macros of
Just get -> get
|
|
| 153
| Nothing -> Cmd.throw $ "unknown macro " <> showt text
|
|
| 154
| <> ", known macros are: "
|
|
| 155
| <> Text.intercalate ", " (map fst filename_macros)
|
|
| 156
157
158
159
160
161
162
163
164
|
filename_macros :: [(Text, Cmd.CmdT IO Text)]
filename_macros =
[ ("y-m-d", liftIO date)
, ("d", do
dir <- Cmd.require "`d` requires a save dir"
=<< Cmd.gets Cmd.state_save_dir
return $ txt dir)
]
|
|
| 165
166
167
168
169
|
date :: IO Text
date = do
tz <- Time.getCurrentTimeZone
today <- Time.utcToLocalTime tz <$> Time.getCurrentTime
|
|
| 170
| return $ txt $ Time.formatTime Locale.defaultTimeLocale "%y-%m-%d" today
|
|
| |
| |
| 174
| save_state :: Cmd.CmdT IO ()
|
|
| 175
176
| save_state = save_state_as =<< Cmd.require "can't save, no save file"
=<< get_state_path
|
|
| |
| 178
| -- | Save the state to the given file and set 'Cmd.state_save_file'.
|
|
| 179
180
181
| --
-- The directory of the filename will become the project directory, so things
-- like the saved REPL history and the ly subdirectory will go there.
|
|
| 182
183
| save_state_as :: FilePath -> Cmd.CmdT IO ()
save_state_as fname = do
|
|
| 184
| fname <- write_current_state fname
|
|
| 185
| set_save_file (Just (Cmd.ReadWrite, SaveState fname)) False
|
|
| |
| 187
| write_current_state :: FilePath -> Cmd.CmdT IO FilePath
|
|
| 188
| write_current_state fname = do
|
|
| 189
| fname <- expand_filename fname
|
|
| |
| 191
192
| ((), _, wall_secs) <- rethrow_io "write_current_state" $ liftIO $
Log.time_eval $ write_state fname state
|
|
| 193
194
| Log.notice $ "wrote state to " <> showt fname <> ", took "
<> pretty wall_secs <> "s"
|
|
| |
| |
| 197
| write_state :: FilePath -> Ui.State -> IO ()
|
|
| 198
199
| write_state fname state = do
now <- Time.getCurrentTime
|
|
| 200
| void $ Serialize.serialize Cmd.Serialize.score_magic fname $
|
|
| 201
202
| Ui.config#Ui.meta#Ui.last_save #= now $
Ui.clear state
|
|
| |
| 204
205
206
| load_state :: FilePath -> Cmd.CmdT IO ()
load_state fname = do
(state, save_file) <- read_state fname
|
|
| 207
| set_state save_file True state
|
|
| |
| 209
| read_state :: FilePath -> Cmd.CmdT IO (Ui.State, StateSaveFile)
|
|
| 210
| read_state fname = do
|
|
| 211
| let mkmsg err = "load " <> txt fname <> ": " <> pretty err
|
|
| 212
| writable <- liftIO $ File.writable fname
|
|
| 213
| Log.notice $ "read state from " <> showt fname
|
|
| 214
| <> if writable then "" else " (ro)"
|
|
| 215
216
| db <- Cmd.gets $ Cmd.config_instrument_db . Cmd.state_config
state <- Cmd.require_right mkmsg =<< liftIO (read_state_ db fname)
|
|
| 217
218
| return (state, Just
(if writable then Cmd.ReadWrite else Cmd.ReadOnly, SaveState fname))
|
|
| |
| 220
221
| -- | Low level 'read_state'.
read_state_ :: Cmd.InstrumentDb -> FilePath
|
|
| 222
| -> IO (Either Serialize.UnserializeError Ui.State)
|
|
| 223
224
225
226
227
228
229
230
| read_state_ db fname =
Serialize.unserialize Cmd.Serialize.score_magic fname >>= \case
Right state -> mapM_ Log.write logs >> return (Right upgraded)
where (upgraded, logs) = upgrade_state db state
Left err -> return $ Left err
-- | Low level 'read_git'.
read_git_ :: Cmd.InstrumentDb -> SaveGit.Repo -> Maybe SaveGit.Commit
|
|
| 231
| -> IO (Either Text (Ui.State, SaveGit.Commit, [Text]))
|
|
| 232
233
234
235
236
237
238
239
240
241
| read_git_ db repo maybe_commit = SaveGit.load repo maybe_commit >>= \case
Right (state, commit, names) -> do
mapM_ Log.write logs
return $ Right (upgraded, commit, names)
where (upgraded, logs) = upgrade_state db state
Left err -> return $ Left err
-- * upgrade
|
|
| 242
| upgrade_state :: Cmd.InstrumentDb -> Ui.State -> (Ui.State, [Log.Msg])
|
|
| 243
244
245
246
247
248
249
250
251
252
253
| upgrade_state db state = Identity.runIdentity $ Log.run $ do
upgraded <- forM allocs $ \alloc -> if is_old alloc
then case upgrade_allocation db alloc of
Left err -> do
Log.warn $ "upgrading " <> pretty alloc <> ": " <> err
return alloc
Right new -> do
Log.warn $ "upgraded old alloc: " <> pretty alloc
<> " to: " <> pretty (alloc_settings new)
return new
else return alloc
|
|
| 254
255
| return $ Ui.config#UiConfig.allocations
#= UiConfig.Allocations upgraded $ state
|
|
| |
| 257
| UiConfig.Allocations allocs = Ui.config#Ui.allocations #$ state
|
|
| 258
| is_old = maybe False (Cmd.Serialize.is_old_settings . Patch.config_settings)
|
|
| 259
| . UiConfig.midi_config . UiConfig.alloc_backend
|
|
| |
| 261
262
263
| alloc_settings :: UiConfig.Allocation -> Maybe Patch.Settings
alloc_settings = fmap Patch.config_settings . UiConfig.midi_config
. UiConfig.alloc_backend
|
|
| |
| 265
266
| upgrade_allocation :: Cmd.InstrumentDb -> UiConfig.Allocation
-> Either Text UiConfig.Allocation
|
|
| 267
| upgrade_allocation db alloc =
|
|
| 268
| case Inst.lookup (UiConfig.alloc_qualified alloc) db of
|
|
| 269
270
271
| Just inst -> MidiInst.merge_defaults inst alloc
Nothing -> Left "no inst for alloc"
|
|
| |
| |
| |
| 275
| get_state_path :: Cmd.M m => m (Maybe FilePath)
|
|
| |
| |
| 278
| return $ make_state_path . snd <$> Cmd.state_save_file state
|
|
| |
| 280
281
282
283
284
285
286
287
288
289
290
291
292
| make_state_path :: Cmd.SaveFile -> FilePath
make_state_path (Cmd.SaveState fn) = fn
make_state_path (Cmd.SaveRepo repo) = state_path_for_repo repo
-- | Get a state save path based on a repo path. This is for saving a backup
-- state, or when switching from SaveRepo to SaveState.
state_path_for_repo :: SaveGit.Repo -> FilePath
state_path_for_repo repo = FilePath.replaceExtension repo ".state"
-- | Figure out a path for a save state based on the namespace.
infer_state_path :: Id.Namespace -> Cmd.State -> FilePath
infer_state_path ns state =
Cmd.path state Config.save_dir </> untxt (Id.un_namespace ns)
|
|
| |
| |
| 295
296
297
298
| default_state :: FilePath
default_state = "save.state"
-- * git serialize
|
|
| |
| 300
| -- | Save a SavePoint to the git repo in 'Cmd.state_save_file', or start a new
|
|
| 301
302
| -- one. Set the 'Cmd.state_save_file' to the repo, so I'll keep saving to
-- that repo.
|
|
| 303
304
305
| save_git :: Cmd.CmdT IO ()
save_git = save_git_as =<< get_git_path
|
|
| 306
| save_git_as :: SaveGit.Repo -- ^ Save to this repo, or create it.
|
|
| 307
| -- 'Cmd.Undo.maintain_history' will start checkpointing to it.
|
|
| 308
| -- @.git@ is appended if it doesn't already have that suffix.
|
|
| |
| 310
| save_git_as repo = do
|
|
| 311
| repo <- expand_filename repo
|
|
| 312
313
| repo <- return $ if SaveGit.git_suffix `List.isSuffixOf` repo then repo
else repo ++ SaveGit.git_suffix
|
|
| |
| 315
| let rethrow = Cmd.require_right (("save git " <> txt repo <> ": ") <>)
|
|
| 316
317
318
| commit <- case Cmd.hist_last_commit $ Cmd.state_history_config cmd_state of
Just commit -> return commit
Nothing -> do
|
|
| 319
| let user = Cmd.config_git_user $ Cmd.state_config cmd_state
|
|
| |
| 321
| rethrow =<< liftIO (SaveGit.checkpoint user repo
|
|
| 322
| (SaveGitTypes.SaveHistory state Nothing [] ["save"]))
|
|
| 323
| save <- rethrow =<< liftIO (SaveGit.set_save_tag repo commit)
|
|
| 324
| Log.notice $ "wrote save " <> showt save <> " to " <> showt repo
|
|
| 325
| set_save_file (Just (Cmd.ReadWrite, SaveRepo repo commit Nothing)) False
|
|
| |
| 327
328
| load_git :: FilePath -> Maybe SaveGit.Commit -> Cmd.CmdT IO ()
load_git repo maybe_commit = do
|
|
| 329
330
331
332
| (state, save_file) <- read_git repo maybe_commit
set_state save_file True state
read_git :: FilePath -> Maybe SaveGit.Commit
|
|
| 333
| -> Cmd.CmdT IO (Ui.State, StateSaveFile)
|
|
| 334
| read_git repo maybe_commit = do
|
|
| 335
| db <- Cmd.gets $ Cmd.config_instrument_db . Cmd.state_config
|
|
| 336
| (state, commit, names) <- Cmd.require_right
|
|
| 337
| (("load git " <> txt repo <> ": ") <>)
|
|
| 338
| =<< liftIO (read_git_ db repo maybe_commit)
|
|
| 339
| writable <- liftIO $ File.writable repo
|
|
| 340
| Log.notice $ "read from " <> showt repo <> ", at " <> pretty commit
|
|
| 341
| <> " names: " <> showt names
|
|
| 342
343
344
| <> if writable then "" else " (read-only, not setting save file)"
return (state, Just (if writable then Cmd.ReadWrite else Cmd.ReadOnly,
SaveRepo repo commit (Just names)))
|
|
| |
| 346
| -- | Revert to given save point, or the last one.
|
|
| 347
348
| revert :: Maybe String -> Cmd.CmdT IO ()
revert maybe_ref = do
|
|
| 349
| save_file <- Cmd.require "can't revert when there is no save file"
|
|
| 350
351
| =<< Cmd.gets Cmd.state_save_file
case save_file of
|
|
| 352
| (_, Cmd.SaveState fn) -> do
|
|
| 353
354
355
| whenJust maybe_ref $ \ref -> Cmd.throw $
"can't revert to a commit when the save file isn't git: "
<> txt ref
|
|
| |
| 357
| (_, Cmd.SaveRepo repo) -> revert_git repo
|
|
| 358
| Log.notice $ "revert to " <> showt save_file
|
|
| 359
360
361
| where
revert_git repo = do
save <- case maybe_ref of
|
|
| 362
| Nothing -> fmap fst $ Cmd.require "no last save"
|
|
| 363
| =<< liftIO (SaveGit.read_last_save repo Nothing)
|
|
| 364
| Just ref -> Cmd.require ("unparseable SavePoint: " <> showt ref)
|
|
| 365
| (SaveGit.ref_to_save ref)
|
|
| 366
| commit <- Cmd.require ("save ref not found: " <> showt save)
|
|
| 367
| =<< rethrow_git "revert" (SaveGit.read_save_ref repo save)
|
|
| 368
| load_git repo (Just commit)
|
|
| |
| 370
371
372
373
374
375
376
| rethrow_git :: Text -> IO a -> Cmd.CmdT IO a
rethrow_git caller io = Cmd.require_right id =<< liftIO (SaveGit.try caller io)
rethrow_io :: Text -> IO a -> Cmd.CmdT IO a
rethrow_io caller io = Cmd.require_right
(\exc -> caller <> ": " <> showt (exc :: Exception.IOException))
=<< liftIO (Exception.try io)
|
|
| |
| |
| 380
| get_git_path :: Cmd.M m => m Git.Repo
|
|
| |
| 382
| ns <- Ui.get_namespace
|
|
| 383
384
385
386
387
| state <- Cmd.get
return $ make_git_path ns state
make_git_path :: Id.Namespace -> Cmd.State -> Git.Repo
make_git_path ns state = case Cmd.state_save_file state of
|
|
| 388
| Nothing -> Cmd.path state Config.save_dir </> untxt (Id.un_namespace ns)
|
|
| |
| 390
391
392
| Just (_, Cmd.SaveState fn) ->
FilePath.replaceExtension fn SaveGit.git_suffix
Just (_, Cmd.SaveRepo repo) -> repo
|
|
| 393
394
|
default_git :: FilePath
|
|
| 395
| default_git = "save" ++ SaveGit.git_suffix
|
|
| |
| |
| 399
400
| save_allocations :: FilePath -> Cmd.CmdT IO ()
save_allocations fname = do
|
|
| 401
| allocs <- Ui.config#Ui.allocations <#> Ui.get
|
|
| 402
| fname <- expand_filename fname
|
|
| 403
| Log.notice $ "write instrument allocations to " <> showt fname
|
|
| 404
| rethrow_io "save_allocations" $ liftIO $ void $
|
|
| 405
| Serialize.serialize Cmd.Serialize.allocations_magic fname allocs
|
|
| |
| 407
408
| load_allocations :: FilePath -> Cmd.CmdT IO ()
load_allocations fname = do
|
|
| 409
| fname <- expand_filename fname
|
|
| 410
411
412
413
414
| Log.notice $ "load instrument allocations from " <> showt fname
let mkmsg err = "unserializing instrument allocations " <> showt fname
<> ": " <> pretty err
allocs <- Cmd.require_right mkmsg
=<< liftIO (Serialize.unserialize Cmd.Serialize.allocations_magic fname)
|
|
| 415
| Ui.modify_config $ Ui.allocations #= allocs
|
|
| |
| |
| |
| 419
420
421
422
423
424
425
426
427
| -- | Git repos don't checkpoint views, but because I'm accustomed to them
-- checkpointing everything else I expect the views to always be saved.
--
-- So call this when quitting or switching away from a save file to save the
-- views.
--
-- They could theoretically checkpoint view changes, but it would be
-- complicated (they mostly come from the GUI, not diff) and inefficient
-- (scrolling emits tons of them).
|
|
| 428
| save_views :: Cmd.State -> Ui.State -> IO ()
|
|
| 429
| save_views cmd_state ui_state = case Cmd.state_save_file cmd_state of
|
|
| 430
| Just (Cmd.ReadWrite, Cmd.SaveRepo repo) ->
|
|
| 431
| SaveGit.save_views repo $ Ui.state_views ui_state
|
|
| |
| |
| 434
435
436
437
438
| -- | This is just like 'Cmd.SaveFile', except SaveRepo has more data.
data SaveFile =
SaveState !FilePath
-- | The Strings are the cmd name of this commit, and only set on a git
-- load.
|
|
| 439
| | SaveRepo !SaveGit.Repo !SaveGit.Commit !(Maybe [Text])
|
|
| |
| 441
| type StateSaveFile = Maybe (Cmd.Writable, SaveFile)
|
|
| |
| 443
444
445
446
447
448
| -- | If I switch away from a repo (either to another repo or to a plain state),
-- I have to clear out all the remains of the old repo, since its Commits are
-- no longer valid.
--
-- It's really important to call this whenever you change
-- 'Cmd.state_save_file'!
|
|
| 449
| set_save_file :: StateSaveFile -> Bool -> Cmd.CmdT IO ()
|
|
| 450
| set_save_file save_file clear_history = do
|
|
| |
| 452
| when (file /= Cmd.state_save_file cmd_state) $ do
|
|
| |
| 454
455
| liftIO $ save_views cmd_state ui_state
Cmd.modify $ \state -> state
|
|
| 456
| { Cmd.state_save_file = file
|
|
| 457
458
459
460
| , Cmd.state_history = let hist = Cmd.state_history state in hist
{ Cmd.hist_past = if clear_history then []
else map clear (Cmd.hist_past hist)
, Cmd.hist_present = (Cmd.hist_present hist)
|
|
| 461
| { Cmd.hist_commit = maybe_commit }
|
|
| 462
463
464
| , Cmd.hist_future = []
}
, Cmd.state_history_config = (Cmd.state_history_config state)
|
|
| 465
| { Cmd.hist_last_commit = maybe_commit }
|
|
| |
| 467
468
469
| -- This is called both when saving and loading, so it's a good place to
-- mark that the state is synced to disk.
Cmd.modify $ \st -> st { Cmd.state_saved = Nothing }
|
|
| |
| 471
| (maybe_commit, file) = case save_file of
|
|
| 472
| Nothing -> (Nothing, Nothing)
|
|
| 473
474
475
476
| Just (writable, save) -> case save of
SaveState fname -> (Nothing, Just (writable, Cmd.SaveState fname))
SaveRepo repo commit _ ->
(Just commit, Just (writable, Cmd.SaveRepo repo))
|
|
| 477
478
| clear entry = entry { Cmd.hist_commit = Nothing }
|
|
| 479
| set_state :: StateSaveFile -> Bool -> Ui.State -> Cmd.CmdT IO ()
|
|
| 480
481
| set_state save_file clear_history state = do
set_save_file save_file clear_history
|
|
| |
| 483
| Cmd.modify $ Cmd.reinit_state (Cmd.empty_history_entry state)
|
|
| 484
485
486
| -- Names is only set on a git load. This will cause "Cmd.Undo" to clear
-- out the history.
case save_file of
|
|
| 487
| Just (_, SaveRepo _ commit (Just names)) -> Cmd.modify $ \st -> st
|
|
| 488
489
490
491
| { Cmd.state_history = (Cmd.state_history st)
{ Cmd.hist_last_cmd = Just $ Cmd.Load (Just commit) names }
}
_ -> return ()
|
|
| 492
493
| old <- Ui.get
Ui.put $ Ui.clear $
|
|
| 494
| Transform.replace_namespace Config.clip_namespace old state
|
|
| 495
| root <- case Ui.config_root (Ui.state_config state) of
|
|
| 496
| Nothing -> return Nothing
|
|
| 497
498
| Just root -> Seq.head . Map.keys <$> Ui.views_of root
let focused = msum [root, Seq.head $ Map.keys (Ui.state_views state)]
|
|
| 499
| whenJust focused Cmd.focus
|
|