Skip to content

Commit f92678c

Browse files
committed
Indexing improvments, persistent rules, diff mapping
1 parent 2c9ce63 commit f92678c

File tree

9 files changed

+210
-124
lines changed

9 files changed

+210
-124
lines changed

ghcide/ghcide.cabal

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ library
5959
hie-compat,
6060
hls-plugin-api >= 0.6,
6161
lens,
62-
hiedb,
62+
hiedb == 0.2.0.*,
6363
mtl,
6464
network-uri,
6565
parallel,
@@ -80,6 +80,8 @@ library
8080
unordered-containers >= 0.2.10.0,
8181
utf8-string,
8282
hslogger,
83+
Diff,
84+
bytestring-encoding,
8385
opentelemetry >=0.6.1,
8486
heapsize ==0.3.*
8587
if flag(ghc-lib)

ghcide/src/Development/IDE/Core/Compile.hs

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,11 @@ import qualified Data.HashMap.Strict as HashMap
109109
import qualified Language.Haskell.LSP.Messages as LSP
110110
import qualified Language.Haskell.LSP.Types as LSP
111111
import Control.Concurrent.STM hiding (orElse)
112+
import Control.Concurrent.Extra
112113
import Data.Functor
113114
import Data.Unique
115+
import GHC.Fingerprint
116+
import Debug.Trace
114117

115118
-- | Given a string buffer, return the string (after preprocessing) and the 'ParsedModule'.
116119
parseModule
@@ -471,66 +474,66 @@ spliceExpresions Splices{..} =
471474
, DL.fromList $ map fst awSplices
472475
]
473476

474-
indexHieFile :: ShakeExtras -> ModSummary -> NormalizedFilePath -> Compat.HieFile -> IO ()
475-
indexHieFile se mod_summary srcPath hf = atomically $ do
477+
indexHieFile :: ShakeExtras -> ModSummary -> NormalizedFilePath -> Fingerprint -> Compat.HieFile -> IO ()
478+
indexHieFile se mod_summary srcPath hash hf = atomically $ do
476479
pending <- readTVar indexPending
477480
case HashMap.lookup srcPath pending of
478-
Just mtime | mtime >= modtime -> pure () -- An index is already scheduled
481+
Just pendingHash | pendingHash == hash -> pure () -- An index is already scheduled
479482
_ -> do
480-
modifyTVar' indexPending $ HashMap.insert srcPath modtime
483+
modifyTVar' indexPending $ HashMap.insert srcPath hash
481484
writeTQueue indexQueue $ \db -> do
482485
-- We are now in the worker thread
486+
-- Check if a newer index of this file has been scheduled, and if so skip this one
483487
newerScheduled <- atomically $ do
484488
pending <- readTVar indexPending
485-
case HashMap.lookup srcPath pending of
486-
Nothing -> pure False
487-
Just mtime -> pure $ mtime > modtime
488-
-- Check if a newer index of this file has been scheduled, and if so skip this one
489+
pure $ case HashMap.lookup srcPath pending of
490+
Nothing -> False
491+
-- If the hash in the pending list doesn't match the current hash, then skip
492+
Just pendingHash -> pendingHash /= hash
489493
unless newerScheduled $ do
490494
tok <- pre
491-
addRefsFromLoaded db targetPath (Just $ fromNormalizedFilePath srcPath) True modtime hf
495+
addRefsFromLoaded db targetPath (Just $ fromNormalizedFilePath srcPath) True hash hf
492496
post tok
493497
where
494-
modtime = ms_hs_date mod_summary
495498
mod_location = ms_location mod_summary
496499
targetPath = Compat.ml_hie_file mod_location
497500
HieDbWriter{..} = hiedbWriter se
498501

