Skip to content

Commit 1f9637f

Browse files
authored
Merge pull request #97 from fwcd/local-ident-lookup
Resolve local identifiers in definition lookups, hover etc.
2 parents c369b27 + 386c823 commit 1f9637f

File tree

4 files changed

+53
-12
lines changed

4 files changed

+53
-12
lines changed

src/Curry/LanguageServer/Handlers/TextDocument/Completion.hs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import Curry.LanguageServer.Utils.Lookup (findScopeAtPos)
2222
import Curry.LanguageServer.Utils.Uri (normalizeUriWithPath)
2323
import Curry.LanguageServer.Utils.VFS (PosPrefixInfo (..), getCompletionPrefix)
2424
import Curry.LanguageServer.Monad (LSM)
25-
import Data.Bifunctor (first)
25+
import Data.Bifunctor (Bifunctor (..))
2626
import Data.List.Extra (nubOrdOn)
2727
import qualified Data.Map as M
2828
import Data.Maybe (maybeToList, fromMaybe, isNothing)
@@ -33,6 +33,7 @@ import qualified Language.LSP.Protocol.Types as J
3333
import qualified Language.LSP.Protocol.Lens as J
3434
import qualified Language.LSP.Protocol.Message as J
3535
import Language.LSP.Server (MonadLsp)
36+
import qualified Curry.Base.Ident as CI
3637

3738
completionHandler :: S.Handlers LSM
3839
completionHandler = S.requestHandler J.SMethod_TextDocumentCompletion $ \req responder -> do
@@ -102,8 +103,8 @@ importCompletions opts store query = do
102103

