Add getCurrentLineNumber
authorEvgenii Akentev <i@ak3n.com>
Sun, 9 Jul 2023 14:52:04 +0000 (18:52 +0400)
committerEvgenii Akentev <i@ak3n.com>
Sun, 9 Jul 2023 14:52:04 +0000 (18:52 +0400)
src/System/IO/LineIndexedCursor.hs
test/Main.hs

index c26c8cf0c4b275db6240c127242fea745311298b..14d62cd7a32c5609473f104c3a76de5f1273d7ff 100644 (file)
@@ -26,6 +26,8 @@ data LineIndexedCursor = LineIndexedCursor
   -- | Same as 'hGetLine' but safe.
   getCurrentLine :: IO (Maybe ByteString)
 
+  -- | Returns current line number.
+  , getCurrentLineNumber :: IO Integer
 
   -- | Rewinds to the requsted line number. Stops at EOF if it's too big.
   -- Returns the reached line number.
@@ -37,7 +39,7 @@ data LineIndexedCursor = LineIndexedCursor
 
 data CursorHandle = CursorHandle
   { fileHandle :: Handle
-  , linesIdx :: MVar ([Integer], Integer)
+  , linesIdx :: MVar ([Integer], Integer, Integer)
   }
 
 {- |
@@ -54,11 +56,12 @@ mkLineIndexedCursor fileHandle = do
   -- reset the handle's offset to the beginning
   hSeek fileHandle AbsoluteSeek 0
 
-  linesIdx <- newMVar ([0], 0)
+  linesIdx <- newMVar ([0], 0, 0)
 
   let cursorHandle = CursorHandle fileHandle linesIdx
   pure $ LineIndexedCursor
     { getCurrentLine = getCurrentLine' cursorHandle
+    , getCurrentLineNumber = getCurrentLineNumber' cursorHandle
     , goToLine = goToLine' cursorHandle
     , getHandle = fileHandle
     }
@@ -68,25 +71,30 @@ getCurrentLine' CursorHandle{..} =
   hIsEOF fileHandle >>= \isEOF -> if isEOF then pure Nothing else do
     line <- hGetLine fileHandle
     offset <- hTell fileHandle
-    modifyMVar_ linesIdx $ \(idx, size) -> pure $
+    modifyMVar_ linesIdx $ \(idx, size, cln) -> pure $
       if (not $ offset `elem` idx)
-      then (offset : idx, size + 1)
-      else (idx, size)
+      then (offset : idx, size + 1, cln + 1)
+      else (idx, size, cln + 1)
     pure $ Just line
 
+getCurrentLineNumber' :: CursorHandle -> IO Integer
+getCurrentLineNumber' CursorHandle{..} = do
+  (_, _, cln) <- readMVar linesIdx
+  pure cln
+
 goToLine' :: CursorHandle -> Integer -> IO Integer
 goToLine' CursorHandle{..} ln = do
-  modifyMVar linesIdx $ \(idx, size) -> do
+  modifyMVar linesIdx $ \(idx, size, _) -> do
     if ln > size then do
       hSeek fileHandle AbsoluteSeek (idx !! 0)
       -- try to read until the requested line number
       idxTail <- readUntil (ln - size) []
       let newSize = size + (fromIntegral $ length idxTail)
-      pure ((idxTail ++ idx, newSize), newSize)
+      pure ((idxTail ++ idx, newSize, newSize), newSize)
     else do
       let nextSeekIndex = fromIntegral $ size - ln
       hSeek fileHandle AbsoluteSeek (idx !! nextSeekIndex)
-      pure ((idx, size), ln)
+      pure ((idx, size, ln), ln)
   where
     readUntil 0 idx = pure idx
     readUntil counter idx =
index d9c86134f7b213c12b893d8a33c63b201e994766..8e002ecf812e2bac33152abc4ef92e1bfeab8853 100644 (file)
@@ -40,18 +40,30 @@ main = hspec $ do
         l' `shouldBe` Nothing
 
       it "read line, then go to beginning and forth" $ \(_, c) -> do
+        cln <- getCurrentLineNumber c
+        cln `shouldBe` 0
+
         l <- getCurrentLine c
         l `shouldBe` Just "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
 
+        cln' <- getCurrentLineNumber c
+        cln' `shouldBe` 1
+
         _ <- getCurrentLine c
         _ <- getCurrentLine c
         _ <- getCurrentLine c
         _ <- getCurrentLine c
         _ <- getCurrentLine c
 
+        cln'' <- getCurrentLineNumber c
+        cln'' `shouldBe` 6
+
         ln <- goToLine c 0
         ln `shouldBe` 0
 
+        cln''' <- getCurrentLineNumber c
+        cln''' `shouldBe` 0
+
         l' <- getCurrentLine c
         l' `shouldBe` Just "Lorem ipsum dolor sit amet, consectetur adipiscing elit."