Skip to content

Commit 8dd3965

Browse files
committed
[psc-ide] Provides AST information on completion
* Unifies response formats for the type and complete commands so go-to-definition and type annotations now also show up in completions
1 parent 03260df commit 8dd3965

File tree

13 files changed

+122
-118
lines changed

13 files changed

+122
-118
lines changed

psc-ide-server/PROTOCOL.md

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -51,27 +51,7 @@ definition position, if it can be found in the passed source files.
5151
```
5252

5353
**Result:**
54-
The possible types are returned in the same format as completions + eventual position information
55-
```json
56-
[
57-
{
58-
"module": "Data.Array",
59-
"identifier": "filter",
60-
"type": "forall a. (a -> Boolean) -> Array a -> Array a"
61-
},
62-
{
63-
"module": "Data.Array",
64-
"identifier": "filter",
65-
"type": "forall a. (a -> Boolean) -> Array a -> Array a",
66-
"definedAt":
67-
{
68-
"name": "/path/to/file",
69-
"start": [1, 3],
70-
"end": [3, 1]
71-
}
72-
}
73-
]
74-
```
54+
The possible types are returned in the same format as completions
7555

7656
### Complete
7757
The `complete` command looks up possible completions/corrections.
@@ -104,12 +84,23 @@ The `complete` command looks up possible completions/corrections.
10484

10585
The following format is returned as the Result:
10686

87+
Both the `definedAt` aswell as the `documentation` field might be `null` if they
88+
couldn't be extracted from a source file.
89+
10790
```json
10891
[
10992
{
11093
"module": "Data.Array",
11194
"identifier": "filter",
112-
"type": "forall a. (a -> Boolean) -> Array a -> Array a"
95+
"type": "forall a. (a -> Boolean) -> Array a -> Array a",
96+
"expandedType": "forall a. (a -> Boolean) -> Array a -> Array a",
97+
"definedAt":
98+
{
99+
"name": "/path/to/file",
100+
"start": [1, 3],
101+
"end": [3, 1]
102+
},
103+
"documentation": "A filtering function"
113104
}
114105
]
115106
```

src/Language/PureScript/Ide.hs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ import Language.PureScript.Ide.SourceFile
3838
import Language.PureScript.Ide.State
3939
import Language.PureScript.Ide.Types
4040
import Language.PureScript.Ide.Util
41-
import System.Directory
42-
import System.FilePath
43-
import System.FilePath.Glob
41+
import System.Directory (getCurrentDirectory, getDirectoryContents, doesDirectoryExist, doesFileExist)
42+
import System.FilePath ((</>))
43+
import System.FilePath.Glob (glob)
4444

4545
-- | Accepts a Commmand and runs it against psc-ide's State. This is the main
4646
-- entry point for the server.
@@ -77,7 +77,7 @@ handleCommand c = case c of
7777
case rs of
7878
Right rs' -> answerRequest outfp rs'
7979
Left question ->
80-
pure (CompletionResult (map completionFromMatch question))
80+
pure (CompletionResult (map (completionFromMatch . map withEmptyAnn) question))
8181
Rebuild file ->
8282
rebuildFile file
8383
Cwd ->
@@ -88,7 +88,7 @@ handleCommand c = case c of
8888
liftIO exitSuccess
8989

9090
findCompletions :: Ide m =>
91-
[Filter] -> Matcher IdeDeclaration -> Maybe P.ModuleName -> m Success
91+
[Filter] -> Matcher IdeDeclarationAnn -> Maybe P.ModuleName -> m Success
9292
findCompletions filters matcher currentModule = do
9393
modules <- getAllModules currentModule
9494
pure . CompletionResult . map completionFromMatch . getCompletions filters matcher $ modules
@@ -97,7 +97,7 @@ findType :: Ide m =>
9797
Text -> [Filter] -> Maybe P.ModuleName -> m Success
9898
findType search filters currentModule = do
9999
modules <- getAllModules currentModule
100-
pure . InfoResult . map infoFromMatch . getExactMatches search filters $ modules
100+
pure . CompletionResult . map completionFromMatch . getExactMatches search filters $ modules
101101

102102
findPursuitCompletions :: MonadIO m =>
103103
PursuitQuery -> m Success

src/Language/PureScript/Ide/Command.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ data Command
3434
}
3535
| Complete
3636
{ completeFilters :: [Filter]
37-
, completeMatcher :: Matcher IdeDeclaration
37+
, completeMatcher :: Matcher IdeDeclarationAnn
3838
, completeCurrentModule :: Maybe P.ModuleName
3939
}
4040
| Pursuit

src/Language/PureScript/Ide/Completion.hs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,23 @@ import Protolude
99
import Language.PureScript.Ide.Filter
1010
import Language.PureScript.Ide.Matcher
1111
import Language.PureScript.Ide.Types
12-
import Language.PureScript.Ide.Util
1312

1413
-- | Applies the CompletionFilters and the Matcher to the given Modules
1514
-- and sorts the found Completions according to the Matching Score
1615
getCompletions
1716
:: [Filter]
18-
-> Matcher IdeDeclaration
17+
-> Matcher IdeDeclarationAnn
1918
-> [Module]
20-
-> [Match IdeDeclaration]
19+
-> [Match IdeDeclarationAnn]
2120
getCompletions filters matcher modules =
22-
runMatcher matcher (completionsFromModules discardAnn (applyFilters filters modules))
21+
runMatcher matcher (completionsFromModules (applyFilters filters modules))
2322

2423
getExactMatches :: Text -> [Filter] -> [Module] -> [Match IdeDeclarationAnn]
2524
getExactMatches search filters modules =
26-
completionsFromModules identity (applyFilters (equalityFilter search : filters) modules)
25+
completionsFromModules (applyFilters (equalityFilter search : filters) modules)
2726

28-
completionsFromModules :: (IdeDeclarationAnn -> a) -> [Module] -> [Match a]
29-
completionsFromModules f = foldMap completionFromModule
27+
completionsFromModules :: [Module] -> [Match IdeDeclarationAnn]
28+
completionsFromModules = foldMap completionFromModule
3029
where
3130
completionFromModule (moduleName, decls) =
32-
map (\x -> Match (moduleName, f x)) decls
31+
map (\x -> Match (moduleName, x)) decls

src/Language/PureScript/Ide/Matcher.hs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type ScoredMatch a = (Match a, Double)
3838

3939
newtype Matcher a = Matcher (Endo [Match a]) deriving (Monoid)
4040

41-
instance FromJSON (Matcher IdeDeclaration) where
41+
instance FromJSON (Matcher IdeDeclarationAnn) where
4242
parseJSON = withObject "matcher" $ \o -> do
4343
(matcher :: Maybe Text) <- o .:? "matcher"
4444
case matcher of
@@ -60,17 +60,17 @@ instance FromJSON (Matcher IdeDeclaration) where
6060
-- Examples:
6161
-- flMa matches flexMatcher. Score: 14.28
6262
-- sons matches sortCompletions. Score: 6.25
63-
flexMatcher :: Text -> Matcher IdeDeclaration
63+
flexMatcher :: Text -> Matcher IdeDeclarationAnn
6464
flexMatcher p = mkMatcher (flexMatch p)
6565

66-
distanceMatcher :: Text -> Int -> Matcher IdeDeclaration
66+
distanceMatcher :: Text -> Int -> Matcher IdeDeclarationAnn
6767
distanceMatcher q maxDist = mkMatcher (distanceMatcher' q maxDist)
6868

69-
distanceMatcher' :: Text -> Int -> [Match IdeDeclaration] -> [ScoredMatch IdeDeclaration]
69+
distanceMatcher' :: Text -> Int -> [Match IdeDeclarationAnn] -> [ScoredMatch IdeDeclarationAnn]
7070
distanceMatcher' q maxDist = mapMaybe go
7171
where
7272
go m = let d = dist (T.unpack y)
73-
y = identifierFromIdeDeclaration (unwrapMatch m)
73+
y = identifierFromIdeDeclaration (discardAnn (unwrapMatch m))
7474
in if d <= maxDist
7575
then Just (m, 1 / fromIntegral d)
7676
else Nothing
@@ -85,12 +85,12 @@ runMatcher (Matcher m)= appEndo m
8585
sortCompletions :: [ScoredMatch a] -> [ScoredMatch a]
8686
sortCompletions = sortBy (flip compare `on` snd)
8787

88-
flexMatch :: Text -> [Match IdeDeclaration] -> [ScoredMatch IdeDeclaration]
88+
flexMatch :: Text -> [Match IdeDeclarationAnn] -> [ScoredMatch IdeDeclarationAnn]
8989
flexMatch = mapMaybe . flexRate
9090

91-
flexRate :: Text -> Match IdeDeclaration -> Maybe (ScoredMatch IdeDeclaration)
91+
flexRate :: Text -> Match IdeDeclarationAnn -> Maybe (ScoredMatch IdeDeclarationAnn)
9292
flexRate p c = do
93-
score <- flexScore p (identifierFromIdeDeclaration (unwrapMatch c))
93+
score <- flexScore p (identifierFromIdeDeclaration (discardAnn (unwrapMatch c)))
9494
return (c, score)
9595

9696
-- FlexMatching ala Sublime.

src/Language/PureScript/Ide/SourceFile.hs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717
module Language.PureScript.Ide.SourceFile
1818
( parseModule
1919
, getImportsForFile
20+
, extractAstInformation
21+
-- for tests
2022
, extractSpans
2123
, extractTypeAnnotations
2224
) where
2325

2426
import Protolude
2527

28+
import qualified Data.Map as Map
2629
import qualified Language.PureScript as P
2730
import Language.PureScript.Ide.Error
2831
import Language.PureScript.Ide.Util
@@ -65,6 +68,15 @@ getImportsForFile fp = do
6568
unwrapImportType (P.Hiding decls) = P.Hiding (map unwrapPositionedRef decls)
6669
unwrapImportType P.Implicit = P.Implicit
6770

71+
-- | Extracts AST information from a parsed module
72+
extractAstInformation
73+
:: P.Module
74+
-> (DefinitionSites P.SourceSpan, TypeAnnotations)
75+
extractAstInformation (P.Module ss _ _ decls _) =
76+
let definitions = Map.fromList (concatMap (extractSpans ss) decls)
77+
typeAnnotations = Map.fromList (extractTypeAnnotations decls)
78+
in (definitions, typeAnnotations)
79+
6880
-- | Extracts type annotations for functions from a given Module
6981
extractTypeAnnotations
7082
:: [P.Declaration]

src/Language/PureScript/Ide/State.hs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,7 @@ populateStage2 = do
180180
populateStage2STM :: TVar IdeState -> STM ()
181181
populateStage2STM ref = do
182182
modules <- s1Modules <$> getStage1STM ref
183-
let astData = map (\(P.Module ss _ _ decls _, _) ->
184-
let definitions = M.fromList (concatMap (extractSpans ss) decls)
185-
typeAnnotations = M.fromList (extractTypeAnnotations decls)
186-
in (definitions, typeAnnotations)) modules
183+
let astData = map (extractAstInformation . fst) modules
187184
setStage2STM ref (Stage2 (AstData astData))
188185

189186
-- | Resolves reexports and populates Stage3 with data to be used in queries.

src/Language/PureScript/Ide/Types.hs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -112,21 +112,25 @@ data Stage3 = Stage3
112112
newtype Match a = Match (P.ModuleName, a)
113113
deriving (Show, Eq, Functor)
114114

115-
newtype Completion =
116-
Completion (Text, Text, Text)
117-
deriving (Show,Eq)
118-
119-
newtype Info =
120-
Info (Text, Text, Text, Maybe P.SourceSpan)
121-
deriving (Show,Eq)
122-
123-
instance ToJSON Info where
124-
toJSON (Info (m, d, t, sourceSpan)) =
125-
object ["module" .= m, "identifier" .= d, "type" .= t, "definedAt" .= sourceSpan]
115+
-- | A completion as it gets sent to the editors
116+
data Completion = Completion
117+
{ complModule :: Text
118+
, complIdentifier :: Text
119+
, complType :: Text
120+
, complExpandedType :: Text
121+
, complLocation :: Maybe P.SourceSpan
122+
, complDocumentation :: Maybe Text
123+
} deriving (Show, Eq)
126124

127125
instance ToJSON Completion where
128-
toJSON (Completion (m, d, t)) =
129-
object ["module" .= m, "identifier" .= d, "type" .= t]
126+
toJSON (Completion {..}) =
127+
object [ "module" .= complModule
128+
, "identifier" .= complIdentifier
129+
, "type" .= complType
130+
, "expandedType" .= complExpandedType
131+
, "definedAt" .= complLocation
132+
, "documentation" .= complDocumentation
133+
]
130134

131135
data ModuleImport =
132136
ModuleImport
@@ -164,22 +168,20 @@ identifierFromDeclarationRef _ = ""
164168

165169
data Success =
166170
CompletionResult [Completion]
167-
| InfoResult [Info]
168171
| TextResult Text
169172
| MultilineTextResult [Text]
170173
| PursuitResult [PursuitResponse]
171174
| ImportList [ModuleImport]
172175
| ModuleList [ModuleIdent]
173176
| RebuildSuccess [P.JSONError]
174-
deriving(Show, Eq)
177+
deriving (Show, Eq)
175178

176179
encodeSuccess :: (ToJSON a) => a -> Value
177180
encodeSuccess res =
178181
object ["resultType" .= ("success" :: Text), "result" .= res]
179182

180183
instance ToJSON Success where
181184
toJSON (CompletionResult cs) = encodeSuccess cs
182-
toJSON (InfoResult i) = encodeSuccess i
183185
toJSON (TextResult t) = encodeSuccess t
184186
toJSON (MultilineTextResult ts) = encodeSuccess ts
185187
toJSON (PursuitResult resp) = encodeSuccess resp

src/Language/PureScript/Ide/Util.hs

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ module Language.PureScript.Ide.Util
2020
, unwrapPositioned
2121
, unwrapPositionedRef
2222
, completionFromMatch
23-
, infoFromMatch
2423
, encodeT
2524
, decodeT
2625
, discardAnn
26+
, withEmptyAnn
2727
, module Language.PureScript.Ide.Conversions
2828
) where
2929

@@ -48,48 +48,52 @@ identifierFromIdeDeclaration d = case d of
4848
discardAnn :: IdeDeclarationAnn -> IdeDeclaration
4949
discardAnn (IdeDeclarationAnn _ d) = d
5050

51+
withEmptyAnn :: IdeDeclaration -> IdeDeclarationAnn
52+
withEmptyAnn = IdeDeclarationAnn emptyAnn
53+
5154
unwrapMatch :: Match a -> a
5255
unwrapMatch (Match (_, ed)) = ed
5356

54-
completionFromMatch :: Match IdeDeclaration -> Completion
55-
completionFromMatch = Completion . completionFromMatch'
56-
57-
completionFromMatch' :: Match IdeDeclaration -> (Text, Text, Text)
58-
completionFromMatch' (Match (m', d)) = case d of
59-
IdeValue name type' -> (m, runIdentT name, prettyTypeT type')
60-
IdeType name kind -> (m, runProperNameT name, toS (P.prettyPrintKind kind))
61-
IdeTypeSynonym name kind -> (m, runProperNameT name, prettyTypeT kind)
62-
IdeDataConstructor name _ type' -> (m, runProperNameT name, prettyTypeT type')
63-
IdeTypeClass name -> (m, runProperNameT name, "class")
64-
IdeValueOperator op ref precedence associativity ->
65-
(m, runOpNameT op, showFixity precedence associativity ref op)
66-
IdeTypeOperator op ref precedence associativity ->
67-
(m, runOpNameT op, showFixity precedence associativity ref op)
57+
completionFromMatch :: Match IdeDeclarationAnn -> Completion
58+
completionFromMatch (Match (m, IdeDeclarationAnn ann decl)) =
59+
Completion {..}
6860
where
69-
m = runModuleNameT m'
61+
(complIdentifier, complExpandedType) = case decl of
62+
IdeValue name type' -> (runIdentT name, prettyTypeT type')
63+
IdeType name kind -> (runProperNameT name, toS (P.prettyPrintKind kind))
64+
IdeTypeSynonym name kind -> (runProperNameT name, prettyTypeT kind)
65+
IdeDataConstructor name _ type' -> (runProperNameT name, prettyTypeT type')
66+
IdeTypeClass name -> (runProperNameT name, "class")
67+
IdeValueOperator op ref precedence associativity ->
68+
(runOpNameT op, showFixity precedence associativity ref op)
69+
IdeTypeOperator op ref precedence associativity ->
70+
(runOpNameT op, showFixity precedence associativity ref op)
71+
72+
complModule = runModuleNameT m
73+
74+
complType = maybe complExpandedType prettyTypeT (annTypeAnnotation ann)
75+
76+
complLocation = annLocation ann
77+
78+
complDocumentation = Nothing
79+
7080
showFixity p a r o =
7181
let asso = case a of
7282
P.Infix -> "infix"
7383
P.Infixl -> "infixl"
7484
P.Infixr -> "infixr"
7585
in T.unwords [asso, show p, r, "as", runOpNameT o]
7686

77-
infoFromMatch :: Match IdeDeclarationAnn -> Info
78-
infoFromMatch (Match (m, IdeDeclarationAnn ann d)) =
79-
Info (a, b, maybe c prettyTypeT (annTypeAnnotation ann), annLocation ann)
80-
where
81-
(a, b, c) = completionFromMatch' (Match (m, d))
82-
8387
encodeT :: (ToJSON a) => a -> Text
8488
encodeT = toS . decodeUtf8 . encode
8589

8690
decodeT :: (FromJSON a) => Text -> Maybe a
8791
decodeT = decode . encodeUtf8 . toS
8892

8993
unwrapPositioned :: P.Declaration -> P.Declaration
90-
unwrapPositioned (P.PositionedDeclaration _ _ x) = x
94+
unwrapPositioned (P.PositionedDeclaration _ _ x) = unwrapPositioned x
9195
unwrapPositioned x = x
9296

9397
unwrapPositionedRef :: P.DeclarationRef -> P.DeclarationRef
94-
unwrapPositionedRef (P.PositionedDeclarationRef _ _ x) = x
98+
unwrapPositionedRef (P.PositionedDeclarationRef _ _ x) = unwrapPositionedRef x
9599
unwrapPositionedRef x = x

0 commit comments

Comments
 (0)