@@ -22,12 +22,13 @@ import Control.Monad.State (evalState, forM, get, put)
23import Data.Text (Text)
23import Data.Text (Text)
24import qualified Data.Text as T
24import qualified Data.Text as T
25−import Data.Text.ICU.Char
26import Data.Text.Internal (Text(..), text)
25import Data.Text.Internal (Text(..), text)
27import Data.Text.Internal.Fusion (stream)
26import Data.Text.Internal.Fusion (stream)
28import Data.Text.Internal.Fusion.Types (Stream(..), Step(..))
27import Data.Text.Internal.Fusion.Types (Stream(..), Step(..))
29import Data.Text.Unsafe
28import Data.Text.Unsafe
30+import Graphics.Text.Width (wcwidth)
31-- | A zipper of the logical text input contents (the "document"). The lines
32-- | A zipper of the logical text input contents (the "document"). The lines
32-- before the line containing the cursor are stored in reverse order.
33-- before the line containing the cursor are stored in reverse order.
33-- The cursor is logically between the "before" and "after" text.
34-- The cursor is logically between the "before" and "after" text.
@@ -331,12 +332,13 @@ dropWidth n = snd . splitAtWidth n
332-- | Get the display width of a 'Char'. "Full width" and "wide" characters
333-- | Get the display width of a 'Char'. "Full width" and "wide" characters
333-- take two columns and everything else takes a single column. See
334-- take two columns and everything else takes a single column. See
334−-- <https://www.unicode.org/reports/tr11/> for more information.
335+-- <https://www.unicode.org/reports/tr11/> for more information
336+-- This is implemented using wcwidth from Vty such that it matches what will
337+-- be displayed on the terminal. Note that this method can change depending
338+-- on how vty is configed. Please see vty documentation for details.
335charWidth :: Char -> Int
339charWidth :: Char -> Int
336−charWidth c = case property EastAsianWidth c of
340+charWidth = wcwidth
341-- | For a given set of wrapped logical lines, computes a map
343-- | For a given set of wrapped logical lines, computes a map
342-- from display line index to text offset in the original text.
344-- from display line index to text offset in the original text.
@@ -380,10 +382,12 @@ goToDisplayLinePosition x y dl tz =
380 in case offset of
382 in case offset of
381 Nothing -> tz
383 Nothing -> tz
382 Just o ->
384 Just o ->
383− let displayLineLength = case drop y $ _displayLines_spans dl of
386+ moveRight = case drop y $ _displayLines_spans dl of
385− (s:_) -> spansWidth s
388+ (s:_) -> charIndexAt x . stream . mconcat . fmap (\(Span _ t) -> t) $ s
386− in rightN (o + min displayLineLength x) $ top tz
389+ in rightN (o + min moveRight x) $ top tz
388-- | Get the width of the text in a set of 'Span's, taking into account unicode character widths
392-- | Get the width of the text in a set of 'Span's, taking into account unicode character widths
389spansWidth :: [Span tag] -> Int
393spansWidth :: [Span tag] -> Int
@@ -408,3 +412,15 @@ widthI (Stream next s0 _len) = loop_length 0 s0
408 Skip s' -> loop_length z s'
412 Skip s' -> loop_length z s'
409 Yield c s' -> loop_length (z + charWidth c) s'
413 Yield c s' -> loop_length (z + charWidth c) s'
410{-# INLINE[0] widthI #-}
414{-# INLINE[0] widthI #-}
416+-- | Compute the logical index position of a stream of characters from a visual
417+-- position taking into account fullwidth unicode forms.
418+charIndexAt :: Int -> Stream Char -> Int
419+charIndexAt pos (Stream next s0 _len) = loop_length 0 0 s0
421+ loop_length i !z s = case next s of
423+ Skip s' -> loop_length i z s'
424+ Yield c s' -> if w > pos then i else loop_length (i+1) w s' where
426+{-# INLINE[0] charIndexAt #-}