@@ -9,31 +9,45 @@ module Ide.Plugin.CabalProject where
9
9
10
10
import Control.Concurrent.Strict
11
11
import Control.DeepSeq
12
+ import Control.Lens ((^.) )
12
13
import Control.Monad.Extra
13
14
import Control.Monad.IO.Class
14
- import qualified Data.ByteString as BS
15
+ import Control.Monad.Trans.Maybe (runMaybeT )
16
+ import qualified Data.ByteString as BS
15
17
import Data.Hashable
16
- import Data.HashMap.Strict (HashMap )
17
- import qualified Data.HashMap.Strict as HashMap
18
- import qualified Data.List.NonEmpty as NE
18
+ import Data.HashMap.Strict (HashMap )
19
+ -- toList)
20
+ import qualified Data.HashMap.Strict as HashMap
21
+ import qualified Data.List.NonEmpty as NE
19
22
import Data.Proxy
20
- import qualified Data.Text ()
21
- import qualified Data.Text.Encoding as Encoding
22
- import Data.Text.Utf16.Rope.Mixed as Rope
23
- import Development.IDE as D
24
- import Development.IDE.Core.Shake (restartShakeSession )
25
- import qualified Development.IDE.Core.Shake as Shake
26
- import Development.IDE.Graph (Key , alwaysRerun )
27
- import Development.IDE.Types.Shake (toKey )
23
+ import qualified Data.Text ()
24
+ import qualified Data.Text.Encoding as Encoding
25
+ import Data.Text.Utf16.Rope.Mixed as Rope
26
+ import Development.IDE as D
27
+ import Development.IDE.Core.Shake (restartShakeSession )
28
+ import qualified Development.IDE.Core.Shake as Shake
29
+ import Development.IDE.Graph (Key ,
30
+ alwaysRerun )
31
+ import qualified Development.IDE.Plugin.Completions.Logic as Ghcide
32
+ import Development.IDE.Types.Shake (toKey )
33
+ import qualified Distribution.Fields as Syntax
34
+ -- import Distribution.PackageDescription (allBuildDepends,
35
+ -- depPkgName,
36
+ -- unPackageName)
37
+ import qualified Distribution.Parsec.Position as Syntax
28
38
import GHC.Generics
29
- import Ide.Plugin.Cabal.Orphans ()
30
- import Ide.Plugin.CabalProject.Diagnostics as Diagnostics
31
- import Ide.Plugin.CabalProject.Parse as Parse
32
- import Ide.Plugin.CabalProject.Types as Types
39
+ import qualified Ide.Plugin.Cabal.Completion.Completer.Types as CompleterTypes
40
+ import qualified Ide.Plugin.Cabal.Completion.Types as CTypes
41
+ import Ide.Plugin.Cabal.Orphans ()
42
+ import qualified Ide.Plugin.CabalProject.Completion.Completions as Completions
43
+ import Ide.Plugin.CabalProject.Diagnostics as Diagnostics
44
+ import Ide.Plugin.CabalProject.Parse as Parse
45
+ import Ide.Plugin.CabalProject.Types as Types
33
46
import Ide.Types
34
- import qualified Language.LSP.Protocol.Message as LSP
47
+ import qualified Language.LSP.Protocol.Lens as JL
48
+ import qualified Language.LSP.Protocol.Message as LSP
35
49
import Language.LSP.Protocol.Types
36
- import qualified Language.LSP.VFS as VFS
50
+ import qualified Language.LSP.VFS as VFS
37
51
38
52
data Log
39
53
= LogModificationTime NormalizedFilePath FileVersion
@@ -43,6 +57,8 @@ data Log
43
57
| LogDocSaved Uri
44
58
| LogDocClosed Uri
45
59
| LogFOI (HashMap NormalizedFilePath FileOfInterestStatus )
60
+ | LogCompletionContext CTypes. Context Position
61
+ | LogCompletions CTypes. Log
46
62
deriving (Show )
47
63
48
64
instance Pretty Log where
@@ -60,11 +76,22 @@ instance Pretty Log where
60
76
" Closed text document:" <+> pretty (getUri uri)
61
77
LogFOI files ->
62
78
" Set files of interest to:" <+> viaShow files
79
+ LogCompletionContext context position ->
80
+ " Determined completion context:"
81
+ <+> pretty context
82
+ <+> " for cursor position:"
83
+ <+> pretty position
84
+ LogCompletions logs -> pretty logs
63
85
64
86
descriptor :: Recorder (WithPriority Log ) -> PluginId -> PluginDescriptor IdeState
65
87
descriptor recorder plId =
66
88
(defaultCabalProjectPluginDescriptor plId " Provides a variety of IDE features in cabal.project files" )
67
89
{ pluginRules = cabalProjectRules recorder plId
90
+ , pluginHandlers =
91
+ mconcat
92
+ [
93
+ mkPluginHandler LSP. SMethod_TextDocumentCompletion $ completion recorder
94
+ ]
68
95
, pluginNotificationHandlers =
69
96
mconcat
70
97
[ mkPluginNotificationHandler LSP. SMethod_TextDocumentDidOpen $
@@ -179,8 +206,8 @@ cabalProjectRules recorder plId = do
179
206
where
180
207
log' = logWith recorder
181
208
182
- {- | This is the kick function for the cabal. project plugin.
183
- We run this action, whenever a shake session is run/restarted, which triggers
209
+ {- | This is the kick function for the cabal project plugin.
210
+ We run this action, whenever we shake session us run/restarted, which triggers
184
211
actions to produce diagnostics for cabal.project files.
185
212
186
213
It is paramount that this kick-function can be run quickly, since it is a blocking
@@ -189,6 +216,7 @@ function invocation.
189
216
kick :: Action ()
190
217
kick = do
191
218
files <- HashMap. keys <$> getCabalProjectFilesOfInterestUntracked
219
+ -- let keys = map Types.ParseCabalProjectFile files
192
220
Shake. runWithSignal (Proxy @ " kick/start/cabal-project" ) (Proxy @ " kick/done/cabal-project" ) files Types. ParseCabalProjectFile
193
221
194
222
@@ -266,3 +294,49 @@ deleteFileOfInterest recorder state f = do
266
294
return [toKey IsFileOfInterest f]
267
295
where
268
296
log' = logWith recorder
297
+
298
+ -- ----------------------------------------------------------------
299
+ -- Completion
300
+ -- ----------------------------------------------------------------
301
+
302
+ completion :: Recorder (WithPriority Log ) -> PluginMethodHandler IdeState 'LSP.Method_TextDocumentCompletion
303
+ completion recorder ide _ complParams = do
304
+ let TextDocumentIdentifier uri = complParams ^. JL. textDocument
305
+ position = complParams ^. JL. position
306
+ mContents <- liftIO $ runAction " cabal-project-plugin.getUriContents" ide $ getUriContents $ toNormalizedUri uri
307
+ case (,) <$> mContents <*> uriToFilePath' uri of
308
+ Just (cnts, path) -> do
309
+ mFields <- liftIO $ runAction " cabal-project-plugin.fields" ide $ useWithStale ParseCabalProjectFields $ toNormalizedFilePath path
310
+ case mFields of
311
+ Nothing ->
312
+ pure . InR $ InR Null
313
+ Just (fields, _) -> do
314
+ let lspPrefInfo = Ghcide. getCompletionPrefixFromRope position cnts
315
+ cabalProjectPrefInfo = Completions. getCabalProjectPrefixInfo path lspPrefInfo
316
+ let res = computeCompletionsAt recorder ide cabalProjectPrefInfo path fields
317
+ liftIO $ fmap InL res
318
+ Nothing -> pure . InR $ InR Null
319
+
320
+ computeCompletionsAt :: Recorder (WithPriority Log ) -> IdeState -> CTypes. CabalPrefixInfo -> FilePath -> [Syntax. Field Syntax. Position ] -> IO [CompletionItem ]
321
+ computeCompletionsAt recorder _ prefInfo _ fields = do
322
+ runMaybeT (context fields) >>= \ case
323
+ Nothing -> pure []
324
+ Just ctx -> do
325
+ logWith recorder Debug $ LogCompletionContext ctx pos
326
+ let completer = Completions. contextToCompleter ctx
327
+ let completerData = CompleterTypes. CompleterData
328
+ {
329
+ getLatestGPD = pure Nothing ,
330
+ getCabalCommonSections = pure Nothing ,
331
+ cabalPrefixInfo = prefInfo
332
+ , stanzaName =
333
+ case fst ctx of
334
+ CTypes. Stanza _ name -> name
335
+ _ -> Nothing
336
+ }
337
+ completions <- completer completerRecorder completerData
338
+ pure completions
339
+ where
340
+ pos = CTypes. completionCursorPosition prefInfo
341
+ context fields = Completions. getContext completerRecorder prefInfo fields
342
+ completerRecorder = cmapWithPrio LogCompletions recorder
0 commit comments