Skip to content

Commit 795dfa5

Browse files
committed
improve memory usage of ExportsMap
Storing rendered names as `Text`, especially for parents, adds a lot of duplication to the ExportsMap. Instead we store the `OccName`s directly, which have hash-consed symbols due stored as `FastStrings` and render it out on demand (which is just decoding the UTF-8 FastString to UTF-16 text for text <2.0, and essentially free on text >2.0).
1 parent f91edea commit 795dfa5

File tree

4 files changed

+76
-60
lines changed

4 files changed

+76
-60
lines changed

ghcide/src/Development/IDE/GHC/Compat.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ module Development.IDE.GHC.Compat(
4343
#endif
4444

4545
FastStringCompat,
46+
bytesFS,
47+
mkFastStringByteString,
4648
nodeInfo',
4749
getNodeIds,
4850
sourceNodeInfo,
@@ -206,6 +208,7 @@ import VarEnv (emptyInScopeSet,
206208
#endif
207209

208210
#if MIN_VERSION_ghc(9,0,0)
211+
import GHC.Data.FastString
209212
import GHC.Core
210213
import GHC.Data.StringBuffer
211214
import GHC.Driver.Session hiding (ExposePackage)
@@ -224,6 +227,7 @@ import GHC.Iface.Make (mkIfaceExports)
224227
import qualified GHC.SysTools.Tasks as SysTools
225228
import qualified GHC.Types.Avail as Avail
226229
#else
230+
import FastString
227231
import qualified Avail
228232
import DynFlags hiding (ExposePackage)
229233
import HscTypes

ghcide/src/Development/IDE/Plugin/Completions/Logic.hs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -284,24 +284,26 @@ mkExtCompl label =
284284

285285

286286
fromIdentInfo :: Uri -> IdentInfo -> Maybe T.Text -> CompItem
287-
fromIdentInfo doc IdentInfo{..} q = CI
287+
fromIdentInfo doc id@IdentInfo{..} q = CI
288288
{ compKind= occNameToComKind name
289-
, insertText=rendered
290-
, provenance = DefinedIn moduleNameText
291-
, label=rendered
289+
, insertText=rend
290+
, provenance = DefinedIn mod
291+
, label=rend
292292
, isInfix=Nothing
293-
, isTypeCompl= not isDatacon && isUpper (T.head rendered)
293+
, isTypeCompl= not (isDatacon id) && isUpper (T.head rend)
294294
, additionalTextEdits= Just $
295295
ExtendImport
296296
{ doc,
297-
thingParent = parent,
298-
importName = moduleNameText,
297+
thingParent = occNameText <$> parent,
298+
importName = mod,
299299
importQual = q,
300-
newThing = rendered
300+
newThing = rend
301301
}
302302
, nameDetails = Nothing
303303
, isLocalCompletion = False
304304
}
305+
where rend = rendered id
306+
mod = moduleNameText id
305307

306308
cacheDataProducer :: Uri -> [ModuleName] -> Module -> GlobalRdrEnv-> GlobalRdrEnv -> [LImportDecl GhcPs] -> CachedCompletions
307309
cacheDataProducer uri visibleMods curMod globalEnv inScopeEnv limports =

ghcide/src/Development/IDE/Types/Exports.hs

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ module Development.IDE.Types.Exports
55
(
66
IdentInfo(..),
77
ExportsMap(..),
8+
rendered,
9+
moduleNameText,
10+
occNameText,
11+
isDatacon,
812
createExportsMap,
913
createExportsMapMg,
1014
createExportsMapTc,
@@ -24,6 +28,7 @@ import Data.HashSet (HashSet)
2428
import qualified Data.HashSet as Set
2529
import Data.List (foldl', isSuffixOf)
2630
import Data.Text (Text, pack)
31+
import Data.Text.Encoding (decodeUtf8)
2732
import Development.IDE.GHC.Compat
2833
import Development.IDE.GHC.Orphans ()
2934
import Development.IDE.GHC.Util
@@ -61,55 +66,63 @@ instance Monoid ExportsMap where
6166
type IdentifierText = Text
6267
type ModuleNameText = Text
6368

69+
70+
rendered :: IdentInfo -> IdentifierText
71+
rendered = occNameText . name
72+
73+
-- | Render an identifier as imported or exported style.
74+
-- TODO: pattern synonymoccNameText :: OccName -> Text
75+
occNameText :: OccName -> IdentifierText
76+
occNameText name
77+
| isTcOcc name && isSymOcc name = "type " <> renderOcc
78+
| otherwise = renderOcc
79+
where
80+
renderOcc = decodeUtf8 . bytesFS . occNameFS $ name
81+
82+
moduleNameText :: IdentInfo -> ModuleNameText
83+
moduleNameText = moduleNameText' . identModuleName
84+
85+
moduleNameText' :: ModuleName -> ModuleNameText
86+
moduleNameText' = decodeUtf8 . bytesFS . moduleNameFS
87+
6488
data IdentInfo = IdentInfo
65-
{ name :: !OccName
66-
, rendered :: Text
67-
, parent :: !(Maybe Text)
68-
, isDatacon :: !Bool
69-
, moduleNameText :: !Text
89+
{ name :: !OccName
90+
, parent :: !(Maybe OccName)
91+
, identModuleName :: !ModuleName
7092
}
7193
deriving (Generic, Show)
7294
deriving anyclass Hashable
7395

96+
isDatacon :: IdentInfo -> Bool
97+
isDatacon = isDataOcc . name
98+
7499
instance Eq IdentInfo where
75100
a == b = name a == name b
76101
&& parent a == parent b
77-
&& isDatacon a == isDatacon b
78-
&& moduleNameText a == moduleNameText b
102+
&& identModuleName a == identModuleName b
79103

80104
instance NFData IdentInfo where
81105
rnf IdentInfo{..} =
82106
-- deliberately skip the rendered field
83-
rnf name `seq` rnf parent `seq` rnf isDatacon `seq` rnf moduleNameText
84-
85-
-- | Render an identifier as imported or exported style.
86-
-- TODO: pattern synonym
87-
renderIEWrapped :: Name -> Text
88-
renderIEWrapped n
89-
| isTcOcc occ && isSymOcc occ = "type " <> pack (printName n)
90-
| otherwise = pack $ printName n
91-
where
92-
occ = occName n
107+
rnf name `seq` rnf parent `seq` rnf identModuleName
93108

94-
mkIdentInfos :: Text -> AvailInfo -> [IdentInfo]
109+
mkIdentInfos :: ModuleName -> AvailInfo -> [IdentInfo]
95110
mkIdentInfos mod (AvailName n) =
96-
[IdentInfo (nameOccName n) (renderIEWrapped n) Nothing (isDataConName n) mod]
111+
[IdentInfo (nameOccName n) Nothing mod]
97112
mkIdentInfos mod (AvailFL fl) =
98-
[IdentInfo (nameOccName n) (renderIEWrapped n) Nothing (isDataConName n) mod]
113+
[IdentInfo (nameOccName n) Nothing mod]
99114
where
100115
n = flSelector fl
101116
mkIdentInfos mod (AvailTC parent (n:nn) flds)
102117
-- Following the GHC convention that parent == n if parent is exported
103118
| n == parent
104-
= [ IdentInfo (nameOccName n) (renderIEWrapped n) (Just $! parentP) (isDataConName n) mod
119+
= [ IdentInfo (nameOccName n) (Just $! nameOccName parent) mod
105120
| n <- nn ++ map flSelector flds
106121
] ++
107-
[ IdentInfo (nameOccName n) (renderIEWrapped n) Nothing (isDataConName n) mod]
108-
where
109-
parentP = pack $ printName parent
122+
[ IdentInfo (nameOccName n) Nothing mod]
110123

111124
mkIdentInfos mod (AvailTC _ nn flds)
112-
= [ IdentInfo (nameOccName n) (renderIEWrapped n) Nothing (isDataConName n) mod
125+
= [ IdentInfo (nameOccName n) Nothing mod
113126
| n <- nn ++ map flSelector flds
114127
]
115128

@@ -160,25 +173,20 @@ createExportsMapHieDb withHieDb = do
160173
mods <- withHieDb getAllIndexedMods
161174
idents <- forM (filter (nonInternalModules . modInfoName . hieModInfo) mods) $ \m -> do
162175
let mn = modInfoName $ hieModInfo m
163-
mText = pack $ moduleNameString mn
164-
fmap (wrap . unwrap mText) <$> withHieDb (\hieDb -> getExportsForModule hieDb mn)
176+
fmap (wrap . unwrap mn) <$> withHieDb (\hieDb -> getExportsForModule hieDb mn)
165177
let exportsMap = Map.fromListWith (<>) (concat idents)
166-
return $ ExportsMap exportsMap $ buildModuleExportMap (concat idents)
178+
return $! ExportsMap exportsMap $ buildModuleExportMap (concat idents)
167179
where
168180
wrap identInfo = (rendered identInfo, Set.fromList [identInfo])
169181
-- unwrap :: ExportRow -> IdentInfo
170-
unwrap m ExportRow{..} = IdentInfo exportName n p exportIsDatacon m
171-
where
172-
n = pack (occNameString exportName)
173-
p = pack . occNameString <$> exportParent
182+
unwrap m ExportRow{..} = IdentInfo exportName exportParent m
174183

175184
unpackAvail :: ModuleName -> IfaceExport -> [(Text, Text, [IdentInfo])]
176185
unpackAvail mn
177-
| nonInternalModules mn = map f . mkIdentInfos mod
186+
| nonInternalModules mn = map f . mkIdentInfos mn
178187
| otherwise = const []
179188
where
180-
!mod = pack $ moduleNameString mn
181-
f id@IdentInfo {..} = (printOutputable name, moduleNameText,[id])
189+
f id@IdentInfo {..} = (printOutputable name, moduleNameText id,[id])
182190

183191

184192
identInfoToKeyVal :: IdentInfo -> (ModuleNameText, IdentInfo)
@@ -198,9 +206,9 @@ buildModuleExportMapFrom modIfaces = do
198206

199207
extractModuleExports :: ModIface -> (Text, HashSet IdentInfo)
200208
extractModuleExports modIFace = do
201-
let modName = pack $ moduleNameString $ moduleName $ mi_module modIFace
209+
let modName = moduleName $ mi_module modIFace
202210
let functionSet = Set.fromList $ concatMap (mkIdentInfos modName) $ mi_exports modIFace
203-
(modName, functionSet)
211+
(moduleNameText' modName, functionSet)
204212

205213
sortAndGroup :: [(ModuleNameText, IdentInfo)] -> Map.HashMap ModuleNameText (HashSet IdentInfo)
206214
sortAndGroup assocs = Map.fromListWith (<>) [(k, Set.fromList [v]) | (k, v) <- assocs]

plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import Data.Maybe
3737
import Data.Ord (comparing)
3838
import qualified Data.Set as S
3939
import qualified Data.Text as T
40+
import qualified Data.Text.Encoding as T
4041
import qualified Data.Text.Utf16.Rope as Rope
4142
import Data.Tuple.Extra (fst3)
4243
import Development.IDE.Core.Rules
@@ -1048,11 +1049,9 @@ suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_
10481049
-- fallback to using GHC suggestion even though it is not always correct
10491050
| otherwise
10501051
= Just IdentInfo
1051-
{ name = mkVarOcc $ T.unpack binding
1052-
, rendered = binding
1052+
{ name = mkVarOccFS $ mkFastStringByteString $ T.encodeUtf8 binding
10531053
, parent = Nothing
1054-
, isDatacon = False
1055-
, moduleNameText = mod}
1054+
, identModuleName = mkModuleNameFS $ mkFastStringByteString $ T.encodeUtf8 mod}
10561055
#endif
10571056

10581057
data HidingMode
@@ -1452,18 +1451,18 @@ suggestNewOrExtendImportForClassMethod packageExportsMap ps fileContents Diagnos
14521451
_message
14531452
"‘([^’]*)’ is not a \\(visible\\) method of class ‘([^’]*)’",
14541453
idents <-
1455-
maybe [] (Set.toList . Set.filter (\x -> parent x == Just className)) $
1454+
maybe [] (Set.toList . Set.filter (\x -> fmap occNameText (parent x) == Just className)) $
14561455
Map.lookup methodName $ getExportsMap packageExportsMap =
14571456
mconcat $ suggest <$> idents
14581457
| otherwise = []
14591458
where
1460-
suggest identInfo@IdentInfo {moduleNameText}
1459+
suggest identInfo
14611460
| importStyle <- NE.toList $ importStyles identInfo,
1462-
mImportDecl <- findImportDeclByModuleName (hsmodImports . unLoc . astA $ ps) (T.unpack moduleNameText) =
1461+
mImportDecl <- findImportDeclByModuleName (hsmodImports . unLoc . astA $ ps) (T.unpack moduleText) =
14631462
case mImportDecl of
14641463
-- extend
14651464
Just decl ->
1466-
[ ( "Add " <> renderImportStyle style <> " to the import list of " <> moduleNameText,
1465+
[ ( "Add " <> renderImportStyle style <> " to the import list of " <> moduleText,
14671466
quickFixImportKind' "extend" style,
14681467
[Right $ uncurry extendImport (unImportStyle style) decl]
14691468
)
@@ -1474,12 +1473,13 @@ suggestNewOrExtendImportForClassMethod packageExportsMap ps fileContents Diagnos
14741473
| Just (range, indent) <- newImportInsertRange ps fileContents
14751474
->
14761475
(\(kind, unNewImport -> x) -> (x, kind, [Left $ TextEdit range (x <> "\n" <> T.replicate indent " ")])) <$>
1477-
[ (quickFixImportKind' "new" style, newUnqualImport moduleNameText rendered False)
1476+
[ (quickFixImportKind' "new" style, newUnqualImport moduleText rendered False)
14781477
| style <- importStyle,
14791478
let rendered = renderImportStyle style
14801479
]
1481-
<> [(quickFixImportKind "new.all", newImportAll moduleNameText)]
1480+
<> [(quickFixImportKind "new.all", newImportAll moduleText)]
14821481
| otherwise -> []
1482+
where moduleText = moduleNameText identInfo
14831483
#endif
14841484

14851485
suggestNewImport :: ExportsMap -> Annotated ParsedSource -> T.Text -> Diagnostic -> [(T.Text, CodeActionKind, TextEdit)]
@@ -2039,16 +2039,18 @@ data ImportStyle
20392039
deriving Show
20402040

20412041
importStyles :: IdentInfo -> NonEmpty ImportStyle
2042-
importStyles IdentInfo {parent, rendered, isDatacon}
2043-
| Just p <- parent
2042+
importStyles i@IdentInfo {parent}
2043+
| Just p <- pr
20442044
-- Constructors always have to be imported via their parent data type, but
20452045
-- methods and associated type/data families can also be imported as
20462046
-- top-level exports.
2047-
= ImportViaParent rendered p
2048-
:| [ImportTopLevel rendered | not isDatacon]
2047+
= ImportViaParent rend p
2048+
:| [ImportTopLevel rend | not (isDatacon i)]
20492049
<> [ImportAllConstructors p]
20502050
| otherwise
2051-
= ImportTopLevel rendered :| []
2051+
= ImportTopLevel rend :| []
2052+
where rend = rendered i
2053+
pr = occNameText <$> parent
20522054

20532055
-- | Used for adding new imports
20542056
renderImportStyle :: ImportStyle -> T.Text

0 commit comments

Comments
 (0)