-
-
Notifications
You must be signed in to change notification settings - Fork 364
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Jump to instance definition and explain typeclass evidence #4392
Open
fendor
wants to merge
4
commits into
haskell:master
Choose a base branch
from
fendor:fendor/wip/evidence-info
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ module Development.IDE.Spans.AtPoint ( | |
atPoint | ||
, gotoDefinition | ||
, gotoTypeDefinition | ||
, gotoImplementation | ||
, documentHighlight | ||
, pointCommand | ||
, referencesAtPoint | ||
|
@@ -23,6 +24,10 @@ module Development.IDE.Spans.AtPoint ( | |
, LookupModule | ||
) where | ||
|
||
|
||
import GHC.Data.FastString (lengthFS) | ||
import qualified GHC.Utils.Outputable as O | ||
|
||
import Development.IDE.GHC.Error | ||
import Development.IDE.GHC.Orphans () | ||
import Development.IDE.Types.Location | ||
|
@@ -52,9 +57,13 @@ import qualified Data.Text as T | |
|
||
import qualified Data.Array as A | ||
import Data.Either | ||
import Data.List (isSuffixOf) | ||
import Data.List.Extra (dropEnd1, nubOrd) | ||
|
||
|
||
import Data.Either.Extra (eitherToMaybe) | ||
import Data.List (isSuffixOf, sortOn) | ||
import Data.Tree | ||
import qualified Data.Tree as T | ||
import Data.Version (showVersion) | ||
import Development.IDE.Types.Shake (WithHieDb) | ||
import HieDb hiding (pointCommand, | ||
|
@@ -171,14 +180,18 @@ documentHighlight hf rf pos = pure highlights | |
highlights = do | ||
n <- ns | ||
ref <- fromMaybe [] (M.lookup (Right n) rf) | ||
pure $ makeHighlight ref | ||
makeHighlight (sp,dets) = | ||
DocumentHighlight (realSrcSpanToRange sp) (Just $ highlightType $ identInfo dets) | ||
maybeToList (makeHighlight n ref) | ||
makeHighlight n (sp,dets) | ||
| isTvNameSpace (nameNameSpace n) && isBadSpan n sp = Nothing | ||
| otherwise = Just $ DocumentHighlight (realSrcSpanToRange sp) (Just $ highlightType $ identInfo dets) | ||
highlightType s = | ||
if any (isJust . getScopeFromContext) s | ||
then DocumentHighlightKind_Write | ||
else DocumentHighlightKind_Read | ||
|
||
isBadSpan :: Name -> RealSrcSpan -> Bool | ||
isBadSpan n sp = srcSpanStartLine sp /= srcSpanEndLine sp || (srcSpanEndCol sp - srcSpanStartCol sp > lengthFS (occNameFS $ nameOccName n)) | ||
|
||
-- | Locate the type definition of the name at a given position. | ||
gotoTypeDefinition | ||
:: MonadIO m | ||
|
@@ -198,12 +211,25 @@ gotoDefinition | |
-> LookupModule m | ||
-> IdeOptions | ||
-> M.Map ModuleName NormalizedFilePath | ||
-> HieASTs a | ||
-> HieAstResult | ||
-> Position | ||
-> MaybeT m [(Location, Identifier)] | ||
gotoDefinition withHieDb getHieFile ideOpts imports srcSpans pos | ||
= lift $ locationsAtPoint withHieDb getHieFile ideOpts imports pos srcSpans | ||
|
||
-- | Locate the implementation definition of the name at a given position. | ||
-- Goto Implementation for an overloaded function. | ||
gotoImplementation | ||
:: MonadIO m | ||
=> WithHieDb | ||
-> LookupModule m | ||
-> IdeOptions | ||
-> HieAstResult | ||
-> Position | ||
-> MaybeT m [Location] | ||
gotoImplementation withHieDb getHieFile ideOpts srcSpans pos | ||
= lift $ instanceLocationsAtPoint withHieDb getHieFile ideOpts pos srcSpans | ||
|
||
-- | Synopsis for the name at a given position. | ||
atPoint | ||
:: IdeOptions | ||
|
@@ -212,13 +238,13 @@ atPoint | |
-> HscEnv | ||
-> Position | ||
-> IO (Maybe (Maybe Range, [T.Text])) | ||
atPoint IdeOptions{} (HAR _ hf _ _ (kind :: HieKind hietype)) (DKMap dm km) env pos = | ||
atPoint IdeOptions{} (HAR _ (hf :: HieASTs a) rf _ (kind :: HieKind hietype)) (DKMap dm km) env pos = | ||
listToMaybe <$> sequence (pointCommand hf pos hoverInfo) | ||
where | ||
-- Hover info for values/data | ||
hoverInfo :: HieAST hietype -> IO (Maybe Range, [T.Text]) | ||
hoverInfo ast = do | ||
prettyNames <- mapM prettyName filteredNames | ||
prettyNames <- mapM prettyName names | ||
pure (Just range, prettyNames ++ pTypes) | ||
where | ||
pTypes :: [T.Text] | ||
|
@@ -235,24 +261,20 @@ atPoint IdeOptions{} (HAR _ hf _ _ (kind :: HieKind hietype)) (DKMap dm km) env | |
info :: NodeInfo hietype | ||
info = nodeInfoH kind ast | ||
|
||
-- We want evidence variables to be displayed last. | ||
-- Evidence trees contain information of secondary relevance. | ||
names :: [(Identifier, IdentifierDetails hietype)] | ||
names = M.assocs $ nodeIdentifiers info | ||
|
||
-- Check for evidence bindings | ||
isInternal :: (Identifier, IdentifierDetails a) -> Bool | ||
isInternal (Right _, dets) = | ||
any isEvidenceContext $ identInfo dets | ||
isInternal (Left _, _) = False | ||
|
||
filteredNames :: [(Identifier, IdentifierDetails hietype)] | ||
filteredNames = filter (not . isInternal) names | ||
names = sortOn (any isEvidenceUse . identInfo . snd) $ M.assocs $ nodeIdentifiers info | ||
|
||
prettyName :: (Either ModuleName Name, IdentifierDetails hietype) -> IO T.Text | ||
prettyName (Right n, dets) = pure $ T.unlines $ | ||
wrapHaskell (printOutputable n <> maybe "" (" :: " <>) ((prettyType <$> identType dets) <|> maybeKind)) | ||
: maybeToList (pretty (definedAt n) (prettyPackageName n)) | ||
++ catMaybes [ T.unlines . spanDocToMarkdown <$> lookupNameEnv dm n | ||
] | ||
prettyName (Right n, dets) | ||
-- We want to print evidence variable using a readable tree structure. | ||
| any isEvidenceUse (identInfo dets) = pure $ maybe "" (printOutputable . renderEvidenceTree) (getEvidenceTree rf n) <> "\n" | ||
| otherwise = pure $ T.unlines $ | ||
wrapHaskell (printOutputable n <> maybe "" (" :: " <>) ((prettyType <$> identType dets) <|> maybeKind)) | ||
: maybeToList (pretty (definedAt n) (prettyPackageName n)) | ||
++ catMaybes [ T.unlines . spanDocToMarkdown <$> lookupNameEnv dm n | ||
] | ||
where maybeKind = fmap printOutputable $ safeTyThingType =<< lookupNameEnv km n | ||
pretty Nothing Nothing = Nothing | ||
pretty (Just define) Nothing = Just $ define <> "\n" | ||
|
@@ -286,7 +308,7 @@ atPoint IdeOptions{} (HAR _ hf _ _ (kind :: HieKind hietype)) (DKMap dm km) env | |
version = T.pack $ showVersion (unitPackageVersion conf) | ||
pure $ pkgName <> "-" <> version | ||
|
||
-- Type info for the current node, it may contains several symbols | ||
-- Type info for the current node, it may contain several symbols | ||
-- for one range, like wildcard | ||
types :: [hietype] | ||
types = nodeType info | ||
|
@@ -295,9 +317,12 @@ atPoint IdeOptions{} (HAR _ hf _ _ (kind :: HieKind hietype)) (DKMap dm km) env | |
prettyTypes = map (("_ :: "<>) . prettyType) types | ||
|
||
prettyType :: hietype -> T.Text | ||
prettyType t = case kind of | ||
HieFresh -> printOutputable t | ||
HieFromDisk full_file -> printOutputable $ hieTypeToIface $ recoverFullType t (hie_types full_file) | ||
prettyType = printOutputable . expandType | ||
|
||
expandType :: a -> SDoc | ||
expandType t = case kind of | ||
HieFresh -> ppr t | ||
HieFromDisk full_file -> ppr $ hieTypeToIface $ recoverFullType t (hie_types full_file) | ||
|
||
definedAt :: Name -> Maybe T.Text | ||
definedAt name = | ||
|
@@ -307,6 +332,39 @@ atPoint IdeOptions{} (HAR _ hf _ _ (kind :: HieKind hietype)) (DKMap dm km) env | |
UnhelpfulLoc {} | isInternalName name || isSystemName name -> Nothing | ||
_ -> Just $ "*Defined " <> printOutputable (pprNameDefnLoc name) <> "*" | ||
|
||
-- We want to render the root constraint even if it is a let, | ||
-- but we don't want to render any subsequent lets | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm assuming this makes sense to someone who knows how evidence is represented in GHC... |
||
renderEvidenceTree :: Tree (EvidenceInfo a) -> SDoc | ||
-- However, if the root constraint is simply an indirection (via let) to a single other constraint, | ||
-- we can still skip rendering it | ||
renderEvidenceTree (T.Node (EvidenceInfo{evidenceDetails=Just (EvLetBind _,_,_)}) [x]) | ||
= renderEvidenceTree x | ||
renderEvidenceTree (T.Node (EvidenceInfo{evidenceDetails=Just (EvLetBind _,_,_), ..}) xs) | ||
= hang (text "Evidence of constraint `" O.<> expandType evidenceType O.<> "`") 2 $ | ||
vcat $ text "constructed using:" : map renderEvidenceTree' xs | ||
renderEvidenceTree (T.Node (EvidenceInfo{..}) _) | ||
= hang (text "Evidence of constraint `" O.<> expandType evidenceType O.<> "`") 2 $ | ||
vcat $ printDets evidenceSpan evidenceDetails : map (text . T.unpack) (maybeToList $ definedAt evidenceVar) | ||
|
||
-- renderEvidenceTree' skips let bound evidence variables and prints the children directly | ||
renderEvidenceTree' (T.Node (EvidenceInfo{evidenceDetails=Just (EvLetBind _,_,_)}) xs) | ||
= vcat (map renderEvidenceTree' xs) | ||
renderEvidenceTree' (T.Node (EvidenceInfo{..}) _) | ||
= hang (text "- `" O.<> expandType evidenceType O.<> "`") 2 $ | ||
vcat $ printDets evidenceSpan evidenceDetails : map (text . T.unpack) (maybeToList $ definedAt evidenceVar) | ||
|
||
printDets :: RealSrcSpan -> Maybe (EvVarSource, Scope, Maybe Span) -> SDoc | ||
printDets _ Nothing = text "using an external instance" | ||
printDets ospn (Just (src,_,mspn)) = pprSrc | ||
$$ text "at" <+> ppr spn | ||
where | ||
-- Use the bind span if we have one, else use the occurrence span | ||
spn = fromMaybe ospn mspn | ||
pprSrc = case src of | ||
-- Users don't know what HsWrappers are | ||
EvWrapperBind -> "bound by type signature or pattern" | ||
_ -> ppr src | ||
|
||
-- | Find 'Location's of type definition at a specific point and return them along with their 'Identifier's. | ||
typeLocationsAtPoint | ||
:: forall m | ||
|
@@ -323,7 +381,7 @@ typeLocationsAtPoint withHieDb lookupModule _ideOptions pos (HAR _ ast _ _ hieKi | |
let arr = hie_types hf | ||
ts = concat $ pointCommand ast pos getts | ||
unfold = map (arr A.!) | ||
getts x = nodeType ni ++ (mapMaybe identType $ M.elems $ nodeIdentifiers ni) | ||
getts x = nodeType ni ++ mapMaybe identType (M.elems $ nodeIdentifiers ni) | ||
where ni = nodeInfo' x | ||
getTypes' ts' = flip concatMap (unfold ts') $ \case | ||
HTyVarTy n -> [n] | ||
|
@@ -337,7 +395,7 @@ typeLocationsAtPoint withHieDb lookupModule _ideOptions pos (HAR _ ast _ _ hieKi | |
in fmap nubOrd $ concatMapM (\n -> fmap (maybe [] (fmap (,Right n))) (nameToLocation withHieDb lookupModule n)) (getTypes' ts) | ||
HieFresh -> | ||
let ts = concat $ pointCommand ast pos getts | ||
getts x = nodeType ni ++ (mapMaybe identType $ M.elems $ nodeIdentifiers ni) | ||
getts x = nodeType ni ++ mapMaybe identType (M.elems $ nodeIdentifiers ni) | ||
where ni = nodeInfo x | ||
in fmap nubOrd $ concatMapM (\n -> fmap (maybe [] (fmap (,Right n))) (nameToLocation withHieDb lookupModule n)) (getTypes ts) | ||
|
||
|
@@ -352,20 +410,20 @@ namesInType (LitTy _) = [] | |
namesInType _ = [] | ||
|
||
getTypes :: [Type] -> [Name] | ||
getTypes ts = concatMap namesInType ts | ||
getTypes = concatMap namesInType | ||
|
||
-- | Find 'Location's of definition at a specific point and return them along with their 'Identifier's. | ||
locationsAtPoint | ||
:: forall m a | ||
:: forall m | ||
. MonadIO m | ||
=> WithHieDb | ||
-> LookupModule m | ||
-> IdeOptions | ||
-> M.Map ModuleName NormalizedFilePath | ||
-> Position | ||
-> HieASTs a | ||
-> HieAstResult | ||
-> m [(Location, Identifier)] | ||
locationsAtPoint withHieDb lookupModule _ideOptions imports pos ast = | ||
locationsAtPoint withHieDb lookupModule _ideOptions imports pos (HAR _ ast _rm _ _) = | ||
let ns = concat $ pointCommand ast pos (M.keys . getNodeIds) | ||
zeroPos = Position 0 0 | ||
zeroRange = Range zeroPos zeroPos | ||
|
@@ -375,6 +433,24 @@ locationsAtPoint withHieDb lookupModule _ideOptions imports pos ast = | |
(\n -> fmap (fmap $ fmap (,Right n)) (nameToLocation withHieDb lookupModule n))) | ||
ns | ||
|
||
-- | Find 'Location's of a implementation definition at a specific point. | ||
instanceLocationsAtPoint | ||
:: forall m | ||
. MonadIO m | ||
=> WithHieDb | ||
-> LookupModule m | ||
-> IdeOptions | ||
-> Position | ||
-> HieAstResult | ||
-> m [Location] | ||
instanceLocationsAtPoint withHieDb lookupModule _ideOptions pos (HAR _ ast _rm _ _) = | ||
let ns = concat $ pointCommand ast pos (M.keys . getNodeIds) | ||
evTrees = mapMaybe (eitherToMaybe >=> getEvidenceTree _rm) ns | ||
evNs = concatMap (map (evidenceVar) . T.flatten) evTrees | ||
in fmap (nubOrd . concat) $ mapMaybeM | ||
(nameToLocation withHieDb lookupModule) | ||
evNs | ||
|
||
-- | Given a 'Name' attempt to find the location where it is defined. | ||
nameToLocation :: MonadIO m => WithHieDb -> LookupModule m -> Name -> m (Maybe [Location]) | ||
nameToLocation withHieDb lookupModule name = runMaybeT $ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{-# LANGUAGE GADTs #-} | ||
|
||
module GotoImplementation where | ||
|
||
data AAA = AAA | ||
instance Num AAA where | ||
aaa :: Num x => x | ||
aaa = 1 | ||
aaa1 :: AAA = aaa | ||
|
||
class BBB a where | ||
bbb :: a -> a | ||
instance BBB AAA where | ||
bbb = const AAA | ||
bbbb :: AAA | ||
bbbb = bbb AAA | ||
|
||
data DDD a where | ||
DDD1 :: Int -> DDD Int | ||
DDD2 :: String -> DDD String | ||
ddd :: DDD a -> a | ||
ddd d = case d of | ||
DDD1 a -> a + a | ||
DDD2 a -> a ++ a | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
cradle: {direct: {arguments: ["Foo", "Bar", "GotoHover", "RecordDotSyntax"]}} | ||
cradle: {direct: {arguments: ["Foo", "Bar", "GotoHover", "RecordDotSyntax", "GotoImplementation"]}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the
maybe ""
seems supicious. What case is that for?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
getEvidenceTree
may returnNothing
for a variety of reasons. For example, when the name is unexpectedly not found in the reference map. I think in practice, it will always produce something.We could log it if we failed to build the EvidenceTree for a given name.