repos / line-indexed-cursor.hs.git


commit
9de8439
parent
117fc9d
author
Evgenii Akentev
date
2023-07-09 18:52:04 +0400 +04
Add getCurrentLineNumber
2 files changed,  +28, -8
M src/System/IO/LineIndexedCursor.hs
+16, -8
 1@@ -26,6 +26,8 @@ data LineIndexedCursor = LineIndexedCursor
 2   -- | Same as 'hGetLine' but safe.
 3   getCurrentLine :: IO (Maybe ByteString)
 4 
 5+  -- | Returns current line number.
 6+  , getCurrentLineNumber :: IO Integer
 7 
 8   -- | Rewinds to the requsted line number. Stops at EOF if it's too big.
 9   -- Returns the reached line number.
10@@ -37,7 +39,7 @@ data LineIndexedCursor = LineIndexedCursor
11 
12 data CursorHandle = CursorHandle
13   { fileHandle :: Handle
14-  , linesIdx :: MVar ([Integer], Integer)
15+  , linesIdx :: MVar ([Integer], Integer, Integer)
16   }
17 
18 {- |
19@@ -54,11 +56,12 @@ mkLineIndexedCursor fileHandle = do
20   -- reset the handle's offset to the beginning
21   hSeek fileHandle AbsoluteSeek 0
22 
23-  linesIdx <- newMVar ([0], 0)
24+  linesIdx <- newMVar ([0], 0, 0)
25 
26   let cursorHandle = CursorHandle fileHandle linesIdx
27   pure $ LineIndexedCursor
28     { getCurrentLine = getCurrentLine' cursorHandle
29+    , getCurrentLineNumber = getCurrentLineNumber' cursorHandle
30     , goToLine = goToLine' cursorHandle
31     , getHandle = fileHandle
32     }
33@@ -68,25 +71,30 @@ getCurrentLine' CursorHandle{..} =
34   hIsEOF fileHandle >>= \isEOF -> if isEOF then pure Nothing else do
35     line <- hGetLine fileHandle
36     offset <- hTell fileHandle
37-    modifyMVar_ linesIdx $ \(idx, size) -> pure $
38+    modifyMVar_ linesIdx $ \(idx, size, cln) -> pure $
39       if (not $ offset `elem` idx)
40-      then (offset : idx, size + 1)
41-      else (idx, size)
42+      then (offset : idx, size + 1, cln + 1)
43+      else (idx, size, cln + 1)
44     pure $ Just line
45 
46+getCurrentLineNumber' :: CursorHandle -> IO Integer
47+getCurrentLineNumber' CursorHandle{..} = do
48+  (_, _, cln) <- readMVar linesIdx
49+  pure cln
50+
51 goToLine' :: CursorHandle -> Integer -> IO Integer
52 goToLine' CursorHandle{..} ln = do
53-  modifyMVar linesIdx $ \(idx, size) -> do
54+  modifyMVar linesIdx $ \(idx, size, _) -> do
55     if ln > size then do
56       hSeek fileHandle AbsoluteSeek (idx !! 0)
57       -- try to read until the requested line number
58       idxTail <- readUntil (ln - size) []
59       let newSize = size + (fromIntegral $ length idxTail)
60-      pure ((idxTail ++ idx, newSize), newSize)
61+      pure ((idxTail ++ idx, newSize, newSize), newSize)
62     else do
63       let nextSeekIndex = fromIntegral $ size - ln
64       hSeek fileHandle AbsoluteSeek (idx !! nextSeekIndex)
65-      pure ((idx, size), ln)
66+      pure ((idx, size, ln), ln)
67   where
68     readUntil 0 idx = pure idx
69     readUntil counter idx =
M test/Main.hs
+12, -0
 1@@ -40,18 +40,30 @@ main = hspec $ do
 2         l' `shouldBe` Nothing
 3 
 4       it "read line, then go to beginning and forth" $ \(_, c) -> do
 5+        cln <- getCurrentLineNumber c
 6+        cln `shouldBe` 0
 7+
 8         l <- getCurrentLine c
 9         l `shouldBe` Just "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
10 
11+        cln' <- getCurrentLineNumber c
12+        cln' `shouldBe` 1
13+
14         _ <- getCurrentLine c
15         _ <- getCurrentLine c
16         _ <- getCurrentLine c
17         _ <- getCurrentLine c
18         _ <- getCurrentLine c
19 
20+        cln'' <- getCurrentLineNumber c
21+        cln'' `shouldBe` 6
22+
23         ln <- goToLine c 0
24         ln `shouldBe` 0
25 
26+        cln''' <- getCurrentLineNumber c
27+        cln''' `shouldBe` 0
28+
29         l' <- getCurrentLine c
30         l' `shouldBe` Just "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
31