@@ -53,7 +53,7 @@ import qualified Data.ByteString.Char8 as BS
5353import Data.Dynamic
5454import Data.Maybe
5555import Data.Map.Strict (Map)
56−import Data.List.Extra (foldl', partition, takeEnd)
56+import Data.List.Extra (partition, takeEnd)
5757import qualified Data.Set as Set
5858import qualified Data.Text as T
5959import Data.Traversable (for)
@@ -97,9 +97,11 @@ data ShakeExtras = ShakeExtras
9797 ,publishedDiagnostics :: Var (HMap.HashMap NormalizedUri [Diagnostic])
9898 -- ^ This represents the set of diagnostics that we have published.
9999 -- Due to debouncing not every change might get published.
100− ,positionMapping :: Var (HMap.HashMap NormalizedUri (Map TextDocumentVersion PositionMapping))
100+ ,positionMapping :: Var (HMap.HashMap NormalizedUri (Map TextDocumentVersion (PositionDelta, PositionMapping)))
101101 -- ^ Map from a text document version to a PositionMapping that describes how to map
102102 -- positions in a version of that document to positions in the latest version
103+ -- First mapping is delta from previous version and second one is an
104+ -- accumlation of all previous mappings.
103105 ,inProgress :: Var (HMap.HashMap NormalizedFilePath Int)
104106 -- ^ How many rules are running for each file
105107 }
@@ -201,12 +203,12 @@ valueVersion = \case
201203 Failed -> Nothing
202204
203205mappingForVersion
204− :: HMap.HashMap NormalizedUri (Map TextDocumentVersion PositionMapping)
206+ :: HMap.HashMap NormalizedUri (Map TextDocumentVersion (a, PositionMapping))
205207 -> NormalizedFilePath
206208 -> TextDocumentVersion
207209 -> PositionMapping
208210mappingForVersion allMappings file ver =
209− fromMaybe idMapping $
211+ maybe zeroMapping snd $
210212 Map.lookup ver =<<
211213 HMap.lookup (filePathToUri' file) allMappings
212214
@@ -536,7 +538,10 @@ usesWithStale key files = do
536538
537539withProgress :: (Eq a, Hashable a) => Var (HMap.HashMap a Int) -> a -> Action b -> Action b
538540withProgress var file = actionBracket (f succ) (const $ f pred) . const
539− where f shift = modifyVar_ var $ return . HMap.alter (Just . shift . fromMaybe 0) file
541+ -- This functions are deliberately eta-expanded to avoid space leaks.
542+ -- Do not remove the eta-expansion without profiling a session with at
543+ -- least 1000 modifications.
544+ where f shift = modifyVar_ var $ \x -> return (HMap.alter (\x -> Just (shift (fromMaybe 0 x))) file x)
540545
541546
542547defineEarlyCutoff
@@ -828,11 +833,17 @@ filterVersionMap =
828833 HMap.intersectionWith $ \versionsToKeep versionMap -> Map.restrictKeys versionMap versionsToKeep
829834
830835updatePositionMapping :: IdeState -> VersionedTextDocumentIdentifier -> List TextDocumentContentChangeEvent -> IO ()
831−updatePositionMapping IdeState{shakeExtras = ShakeExtras{positionMapping}} VersionedTextDocumentIdentifier{..} changes = do
836+updatePositionMapping IdeState{shakeExtras = ShakeExtras{positionMapping}} VersionedTextDocumentIdentifier{..} (List changes) = do
832837 modifyVar_ positionMapping $ \allMappings -> do
833838 let uri = toNormalizedUri _uri
834839 let mappingForUri = HMap.lookupDefault Map.empty uri allMappings
835− let updatedMapping =
836− Map.insert _version idMapping $
837− Map.map (\oldMapping -> foldl' applyChange oldMapping changes) mappingForUri
840+ let (_, updatedMapping) =
841+ -- Very important to use mapAccum here so that the tails of
842+ -- each mapping can be shared, otherwise quadratic space is
843+ -- used which is evident in long running sessions.
844+ Map.mapAccumRWithKey (\acc _k (delta, _) -> let new = addDelta delta acc in (new, (delta, acc)))
845+ zeroMapping
846+ (Map.insert _version (shared_change, zeroMapping) mappingForUri)
838847 pure $! HMap.insert uri updatedMapping allMappings
848+ where
849+ shared_change = mkDelta changes