diff options
| -rw-r--r-- | src/TextViewport/Buffer/Buffer.hs | 14 | ||||
| -rw-r--r-- | src/TextViewport/Buffer/Item.hs | 10 | ||||
| -rw-r--r-- | src/TextViewport/Render/CachedRender.hs | 9 | ||||
| -rw-r--r-- | src/TextViewport/Render/RenderBuffer.hs | 6 | ||||
| -rw-r--r-- | src/TextViewport/Render/RenderCache.hs | 8 | ||||
| -rw-r--r-- | src/TextViewport/Render/RenderItem.hs | 5 | ||||
| -rw-r--r-- | src/TextViewport/Render/RenderState.hs | 32 | ||||
| -rw-r--r-- | src/TextViewport/Render/RenderedBuffer.hs | 6 | ||||
| -rw-r--r-- | src/TextViewport/Render/RenderedItem.hs | 4 | ||||
| -rw-r--r-- | src/TextViewport/Render/RenderedLine.hs | 4 | ||||
| -rw-r--r-- | src/TextViewport/Render/Segmentation.hs | 109 | ||||
| -rw-r--r-- | src/TextViewport/Viewport/Instance.hs | 34 | ||||
| -rw-r--r-- | src/TextViewport/Viewport/Position.hs | 2 | ||||
| -rw-r--r-- | src/TextViewport/Viewport/Viewport.hs | 6 | ||||
| -rw-r--r-- | text-viewport.cabal | 2 |
15 files changed, 136 insertions, 115 deletions
diff --git a/src/TextViewport/Buffer/Buffer.hs b/src/TextViewport/Buffer/Buffer.hs index 7b9582f..62ce232 100644 --- a/src/TextViewport/Buffer/Buffer.hs +++ b/src/TextViewport/Buffer/Buffer.hs @@ -3,33 +3,33 @@ module TextViewport.Buffer.Buffer where import Data.Sequence qualified as Seq import TextViewport.Buffer.Item (Item) -newtype Buffer = Buffer { unBuffer :: Seq.Seq Item } +newtype Buffer a = Buffer { unBuffer :: Seq.Seq (Item a) } deriving (Eq, Show) -- | Build a buffer from a list -fromList :: [Item] -> Buffer +fromList :: [Item a] -> Buffer a fromList xs = Buffer (Seq.fromList xs) -- | Modify an item at index -modifyItem :: Int -> (Item -> Item) -> Buffer -> Buffer +modifyItem :: Int -> (Item a -> Item a) -> Buffer a -> Buffer a modifyItem ix f (Buffer xs) = Buffer (Seq.adjust' f ix xs) -- | Insert an item -insertItem :: Int -> Item -> Buffer -> Buffer +insertItem :: Int -> Item a -> Buffer a -> Buffer a insertItem ix x (Buffer xs) = Buffer (Seq.insertAt ix x xs) -- | Delete an item -deleteItem :: Int -> Buffer -> Buffer +deleteItem :: Int -> Buffer a -> Buffer a deleteItem ix (Buffer xs) = Buffer (Seq.deleteAt ix xs) -- | Append an item -appendItem :: Item -> Buffer -> Buffer +appendItem :: Item a -> Buffer a -> Buffer a appendItem x (Buffer xs) = Buffer (xs Seq.|> x) -- | Extract underlying Seq (if needed) -toSeq :: Buffer -> Seq.Seq Item +toSeq :: Buffer a -> Seq.Seq (Item a) toSeq (Buffer xs) = xs diff --git a/src/TextViewport/Buffer/Item.hs b/src/TextViewport/Buffer/Item.hs index 81d18ef..b5ea743 100644 --- a/src/TextViewport/Buffer/Item.hs +++ b/src/TextViewport/Buffer/Item.hs @@ -5,17 +5,17 @@ import Data.HashMap.Strict qualified as HM import Text.Hyphenation qualified as H -data Item = Item - { itemText :: Text - , itemSegments :: SegmentStrategy +data Item a = Item + { itemText :: a + , itemSegments :: SegmentStrategy a } deriving (Eq, Show) -data SegmentStrategy +data SegmentStrategy a = NoSegments | FixedWidthSegments | HyphenateSegments { hsLang :: H.Language - , hsCache :: HM.HashMap Text [(Text, Text)] + , hsCache :: HM.HashMap a [(a, a)] } deriving (Eq, Show) diff --git a/src/TextViewport/Render/CachedRender.hs b/src/TextViewport/Render/CachedRender.hs index c4b6cf2..b5af8d4 100644 --- a/src/TextViewport/Render/CachedRender.hs +++ b/src/TextViewport/Render/CachedRender.hs @@ -1,15 +1,14 @@ module TextViewport.Render.CachedRender where -import Data.Text (Text) import TextViewport.Buffer.Item (SegmentStrategy) import TextViewport.Render.RenderedItem (RenderedItem) -data CachedRender = CachedRender +data CachedRender a = CachedRender { crWidth :: !Int - , crStrategy :: !SegmentStrategy - , crText :: !Text - , crRendered :: !RenderedItem + , crStrategy :: !(SegmentStrategy a) + , crText :: !a + , crRendered :: !(RenderedItem a) } deriving (Eq, Show) diff --git a/src/TextViewport/Render/RenderBuffer.hs b/src/TextViewport/Render/RenderBuffer.hs index a1122a4..fb782f5 100644 --- a/src/TextViewport/Render/RenderBuffer.hs +++ b/src/TextViewport/Render/RenderBuffer.hs @@ -1,13 +1,15 @@ module TextViewport.Render.RenderBuffer where +import Data.Hashable (Hashable) import Data.Sequence qualified as Seq +import Data.Sequences (Index, Textual) import TextViewport.Buffer.Buffer (Buffer(..)) import TextViewport.Render.CachedRender import TextViewport.Render.RenderCache import TextViewport.Render.RenderItem (renderItem) import TextViewport.Render.RenderedBuffer -renderBuffer :: Int -> Buffer -> RenderCache -> (RenderCache, RenderedBuffer) +renderBuffer :: (Hashable t, Textual t, Index t ~ Int) => Int -> Buffer t -> RenderCache t -> (RenderCache t, RenderedBuffer t) renderBuffer width (Buffer items) (RenderCache cache) = let n = Seq.length items go i (cAcc, rAcc) @@ -21,7 +23,7 @@ renderBuffer width (Buffer items) (RenderCache cache) = in go (i + 1) (cAcc', rAcc') in go 0 (cache, Seq.empty) -updateRenderedItem :: Int -> Int -> Buffer -> RenderCache -> RenderedBuffer -> (RenderCache, RenderedBuffer) +updateRenderedItem :: (Hashable t, Textual t, Index t ~ Int) => Int -> Int -> Buffer t -> RenderCache t -> RenderedBuffer t -> (RenderCache t, RenderedBuffer t) updateRenderedItem width itemIx (Buffer items) (RenderCache cache) (RenderedBuffer rb) = let item = Seq.index items itemIx mOld = Seq.index cache itemIx diff --git a/src/TextViewport/Render/RenderCache.hs b/src/TextViewport/Render/RenderCache.hs index 1db32fe..dcd65e0 100644 --- a/src/TextViewport/Render/RenderCache.hs +++ b/src/TextViewport/Render/RenderCache.hs @@ -6,16 +6,16 @@ import TextViewport.Buffer.Buffer (Buffer(Buffer)) import TextViewport.Render.CachedRender (CachedRender) -newtype RenderCache = RenderCache { unRenderCache :: Seq (Maybe CachedRender) } +newtype RenderCache a = RenderCache { unRenderCache :: Seq (Maybe (CachedRender a)) } deriving (Eq, Show) -- | Create an empty cache matching the buffer size -emptyRenderCacheFor :: Buffer -> RenderCache +emptyRenderCacheFor :: Buffer a -> RenderCache a emptyRenderCacheFor (Buffer xs) = RenderCache (Seq.replicate (Seq.length xs) Nothing) -- | Resize cache to match buffer length -resizeCache :: Buffer -> RenderCache -> RenderCache +resizeCache :: Buffer a -> RenderCache a -> RenderCache a resizeCache (Buffer xs) (RenderCache cache) = let n = Seq.length xs m = Seq.length cache @@ -24,5 +24,5 @@ resizeCache (Buffer xs) (RenderCache cache) = else Seq.take n cache -- | Number of cached items -length :: RenderCache -> Int +length :: RenderCache a -> Int length (RenderCache xs) = Seq.length xs diff --git a/src/TextViewport/Render/RenderItem.hs b/src/TextViewport/Render/RenderItem.hs index 6c9cbc3..7e00cf1 100644 --- a/src/TextViewport/Render/RenderItem.hs +++ b/src/TextViewport/Render/RenderItem.hs @@ -1,11 +1,14 @@ module TextViewport.Render.RenderItem where +import Data.Hashable (Hashable) +import Data.Sequences (Index, Textual) import TextViewport.Buffer.Item (Item(..)) import TextViewport.Render.CachedRender import TextViewport.Render.RenderedItem import TextViewport.Render.Segmentation (applyStrategy) -renderItem :: Int -> Int -> Item -> Maybe CachedRender -> CachedRender + +renderItem :: (Hashable t, Textual t, Index t ~ Int) => Int -> Int -> Item t -> Maybe (CachedRender t) -> CachedRender t renderItem width itemIx (Item txt strategy) mOld = case mOld of Just old diff --git a/src/TextViewport/Render/RenderState.hs b/src/TextViewport/Render/RenderState.hs index 26e92e1..8c0cdef 100644 --- a/src/TextViewport/Render/RenderState.hs +++ b/src/TextViewport/Render/RenderState.hs @@ -1,24 +1,26 @@ module TextViewport.Render.RenderState where +import Data.Hashable (Hashable) import Data.Sequence (Seq) import Data.Sequence qualified as Seq -import TextViewport.Buffer.Item +import Data.Sequences (Index, Textual) import TextViewport.Buffer.Buffer (Buffer(Buffer)) import TextViewport.Buffer.Buffer qualified as Buffer +import TextViewport.Buffer.Item import TextViewport.Render.RenderBuffer (renderBuffer) import TextViewport.Render.RenderCache (RenderCache(..), emptyRenderCacheFor, resizeCache) import TextViewport.Render.RenderedBuffer (RenderedBuffer(RenderedBuffer)) import TextViewport.Render.RenderedBuffer qualified as RenderedBuffer -data RenderState = RenderState - { rsBuffer :: Buffer -- original items - , rsCache :: RenderCache -- per-item cached renders - , rsRendered :: RenderedBuffer -- fully segmented + hyphenated lines +data RenderState a = RenderState + { rsBuffer :: Buffer a -- original items + , rsCache :: RenderCache a -- per-item cached renders + , rsRendered :: RenderedBuffer a -- fully segmented + hyphenated lines , rsWidth :: Int -- segmenting width , rsLineCount :: Int } deriving (Eq, Show) -mkRenderState :: Int -> Buffer -> RenderState +mkRenderState :: (Hashable a, Textual a, Index a ~ Int) => Int -> Buffer a -> RenderState a mkRenderState width buf = let (cache1, rendered) = renderBuffer width buf (emptyRenderCacheFor buf) in RenderState @@ -30,7 +32,7 @@ mkRenderState width buf = } -- RenderState has to be rebuilt whenever the buffer or the width changes. -updateRenderState :: Int -> Buffer -> RenderState -> RenderState +updateRenderState :: (Hashable a, Textual a, Index a ~ Int) => Int -> Buffer a -> RenderState a -> RenderState a updateRenderState width buf rs = let (cache1, rendered) = renderBuffer width buf (rsCache rs) in rs @@ -41,7 +43,7 @@ updateRenderState width buf rs = , rsLineCount = length (RenderedBuffer.flatten rendered) } -modifyItemRS :: Int -> (Item -> Item) -> RenderState -> RenderState +modifyItemRS :: (Hashable a, Textual a, Index a ~ Int) => Int -> (Item a -> Item a) -> RenderState a -> RenderState a modifyItemRS ix f st = let buf' = Buffer.modifyItem ix f (rsBuffer st) cache' = resizeCache buf' (rsCache st) @@ -52,7 +54,7 @@ modifyItemRS ix f st = , rsLineCount = length (RenderedBuffer.flatten rendered1) } -insertItem :: Int -> Item -> RenderState -> RenderState +insertItem :: (Hashable a, Textual a, Index a ~ Int) => Int -> Item a -> RenderState a -> RenderState a insertItem i newItem st = let Buffer items = rsBuffer st items' = Seq.insertAt i newItem items @@ -65,7 +67,7 @@ insertItem i newItem st = , rsLineCount = length (RenderedBuffer.flatten rendered1) } -deleteItem :: Int -> RenderState -> RenderState +deleteItem :: (Hashable a, Textual a, Index a ~ Int) => Int -> RenderState a -> RenderState a deleteItem i st = let Buffer items = rsBuffer st items' = Seq.deleteAt i items @@ -78,7 +80,7 @@ deleteItem i st = , rsLineCount = length (RenderedBuffer.flatten rendered1) } -replaceItem :: Int -> Item -> RenderState -> RenderState +replaceItem :: (Hashable a, Textual a, Index a ~ Int) => Int -> Item a -> RenderState a -> RenderState a replaceItem i newItem st = let Buffer items = rsBuffer st items' = Seq.update i newItem items @@ -91,11 +93,11 @@ replaceItem i newItem st = , rsLineCount = length (RenderedBuffer.flatten rendered1) } -appendItem :: Item -> RenderState -> RenderState +appendItem :: (Hashable a, Textual a, Index a ~ Int) => Item a -> RenderState a -> RenderState a appendItem newItem st = insertItem (Seq.length (let Buffer xs = rsBuffer st in xs)) newItem st -clearBuffer :: RenderState -> RenderState +clearBuffer :: RenderState a -> RenderState a clearBuffer st = let buf' = Buffer Seq.empty cache' = RenderCache Seq.empty @@ -105,7 +107,7 @@ clearBuffer st = , rsLineCount = 0 } -fromList :: Int -> [Item] -> RenderState +fromList :: (Hashable a, Textual a, Index a ~ Int) => Int -> [Item a] -> RenderState a fromList width xs = let buf = Buffer (Seq.fromList xs) cache0 = RenderCache (Seq.replicate (length xs) Nothing) @@ -118,7 +120,7 @@ fromList width xs = , rsLineCount = length (RenderedBuffer.flatten rendered) } -fromSeq :: Int -> Seq Item -> RenderState +fromSeq :: (Hashable a, Textual a, Index a ~ Int) => Int -> Seq (Item a) -> RenderState a fromSeq width items = let buf = Buffer items cache0 = RenderCache (Seq.replicate (Seq.length items) Nothing) diff --git a/src/TextViewport/Render/RenderedBuffer.hs b/src/TextViewport/Render/RenderedBuffer.hs index cbff8ca..9b8fa61 100644 --- a/src/TextViewport/Render/RenderedBuffer.hs +++ b/src/TextViewport/Render/RenderedBuffer.hs @@ -8,12 +8,12 @@ import TextViewport.Render.RenderedItem qualified as RenderedItem import TextViewport.Render.RenderedLine (RenderedLine) -newtype RenderedBuffer = RenderedBuffer { unRenderedBuffer :: Seq RenderedItem } +newtype RenderedBuffer a = RenderedBuffer { unRenderedBuffer :: Seq (RenderedItem a) } deriving (Eq, Show) -flatten :: RenderedBuffer -> [RenderedLine] +flatten :: RenderedBuffer a -> [RenderedLine a] flatten = concatMap (F.toList . RenderedItem.riLines) . F.toList . unRenderedBuffer -fromList :: [RenderedItem] -> RenderedBuffer +fromList :: [RenderedItem a] -> RenderedBuffer a fromList = RenderedBuffer . Seq.fromList diff --git a/src/TextViewport/Render/RenderedItem.hs b/src/TextViewport/Render/RenderedItem.hs index b32bd6d..f043e96 100644 --- a/src/TextViewport/Render/RenderedItem.hs +++ b/src/TextViewport/Render/RenderedItem.hs @@ -4,6 +4,6 @@ import Data.Vector (Vector) import TextViewport.Render.RenderedLine -data RenderedItem = RenderedItem - { riLines :: !(Vector RenderedLine) +data RenderedItem a = RenderedItem + { riLines :: !(Vector (RenderedLine a)) } deriving (Eq, Show) diff --git a/src/TextViewport/Render/RenderedLine.hs b/src/TextViewport/Render/RenderedLine.hs index 579d28e..ffc2be2 100644 --- a/src/TextViewport/Render/RenderedLine.hs +++ b/src/TextViewport/Render/RenderedLine.hs @@ -3,8 +3,8 @@ module TextViewport.Render.RenderedLine where import Data.Text (Text) -data RenderedLine = RenderedLine - { rlText :: !Text +data RenderedLine a = RenderedLine + { rlText :: !a , rlItemIx :: !Int , rlLineIx :: !Int , rlCharStart :: !Int diff --git a/src/TextViewport/Render/Segmentation.hs b/src/TextViewport/Render/Segmentation.hs index 55971ce..3d64748 100644 --- a/src/TextViewport/Render/Segmentation.hs +++ b/src/TextViewport/Render/Segmentation.hs @@ -2,8 +2,12 @@ module TextViewport.Render.Segmentation where import Data.DList qualified as DL import Data.HashMap.Strict qualified as HM +import Data.Hashable (Hashable) import Data.List (minimumBy) +import Data.MonoTraversable.Unprefixed qualified as O import Data.Ord (comparing) +import Data.Sequences (Index, IsSequence, Textual) +import Data.Sequences qualified as S import Data.Text (Text) import Data.Text qualified as T import Data.Vector (Vector) @@ -12,11 +16,12 @@ import Text.Hyphenation qualified as H import TextViewport.Buffer.Item import TextViewport.Render.RenderedLine (RenderedLine(..)) -- TODO qualified -applyStrategy :: SegmentStrategy -> Int -> Int -> Text -> Vector RenderedLine + +applyStrategy :: (Hashable t, Textual t, Index t ~ Int) => SegmentStrategy t -> Int -> Int -> t -> Vector (RenderedLine t) applyStrategy NoSegments width itemIx txt = - let rawLines = T.splitOn "\n" txt - chunks = map (T.take width) rawLines -- crop + let rawLines = S.splitWhen (=='\n') txt + chunks = map (S.take width) rawLines -- crop offsets = scanOffsetsWithNewlines chunks in V.fromList [ RenderedLine { rlText = chunk, rlItemIx = itemIx, rlLineIx = lineIx, rlCharStart = off } @@ -24,12 +29,12 @@ applyStrategy NoSegments width itemIx txt = ] applyStrategy FixedWidthSegments width itemIx txt = - let rawLines = T.splitOn "\n" txt + let rawLines = S.splitWhen (=='\n') txt (dl, _) = foldl step (DL.empty, 0) rawLines step (acc, off0) line = let chunks = chunkFixed width line offsets = scanOffsetsFrom off0 chunks - offNext = off0 + T.length line + 1 + offNext = off0 + O.length line + 1 acc' = acc `DL.append` DL.fromList (zip offsets chunks) in (acc', offNext) allChunks = DL.toList dl @@ -39,14 +44,14 @@ applyStrategy FixedWidthSegments width itemIx txt = ] applyStrategy (HyphenateSegments lang cache0) width itemIx txt = - let rawLines = T.splitOn "\n" txt + let rawLines = S.splitWhen (=='\n') txt -- fold over each physical line, accumulating: -- * all rendered (offset, chunk) pairs -- * updated hyphenation cache (unused for now) -- * running character offset across lines (dl, _cache1, _) = - foldl segmentOneLine (DL.empty, cache0, 0) rawLines + foldl (segmentOneLine lang width) (DL.empty, cache0, 0) rawLines allChunks = DL.toList dl in V.fromList [ RenderedLine { rlText = chunk, rlItemIx = itemIx, rlLineIx = lineIx, rlCharStart = off } @@ -54,61 +59,65 @@ applyStrategy (HyphenateSegments lang cache0) width itemIx txt = ] where -- Segment a single physical line using TeX‑lite hyphenation - --segmentOneLine - -- :: ([(Int, Text)], HM.HashMap Text [(Text, Text)], Int) - -- -> Text - -- -> ([(Int, Text)], HM.HashMap Text [(Text, Text)], Int) - segmentOneLine (acc, cache, off0) line = + segmentOneLine + :: (Hashable t, Textual t, Index t ~ Int) + => H.Language + -> Int + -> (DL.DList (Int, t), HM.HashMap t [(t, t)], Int) + -> t + -> (DL.DList (Int, t), HM.HashMap t [(t, t)], Int) + segmentOneLine lang width (acc, cache, off0) line = let (chunks, cache1) = segmentWithHyphenationTeXLite lang width line cache offsets = scanOffsetsFrom off0 chunks - offNext = off0 + T.length line + 1 + offNext = off0 + O.length line + 1 acc' = acc `DL.append` DL.fromList (zip offsets chunks) in (acc', cache1, offNext) --segmentOneLine (acc, cache, off0) line = -- let chunks = segmentWithHyphenationTeXLite lang width line -- offsets = scanOffsetsFrom off0 chunks - -- offNext = off0 + T.length line + 1 -- +1 for newline + -- offNext = off0 + O.length line + 1 -- +1 for newline -- acc' = acc ++ zip offsets chunks -- in (acc', cache, offNext) -- | Compute running character offsets for a list of chunks. -scanOffsetsWithNewlines :: [Text] -> [Int] +scanOffsetsWithNewlines :: IsSequence t => [t] -> [Int] scanOffsetsWithNewlines = go 0 where go !_ [] = [] go !o (l:ls) = let off = o - o' = o + T.length l + 1 -- +1 for newline + o' = o + O.length l + 1 -- +1 for newline in off : go o' ls -- | Chunk a single line into fixed-width pieces. -chunkFixed :: Int -> Text -> [Text] +chunkFixed :: (IsSequence t, Index t ~ Int) => Int -> t -> [t] chunkFixed w t | w <= 0 = [] - | T.null t = [""] + | O.null t = [mempty] | otherwise = - let (h, rest) = T.splitAt w t - in h : if T.null rest then [] else chunkFixed w rest + let (h, rest) = S.splitAt w t + in h : if O.null rest then [] else chunkFixed w rest -------------------------------------------------------------------------------- -- Hyphenation-aware segmenting (TeX-lite) -------------------------------------------------------------------------------- -- Compute offsets starting from a base offset -scanOffsetsFrom :: Int -> [Text] -> [Int] +scanOffsetsFrom :: IsSequence t => Int -> [t] -> [Int] scanOffsetsFrom start = go start where go !_ [] = [] - go !o (t:ts) = o : go (o + T.length t) ts + go !o (t:ts) = o : go (o + O.length t) ts segmentWithHyphenationTeXLite - :: H.Language + :: (Hashable t, Textual t, Index t ~ Int) + => H.Language -> Int - -> Text - -> HM.HashMap Text [(Text, Text)] - -> ([Text], HM.HashMap Text [(Text, Text)]) + -> t + -> HM.HashMap t [(t, t)] + -> ([t], HM.HashMap t [(t, t)]) segmentWithHyphenationTeXLite lang width txt cache0 = - go cache0 (T.words txt) + go cache0 (S.words txt) where go cache [] = ([], cache) go cache ws = @@ -124,37 +133,38 @@ segmentWithHyphenationTeXLite lang width txt cache0 = -- | Lossless fallback: treat remaining words as one long text and -- chunk it into width-sized pieces. Never truncates, never drops text. -breakWordSafe :: Int -> [Text] -> [Text] +breakWordSafe :: (Textual t, Index t ~ Int) => Int -> [t] -> [t] breakWordSafe width ws = - chunk (T.unwords ws) + chunk (S.unwords ws) where chunk t - | T.null t = [] - | T.length t <= width = [t] + | O.null t = [] + | O.length t <= width = [t] | otherwise = - let (c, r) = T.splitAt width t + let (c, r) = S.splitAt width t in c : chunk r -type Candidate = (Text, [Text], Bool) +type Candidate a = (a, [a], Bool) lineCandidates - :: H.Language + :: (Hashable t, Textual t, Index t ~ Int) + => H.Language -> Int - -> HM.HashMap Text [(Text, Text)] - -> [Text] - -> ([(Text, [Text], Bool)], HM.HashMap Text [(Text, Text)]) + -> HM.HashMap t [(t, t)] + -> [t] + -> ([(t, [t], Bool)], HM.HashMap t [(t, t)]) lineCandidates lang width cache0 ws0 = - go [] [] cache0 ws0 + go mempty [] cache0 ws0 where go _ acc cache [] = (acc, cache) go line acc cache (w:ws) = - let space = if null line then "" else " " - baseTxt = T.unwords line + let space = if O.null line then mempty else S.singleton ' ' + baseTxt = S.unwords line wholeTxt = baseTxt <> space <> w - wholeLen = T.length wholeTxt + wholeLen = O.length wholeTxt acc1 = - if wholeLen <= width && not (T.null wholeTxt) + if wholeLen <= width && not (O.null wholeTxt) then (wholeTxt, ws, False) : acc else acc @@ -162,15 +172,16 @@ lineCandidates lang width cache0 ws0 = case HM.lookup w cache of Just hs -> (hs, cache) Nothing -> - let hs = hyphenateWord lang w - in (hs, HM.insert w hs cache) + let hs = hyphenateWord lang (T.pack $ S.unpack w) + hs' = [ (S.pack $ T.unpack pre, S.pack $ T.unpack suf) | (pre, suf) <- hs ] + in (hs', HM.insert w hs' cache) hyphCands = [ (preTxt, suf : ws, True) | (pre, suf) <- hyphs - , not (T.null pre) - , let preTxt = baseTxt <> space <> pre <> "-" - , T.length preTxt <= width + , not (O.null pre) + , let preTxt = baseTxt <> space <> pre <> S.singleton '-' + , O.length preTxt <= width ] acc2 = hyphCands ++ acc1 @@ -187,9 +198,9 @@ hyphenateWord lang word = | i <- [1 .. length parts - 1] ] -scoreCandidate :: Int -> Candidate -> Int +scoreCandidate :: IsSequence t => Int -> Candidate t -> Int scoreCandidate width (line, _, endsWithHyphen) = - let len = T.length line + let len = O.length line remSpace = max 0 (width - len) badness = remSpace * remSpace * remSpace hyphenPenalty = diff --git a/src/TextViewport/Viewport/Instance.hs b/src/TextViewport/Viewport/Instance.hs index 1bafcbd..c3ce338 100644 --- a/src/TextViewport/Viewport/Instance.hs +++ b/src/TextViewport/Viewport/Instance.hs @@ -1,5 +1,7 @@ module TextViewport.Viewport.Instance where +import Data.Hashable (Hashable) +import Data.Sequences (Index, Textual) import TextViewport.Buffer.Item import TextViewport.Buffer.Buffer (Buffer) import TextViewport.Buffer.Buffer qualified as Buffer @@ -12,64 +14,64 @@ import TextViewport.Viewport.Viewport (Viewport, clampViewport, mkViewport) import TextViewport.Viewport.Viewport qualified as Viewport -data Instance = Instance - { viRender :: RenderState +data Instance a = Instance + { viRender :: RenderState a , viView :: Viewport } deriving (Show) -mkInstance :: Int -> Int -> Buffer -> Instance +mkInstance :: (Hashable a, Textual a, Index a ~ Int) => Int -> Int -> Buffer a -> Instance a mkInstance width height buf = let rs = mkRenderState width buf vp = mkViewport width height rs in Instance rs vp -visibleLines :: Instance -> [RenderedLine] +visibleLines :: Instance a -> [RenderedLine a] visibleLines (Instance rs vp) = take (Viewport.vpHeight vp) . drop (Viewport.vpOffset vp) . RenderedBuffer.flatten $ RenderState.rsRendered rs -applyToInstance :: (Viewport -> Viewport) -> Instance -> Instance +applyToInstance :: (Viewport -> Viewport) -> Instance a -> Instance a applyToInstance f (Instance rs vp) = let vp' = f vp in Instance rs (clampViewport rs vp') -applyToInstanceRS :: (RenderState -> Viewport -> Viewport) -> Instance -> Instance +applyToInstanceRS :: (RenderState a -> Viewport -> Viewport) -> Instance a -> Instance a applyToInstanceRS f (Instance rs vp) = let vp' = f rs vp in Instance rs (clampViewport rs vp') -scrollByI :: Int -> Instance -> Instance +scrollByI :: Int -> Instance a -> Instance a scrollByI delta = applyToInstance (Viewport.scrollBy delta) -scrollUpI :: Int -> Instance -> Instance +scrollUpI :: Int -> Instance a -> Instance a scrollUpI delta = applyToInstance (Viewport.scrollUp delta) -scrollDownI :: Int -> Instance -> Instance +scrollDownI :: Int -> Instance a -> Instance a scrollDownI delta = applyToInstance (Viewport.scrollDown delta) -pageUpI :: Instance -> Instance +pageUpI :: Instance a -> Instance a pageUpI = applyToInstance Viewport.pageUp -pageDownI :: Instance -> Instance +pageDownI :: Instance a -> Instance a pageDownI = applyToInstance Viewport.pageDown -alignTopI :: Instance -> Instance +alignTopI :: Instance a -> Instance a alignTopI = applyToInstance Viewport.alignTop -alignBottomI :: Instance -> Instance +alignBottomI :: Instance a -> Instance a alignBottomI = applyToInstanceRS Viewport.alignBottom -modifyItemI :: Int -> (Item -> Item) -> Instance -> Instance +modifyItemI :: (Hashable a, Textual a, Index a ~ Int) => Int -> (Item a -> Item a) -> Instance a -> Instance a modifyItemI ix f (Instance rs vp) = let buf' = Buffer.modifyItem ix f (RenderState.rsBuffer rs) rs' = mkRenderState (RenderState.rsWidth rs) buf' vp' = clampViewport rs' vp in Instance rs' vp' -lookupPositionI :: Int -> Int -> Instance -> Maybe (Int, Int) +lookupPositionI :: Int -> Int -> Instance a -> Maybe (Int, Int) lookupPositionI x y (Instance rs vp) = lookupPosition x y vp (RenderState.rsRendered rs) ---debugVI :: Instance -> IO () +--debugVI :: Instance a -> IO () --debugVI (Instance rs vp) = do -- putStrLn ("offset = " ++ show (Viewport.vpOffset vp)) -- putStrLn ("height = " ++ show (Viewport.vpHeight vp)) diff --git a/src/TextViewport/Viewport/Position.hs b/src/TextViewport/Viewport/Position.hs index 149fc9e..593bd67 100644 --- a/src/TextViewport/Viewport/Position.hs +++ b/src/TextViewport/Viewport/Position.hs @@ -10,7 +10,7 @@ lookupPosition :: Int -> Int -> Viewport - -> RenderedBuffer + -> RenderedBuffer a -> Maybe (Int, Int) lookupPosition x y vp rb = let allLines = RenderedBuffer.flatten rb diff --git a/src/TextViewport/Viewport/Viewport.hs b/src/TextViewport/Viewport/Viewport.hs index e6fdaab..65f48e4 100644 --- a/src/TextViewport/Viewport/Viewport.hs +++ b/src/TextViewport/Viewport/Viewport.hs @@ -10,7 +10,7 @@ data Viewport = Viewport , vpOffset :: !Int } deriving (Show) -mkViewport :: Int -> Int -> RenderState -> Viewport +mkViewport :: Int -> Int -> RenderState a -> Viewport mkViewport width height rs = alignBottom rs Viewport { vpWidth = width @@ -19,7 +19,7 @@ mkViewport width height rs = } -- any function that sets vpOffset and can overshoot should use clampViewport -clampViewport :: RenderState -> Viewport -> Viewport +clampViewport :: RenderState a -> Viewport -> Viewport clampViewport rs vp = let total = RenderState.rsLineCount rs maxOff = max 0 (total - vpHeight vp) @@ -48,7 +48,7 @@ alignTop :: Viewport -> Viewport alignTop vp = vp { vpOffset = 0 } -alignBottom :: RenderState -> Viewport -> Viewport +alignBottom :: RenderState a -> Viewport -> Viewport alignBottom rs vp = let total = RenderState.rsLineCount rs off = max 0 (total - vpHeight vp) diff --git a/text-viewport.cabal b/text-viewport.cabal index f35b113..f0099e6 100644 --- a/text-viewport.cabal +++ b/text-viewport.cabal @@ -33,7 +33,9 @@ library base , containers , dlist + , hashable , hyphenation + , mono-traversable , text , unordered-containers , vector |
