Skip to content

Commit 1a1012a

Browse files
committed
Use syb in Context chunks
1 parent fb59ce3 commit 1a1012a

2 files changed

Lines changed: 101 additions & 113 deletions

File tree

ghcide-test/exe/CompletionTests.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ contextCompletionTests =
690690
, " sig :: Xxx"
691691
, " sig = undefined"
692692
]
693-
(Position 8 19) -- after "Xxx" in " sig :: Xxx"
693+
(Position 8 18)
694694
[("Xxxtype", CompletionItemKind_Struct, "Xxxtype", False, True, Nothing)]
695695

696696
, completionTest

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

Lines changed: 100 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,14 @@ module Development.IDE.Plugin.Completions.Context
1313
) where
1414

1515
import Control.DeepSeq (NFData (..), rwhnf)
16+
import Data.Generics (extQ, mkQ)
17+
import Data.Generics.Schemes (everythingBut)
1618
import Data.Hashable (Hashable)
17-
import Data.List (maximumBy)
18-
import Data.Maybe (catMaybes, mapMaybe)
19-
import Data.Ord (Down (..), comparing)
19+
import Data.Maybe (mapMaybe)
2020
import qualified Data.Text as T
2121
import Development.IDE
2222
import Development.IDE.Core.PositionMapping
2323
import Development.IDE.GHC.Compat hiding (getContext)
24-
import Development.IDE.GHC.Compat.Util (bagToList)
2524
import GHC.Generics (Generic)
2625
import GHC.Hs (HasLoc)
2726

@@ -76,34 +75,46 @@ instance Hashable GetContextMap
7675
instance NFData GetContextMap
7776
type instance RuleResult GetContextMap = ContextMap
7877

