packages feed

patat 0.9.0.0 → 0.9.2.0

raw patch · 8 files changed

+175/−52 lines, 8 filesdep ~skylightingPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: skylighting

API changes (from Hackage documentation)

+ Patat.EncodingFallback: NoFallback :: EncodingFallback
+ Patat.EncodingFallback: Utf8Fallback :: EncodingFallback
+ Patat.EncodingFallback: data EncodingFallback
+ Patat.EncodingFallback: instance GHC.Classes.Eq Patat.EncodingFallback.EncodingFallback
+ Patat.EncodingFallback: instance GHC.Show.Show Patat.EncodingFallback.EncodingFallback
+ Patat.EncodingFallback: readFile :: FilePath -> IO (EncodingFallback, Text)
+ Patat.EncodingFallback: withHandle :: Handle -> EncodingFallback -> IO a -> IO a
+ Patat.Presentation: [pEncodingFallback] :: Presentation -> !EncodingFallback
+ Patat.Presentation.Internal: [pEncodingFallback] :: Presentation -> !EncodingFallback
- Patat.Presentation: Presentation :: !FilePath -> ![Inline] -> ![Inline] -> !PresentationSettings -> !Seq Slide -> !Seq Breadcrumbs -> !Index -> !SyntaxMap -> Presentation
+ Patat.Presentation: Presentation :: !FilePath -> !EncodingFallback -> ![Inline] -> ![Inline] -> !PresentationSettings -> !Seq Slide -> !Seq Breadcrumbs -> !Index -> !SyntaxMap -> Presentation
- Patat.Presentation.Internal: Presentation :: !FilePath -> ![Inline] -> ![Inline] -> !PresentationSettings -> !Seq Slide -> !Seq Breadcrumbs -> !Index -> !SyntaxMap -> Presentation
+ Patat.Presentation.Internal: Presentation :: !FilePath -> !EncodingFallback -> ![Inline] -> ![Inline] -> !PresentationSettings -> !Seq Slide -> !Seq Breadcrumbs -> !Index -> !SyntaxMap -> Presentation
- Patat.Presentation.SpeakerNotes: write :: Handle -> SpeakerNotes -> IO ()
+ Patat.Presentation.SpeakerNotes: write :: Handle -> EncodingFallback -> SpeakerNotes -> IO ()

Files

