@@ -7,35 +7,33 @@ Description: A zipper for text documents that allows convenient editing and navi
77'TextZipper's can be converted into 'DisplayLines', which describe how the contents of the zipper will be displayed when wrapped to fit within a container of a certain width. It also provides some convenience facilities for converting interactions with the rendered DisplayLines back into manipulations of the underlying TextZipper.
88
99-}
10−{-# LANGUAGE BangPatterns #-}
11−{-# LANGUAGE FlexibleContexts #-}
12−{-# LANGUAGE LambdaCase #-}
10+{-# LANGUAGE BangPatterns #-}
11+{-# LANGUAGE FlexibleContexts #-}
12+{-# LANGUAGE LambdaCase #-}
1313{-# LANGUAGE OverloadedStrings #-}
1414
1515module Data.Text.Zipper where
1616
1717import Prelude
1818
19−import Control.Exception (assert)
20−import Control.Monad (join)
21−import Control.Monad.State (evalState, forM, get, put)
22−import Data.Char (isSpace)
23−import Data.Map (Map)
24−import qualified Data.Map as Map
25−import Data.Maybe (fromMaybe)
26−import Data.String
19+import Control.Exception (assert)
20+import Control.Monad.State (evalState, forM, get, put, join)
21+import Data.Char (isSpace)
22+import Data.Map (Map)
23+import Data.Maybe (fromMaybe)
24+import Data.String
25+import Data.Text (Text)
26+import Data.Text.Internal (Text(..), text)
27+import Data.Text.Internal.Fusion (stream)
28+import Data.Text.Internal.Fusion.Types (Stream(..), Step(..))
29+import Data.Text.Unsafe
30+import Data.Tuple.Extra
31+import qualified Data.List as L
32+import qualified Data.Map as Map
33+import qualified Data.Text as T
34+
35+import Graphics.Text.Width (wcwidth)
2736
28−import qualified Data.List as L
29−import Data.Text (Text)
30−import qualified Data.Text as T
31−import Data.Text.Internal (Text (..), text)
32−import Data.Text.Internal.Fusion (stream)
33−import Data.Text.Internal.Fusion.Types (Step (..), Stream (..))
34−import Data.Text.Unsafe
35−import Data.Tuple.Extra
36−
37−
38−import Graphics.Text.Width (wcwidth)
3937
4038-- | A zipper of the logical text input contents (the "document"). The lines
4139-- before the line containing the cursor are stored in reverse order.
@@ -44,13 +42,12 @@ import Graphics.Text.Width (wcwidth)
4442-- character (as compared to a "display" line, which is wrapped to fit within
4543-- a given viewport width).
4644data TextZipper = TextZipper
47− { _textZipper_linesBefore :: [Text] -- reversed
48− , _textZipper_before :: Text
49− -- The cursor is on top of the first character of this text
50− , _textZipper_after :: Text -- The cursor is on top of the first character of this text
51− , _textZipper_linesAfter :: [Text]
52− }
53− deriving (Show)
45+ { _textZipper_linesBefore :: [Text] -- reversed
46+ , _textZipper_before :: Text
47+ , _textZipper_after :: Text -- The cursor is on top of the first character of this text
48+ , _textZipper_linesAfter :: [Text]
49+ }
50+ deriving (Show)
5451
5552instance IsString TextZipper where
5653 fromString = fromText . T.pack
@@ -133,7 +130,7 @@ end (TextZipper lb b a la) = TextZipper lb (b <> a) "" la
133130-- | Move the cursor to the top of the document
134131top :: TextZipper -> TextZipper
135132top (TextZipper lb b a la) = case reverse lb of
136− [] -> TextZipper [] "" (b <> a) la
133+ [] -> TextZipper [] "" (b <> a) la
137134 (start:rest) -> TextZipper [] "" start (rest <> [b <> a] <> la)
138135
139136-- | Insert a character at the current cursor position
@@ -145,14 +142,14 @@ insert :: Text -> TextZipper -> TextZipper
145142insert i z@(TextZipper lb b a la) = case T.split (=='\n') i of
146143 [] -> z
147144 (start:rest) -> case reverse rest of
148− [] -> TextZipper lb (b <> start) a la
145+ [] -> TextZipper lb (b <> start) a la
149146 (l:ls) -> TextZipper (ls <> [b <> start] <> lb) l a la
150147
151148-- | Delete the character to the left of the cursor
152149deleteLeft :: TextZipper-> TextZipper
153150deleteLeft z@(TextZipper lb b a la) = case T.unsnoc b of
154151 Nothing -> case lb of
155− [] -> z
152+ [] -> z
156153 (l:ls) -> TextZipper ls l a la
157154 Just (b', _) -> TextZipper lb b' a la
158155
@@ -172,7 +169,7 @@ deleteLeftWord (TextZipper lb b a la) =
172169 let b' = T.dropWhileEnd isSpace b
173170 in if T.null b'
174171 then case lb of
175− [] -> TextZipper [] b' a la
172+ [] -> TextZipper [] b' a la
176173 (l:ls) -> deleteLeftWord $ TextZipper ls l a la
177174 else TextZipper lb (T.dropWhileEnd (not . isSpace) b') a la
178175
@@ -204,11 +201,11 @@ data Span tag = Span tag Text
204201
205202-- | Information about the document as it is displayed (i.e., post-wrapping)
206203data DisplayLines tag = DisplayLines
207− { _displayLines_spans :: [[Span tag]]
208− , _displayLines_offsetMap :: Map Int Int
209− , _displayLines_cursorY :: Int
210− }
211− deriving (Show)
204+ { _displayLines_spans :: [[Span tag]]
205+ , _displayLines_offsetMap :: Map Int Int
206+ , _displayLines_cursorY :: Int
207+ }
208+ deriving (Show)
212209
213210-- | Given a width and a 'TextZipper', produce a list of display lines
214211-- (i.e., lines of wrapped text) with special attributes applied to
@@ -245,7 +242,7 @@ displayLines width tag cursorTag (TextZipper lb b a la) =
245242 -- the cursor has to go to the next line
246243 cursorAfterEOL = curLineOffset == width
247244 cursorCharWidth = case T.uncons a of
248− Nothing -> 1
245+ Nothing -> 1
249246 Just (c, _) -> charWidth c
250247 -- Separate the span after the cursor into
251248 -- * spans that are on the same display line, and
@@ -257,7 +254,7 @@ displayLines width tag cursorTag (TextZipper lb b a la) =
257254 let o = if cursorAfterEOL then cursorCharWidth else curLineOffset + cursorCharWidth
258255 cursor = Span cursorTag (T.singleton c)
259256 in case map ((:[]) . Span tag) (wrapWithOffset width o rest) of
260− [] -> [[cursor]]
257+ [] -> [[cursor]]
261258 (l:ls) -> (cursor : l) : ls
262259 in DisplayLines
263260 { _displayLines_spans = concat
@@ -281,7 +278,7 @@ displayLines width tag cursorTag (TextZipper lb b a la) =
281278 initLast = \case
282279 [] -> Nothing
283280 (x:xs) -> case initLast xs of
284− Nothing -> Just ([], x)
281+ Nothing -> Just ([], x)
285282 Just (ys, y) -> Just (x:ys, y)
286283 headTail :: [a] -> Maybe (a, [a])
287284 headTail = \case
@@ -393,7 +390,7 @@ goToDisplayLinePosition x y dl tz =
393390 Just o ->
394391 let
395392 moveRight = case drop y $ _displayLines_spans dl of
396− [] -> x
393+ [] -> x
397394 (s:_) -> charIndexAt x . stream . mconcat . fmap (\(Span _ t) -> t) $ s
398395 in rightN (o + min moveRight x) $ top tz
399396