79-
-- | A lazy chunked interval structure for context lookups.
80-
--
81-
-- Entries within each chunk are from a contiguous group of source items
78+
-- | Entries within each chunk are from a contiguous group of source items
8279
-- (imports or declarations).
8380
data ContextChunk = Chunk
84-
{ low :: {-# UNPACK #-} !Position
85-
, high :: {-# UNPACK #-} !Position
86-
, group :: {-# UNPACK #-} !ContextGroup
87-
, items :: [(Range, Context)]
81+
{ low :: {-# UNPACK #-} !Position
82+
, high :: {-# UNPACK #-} !Position
83+
, group :: {-# UNPACK #-} !ContextGroup
84+
, context :: Range -> ContextResult
8885
}
8986

9087
-- | Build lazy 'ContextChunk' by processing @n@ source items at a time.
91-
groupedChunks :: Int -> ContextGroup -> (a -> Maybe Range) -> (a -> [(Range, Context)]) -> [a] -> ContextMap
92-
groupedChunks n group getPos getRanges xs = ContextMap $ go xs
88+
groupedChunks :: Int -> ContextGroup -> (a -> Maybe Range) -> (a -> Range -> ContextResult) -> [a] -> ContextMap
89+
groupedChunks n group getPos locate xs = ContextMap $ go xs
9390
where
9491
go [] = []
9592
go xs =
9693
let (chunk, rest) = splitAt n xs
97-
items = concatMap getRanges chunk
98-
in case items of
94+
context = foldMap locate chunk
95+
in case chunk of
9996
[] -> go rest
10097
_ -> Chunk
10198
{ low = minimum (mapMaybe (fmap _start . getPos) chunk)
10299
, high = maximum (mapMaybe (fmap _end . getPos) chunk)
103100
, group
104-
, items
101+
, context
105102
} : go rest
106103

104+
data ContextResult
105+
= NoContext
106+
| ContextResult Range Context
107+
108+
instance Semigroup ContextResult where
109+
NoContext <> b = b
110+
a <> NoContext = a
111+
ar@(ContextResult a _) <> br@(ContextResult b _) = if a `dominates` b
112+
then br
113+
else ar
114+
115+
instance Monoid ContextResult where
116+
mempty = NoContext
117+
107118
newtype ContextMap = ContextMap [ContextChunk]
108119
deriving newtype (Monoid, Semigroup)
109120
instance Show ContextMap where show _ = "<context map>"
@@ -116,119 +127,96 @@ instance NFData ContextMap where rnf = rwhnf
116127
-- as a Shake rule.
117128
getContextMap :: ParsedModule -> ContextMap
118129
getContextMap pm =
119-
groupedChunks 10 ImportGroup rangeOf importEntry hsmodImports
120-
<> groupedChunks 10 DeclarationGroup rangeOf declEntry hsmodDecls
130+
groupedChunks 10 ImportGroup rangeOf getImportContext hsmodImports
131+
<> groupedChunks 5 DeclarationGroup rangeOf getDeclContext hsmodDecls
121132
where
122133
HsModule {hsmodImports, hsmodDecls} =
123134
unLoc (pm_parsed_source pm)
124135

125-
rangeOf :: HasLoc (Anno a) => XRec GhcPs a -> Maybe Range
126-
rangeOf (L (locA -> ss) _) = srcSpanToRange ss
127-
fromSpan context ss = (,context) <$> rangeOf ss
128-
129-
importEntry :: LImportDecl GhcPs -> [(Range, Context)]
130-
importEntry decl@(L _ impDecl) =
131-
let modName = T.pack $ moduleNameString $ unLoc $ ideclName impDecl
132-
outerCtx = fromSpan (ImportContext modName) decl
133-
innerCtx = importListEntry modName (fmap (fmap reLoc) $ ideclImportList impDecl)
134-
in catMaybes [outerCtx, innerCtx]
135-
136-
importListEntry modName (Just (EverythingBut, imps)) = fromSpan (ImportHidingContext modName) imps
137-
importListEntry modName (Just (Exactly, imps)) = fromSpan (ImportHidingContext modName) imps
138-
importListEntry _ _ = Nothing
139-
140-
declEntry :: LHsDecl GhcPs -> [(Range, Context)]
141-
declEntry (L (locA -> ss) decl) = case srcSpanToRange ss of
142-
Nothing -> []
143-
Just range -> case decl of
144-
SigD {} -> [(range, TypeContext)]
145-
ValD _ bind -> (range, ValueContext) : bindEntries bind
146-
TyClD _ cd@ClassDecl{} -> (range, TypeContext) : classEntries cd
147-
TyClD {} -> [(range, TypeContext)] -- DataDecl, SynDecl, FamilyDecl
148-
InstD _ instDecl -> (range, ValueContext) : instEntries instDecl
149-
DerivD {} -> [(range, TypeContext)]
150-
ForD {} -> [(range, ValueContext)]
151-
SpliceD {} -> [(range, TopContext [])]
152-
_ -> [(range, DefaultContext)] -- DefD, WarningD, AnnD, RuleD, DocD, KindSigD
153-
154-
sigsAndBindEntries :: [LSig GhcPs] -> LHsBinds GhcPs -> [(Range, Context)]
155-
sigsAndBindEntries sigs binds =
156-
[ (r, TypeContext)
157-
| L (locA -> ss) _ <- sigs
158-
, Just r <- [srcSpanToRange ss]
159-
] ++
160-
[ entry
161-
| L (locA -> ss) bind <- bagToList binds
162-
, Just r <- [srcSpanToRange ss]
163-
, entry <- (r, ValueContext) : bindEntries bind
164-
]
165-
166-
classEntries :: TyClDecl GhcPs -> [(Range, Context)]
167-
classEntries ClassDecl { tcdSigs, tcdMeths } = sigsAndBindEntries tcdSigs tcdMeths
168-
classEntries _ = []
169-
170-
instEntries :: InstDecl GhcPs -> [(Range, Context)]
171-
instEntries ClsInstD { cid_inst = ClsInstDecl { cid_sigs, cid_binds } } =
172-
sigsAndBindEntries cid_sigs cid_binds
173-
instEntries _ = []
174-
175-
bindEntries :: HsBind GhcPs -> [(Range, Context)]
176-
bindEntries FunBind { fun_matches = MG { mg_alts = L _ alts } } =
177-
concatMap matchLocalEntries alts
178-
bindEntries PatBind { pat_rhs = GRHSs { grhssLocalBinds, grhssGRHSs } } =
179-
localBindEntries grhssLocalBinds
180-
++ concatMap exprLocalEntries [ body | L _ (GRHS _ _ body) <- grhssGRHSs ]
181-
bindEntries _ = []
182-
183-
matchLocalEntries :: LMatch GhcPs (LHsExpr GhcPs) -> [(Range, Context)]
184-
matchLocalEntries (L _ Match { m_grhss = GRHSs { grhssLocalBinds, grhssGRHSs } }) =
185-
localBindEntries grhssLocalBinds
186-
++ concatMap exprLocalEntries [ body | L _ (GRHS _ _ body) <- grhssGRHSs ]
187-
188-
localBindEntries :: HsLocalBinds GhcPs -> [(Range, Context)]
189-
localBindEntries (HsValBinds _ (ValBinds _ binds sigs)) =
190-
sigsAndBindEntries sigs binds
191-
localBindEntries _ = []
192-
193-
exprLocalEntries :: LHsExpr GhcPs -> [(Range, Context)]
194-
exprLocalEntries (L _ expr) = case expr of
195-
#if !MIN_VERSION_ghc(9,9,0)
196-
HsLet _ _ binds _ body -> localBindEntries binds ++ exprLocalEntries body
197-
#else
198-
HsLet _ binds body -> localBindEntries binds ++ exprLocalEntries body
199-
#endif
200-
HsDo _ _ stmts ->
201-
[ entry
202-
| L _ (LetStmt _ lbs) <- unLoc stmts
203-
, entry <- localBindEntries lbs
204-
]
205-
_ -> []
136+
rangeOf :: HasLoc a => a -> Maybe Range
137+
rangeOf = srcSpanToRange . locA
138+
139+
contextual :: HasLoc a => Context -> Bool -> Range -> a -> (ContextResult, Bool)
140+
contextual context shouldStop query s =
141+
let range = rangeOf s
142+
in case range of
143+
Nothing -> (mempty, True)
144+
Just range | outside query range -> (mempty, True)
145+
Just range -> (ContextResult range context, shouldStop)
146+
147+
getImportContext :: LImportDecl GhcPs -> Range -> ContextResult
148+
getImportContext imports query =
149+
everythingBut
150+
(<>)
151+
((mempty, False) `mkQ` importQ query)
152+
imports
153+
154+
getDeclContext :: LHsDecl GhcPs -> Range -> ContextResult
155+
getDeclContext declarations query =
156+
everythingBut
157+
(<>)
158+
((mempty, False) `mkQ` sigQ query `extQ` bindQ query `extQ` declQ query)
159+
declarations
160+
161+
importQ :: Range -> LImportDecl GhcPs -> (ContextResult, Bool)
162+
importQ query impDecl'@(L _ impDecl) =
163+
let importModuleName = T.pack $ moduleNameString $ unLoc $ ideclName impDecl
164+
inlineResults = fst $ importInline query importModuleName (ideclImportList impDecl)
165+
importResult = fst $ contextual (ImportContext importModuleName) True query impDecl'
166+
importInline _ _ Nothing = (mempty, False)
167+
importInline query modName (Just (which, l)) =
168+
case which of
169+
EverythingBut -> contextual (ImportHidingContext modName) True query l
170+
Exactly -> contextual (ImportListContext modName) True query l
171+
in (inlineResults <> importResult, False)
172+
173+
174+
declQ :: Range -> LHsDecl GhcPs -> (ContextResult, Bool)
175+
declQ query (L (locA -> ss) decl) = case srcSpanToRange ss of
176+
Nothing -> (mempty, True)
177+
Just range | outside query range -> (mempty, True)
178+
Just range -> case decl of
179+
SigD {} -> (ContextResult range TypeContext, True)
180+
ValD {} -> (ContextResult range ValueContext, False)
181+
TyClD {} -> (ContextResult range TypeContext, False) -- DataDecl, SynDecl, FamilyDecl
182+
InstD {} -> (ContextResult range ValueContext, False)
183+
DerivD {} -> (ContextResult range TypeContext, True)
184+
SpliceD {} -> (ContextResult range (TopContext []), True)
185+
_ -> (ContextResult range DefaultContext, True) -- DefD, WarningD, AnnD, RuleD, DocD, KindSigD
186+
187+
sigQ :: Range -> LSig GhcPs -> (ContextResult, Bool)
188+
sigQ = contextual TypeContext True
189+
190+
bindQ :: Range -> LHsBind GhcPs -> (ContextResult, Bool)
191+
bindQ = contextual ValueContext False
192+
193+
dominates :: Range -> Range -> Bool
194+
dominates (Range s e) (Range qs qe) = s <= qs && qe <= e
195+
196+
outside :: Range -> Range -> Bool
197+
outside (Range ps pe) (Range qs qe) = pe < qs || ps > qe
206198

207199
-- | Look up the completion context at a given position.
208200
-- Returns the innermost (most specific) context that contains the position.
209201
--
210202
-- Only the 'ContextChunks' up to and including the chunk containing the
211203
-- query position are forced; later chunks remain as unevaluated thunks.
212204
getContext :: ContextMap -> PositionResult Position -> Context
213-
getContext (ContextMap chunks) pos =
205+
getContext (ContextMap chunks) query =
214206
case searchChunks True chunks of
215-
([], []) -> TopContext []
216-
(groups, []) -> TopContext groups
217-
(_, xs) -> snd $ maximumBy (comparing (\(Range s e, _) -> (s, Down e))) xs
207+
(groups, NoContext) -> TopContext groups
208+
(_, ContextResult _ found) -> found
218209
where
219-
(qLo, qHi) = case pos of
210+
(qLo, qHi) = case query of
220211
PositionExact p -> (p, p)
221212
PositionRange l u -> (l, u)
222213

223-
dominates :: (Range, Context) -> Bool
224-
dominates (Range s e, _) = s <= qLo && qHi <= e
225-
226-
searchChunks :: Bool -> [ContextChunk] -> ([ContextGroup], [(Range, Context)])
227-
searchChunks _ [] = ([], [])
228-
searchChunks firstChunk (Chunk cLo cHi group items : rest)
214+
searchChunks :: Bool -> [ContextChunk] -> ([ContextGroup], ContextResult)
215+
searchChunks _ [] = ([], mempty)
216+
searchChunks firstChunk (Chunk cLo cHi group contextOf : rest)
229217
| -- query is past this chunk
230218
qLo > cHi = searchChunks False rest
231219
| -- query is before this chunk
232-
qHi < cLo = (if firstChunk then [HeaderGroup] else [], [])
220+
qHi < cLo = (if firstChunk then [HeaderGroup] else [], mempty)
233221
-- this chunk is relevant, emit the group and all relevant intervals
234-
| otherwise = ([group], filter dominates items) <> searchChunks False rest
222+
| otherwise = ([group], contextOf (Range qLo qHi)) <> searchChunks False rest

0 commit comments

Comments
 (0)