Skip to content

Commit 06595c6

Browse files
committed
Cleanup Context module a bit
1 parent 1a1012a commit 06595c6

1 file changed

Lines changed: 70 additions & 63 deletions

File tree

  • ghcide/src/Development/IDE/Plugin/Completions

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

Lines changed: 70 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ import Development.IDE.GHC.Compat hiding (getContext)
2424
import GHC.Generics (Generic)
2525
import GHC.Hs (HasLoc)
2626

27-
-- | A context of a declaration in the program
28-
-- e.g. is the declaration a type declaration or a value declaration
29-
-- Used for determining which code completions to show
27+
-- | A context of a declaration in the program e.g. is the declaration a
28+
-- type declaration or a value declaration. Used for determining which code
29+
-- completions to show.
3030
data Context
3131
= TypeContext
3232
| ValueContext
@@ -53,22 +53,6 @@ data ContextGroup
5353
| DeclarationGroup
5454
deriving (Show, Eq, Ord)
5555

56-
instance Pretty Context where
57-
pretty = \case
58-
TypeContext -> "type context"
59-
ValueContext -> "value context"
60-
ImportContext mod -> "import context " <> pretty mod
61-
ImportListContext mod -> "import explicit context " <> pretty mod
62-
ImportHidingContext mod -> "import hiding context " <> pretty mod
63-
TopContext cg -> "top context " <> pretty cg
64-
DefaultContext -> "unknown context"
65-
66-
instance Pretty ContextGroup where
67-
pretty = \case
68-
HeaderGroup -> "header"
69-
ImportGroup -> "imports"
70-
DeclarationGroup -> "declarations"
71-
7256
data GetContextMap = GetContextMap
7357
deriving (Eq, Show, Generic)
7458
instance Hashable GetContextMap
@@ -101,26 +85,27 @@ groupedChunks n group getPos locate xs = ContextMap $ go xs
10185
, context
10286
} : go rest
10387

104-
data ContextResult
105-
= NoContext
106-
| ContextResult Range Context
88+
-- | Used during context finding, combines into the tightest interval.
89+
-- As an intuition, the primary interface is through
90+
-- @Monoid (Position -> ContextResult)@.
91+
data ContextResult = NoContext | ContextResult Range Context
92+
instance Monoid ContextResult where mempty = NoContext
93+
instance Semigroup ContextResult where (<>) = tighten
10794

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
95+
tighten :: ContextResult -> ContextResult -> ContextResult
96+
tighten NoContext b = b
97+
tighten a NoContext = a
98+
tighten ar@(ContextResult a _) br@(ContextResult b _) =
99+
if a `dominates` b then br else ar
117100

118101
newtype ContextMap = ContextMap [ContextChunk]
119102
deriving newtype (Monoid, Semigroup)
120103
instance Show ContextMap where show _ = "<context map>"
121104
instance NFData ContextMap where rnf = rwhnf
122105

123-
-- | Build a 'ContextMap' from a parsed module.
106+
-- * Building
107+
108+
-- | Build a @ContextMap@ from a parsed module.
124109
--
125110
-- Walks module header, exports, imports, and top-level declarations
126111
-- (one level into class bodies). Built once per file edit and cached
@@ -136,14 +121,6 @@ getContextMap pm =
136121
rangeOf :: HasLoc a => a -> Maybe Range
137122
rangeOf = srcSpanToRange . locA
138123

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-
147124
getImportContext :: LImportDecl GhcPs -> Range -> ContextResult
148125
getImportContext imports query =
149126
everythingBut
@@ -158,6 +135,35 @@ getDeclContext declarations query =
158135
((mempty, False) `mkQ` sigQ query `extQ` bindQ query `extQ` declQ query)
159136
declarations
160137

