@@ -16,29 +16,29 @@ import Development.IDE (GetHieAst (GetHieAst),
1616 IdeState (shakeExtras ),
1717 Pretty (pretty ),
1818 Recorder , WithPriority ,
19- printOutputable )
19+ printOutputableOneLine )
2020import Development.IDE.Core.PluginUtils (runIdeActionE ,
2121 useWithStaleFastE )
2222import Development.IDE.Core.PositionMapping (fromCurrentPosition )
2323import Development.IDE.GHC.Compat (FastStringCompat , Name ,
24- RealSrcSpan , SDoc ,
24+ RealSrcSpan ,
2525 getSourceNodeIds ,
26- hie_types ,
2726 isAnnotationInNodeInfo ,
2827 mkRealSrcLoc ,
2928 mkRealSrcSpan , ppr ,
3029 sourceNodeInfo )
3130import Development.IDE.GHC.Compat.Util (LexicalFastString (LexicalFastString ))
3231import GHC.Core.Map.Type (deBruijnize )
32+ import GHC.Core.Type (FunTyFlag (FTF_T_T ),
33+ Type , dropForAlls ,
34+ splitFunTy_maybe )
3335import GHC.Data.Maybe (rightToMaybe )
3436import GHC.Iface.Ext.Types (ContextInfo (Use ),
3537 HieAST (nodeChildren , nodeSpan ),
3638 HieASTs (getAsts ),
3739 IdentifierDetails (identInfo , identType ),
3840 nodeType )
39- import GHC.Iface.Ext.Utils (hieTypeToIface ,
40- recoverFullType ,
41- smallestContainingSatisfying )
41+ import GHC.Iface.Ext.Utils (smallestContainingSatisfying )
4242import GHC.Types.SrcLoc (isRealSubspanOf )
4343import Ide.Plugin.Error (getNormalizedFilePathE )
4444import Ide.Types (PluginDescriptor (pluginHandlers ),
@@ -91,44 +91,99 @@ signatureHelpProvider ideState _pluginId (SignatureHelpParams (TextDocumentIdent
9191 Just (functionName, functionTypes, argumentNumber)
9292 )
9393 case results of
94- -- TODO(@linj) what does non-singleton list mean?
94+ [(_functionName, [] , _argumentNumber)] -> pure $ InR Null
9595 [(functionName, functionTypes, argumentNumber)] ->
9696 pure $ InL $ mkSignatureHelp (fromIntegral argumentNumber - 1 ) functionName functionTypes
97+ -- TODO(@linj) what does non-singleton list mean?
9798 _ -> pure $ InR Null
9899
99- mkSignatureHelp :: UInt -> Name -> [Text ] -> SignatureHelp
100+ mkSignatureHelp :: UInt -> Name -> [Type ] -> SignatureHelp
100101mkSignatureHelp argumentNumber functionName functionTypes =
101102 SignatureHelp
102103 (mkSignatureInformation argumentNumber functionName <$> functionTypes)
103104 (Just 0 )
104105 (Just $ InL argumentNumber)
105106
106- mkSignatureInformation :: UInt -> Name -> Text -> SignatureInformation
107+ mkSignatureInformation :: UInt -> Name -> Type -> SignatureInformation
107108mkSignatureInformation argumentNumber functionName functionType =
108- let functionNameLabelPrefix = printOutputable (ppr functionName) <> " :: "
109+ let functionNameLabelPrefix = printOutputableOneLine (ppr functionName) <> " :: "
109110 in SignatureInformation
110- (functionNameLabelPrefix <> functionType)
111+ (functionNameLabelPrefix <> printOutputableOneLine functionType)
111112 Nothing
112113 (Just $ mkArguments (fromIntegral $ T. length functionNameLabelPrefix) functionType)
113114 (Just $ InL argumentNumber)
114115
115- -- TODO(@linj) can type string be a multi-line string?
116- mkArguments :: UInt -> Text -> [ParameterInformation ]
116+ mkArguments :: UInt -> Type -> [ParameterInformation ]
117117mkArguments offset functionType =
118- let separator = " -> "
119- separatorLength = fromIntegral $ T. length separator
120- splits = T. breakOnAll separator functionType
121- prefixes = fst <$> splits
122- prefixLengths = fmap (T. length >>> fromIntegral ) prefixes
123- ranges =
124- [ ( if previousPrefixLength == 0 then 0 else previousPrefixLength + separatorLength,
125- currentPrefixLength
126- )
127- | (previousPrefixLength, currentPrefixLength) <- zip (0 : prefixLengths) prefixLengths
128- ]
129- in [ ParameterInformation (InR range) Nothing
130- | range <- bimap (+ offset) (+ offset) <$> ranges
131- ]
118+ [ ParameterInformation (InR range) Nothing
119+ | range <- bimap (+ offset) (+ offset) <$> findArgumentRanges functionType
120+ ]
121+
122+ findArgumentRanges :: Type -> [(UInt , UInt )]
123+ findArgumentRanges functionType =
124+ let functionTypeString = printOutputableOneLine functionType
125+ functionTypeStringLength = fromIntegral $ T. length functionTypeString
126+ splitFunctionTypes = filter notTypeConstraint $ splitFunTysIgnoringForAll functionType
127+ splitFunctionTypeStrings = printOutputableOneLine . fst <$> splitFunctionTypes
128+ -- reverse to avoid matching "a" of "forall a" in "forall a. a -> a"
129+ reversedRanges =
130+ drop 1 $ -- do not need the range of the result (last) type
131+ findArgumentStringRanges
132+ 0
133+ (T. reverse functionTypeString)
134+ (T. reverse <$> reverse splitFunctionTypeStrings)
135+ in reverse $ modifyRange functionTypeStringLength <$> reversedRanges
136+ where
137+ modifyRange functionTypeStringLength (start, end) =
138+ (functionTypeStringLength - end, functionTypeStringLength - start)
139+
140+ {-
141+ The implemented method uses both structured type and unstructured type string.
142+ It provides good enough results and is easier to implement than alternative
143+ method 1 or 2.
144+
145+ Alternative method 1: use only structured type
146+ This method is hard to implement because we need to duplicate some logic of 'ppr' for 'Type'.
147+ Some tricky cases are as follows:
148+ - 'Eq a => Num b -> c' is shown as '(Eq a, Numb) => c'
149+ - 'forall' can appear anywhere in a type when RankNTypes is enabled
150+ f :: forall a. Maybe a -> forall b. (a, b) -> b
151+ - '=>' can appear anywhere in a type
152+ g :: forall a b. Eq a => a -> Num b => b -> b
153+ - ppr the first argument type of '(a -> b) -> a -> b' is 'a -> b' (no parentheses)
154+ - 'forall' is not always shown
155+
156+ Alternative method 2: use only unstructured type string
157+ This method is hard to implement because we need to parse the type string.
158+ Some tricky cases are as follows:
159+ - h :: forall a (m :: Type -> Type). Monad m => a -> m a
160+ -}
161+ findArgumentStringRanges :: UInt -> Text -> [Text ] -> [(UInt , UInt )]
162+ findArgumentStringRanges _totalPrefixLength _functionTypeString [] = []
163+ findArgumentStringRanges totalPrefixLength functionTypeString (argumentTypeString: restArgumentTypeStrings) =
164+ let (prefix, match) = T. breakOn argumentTypeString functionTypeString
165+ prefixLength = fromIntegral $ T. length prefix
166+ argumentTypeStringLength = fromIntegral $ T. length argumentTypeString
167+ start = totalPrefixLength + prefixLength
168+ in (start, start + argumentTypeStringLength)
169+ : findArgumentStringRanges
170+ (totalPrefixLength + prefixLength + argumentTypeStringLength)
171+ (T. drop (fromIntegral argumentTypeStringLength) match)
172+ restArgumentTypeStrings
173+
174+ -- similar to 'splitFunTys' but
175+ -- 1) the result (last) type is included and
176+ -- 2) toplevel foralls are ignored
177+ splitFunTysIgnoringForAll :: Type -> [(Type , Maybe FunTyFlag )]
178+ splitFunTysIgnoringForAll ty = case ty & dropForAlls & splitFunTy_maybe of
179+ Just (funTyFlag, _mult, argumentType, resultType) ->
180+ (argumentType, Just funTyFlag) : splitFunTysIgnoringForAll resultType
181+ Nothing -> [(ty, Nothing )]
182+
183+ notTypeConstraint :: (Type , Maybe FunTyFlag ) -> Bool
184+ notTypeConstraint (_type, Just FTF_T_T ) = True
185+ notTypeConstraint (_type, Nothing ) = True
186+ notTypeConstraint _ = False
132187
133188extractInfoFromSmallestContainingFunctionApplicationAst ::
134189 Position -> HieASTs a -> (RealSrcSpan -> HieAST a -> Maybe b ) -> [b ]
@@ -156,7 +211,7 @@ getLeftMostNode thisNode =
156211 [] -> thisNode
157212 leftChild: _ -> getLeftMostNode leftChild
158213
159- getNodeNameAndTypes :: forall a . HieKind a -> HieAST a -> Maybe (Name , [Text ])
214+ getNodeNameAndTypes :: HieKind a -> HieAST a -> Maybe (Name , [Type ])
160215getNodeNameAndTypes hieKind hieAst =
161216 if nodeHasAnnotation (" HsVar" , " HsExpr" ) hieAst
162217 then case hieAst & getSourceNodeIds & M. filter isUse & M. assocs of
@@ -171,26 +226,21 @@ getNodeNameAndTypes hieKind hieAst =
171226 allTypes = case mTypeOfName of
172227 Nothing -> typesOfNode
173228 Just typeOfName -> typeOfName : filter (isDifferentType typeOfName) typesOfNode
174- in Just (name, prettyType <$> allTypes)
229+ in Just (name, filterCoreTypes allTypes)
175230 [] -> Nothing
176231 _ -> Nothing -- seems impossible
177232 else Nothing -- TODO(@linj) must function node be HsVar?
178233 where
179234 extractName = rightToMaybe
180235
181- isDifferentType :: a -> a -> Bool
182236 isDifferentType type1 type2 = case hieKind of
183- HieFresh -> deBruijnize type1 /= deBruijnize type2
184- HieFromDisk _hieFile -> type1 /= type2
185-
186- -- modified from Development.IDE.Spans.AtPoint.atPoint
187- prettyType :: a -> Text
188- prettyType = expandType >>> printOutputable
237+ HieFresh -> deBruijnize type1 /= deBruijnize type2
238+ HieFromDisk {} -> type1 /= type2
189239
190- expandType :: a -> SDoc
191- expandType t = case hieKind of
192- HieFresh -> ppr t
193- HieFromDisk hieFile -> ppr $ hieTypeToIface $ recoverFullType t (hie_types hieFile)
240+ filterCoreTypes types = case hieKind of
241+ HieFresh -> types
242+ -- ignore this case since this only happens before we finish startup
243+ HieFromDisk {} -> []
194244
195245isUse :: IdentifierDetails a -> Bool
196246isUse = identInfo >>> S. member Use
0 commit comments