From: Evgenii Akentev Date: Sun, 9 Jul 2023 14:52:04 +0000 (+0400) Subject: Add getCurrentLineNumber X-Git-Url: https://git.ak3n.com/?a=commitdiff_plain;h=9de843934896d2eeaca2983c5491763cb7288c98;p=line-indexed-cursor.git Add getCurrentLineNumber --- diff --git a/src/System/IO/LineIndexedCursor.hs b/src/System/IO/LineIndexedCursor.hs index c26c8cf..14d62cd 100644 --- a/src/System/IO/LineIndexedCursor.hs +++ b/src/System/IO/LineIndexedCursor.hs @@ -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 = diff --git a/test/Main.hs b/test/Main.hs index d9c8613..8e002ec 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -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."