138+
-- * Querying
139+
140+
-- | Look up the completion context at a given position.
141+
-- Returns the innermost (most specific) context that contains the position.
142+
--
143+
-- Only the 'ContextChunks' up to and including the chunk containing the
144+
-- query position are forced; later chunks remain as unevaluated thunks.
145+
getContext :: ContextMap -> PositionResult Position -> Context
146+
getContext (ContextMap chunks) query =
147+
case searchChunks True chunks of
148+
(groups, NoContext) -> TopContext groups
149+
(_, ContextResult _ found) -> found
150+
where
151+
(qLo, qHi) = case query of
152+
PositionExact p -> (p, p)
153+
PositionRange l u -> (l, u)
154+
155+
searchChunks :: Bool -> [ContextChunk] -> ([ContextGroup], ContextResult)
156+
searchChunks _ [] = ([], mempty)
157+
searchChunks firstChunk (Chunk cLo cHi group contextOf : rest)
158+
| -- query is past this chunk
159+
qLo > cHi = searchChunks False rest
160+
| -- query is before this chunk
161+
qHi < cLo = (if firstChunk then [HeaderGroup] else [], mempty)
162+
-- this chunk is relevant, emit the group and all relevant intervals
163+
| otherwise = ([group], contextOf (Range qLo qHi)) <> searchChunks False rest
164+
165+
-- * SYB queries types
166+
161167
importQ :: Range -> LImportDecl GhcPs -> (ContextResult, Bool)
162168
importQ query impDecl'@(L _ impDecl) =
163169
let importModuleName = T.pack $ moduleNameString $ unLoc $ ideclName impDecl
@@ -190,33 +196,34 @@ sigQ = contextual TypeContext True
190196
bindQ :: Range -> LHsBind GhcPs -> (ContextResult, Bool)
191197
bindQ = contextual ValueContext False
192198

199+
contextual :: HasLoc a => Context -> Bool -> Range -> a -> (ContextResult, Bool)
200+
contextual context shouldStop query s =
201+
let range = rangeOf s
202+
in case range of
203+
Nothing -> (mempty, True)
204+
Just range | outside query range -> (mempty, True)
205+
Just range -> (ContextResult range context, shouldStop)
206+
207+
-- * Helpers
208+
193209
dominates :: Range -> Range -> Bool
194210
dominates (Range s e) (Range qs qe) = s <= qs && qe <= e
195211

196212
outside :: Range -> Range -> Bool
197213
outside (Range ps pe) (Range qs qe) = pe < qs || ps > qe
198214

199-
-- | Look up the completion context at a given position.
200-
-- Returns the innermost (most specific) context that contains the position.
201-
--
202-
-- Only the 'ContextChunks' up to and including the chunk containing the
203-
-- query position are forced; later chunks remain as unevaluated thunks.
204-
getContext :: ContextMap -> PositionResult Position -> Context
205-
getContext (ContextMap chunks) query =
206-
case searchChunks True chunks of
207-
(groups, NoContext) -> TopContext groups
208-
(_, ContextResult _ found) -> found
209-
where
210-
(qLo, qHi) = case query of
211-
PositionExact p -> (p, p)
212-
PositionRange l u -> (l, u)
215+
instance Pretty Context where
216+
pretty = \case
217+
TypeContext -> "type context"
218+
ValueContext -> "value context"
219+
ImportContext mod -> "import context " <> pretty mod
220+
ImportListContext mod -> "import explicit context " <> pretty mod
221+
ImportHidingContext mod -> "import hiding context " <> pretty mod
222+
TopContext cg -> "top context " <> pretty cg
223+
DefaultContext -> "unknown context"
213224

214-
searchChunks :: Bool -> [ContextChunk] -> ([ContextGroup], ContextResult)
215-
searchChunks _ [] = ([], mempty)
216-
searchChunks firstChunk (Chunk cLo cHi group contextOf : rest)
217-
| -- query is past this chunk
218-
qLo > cHi = searchChunks False rest
219-
| -- query is before this chunk
220-
qHi < cLo = (if firstChunk then [HeaderGroup] else [], mempty)
221-
-- this chunk is relevant, emit the group and all relevant intervals
222-
| otherwise = ([group], contextOf (Range qLo qHi)) <> searchChunks False rest
225+
instance Pretty ContextGroup where
226+
pretty = \case
227+
HeaderGroup -> "header"
228+
ImportGroup -> "imports"
229+
DeclarationGroup -> "declarations"

0 commit comments

Comments
 (0)