git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
[psc-ide] - Import helper commands
0d78fc1Christoph Hegemann
.travis.yml+3
@@ -19,6 +19,9 @@ before_install:
19 - export PATH="$HOME/.cabal/bin:$PATH"
19 - export PATH="$HOME/.cabal/bin:$PATH"
20 - export PATH="/opt/happy/1.19.5/bin:/$PATH"
20 - export PATH="/opt/happy/1.19.5/bin:/$PATH"
21 - export PATH="/opt/alex/3.1.4/bin:/$PATH"
21 - export PATH="/opt/alex/3.1.4/bin:/$PATH"
22+ - export PATH="$HOME/build/purescript/purescript/dist/build/psc:/$PATH"
23+ - export PATH="$HOME/build/purescript/purescript/dist/build/psc-ide-server:/$PATH"
24+ - export PATH="$HOME/build/purescript/purescript/dist/build/psc-ide-client:/$PATH"
22install:
25install:
23 - cabal --version
26 - cabal --version
24 - echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
27 - echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
CONTRIBUTORS.md+1
@@ -69,6 +69,7 @@ This file lists the contributors to the PureScript compiler project, and the ter
69- [@vkorablin](https://github.com/vkorablin) (Vladimir Korablin) - My existing contributions and all future contributions until further notice are Copyright Vladimir Korablin, and are licensed to the owners and users of the PureScript compiler project under the terms of the MIT license.
69- [@vkorablin](https://github.com/vkorablin) (Vladimir Korablin) - My existing contributions and all future contributions until further notice are Copyright Vladimir Korablin, and are licensed to the owners and users of the PureScript compiler project under the terms of the MIT license.
70- [@zudov](https://github.com/zudov) (Konstantin Zudov) My existing contributions and all future contributions until further notice are Copyright Konstantin Zudov, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
70- [@zudov](https://github.com/zudov) (Konstantin Zudov) My existing contributions and all future contributions until further notice are Copyright Konstantin Zudov, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
71- [@LiamGoodacre](https://github.com/LiamGoodacre) (Liam Goodacre) My existing contributions and all future contributions until further notice are Copyright Liam Goodacre, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
71- [@LiamGoodacre](https://github.com/LiamGoodacre) (Liam Goodacre) My existing contributions and all future contributions until further notice are Copyright Liam Goodacre, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
72+- [@bsermons](https://github.com/bsermons) (Brian Sermons) My existing contributions and all future contributions until further notice are Copyright Brian Sermons, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
72
73
73### Companies
74### Companies
74
75
psc-ide-server/Main.hs+16−1
@@ -1,9 +1,24 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Main
4+-- Description : The server accepting commands for psc-ide
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- The server accepting commands for psc-ide
13+-----------------------------------------------------------------------------
14+
1{-# LANGUAGE ConstraintKinds #-}
15{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE FlexibleContexts #-}
16{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
17{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE OverloadedStrings #-}
18{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE PackageImports #-}
19{-# LANGUAGE PackageImports #-}
6{-# LANGUAGE TemplateHaskell #-}
20{-# LANGUAGE TemplateHaskell #-}
21+
7module Main where
22module Main where
8
23
9import Prelude ()
24import Prelude ()
@@ -21,7 +36,7 @@ import qualified Data.Text as T
21import qualified Data.Text.IO as T
36import qualified Data.Text.IO as T
22import Data.Version (showVersion)
37import Data.Version (showVersion)
23import Language.PureScript.Ide
38import Language.PureScript.Ide
24−import Language.PureScript.Ide.CodecJSON
39+import Language.PureScript.Ide.Util
25import Language.PureScript.Ide.Error
40import Language.PureScript.Ide.Error
26import Language.PureScript.Ide.Types
41import Language.PureScript.Ide.Types
27import Language.PureScript.Ide.Watcher
42import Language.PureScript.Ide.Watcher
psc-ide-server/PROTOCOL.md+95−3
@@ -8,8 +8,9 @@ this document.
8
8
9## Command:
9## Command:
10### Load
10### Load
11−The `load` command "loads" the requested modules into the server
11+The `load` command "loads" the requested modules into the server for completion
12−for completion and type info.
12+and type info. If the `params` object is left off, the `load` command will try
13+to detect all the compiled modules in your project and load them.
13
14
14**Params:**
15**Params:**
15 - `modules :: (optional) [ModuleName]`: A list of modules to load.
16 - `modules :: (optional) [ModuleName]`: A list of modules to load.
@@ -21,7 +22,7 @@ for completion and type info.
21```json
22```json
22{
23{
23 "command": "load",
24 "command": "load",
24− "params": {
25+ "params": (optional) {
25 "modules": (optional)["Module.Name1", "Module.Name2"],
26 "modules": (optional)["Module.Name1", "Module.Name2"],
26 "dependencies": (optional)["Module.Name3"]
27 "dependencies": (optional)["Module.Name3"]
27 }
28 }
@@ -148,6 +149,97 @@ The following format is returned as the Result:
148```
149```
149You should then be able to replace the affected line of code in the editor with the new suggestions.
150You should then be able to replace the affected line of code in the editor with the new suggestions.
150
151
152+### Import
153+
154+For now all of the import related commands work with a file on the filesystem.
155+
156+You can specify it with the `file` parameter.
157+
158+If you supply the optional `outfile` parameter, the output will be written to
159+that file, and an info message will be returned from the client.
160+
161+If you don't supply `outfile`, the server responds with a list of strings which,
162+when inserted into a file linewise create the module with the applied changes.
163+
164+Arguments:
165+
166+- `file` :: String
167+- `outfile` :: Maybe String
168+- `filters` :: Maybe [Filter]
169+
170+Example:
171+
172+```json
173+{
174+ "command": "import",
175+ "params": {
176+ "file": "/home/creek/Documents/chromacannon/src/Main.purs",
177+ "outfile": "/home/creek/Documents/chromacannon/src/Main.purs",
178+ "filters": [{
179+ "filter": "modules",
180+ "params": {
181+ "modules": ["My.Module"]
182+ }
183+ }],
184+ "importCommand": {
185+ "yadda": "yadda"
186+ }
187+ }
188+}
189+```
190+
191+
192+#### Subcommand `addImplicitImport`
193+
194+This command just adds an unqualified import for the given modulename.
195+
196+Arguments:
197+- `moduleName :: String`
198+
199+Example:
200+```json
201+{
202+ "command": "import",
203+ "params": {
204+ "file": "/home/creek/Documents/chromacannon/src/Main.purs",
205+ "importCommand": {
206+ "importCommand": "addImplicitImport",
207+ "module": "Data.Array.LOL"
208+ }
209+ }
210+}
211+```
212+#### Subcommand `addImport`
213+
214+This command takes an identifier and searches the currently loaded modules for
215+it. If it finds no matches it responds with an Error. If it finds exactly one
216+match it adds the import and returns. If it finds more than one match it
217+responds with a list of the found matches as completions like the complete
218+command.
219+
220+You can also supply a list of filters like the ones for completion. This way you
221+can narrow down the search to a certain module and resolve the case in which
222+more then one match was found.
223+
224+Arguments:
225+- `moduleName :: String`
226+- `filters :: [Filter]`
227+
228+Example:
229+```json
230+{
231+ "command": "import",
232+ "params": {
233+ "file": "/home/creek/Documents/chromacannon/src/Demo.purs",
234+ "outfile": "/home/creek/Documents/chromacannon/src/Demo.purs",
235+ "importCommand": {
236+ "importCommand": "addImport",
237+ "identifier": "bind"
238+ }
239+ }
240+}
241+```
242+
151### Pursuit
243### Pursuit
152The `pursuit` command looks up the packages/completions for a given identifier from Pursuit.
244The `pursuit` command looks up the packages/completions for a given identifier from Pursuit.
153
245
purescript.cabal+18−4
@@ -36,6 +36,7 @@ extra-source-files: examples/passing/*.purs
36 , tests/support/flattened/*.purs
36 , tests/support/flattened/*.purs
37 , tests/support/flattened/*.js
37 , tests/support/flattened/*.js
38 , tests/support/psci/*.purs
38 , tests/support/psci/*.purs
39+ , tests/support/pscide/src/*.purs
39 , stack.yaml
40 , stack.yaml
40 , stack-lts-5.yaml
41 , stack-lts-5.yaml
41 , stack-nightly.yaml
42 , stack-nightly.yaml
@@ -201,7 +202,6 @@ library
201 Language.PureScript.Ide.Command
202 Language.PureScript.Ide.Command
202 Language.PureScript.Ide.Externs
203 Language.PureScript.Ide.Externs
203 Language.PureScript.Ide.Error
204 Language.PureScript.Ide.Error
204− Language.PureScript.Ide.CodecJSON
205 Language.PureScript.Ide.Pursuit
205 Language.PureScript.Ide.Pursuit
206 Language.PureScript.Ide.Completion
206 Language.PureScript.Ide.Completion
207 Language.PureScript.Ide.Matcher
207 Language.PureScript.Ide.Matcher
@@ -212,6 +212,8 @@ library
212 Language.PureScript.Ide.SourceFile
212 Language.PureScript.Ide.SourceFile
213 Language.PureScript.Ide.Watcher
213 Language.PureScript.Ide.Watcher
214 Language.PureScript.Ide.Reexports
214 Language.PureScript.Ide.Reexports
215+ Language.PureScript.Ide.Imports
216+ Language.PureScript.Ide.Util
215
217
216 Control.Monad.Logger
218 Control.Monad.Logger
217 Control.Monad.Supply
219 Control.Monad.Supply
@@ -313,7 +315,7 @@ executable psc-bundle
313
315
314executable psc-ide-server
316executable psc-ide-server
315 main-is: Main.hs
317 main-is: Main.hs
316− other-modules:
318+ other-modules: Paths_purescript
317 other-extensions:
319 other-extensions:
318 build-depends: base >=4 && <5
320 build-depends: base >=4 && <5
319 , purescript -any
321 , purescript -any
@@ -333,7 +335,7 @@ executable psc-ide-server
333
335
334executable psc-ide-client
336executable psc-ide-client
335 main-is: Main.hs
337 main-is: Main.hs
336− other-modules:
338+ other-modules: Paths_purescript
337 other-extensions:
339 other-extensions:
338 build-depends: base >=4 && <5
340 build-depends: base >=4 && <5
339 , mtl -any
341 , mtl -any
@@ -350,7 +352,8 @@ test-suite tests
350 transformers -any, process -any, transformers-compat -any, time -any,
352 transformers -any, process -any, transformers-compat -any, time -any,
351 Glob -any, aeson-better-errors -any, bytestring -any, aeson -any,
353 Glob -any, aeson-better-errors -any, bytestring -any, aeson -any,
352 base-compat -any, haskeline >= 0.7.0.0, optparse-applicative -any,
354 base-compat -any, haskeline >= 0.7.0.0, optparse-applicative -any,
353− boxes -any, HUnit -any, hspec -any, hspec-discover -any, stm -any, text -any
355+ boxes -any, HUnit -any, hspec -any, hspec-discover -any, stm -any, text -any,
356+ vector -any, utf8-string -any
354 ghc-options: -Wall
357 ghc-options: -Wall
355 type: exitcode-stdio-1.0
358 type: exitcode-stdio-1.0
356 main-is: Main.hs
359 main-is: Main.hs
@@ -361,5 +364,16 @@ test-suite tests
361 TestPsci
364 TestPsci
362 TestPscIde
365 TestPscIde
363 PscIdeSpec
366 PscIdeSpec
367+ Language.PureScript.Ide.FilterSpec
368+ Language.PureScript.Ide.ImportsSpec
369+ Language.PureScript.Ide.Imports.IntegrationSpec
370+ Language.PureScript.Ide.Integration
371+ Language.PureScript.Ide.MatcherSpec
372+ Language.PureScript.Ide.ReexportsSpec
373+ Language.PureScript.IdeSpec
374+ PSCi.Completion
375+ PSCi.Directive
376+ PSCi.Module
377+ PSCi.Types
364 buildable: True
378 buildable: True
365 hs-source-dirs: tests psci
379 hs-source-dirs: tests psci
src/Language/PureScript/Ide.hs+63−21
@@ -1,3 +1,17 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide
4+-- Description : Interface for the psc-ide-server
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- Interface for the psc-ide-server
13+-----------------------------------------------------------------------------
14+
1{-# LANGUAGE ConstraintKinds #-}
15{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE FlexibleContexts #-}
16{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
17{-# LANGUAGE MultiParamTypeClasses #-}
@@ -6,7 +20,11 @@
6{-# LANGUAGE TemplateHaskell #-}
20{-# LANGUAGE TemplateHaskell #-}
7{-# LANGUAGE TupleSections #-}
21{-# LANGUAGE TupleSections #-}
8
22
9−module Language.PureScript.Ide where
23+module Language.PureScript.Ide
24+ ( handleCommand
25+ -- for tests
26+ , printModules
27+ ) where
10
28
11import Prelude ()
29import Prelude ()
12import Prelude.Compat
30import Prelude.Compat
@@ -27,12 +45,14 @@ import Language.PureScript.Ide.Completion
27import Language.PureScript.Ide.Error
45import Language.PureScript.Ide.Error
28import Language.PureScript.Ide.Externs
46import Language.PureScript.Ide.Externs
29import Language.PureScript.Ide.Filter
47import Language.PureScript.Ide.Filter
48+import Language.PureScript.Ide.Imports hiding (Import)
30import Language.PureScript.Ide.Matcher
49import Language.PureScript.Ide.Matcher
31import Language.PureScript.Ide.Pursuit
50import Language.PureScript.Ide.Pursuit
32import Language.PureScript.Ide.Reexports
51import Language.PureScript.Ide.Reexports
33import Language.PureScript.Ide.SourceFile
52import Language.PureScript.Ide.SourceFile
34import Language.PureScript.Ide.State
53import Language.PureScript.Ide.State
35import Language.PureScript.Ide.Types
54import Language.PureScript.Ide.Types
55+import Language.PureScript.Ide.Util
36import System.Directory
56import System.Directory
37import System.FilePath
57import System.FilePath
38import System.Exit
58import System.Exit
@@ -40,39 +60,48 @@ import System.Exit
40
60
41handleCommand :: (PscIde m, MonadLogger m, MonadError PscIdeError m) =>
61handleCommand :: (PscIde m, MonadLogger m, MonadError PscIdeError m) =>
42 Command -> m Success
62 Command -> m Success
63+handleCommand (Load [] []) = loadAllModules
43handleCommand (Load modules deps) =
64handleCommand (Load modules deps) =
44− loadModulesAndDeps modules deps
65+ loadModulesAndDeps modules deps
45handleCommand (Type search filters) =
66handleCommand (Type search filters) =
46− findType search filters
67+ findType search filters
47handleCommand (Complete filters matcher) =
68handleCommand (Complete filters matcher) =
48− findCompletions filters matcher
69+ findCompletions filters matcher
49handleCommand (Pursuit query Package) =
70handleCommand (Pursuit query Package) =
50− findPursuitPackages query
71+ findPursuitPackages query
51handleCommand (Pursuit query Identifier) =
72handleCommand (Pursuit query Identifier) =
52− findPursuitCompletions query
73+ findPursuitCompletions query
53handleCommand (List LoadedModules) =
74handleCommand (List LoadedModules) =
54− printModules
75+ printModules
55handleCommand (List AvailableModules) =
76handleCommand (List AvailableModules) =
56− listAvailableModules
77+ listAvailableModules
57handleCommand (List (Imports fp)) =
78handleCommand (List (Imports fp)) =
58− importsForFile fp
79+ importsForFile fp
59handleCommand (CaseSplit l b e wca t) =
80handleCommand (CaseSplit l b e wca t) =
60− caseSplit l b e wca t
81+ caseSplit l b e wca t
61handleCommand (AddClause l wca) =
82handleCommand (AddClause l wca) =
62− pure $ addClause l wca
83+ pure $ addClause l wca
84+handleCommand (Import fp outfp _ (AddImplicitImport mn)) = do
85+ rs <- addImplicitImport fp mn
86+ answerRequest outfp rs
87+handleCommand (Import fp outfp filters (AddImportForIdentifier ident)) = do
88+ rs <- addImportForIdentifier fp ident filters
89+ case rs of
90+ Right rs' -> answerRequest outfp rs'
91+ Left question -> pure $ CompletionResult (mapMaybe completionFromMatch question)
63handleCommand Cwd =
92handleCommand Cwd =
64− TextResult . T.pack <$> liftIO getCurrentDirectory
93+ TextResult . T.pack <$> liftIO getCurrentDirectory
65handleCommand Quit = liftIO exitSuccess
94handleCommand Quit = liftIO exitSuccess
66
95
67findCompletions :: (PscIde m, MonadLogger m) =>
96findCompletions :: (PscIde m, MonadLogger m) =>
68 [Filter] -> Matcher -> m Success
97 [Filter] -> Matcher -> m Success
69findCompletions filters matcher =
98findCompletions filters matcher =
70− CompletionResult . getCompletions filters matcher <$> getAllModulesWithReexports
99+ CompletionResult . mapMaybe completionFromMatch . getCompletions filters matcher <$> getAllModulesWithReexports
71
100
72findType :: (PscIde m, MonadLogger m) =>
101findType :: (PscIde m, MonadLogger m) =>
73 DeclIdent -> [Filter] -> m Success
102 DeclIdent -> [Filter] -> m Success
74findType search filters =
103findType search filters =
75− CompletionResult . getExactMatches search filters <$> getAllModulesWithReexports
104+ CompletionResult . mapMaybe completionFromMatch . getExactMatches search filters <$> getAllModulesWithReexports
76
105
77findPursuitCompletions :: (MonadIO m, MonadLogger m) =>
106findPursuitCompletions :: (MonadIO m, MonadLogger m) =>
78 PursuitQuery -> m Success
107 PursuitQuery -> m Success
@@ -179,6 +208,26 @@ loadModule mn = do
179 $(logDebug) ("Loaded extern file at: " <> T.pack path)
208 $(logDebug) ("Loaded extern file at: " <> T.pack path)
180 pure ("Loaded extern file at: " <> T.pack path)
209 pure ("Loaded extern file at: " <> T.pack path)
181
210
211+loadAllModules :: (PscIde m, MonadLogger m, MonadError PscIdeError m) => m Success
212+loadAllModules = do
213+ outputPath <- confOutputPath . envConfiguration <$> ask
214+ cwd <- liftIO getCurrentDirectory
215+ let outputDirectory = cwd </> outputPath
216+ liftIO (getDirectoryContents outputDirectory)
217+ >>= liftIO . traverse (getExternsPath outputDirectory)
218+ >>= traverse_ loadExtern . catMaybes
219+ pure (TextResult "All modules loaded.")
220+ where
221+ getExternsPath :: FilePath -> FilePath -> IO (Maybe FilePath)
222+ getExternsPath outputDirectory d
223+ | d `elem` [".", ".."] = pure Nothing
224+ | otherwise = do
225+ let file = outputDirectory </> d </> "externs.json"
226+ ex <- doesFileExist file
227+ if ex
228+ then pure (Just file)
229+ else pure Nothing
230+
182filePathFromModule :: (PscIde m, MonadError PscIdeError m) =>
231filePathFromModule :: (PscIde m, MonadError PscIdeError m) =>
183 ModuleIdent -> m FilePath
232 ModuleIdent -> m FilePath
184filePathFromModule moduleName = do
233filePathFromModule moduleName = do
@@ -190,10 +239,3 @@ filePathFromModule moduleName = do
190 then pure path
239 then pure path
191 else throwError (ModuleFileNotFound moduleName)
240 else throwError (ModuleFileNotFound moduleName)
192
241
193−-- | Taken from Data.Either.Utils
194−maybeToEither :: MonadError e m =>
195− e -- ^ (Left e) will be returned if the Maybe value is Nothing
196− -> Maybe a -- ^ (Right a) will be returned if this is (Just a)
197− -> m a
198−maybeToEither errorval Nothing = throwError errorval
199−maybeToEither _ (Just normalval) = return normalval
src/Language/PureScript/Ide/CaseSplit.hs+47−38
@@ -1,3 +1,17 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.CaseSplit
4+-- Description : Casesplitting and adding function clauses
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- Casesplitting and adding function clauses
13+-----------------------------------------------------------------------------
14+
1{-# LANGUAGE ConstraintKinds #-}
15{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE DataKinds #-}
16{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE FlexibleContexts #-}
17{-# LANGUAGE FlexibleContexts #-}
@@ -26,23 +40,18 @@ import Data.List (find)
26import Data.Monoid
40import Data.Monoid
27import Data.Text (Text)
41import Data.Text (Text)
28import qualified Data.Text as T
42import qualified Data.Text as T
29−import Language.PureScript.AST
43+import qualified Language.PureScript as P
30−import Language.PureScript.Environment
44+
31import Language.PureScript.Externs
45import Language.PureScript.Externs
32import Language.PureScript.Ide.Error
46import Language.PureScript.Ide.Error
33import Language.PureScript.Ide.Externs (unwrapPositioned)
47import Language.PureScript.Ide.Externs (unwrapPositioned)
34import Language.PureScript.Ide.State
48import Language.PureScript.Ide.State
35−import Language.PureScript.Ide.Types hiding (Type)
49+import Language.PureScript.Ide.Types
36−import Language.PureScript.Names
37−import Language.PureScript.Parser.Common (runTokenParser)
38−import Language.PureScript.Parser.Declarations
50+import Language.PureScript.Ide.Util
39−import Language.PureScript.Parser.Lexer (lex)
51+
40−import Language.PureScript.Parser.Types
41−import Language.PureScript.Pretty
42−import Language.PureScript.Types
43−import Text.Parsec as P
52+import Text.Parsec as Parsec
44−
53+
45−type Constructor = (ProperName 'ConstructorName, [Type])
54+type Constructor = (P.ProperName 'P.ConstructorName, [P.Type])
46
55
47newtype WildcardAnnotations = WildcardAnnotations Bool
56newtype WildcardAnnotations = WildcardAnnotations Bool
48
57
@@ -57,13 +66,13 @@ caseSplit :: (PscIde m, MonadLogger m, MonadError PscIdeError m) =>
57caseSplit q = do
66caseSplit q = do
58 type' <- parseType' (T.unpack q)
67 type' <- parseType' (T.unpack q)
59 (tc, args) <- splitTypeConstructor type'
68 (tc, args) <- splitTypeConstructor type'
60− (EDType _ _ (DataType typeVars ctors)) <- findTypeDeclaration tc
69+ (EDType _ _ (P.DataType typeVars ctors)) <- findTypeDeclaration tc
61− let applyTypeVars = everywhereOnTypes (replaceAllTypeVars (zip (map fst typeVars) args))
70+ let applyTypeVars = P.everywhereOnTypes (P.replaceAllTypeVars (zip (map fst typeVars) args))
62 let appliedCtors = map (second (map applyTypeVars)) ctors
71 let appliedCtors = map (second (map applyTypeVars)) ctors
63 pure appliedCtors
72 pure appliedCtors
64
73
65findTypeDeclaration :: (PscIde m, MonadLogger m, MonadError PscIdeError m) =>
74findTypeDeclaration :: (PscIde m, MonadLogger m, MonadError PscIdeError m) =>
66− ProperName 'TypeName -> m ExternsDeclaration
75+ P.ProperName 'P.TypeName -> m ExternsDeclaration
67findTypeDeclaration q = do
76findTypeDeclaration q = do
68 efs <- getExternFiles
77 efs <- getExternFiles
69 let m = getFirst $ foldMap (findTypeDeclaration' q) efs
78 let m = getFirst $ foldMap (findTypeDeclaration' q) efs
@@ -72,7 +81,7 @@ findTypeDeclaration q = do
72 Nothing -> throwError (GeneralError "Not Found")
81 Nothing -> throwError (GeneralError "Not Found")
73
82
74findTypeDeclaration' ::
83findTypeDeclaration' ::
75− ProperName 'TypeName
84+ P.ProperName 'P.TypeName
76 -> ExternsFile
85 -> ExternsFile
77 -> First ExternsDeclaration
86 -> First ExternsDeclaration
78findTypeDeclaration' t ExternsFile{..} =
87findTypeDeclaration' t ExternsFile{..} =
@@ -81,25 +90,25 @@ findTypeDeclaration' t ExternsFile{..} =
81 _ -> False) efDeclarations
90 _ -> False) efDeclarations
82
91
83splitTypeConstructor :: (MonadError PscIdeError m) =>
92splitTypeConstructor :: (MonadError PscIdeError m) =>
84− Type -> m (ProperName 'TypeName, [Type])
93+ P.Type -> m (P.ProperName 'P.TypeName, [P.Type])
85splitTypeConstructor = go []
94splitTypeConstructor = go []
86 where
95 where
87− go acc (TypeApp ty arg) = go (arg : acc) ty
96+ go acc (P.TypeApp ty arg) = go (arg : acc) ty
88− go acc (TypeConstructor tc) = pure (disqualify tc, acc)
97+ go acc (P.TypeConstructor tc) = pure (P.disqualify tc, acc)
89 go _ _ = throwError (GeneralError "Failed to read TypeConstructor")
98 go _ _ = throwError (GeneralError "Failed to read TypeConstructor")
90
99
91prettyCtor :: WildcardAnnotations -> Constructor -> Text
100prettyCtor :: WildcardAnnotations -> Constructor -> Text
92−prettyCtor _ (ctorName, []) = T.pack (runProperName ctorName)
101+prettyCtor _ (ctorName, []) = runProperNameT ctorName
93prettyCtor wsa (ctorName, ctorArgs) =
102prettyCtor wsa (ctorName, ctorArgs) =
94− "("<> T.pack (runProperName ctorName) <> " "
103+ "("<> runProperNameT ctorName <> " "
95 <> T.unwords (map (prettyPrintWildcard wsa) ctorArgs) <>")"
104 <> T.unwords (map (prettyPrintWildcard wsa) ctorArgs) <>")"
96
105
97−prettyPrintWildcard :: WildcardAnnotations -> Type -> Text
106+prettyPrintWildcard :: WildcardAnnotations -> P.Type -> Text
98prettyPrintWildcard (WildcardAnnotations True) = prettyWildcard
107prettyPrintWildcard (WildcardAnnotations True) = prettyWildcard
99prettyPrintWildcard (WildcardAnnotations False) = const "_"
108prettyPrintWildcard (WildcardAnnotations False) = const "_"
100
109
101−prettyWildcard :: Type -> Text
110+prettyWildcard :: P.Type -> Text
102−prettyWildcard t = "( _ :: " <> T.strip (T.pack (prettyPrintTypeAtom t)) <> ")"
111+prettyWildcard t = "( _ :: " <> T.strip (T.pack (P.prettyPrintTypeAtom t)) <> ")"
103
112
104-- | Constructs Patterns to insert into a sourcefile
113-- | Constructs Patterns to insert into a sourcefile
105makePattern :: Text -- ^ Current line
114makePattern :: Text -- ^ Current line
@@ -116,38 +125,38 @@ addClause :: Text -> WildcardAnnotations -> [Text]
116addClause s wca =
125addClause s wca =
117 let (fName, fType) = parseTypeDeclaration' (T.unpack s)
126 let (fName, fType) = parseTypeDeclaration' (T.unpack s)
118 (args, _) = splitFunctionType fType
127 (args, _) = splitFunctionType fType
119− template = T.pack (runIdent fName) <> " " <>
128+ template = runIdentT fName <> " " <>
120 T.unwords (map (prettyPrintWildcard wca) args) <>
129 T.unwords (map (prettyPrintWildcard wca) args) <>
121− " = ?" <> (T.strip . T.pack . runIdent $ fName)
130+ " = ?" <> (T.strip . runIdentT $ fName)
122 in [s, template]
131 in [s, template]
123
132
124parseType' :: (MonadError PscIdeError m) =>
133parseType' :: (MonadError PscIdeError m) =>
125− String -> m Type
134+ String -> m P.Type
126parseType' s =
135parseType' s =
127− case lex "<psc-ide>" s >>= runTokenParser "<psc-ide>" (parseType <* P.eof) of
136+ case P.lex "<psc-ide>" s >>= P.runTokenParser "<psc-ide>" (P.parseType <* Parsec.eof) of
128 Right type' -> pure type'
137 Right type' -> pure type'
129 Left err ->
138 Left err ->
130 throwError (GeneralError ("Parsing the splittype failed with:"
139 throwError (GeneralError ("Parsing the splittype failed with:"
131 ++ show err))
140 ++ show err))
132
141
133−parseTypeDeclaration' :: String -> (Ident, Type)
142+parseTypeDeclaration' :: String -> (P.Ident, P.Type)
134parseTypeDeclaration' s =
143parseTypeDeclaration' s =
135 let x = do
144 let x = do
136− ts <- lex "" s
145+ ts <- P.lex "" s
137− runTokenParser "" (parseDeclaration <* P.eof) ts
146+ P.runTokenParser "" (P.parseDeclaration <* Parsec.eof) ts
138 in
147 in
139 case unwrapPositioned <$> x of
148 case unwrapPositioned <$> x of
140− Right (TypeDeclaration i t) -> (i, t)
149+ Right (P.TypeDeclaration i t) -> (i, t)
141 y -> error (show y)
150 y -> error (show y)
142
151
143−splitFunctionType :: Type -> ([Type], Type)
152+splitFunctionType :: P.Type -> ([P.Type], P.Type)
144splitFunctionType t = (arguments, returns)
153splitFunctionType t = (arguments, returns)
145 where
154 where
146 returns = last splitted
155 returns = last splitted
147 arguments = init splitted
156 arguments = init splitted
148 splitted = splitType' t
157 splitted = splitType' t
149− splitType' (ForAll _ t' _) = splitType' t'
158+ splitType' (P.ForAll _ t' _) = splitType' t'
150− splitType' (ConstrainedType _ t') = splitType' t'
159+ splitType' (P.ConstrainedType _ t') = splitType' t'
151− splitType' (TypeApp (TypeApp t' lhs) rhs)
160+ splitType' (P.TypeApp (P.TypeApp t' lhs) rhs)
152− | t' == tyFunction = lhs : splitType' rhs
161+ | t' == P.tyFunction = lhs : splitType' rhs
153 splitType' t' = [t']
162 splitType' t' = [t']
src/Language/PureScript/Ide/CodecJSON.hsdeleted−13
@@ -1,13 +0,0 @@
1−module Language.PureScript.Ide.CodecJSON where
2−
3−import Data.Aeson
4−import Data.Text (Text())
5−import Data.Text.Lazy (toStrict, fromStrict)
6−import Data.Text.Lazy.Encoding (decodeUtf8, encodeUtf8)
7−
8−encodeT :: (ToJSON a) => a -> Text
9−encodeT = toStrict . decodeUtf8 . encode
10−
11−decodeT :: (FromJSON a) => Text -> Maybe a
12−decodeT = decode . encodeUtf8 . fromStrict
13−
src/Language/PureScript/Ide/Command.hs+72−20
@@ -1,3 +1,17 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.Command
4+-- Description : Datatypes for the commands psc-ide accepts
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- Datatypes for the commands psc-ide accepts
13+-----------------------------------------------------------------------------
14+
1{-# LANGUAGE MultiParamTypeClasses #-}
15{-# LANGUAGE MultiParamTypeClasses #-}
2{-# LANGUAGE OverloadedStrings #-}
16{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE ScopedTypeVariables #-}
17{-# LANGUAGE ScopedTypeVariables #-}
@@ -11,33 +25,64 @@ import Control.Monad
11import Data.Aeson
25import Data.Aeson
12import Data.Maybe
26import Data.Maybe
13import Data.Text (Text)
27import Data.Text (Text)
28+import Language.PureScript (ModuleName,
29+ moduleNameFromString)
14import Language.PureScript.Ide.CaseSplit
30import Language.PureScript.Ide.CaseSplit
15import Language.PureScript.Ide.Filter
31import Language.PureScript.Ide.Filter
16import Language.PureScript.Ide.Matcher
32import Language.PureScript.Ide.Matcher
17import Language.PureScript.Ide.Types
33import Language.PureScript.Ide.Types
18
34
19data Command
35data Command
36+ = Load
20− = Load { loadModules :: [ModuleIdent]
37+ { loadModules :: [ModuleIdent]
21− , loadDependencies :: [ModuleIdent]}
38+ , loadDependencies :: [ModuleIdent]
39+ }
40+ | Type
22− | Type { typeSearch :: DeclIdent
41+ { typeSearch :: DeclIdent
23− , typeFilters :: [Filter]}
42+ , typeFilters :: [Filter]
43+ }
44+ | Complete
24− | Complete { completeFilters :: [Filter]
45+ { completeFilters :: [Filter]
25− , completeMatcher :: Matcher}
46+ , completeMatcher :: Matcher
47+ }
48+ | Pursuit
26− | Pursuit { pursuitQuery :: PursuitQuery
49+ { pursuitQuery :: PursuitQuery
27− , pursuitSearchType :: PursuitSearchType}
50+ , pursuitSearchType :: PursuitSearchType
28− | List {listType :: ListType}
51+ }
29− | CaseSplit {
52+ | CaseSplit
30− caseSplitLine :: Text
53+ { caseSplitLine :: Text
31 , caseSplitBegin :: Int
54 , caseSplitBegin :: Int
32 , caseSplitEnd :: Int
55 , caseSplitEnd :: Int
33 , caseSplitAnnotations :: WildcardAnnotations
56 , caseSplitAnnotations :: WildcardAnnotations
34− , caseSplitType :: Type}
57+ , caseSplitType :: Text
58+ }
35− | AddClause {
59+ | AddClause
36− addClauseLine :: Text
60+ { addClauseLine :: Text
37− , addClauseAnnotations :: WildcardAnnotations}
61+ , addClauseAnnotations :: WildcardAnnotations
62+ }
63+ -- Import InputFile OutputFile
64+ | Import FilePath (Maybe FilePath) [Filter] ImportCommand
65+ | List { listType :: ListType }
38 | Cwd
66 | Cwd
39 | Quit
67 | Quit
40
68
69+data ImportCommand
70+ = AddImplicitImport ModuleName
71+ | AddImportForIdentifier DeclIdent
72+ deriving (Show, Eq)
73+
74+instance FromJSON ImportCommand where
75+ parseJSON = withObject "ImportCommand" $ \o -> do
76+ (command :: String) <- o .: "importCommand"
77+ case command of
78+ "addImplicitImport" -> do
79+ mn <- o .: "module"
80+ pure (AddImplicitImport (moduleNameFromString mn))
81+ "addImport" -> do
82+ ident <- o .: "identifier"
83+ pure (AddImportForIdentifier ident)
84+ _ -> mzero
85+
41data ListType = LoadedModules | Imports FilePath | AvailableModules
86data ListType = LoadedModules | Imports FilePath | AvailableModules
42
87
43instance FromJSON ListType where
88instance FromJSON ListType where
@@ -60,11 +105,11 @@ instance FromJSON Command where
60 return $ List (fromMaybe LoadedModules listType')
105 return $ List (fromMaybe LoadedModules listType')
61 "cwd" -> return Cwd
106 "cwd" -> return Cwd
62 "quit" -> return Quit
107 "quit" -> return Quit
63− "load" -> do
108+ "load" ->
64− params <- o .: "params"
109+ maybe (pure (Load [] [])) (\params -> do
65− mods <- params .:? "modules"
110+ mods <- params .:? "modules"
66− deps <- params .:? "dependencies"
111+ deps <- params .:? "dependencies"
67− return $ Load (fromMaybe [] mods) (fromMaybe [] deps)
112+ pure $ Load (fromMaybe [] mods) (fromMaybe [] deps)) =<< o .:? "params"
68 "type" -> do
113 "type" -> do
69 params <- o .: "params"
114 params <- o .: "params"
70 search <- params .: "search"
115 search <- params .: "search"
@@ -97,5 +142,12 @@ instance FromJSON Command where
97 return $ AddClause line (if annotations
142 return $ AddClause line (if annotations
98 then explicitAnnotations
143 then explicitAnnotations
99 else noAnnotations)
144 else noAnnotations)
145+ "import" -> do
146+ params <- o .: "params"
147+ fp <- params .: "file"
148+ out <- params .:? "outfile"
149+ filters <- params .:? "filters"
150+ importCommand <- params .: "importCommand"
151+ pure $ Import fp out (fromMaybe [] filters) importCommand
100 _ -> mzero
152 _ -> mzero
101
153
src/Language/PureScript/Ide/Completion.hs+7−10
@@ -13,23 +13,20 @@ import Language.PureScript.Ide.Types
13
13
14-- | Applies the CompletionFilters and the Matcher to the given Modules
14-- | Applies the CompletionFilters and the Matcher to the given Modules
15-- and sorts the found Completions according to the Matching Score
15-- and sorts the found Completions according to the Matching Score
16−getCompletions :: [Filter] -> Matcher -> [Module] -> [Completion]
16+getCompletions :: [Filter] -> Matcher -> [Module] -> [Match]
17getCompletions filters matcher modules =
17getCompletions filters matcher modules =
18 runMatcher matcher $ completionsFromModules (applyFilters filters modules)
18 runMatcher matcher $ completionsFromModules (applyFilters filters modules)
19
19
20−getExactMatches :: DeclIdent -> [Filter] -> [Module] -> [Completion]
20+getExactMatches :: DeclIdent -> [Filter] -> [Module] -> [Match]
21getExactMatches search filters modules =
21getExactMatches search filters modules =
22 completionsFromModules $
22 completionsFromModules $
23 applyFilters (equalityFilter search : filters) modules
23 applyFilters (equalityFilter search : filters) modules
24
24
25−completionsFromModules :: [Module] -> [Completion]
25+completionsFromModules :: [Module] -> [Match]
26completionsFromModules = foldMap completionFromModule
26completionsFromModules = foldMap completionFromModule
27 where
27 where
28− completionFromModule :: Module -> [Completion]
28+ completionFromModule :: Module -> [Match]
29− completionFromModule (moduleIdent, decls) = mapMaybe (completionFromDecl moduleIdent) decls
29+ completionFromModule (moduleIdent, decls) = mapMaybe (matchFromDecl moduleIdent) decls
30
30
31−completionFromDecl :: ModuleIdent -> ExternDecl -> Maybe Completion
31+matchFromDecl :: ModuleIdent -> ExternDecl -> Maybe Match
32−completionFromDecl mi (FunctionDecl name type') = Just (Completion (mi, name, type'))
32+matchFromDecl mi = Just . Match mi
33−completionFromDecl mi (DataDecl name kind) = Just (Completion (mi, name, kind))
34−completionFromDecl _ (ModuleDecl name _) = Just (Completion ("module", name, "module"))
35−completionFromDecl _ _ = Nothing
src/Language/PureScript/Ide/Error.hs+15−6
@@ -1,6 +1,20 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.Error
4+-- Description : Error types for psc-ide
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- Error types for psc-ide
13+-----------------------------------------------------------------------------
14+
1{-# LANGUAGE OverloadedStrings #-}
15{-# LANGUAGE OverloadedStrings #-}
2module Language.PureScript.Ide.Error
16module Language.PureScript.Ide.Error
3− (ErrorMsg, PscIdeError(..), textError, first)
17+ (ErrorMsg, PscIdeError(..), textError)
4 where
18 where
5
19
6import Data.Aeson
20import Data.Aeson
@@ -35,8 +49,3 @@ textError (ParseError parseError msg) = pack $ msg <> ": " <> show (escape parse
35 -- escape newlines and other special chars so we can send the error over the socket as a single line
49 -- escape newlines and other special chars so we can send the error over the socket as a single line
36 escape :: P.ParseError -> String
50 escape :: P.ParseError -> String
37 escape = show
51 escape = show
38−
39−-- | Specialized version of `first` from `Data.Bifunctors`
40−first :: (a -> b) -> Either a r -> Either b r
41−first f (Left x) = Left (f x)
42−first _ (Right r2) = Right r2
src/Language/PureScript/Ide/Externs.hs+58−46
@@ -1,3 +1,17 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.Externs
4+-- Description : Handles externs files for psc-ide
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- Handles externs files for psc-ide
13+-----------------------------------------------------------------------------
14+
1{-# LANGUAGE FlexibleContexts #-}
15{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE MultiParamTypeClasses #-}
16{-# LANGUAGE MultiParamTypeClasses #-}
3{-# LANGUAGE OverloadedStrings #-}
17{-# LANGUAGE OverloadedStrings #-}
@@ -5,34 +19,32 @@
5{-# LANGUAGE ScopedTypeVariables #-}
19{-# LANGUAGE ScopedTypeVariables #-}
6
20
7module Language.PureScript.Ide.Externs
21module Language.PureScript.Ide.Externs
8− (
9− ExternDecl(..),
22+ ( ExternDecl(..),
10 ModuleIdent,
23 ModuleIdent,
11 DeclIdent,
24 DeclIdent,
12− Type,
13− Fixity(..),
14 readExternFile,
25 readExternFile,
15 convertExterns,
26 convertExterns,
16 unwrapPositioned,
27 unwrapPositioned,
17 unwrapPositionedRef
28 unwrapPositionedRef
18 ) where
29 ) where
19
30
20−import Prelude ()
31+import Prelude ()
21import Prelude.Compat
32import Prelude.Compat
22
33
23import Control.Monad.Error.Class
34import Control.Monad.Error.Class
24import Control.Monad.IO.Class
35import Control.Monad.IO.Class
36+import Data.List (nub)
25−import Data.Maybe (mapMaybe)
37+import Data.Maybe (mapMaybe)
26−import Data.Text (Text)
38+import Data.Monoid
39+import Data.Text (Text)
27−import qualified Data.Text as T
40+import qualified Data.Text as T
28−import qualified Data.Text.IO as T
41+import qualified Data.Text.IO as T
29−import qualified Language.PureScript.AST.Declarations as D
30−import qualified Language.PureScript.Externs as PE
31−import Language.PureScript.Ide.CodecJSON
32−import Language.PureScript.Ide.Error (PscIdeError (..))
42+import Language.PureScript.Ide.Error (PscIdeError (..))
33import Language.PureScript.Ide.Types
43import Language.PureScript.Ide.Types
44+import Language.PureScript.Ide.Util
45+
34−import qualified Language.PureScript.Names as N
46+import qualified Language.PureScript as P
35−import qualified Language.PureScript.Pretty as PP
47+import qualified Language.PureScript.Externs as PE
36
48
37readExternFile :: (MonadIO m, MonadError PscIdeError m) =>
49readExternFile :: (MonadIO m, MonadError PscIdeError m) =>
38 FilePath -> m PE.ExternsFile
50 FilePath -> m PE.ExternsFile
@@ -42,17 +54,14 @@ readExternFile fp = do
42 Nothing -> throwError . GeneralError $ "Parsing the extern at: " ++ fp ++ " failed"
54 Nothing -> throwError . GeneralError $ "Parsing the extern at: " ++ fp ++ " failed"
43 Just externs -> pure externs
55 Just externs -> pure externs
44
56
45−moduleNameToText :: N.ModuleName -> Text
57+moduleNameToText :: P.ModuleName -> Text
46−moduleNameToText = T.pack . N.runModuleName
58+moduleNameToText = T.pack . P.runModuleName
47
59
48−properNameToText :: N.ProperName a -> Text
49−properNameToText = T.pack . N.runProperName
50−
51−identToText :: N.Ident -> Text
60+identToText :: P.Ident -> Text
52−identToText = T.pack . N.runIdent
61+identToText = T.pack . P.runIdent
53
62
54convertExterns :: PE.ExternsFile -> Module
63convertExterns :: PE.ExternsFile -> Module
55−convertExterns ef = (moduleName, exportDecls ++ importDecls ++ otherDecls)
64+convertExterns ef = (moduleName, exportDecls ++ importDecls ++ decls)
56 where
65 where
57 moduleName = moduleNameToText (PE.efModuleName ef)
66 moduleName = moduleNameToText (PE.efModuleName ef)
58 importDecls = convertImport <$> PE.efImports ef
67 importDecls = convertImport <$> PE.efImports ef
@@ -61,42 +70,45 @@ convertExterns ef = (moduleName, exportDecls ++ importDecls ++ otherDecls)
61 -- operatorDecls = convertOperator <$> PE.efFixities ef
70 -- operatorDecls = convertOperator <$> PE.efFixities ef
62 otherDecls = mapMaybe convertDecl (PE.efDeclarations ef)
71 otherDecls = mapMaybe convertDecl (PE.efDeclarations ef)
63
72
73+ typeClassFilter = foldMap removeTypeDeclarationsForClass (filter isTypeClassDeclaration otherDecls)
74+ decls = nub $ appEndo typeClassFilter otherDecls
75+
76+removeTypeDeclarationsForClass :: ExternDecl -> Endo [ExternDecl]
77+removeTypeDeclarationsForClass (TypeClassDeclaration n) = Endo (filter notDuplicate)
78+ where notDuplicate (TypeDeclaration n' _) = runProperNameT n /= runProperNameT n'
79+ notDuplicate (TypeSynonymDeclaration n' _) = runProperNameT n /= runProperNameT n'
80+ notDuplicate _ = True
81+removeTypeDeclarationsForClass _ = mempty
82+
83+isTypeClassDeclaration :: ExternDecl -> Bool
84+isTypeClassDeclaration TypeClassDeclaration{} = True
85+isTypeClassDeclaration _ = False
86+
64convertImport :: PE.ExternsImport -> ExternDecl
87convertImport :: PE.ExternsImport -> ExternDecl
65convertImport ei = Dependency
88convertImport ei = Dependency
66 (moduleNameToText (PE.eiModule ei))
89 (moduleNameToText (PE.eiModule ei))
67 []
90 []
68 (moduleNameToText <$> PE.eiImportedAs ei)
91 (moduleNameToText <$> PE.eiImportedAs ei)
69
92
70−convertExport :: D.DeclarationRef -> Maybe ExternDecl
93+convertExport :: P.DeclarationRef -> Maybe ExternDecl
71−convertExport (D.ModuleRef mn) = Just (Export (moduleNameToText mn))
94+convertExport (P.ModuleRef mn) = Just (Export (moduleNameToText mn))
72convertExport _ = Nothing
95convertExport _ = Nothing
73
96
74convertDecl :: PE.ExternsDeclaration -> Maybe ExternDecl
97convertDecl :: PE.ExternsDeclaration -> Maybe ExternDecl
75−convertDecl PE.EDType{..} = Just $
98+convertDecl PE.EDType{..} = Just $ TypeDeclaration edTypeName edTypeKind
76− DataDecl
77− (properNameToText edTypeName)
78− (packAndStrip (PP.prettyPrintKind edTypeKind))
79convertDecl PE.EDTypeSynonym{..} = Just $
99convertDecl PE.EDTypeSynonym{..} = Just $
80− DataDecl
100+ TypeSynonymDeclaration edTypeSynonymName edTypeSynonymType
81− (properNameToText edTypeSynonymName)
82− (packAndStrip (PP.prettyPrintType edTypeSynonymType))
83convertDecl PE.EDDataConstructor{..} = Just $
101convertDecl PE.EDDataConstructor{..} = Just $
84− DataDecl
102+ DataConstructor (runProperNameT edDataCtorName) edDataCtorTypeCtor edDataCtorType
85− (properNameToText edDataCtorName)
86− (packAndStrip (PP.prettyPrintType edDataCtorType))
87convertDecl PE.EDValue{..} = Just $
103convertDecl PE.EDValue{..} = Just $
88− FunctionDecl
89− (identToText edValueName)
104+ ValueDeclaration (identToText edValueName) edValueType
90− (packAndStrip (PP.prettyPrintType edValueType))
105+convertDecl PE.EDClass{..} = Just $ TypeClassDeclaration edClassName
91−convertDecl _ = Nothing
106+convertDecl PE.EDInstance{} = Nothing
92−
93−packAndStrip :: String -> Text
94−packAndStrip = T.unwords . fmap T.strip . T.lines . T.pack
95
107
96−unwrapPositioned :: D.Declaration -> D.Declaration
108+unwrapPositioned :: P.Declaration -> P.Declaration
97−unwrapPositioned (D.PositionedDeclaration _ _ x) = x
109+unwrapPositioned (P.PositionedDeclaration _ _ x) = x
98unwrapPositioned x = x
110unwrapPositioned x = x
99
111
100−unwrapPositionedRef :: D.DeclarationRef -> D.DeclarationRef
112+unwrapPositionedRef :: P.DeclarationRef -> P.DeclarationRef
101−unwrapPositionedRef (D.PositionedDeclarationRef _ _ x) = x
113+unwrapPositionedRef (P.PositionedDeclarationRef _ _ x) = x
102unwrapPositionedRef x = x
114unwrapPositionedRef x = x
src/Language/PureScript/Ide/Filter.hs+28−11
@@ -1,10 +1,30 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.Filter
4+-- Description : Filters for psc-ide commands
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- Filters for psc-ide commands
13+-----------------------------------------------------------------------------
14+
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
15{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2{-# LANGUAGE OverloadedStrings #-}
16{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE ScopedTypeVariables #-}
17{-# LANGUAGE ScopedTypeVariables #-}
18+
4module Language.PureScript.Ide.Filter
19module Language.PureScript.Ide.Filter
20+ ( Filter
21+ , moduleFilter
22+ , prefixFilter
23+ , equalityFilter
5− (Filter, moduleFilter, prefixFilter, equalityFilter, dependencyFilter,
24+ , dependencyFilter
25+ , runFilter
6− runFilter, applyFilters)
26+ , applyFilters
7− where
27+ ) where
8
28
9import Prelude ()
29import Prelude ()
10import Prelude.Compat
30import Prelude.Compat
@@ -16,6 +36,7 @@ import Data.Maybe (listToMaybe, mapMaybe)
16import Data.Monoid
36import Data.Monoid
17import Data.Text (Text, isPrefixOf)
37import Data.Text (Text, isPrefixOf)
18import Language.PureScript.Ide.Types
38import Language.PureScript.Ide.Types
39+import Language.PureScript.Ide.Util
19
40
20newtype Filter = Filter (Endo [Module]) deriving(Monoid)
41newtype Filter = Filter (Endo [Module]) deriving(Monoid)
21
42
@@ -57,10 +78,9 @@ prefixFilter "" = mkFilter id
57prefixFilter t = mkFilter $ identFilter prefix t
78prefixFilter t = mkFilter $ identFilter prefix t
58 where
79 where
59 prefix :: ExternDecl -> Text -> Bool
80 prefix :: ExternDecl -> Text -> Bool
60− prefix (FunctionDecl name _) search = search `isPrefixOf` name
81+ prefix Export{} _ = False
61− prefix (DataDecl name _) search = search `isPrefixOf` name
82+ prefix Dependency{} _ = False
62− prefix (ModuleDecl name _) search = search `isPrefixOf` name
83+ prefix ed search = search `isPrefixOf` identifierFromExternDecl ed
63− prefix _ _ = False
64
84
65
85
66-- | Only keeps Identifiers that are equal to the search string
86-- | Only keeps Identifiers that are equal to the search string
@@ -68,10 +88,7 @@ equalityFilter :: Text -> Filter
68equalityFilter = mkFilter . identFilter equality
88equalityFilter = mkFilter . identFilter equality
69 where
89 where
70 equality :: ExternDecl -> Text -> Bool
90 equality :: ExternDecl -> Text -> Bool
71− equality (FunctionDecl name _) prefix = prefix == name
91+ equality ed search = identifierFromExternDecl ed == search
72− equality (DataDecl name _) prefix = prefix == name
73− equality _ _ = False
74−
75
92
76identFilter :: (ExternDecl -> Text -> Bool ) -> Text -> [Module] -> [Module]
93identFilter :: (ExternDecl -> Text -> Bool ) -> Text -> [Module] -> [Module]
77identFilter predicate search =
94identFilter predicate search =
src/Language/PureScript/Ide/Imports.hsnew file+355
@@ -0,0 +1,355 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.Imports
4+-- Description : Provides functionality to manage imports
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- Provides functionality to manage imports
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE ConstraintKinds #-}
16+{-# LANGUAGE FlexibleContexts #-}
17+{-# LANGUAGE LambdaCase #-}
18+{-# LANGUAGE MultiParamTypeClasses #-}
19+{-# LANGUAGE OverloadedStrings #-}
20+{-# LANGUAGE PackageImports #-}
21+
22+module Language.PureScript.Ide.Imports
23+ ( addImplicitImport
24+ , addImportForIdentifier
25+ , answerRequest
26+ -- for tests
27+ , parseImport
28+ , prettyPrintImportSection
29+ , addImplicitImport'
30+ , addExplicitImport'
31+ , sliceImportSection
32+ , prettyPrintImport'
33+ , Import(Import)
34+ )
35+ where
36+
37+import Control.Applicative ((<|>))
38+import Control.Monad.Error.Class
39+import Control.Monad.IO.Class
40+import "monad-logger" Control.Monad.Logger
41+import Data.Bifunctor (first, second)
42+import Data.Function (on)
43+import qualified Data.List as List
44+import Data.Maybe (isNothing)
45+import Data.Monoid ((<>))
46+import Data.Text (Text)
47+import qualified Data.Text as T
48+import qualified Data.Text.IO as TIO
49+import qualified Language.PureScript as P
50+import Language.PureScript.Ide.Completion
51+import Language.PureScript.Ide.Error
52+import Language.PureScript.Ide.Externs (unwrapPositioned,
53+ unwrapPositionedRef)
54+import Language.PureScript.Ide.Filter
55+import Language.PureScript.Ide.State
56+import Language.PureScript.Ide.Types
57+import Language.PureScript.Ide.Util
58+
59+data Import = Import P.ModuleName P.ImportDeclarationType (Maybe P.ModuleName)
60+ deriving (Eq, Show)
61+
62+instance Ord Import where
63+ compare = compImport
64+
65+compImportType :: P.ImportDeclarationType -> P.ImportDeclarationType -> Ordering
66+compImportType P.Implicit P.Implicit = EQ
67+compImportType P.Implicit _ = LT
68+compImportType (P.Explicit _) (P.Hiding _) = LT
69+compImportType (P.Explicit _) (P.Explicit _) = EQ
70+compImportType (P.Explicit _) P.Implicit = GT
71+compImportType (P.Hiding _) (P.Hiding _) = EQ
72+compImportType (P.Hiding _) _ = GT
73+
74+compImport :: Import -> Import -> Ordering
75+compImport (Import n i q) (Import n' i' q')
76+ | compImportType i i' /= EQ = compImportType i i'
77+ -- This means that for a stable sort, the first implicit import will stay
78+ -- the first implicit import
79+ | P.isImplicit i && isNothing q = LT
80+ | P.isImplicit i && isNothing q' = GT
81+ | otherwise = compare n n'
82+
83+-- | Reads a file and returns the (lines before the imports, the imports, the
84+-- lines after the imports)
85+parseImportsFromFile :: (MonadIO m, MonadError PscIdeError m) =>
86+ FilePath -> m (P.ModuleName, [Text], [Import], [Text])
87+parseImportsFromFile fp = do
88+ file <- liftIO (TIO.readFile fp)
89+ case sliceImportSection (T.lines file) of
90+ Right res -> pure res
91+ Left err -> throwError (GeneralError err)
92+
93+parseImportsWithModuleName :: [Text] -> Either String (P.ModuleName, [Import])
94+parseImportsWithModuleName ls = do
95+ (P.Module _ _ mn decls _) <- moduleParse ls
96+ pure (mn, concatMap mkImport (unwrapPositioned <$> decls))
97+ where
98+ mkImport (P.ImportDeclaration mn (P.Explicit refs) qual _) =
99+ [Import mn (P.Explicit (unwrapPositionedRef <$> refs)) qual]
100+ mkImport (P.ImportDeclaration mn it qual _) = [Import mn it qual]
101+ mkImport _ = []
102+
103+sliceImportSection :: [Text] -> Either String (P.ModuleName, [Text], [Import], [Text])
104+sliceImportSection ts =
105+ case foldl step (ModuleHeader 0) (zip [0..] ts) of
106+ Res start end ->
107+ let
108+ (moduleHeader, (importSection, remainingFile)) =
109+ List.splitAt (succ (end - start)) `second` List.splitAt start ts
110+ in
111+ (\(mn, is) -> (mn, moduleHeader, is, remainingFile)) <$>
112+ parseImportsWithModuleName (moduleHeader <> importSection)
113+
114+ -- If we don't find any imports, we insert a newline after the module
115+ -- declaration and begin a new importsection
116+ ModuleHeader ix ->
117+ let (moduleHeader, remainingFile) = List.splitAt (succ ix) ts
118+ in
119+ (\(mn, is) -> (mn, moduleHeader ++ [""], is, remainingFile)) <$>
120+ parseImportsWithModuleName moduleHeader
121+ _ -> Left "Failed to detect the import section"
122+
123+data ImportStateMachine = ModuleHeader Int | ImportSection Int Int | Res Int Int
124+
125+-- | We start in the
126+--
127+-- * ModuleHeader state.
128+--
129+-- We skip every line we encounter, that doesn't start with "import". If we find
130+-- a line that starts with module we store that linenumber. Once we find a line
131+-- with "import" we store its linenumber as the start of the import section and
132+-- change into the
133+--
134+-- * ImportSection state
135+--
136+-- For any line that starts with import or whitespace(is thus indented) we
137+-- expand the end of the import section to that line and continue. If we
138+-- encounter a commented or empty line, we continue moving forward in the
139+-- ImportSection state but don't expand the import section end yet. This allows
140+-- us to exclude newlines or comments that directly follow the import section.
141+-- Once we encounter a line that is not a comment, newline, indentation or
142+-- import we switch into the
143+--
144+-- * Res state
145+--
146+-- , which just shortcuts to the end of the file and carries the detected import
147+-- section boundaries
148+step :: ImportStateMachine -> (Int, Text) -> ImportStateMachine
149+step (ModuleHeader mi) (ix, l)
150+ | T.isPrefixOf "module " l = ModuleHeader ix
151+ | T.isPrefixOf "import " l = ImportSection ix ix
152+ | otherwise = ModuleHeader mi
153+step (ImportSection start lastImportLine) (ix, l)
154+ | any (`T.isPrefixOf` l) ["import", " "] = ImportSection start ix
155+ | T.isPrefixOf "--" l || l == "" = ImportSection start lastImportLine
156+ | otherwise = Res start lastImportLine
157+step (Res start end) _ = Res start end
158+
159+moduleParse :: [Text] -> Either String P.Module
160+moduleParse t = first show $ do
161+ tokens <- (P.lex "" . T.unpack . T.unlines) t
162+ P.runTokenParser "<psc-ide>" P.parseModule tokens
163+
164+-- | Adds an implicit import like @import Prelude@ to a Sourcefile.
165+addImplicitImport :: (MonadIO m, MonadError PscIdeError m)
166+ => FilePath -- ^ The Sourcefile read from
167+ -> P.ModuleName -- ^ The module to import
168+ -> m [Text]
169+addImplicitImport fp mn = do
170+ (_, pre, imports, post) <- parseImportsFromFile fp
171+ let newImportSection = addImplicitImport' imports mn
172+ pure $ pre ++ newImportSection ++ post
173+
174+addImplicitImport' :: [Import] -> P.ModuleName -> [Text]
175+addImplicitImport' imports mn =
176+ -- We need to append the new import, because there could already be implicit
177+ -- imports and we need to preserve the order on these, as the first implicit
178+ -- import is the one that doesn't generate warnings.
179+ prettyPrintImportSection ( imports ++ [Import mn P.Implicit Nothing])
180+
181+-- | Adds an explicit import like @import Prelude (unit)@ to a Sourcefile. If an
182+-- explicit import already exists for the given module, it adds the identifier
183+-- to that imports list.
184+--
185+-- So @addExplicitImport "/File.purs" "bind" "Prelude"@ with an already existing
186+-- @import Prelude (bind)@ in the file File.purs returns @["import Prelude
187+-- (bind, unit)"]@
188+addExplicitImport :: (MonadIO m, MonadError PscIdeError m, MonadLogger m) =>
189+ FilePath -> ExternDecl -> P.ModuleName -> m [Text]
190+addExplicitImport fp decl moduleName = do
191+ (mn, pre, imports, post) <- parseImportsFromFile fp
192+ let newImportSection =
193+ -- TODO: Open an issue when this PR is merged, we should optimise this
194+ -- so that this case does not write to disc
195+ if mn == moduleName
196+ then imports
197+ else addExplicitImport' decl moduleName imports
198+ pure (pre ++ prettyPrintImportSection newImportSection ++ post)
199+
200+addExplicitImport' :: ExternDecl -> P.ModuleName -> [Import] -> [Import]
201+addExplicitImport' decl moduleName imports =
202+ let
203+ isImplicitlyImported =
204+ not . null $ filter (\case
205+ (Import mn P.Implicit Nothing) -> mn == moduleName
206+ _ -> False) imports
207+ matches (Import mn (P.Explicit _) Nothing) = mn == moduleName
208+ matches _ = False
209+ freshImport = Import moduleName (P.Explicit [refFromDeclaration decl]) Nothing
210+ in
211+ if isImplicitlyImported
212+ then imports
213+ else updateAtFirstOrPrepend matches (insertDeclIntoImport decl) freshImport imports
214+ where
215+ refFromDeclaration (TypeClassDeclaration n) = P.TypeClassRef n
216+ refFromDeclaration (DataConstructor n tn _) =
217+ P.TypeRef tn (Just [P.ProperName (T.unpack n)])
218+ refFromDeclaration (TypeDeclaration n _) = P.TypeRef n (Just [])
219+ refFromDeclaration d =
220+ let
221+ ident = T.unpack (identifierFromExternDecl d)
222+ in
223+ P.ValueRef ((if all P.isSymbolChar ident then P.Op else P.Ident) ident)
224+
225+ -- | Adds a declaration to an import:
226+ -- TypeDeclaration "Maybe" + Data.Maybe (maybe) -> Data.Maybe(Maybe, maybe)
227+ insertDeclIntoImport :: ExternDecl -> Import -> Import
228+ insertDeclIntoImport decl' (Import mn (P.Explicit refs) Nothing) =
229+ Import mn (P.Explicit (insertDeclIntoRefs decl' refs)) Nothing
230+ insertDeclIntoImport _ is = is
231+
232+ insertDeclIntoRefs :: ExternDecl -> [P.DeclarationRef] -> [P.DeclarationRef]
233+ insertDeclIntoRefs (DataConstructor dtor tn _) refs =
234+ let
235+ dtor' = P.ProperName (T.unpack dtor)
236+ -- TODO: Get rid of this once typeclasses can't be imported like types
237+ refs' = properRefToTypeRef <$> refs
238+ in
239+ updateAtFirstOrPrepend (matchType tn) (insertDtor dtor') (P.TypeRef tn (Just [dtor'])) refs'
240+ insertDeclIntoRefs dr refs = List.nubBy ((==) `on` P.prettyPrintRef) (refFromDeclaration dr : refs)
241+
242+ insertDtor dtor (P.TypeRef tn' dtors) =
243+ case dtors of
244+ Just dtors' -> P.TypeRef tn' (Just (List.nub (dtor : dtors')))
245+ -- This means the import was opened. We don't add anything in this case
246+ -- import Data.Maybe (Maybe(..)) -> import Data.Maybe (Maybe(Just))
247+ Nothing -> P.TypeRef tn' Nothing
248+ insertDtor _ refs = refs
249+
250+
251+ -- TODO: Get rid of this once typeclasses can't be imported like types
252+ properRefToTypeRef (P.ProperRef n) = P.TypeRef (P.ProperName n) (Just [])
253+ properRefToTypeRef r = r
254+
255+ matchType :: P.ProperName 'P.TypeName -> P.DeclarationRef -> Bool
256+ matchType tn (P.TypeRef n _) = tn == n
257+ matchType _ _ = False
258+
259+updateAtFirstOrPrepend :: (a -> Bool) -> (a -> a) -> a -> [a] -> [a]
260+updateAtFirstOrPrepend p t d l =
261+ case List.findIndex p l of
262+ Nothing -> d : l
263+ Just ix ->
264+ let (x, a : y) = List.splitAt ix l
265+ in x ++ [t a] ++ y
266+
267+-- | Looks up the given identifier in the currently loaded modules.
268+--
269+-- * Throws an error if the identifier cannot be found.
270+--
271+-- * If exactly one match is found, adds an explicit import to the importsection
272+--
273+-- * If more than one possible imports are found, reports the possibilities as a
274+-- list of completions.
275+addImportForIdentifier :: (PscIde m, MonadError PscIdeError m, MonadLogger m)
276+ => FilePath -- ^ The Sourcefile to read from
277+ -> Text -- ^ The identifier to import
278+ -> [Filter] -- ^ Filters to apply before searching for
279+ -- the identifier
280+ -> m (Either [Match] [Text])
281+addImportForIdentifier fp ident filters = do
282+ modules <- getAllModulesWithReexports
283+ case getExactMatches ident filters modules of
284+ [] ->
285+ throwError (NotFound "Couldn't find the given identifier. \
286+ \Have you loaded the corresponding module?")
287+
288+ -- Only one match was found for the given identifier, so we can insert it
289+ -- right away
290+ [Match m decl] ->
291+ Right <$> addExplicitImport fp decl (P.moduleNameFromString (T.unpack m))
292+
293+ -- This case comes up for newtypes and dataconstructors. Because values and
294+ -- types don't share a namespace we can get multiple matches from the same
295+ -- module. This also happens for parameterized types, as these generate both
296+ -- a type aswell as a type synonym.
297+
298+ ms@[Match m1 d1, Match m2 d2] ->
299+ if m1 /= m2
300+ -- If the modules don't line up we just ask the user to specify the
301+ -- module
302+ then pure $ Left ms
303+ else case decideRedundantCase d1 d2 <|> decideRedundantCase d2 d1 of
304+ -- If dataconstructor and type line up we just import the
305+ -- dataconstructor as that will give us an unnecessary import warning at
306+ -- worst
307+ Just decl ->
308+ Right <$> addExplicitImport fp decl (P.moduleNameFromString (T.unpack m1))
309+ -- Here we need the user to specify whether he wanted a dataconstructor
310+ -- or a type
311+ Nothing ->
312+ throwError (GeneralError "Undecidable between type and dataconstructor")
313+
314+ -- Multiple matches were found so we need to ask the user to clarify which
315+ -- module he meant
316+ xs ->
317+ pure $ Left xs
318+ where
319+ decideRedundantCase dtor@(DataConstructor _ t _) (TypeDeclaration t' _) =
320+ if t == t' then Just dtor else Nothing
321+ decideRedundantCase TypeDeclaration{} ts@TypeSynonymDeclaration{} =
322+ Just ts
323+ decideRedundantCase _ _ = Nothing
324+
325+prettyPrintImport' :: Import -> Text
326+-- TODO: remove this clause once P.prettyPrintImport can properly handle PositionedRefs
327+prettyPrintImport' (Import mn (P.Explicit refs) qual) =
328+ T.pack $ "import " ++ P.prettyPrintImport mn (P.Explicit (unwrapPositionedRef <$> refs)) qual
329+prettyPrintImport' (Import mn idt qual) =
330+ T.pack $ "import " ++ P.prettyPrintImport mn idt qual
331+
332+prettyPrintImportSection :: [Import] -> [Text]
333+prettyPrintImportSection imports = map prettyPrintImport' (List.sort imports)
334+
335+-- | Writes a list of lines to @Just filepath@ and responds with a @TextResult@,
336+-- or returns the lines as a @MultilineTextResult@ if @Nothing@ was given as the
337+-- first argument.
338+answerRequest :: (MonadIO m) => Maybe FilePath -> [Text] -> m Success
339+answerRequest outfp rs =
340+ case outfp of
341+ Nothing -> pure $ MultilineTextResult rs
342+ Just outfp' -> do
343+ liftIO $ TIO.writeFile outfp' (T.unlines rs)
344+ pure $ TextResult $ "Written to " <> T.pack outfp'
345+
346+-- | Test and ghci helper
347+parseImport :: Text -> Maybe Import
348+parseImport t =
349+ case P.lex "<psc-ide>" (T.unpack t)
350+ >>= P.runTokenParser "<psc-ide>" P.parseImportDeclaration' of
351+ Right (mn, P.Explicit refs, mmn, _) ->
352+ Just (Import mn (P.Explicit (unwrapPositionedRef <$> refs)) mmn)
353+ Right (mn, idt, mmn, _) -> Just (Import mn idt mmn)
354+ Left _ -> Nothing
355+
src/Language/PureScript/Ide/Matcher.hs+46−25
@@ -1,7 +1,26 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.Matcher
4+-- Description : Matchers for psc-ide commands
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- Matchers for psc-ide commands
13+-----------------------------------------------------------------------------
14+
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
15{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2{-# LANGUAGE OverloadedStrings #-}
16{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE ScopedTypeVariables #-}
17{-# LANGUAGE ScopedTypeVariables #-}
18+
4−module Language.PureScript.Ide.Matcher (Matcher, flexMatcher, runMatcher) where
19+module Language.PureScript.Ide.Matcher
20+ ( Matcher
21+ , flexMatcher
22+ , runMatcher
23+ ) where
5
24
6import Prelude ()
25import Prelude ()
7import Prelude.Compat
26import Prelude.Compat
@@ -16,13 +35,14 @@ import Data.Text (Text)
16import qualified Data.Text as T
35import qualified Data.Text as T
17import qualified Data.Text.Encoding as TE
36import qualified Data.Text.Encoding as TE
18import Language.PureScript.Ide.Types
37import Language.PureScript.Ide.Types
38+import Language.PureScript.Ide.Util
19import Text.EditDistance
39import Text.EditDistance
20import Text.Regex.TDFA ((=~))
40import Text.Regex.TDFA ((=~))
21
41
22
42
23−type ScoredCompletion = (Completion, Double)
43+type ScoredMatch = (Match, Double)
24
44
25−newtype Matcher = Matcher (Endo [Completion]) deriving(Monoid)
45+newtype Matcher = Matcher (Endo [Match]) deriving(Monoid)
26
46
27instance FromJSON Matcher where
47instance FromJSON Matcher where
28 parseJSON = withObject "matcher" $ \o -> do
48 parseJSON = withObject "matcher" $ \o -> do
@@ -41,42 +61,43 @@ instance FromJSON Matcher where
41 Nothing -> return mempty
61 Nothing -> return mempty
42
62
43-- | Matches any occurence of the search string with intersections
63-- | Matches any occurence of the search string with intersections
44−-- |
64+--
45−-- | The scoring measures how far the matches span the string where
65+-- The scoring measures how far the matches span the string where
46−-- | closer is better.
66+-- closer is better.
47−-- | Examples:
67+-- Examples:
48−-- | flMa matches flexMatcher. Score: 14.28
68+-- flMa matches flexMatcher. Score: 14.28
49−-- | sons matches sortCompletions. Score: 6.25
69+-- sons matches sortCompletions. Score: 6.25
50flexMatcher :: Text -> Matcher
70flexMatcher :: Text -> Matcher
51−flexMatcher pattern = mkMatcher (flexMatch pattern)
71+flexMatcher p = mkMatcher (flexMatch p)
52
72
53distanceMatcher :: Text -> Int -> Matcher
73distanceMatcher :: Text -> Int -> Matcher
54distanceMatcher q maxDist = mkMatcher (distanceMatcher' q maxDist)
74distanceMatcher q maxDist = mkMatcher (distanceMatcher' q maxDist)
55
75
56−distanceMatcher' :: Text -> Int -> [Completion] -> [ScoredCompletion]
76+distanceMatcher' :: Text -> Int -> [Match] -> [ScoredMatch]
57distanceMatcher' q maxDist = mapMaybe go
77distanceMatcher' q maxDist = mapMaybe go
58 where
78 where
59− go c@(Completion (_, y, _)) = let d = dist (T.unpack y)
79+ go m = let d = dist (T.unpack y)
80+ y = identifierFromMatch m
60− in if d <= maxDist
81+ in if d <= maxDist
61− then Just (c, 1 / fromIntegral d)
82+ then Just (m, 1 / fromIntegral d)
62− else Nothing
83+ else Nothing
63 dist = levenshteinDistance defaultEditCosts (T.unpack q)
84 dist = levenshteinDistance defaultEditCosts (T.unpack q)
64
85
65−mkMatcher :: ([Completion] -> [ScoredCompletion]) -> Matcher
86+mkMatcher :: ([Match] -> [ScoredMatch]) -> Matcher
66mkMatcher matcher = Matcher . Endo $ fmap fst . sortCompletions . matcher
87mkMatcher matcher = Matcher . Endo $ fmap fst . sortCompletions . matcher
67
88
68−runMatcher :: Matcher -> [Completion] -> [Completion]
89+runMatcher :: Matcher -> [Match] -> [Match]
69runMatcher (Matcher m)= appEndo m
90runMatcher (Matcher m)= appEndo m
70
91
71−sortCompletions :: [ScoredCompletion] -> [ScoredCompletion]
92+sortCompletions :: [ScoredMatch] -> [ScoredMatch]
72sortCompletions = sortBy (flip compare `on` snd)
93sortCompletions = sortBy (flip compare `on` snd)
73
94
74−flexMatch :: Text -> [Completion] -> [ScoredCompletion]
95+flexMatch :: Text -> [Match] -> [ScoredMatch]
75−flexMatch pattern = mapMaybe (flexRate pattern)
96+flexMatch = mapMaybe . flexRate
76
97
77−flexRate :: Text -> Completion -> Maybe ScoredCompletion
98+flexRate :: Text -> Match -> Maybe ScoredMatch
78−flexRate pattern c@(Completion (_,ident,_)) = do
99+flexRate p c = do
79− score <- flexScore pattern ident
100+ score <- flexScore p (identifierFromMatch c)
80 return (c, score)
101 return (c, score)
81
102
82-- FlexMatching ala Sublime.
103-- FlexMatching ala Sublime.
@@ -89,13 +110,13 @@ flexScore :: Text -> DeclIdent -> Maybe Double
89flexScore pat str =
110flexScore pat str =
90 case T.uncons pat of
111 case T.uncons pat of
91 Nothing -> Nothing
112 Nothing -> Nothing
92− Just (first, pattern) ->
113+ Just (first, p) ->
93 case TE.encodeUtf8 str =~ TE.encodeUtf8 pat' :: (Int, Int) of
114 case TE.encodeUtf8 str =~ TE.encodeUtf8 pat' :: (Int, Int) of
94 (-1,0) -> Nothing
115 (-1,0) -> Nothing
95 (start,len) -> Just $ calcScore start (start + len)
116 (start,len) -> Just $ calcScore start (start + len)
96 where
117 where
97 escapedPattern :: [Text]
118 escapedPattern :: [Text]
98− escapedPattern = map escape (T.unpack pattern)
119+ escapedPattern = map escape (T.unpack p)
99
120
100 -- escape prepends a backslash to "regexy" characters to prevent the
121 -- escape prepends a backslash to "regexy" characters to prevent the
101 -- matcher from crashing when trying to build the regex
122 -- matcher from crashing when trying to build the regex
src/Language/PureScript/Ide/Pursuit.hs+14
@@ -1,3 +1,17 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.Pursuit
4+-- Description : Pursuit client for psc-ide
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- Pursuit client for psc-ide
13+-----------------------------------------------------------------------------
14+
1{-# LANGUAGE OverloadedStrings #-}
15{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE ScopedTypeVariables #-}
16{-# LANGUAGE ScopedTypeVariables #-}
3
17
src/Language/PureScript/Ide/Reexports.hs+16
@@ -1,6 +1,22 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.Reexports
4+-- Description : Resolves reexports for psc-ide
5+-- Copyright : Christoph Hegemann 2016
6+-- Brian Sermons 2016
7+-- License : MIT (http://opensource.org/licenses/MIT)
8+--
9+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
10+-- Stability : experimental
11+--
12+-- |
13+-- Resolves reexports for psc-ide
14+-----------------------------------------------------------------------------
15+
1{-# LANGUAGE OverloadedStrings #-}
16{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE PatternGuards #-}
17{-# LANGUAGE PatternGuards #-}
3{-# LANGUAGE TupleSections #-}
18{-# LANGUAGE TupleSections #-}
19+
4module Language.PureScript.Ide.Reexports where
20module Language.PureScript.Ide.Reexports where
5
21
6
22
src/Language/PureScript/Ide/SourceFile.hs+16−1
@@ -1,6 +1,21 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.SourceFile
4+-- Description : Getting declarations from PureScript sourcefiles
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- Getting declarations from PureScript sourcefiles
13+-----------------------------------------------------------------------------
14+
1{-# LANGUAGE FlexibleContexts #-}
15{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE MultiParamTypeClasses #-}
16{-# LANGUAGE MultiParamTypeClasses #-}
3{-# LANGUAGE OverloadedStrings #-}
17{-# LANGUAGE OverloadedStrings #-}
18+
4module Language.PureScript.Ide.SourceFile where
19module Language.PureScript.Ide.SourceFile where
5
20
6import Prelude
21import Prelude
@@ -69,7 +84,7 @@ getPositionedImports :: D.Module -> [D.Declaration]
69getPositionedImports (D.Module _ _ _ declarations _) =
84getPositionedImports (D.Module _ _ _ declarations _) =
70 mapMaybe isImport declarations
85 mapMaybe isImport declarations
71 where
86 where
72− isImport i@(D.PositionedDeclaration _ _ (D.ImportDeclaration{})) = Just i
87+ isImport i@(D.PositionedDeclaration _ _ D.ImportDeclaration{}) = Just i
73 isImport _ = Nothing
88 isImport _ = Nothing
74
89
75getDeclPosition :: D.Module -> String -> Maybe SP.SourceSpan
90getDeclPosition :: D.Module -> String -> Maybe SP.SourceSpan
src/Language/PureScript/Ide/State.hs+21−6
@@ -1,3 +1,17 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.State
4+-- Description : Functions to access psc-ide's state
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- Functions to access psc-ide's state
13+-----------------------------------------------------------------------------
14+
1{-# LANGUAGE ConstraintKinds #-}
15{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE FlexibleContexts #-}
16{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
17{-# LANGUAGE MultiParamTypeClasses #-}
@@ -68,12 +82,13 @@ insertModule ::(PscIde m, MonadLogger m) =>
68insertModule externsFile = do
82insertModule externsFile = do
69 env <- ask
83 env <- ask
70 let moduleName = efModuleName externsFile
84 let moduleName = efModuleName externsFile
71− $(logDebug) $ "Inserting Module: " <> (T.pack (runModuleName moduleName))
85+ $(logDebug) $ "Inserting Module: " <> T.pack (runModuleName moduleName)
72 liftIO . atomically $ insertModule' (envStateVar env) externsFile
86 liftIO . atomically $ insertModule' (envStateVar env) externsFile
73
87
74insertModule' :: TVar PscIdeState -> ExternsFile -> STM ()
88insertModule' :: TVar PscIdeState -> ExternsFile -> STM ()
75−insertModule' st ef = modifyTVar st $ \x ->
89+insertModule' st ef =
90+ modifyTVar st $ \x ->
76− x { externsFiles = M.insert (efModuleName ef) ef (externsFiles x)
91+ x { externsFiles = M.insert (efModuleName ef) ef (externsFiles x)
77− , pscStateModules = let (mn, decls) = convertExterns ef
92+ , pscStateModules = let (mn, decls) = convertExterns ef
78− in M.insert mn decls (pscStateModules x)
93+ in M.insert mn decls (pscStateModules x)
79− }
94+ }
src/Language/PureScript/Ide/Types.hs+46−48
@@ -1,3 +1,17 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.Types
4+-- Description : Type definitions for psc-ide
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- Type definitions for psc-ide
13+-----------------------------------------------------------------------------
14+
1{-# LANGUAGE ConstraintKinds #-}
15{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE FlexibleContexts #-}
16{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
17{-# LANGUAGE GeneralizedNewtypeDeriving #-}
@@ -19,51 +33,40 @@ import Data.Maybe (maybeToList)
19import Data.Text (Text (), pack, unpack)
33import Data.Text (Text (), pack, unpack)
20import qualified Language.PureScript.AST.Declarations as D
34import qualified Language.PureScript.AST.Declarations as D
21import Language.PureScript.Externs
35import Language.PureScript.Externs
22−import Language.PureScript.Names
23import qualified Language.PureScript.Names as N
36import qualified Language.PureScript.Names as N
37+import qualified Language.PureScript as P
24
38
25import Text.Parsec
39import Text.Parsec
26import Text.Parsec.Text
40import Text.Parsec.Text
27
41
42+type Ident = Text
43+type DeclIdent = Text
28type ModuleIdent = Text
44type ModuleIdent = Text
29−type DeclIdent = Text
30−type Type = Text
31−
32−data Fixity = Infix | Infixl | Infixr deriving(Show, Eq, Ord)
33
45
34data ExternDecl
46data ExternDecl
35 -- | A function/value declaration
47 -- | A function/value declaration
36− = FunctionDecl
48+ = ValueDeclaration Ident P.Type
37− DeclIdent -- The functions name
49+ | TypeDeclaration (P.ProperName 'P.TypeName) P.Kind
38− Type -- The functions type
50+ | TypeSynonymDeclaration (P.ProperName 'P.TypeName) P.Type
39− | FixityDeclaration Fixity Int DeclIdent
40 -- | A Dependency onto another Module
51 -- | A Dependency onto another Module
41 | Dependency
52 | Dependency
42 ModuleIdent -- name of the dependency
53 ModuleIdent -- name of the dependency
43 [Text] -- explicit imports
54 [Text] -- explicit imports
44 (Maybe Text) -- An eventual qualifier
55 (Maybe Text) -- An eventual qualifier
45−
46 -- | A module declaration
56 -- | A module declaration
47 | ModuleDecl
57 | ModuleDecl
48 ModuleIdent -- The modules name
58 ModuleIdent -- The modules name
49 [DeclIdent] -- The exported identifiers
59 [DeclIdent] -- The exported identifiers
50 -- | A data/newtype declaration
60 -- | A data/newtype declaration
61+ | DataConstructor
51− | DataDecl DeclIdent -- The type name
62+ DeclIdent -- ^ The type name
63+ (P.ProperName 'P.TypeName)
52− Text -- The "type"
64+ P.Type -- ^ The "type"
53 -- | An exported module
65 -- | An exported module
66+ | TypeClassDeclaration (P.ProperName 'P.ClassName)
54 | Export ModuleIdent -- The exported Modules name
67 | Export ModuleIdent -- The exported Modules name
55 deriving (Show,Eq,Ord)
68 deriving (Show,Eq,Ord)
56
69
57−instance ToJSON ExternDecl where
58− toJSON (FunctionDecl n t) = object ["name" .= n, "type" .= t]
59− toJSON (ModuleDecl n t) = object ["name" .= n, "type" .= t]
60− toJSON (DataDecl n t) = object ["name" .= n, "type" .= t]
61− toJSON (Dependency n names _) = object ["module" .= n, "names" .= names]
62− toJSON (FixityDeclaration f p n) = object ["name" .= n
63− , "fixity" .= show f
64− , "precedence" .= p]
65− toJSON (Export _) = object []
66−
67type Module = (ModuleIdent, [ExternDecl])
70type Module = (ModuleIdent, [ExternDecl])
68
71
69data Configuration =
72data Configuration =
@@ -83,15 +86,22 @@ type PscIde m = (MonadIO m, MonadReader PscIdeEnvironment m)
83data PscIdeState =
86data PscIdeState =
84 PscIdeState
87 PscIdeState
85 { pscStateModules :: M.Map Text [ExternDecl]
88 { pscStateModules :: M.Map Text [ExternDecl]
86− , externsFiles :: M.Map ModuleName ExternsFile
89+ , externsFiles :: M.Map P.ModuleName ExternsFile
87 } deriving Show
90 } deriving Show
88
91
89emptyPscIdeState :: PscIdeState
92emptyPscIdeState :: PscIdeState
90emptyPscIdeState = PscIdeState M.empty M.empty
93emptyPscIdeState = PscIdeState M.empty M.empty
91
94
95+data Match = Match ModuleIdent ExternDecl
96+ deriving (Show, Eq)
97+
92newtype Completion =
98newtype Completion =
93− Completion (ModuleIdent, DeclIdent, Type)
99+ Completion (ModuleIdent, DeclIdent, Text)
94− deriving (Show,Eq)
100+ deriving (Show,Eq)
101+
102+instance ToJSON Completion where
103+ toJSON (Completion (m,d,t)) =
104+ object ["module" .= m, "identifier" .= d, "type" .= t]
95
105
96data ModuleImport =
106data ModuleImport =
97 ModuleImport
107 ModuleImport
@@ -127,18 +137,6 @@ identifierFromDeclarationRef (D.ValueRef ident) = N.runIdent ident
127identifierFromDeclarationRef (D.TypeClassRef name) = N.runProperName name
137identifierFromDeclarationRef (D.TypeClassRef name) = N.runProperName name
128identifierFromDeclarationRef _ = ""
138identifierFromDeclarationRef _ = ""
129
139
130−instance FromJSON Completion where
131− parseJSON (Object o) = do
132− m <- o .: "module"
133− d <- o .: "identifier"
134− t <- o .: "type"
135− pure (Completion (m, d, t))
136− parseJSON _ = mzero
137−
138−instance ToJSON Completion where
139− toJSON (Completion (m,d,t)) =
140− object ["module" .= m, "identifier" .= d, "type" .= t]
141−
142data Success =
140data Success =
143 CompletionResult [Completion]
141 CompletionResult [Completion]
144 | TextResult Text
142 | TextResult Text
@@ -174,7 +172,7 @@ instance FromJSON PursuitSearchType where
174 parseJSON _ = mzero
172 parseJSON _ = mzero
175
173
176instance FromJSON PursuitQuery where
174instance FromJSON PursuitQuery where
177− parseJSON o = PursuitQuery <$> (parseJSON o)
175+ parseJSON o = PursuitQuery <$> parseJSON o
178
176
179data PursuitResponse =
177data PursuitResponse =
180 -- | A Pursuit Response for a module. Consists of the modules name and the
178 -- | A Pursuit Response for a module. Consists of the modules name and the
@@ -182,7 +180,7 @@ data PursuitResponse =
182 ModuleResponse ModuleIdent Text
180 ModuleResponse ModuleIdent Text
183 -- | A Pursuit Response for a declaration. Consist of the declarations type,
181 -- | A Pursuit Response for a declaration. Consist of the declarations type,
184 -- module, name and package
182 -- module, name and package
185− | DeclarationResponse Type ModuleIdent DeclIdent Text
183+ | DeclarationResponse Text ModuleIdent DeclIdent Text
186 deriving (Show,Eq)
184 deriving (Show,Eq)
187
185
188instance FromJSON PursuitResponse where
186instance FromJSON PursuitResponse where
@@ -215,15 +213,15 @@ typeParse t = case parse parseType "" t of
215 type' <- many1 anyChar
213 type' <- many1 anyChar
216 pure (unpack name, type')
214 pure (unpack name, type')
217
215
218−identifier :: Parser Text
216+ identifier :: Parser Text
219−identifier = do
217+ identifier = do
220− spaces
218+ spaces
221− ident <-
219+ ident <-
222− -- necessary for being able to parse the following ((++), concat)
220+ -- necessary for being able to parse the following ((++), concat)
223− between (char '(') (char ')') (many1 (noneOf ", )")) <|>
221+ between (char '(') (char ')') (many1 (noneOf ", )")) <|>
224− many1 (noneOf ", )")
222+ many1 (noneOf ", )")
225− spaces
223+ spaces
226− pure (pack ident)
224+ pure (pack ident)
227
225
228instance ToJSON PursuitResponse where
226instance ToJSON PursuitResponse where
229 toJSON (ModuleResponse name package) =
227 toJSON (ModuleResponse name package) =
src/Language/PureScript/Ide/Util.hsnew file+65
@@ -0,0 +1,65 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.Util
4+-- Description : Generally useful functions and conversions
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- Generally useful functions and conversions
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE OverloadedStrings #-}
16+
17+module Language.PureScript.Ide.Util where
18+
19+import Data.Aeson
20+import Data.Text (Text)
21+import qualified Data.Text as T
22+import Data.Text.Lazy (fromStrict, toStrict)
23+import Data.Text.Lazy.Encoding (decodeUtf8, encodeUtf8)
24+import qualified Language.PureScript as P
25+import Language.PureScript.Ide.Types
26+
27+runProperNameT :: P.ProperName a -> Text
28+runProperNameT = T.pack . P.runProperName
29+
30+runIdentT :: P.Ident -> Text
31+runIdentT = T.pack . P.runIdent
32+
33+prettyTypeT :: P.Type -> Text
34+prettyTypeT = T.unwords . fmap T.strip . T.lines . T.pack . P.prettyPrintType
35+
36+identifierFromExternDecl :: ExternDecl -> Text
37+identifierFromExternDecl (ValueDeclaration name _) = name
38+identifierFromExternDecl (TypeDeclaration name _) = runProperNameT name
39+identifierFromExternDecl (TypeSynonymDeclaration name _) = runProperNameT name
40+identifierFromExternDecl (DataConstructor name _ _) = name
41+identifierFromExternDecl (TypeClassDeclaration name) = runProperNameT name
42+identifierFromExternDecl (ModuleDecl name _) = name
43+identifierFromExternDecl Dependency{} = "~Dependency~"
44+identifierFromExternDecl Export{} = "~Export~"
45+
46+identifierFromMatch :: Match -> Text
47+identifierFromMatch (Match _ ed) = identifierFromExternDecl ed
48+
49+completionFromMatch :: Match -> Maybe Completion
50+completionFromMatch (Match _ Dependency{}) = Nothing
51+completionFromMatch (Match _ Export{}) = Nothing
52+completionFromMatch (Match m d) = Just $ case d of
53+ ValueDeclaration name type' -> Completion (m, name, prettyTypeT type')
54+ TypeDeclaration name kind -> Completion (m, runProperNameT name, T.pack $ P.prettyPrintKind kind)
55+ TypeSynonymDeclaration name kind -> Completion (m, runProperNameT name, prettyTypeT kind)
56+ DataConstructor name _ type' -> Completion (m, name, prettyTypeT type')
57+ TypeClassDeclaration name -> Completion (m, runProperNameT name, "class")
58+ ModuleDecl name _ -> Completion ("module", name, "module")
59+ _ -> error "the impossible happened in completionFromMatch"
60+
61+encodeT :: (ToJSON a) => a -> Text
62+encodeT = toStrict . decodeUtf8 . encode
63+
64+decodeT :: (FromJSON a) => Text -> Maybe a
65+decodeT = decode . encodeUtf8 . fromStrict
src/Language/PureScript/Ide/Watcher.hs+15
@@ -1,4 +1,19 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.Watcher
4+-- Description : File watcher for externs files
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- File watcher for externs files
13+-----------------------------------------------------------------------------
14+
1{-# LANGUAGE RecordWildCards #-}
15{-# LANGUAGE RecordWildCards #-}
16+
2module Language.PureScript.Ide.Watcher where
17module Language.PureScript.Ide.Watcher where
3
18
4import Prelude ()
19import Prelude ()
tests/Language/PureScript/Ide/FilterSpec.hs+7−3
@@ -4,15 +4,19 @@ module Language.PureScript.Ide.FilterSpec where
4import Data.Text (Text)
4import Data.Text (Text)
5import Language.PureScript.Ide.Filter
5import Language.PureScript.Ide.Filter
6import Language.PureScript.Ide.Types
6import Language.PureScript.Ide.Types
7+import qualified Language.PureScript as P
7import Test.Hspec
8import Test.Hspec
8
9
10+value :: Text -> ExternDecl
11+value s = ValueDeclaration s P.TypeWildcard
12+
9modules :: [Module]
13modules :: [Module]
10modules =
14modules =
11 [
15 [
12− ("Module.A", [FunctionDecl "function1" ""]),
16+ ("Module.A", [value "function1"]),
13− ("Module.B", [DataDecl "data1" ""]),
17+ ("Module.B", [value "data1"]),
14 ("Module.C", [ModuleDecl "Module.C" []]),
18 ("Module.C", [ModuleDecl "Module.C" []]),
15− ("Module.D", [Dependency "Module.C" [] Nothing, FunctionDecl "asd" ""])
19+ ("Module.D", [Dependency "Module.C" [] Nothing, value "asd"])
16 ]
20 ]
17
21
18runEq :: Text -> [Module]
22runEq :: Text -> [Module]
tests/Language/PureScript/Ide/Imports/IntegrationSpec.hsnew file+101
@@ -0,0 +1,101 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+module Language.PureScript.Ide.Imports.IntegrationSpec where
3+
4+import Control.Monad
5+import Data.Text (Text)
6+import qualified Data.Text as T
7+import qualified Data.Text.IO as TIO
8+import qualified Language.PureScript.Ide.Integration as Integration
9+import Test.Hspec
10+
11+import System.Directory
12+import System.FilePath
13+
14+setup :: IO ()
15+setup = do
16+ Integration.deleteOutputFolder
17+ s <- Integration.compileTestProject
18+ unless s $ fail "Failed to compile .purs sources"
19+ Integration.quitServer -- kill a eventually running psc-ide-server instance
20+ _ <- Integration.startServer
21+ mapM_ Integration.loadModuleWithDeps ["ImportsSpec", "ImportsSpec1"]
22+
23+teardown :: IO ()
24+teardown = Integration.quitServer
25+
26+withSupportFiles :: (FilePath -> FilePath -> IO a) -> IO ()
27+withSupportFiles test = do
28+ pdir <- Integration.projectDirectory
29+ let sourceFp = pdir </> "src" </> "ImportsSpec.purs"
30+ outFp = pdir </> "src" </> "ImportsSpecOut.tmp"
31+ Integration.deleteFileIfExists outFp
32+ void $ test sourceFp outFp
33+
34+outputFileShouldBe :: [Text] -> IO ()
35+outputFileShouldBe expectation = do
36+ outFp <- (</> "src" </> "ImportsSpecOut.tmp") <$> Integration.projectDirectory
37+ outRes <- TIO.readFile outFp
38+ shouldBe (T.lines outRes) expectation
39+
40+spec :: Spec
41+spec = beforeAll_ setup $ afterAll_ teardown $ describe "Adding imports" $ do
42+ let
43+ sourceFileSkeleton :: [Text] -> [Text]
44+ sourceFileSkeleton importSection =
45+ [ "module ImportsSpec where" , ""] ++ importSection ++ [ "" , "myId = id"]
46+ it "adds an implicit import" $ do
47+ withSupportFiles (Integration.addImplicitImport "Prelude")
48+ outputFileShouldBe (sourceFileSkeleton
49+ [ "import Prelude"
50+ , "import Main (id)"
51+ ])
52+ it "adds an explicit unqualified import" $ do
53+ withSupportFiles (Integration.addImport "exportedFunction")
54+ outputFileShouldBe (sourceFileSkeleton
55+ [ "import ImportsSpec1 (exportedFunction)"
56+ , "import Main (id)"
57+ ])
58+ it "adds an explicit unqualified import (type)" $ do
59+ withSupportFiles (Integration.addImport "MyType")
60+ outputFileShouldBe (sourceFileSkeleton [ "import ImportsSpec1 (MyType)"
61+ , "import Main (id)"
62+ ])
63+ it "adds an explicit unqualified import (parameterized type)" $ do
64+ withSupportFiles (Integration.addImport "MyParamType")
65+ outputFileShouldBe (sourceFileSkeleton [ "import ImportsSpec1 (MyParamType)"
66+ , "import Main (id)"
67+ ])
68+ it "adds an explicit unqualified import (typeclass)" $ do
69+ withSupportFiles (Integration.addImport "ATypeClass")
70+ outputFileShouldBe (sourceFileSkeleton [ "import ImportsSpec1 (class ATypeClass)"
71+ , "import Main (id)"])
72+ it "adds an explicit unqualified import (dataconstructor)" $ do
73+ withSupportFiles (Integration.addImport "MyJust")
74+ outputFileShouldBe (sourceFileSkeleton [ "import ImportsSpec1 (MyMaybe(MyJust))"
75+ , "import Main (id)"])
76+ it "adds an explicit unqualified import (newtype)" $ do
77+ withSupportFiles (Integration.addImport "MyNewtype")
78+ outputFileShouldBe (sourceFileSkeleton [ "import ImportsSpec1 (MyNewtype(MyNewtype))"
79+ , "import Main (id)"])
80+ it "adds an explicit unqualified import (typeclass member function)" $ do
81+ withSupportFiles (Integration.addImport "typeClassFun")
82+ outputFileShouldBe (sourceFileSkeleton [ "import ImportsSpec1 (typeClassFun)"
83+ , "import Main (id)"])
84+ it "doesn't add a newtypes constructor if only the type is exported" $ do
85+ withSupportFiles (Integration.addImport "OnlyTypeExported")
86+ outputFileShouldBe (sourceFileSkeleton [ "import ImportsSpec1 (OnlyTypeExported)"
87+ , "import Main (id)"])
88+ it "doesn't add an import if the identifier is defined in the module itself" $ do
89+ withSupportFiles (Integration.addImport "myId")
90+ outputFileShouldBe (sourceFileSkeleton [ "import Main (id)"])
91+ it "responds with an error if it's undecidable whether we want a type or constructor" $
92+ withSupportFiles (\sourceFp outFp -> do
93+ r <- Integration.addImport "SpecialCase" sourceFp outFp
94+ shouldBe False (Integration.resultIsSuccess r)
95+ shouldBe False =<< doesFileExist outFp)
96+ it "responds with an error if the identifier cannot be found and doesn't \
97+ \write to the output file" $
98+ withSupportFiles (\sourceFp outFp -> do
99+ r <- Integration.addImport "doesntExist" sourceFp outFp
100+ shouldBe False (Integration.resultIsSuccess r)
101+ shouldBe False =<< doesFileExist outFp)
tests/Language/PureScript/Ide/ImportsSpec.hsnew file+125
@@ -0,0 +1,125 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+module Language.PureScript.Ide.ImportsSpec where
3+
4+import Data.Maybe (fromJust)
5+import Data.Text (Text)
6+import qualified Language.PureScript as P
7+import Language.PureScript.Ide.Imports
8+import Language.PureScript.Ide.Types
9+import Test.Hspec
10+
11+simpleFile :: [Text]
12+simpleFile =
13+ [ "module Main where"
14+ , "import Prelude"
15+ , ""
16+ , "myFunc x y = x + y"
17+ ]
18+
19+splitSimpleFile :: (P.ModuleName, [Text], [Import], [Text])
20+splitSimpleFile = fromRight $ sliceImportSection simpleFile
21+ where
22+ fromRight (Right r) = r
23+ fromRight (Left _) = error "fromRight"
24+
25+withImports :: [Text] -> [Text]
26+withImports is =
27+ take 2 simpleFile ++ is ++ drop 2 simpleFile
28+
29+testParseImport :: Text -> Import
30+testParseImport = fromJust . parseImport
31+
32+preludeImport, arrayImport, listImport, consoleImport, maybeImport :: Import
33+preludeImport = testParseImport "import Prelude"
34+arrayImport = testParseImport "import Data.Array (head, cons)"
35+listImport = testParseImport "import Data.List as List"
36+consoleImport = testParseImport "import Control.Monad.Eff.Console (log) as Console"
37+maybeImport = testParseImport "import Data.Maybe (Maybe(Just))"
38+
39+spec :: Spec
40+spec = do
41+ describe "determining the importsection" $ do
42+ let moduleSkeleton imports =
43+ Right (P.moduleNameFromString "Main", take 1 simpleFile, imports, drop 2 simpleFile)
44+ it "finds a simple import" $
45+ shouldBe (sliceImportSection simpleFile) (moduleSkeleton [preludeImport])
46+
47+ it "allows multiline import statements" $
48+ shouldBe
49+ (sliceImportSection (withImports [ "import Data.Array (head,"
50+ , " cons)"
51+ ]))
52+ (moduleSkeleton [preludeImport, arrayImport])
53+ describe "pretty printing imports" $ do
54+ it "pretty prints a simple import" $
55+ shouldBe (prettyPrintImport' preludeImport) "import Prelude"
56+ it "pretty prints an explicit import" $
57+ shouldBe (prettyPrintImport' arrayImport) "import Data.Array (head, cons)"
58+ it "pretty prints a qualified import" $
59+ shouldBe (prettyPrintImport' listImport) "import Data.List as List"
60+ it "pretty prints a qualified explicit import" $
61+ shouldBe (prettyPrintImport' consoleImport) "import Control.Monad.Eff.Console (log) as Console"
62+ it "pretty prints an import with a datatype (and PositionedRef's for the dtors)" $
63+ shouldBe (prettyPrintImport' maybeImport) "import Data.Maybe (Maybe(Just))"
64+
65+ describe "import commands" $ do
66+ let simpleFileImports = let (_, _, i, _) = splitSimpleFile in i
67+ addValueImport i mn is =
68+ prettyPrintImportSection (addExplicitImport' (ValueDeclaration i P.TypeWildcard) mn is)
69+ addDtorImport i t mn is =
70+ prettyPrintImportSection (addExplicitImport' (DataConstructor i t P.TypeWildcard) mn is)
71+ it "adds an implicit unqualified import" $
72+ shouldBe
73+ (addImplicitImport' simpleFileImports (P.moduleNameFromString "Data.Map"))
74+ [ "import Prelude"
75+ , "import Data.Map"
76+ ]
77+ it "adds an explicit unqualified import" $
78+ shouldBe
79+ (addValueImport "head" (P.moduleNameFromString "Data.Array") simpleFileImports)
80+ [ "import Prelude"
81+ , "import Data.Array (head)"
82+ ]
83+ it "doesn't add an import if the containing module is imported implicitly" $
84+ shouldBe
85+ (addValueImport "const" (P.moduleNameFromString "Prelude") simpleFileImports)
86+ ["import Prelude"]
87+ let Right (_, _, explicitImports, _) = sliceImportSection (withImports ["import Data.Array (tail)"])
88+ it "adds an identifier to an explicit import list" $
89+ shouldBe
90+ (addValueImport "head" (P.moduleNameFromString "Data.Array") explicitImports)
91+ [ "import Prelude"
92+ , "import Data.Array (head, tail)"
93+ ]
94+ it "adds an operator to an explicit import list" $
95+ shouldBe
96+ (addValueImport "<~>" (P.moduleNameFromString "Data.Array") explicitImports)
97+ [ "import Prelude"
98+ , "import Data.Array ((<~>), tail)"
99+ ]
100+ it "adds the type for a given DataConstructor" $
101+ shouldBe
102+ (addDtorImport "Just" (P.ProperName "Maybe") (P.moduleNameFromString "Data.Maybe") simpleFileImports)
103+ [ "import Prelude"
104+ , "import Data.Maybe (Maybe(Just))"
105+ ]
106+ it "adds a dataconstructor to an existing type import" $ do
107+ let Right (_, _, typeImports, _) = sliceImportSection (withImports ["import Data.Maybe (Maybe)"])
108+ shouldBe
109+ (addDtorImport "Just" (P.ProperName "Maybe") (P.moduleNameFromString "Data.Maybe") typeImports)
110+ [ "import Prelude"
111+ , "import Data.Maybe (Maybe(Just))"
112+ ]
113+ it "doesn't add a dataconstructor to an existing type import with open dtors" $ do
114+ let Right (_, _, typeImports, _) = sliceImportSection (withImports ["import Data.Maybe (Maybe(..))"])
115+ shouldBe
116+ (addDtorImport "Just" (P.ProperName "Maybe") (P.moduleNameFromString "Data.Maybe") typeImports)
117+ [ "import Prelude"
118+ , "import Data.Maybe (Maybe(..))"
119+ ]
120+ it "doesn't add an identifier to an explicit import list if it's already imported" $
121+ shouldBe
122+ (addValueImport "tail" (P.moduleNameFromString "Data.Array") explicitImports)
123+ [ "import Prelude"
124+ , "import Data.Array (tail)"
125+ ]
tests/Language/PureScript/Ide/Integration.hsnew file+238
@@ -0,0 +1,238 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.Ide.Integration
4+-- Description : A psc-ide client for use in integration tests
5+-- Copyright : Christoph Hegemann 2016
6+-- License : MIT (http://opensource.org/licenses/MIT)
7+--
8+-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9+-- Stability : experimental
10+--
11+-- |
12+-- A psc-ide client for use in integration tests
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE OverloadedStrings #-}
16+{-# LANGUAGE ScopedTypeVariables #-}
17+module Language.PureScript.Ide.Integration
18+ (
19+ -- managing the server process
20+ startServer
21+ , withServer
22+ , stopServer
23+ , quitServer
24+ -- util
25+ , compileTestProject
26+ , deleteOutputFolder
27+ , projectDirectory
28+ , deleteFileIfExists
29+ -- sending commands
30+ , loadModuleWithDeps
31+ , getFlexCompletions
32+ , getType
33+ , addImport
34+ , addImplicitImport
35+ -- checking results
36+ , resultIsSuccess
37+ , parseCompletions
38+ , parseTextResult
39+ ) where
40+
41+import Control.Concurrent (threadDelay)
42+import Control.Exception
43+import Control.Monad (join, when)
44+import Data.Aeson
45+import Data.Aeson.Types
46+import qualified Data.ByteString.Lazy.UTF8 as BSL
47+import Data.Either (isRight)
48+import Data.Maybe (fromJust)
49+import qualified Data.Text as T
50+import qualified Data.Vector as V
51+import Language.PureScript.Ide.Util
52+import System.Directory
53+import System.Exit
54+import System.FilePath
55+import System.Process
56+
57+projectDirectory :: IO FilePath
58+projectDirectory = do
59+ cd <- getCurrentDirectory
60+ return $ cd </> "tests" </> "support" </> "pscide"
61+
62+startServer :: IO ProcessHandle
63+startServer = do
64+ pdir <- projectDirectory
65+ (_, _, _, procHandle) <- createProcess $ (shell "psc-ide-server") {cwd=Just pdir}
66+ threadDelay 500000 -- give the server 500ms to start up
67+ return procHandle
68+
69+stopServer :: ProcessHandle -> IO ()
70+stopServer = terminateProcess
71+
72+withServer :: IO a -> IO a
73+withServer s = do
74+ _ <- startServer
75+ r <- s
76+ quitServer
77+ return r
78+
79+-- project management utils
80+
81+compileTestProject :: IO Bool
82+compileTestProject = do
83+ pdir <- projectDirectory
84+ (_, _, _, procHandle) <- createProcess $
85+ (shell $ "psc " ++ fileGlob) {cwd=Just pdir
86+ ,std_out=CreatePipe
87+ ,std_err=CreatePipe
88+ }
89+ isSuccess <$> waitForProcess procHandle
90+
91+deleteOutputFolder :: IO ()
92+deleteOutputFolder = do
93+ odir <- fmap (</> "output") projectDirectory
94+ whenM (doesDirectoryExist odir) (removeDirectoryRecursive odir)
95+
96+deleteFileIfExists :: FilePath -> IO ()
97+deleteFileIfExists fp = whenM (doesFileExist fp) (removeFile fp)
98+
99+whenM :: Monad m => m Bool -> m () -> m ()
100+whenM p f = do
101+ x <- p
102+ when x f
103+
104+isSuccess :: ExitCode -> Bool
105+isSuccess ExitSuccess = True
106+isSuccess (ExitFailure _) = False
107+
108+fileGlob :: String
109+fileGlob = unwords
110+ [ "\"src/**/*.purs\""
111+ , "\"src/**/*.js\""
112+ , "\"bower_components/purescript-*/**/*.purs\""
113+ , "\"bower_components/purescript-*/**/*.js\""
114+ ]
115+
116+-- Integration Testing API
117+
118+sendCommand :: Value -> IO String
119+sendCommand v = readCreateProcess
120+ ((shell "psc-ide-client") { std_out=CreatePipe
121+ , std_err=CreatePipe
122+ })
123+ (T.unpack (encodeT v))
124+
125+quitServer :: IO ()
126+quitServer = do
127+ let quitCommand = object ["command" .= ("quit" :: String)]
128+ _ <- try $ sendCommand quitCommand :: IO (Either SomeException String)
129+ return ()
130+
131+loadModuleWithDeps :: String -> IO String
132+loadModuleWithDeps m = sendCommand $ load [] [m]
133+
134+getFlexCompletions :: String -> IO [(String, String, String)]
135+getFlexCompletions q = parseCompletions <$> sendCommand (completion [] (Just (flexMatcher q)))
136+
137+getType :: String -> IO [(String, String, String)]
138+getType q = parseCompletions <$> sendCommand (typeC q [])
139+
140+addImport :: String -> FilePath -> FilePath -> IO String
141+addImport identifier fp outfp = sendCommand (addImportC identifier fp outfp)
142+
143+addImplicitImport :: String -> FilePath -> FilePath -> IO String
144+addImplicitImport mn fp outfp = sendCommand (addImplicitImportC mn fp outfp)
145+
146+-- Command Encoding
147+
148+commandWrapper :: String -> Value -> Value
149+commandWrapper c p = object ["command" .= c, "params" .= p]
150+
151+load :: [String] -> [String] -> Value
152+load ms ds = commandWrapper "load" (object ["modules" .= ms, "dependencies" .= ds])
153+
154+typeC :: String -> [Value] -> Value
155+typeC q filters = commandWrapper "type" (object ["search" .= q, "filters" .= filters])
156+
157+addImportC :: String -> FilePath -> FilePath -> Value
158+addImportC identifier = addImportW $
159+ object [ "importCommand" .= ("addImport" :: String)
160+ , "identifier" .= identifier
161+ ]
162+
163+addImplicitImportC :: String -> FilePath -> FilePath -> Value
164+addImplicitImportC mn = addImportW $
165+ object [ "importCommand" .= ("addImplicitImport" :: String)
166+ , "module" .= mn
167+ ]
168+
169+addImportW :: Value -> FilePath -> FilePath -> Value
170+addImportW importCommand fp outfp =
171+ commandWrapper "import" (object [ "file" .= fp
172+ , "outfile" .= outfp
173+ , "importCommand" .= importCommand
174+ ])
175+
176+
177+completion :: [Value] -> Maybe Value -> Value
178+completion filters matcher =
179+ let
180+ matcher' = case matcher of
181+ Nothing -> []
182+ Just m -> ["matcher" .= m]
183+ in
184+ commandWrapper "complete" (object $ "filters" .= filters : matcher')
185+
186+flexMatcher :: String -> Value
187+flexMatcher q = object [ "matcher" .= ("flex" :: String)
188+ , "params" .= object ["search" .= q]
189+ ]
190+
191+-- Result parsing
192+
193+unwrapResult :: Value -> Parser (Either String Value)
194+unwrapResult = withObject "result" $ \o -> do
195+ (rt :: String) <- o .: "resultType"
196+ case rt of
197+ "error" -> do
198+ res <- o .: "result"
199+ pure (Left res)
200+ "success" -> do
201+ res <- o .: "result"
202+ pure (Right res)
203+ _ -> fail "lol"
204+
205+withResult :: (Value -> Parser a) -> Value -> Parser (Either String a)
206+withResult p v = do
207+ r <- unwrapResult v
208+ case r of
209+ Left err -> pure (Left err)
210+ Right res -> Right <$> p res
211+
212+completionParser :: Value -> Parser [(String, String, String)]
213+completionParser = withArray "res" $ \cs ->
214+ mapM (withObject "completion" $ \o -> do
215+ ident <- o .: "identifier"
216+ module' <- o .: "module"
217+ ty <- o .: "type"
218+ pure (module', ident, ty)) (V.toList cs)
219+
220+valueFromString :: String -> Value
221+valueFromString = fromJust . decode . BSL.fromString
222+
223+resultIsSuccess :: String -> Bool
224+resultIsSuccess = isRight . join . parseEither unwrapResult . valueFromString
225+
226+parseCompletions :: String -> [(String, String, String)]
227+parseCompletions s = fromJust $ do
228+ cs <- parseMaybe (withResult completionParser) (valueFromString s)
229+ case cs of
230+ Left _ -> error "Failed to parse completions"
231+ Right cs' -> pure cs'
232+
233+parseTextResult :: String -> String
234+parseTextResult s = fromJust $ do
235+ r <- parseMaybe (withResult (withText "tr" pure)) (valueFromString s)
236+ case r of
237+ Left _ -> error "Failed to parse textResult"
238+ Right r' -> pure (T.unpack r')
tests/Language/PureScript/Ide/MatcherSpec.hs+32−7
@@ -2,24 +2,40 @@
2
2
3module Language.PureScript.Ide.MatcherSpec where
3module Language.PureScript.Ide.MatcherSpec where
4
4
5−import Data.Text (Text)
5+import Data.Text (Text)
6+import Language.PureScript.Ide.Integration
6import Language.PureScript.Ide.Matcher
7import Language.PureScript.Ide.Matcher
7import Language.PureScript.Ide.Types
8import Language.PureScript.Ide.Types
9+import qualified Language.PureScript as P
8import Test.Hspec
10import Test.Hspec
9
11
12+value :: Text -> ExternDecl
13+value s = ValueDeclaration s P.TypeWildcard
14+
10−completions :: [Completion]
15+completions :: [Match]
11completions = [
16completions = [
12− Completion ("", "firstResult", ""),
17+ Match "" $ value "firstResult",
13− Completion ("", "secondResult", ""),
18+ Match "" $ value "secondResult",
14− Completion ("", "fiult", "")
19+ Match "" $ value "fiult"
15 ]
20 ]
16
21
17−mkResult :: [Int] -> [Completion]
22+mkResult :: [Int] -> [Match]
18mkResult = map (completions !!)
23mkResult = map (completions !!)
19
24
20−runFlex :: Text -> [Completion]
25+runFlex :: Text -> [Match]
21runFlex s = runMatcher (flexMatcher s) completions
26runFlex s = runMatcher (flexMatcher s) completions
22
27
28+setup :: IO ()
29+setup = do
30+ deleteOutputFolder
31+ _ <- compileTestProject
32+ _ <- startServer
33+ _ <- loadModuleWithDeps "Main"
34+ return ()
35+
36+teardown :: IO ()
37+teardown = quitServer
38+
23spec :: Spec
39spec :: Spec
24spec = do
40spec = do
25 describe "Flex Matcher" $ do
41 describe "Flex Matcher" $ do
@@ -29,3 +45,12 @@ spec = do
29 runFlex "firstResult" `shouldBe` mkResult [0]
45 runFlex "firstResult" `shouldBe` mkResult [0]
30 it "scores short matches higher and sorts accordingly" $
46 it "scores short matches higher and sorts accordingly" $
31 runFlex "filt" `shouldBe` mkResult [2, 0]
47 runFlex "filt" `shouldBe` mkResult [2, 0]
48+
49+ beforeAll_ setup $ afterAll_ teardown $
50+ describe "Integration Tests: Flex Matcher" $ do
51+ it "doesn't match on an empty string" $ do
52+ cs <- getFlexCompletions ""
53+ cs `shouldBe` []
54+ it "matches on equality" $ do
55+ cs <- getFlexCompletions "const"
56+ cs `shouldBe` [("Main", "const", "forall a b. a -> b -> a")]
tests/Language/PureScript/Ide/ReexportsSpec.hs+5−4
@@ -6,14 +6,15 @@ import Data.List (sort)
6import qualified Data.Map as Map
6import qualified Data.Map as Map
7import Language.PureScript.Ide.Reexports
7import Language.PureScript.Ide.Reexports
8import Language.PureScript.Ide.Types
8import Language.PureScript.Ide.Types
9+import qualified Language.PureScript as P
9import Test.Hspec
10import Test.Hspec
10
11
11decl1 :: ExternDecl
12decl1 :: ExternDecl
12−decl1 = FunctionDecl "filter" "asdasd"
13+decl1 = ValueDeclaration "filter" P.TypeWildcard
13decl2 :: ExternDecl
14decl2 :: ExternDecl
14−decl2 = DataDecl "Tree" "* -> *"
15+decl2 = ValueDeclaration "map" P.TypeWildcard
15decl3 :: ExternDecl
16decl3 :: ExternDecl
16−decl3 = DataDecl "TreeAsd" "* -> *"
17+decl3 = ValueDeclaration "catMaybe" P.TypeWildcard
17dep1 :: ExternDecl
18dep1 :: ExternDecl
18dep1 = Dependency "Test.Foo" [] (Just "T")
19dep1 = Dependency "Test.Foo" [] (Just "T")
19dep2 :: ExternDecl
20dep2 :: ExternDecl
@@ -44,7 +45,7 @@ shouldBeEqualSorted :: Module -> Module -> Expectation
44shouldBeEqualSorted (n1, d1) (n2, d2) = (n1, sort d1) `shouldBe` (n2, sort d2)
45shouldBeEqualSorted (n1, d1) (n2, d2) = (n1, sort d1) `shouldBe` (n2, sort d2)
45
46
46spec :: Spec
47spec :: Spec
47−spec = do
48+spec =
48 describe "Reexports" $ do
49 describe "Reexports" $ do
49 it "finds all reexports" $
50 it "finds all reexports" $
50 getReexports module1 `shouldBe` [Export "Module2", Export "Module3"]
51 getReexports module1 `shouldBe` [Export "Module2", Export "Module3"]
tests/Language/PureScript/IdeSpec.hs+8−8
@@ -1,17 +1,17 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
2−{-# LANGUAGE PackageImports #-}
2+
3module Language.PureScript.IdeSpec where
3module Language.PureScript.IdeSpec where
4
4
5import Control.Concurrent.STM
5import Control.Concurrent.STM
6import Control.Monad.Reader
6import Control.Monad.Reader
7import Data.List
7import Data.List
8−import qualified Data.Map as Map
8+import qualified Data.Map as Map
9import Language.PureScript.Ide
9import Language.PureScript.Ide
10import Language.PureScript.Ide.Types
10import Language.PureScript.Ide.Types
11import Test.Hspec
11import Test.Hspec
12
12
13testState :: PscIdeState
13testState :: PscIdeState
14−testState = PscIdeState (Map.fromList [("Data.Array", []), ("Control.Monad.Eff", [])]) (Map.empty)
14+testState = PscIdeState (Map.fromList [("Data.Array", []), ("Control.Monad.Eff", [])]) Map.empty
15
15
16defaultConfig :: Configuration
16defaultConfig :: Configuration
17defaultConfig =
17defaultConfig =
@@ -22,13 +22,13 @@ defaultConfig =
22 }
22 }
23
23
24spec :: SpecWith ()
24spec :: SpecWith ()
25−spec = do
25+spec =
26− describe "list" $ do
26+ describe "list" $
27 describe "loadedModules" $ do
27 describe "loadedModules" $ do
28 it "returns an empty list when no modules are loaded" $ do
28 it "returns an empty list when no modules are loaded" $ do
29− st <- newTVarIO emptyPscIdeState
29+ st <- newTVarIO emptyPscIdeState
30− result <- runReaderT printModules (PscIdeEnvironment st defaultConfig)
30+ result <- runReaderT printModules (PscIdeEnvironment st defaultConfig)
31− result `shouldBe` ModuleList []
31+ result `shouldBe` ModuleList []
32 it "returns the list of loaded modules" $ do
32 it "returns the list of loaded modules" $ do
33 st <- newTVarIO testState
33 st <- newTVarIO testState
34 ModuleList result <- runReaderT printModules (PscIdeEnvironment st defaultConfig)
34 ModuleList result <- runReaderT printModules (PscIdeEnvironment st defaultConfig)
tests/support/pscide/.gitignorenew file+7
@@ -0,0 +1,7 @@
1+/bower_components/
2+/node_modules/
3+/.pulp-cache/
4+/output/
5+/.psci*
6+/src/.webpack.js
7+/src/*.tmp
tests/support/pscide/src/ImportsSpec.pursnew file+5
@@ -0,0 +1,5 @@
1+module ImportsSpec where
2+
3+import Main (id)
4+
5+myId = id
tests/support/pscide/src/ImportsSpec1.pursnew file+32
@@ -0,0 +1,32 @@
1+module ImportsSpec1
2+ ( exportedFunction
3+ , MyType
4+ , MyParamType
5+ , MyNewtype(..)
6+ , MyMaybe(..)
7+ , SpecialCase
8+ , X(..)
9+ , class ATypeClass
10+ , typeClassFun
11+ , OnlyTypeExported
12+ )
13+ where
14+
15+exportedFunction ∷ ∀ a. a → a
16+exportedFunction x = x
17+
18+type MyType = String
19+
20+type MyParamType a = Array a
21+
22+newtype MyNewtype = MyNewtype String
23+
24+data MyMaybe a = MyJust a | MyNothing
25+
26+data SpecialCase
27+data X = SpecialCase
28+
29+newtype OnlyTypeExported = OnlyTypeExported String
30+
31+class ATypeClass a where
32+ typeClassFun ∷ a -> a
tests/support/pscide/src/Main.pursnew file+7
@@ -0,0 +1,7 @@
1+module Main where
2+
3+id :: forall a. a -> a
4+id x = x
5+
6+const :: forall a b. a -> b -> a
7+const x _ = x