linebreak 0.1.0.3 → 1.0.0.1
raw patch · 3 files changed
+135/−86 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Text.LineBreak: instance Eq WordPart
- Text.LineBreak: instance Show WordPart
+ Text.LineBreak: bfTabRep :: BreakFormat -> Int
+ Text.LineBreak: instance Show Element
- Text.LineBreak: BreakFormat :: Int -> Char -> Maybe Hyphenator -> BreakFormat
+ Text.LineBreak: BreakFormat :: Int -> Int -> Char -> Maybe Hyphenator -> BreakFormat
Files
- LICENSE +1/−1
- Text/LineBreak.hs +131/−82
- linebreak.cabal +3/−3
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2013, Francesco Ariis+Copyright (c) 2014, Francesco Ariis All rights reserved.
Text/LineBreak.hs view
@@ -1,7 +1,7 @@ -------------------------------------------------------------------------------- -- | -- Module : Text.LineBreak--- Copyright : (C) 2013 Francesco Ariis+-- Copyright : (C) 2014 Francesco Ariis -- License : BSD3 (see LICENSE file) -- -- Maintainer : Francesco Ariis <fa-ml@ariis.it>@@ -17,9 +17,8 @@ -- > import Text.LineBreak -- > -- > hyp = Just english_US--- > bf = BreakFormat 25 '-' hyp+-- > bf = BreakFormat 25 4 '-' hyp -- > cs = "Using hyphenation with gruesomely non parsimonious wording."--- > -- > main = putStr $ breakString bf cs -- -- will output:@@ -30,109 +29,159 @@ -- ------------------------------------------------------------------------------- -module Text.LineBreak ( breakString, breakStringLn, BreakFormat (..) ) where+module Text.LineBreak ( breakString, breakStringLn, BreakFormat(..) ) where -import Data.List (intercalate) import Text.Hyphenation+import Data.Char (isSpace)+import Data.List (find, inits, tails, span) --- TODO: what to do in overflow case? --- @--- let hyp = Just english_US in--- putStr $ breakString (BreakFormat 55 '-' hyp) str2--- @------ @--- Mathematicians seek out patterns and use them to formu---- late new conjectures. Mathematicians resolve the truth--- or falsity of conjectures by mathematical proof.--- @-+----------- -- TYPES --+----------- --- | How to break the Strings: maximum width of the lines, symbol to use--- to hyphenate a word, Hypenator to use (language, exceptions, etc. Refer to--- "Text.Hyphenation" for more info). To break lines without hyphenating, put--- @Nothing@ in @bfHyphenator@.+-- | How to break the strings: maximum width of the lines, number of spaces+-- to replace tabs with (dumb replacement), symbol to use to hyphenate+-- words, hypenator to use (language, exceptions, etc.; refer to+-- "Text.Hyphenation" for usage instructions). To break lines without+-- hyphenating, put @Nothing@ in @bfHyphenator@. data BreakFormat = BreakFormat { bfMaxCol :: Int,+ bfTabRep :: Int, bfHyphenSymbol :: Char, bfHyphenator :: Maybe Hyphenator } +data BrState = BrState { bsCurrCol :: Int, -- current column+ bsBroken :: String } -- output string --- bit: hyphenated part of a word-data WordPart = Init | Mid | End | Single- deriving (Eq, Show)+data Element = ElWord String -- things we need to place+ | ElSpace Int Bool -- n of spaces, presence of final breakline+ deriving (Show) -isInit Init = True-isInit Single = True-isInit _ = False+---------------+-- FUNCTIONS --+--------------- -isEnd End = True-isEnd Single = True-isEnd _ = False+-- | Breaks some text (String) to make it fit in a certain width. The output+-- is a String, suitable for writing to screen or file.+breakString :: BreakFormat -> String -> String+breakString bf cs = hackClean out+ where els = parseEls (subTabs (bfTabRep bf) cs)+ out = bsBroken $ foldl (putElem bf) (BrState 0 "") els +-- | Convenience for @lines $ breakString bf cs@+breakStringLn :: BreakFormat -> String -> [String]+breakStringLn bf cs = lines $ breakString bf cs -putBit :: String -> BreakFormat -> WordPart -> String -> String-putBit oldcs (BreakFormat maxcol hypsym _) wp bit =- let- spaceleft =- maxcol - currcol +- (if isInit wp then (-1) else 0) + -- new world- (if not $ isEnd wp then (-1) else 0) -- possible hyp. space- lenbit = length bit - addbefore- -- begin of word, no space left- | isInit wp && lenbit > spaceleft = "\n" -- put newline- -- begin of word, not @ first column- | isInit wp && currcol /= 0 = " " -- put space- -- not begin of word, need to newline- | not (isInit wp) &&- lenbit > spaceleft = hypsym : "\n" -- put hyphens\n- -- nothing special- | otherwise = "" -- put nothing- in+-----------------+-- ANCILLARIES --+----------------- - oldcs ++ addbefore ++ bit- where currcol = length . last . lines $ ('\n' : oldcs)+-- PARSING -- +-- fino a qui+-- o word 'till ws+-- o wspa 'till (\n | word). se \n, prendilo+parseEls :: String -> [Element]+parseEls [] = []+parseEls cs@(c:_) | isSpace c = let (p, r) = span isSpace cs+ in parseWS p ++ parseEls r+ | otherwise = let (p, r) = span (not . isSpace) cs+ in parseWord p : parseEls r -putWord :: String -> BreakFormat -> String -> String-putWord oldcs bf@(BreakFormat _ _ mhyp) word =- let bit = case mhyp of- (Just hyp) -> hyphenate hyp word- Nothing -> [word]- in+-- Signatures between the two |parse| are different because there can+-- be more element in a single white-space string (newline newline), while+-- that is not possible with parseWord+parseWS :: String -> [Element]+parseWS [] = []+parseWS ws = case span (/= '\n') ws of+ (a, "") -> [elspace a False] -- no newlines+ (a, '\n':rs) -> elspace a True : parseWS rs+ where elspace cs b = ElSpace (length cs) b - -- There is a possible optimisation here (check if the word is bigger- -- than the remaining space and pass it as a singleton. Since there- -- wasn't a notable gain in time, I scrapped it.+parseWord :: String -> Element+parseWord wr = ElWord wr - case bit of- (ba:[]) -> putBit oldcs bf Single ba -- singleton- (ba:bb:[]) -> let newcs = putBit oldcs bf Init ba in- putBit newcs bf End bb -- pair- (ba:bs) -> let inics = putBit oldcs bf Init ba -- triplet+- bodcs = foldl f inics (init bs) in- putBit bodcs bf End (last bs)- where f oldcs bit = putBit oldcs bf Mid bit+-- number of spaces to replace \t with, string+subTabs :: Int -> String -> String+subTabs i cs = cs >>= f i+ where f n '\t' = replicate i ' '+ f _ c = return c +-- COMPUTATION -- -putLine :: String -> BreakFormat -> String -> String-putLine oldcs bf line =- let ws = words line in- foldl f oldcs ws- where f oldcs w = putWord oldcs bf w+putElem :: BreakFormat -> BrState -> Element -> BrState+putElem (BreakFormat maxc _ sym hyp)+ bs@(BrState currc currstr) el =+ if avspace >= elLenght el+ then putString bs maxc (el2string el)+ else case el of+ (ElSpace _ _) -> putString bs maxc "\n"+ (ElWord cs) -> putString bs maxc (broken cs)+ where avspace = maxc - currc -- starting col: 1+ fstcol = currc == 0+ broken cs = breakWord hyp sym avspace cs fstcol +elLenght :: Element -> Int+elLenght (ElWord cs) = length cs+elLenght (ElSpace i _) = i --- | Breaks a String to make it fit in a certain width. The output is a String,--- suitable for writing to screen or file.-breakString :: BreakFormat -> String -> String-breakString bf para = unlines $ breakStringLn bf para+-- convert element to string+el2string :: Element -> String+el2string (ElSpace i False) = replicate i ' '+el2string (ElSpace i True) = "\n"+el2string (ElWord cs) = cs --- | Convenience for @lines $ breakString bf cs@-breakStringLn :: BreakFormat -> String -> [String]-breakStringLn bf para =- let ls = lines para in- map (putLine "" bf) ls+-- put a string and updates the state+-- (more than macol? new line, but no hyphenation!)+putString :: BrState -> Int -> String -> BrState+putString bs _ [] = bs+putString (BrState currc currstr) maxcol (c:cs) =+ let currc' = if c == '\n'+ then 0+ else currc + 1+ bs' = if currc' <= maxcol+ then BrState currc' (currstr ++ [c])+ else BrState 1 (currstr ++ "\n" ++ [c])+ in putString bs' maxcol cs++-- breaks a word given remaining space, using an hypenator+-- the last bool is a "you are on the first col, can't start+-- a new line+breakWord :: Maybe Hyphenator -> Char -> Int -> String -> Bool -> String+breakWord mhy ch avspace cs nlb = case find ((<= avspace) . hypLen) poss of+ Just a -> a+ Nothing -> cs -- don't find? return input+ where hw = case mhy of+ Just hy -> hyphenate hy cs+ Nothing -> [cs]+ poss = error $ show $ map cf $ reverse $ zip (inits hw) (tails hw)+ -- poss ~= ["hyphenation\n","hyphen-\nation",+ -- "hy-\nphenation","\nhyphenation"]++ -- crea hyphenated from two bits+ cf ([], ew) = (if nlb then "" else "\n") ++ concat ew+ cf (iw, []) = concat iw ++ "\n"+ cf (iw, ew) = concat iw ++ [ch] ++ "\n" ++ concat ew++ hypLen cs = length . takeWhile (/= '\n') $ cs++-- CLEAN --++-- removes eof/eol whitespace+hackClean :: String -> String+hackClean cs = noEoflWs cs+ where noEoflWs cs = f "" cs++ -- the ugliness+ f acc [] = acc+ f acc cs@(a:as) =+ let (i, e) = span (== ' ') cs in+ if i == ""+ then f (acc ++ [a]) as+ else case e of+ ('\n':rest) -> f (acc ++ "\n") rest -- eol ws+ [] -> f acc [] -- eof ws+ _ -> f (acc ++ [a]) as -- normal+
linebreak.cabal view
@@ -1,12 +1,12 @@ name: linebreak-version: 0.1.0.3+version: 1.0.0.1 synopsis: breaks strings to fit width description: Simple functions to break a String to fit a maximum text width, using Knuth-Liang hyphenation algorhitm. license: BSD3 license-file: LICENSE author: Francesco Ariis-homepage: http://ariis.it/items/linebreak/page.html+homepage: http://ariis.it/static/articles/linebreak/page.html maintainer: fa-ml@ariis.it category: Text build-type: Simple@@ -14,7 +14,7 @@ source-repository head type: darcs- location: http://www.ariis.it/share/repos/linebreak/+ location: http://www.ariis.it/link/repos/linebreak/ library -- Modules exported by the library.