From eb62801c3c23f196ddef66c183231906ab4ef51c Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt Date: Tue, 26 Nov 2024 20:43:28 +0100 Subject: [PATCH] Atomically write speaker notes --- CHANGELOG.md | 8 ++++++++ README.md | 8 +++----- lib/Patat/Presentation/Comments.hs | 20 ++++++++++++++++---- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86e3880..a6a7523 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## unreleased + + * Use a temporary file to atomically write speaker notes. + + We weren't writing the file all-at-once before, so if you were using a + simple tool like `tail -F` before, this could cause some speaker notes to + not be displayed. + ## 0.13.0.0 (2024-10-30) * Incrementally display output of `eval` commands (#132) diff --git a/README.md b/README.md index f608525..6c81c4e 100644 --- a/README.md +++ b/README.md @@ -791,13 +791,11 @@ patat: ``` Then, you can display these in a second terminal (presumably on a second -monitor) by just displaying this file whenever it changes. [entr] is one -way to do that: - -[entr]: http://eradman.com/entrproject/ +monitor) by just displaying this file whenever it changes. `tail` is a primitive +way of doing that: ```bash -echo /tmp/notes.txt | entr -s 'clear; cat /tmp/notes.txt' +tail -F /tmp/notes.txt ``` Alternatively, just use a second `patat` instance with `--watch` enabled: diff --git a/lib/Patat/Presentation/Comments.hs b/lib/Patat/Presentation/Comments.hs index 92dbdcd..f49150c 100644 --- a/lib/Patat/Presentation/Comments.hs +++ b/lib/Patat/Presentation/Comments.hs @@ -35,7 +35,8 @@ import qualified Data.Yaml as Yaml import Patat.EncodingFallback (EncodingFallback) import qualified Patat.EncodingFallback as EncodingFallback import Patat.Presentation.Settings -import System.Directory (removeFile) +import System.Directory (removeFile, renameFile) +import System.FilePath (splitFileName, ()) import qualified System.IO as IO import qualified Text.Pandoc as Pandoc @@ -136,9 +137,20 @@ writeSpeakerNotes :: SpeakerNotesHandle -> EncodingFallback -> SpeakerNotes -> IO () writeSpeakerNotes h encodingFallback sn = do change <- IORef.atomicModifyIORef' (snhActive h) $ \old -> (sn, old /= sn) - when change $ IO.withFile (snsFile $ snhSettings h) IO.WriteMode $ \ioh -> - EncodingFallback.withHandle ioh encodingFallback $ - T.hPutStr ioh $ speakerNotesToText sn + when change $ do + -- Write the file and then rename it to get atomic replace. + IO.withFile speakerNotesTmpPath IO.WriteMode $ \ioh -> + EncodingFallback.withHandle ioh encodingFallback $ + T.hPutStr ioh $ speakerNotesToText sn + renameFile speakerNotesTmpPath speakerNotesPath + where + speakerNotesPath = snsFile $ snhSettings h + speakerNotesTmpPath = + -- We only get atomic rename if we're on the same filesystem. I think + -- we can assume that the directory the speaker notes are in is + -- | entr -s 'clear; cat /tmp/notes.txt'writable. + let (dir, name) = splitFileName speakerNotesPath in + dir ".tmp." <> name --------------------------------------------------------------------------------