@@ -12,7 +12,7 @@ module Data.Text.Zipper where
12import Prelude
12import Prelude
14import Control.Exception (assert)
14import Control.Exception (assert)
15−import Control.Monad.State (evalState, forM, get, put)
15+import Control.Monad.State (evalState, forM, get, put, join)
16import Data.Char (isSpace)
16import Data.Char (isSpace)
17import Data.Map (Map)
17import Data.Map (Map)
18import Data.Maybe (fromMaybe)
18import Data.Maybe (fromMaybe)
@@ -170,7 +170,7 @@ deleteLeftWord (TextZipper lb b a la) =
170-- | Insert up to n spaces to get to the next logical column that is a multiple of n
170-- | Insert up to n spaces to get to the next logical column that is a multiple of n
171tab :: Int -> TextZipper -> TextZipper
171tab :: Int -> TextZipper -> TextZipper
172tab n z@(TextZipper _ b _ _) =
172tab n z@(TextZipper _ b _ _) =
173− insert (T.replicate (fromEnum $ n - T.length b `mod` max 1 n) " ") z
173+ insert (T.replicate (fromEnum $ n - (T.length b `mod` max 1 n)) " ") z
175-- | The plain text contents of the zipper
175-- | The plain text contents of the zipper
176value :: TextZipper -> Text
176value :: TextZipper -> Text
@@ -314,7 +314,9 @@ wordsWithWhitespace :: Text -> [Text]
314wordsWithWhitespace t@(Text arr off len) = loop 0 0 False
314wordsWithWhitespace t@(Text arr off len) = loop 0 0 False
316 loop !start !n !wasSpace
316 loop !start !n !wasSpace
317+ | n >= len = if start == n
317− | n >= len = [Text arr (start+off) (n-start) | not (start == n)]
319+ else [Text arr (start+off) (n-start)]
318 | isSpace c = loop start (n+d) True
320 | isSpace c = loop start (n+d) True
319 | wasSpace = Text arr (start+off) (n-start) : loop n n False
321 | wasSpace = Text arr (start+off) (n-start) : loop n n False
320 | otherwise = loop start (n+d) False
322 | otherwise = loop start (n+d) False
@@ -365,7 +367,6 @@ wrapWithOffsetAndAlignment
365 -> [WrappedLine] -- (words on that line, hidden space char, offset from beginning of line)
367 -> [WrappedLine] -- (words on that line, hidden space char, offset from beginning of line)
366wrapWithOffsetAndAlignment _ maxWidth _ _ | maxWidth <= 0 = []
368wrapWithOffsetAndAlignment _ maxWidth _ _ | maxWidth <= 0 = []
367wrapWithOffsetAndAlignment alignment maxWidth n txt = assert (n <= maxWidth) r where
369wrapWithOffsetAndAlignment alignment maxWidth n txt = assert (n <= maxWidth) r where
368− -- we pad by offset amount with any non-space character which we will remove later so that no changes need to be made to splitWordsAtDisplayWidth
369 r' = splitWordsAtDisplayWidth maxWidth $ wordsWithWhitespace ( T.replicate n "." <> txt)
370 r' = splitWordsAtDisplayWidth maxWidth $ wordsWithWhitespace ( T.replicate n "." <> txt)
370 fmapfn (t,b) = case alignment of
371 fmapfn (t,b) = case alignment of
371 TextAlignment_Left -> WrappedLine t b 0
372 TextAlignment_Left -> WrappedLine t b 0
@@ -379,7 +380,7 @@ wrapWithOffsetAndAlignment alignment maxWidth n txt = assert (n <= maxWidth) r w
380-- converts deleted eol spaces into logical lines
381-- converts deleted eol spaces into logical lines
381eolSpacesToLogicalLines :: [[WrappedLine]] -> [[(Text, Int)]]
382eolSpacesToLogicalLines :: [[WrappedLine]] -> [[(Text, Int)]]
382−eolSpacesToLogicalLines = fmap (fmap (\(WrappedLine a _ c) -> (a,c))) . concatMap (L.groupBy (\(WrappedLine _ b _) _ -> not b))
383+eolSpacesToLogicalLines = fmap (fmap (\(WrappedLine a _ c) -> (a,c))) . join . fmap (L.groupBy (\(WrappedLine _ b _) _ -> not b))
384offsetMapWithAlignmentInternal :: [[WrappedLine]] -> OffsetMapWithAlignment
385offsetMapWithAlignmentInternal :: [[WrappedLine]] -> OffsetMapWithAlignment
385offsetMapWithAlignmentInternal = offsetMapWithAlignment . eolSpacesToLogicalLines
386offsetMapWithAlignmentInternal = offsetMapWithAlignment . eolSpacesToLogicalLines
@@ -418,13 +419,10 @@ displayLinesWithAlignment alignment width tag cursorTag (TextZipper lb b a la) =
418 linesBefore = map (wrapWithOffsetAndAlignment alignment width 0) $ reverse lb
419 linesBefore = map (wrapWithOffsetAndAlignment alignment width 0) $ reverse lb
419 linesAfter :: [[WrappedLine]] -- The wrapped lines after the cursor line
420 linesAfter :: [[WrappedLine]] -- The wrapped lines after the cursor line
420 linesAfter = map (wrapWithOffsetAndAlignment alignment width 0) la
421 linesAfter = map (wrapWithOffsetAndAlignment alignment width 0) la
422− -- simulate trailing cursor character when computing OffsetMap
423− afterWithCursor = if T.null a then " " else a
424 offsets :: OffsetMapWithAlignment
422 offsets :: OffsetMapWithAlignment
425 offsets = offsetMapWithAlignmentInternal $ mconcat
423 offsets = offsetMapWithAlignmentInternal $ mconcat
426 [ linesBefore
424 [ linesBefore
427− , [wrapWithOffsetAndAlignment alignment width 0 $ b <> afterWithCursor]
425+ , [wrapWithOffsetAndAlignment alignment width 0 $ b <> a]
428 , linesAfter
426 , linesAfter
430 flattenLines = concatMap (fmap _wrappedLines_text)
428 flattenLines = concatMap (fmap _wrappedLines_text)
@@ -434,8 +432,9 @@ displayLinesWithAlignment alignment width tag cursorTag (TextZipper lb b a la) =
434 -- * spans that are on earlier display lines (though on the same logical line), and
432 -- * spans that are on earlier display lines (though on the same logical line), and
435 -- * spans that are on the same display line
433 -- * spans that are on the same display line
435+ -- TODO cursor goes here if align right
437 (spansCurrentBefore, spansCurLineBefore) = fromMaybe ([], []) $
436 (spansCurrentBefore, spansCurLineBefore) = fromMaybe ([], []) $
438− initLast $ map ((:[]) . Span tag) $ _wrappedLines_text <$> (wrapWithOffsetAndAlignment alignment width 0 b)
437+ initLast $ map ((:[]) . Span tag) $ fmap _wrappedLines_text $ (wrapWithOffsetAndAlignment alignment width 0 b)
439 -- Calculate the number of columns on the cursor's display line before the cursor
438 -- Calculate the number of columns on the cursor's display line before the cursor
440 curLineOffset = spansWidth spansCurLineBefore
439 curLineOffset = spansWidth spansCurLineBefore
441 -- Check whether the spans on the current display line are long enough that
440 -- Check whether the spans on the current display line are long enough that
@@ -449,29 +448,17 @@ displayLinesWithAlignment alignment width tag cursorTag (TextZipper lb b a la) =
449 -- * spans that are on the same display line, and
448 -- * spans that are on the same display line, and
450 -- * spans that are on later display lines (though on the same logical line)
449 -- * spans that are on later display lines (though on the same logical line)
451+ -- TODO do not show cursor if align right
452 (spansCurLineAfter, spansCurrentAfter) = fromMaybe ([], []) $
452 (spansCurLineAfter, spansCurrentAfter) = fromMaybe ([], []) $
453 headTail $ case T.uncons a of
453 headTail $ case T.uncons a of
454 Nothing -> [[Span cursorTag " "]]
454 Nothing -> [[Span cursorTag " "]]
455 Just (c, rest) ->
455 Just (c, rest) ->
456 let o = if cursorAfterEOL then cursorCharWidth else curLineOffset + cursorCharWidth
456 let o = if cursorAfterEOL then cursorCharWidth else curLineOffset + cursorCharWidth
457 cursor = Span cursorTag (T.singleton c)
457 cursor = Span cursorTag (T.singleton c)
458− in case map ((:[]) . Span tag) $ _wrappedLines_text <$> (wrapWithOffsetAndAlignment alignment width o rest) of
458+ in case map ((:[]) . Span tag) $ fmap _wrappedLines_text $ (wrapWithOffsetAndAlignment alignment width o rest) of
459 [] -> [[cursor]]
459 [] -> [[cursor]]
460 (l:ls) -> (cursor : l) : ls
460 (l:ls) -> (cursor : l) : ls
462− curLineSpanNormalCase = if cursorAfterEOL
463− then [ spansCurLineBefore, spansCurLineAfter ]
464− else [ spansCurLineBefore <> spansCurLineAfter ]
466− -- for right alignment, we want draw the cursor tag to be on the character just before the logical cursor position
467− curLineSpan = if alignment == TextAlignment_Right && not cursorAfterEOL
468− then case reverse spansCurLineBefore of
469− [] -> curLineSpanNormalCase
470− (Span _ x):xs -> case spansCurLineAfter of
471− [] -> error "should not be possible" -- curLineSpanNormalCase
472− (Span _ y):ys -> [reverse (Span cursorTag x:xs) <> ((Span tag y):ys)]
473− else curLineSpanNormalCase
475 cursorY = sum
462 cursorY = sum
476 [ length spansBefore
463 [ length spansBefore
477 , length spansCurrentBefore
464 , length spansCurrentBefore
@@ -484,7 +471,9 @@ displayLinesWithAlignment alignment width tag cursorTag (TextZipper lb b a la) =
484 { _displayLines_spans = concat
471 { _displayLines_spans = concat
485 [ spansBefore
472 [ spansBefore
486 , spansCurrentBefore
473 , spansCurrentBefore
487− , curLineSpan
474+ , if cursorAfterEOL
475+ then [ spansCurLineBefore, spansCurLineAfter ]
476+ else [ spansCurLineBefore <> spansCurLineAfter ]
488 , spansCurrentAfter
477 , spansCurrentAfter
489 , spansAfter
478 , spansAfter
@@ -542,4 +531,4 @@ wrapWithOffset
542 -> Int -- ^ Offset for first line
531 -> Int -- ^ Offset for first line
543 -> Text -- ^ Text to be wrapped
532 -> Text -- ^ Text to be wrapped
544 -> [Text]
533 -> [Text]
545−wrapWithOffset maxWidth n xs = _wrappedLines_text <$> wrapWithOffsetAndAlignment TextAlignment_Left maxWidth n xs
534+wrapWithOffset maxWidth n xs = fmap _wrappedLines_text $ wrapWithOffsetAndAlignment TextAlignment_Left maxWidth n xs