CHANGELOG.md view
@@ -1,5 +1,34 @@ # Changelog +## 0.9.2.0 (2023-09-26)++ *  Read configuration from XDG standard directory (#146)++    The per-user patat configuration file was `$HOME/.patat.yaml`,+    which does not follow the XDG standard.  We now support+    `$XDG_CONFIG_DIRECTORY/patat/config.yaml` (typically `$XDG_CONFIG_DIRECTORY`+    is set to `$HOME/.config`) which is compliant with the standard.++    Note that `$HOME/.patat.yaml` is still supported for backward-compatibility,+    but anything in `$XDG_CONFIG_DIRECTORY` takes precedence.++ *  Support filenames in bash completion (#145) (#126)++## 0.9.1.0 (2023-09-25)++ *  Fall back to forcing UTF-8 if decoding fails (#144) (#127)++    When we try to read a file that is encoded in UTF-8, and the system locale+    is not set to UTF-8, the GHC runtime system will throw an error.++    While this typically indicates that the user should update their system+    locale using e.g. the `LANG` environment variable, we want to provide a good+    initial experience for people unfamiliar with this, and in 2023 it's+    reasonable to assume files may be encoded in UTF-8.++ *  Dependency updates:+     -  Bump `skylighting` upper bound to 0.15 (#143)+ ## 0.9.0.0 (2023-09-13)   *  Add proper support for speaker notes (#142)
README.md view
@@ -1,34 +1,31 @@-patat-=====+🥔 patat+========  ![CI](https://github.com/jaspervdj/patat/workflows/CI/badge.svg) [![Hackage](https://img.shields.io/hackage/v/patat.svg)](https://hackage.haskell.org/package/patat) [![GitHub tag](https://img.shields.io/github/tag/jaspervdj/patat.svg)]() -`patat` (**P**resentations **A**top **T**he **A**NSI **T**erminal) is a small-tool that allows you to show presentations using only an ANSI terminal.  It does-not require `ncurses`.--Features:+`patat` (**P**resentations **A**top **T**he **A**NSI **T**erminal) is a+feature-rich presentation tool that runs in the terminal. -- Leverages the great [Pandoc] library to support many input formats including-  [Literate Haskell].-- Supports [smart slide splitting](#input-format).-- Slides can be split up into [multiple fragments](#fragmented-slides)-- There is a [live reload](#running) mode.-- Supports [evaluating code snippets and showing the result](#evaluating-code).+- Understands most markdown extensions and many other input formats+  (rST, Org-mode...) by building on top of [Pandoc].+- [Evaluate code snippets and show the result](#evaluating-code).+- Syntax highlighting for nearly one hundred languages generated from [Kate]+  syntax files.+- [Automatically reload](#running) your slides as you edit them. - Display [speaker notes](#speaker-notes) in a second window or monitor.-- [Auto advancing](#auto-advancing) with configurable delay.+- [Incremental slide display](#fragmented-slides). - Experimental [images](#images) support.+- Supports [smart slide splitting](#input-format).+- [Auto advancing](#auto-advancing) with configurable delay. - Optionally [re-wrapping](#line-wrapping) text to terminal width with proper   indentation.-- Syntax highlighting for nearly one hundred languages generated from [Kate]-  syntax files. - [Theming](#theming) support including 24-bit RGB.-- Written in [Haskell].+- Hihgly portable as it only requires an ANSI terminal as opposed to+  something like `ncurses`.  ![screenshot](extra/demo.gif?raw=true)  [Kate]: https://kate-editor.org/-[Haskell]: http://haskell.org/ [Pandoc]: http://pandoc.org/  Table of Contents@@ -67,6 +64,7 @@ - Ubuntu: <https://packages.ubuntu.com/bionic/patat> - openSUSE: <https://build.opensuse.org/package/show/openSUSE:Factory:ARM/patat> - Fedora: <https://src.fedoraproject.org/rpms/patat>+- NixOS: <https://search.nixos.org/packages?show=haskellPackages.patat>  You can also find generic Linux and Mac OS binaries here: <https://github.com/jaspervdj/patat/releases>.@@ -247,10 +245,14 @@ -------------  `patat` is fairly configurable.  The configuration is done using [YAML].  There-are two places where you can put your configuration:+are several places where you can put your configuration. -1. In the presentation file itself, using the [Pandoc metadata header].-2. In `$HOME/.patat.yaml`+1.  For per-user configuration you can use+    `$XDG_CONFIG_DIRECTORY/patat/config.yaml`+    (typically `$HOME/.config/patat/config.yaml`) or `$HOME/.patat.yaml`.+2.  In the presentation file itself, using the [Pandoc metadata header].+    These settings take precedence over anything specified in the per-user+    configuration file.  [YAML]: http://yaml.org/ [Pandoc metadata header]: http://pandoc.org/MANUAL.html#extension-yaml_metadata_block@@ -268,7 +270,8 @@ Hello world. ``` -Or we can use a normal presentation and have the following `$HOME/.patat.yaml`:+Or we can use a "plain" presentation and have the following+`$XDG_CONFIG_DIRECTORY/patat/config.yaml`:      key: val 
+ lib/Patat/EncodingFallback.hs view
@@ -0,0 +1,56 @@+-- | When we try to read a file that is encoded in UTF-8, and the system locale+-- is not set to UTF-8, the GHC runtime system will throw an error:+--+-- <https://github.com/jaspervdj/patat/issues/127>+--+-- However, we don't want to force people to use UTF-8 for everything.  So what+-- we do is provide a replacement readFile, which first tries to read the file+-- in the system locale, and then falls back to forcing UTF-8.+--+-- If we forced UTF-8, we also want to propagate that to the output handle;+-- otherwise will get errors when we try to display these characters; so+-- withHandle should be used on the output handle (typically stdout).+module Patat.EncodingFallback+    ( EncodingFallback (..)+    , readFile+    , withHandle+    ) where+++--------------------------------------------------------------------------------+import           Control.Exception (bracket, throwIO)+import           Control.Monad     (when)+import qualified Data.Text         as T+import qualified Data.Text.IO      as T+import           Prelude           hiding (readFile)+import qualified System.IO         as IO+import qualified System.IO.Error   as IO+++--------------------------------------------------------------------------------+data EncodingFallback = NoFallback | Utf8Fallback+    deriving (Eq, Show)+++--------------------------------------------------------------------------------+readFile :: FilePath -> IO (EncodingFallback, T.Text)+readFile path = IO.catchIOError readSystem $ \ioe -> do+    when (IO.isDoesNotExistError ioe) $ throwIO ioe  -- Don't retry on these+    readUtf8+  where+    readSystem = ((,) NoFallback <$> T.readFile path)+    readUtf8   = IO.withFile path IO.ReadMode $ \h -> do+        IO.hSetEncoding h IO.utf8_bom+        (,) Utf8Fallback <$> T.hGetContents h+++--------------------------------------------------------------------------------+withHandle :: IO.Handle -> EncodingFallback -> IO a -> IO a+withHandle _ NoFallback   mx = mx+withHandle h Utf8Fallback mx = bracket+    (do+        mbOld <- IO.hGetEncoding h+        IO.hSetEncoding h IO.utf8+        pure mbOld)+    (\mbOld -> traverse (IO.hSetEncoding h) mbOld)+    (\_ -> mx)
lib/Patat/Main.hs view
@@ -22,6 +22,7 @@ import qualified Options.Applicative             as OA import qualified Options.Applicative.Help.Pretty as OA.PP import           Patat.AutoAdvance+import qualified Patat.EncodingFallback          as EncodingFallback import qualified Patat.Images                    as Images import           Patat.Presentation import qualified Patat.Presentation.SpeakerNotes as SpeakerNotes@@ -51,6 +52,7 @@ parseOptions = Options     <$> (OA.optional $ OA.strArgument $             OA.metavar "FILENAME" <>+            OA.action  "file" <>  -- For bash file completion             OA.help    "Input file")     <*> (OA.switch $             OA.long    "force" <>@@ -145,9 +147,11 @@     withMaybeHandle SpeakerNotes.with         (psSpeakerNotes $ pSettings pres) $ \speakerNotes -> -        if oDump options-            then dumpPresentation pres-            else interactiveLoop options images speakerNotes pres+        if oDump options then+            EncodingFallback.withHandle IO.stdout (pEncodingFallback pres) $+            dumpPresentation pres+        else+            interactiveLoop options images speakerNotes pres   where     interactiveLoop         :: Options -> Maybe Images.Handle -> Maybe SpeakerNotes.Handle@@ -168,8 +172,9 @@          let loop :: Presentation -> Maybe String -> IO ()             loop pres mbError = do-                for_ speakerNotes $ \sn ->-                    SpeakerNotes.write sn $ activeSpeakerNotes pres+                for_ speakerNotes $ \sn -> SpeakerNotes.write sn+                    (pEncodingFallback pres)+                    (activeSpeakerNotes pres)                  size <- getDisplaySize pres                 let display = case mbError of@@ -180,7 +185,9 @@                 Ansi.clearScreen                 Ansi.setCursorPosition 0 0                 cleanup <- case display of-                    DisplayDoc doc -> PP.putDoc doc $> mempty+                    DisplayDoc doc -> EncodingFallback.withHandle+                        IO.stdout (pEncodingFallback pres) $+                        PP.putDoc doc $> mempty                     DisplayImage path -> case images of                         Nothing -> do                             PP.putDoc $ displayPresentationError
lib/Patat/Presentation/Internal.hs view
@@ -45,6 +45,7 @@ import           Data.Sequence.Extended          (Seq) import qualified Data.Sequence.Extended          as Seq import qualified Data.Text                       as T+import           Patat.EncodingFallback          (EncodingFallback) import qualified Patat.Presentation.Instruction  as Instruction import qualified Patat.Presentation.SpeakerNotes as SpeakerNotes import qualified Patat.Theme                     as Theme@@ -60,14 +61,15 @@  -------------------------------------------------------------------------------- data Presentation = Presentation-    { pFilePath       :: !FilePath-    , pTitle          :: ![Pandoc.Inline]-    , pAuthor         :: ![Pandoc.Inline]-    , pSettings       :: !PresentationSettings-    , pSlides         :: !(Seq Slide)-    , pBreadcrumbs    :: !(Seq Breadcrumbs)  -- One for each slide.-    , pActiveFragment :: !Index-    , pSyntaxMap      :: !Skylighting.SyntaxMap+    { pFilePath         :: !FilePath+    , pEncodingFallback :: !EncodingFallback+    , pTitle            :: ![Pandoc.Inline]+    , pAuthor           :: ![Pandoc.Inline]+    , pSettings         :: !PresentationSettings+    , pSlides           :: !(Seq Slide)+    , pBreadcrumbs      :: !(Seq Breadcrumbs)  -- One for each slide.+    , pActiveFragment   :: !Index+    , pSyntaxMap        :: !Skylighting.SyntaxMap     } deriving (Show)  
lib/Patat/Presentation/Read.hs view
@@ -24,8 +24,9 @@ import qualified Data.Sequence.Extended          as Seq import qualified Data.Text                       as T import qualified Data.Text.Encoding              as T-import qualified Data.Text.IO                    as T import qualified Data.Yaml                       as Yaml+import           Patat.EncodingFallback          (EncodingFallback)+import qualified Patat.EncodingFallback          as EncodingFallback import           Patat.Eval                      (eval) import           Patat.Presentation.Fragment import qualified Patat.Presentation.Instruction  as Instruction@@ -33,8 +34,10 @@ import qualified Patat.Presentation.SpeakerNotes as SpeakerNotes import           Prelude import qualified Skylighting                     as Skylighting-import           System.Directory                (doesFileExist,-                                                  getHomeDirectory)+import           System.Directory                (XdgDirectory (XdgConfig),+                                                  doesFileExist,+                                                  getHomeDirectory,+                                                  getXdgDirectory) import           System.FilePath                 (splitFileName, takeExtension,                                                   (</>)) import qualified Text.Pandoc.Error               as Pandoc@@ -45,10 +48,15 @@ readPresentation :: FilePath -> IO (Either String Presentation) readPresentation filePath = runExceptT $ do     -- We need to read the settings first.-    src          <- liftIO $ T.readFile filePath+    (enc, src)   <- liftIO $ EncodingFallback.readFile filePath     homeSettings <- ExceptT readHomeSettings+    xdgSettings  <- ExceptT readXdgSettings     metaSettings <- ExceptT $ return $ readMetaSettings src-    let settings = metaSettings <> homeSettings <> defaultPresentationSettings+    let settings =+            metaSettings <>+            xdgSettings  <>+            homeSettings <>+            defaultPresentationSettings      syntaxMap <- ExceptT $ readSyntaxMap $ fromMaybe [] $         psSyntaxDefinitions settings@@ -61,7 +69,7 @@         Right x -> return x      pres <- ExceptT $ pure $-        pandocToPresentation filePath settings syntaxMap doc+        pandocToPresentation filePath enc settings syntaxMap doc     liftIO $ eval pres   where     ext = takeExtension filePath@@ -112,10 +120,9 @@  -------------------------------------------------------------------------------- pandocToPresentation-    :: FilePath -> PresentationSettings -> Skylighting.SyntaxMap-    -> Pandoc.Pandoc-    -> Either String Presentation-pandocToPresentation pFilePath pSettings pSyntaxMap+    :: FilePath -> EncodingFallback -> PresentationSettings+    -> Skylighting.SyntaxMap -> Pandoc.Pandoc -> Either String Presentation+pandocToPresentation pFilePath pEncodingFallback pSettings pSyntaxMap         pandoc@(Pandoc.Pandoc meta _) = do     let !pTitle          = case Pandoc.docTitle meta of             []    -> [Pandoc.Str . T.pack . snd $ splitFileName pFilePath]@@ -162,7 +169,20 @@ readHomeSettings :: IO (Either String PresentationSettings) readHomeSettings = do     home <- getHomeDirectory-    let path = home </> ".patat.yaml"+    readSettings $ home </> ".patat.yaml"+++--------------------------------------------------------------------------------+-- | Read settings from "$XDG_CONFIG_DIRECTORY/patat/config.yaml".+readXdgSettings :: IO (Either String PresentationSettings)+readXdgSettings =+    getXdgDirectory XdgConfig ("patat" </> "config.yaml") >>= readSettings+++--------------------------------------------------------------------------------+-- | Read settings from the specified path, if it exists.+readSettings :: FilePath -> IO (Either String PresentationSettings)+readSettings path = do     exists <- doesFileExist path     if not exists         then return (Right mempty)
lib/Patat/Presentation/SpeakerNotes.hs view
@@ -25,7 +25,10 @@ import           Data.List              (intersperse) import qualified Data.Text              as T import qualified Data.Text.IO           as T+import           Patat.EncodingFallback (EncodingFallback)+import qualified Patat.EncodingFallback as EncodingFallback import           System.Directory       (removeFile)+import qualified System.IO              as IO import qualified Text.Pandoc            as Pandoc  @@ -95,10 +98,12 @@   ---------------------------------------------------------------------------------write :: Handle -> SpeakerNotes -> IO ()-write h sn = do+write :: Handle -> EncodingFallback -> SpeakerNotes -> IO ()+write h encodingFallback sn = do     change <- IORef.atomicModifyIORef' (hActive h) $ \old -> (sn, old /= sn)-    when change $ T.writeFile (sFile $ hSettings h) $ toText sn+    when change $ IO.withFile (sFile $ hSettings h) IO.WriteMode $ \ioh ->+        EncodingFallback.withHandle ioh encodingFallback $+        T.hPutStr ioh $ toText sn   --------------------------------------------------------------------------------
patat.cabal view
@@ -1,5 +1,5 @@ Name:                patat-Version:             0.9.0.0+Version:             0.9.2.0 Synopsis:            Terminal-based presentations using Pandoc Description:         Terminal-based presentations using Pandoc. License:             GPL-2@@ -47,7 +47,7 @@     pandoc               >= 3.1  && < 3.2,     pandoc-types         >= 1.23 && < 1.24,     process              >= 1.6  && < 1.7,-    skylighting          >= 0.10 && < 0.14,+    skylighting          >= 0.10 && < 0.15,     terminal-size        >= 0.3  && < 0.4,     text                 >= 1.2  && < 2.1,     time                 >= 1.4  && < 1.13,@@ -66,6 +66,7 @@   Exposed-modules:     Patat.AutoAdvance     Patat.Cleanup+    Patat.EncodingFallback     Patat.Eval     Patat.Images     Patat.Images.Internal