packages feed

word-wrap 0.4.1 → 0.5

raw patch · 4 files changed

+146/−14 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Text.Wrap: FillAfterFirst :: FillScope
+ Text.Wrap: FillAll :: FillScope
+ Text.Wrap: FillIndent :: Int -> FillStrategy
+ Text.Wrap: FillPrefix :: Text -> FillStrategy
+ Text.Wrap: NoFill :: FillStrategy
+ Text.Wrap: [fillScope] :: WrapSettings -> FillScope
+ Text.Wrap: [fillStrategy] :: WrapSettings -> FillStrategy
+ Text.Wrap: data FillScope
+ Text.Wrap: data FillStrategy
+ Text.Wrap: instance GHC.Classes.Eq Text.Wrap.FillScope
+ Text.Wrap: instance GHC.Classes.Eq Text.Wrap.FillStrategy
+ Text.Wrap: instance GHC.Read.Read Text.Wrap.FillScope
+ Text.Wrap: instance GHC.Read.Read Text.Wrap.FillStrategy
+ Text.Wrap: instance GHC.Show.Show Text.Wrap.FillScope
+ Text.Wrap: instance GHC.Show.Show Text.Wrap.FillStrategy
- Text.Wrap: WrapSettings :: Bool -> Bool -> WrapSettings
+ Text.Wrap: WrapSettings :: Bool -> Bool -> FillStrategy -> FillScope -> WrapSettings

Files

