packages feed

word-wrap (empty) → 0.1

raw patch · 7 files changed

+206/−0 lines, 7 filesdep +basedep +hspecdep +textsetup-changed

Dependencies added: base, hspec, text, word-wrap

Files

+ ChangeLog.md view
@@ -0,0 +1,5 @@++0.1+===++* First version.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2017, Jonathan Daugherty++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Jonathan Daugherty nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,4 @@+word-wrap+=========++This library provides text-wrapping functionality.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Text/Wrap.hs view
@@ -0,0 +1,99 @@+module Text.Wrap+  ( wrapTextToLines+  , wrapText+  )+where++import Data.Char (isSpace)+import qualified Data.Text as 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.+wrapTextToLines :: Int -> T.Text -> [T.Text]+wrapTextToLines amt s = concat $ fmap (wrapLine amt) $ T.lines s++-- | Like 'wrapTextToLines', but returns the wrapped text reconstructed+-- with newlines inserted at wrap points.+wrapText :: Int -> T.Text -> T.Text+wrapText amt s = T.intercalate (T.pack "\n") $ wrapTextToLines amt s++data Token = WS T.Text | NonWS T.Text+           deriving (Show)++tokenLength :: Token -> Int+tokenLength (WS t) = T.length t+tokenLength (NonWS t) = T.length t++tokenContent :: Token -> T.Text+tokenContent (WS t) = t+tokenContent (NonWS t) = t++-- | Tokenize text into whitespace and non-whitespace chunks.+tokenize :: T.Text -> [Token]+tokenize t | T.null t = []+tokenize t =+    let leadingWs = T.takeWhile isSpace t+        leadingNonWs = T.takeWhile (not . isSpace) t+        tok = if T.null leadingWs+              then NonWS leadingNonWs+              else WS leadingWs+    in tok : tokenize (T.drop (tokenLength tok) t)++-- | Wrap a single line of text into a list of lines that all satisfy+-- the wrapping width.+wrapLine :: Int+         -- ^ The wrapping width.+         -> T.Text+         -- ^ A single line of text.+         -> [T.Text]+wrapLine limit t =+    let go []     = []+        go [WS _] = []+        go [tok]  = [tokenContent tok]+        go ts =+            let (firstLine, maybeRest) = breakTokens limit ts+                firstLineText = T.stripEnd $ T.concat $ fmap tokenContent firstLine+            in case maybeRest of+                Nothing -> [firstLineText]+                Just rest -> firstLineText : go rest+    in go (tokenize t)++-- | 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+-- right, return Just those too (or Nothing if there weren't any). If+-- this breaks a sequence at at point where the next token after the+-- break point is whitespace, that whitespace token is removed.+breakTokens :: Int -> [Token] -> ([Token], Maybe [Token])+breakTokens _ [] = ([], Nothing)+breakTokens _ [t] = ([t], Nothing)+breakTokens limit ts =+    -- Take enough tokens until we reach the point where taking more+    -- would exceed the line length.+    let go _ [] = []+        -- If the line starts with a token that itself exceeds the+        -- limit, just take that single token as the only one on this+        -- line.+        go acc (tok:_) | acc == 0 && tokenLength tok > limit = [tok]+        -- Otherwise take a token if its length plus the accumulator+        -- doesn't exceed the limit.+        go acc (tok:toks) =+            if tokenLength tok + acc <= limit+            then tok : go (acc + tokenLength tok) toks+            else []++        -- Allowed tokens are the ones we keep on this line.+        allowed = go 0 ts+        -- The rest go on the next line, to be wrapped again.+        rest = maybeTrim $ drop (length allowed) ts++        -- Trim leading whitespace on wrapped lines.+        maybeTrim [] = []+        maybeTrim (WS _:toks) = toks+        maybeTrim toks = toks++        result = if null rest+                 then (allowed, Nothing)+                 else (allowed, Just rest)+    in result
+ tests/Main.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE OverloadedStrings #-}+module Main where++import Test.Hspec++import Text.Wrap++main :: IO ()+main = hspec $ do+    it "leaves short lines untouched" $ do+      wrapTextToLines 5 "foo" `shouldBe` ["foo"]++    it "wraps long lines" $ do+      wrapTextToLines 7 "Hello, World!" `shouldBe` ["Hello,", "World!"]++    it "preserves leading whitespace" $ do+      wrapTextToLines 10 "  Hello, World!" `shouldBe` ["  Hello,", "World!"]++    it "honors preexisting newlines" $ do+      wrapTextToLines 100 "Hello,\nWorld!" `shouldBe` ["Hello,", "World!"]++    it "wraps long lines without truncation" $ do+      wrapTextToLines 2 "Hello, World!" `shouldBe` ["Hello,", "World!"]
+ word-wrap.cabal view
@@ -0,0 +1,43 @@+name:                word-wrap+version:             0.1+synopsis:            A library for word-wrapping+description:         A library for wrapping long lines of text.+license:             BSD3+license-file:        LICENSE+author:              Jonathan Daugherty+maintainer:          cygnus@foobox.com+copyright:           2017 Jonathan Daugherty+category:            Text+build-type:          Simple+extra-source-files:  ChangeLog.md+cabal-version:       >=1.10+Homepage:            https://github.com/jtdaugherty/word-wrap/+Bug-reports:         https://github.com/jtdaugherty/word-wrap/issues++extra-doc-files:+  README.md+  CHANGELOG.md++Source-Repository head+  type:     git+  location: git://github.com/jtdaugherty/word-wrap.git++library+  exposed-modules:+    Text.Wrap++  hs-source-dirs:      src+  default-language:    Haskell2010+  ghc-options:         -Wall+  build-depends:       base < 5,+                       text++test-suite word-wrap-tests+  type:                exitcode-stdio-1.0+  default-language:    Haskell2010+  hs-source-dirs:      tests+  main-is:             Main.hs+  ghc-options:         -Wall+  build-depends:       base < 5,+                       word-wrap,+                       hspec >= 2.4