-- | 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.
data CursorHandle = CursorHandle
{ fileHandle :: Handle
- , linesIdx :: MVar ([Integer], Integer)
+ , linesIdx :: MVar ([Integer], Integer, Integer)
}
{- |
-- 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
}
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 =