Skip to content

Commit 484ad55

Browse files
committed
Fail if the position is out of bounds
1 parent 556b249 commit 484ad55

File tree

2 files changed

+33
-26
lines changed

2 files changed

+33
-26
lines changed

lsp/src/Language/LSP/VFS.hs

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
417419
positionToCodePointPosition :: VirtualFile -> J.Position -> Maybe CodePointPosition
418420
positionToCodePointPosition 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*

lsp/test/VspSpec.hs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -309,14 +309,16 @@ vspSpec = do
309309

310310
positionToCodePointPosition vfile (J.Position 1 0) `shouldBe` Just (CodePointPosition 1 0)
311311
positionToCodePointPosition vfile (J.Position 1 1) `shouldBe` Just (CodePointPosition 1 1)
312+
-- Split inside code point
312313
positionToCodePointPosition vfile (J.Position 1 2) `shouldBe` Nothing
313314
positionToCodePointPosition vfile (J.Position 1 3) `shouldBe` Just (CodePointPosition 1 2)
314315
positionToCodePointPosition vfile (J.Position 1 4) `shouldBe` Just (CodePointPosition 1 3)
315316
positionToCodePointPosition vfile (J.Position 1 5) `shouldBe` Just (CodePointPosition 1 4)
316317
-- Greater column than max column
317-
positionToCodePointPosition vfile (J.Position 1 6) `shouldBe` Just (CodePointPosition 1 4)
318+
positionToCodePointPosition vfile (J.Position 1 6) `shouldBe` Nothing
319+
positionToCodePointPosition vfile (J.Position 2 1) `shouldBe` Nothing
318320
-- Greater line than max line
319-
positionToCodePointPosition vfile (J.Position 2 2) `shouldBe` Just (CodePointPosition 2 0)
321+
positionToCodePointPosition vfile (J.Position 3 0) `shouldBe` Nothing
320322

321323
it "converts code points to code units" $ do
322324
let
@@ -326,15 +328,16 @@ vspSpec = do
326328
]
327329
vfile = VirtualFile 0 0 (fromString orig)
328330

329-
codePointPositionToPosition vfile (CodePointPosition 1 0) `shouldBe` J.Position 1 0
330-
codePointPositionToPosition vfile (CodePointPosition 1 1) `shouldBe` J.Position 1 1
331-
codePointPositionToPosition vfile (CodePointPosition 1 2) `shouldBe` J.Position 1 3
332-
codePointPositionToPosition vfile (CodePointPosition 1 3) `shouldBe` J.Position 1 4
333-
codePointPositionToPosition vfile (CodePointPosition 1 4) `shouldBe` J.Position 1 5
331+
codePointPositionToPosition vfile (CodePointPosition 1 0) `shouldBe` Just (J.Position 1 0)
332+
codePointPositionToPosition vfile (CodePointPosition 1 1) `shouldBe` Just (J.Position 1 1)
333+
codePointPositionToPosition vfile (CodePointPosition 1 2) `shouldBe` Just (J.Position 1 3)
334+
codePointPositionToPosition vfile (CodePointPosition 1 3) `shouldBe` Just (J.Position 1 4)
335+
codePointPositionToPosition vfile (CodePointPosition 1 4) `shouldBe` Just (J.Position 1 5)
334336
-- Greater column than max column
335-
codePointPositionToPosition vfile (CodePointPosition 1 5) `shouldBe` J.Position 1 5
337+
codePointPositionToPosition vfile (CodePointPosition 1 5) `shouldBe` Nothing
338+
codePointPositionToPosition vfile (CodePointPosition 2 1) `shouldBe` Nothing
336339
-- Greater line than max line
337-
codePointPositionToPosition vfile (CodePointPosition 2 2) `shouldBe` J.Position 2 0
340+
codePointPositionToPosition vfile (CodePointPosition 3 0) `shouldBe` Nothing
338341

339342
-- ---------------------------------
340343

0 commit comments

Comments
 (0)