@@ -378,48 +378,52 @@ line. Which is okay-ish, so long as we don't have very long lines.
378378
379379-- | Extracts a specific line from a 'Rope.Rope'.
380380-- Logarithmic in the number of lines.
381- extractLine :: Rope. Rope -> Word -> Rope. Rope
382- extractLine rope l =
381+ extractLine :: Rope. Rope -> Word -> Maybe Rope. Rope
382+ extractLine rope l = do
383+ -- Check for the line being out of bounds
384+ let lastLine = Rope. posLine $ Rope. lengthAsPosition rope
385+ guard $ l <= lastLine
386+
383387 let (_, suffix) = Rope. splitAtLine l rope
384388 (prefix, _) = Rope. splitAtLine 1 suffix
385- in prefix
389+ pure $ prefix
386390
387391-- | Given a virtual file, translate a 'CodePointPosition' in that file into a 'J.Position' in that file.
388392--
389- -- If the position is out of bounds (i.e. beyond the last line or the last character in a line), then the
390- -- greatest valid position less than that will be returned.
393+ -- Will return 'Nothing' if the requested position is out of bounds of the document.
391394--
392395-- We need the file itself because this requires translating between code points and code units.
393- codePointPositionToPosition :: VirtualFile -> CodePointPosition -> J. Position
394- codePointPositionToPosition vFile (CodePointPosition l cpc) =
396+ codePointPositionToPosition :: VirtualFile -> CodePointPosition -> Maybe J. Position
397+ codePointPositionToPosition vFile (CodePointPosition l cpc) = do
395398 -- See Note [Converting between code points and code units]
396399 let text = _file_text vFile
397- utf16Line = extractLine text (fromIntegral l)
400+ utf16Line <- extractLine text (fromIntegral l)
398401
399- -- Convert the line a rope using *code points*
400- utfLine = URope. fromText $ Rope. toText utf16Line
402+ -- Convert the line a rope using *code points*
403+ let utfLine = URope. fromText $ Rope. toText utf16Line
404+ -- Check for the position being out of bounds
405+ guard $ (fromIntegral cpc) <= URope. length utfLine
401406 -- Split at the given position in *code points*
402- (utfLinePrefix, _) = URope. splitAt (fromIntegral cpc) utfLine
407+ let (utfLinePrefix, _) = URope. splitAt (fromIntegral cpc) utfLine
403408 -- Convert the prefix to a rope using *code units*
404409 utf16LinePrefix = Rope. fromText $ URope. toText utfLinePrefix
405410 -- Get the length of the prefix in *code units*
406411 cuc = Rope. length utf16LinePrefix
407- in J. Position l (fromIntegral cuc)
412+ pure $ J. Position l (fromIntegral cuc)
408413
409414-- | Given a virtual file, translate a 'J.Position' in that file into a 'CodePointPosition' in that file.
410415--
411- -- May fail if the requested position lies inside a code point.
412- --
413- -- If the position is out of bounds (i.e. beyond the last line or the last character in a line), then the
414- -- greatest valid position less than that will be returned.
416+ -- Will return 'Nothing' if the requested position lies inside a code point, or if it is out of bounds of the document.
415417--
416418-- We need the file itself because this requires translating between code unit and code points.
417419positionToCodePointPosition :: VirtualFile -> J. Position -> Maybe CodePointPosition
418420positionToCodePointPosition vFile (J. Position l cuc) = do
419421 -- See Note [Converting between code points and code units]
420422 let text = _file_text vFile
421- utf16Line = extractLine text (fromIntegral l)
423+ utf16Line <- extractLine text (fromIntegral l)
422424
425+ -- Check for the position being out of bounds
426+ guard $ (fromIntegral cuc) <= Rope. length utf16Line
423427 -- Split at the given position in *code units*
424428 (utf16LinePrefix, _) <- Rope. splitAt (fromIntegral cuc) utf16Line
425429 -- Convert the prefixto a rope using *code points*
0 commit comments