on develop help sign in
convert tuple to WrappedLine
commitd17426a91e1ce001e8ff2592b818afac684c0083 authorpdlla <chippermonky@gmail.com> dateTue 23 Feb 2021, 22:25 (6 years ago) parent3080ff0
3 files changed+35−28
reflex-vty.cabal+1−2 src/Data/Text/Zipper.hs+27−19 test/Data/Text/ZipperSpec.hs+7−7
reflex-vty.cabal+1−2
@@ -46,8 +46,7 @@ library
4646 ref-tf >= 0.4.0 && < 0.5,
4747 reflex >= 0.7.2 && < 0.8,
4848 time >= 1.8.0 && < 1.10,
49− vty >= 5.28 && < 5.29,
50− extra >= 1.7 && < 1.8
49+ vty >= 5.28 && < 5.29
5150 hs-source-dirs: src
5251 default-language: Haskell2010
5352 ghc-options: -Wall
src/Data/Text/Zipper.hs+27−19
@@ -27,7 +27,6 @@ import Data.Text.Internal (Text(..), text)
2727import Data.Text.Internal.Fusion (stream)
2828import Data.Text.Internal.Fusion.Types (Stream(..), Step(..))
2929import Data.Text.Unsafe
30−import Data.Tuple.Extra
3130import qualified Data.List as L
3231import qualified Data.Map as Map
3332import qualified Data.Text as T
@@ -434,7 +433,7 @@ charIndexAt pos (Stream next s0 _len) = loop_length 0 0 s0
434433
435434
436435
437−-- same as T.words except whitespace characters are included at end (i.e. ["line1 ", ...])
436+-- | Same as T.words except whitespace characters are included at end (i.e. ["line1 ", ...])
438437-- 'Char's representing white space.
439438wordsWithWhitespace :: Text -> [Text]
440439wordsWithWhitespace t@(Text arr off len) = loop 0 0 False
@@ -449,7 +448,7 @@ wordsWithWhitespace t@(Text arr off len) = loop 0 0 False
449448 where Iter c d = iter t n
450449{-# INLINE wordsWithWhitespace #-}
451450
452−-- take sum of word length, returns True if ends with trailng space
451+-- | Split words into logical lines, 'True' in the tuple indicates line ends with a whitespace character that got deleted
453452splitWordsAtDisplayWidth :: Int -> [Text] -> [(Text, Bool)]
454453splitWordsAtDisplayWidth maxWidth wwws = reverse $ loop wwws 0 [] where
455454 appendOut :: [(Text,Bool)] -> Text -> Bool -> [(Text,Bool)]
@@ -476,14 +475,15 @@ splitWordsAtDisplayWidth maxWidth wwws = reverse $ loop wwws 0 [] where
476475 -- single word exceeds max width, so just split on the word
477476 then let (t1,t2) = splitAtWidth (maxWidth - cumw) x
478477 in loop (t2:xs) 0 [] <> appendOut out t1 False
479−
480478 -- otherwise start a new line
481479 else loop (x:xs) 0 [] <> modifyOutForNewLine out
482480 else loop xs newWidth $ appendOut out x False
483481
484−data TextAlignment = TextAlignment_Left
485− | TextAlignment_Right
486− | TextAlignment_Center
482+data TextAlignment =
483+ TextAlignment_Left
484+ | TextAlignment_Right
485+ | TextAlignment_Center
486+ deriving (Eq, Show)
487487
488488-- A map from the index (row) of display line to
489489-- fst: leading empty spaces from left (may be negative) to adjust for alignment
@@ -500,6 +500,14 @@ data DisplayLinesWithAlignment tag = DisplayLinesWithAlignment
500500 }
501501 deriving (Show)
502502
503+data WrappedLine = WrappedLine
504+ { _wrappedLines_text :: Text
505+ , _wrappedLines_hiddenWhitespace :: Bool -- ^ 'True' if this line ends with a deleted whitespace character
506+ , _wrappedLines_offset :: Int -- ^ offset from beginning of line
507+ }
508+ deriving (Eq, Show)
509+
510+
503511-- | Wraps a logical line of text to fit within the given width. The first
504512-- wrapped line is offset by the number of columns provided. Subsequent wrapped
505513-- lines are not.
@@ -508,14 +516,14 @@ wrapWithOffsetAndAlignment
508516 -> Int -- ^ Maximum width
509517 -> Int -- ^ Offset for first line
510518 -> Text -- ^ Text to be wrapped
511− -> [(Text,Bool,Int)] -- (words on that line, hidden space char, offset from beginning of line)
519+ -> [WrappedLine] -- (words on that line, hidden space char, offset from beginning of line)
512520wrapWithOffsetAndAlignment _ maxWidth _ _ | maxWidth <= 0 = []
513521wrapWithOffsetAndAlignment alignment maxWidth n txt = assert (n <= maxWidth) r where
514522 r' = splitWordsAtDisplayWidth maxWidth $ T.replicate n " " : wordsWithWhitespace txt
515523 fmapfn (t,b) = case alignment of
516− TextAlignment_Left -> (t,b,0)
517− TextAlignment_Right -> (t,b,maxWidth-l)
518− TextAlignment_Center -> (t,b,(maxWidth-l) `div` 2)
524+ TextAlignment_Left -> WrappedLine t b 0
525+ TextAlignment_Right -> WrappedLine t b (maxWidth-l)
526+ TextAlignment_Center -> WrappedLine t b ((maxWidth-l) `div` 2)
519527 where l = textWidth t
520528 r'' = case r' of
521529 [] -> []
@@ -523,10 +531,10 @@ wrapWithOffsetAndAlignment alignment maxWidth n txt = assert (n <= maxWidth) r w
523531 r = fmap fmapfn r''
524532
525533-- converts deleted eol spaces into logical lines
526−eolSpacesToLogicalLines :: [[(Text, Bool, Int)]] -> [[(Text, Int)]]
527−eolSpacesToLogicalLines = fmap (fmap (\(a, _, c) -> (a,c))) . join . fmap (L.groupBy (\(_,b,_) _ -> not b))
534+eolSpacesToLogicalLines :: [[WrappedLine]] -> [[(Text, Int)]]
535+eolSpacesToLogicalLines = fmap (fmap (\(WrappedLine a _ c) -> (a,c))) . join . fmap (L.groupBy (\(WrappedLine _ b _) _ -> not b))
528536
529−offsetMapWithAlignmentInternal :: [[(Text, Bool, Int)]] -> OffsetMapWithAlignment
537+offsetMapWithAlignmentInternal :: [[WrappedLine]] -> OffsetMapWithAlignment
530538offsetMapWithAlignmentInternal = offsetMapWithAlignment . eolSpacesToLogicalLines
531539
532540offsetMapWithAlignment
@@ -559,9 +567,9 @@ displayLinesWithAlignment
559567 -> TextZipper -- ^ The text input contents and cursor state
560568 -> DisplayLinesWithAlignment tag
561569displayLinesWithAlignment alignment width tag cursorTag (TextZipper lb b a la) =
562− let linesBefore :: [[(Text, Bool, Int)]] -- The wrapped lines before the cursor line
570+ let linesBefore :: [[WrappedLine]] -- The wrapped lines before the cursor line
563571 linesBefore = map (wrapWithOffsetAndAlignment alignment width 0) $ reverse lb
564− linesAfter :: [[(Text, Bool, Int)]] -- The wrapped lines after the cursor line
572+ linesAfter :: [[WrappedLine]] -- The wrapped lines after the cursor line
565573 linesAfter = map (wrapWithOffsetAndAlignment alignment width 0) la
566574 offsets :: OffsetMapWithAlignment
567575 offsets = offsetMapWithAlignmentInternal $ mconcat
@@ -569,7 +577,7 @@ displayLinesWithAlignment alignment width tag cursorTag (TextZipper lb b a la) =
569577 , [wrapWithOffsetAndAlignment alignment width 0 $ b <> a]
570578 , linesAfter
571579 ]
572− flattenLines = concatMap (fmap fst3)
580+ flattenLines = concatMap (fmap _wrappedLines_text)
573581 spansBefore = map ((:[]) . Span tag) $ flattenLines linesBefore
574582 spansAfter = map ((:[]) . Span tag) $ flattenLines linesAfter
575583 -- Separate the spans before the cursor into
@@ -578,7 +586,7 @@ displayLinesWithAlignment alignment width tag cursorTag (TextZipper lb b a la) =
578586
579587 -- TODO cursor goes here if align right
580588 (spansCurrentBefore, spansCurLineBefore) = fromMaybe ([], []) $
581− initLast $ map ((:[]) . Span tag) $ fmap fst3 $ (wrapWithOffsetAndAlignment alignment width 0 b)
589+ initLast $ map ((:[]) . Span tag) $ fmap _wrappedLines_text $ (wrapWithOffsetAndAlignment alignment width 0 b)
582590 -- Calculate the number of columns on the cursor's display line before the cursor
583591 curLineOffset = spansWidth spansCurLineBefore
584592 -- Check whether the spans on the current display line are long enough that
@@ -599,7 +607,7 @@ displayLinesWithAlignment alignment width tag cursorTag (TextZipper lb b a la) =
599607 Just (c, rest) ->
600608 let o = if cursorAfterEOL then cursorCharWidth else curLineOffset + cursorCharWidth
601609 cursor = Span cursorTag (T.singleton c)
602− in case map ((:[]) . Span tag) $ fmap fst3 $ (wrapWithOffsetAndAlignment alignment width o rest) of
610+ in case map ((:[]) . Span tag) $ fmap _wrappedLines_text $ (wrapWithOffsetAndAlignment alignment width o rest) of
603611 [] -> [[cursor]]
604612 (l:ls) -> (cursor : l) : ls
605613
test/Data/Text/ZipperSpec.hs+7−7
@@ -35,14 +35,14 @@ spec = do
3535 fmap fst (splitSentenceAtDisplayWidth 5 "1234 56") `shouldBe` ["1234 "," 56"]
3636 fmap fst (splitSentenceAtDisplayWidth 8 "1 2 3 4 5 6 7 8 9 1") `shouldBe` ["1 2 3 4","5 6 7 8", "9 1"]
3737 it "wrapWithOffsetAndAlignment" $ do
38− wrapWithOffsetAndAlignment TextAlignment_Left 5 0 someSentence `shouldBe` [("12345", True, 0), ("1234", True, 0), ("12", False, 0)]
39− wrapWithOffsetAndAlignment TextAlignment_Right 5 0 someSentence `shouldBe` [("12345", True, 0), ("1234", True, 1), ("12", False, 3)]
40− wrapWithOffsetAndAlignment TextAlignment_Center 5 0 someSentence `shouldBe` [("12345", True, 0), ("1234", True, 0), ("12", False, 1)]
38+ wrapWithOffsetAndAlignment TextAlignment_Left 5 0 someSentence `shouldBe` [(WrappedLine "12345" True 0), (WrappedLine "1234" True 0), (WrappedLine "12" False 0)]
39+ wrapWithOffsetAndAlignment TextAlignment_Right 5 0 someSentence `shouldBe` [(WrappedLine "12345" True 0), (WrappedLine "1234" True 1), (WrappedLine "12" False 3)]
40+ wrapWithOffsetAndAlignment TextAlignment_Center 5 0 someSentence `shouldBe` [(WrappedLine "12345" True 0), (WrappedLine "1234" True 0), (WrappedLine "12" False 1)]
4141 it "eolSpacesToLogicalLines" $ do
4242 eolSpacesToLogicalLines
4343 [
44− [ ("😱",True,1), ("😱",False,2), ("😱",False,3) ]
45− , [ ("aa",True,1), ("aa",True,2), ("aa",False,3) ]
44+ [ (WrappedLine "😱" True 1), (WrappedLine "😱" False 2), (WrappedLine "😱" False 3) ]
45+ , [ (WrappedLine "aa" True 1), (WrappedLine "aa" True 2), (WrappedLine "aa" False 3) ]
4646 ]
4747 `shouldBe`
4848 [
@@ -55,8 +55,8 @@ spec = do
5555 it "offsetMapWithAlignmentInternal" $ do
5656 offsetMapWithAlignmentInternal
5757 [
58− [ ("😱",True,1), ("😱",False,2), ("😱",False,3) ]
59− , [ ("aa",True,1), ("aa",True,2), ("aa",False,3) ]
58+ [ (WrappedLine "😱" True 1), (WrappedLine "😱" False 2), (WrappedLine "😱" False 3) ]
59+ , [ (WrappedLine "aa" True 1), (WrappedLine "aa" True 2), (WrappedLine "aa" False 3) ]
6060 ]
6161 `shouldBe`
6262 Map.fromList