CHANGELOG.md view
@@ -1,4 +1,14 @@ +0.5+===++API changes:+ * Line-wrapping now supports optional "filling", i.e., placing prefix+   strings at the start of wrapped lines. The behavior of filling is+   configured by new `WrapSettings` fields: `fillStrategy` (what to+   add to filled lines) and `fillScope` (which lines to affect with+   filling). Thanks to Brent Yorgey for this work!+ 0.4.1 ===== 
src/Text/Wrap.hs view
@@ -1,5 +1,7 @@ module Text.Wrap-  ( WrapSettings(..)+  ( FillStrategy(..)+  , FillScope(..)+  , WrapSettings(..)   , defaultWrapSettings    , wrapTextToLines@@ -11,6 +13,27 @@ import Data.Char (isSpace) import qualified Data.Text as T +-- | How should wrapped lines be filled (i.e. what kind of prefix+--   should be attached?)+data FillStrategy+  = NoFill             -- ^ Don't do any filling (default)+  | FillIndent Int     -- ^ Indent by this many spaces+  | FillPrefix T.Text  -- ^ Prepend this text+  deriving (Eq, Show, Read)++fillWidth :: FillStrategy -> Int+fillWidth NoFill         = 0+fillWidth (FillIndent n) = n+fillWidth (FillPrefix t) = T.length t++-- | To which lines should the fill strategy be applied?+data FillScope+  = FillAfterFirst     -- ^ Apply any fill prefix only to lines after+                       --   the first line (default)+  | FillAll            -- ^ Apply any fill prefix to all lines, even+                       --   if there is only one line+  deriving (Eq, Show, Read)+ -- | Settings to control how wrapping is performed. data WrapSettings =     WrapSettings { preserveIndentation :: Bool@@ -19,6 +42,12 @@                  , breakLongWords :: Bool                  -- ^ Whether to break in the middle of the first word                  -- on a line when that word exceeds the wrapping width.+                 , fillStrategy        :: FillStrategy+                 -- ^ What kind of prefix should be applied to lines+                 --   after wrapping? (default: none)+                 , fillScope           :: FillScope+                 -- ^ To which lines should the fill strategy be applied?+                 --   (default: all but the first)                  }                  deriving (Eq, Show, Read) @@ -26,12 +55,35 @@ defaultWrapSettings =     WrapSettings { preserveIndentation = False                  , breakLongWords = False+                 , fillStrategy = NoFill+                 , fillScope = FillAfterFirst                  } +-- | Apply a function to the portion of a list of lines indicated by+--   the 'FillScope'.+withScope :: FillScope -> (a -> a) -> [a] -> [a]+withScope FillAfterFirst = onTail+withScope FillAll        = map++-- | Map a function over the tail of a list.+onTail :: (a -> a) -> [a] -> [a]+onTail _ []     = []+onTail f (a:as) = a : map f as++-- | Apply the fill specified in the 'WrapSettings' to a list of lines.+applyFill :: WrapSettings -> [T.Text] -> [T.Text]+applyFill settings =+    let scope = fillScope settings+    in case fillStrategy settings of+           NoFill       -> id+           FillIndent n -> withScope scope (T.append (T.replicate n (T.pack " ")))+           FillPrefix t -> withScope scope (T.append t)+ -- | Wrap text at the specified width. Newlines and whitespace in the--- input text are preserved. Returns the lines of text in wrapped form.--- New lines introduced due to wrapping will have leading whitespace--- stripped.+-- input text are preserved. Returns the lines of text in wrapped+-- form.  New lines introduced due to wrapping will have leading+-- whitespace stripped prior to having any fill applied.  Preserved+-- indentation is always placed before any fill. wrapTextToLines :: WrapSettings -> Int -> T.Text -> [T.Text] wrapTextToLines settings amt s =     concat $ fmap (wrapLine settings amt) $ T.lines s@@ -73,20 +125,28 @@          -- ^ A single line of text.          -> [T.Text] wrapLine settings limit t =-    let go _ []     = [T.empty]+    let restFillWidth      = fillWidth (fillStrategy settings)+        firstLineFillWidth = if fillScope settings == FillAll then restFillWidth else 0++        firstLineLimit = limit - T.length indent - firstLineFillWidth+        restLimit      = limit - T.length indent - restFillWidth++        go _ []     = [T.empty]         go _ [WS _] = [T.empty]-        go lim ts =-            let (firstLine, maybeRest) = breakTokens settings lim ts+        go isFirstLine ts =+            let lim = if isFirstLine then firstLineLimit else restLimit+                (firstLine, maybeRest) = breakTokens settings lim ts                 firstLineText = T.stripEnd $ T.concat $ fmap tokenContent firstLine             in case maybeRest of-                Nothing -> [firstLineText]-                Just rest -> firstLineText : go lim rest+                Nothing   -> [firstLineText]+                Just rest -> firstLineText : go False rest         (indent, modifiedText) = if preserveIndentation settings                                  then let i = T.takeWhile isSpace t                                       in (T.take (limit - 1) i, T.drop (T.length i) t)                                  else (T.empty, t)-        result = go (limit - T.length indent) (tokenize modifiedText)-    in (indent <>) <$> result++        result = go True (tokenize modifiedText)+    in map (indent <>) . applyFill settings $ result  -- | Break a token sequence so that all tokens up to but not exceeding -- a length limit are included on the left, and if any remain on the
tests/Main.hs view
@@ -64,3 +64,65 @@                                   }       wrapTextToLines s 4 "           foo bar"         `shouldBe` ["   f", "   o", "   o", "   b", "   a", "   r"]++    it "indents all but the first line" $ do+      let s = defaultWrapSettings { fillStrategy = FillIndent 2 }+      wrapTextToLines s 8 "Hello there, World!"+        `shouldBe` ["Hello", "  there,", "  World!"]++    it "indents all lines" $ do+      let s = defaultWrapSettings { fillStrategy = FillIndent 2+                                  , fillScope = FillAll+                                  }+      wrapTextToLines s 8 "Hello there, World!"+        `shouldBe` ["  Hello", "  there,", "  World!"]++    it "fills all lines but the first with a prefix" $ do+      let s = defaultWrapSettings { fillStrategy = FillPrefix "- " }+      wrapTextToLines s 8 "Hello there, World!"+        `shouldBe` ["Hello", "- there,", "- World!"]++    it "fills all lines with a prefix" $ do+      let s = defaultWrapSettings { fillStrategy = FillPrefix "- "+                                  , fillScope = FillAll+                                  }+      wrapTextToLines s 8 "Hello there, World!"+        `shouldBe` ["- Hello", "- there,", "- World!"]++    it "takes fill width into account" $ do+      let s = defaultWrapSettings { fillStrategy = FillPrefix "- " }+      wrapTextToLines s 3 "a b c d"+        `shouldBe` ["a b", "- c", "- d"]++    it "takes fill indent into account" $ do+      let s = defaultWrapSettings { fillStrategy = FillIndent 2 }+      wrapTextToLines s 3 "a b c d"+        `shouldBe` ["a b", "  c", "  d"]++    it "takes fill width into account on all lines" $ do+      let s = defaultWrapSettings { fillStrategy = FillPrefix "- "+                                  , fillScope = FillAll+                                  }+      wrapTextToLines s 3 "a b c d"+        `shouldBe` ["- a", "- b", "- c", "- d"]++    it "takes fill indent into account on all lines" $ do+      let s = defaultWrapSettings { fillStrategy = FillIndent 2+                                  , fillScope = FillAll+                                  }+      wrapTextToLines s 3 "a b c d"+        `shouldBe` ["  a", "  b", "  c", "  d"]++    it "places fill after preserved indent" $ do+      let s = defaultWrapSettings { preserveIndentation = True+                                  , fillStrategy = FillPrefix "- "+                                  }+      wrapTextToLines s 5 "  a b c d"+        `shouldBe` ["  a b", "  - c", "  - d"]++    it "places fill indent after preserved indent" $ do+      let s = defaultWrapSettings { preserveIndentation = True+                                  , fillStrategy = FillIndent 2+                                  }+      wrapTextToLines s 5 "  a b c d"+        `shouldBe` ["  a b", "    c", "    d"]
word-wrap.cabal view
@@ -1,5 +1,5 @@ name:                word-wrap-version:             0.4.1+version:             0.5 synopsis:            A library for word-wrapping description:         A library for wrapping long lines of text. license:             BSD3@@ -9,7 +9,7 @@ copyright:           2017 Jonathan Daugherty category:            Text build-type:          Simple-cabal-version:       >=1.18+cabal-version:       1.18 Homepage:            https://github.com/jtdaugherty/word-wrap/ Bug-reports:         https://github.com/jtdaugherty/word-wrap/issues @@ -28,7 +28,7 @@   hs-source-dirs:      src   default-language:    Haskell2010   ghc-options:         -Wall-  build-depends:       base < 5,+  build-depends:       base >= 4.8 && < 5,                        text  benchmark word-wrap-benchmarks