@@ -22,12 +22,13 @@ import Control.Monad.State (evalState, forM, get, put)
2222
2323import Data.Text (Text)
2424import qualified Data.Text as T
25−import Data.Text.ICU.Char
2625import Data.Text.Internal (Text(..), text)
2726import Data.Text.Internal.Fusion (stream)
2827import Data.Text.Internal.Fusion.Types (Stream(..), Step(..))
2928import Data.Text.Unsafe
3029
30+import Graphics.Text.Width (wcwidth)
31+
3132-- | A zipper of the logical text input contents (the "document"). The lines
3233-- before the line containing the cursor are stored in reverse order.
3334-- The cursor is logically between the "before" and "after" text.
@@ -331,12 +332,13 @@ dropWidth n = snd . splitAtWidth n
331332
332333-- | Get the display width of a 'Char'. "Full width" and "wide" characters
333334-- 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.
335339charWidth :: Char -> Int
336−charWidth c = case property EastAsianWidth c of
337− EAFull -> 2
338− EAWide -> 2
339− _ -> 1
340+charWidth = wcwidth
341+
340342
341343-- | For a given set of wrapped logical lines, computes a map
342344-- from display line index to text offset in the original text.
@@ -380,10 +382,12 @@ goToDisplayLinePosition x y dl tz =
380382 in case offset of
381383 Nothing -> tz
382384 Just o ->
383− let displayLineLength = case drop y $ _displayLines_spans dl of
384− [] -> x
385− (s:_) -> spansWidth s
386− in rightN (o + min displayLineLength x) $ top tz
385+ let
386+ moveRight = case drop y $ _displayLines_spans dl of
387+ [] -> x
388+ (s:_) -> charIndexAt x . stream . mconcat . fmap (\(Span _ t) -> t) $ s
389+ in rightN (o + min moveRight x) $ top tz
390+
387391
388392-- | Get the width of the text in a set of 'Span's, taking into account unicode character widths
389393spansWidth :: [Span tag] -> Int
@@ -408,3 +412,15 @@ widthI (Stream next s0 _len) = loop_length 0 s0
408412 Skip s' -> loop_length z s'
409413 Yield c s' -> loop_length (z + charWidth c) s'
410414{-# INLINE[0] widthI #-}
415+
416+-- | Compute the width of a stream of characters, taking into account
417+-- fullwidth unicode forms.
418+charIndexAt :: Int -> Stream Char -> Int
419+charIndexAt pos (Stream next s0 _len) = loop_length 0 0 s0
420+ where
421+ loop_length i !z s = case next s of
422+ Done -> i
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
425+ w = z + charWidth c
426+{-# INLINE[0] charIndexAt #-}