499502
-- Get a progress token to report progress and update it for the current file
500503
pre = do
501-
(!oldTok, !done, !remaining) <- atomically $ do
502-
oldTok <- readTVar indexProgressToken
503-
done <- readTVar indexCompleted
504-
remaining <- HashMap.size <$> readTVar indexPending
505-
pure (oldTok, done, remaining)
506-
tok <- case oldTok of
507-
Just x -> pure x
504+
tok <- modifyVar indexProgressToken $ \case
505+
x@(Just tok) -> pure (x, tok)
506+
-- Create a token if we don't already have one
508507
Nothing -> do
509-
lspId <- getLspId se
510508
u <- LSP.ProgressTextToken . T.pack . show . hashUnique <$> newUnique
511-
eventer se $ LSP.ReqWorkDoneProgressCreate $
509+
lspId <- getLspId se
510+
eventer se $ traceShowId $ LSP.ReqWorkDoneProgressCreate $
512511
LSP.fmServerWorkDoneProgressCreateRequest lspId $
513512
LSP.WorkDoneProgressCreateParams { _token = u }
514-
eventer se $ LSP.NotWorkDoneProgressBegin $
513+
eventer se $ traceShowId $ LSP.NotWorkDoneProgressBegin $
515514
LSP.fmServerWorkDoneProgressBeginNotification
516515
LSP.ProgressParams
517516
{ _token = u
518517
, _value = LSP.WorkDoneProgressBeginParams
519-
{ _title = "Indexing References"
518+
{ _title = "Indexing references from:"
520519
, _cancellable = Nothing
521520
, _message = Nothing
522521
, _percentage = Nothing
523522
}
524523
}
525-
pure u
524+
pure (Just u, u)
525+
(!done, !remaining) <- atomically $ do
526+
done <- readTVar indexCompleted
527+
remaining <- HashMap.size <$> readTVar indexPending
528+
pure (done, remaining)
526529
let progress = " (" <> T.pack (show done) <> "/" <> T.pack (show $ done + remaining) <> ")..."
527-
eventer se $ LSP.NotWorkDoneProgressReport $
530+
eventer se $ traceShowId $ LSP.NotWorkDoneProgressReport $
528531
LSP.fmServerWorkDoneProgressReportNotification
529532
LSP.ProgressParams
530533
{ _token = tok
531534
, _value = LSP.WorkDoneProgressReportParams
532535
{ _cancellable = Nothing
533-
, _message = Just $ "Indexing " <> T.pack (show srcPath) <> progress
536+
, _message = Just $ T.pack (show srcPath) <> progress
534537
, _percentage = Nothing
535538
}
536539
}
@@ -540,7 +543,7 @@ indexHieFile se mod_summary srcPath hf = atomically $ do
540543
post tok = do
541544
mdone <- atomically $ do
542545
-- Remove current element from pending
543-
modifyTVar' indexPending $ HashMap.update (\qTime -> guard (qTime <= modtime) $> qTime) srcPath
546+
modifyTVar' indexPending $ HashMap.update (\pendingHash -> guard (pendingHash /= hash) $> pendingHash) srcPath
544547
pending <- readTVar indexPending
545548
if HashMap.null pending
546549
then Just <$> swapTVar indexCompleted 0
@@ -549,22 +552,27 @@ indexHieFile se mod_summary srcPath hf = atomically $ do
549552
pure Nothing
550553
case mdone of
551554
Nothing -> pure ()
552-
Just done -> eventer se $ LSP.NotWorkDoneProgressEnd $
553-
LSP.fmServerWorkDoneProgressEndNotification
554-
LSP.ProgressParams
555-
{ _token = tok
556-
, _value = LSP.WorkDoneProgressEndParams
557-
{ _message = Just $ "Finished indexing " <> T.pack (show done) <> "files"
558-
}
559-
}
555+
Just done ->
556+
modifyVar_ indexProgressToken $ \_ -> do
557+
eventer se $ traceShowId $ LSP.NotWorkDoneProgressEnd $
558+
LSP.fmServerWorkDoneProgressEndNotification
559+
LSP.ProgressParams
560+
{ _token = tok
561+
, _value = LSP.WorkDoneProgressEndParams
562+
{ _message = Just $ "Finished indexing " <> T.pack (show done) <> " files"
563+
}
564+
}
565+
-- We are done with the current indexing cycle, so destroy the token
566+
pure Nothing
560567

561568
writeAndIndexHieFile :: HscEnv -> ShakeExtras -> ModSummary -> NormalizedFilePath -> [GHC.AvailInfo] -> HieASTs Type -> BS.ByteString -> IO [FileDiagnostic]
562569
writeAndIndexHieFile hscEnv se mod_summary srcPath exports ast source =
563570
handleGenerationErrors dflags "extended interface write/compression" $ do
564571
hf <- runHsc hscEnv $
565572
GHC.mkHieFile' mod_summary exports ast source
566573
atomicFileWrite targetPath $ flip GHC.writeHieFile hf
567-
indexHieFile se mod_summary srcPath hf
574+
hash <- getFileHash targetPath
575+
indexHieFile se mod_summary srcPath hash hf
568576
where
569577
dflags = hsc_dflags hscEnv
570578
mod_location = ms_location mod_summary

ghcide/src/Development/IDE/Core/FileStore.hs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,6 @@ import qualified Development.IDE.Types.Logger as L
5959
import Language.Haskell.LSP.Core
6060
import Language.Haskell.LSP.VFS
6161

62-
-- | haskell-lsp manages the VFS internally and automatically so we cannot use
63-
-- the builtin VFS without spawning up an LSP server. To be able to test things
64-
-- like `setBufferModified` we abstract over the VFS implementation.
65-
data VFSHandle = VFSHandle
66-
{ getVirtualFile :: NormalizedUri -> IO (Maybe VirtualFile)
67-
-- ^ get the contents of a virtual file
68-
, setVirtualFileContents :: Maybe (NormalizedUri -> Maybe T.Text -> IO ())
69-
-- ^ set a specific file to a value. If Nothing then we are ignoring these
70-
-- signals anyway so can just say something was modified
71-
}
72-
73-
instance IsIdeGlobal VFSHandle
74-
7562
makeVFSHandle :: IO VFSHandle
7663
makeVFSHandle = do
7764
vfsVar <- newVar (1, Map.empty)

ghcide/src/Development/IDE/Core/PositionMapping.hs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module Development.IDE.Core.PositionMapping
1515
, fromCurrentRange
1616
, applyChange
1717
, zeroMapping
18+
, mappingFromDiff
1819
-- toCurrent and fromCurrent are mainly exposed for testing
1920
, toCurrent
2021
, fromCurrent
@@ -24,6 +25,9 @@ import Control.Monad
2425
import qualified Data.Text as T
2526
import Language.Haskell.LSP.Types
2627
import Data.List
28+
import Data.Algorithm.Diff
29+
import Data.Maybe
30+
import Data.Bifunctor
2731

2832
-- | Either an exact position, or the range of text that was substituted
2933
data PositionResult a
@@ -158,3 +162,35 @@ fromCurrent (Range start@(Position startLine startColumn) end@(Position endLine
158162
| line == newEndLine = column - (newEndColumn - endColumn)
159163
| otherwise = column
160164
newLine = line - lineDiff
165+
166+
mappingFromDiff :: T.Text -> T.Text -> PositionMapping
167+
mappingFromDiff (T.lines -> old) (T.lines -> new) = PositionMapping (PositionDelta (lookupPos lnew old2new) (lookupPos lold new2old))
168+
where
169+
diff = getDiff old new
170+
171+
(old2new, new2old) = go diff 0 0
172+
173+
lnew = length new
174+
lold = length old
175+
176+
lookupPos :: Int -> [(Int,Maybe Int)] -> Position -> PositionResult Position
177+
lookupPos maxNew xs (Position line col) = go (-1) xs
178+
where
179+
go prev [] = PositionRange (Position (prev+1) 0) (Position maxNew 0)
180+
go prev ((l,b):xs)
181+
| l == line = case b of
182+
Just l' -> PositionExact $ Position l' col
183+
Nothing ->
184+
let next = case mapMaybe snd xs of
185+
[] -> maxNew
186+
(x:_) -> x
187+
in PositionRange (Position (prev+1) 0) (Position next 0)
188+
| l < line = go (maybe prev id b) xs
189+
| otherwise = error $ "lookupPos: monotonicity invariant violated: " ++ show (l,line,old,new)
190+
191+
go [] _ _ = ([],[])
192+
go (Both _ _ : xs) lold lnew = bimap ((lold,Just lnew) :) ((lnew,Just lold) :) $ go xs (lold+1) (lnew+1)
193+
go (First _ : xs) lold lnew = first ((lold,Nothing) :) $ go xs (lold+1) lnew
194+
go (Second _ : xs) lold lnew = second ((lnew,Nothing) :) $ go xs lold (lnew+1)
195+
196+

0 commit comments

Comments
 (0)