103104
generalCompletions :: (MonadIO m, MonadLsp CFG.Config m) => CompletionOptions -> I.ModuleStoreEntry -> I.IndexStore -> PosPrefixInfo -> m [J.CompletionItem]
104105
generalCompletions opts entry store query = do
105-
let localIdentifiers = join <$> maybe M.empty (`findScopeAtPos` query.cursorPos) entry.moduleAST
106-
localIdentifiers' = M.fromList $ map (first ppToText) $ M.toList localIdentifiers
106+
let localIdentifiers = M.fromList . map (second join . snd) . M.toList $ maybe M.empty (`findScopeAtPos` query.cursorPos) entry.moduleAST
107+
localIdentifiers' = M.mapKeys ppToText (localIdentifiers :: M.Map CI.Ident (Maybe CT.PredType))
107108
localCompletions = toMatchingCompletions opts query $ uncurry Local <$> M.toList localIdentifiers'
108109
symbols = filter (flip M.notMember localIdentifiers' . (.ident)) $ nubOrdOn (.qualIdent)
109110
$ I.storedSymbolsWithPrefix query.prefixText store

src/Curry/LanguageServer/Handlers/TextDocument/References.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Control.Monad.Trans (MonadTrans (..))
1010
import Control.Monad.Trans.Maybe (MaybeT (..))
1111
import qualified Curry.LanguageServer.Config as CFG
1212
import Curry.LanguageServer.Monad (LSM, getStore)
13-
import Curry.LanguageServer.Utils.Convert (ppToText, currySpanInfo2Location)
13+
import Curry.LanguageServer.Utils.Convert (currySpanInfo2Location)
1414
import Curry.LanguageServer.Utils.General (liftMaybe, (<.$>), joinFst)
1515
import Curry.LanguageServer.Utils.Logging (debugM, infoM)
1616
import Curry.LanguageServer.Utils.Sema (ModuleAST)

src/Curry/LanguageServer/Index/Resolve.hs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE NoFieldSelectors #-}
12
-- | Lookup and resolution with the index.
23
module Curry.LanguageServer.Index.Resolve
34
( resolveAtPos
@@ -9,16 +10,23 @@ import qualified Curry.Base.Ident as CI
910
import qualified Curry.Syntax as CS
1011

1112
import Control.Applicative (Alternative ((<|>)))
13+
import Control.Monad (join, when)
14+
import Control.Monad.Trans.Maybe (MaybeT(..))
1215
import qualified Curry.LanguageServer.Index.Store as I
1316
import qualified Curry.LanguageServer.Index.Symbol as I
14-
import Curry.LanguageServer.Utils.Convert (currySpanInfo2Range)
17+
import Curry.LanguageServer.Index.Symbol (Symbol (..))
18+
import Curry.LanguageServer.Utils.Convert (currySpanInfo2Range, currySpanInfo2Location, ppToText)
1519
import Curry.LanguageServer.Utils.Sema (ModuleAST)
16-
import Curry.LanguageServer.Utils.Lookup (findQualIdentAtPos, findModuleIdentAtPos)
20+
import Curry.LanguageServer.Utils.Lookup (findQualIdentAtPos, findModuleIdentAtPos, findScopeAtPos)
1721
import qualified Language.LSP.Protocol.Types as J
22+
import Data.Default (Default(def))
23+
import qualified Data.Map as M
24+
import System.IO.Unsafe (unsafePerformIO)
1825

1926
-- | Resolves the identifier at the given position.
2027
resolveAtPos :: I.IndexStore -> ModuleAST -> J.Position -> Maybe ([I.Symbol], J.Range)
21-
resolveAtPos store ast pos = resolveQualIdentAtPos store ast pos
28+
resolveAtPos store ast pos = resolveLocalIdentAtPos ast pos
29+
<|> resolveQualIdentAtPos store ast pos
2230
<|> resolveModuleIdentAtPos store ast pos
2331

2432
-- | Resolves the qualified identifier at the given position.
@@ -37,6 +45,25 @@ resolveModuleIdentAtPos store ast pos = do
3745
let symbols = resolveModuleIdent store mid
3846
return (symbols, range)
3947

48+
-- | Resolves the local identifier at the given position.
49+
resolveLocalIdentAtPos :: ModuleAST -> J.Position -> Maybe ([I.Symbol], J.Range)
50+
resolveLocalIdentAtPos ast pos = do
51+
let scope = findScopeAtPos ast pos
52+
(qid, spi) <- findQualIdentAtPos ast pos
53+
range <- currySpanInfo2Range spi
54+
let symbols = [def { ident = ppToText lid
55+
, qualIdent = ppToText lid
56+
, printedType = ppToText <$> join lty
57+
, location = unsafePerformIO (runMaybeT (currySpanInfo2Location lid)) -- SAFETY: We expect this conversion to be pure
58+
}
59+
| (_, (lid, lty)) <- M.toList scope
60+
, CI.idName lid == CI.idName (CI.qidIdent qid)
61+
]
62+
-- Fail the computation when no local source identifier could be found
63+
when (null symbols)
64+
Nothing
65+
return (symbols, range)
66+
4067
-- | Resolves the qualified identifier at the given position.
4168
resolveQualIdent :: I.IndexStore -> ModuleAST -> CI.QualIdent -> [I.Symbol]
4269
resolveQualIdent store (CS.Module _ _ _ mid _ imps _) qid = do

src/Curry/LanguageServer/Utils/Lookup.hs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@
22
-- | Position lookup in the AST.
33
module Curry.LanguageServer.Utils.Lookup
44
( findQualIdentAtPos
5+
, findExprIdentAtPos
56
, findModuleIdentAtPos
67
, findTypeAtPos
78
, findScopeAtPos
9+
, showScope
810
, Scope
911
) where
1012

1113
-- Curry Compiler Libraries + Dependencies
1214
import qualified Curry.Base.Ident as CI
1315
import qualified Curry.Base.SpanInfo as CSPI
1416
import qualified Curry.Syntax as CS
17+
import qualified Curry.Base.Position as CP
1518

1619
import Control.Applicative (Alternative ((<|>)))
1720
import Control.Monad (when)
@@ -28,19 +31,24 @@ import Curry.LanguageServer.Utils.Syntax
2831
)
2932
import Curry.LanguageServer.Utils.Sema
3033
( HasTypedSpanInfos(typedSpanInfos), TypedSpanInfo )
34+
import Data.Bifunctor (Bifunctor(..))
3135
import qualified Data.Map as M
3236
import qualified Language.LSP.Protocol.Types as J
3337

3438
-- | A collectScope of bound identifiers.
35-
type Scope a = M.Map CI.Ident (Maybe a)
39+
type Scope a = M.Map String (CI.Ident, Maybe a)
3640

3741
-- | Finds identifier and (occurrence) span info at a given position.
3842
findQualIdentAtPos :: CS.Module a -> J.Position -> Maybe (CI.QualIdent, CSPI.SpanInfo)
3943
findQualIdentAtPos ast pos = qualIdent <|> exprIdent <|> basicIdent
4044
where qualIdent = withSpanInfo <$> elementAt pos (qualIdentifiers ast)
41-
exprIdent = joinFst $ qualIdentifier <.$> withSpanInfo <$> elementAt pos (expressions ast)
45+
exprIdent = findExprIdentAtPos ast pos
4246
basicIdent = CI.qualify <.$> withSpanInfo <$> elementAt pos (identifiers ast)
4347

48+
--- | Finds expression identifier and (occurrence) span info at a given position.
49+
findExprIdentAtPos :: CS.Module a -> J.Position -> Maybe (CI.QualIdent, CSPI.SpanInfo)
50+
findExprIdentAtPos ast pos = joinFst $ qualIdentifier <.$> withSpanInfo <$> elementAt pos (expressions ast)
51+
4452
-- | Finds module identifier and (occurrence) span info at a given position.
4553
findModuleIdentAtPos :: CS.Module a -> J.Position -> Maybe (CI.ModuleIdent, CSPI.SpanInfo)
4654
findModuleIdentAtPos ast pos = withSpanInfo <$> elementAt pos (moduleIdentifiers ast)
@@ -65,12 +73,17 @@ containsPos x pos = maybe False (rangeElem pos) $ currySpanInfo2Range x
6573

6674
-- | Binds an identifier in the innermost scope.
6775
bindInScopes :: CI.Ident -> Maybe a -> [Scope a] -> [Scope a]
68-
bindInScopes i t (sc:scs) = M.insert (CI.unRenameIdent i) t sc : scs
76+
bindInScopes i t (sc:scs) = M.insert (CI.idName i') (i', t) sc : scs
77+
where i' = CI.unRenameIdent i
6978
bindInScopes _ _ _ = error "Cannot bind without a scope!"
7079

80+
-- | Shows a scope with line numbers (for debugging).
81+
showScope :: Scope a -> String
82+
showScope = show . map (second (CP.line . CSPI.getStartPosition . CI.idSpanInfo . fst)) . M.toList
83+
7184
-- | Flattens the given scopes, preferring earlier binds.
7285
flattenScopes :: [Scope a] -> Scope a
73-
flattenScopes = foldr M.union M.empty
86+
flattenScopes = M.unions
7487

7588
-- | Stores nested scopes and a cursor position. The head of the list is always the innermost collectScope.
7689
data ScopeState a = ScopeState
@@ -98,7 +111,7 @@ updateEnvs :: CSPI.HasSpanInfo e => e -> ScopeM a ()
98111
updateEnvs (CSPI.getSpanInfo -> spi) = do
99112
pos <- gets (.position)
100113
when (spi `containsPos` pos) $
101-
modify $ \s -> s { matchingEnv = M.union (flattenScopes s.currentEnv) s.matchingEnv }
114+
modify $ \s -> s { matchingEnv = M.union s.matchingEnv (flattenScopes s.currentEnv) }
102115

103116
class CollectScope e a where
104117
collectScope :: e -> ScopeM a ()

0 commit comments

Comments
 (0)