@@ -9,56 +9,63 @@ module Ide.Plugin.CabalProject where
9
9
10
10
import Control.Concurrent.Strict
11
11
import Control.DeepSeq
12
- import Control.Lens ((^.) )
12
+ import Control.Lens ((^.) )
13
13
import Control.Monad.Extra
14
14
import Control.Monad.IO.Class
15
- import Control.Monad.Trans.Class (lift )
16
- import Control.Monad.Trans.Maybe (runMaybeT )
17
- import qualified Data.ByteString as BS
15
+ import Control.Monad.Trans.Class (lift )
16
+ import Control.Monad.Trans.Maybe (runMaybeT )
17
+ import qualified Data.ByteString as BS
18
18
import Data.Hashable
19
- import Data.HashMap.Strict (HashMap , toList )
20
- import qualified Data.HashMap.Strict as HashMap
21
- import qualified Data.List as List
22
- import qualified Data.List.NonEmpty as NE
23
- import qualified Data.Maybe as Maybe
19
+ import Data.HashMap.Strict (HashMap ,
20
+ toList )
21
+ import qualified Data.HashMap.Strict as HashMap
22
+ import qualified Data.List as List
23
+ import qualified Data.List.NonEmpty as NE
24
+ import qualified Data.Maybe as Maybe
24
25
import Data.Proxy
25
- import qualified Data.Text ()
26
- import qualified Data.Text as T
27
- import qualified Data.Text.Encoding as Encoding
28
- import Data.Text.Utf16.Rope.Mixed as Rope
29
- import Development.IDE as D
30
- import Development.IDE.Core.FileStore (getVersionedTextDoc )
26
+ import qualified Data.Text ()
27
+ import qualified Data.Text as T
28
+ import qualified Data.Text.Encoding as Encoding
29
+ import Data.Text.Utf16.Rope.Mixed as Rope
30
+ import Development.IDE as D
31
+ import Development.IDE.Core.FileStore (getVersionedTextDoc )
31
32
import Development.IDE.Core.PluginUtils
32
- import Development.IDE.Core.Shake (restartShakeSession )
33
- import qualified Development.IDE.Core.Shake as Shake
34
- import Development.IDE.Graph (Key ,
35
- alwaysRerun )
36
- import Development.IDE.LSP.HoverDefinition (foundHover )
37
- import qualified Development.IDE.Plugin.Completions.Logic as Ghcide
38
- import Development.IDE.Types.Shake (toKey )
39
- import qualified Distribution.CabalSpecVersion as Cabal
40
- import qualified Distribution.Fields as Syntax
41
- import Distribution.Package (Dependency )
42
- import Distribution.PackageDescription (allBuildDepends ,
43
- depPkgName ,
44
- unPackageName )
45
- import Distribution.PackageDescription.Configuration (flattenPackageDescription )
33
+ import Development.IDE.Core.Shake (restartShakeSession )
34
+ import qualified Development.IDE.Core.Shake as Shake
35
+ import Development.IDE.Graph (Key ,
36
+ alwaysRerun )
37
+ import Development.IDE.LSP.HoverDefinition (foundHover )
38
+ import qualified Development.IDE.Plugin.Completions.Logic as Ghcide
39
+ import Development.IDE.Types.Shake (toKey )
40
+ import qualified Distribution.CabalSpecVersion as Cabal
41
+ import qualified Distribution.Fields as Syntax
42
+ import Distribution.Package (Dependency )
43
+ import Distribution.PackageDescription (allBuildDepends ,
44
+ depPkgName ,
45
+ unPackageName )
46
+ import Distribution.PackageDescription.Configuration (flattenPackageDescription )
46
47
import Distribution.Parsec.Error
47
- import qualified Distribution.Parsec.Position as Syntax
48
+ import qualified Distribution.Parsec.Position as Syntax
48
49
import GHC.Generics
49
- import Ide.Plugin.Cabal.Orphans ()
50
- import Ide.Plugin.CabalProject.Diagnostics as Diagnostics
51
- import Ide.Plugin.CabalProject.Parse as Parse
52
- import Ide.Plugin.CabalProject.Types as Types
50
+ import Ide.Plugin.Cabal.Completion.CabalFields as CabalFields
51
+ import qualified Ide.Plugin.Cabal.Completion.Completer.Types as CompleterTypes
52
+ import qualified Ide.Plugin.Cabal.Completion.Data as Data
53
+ import qualified Ide.Plugin.Cabal.Completion.Types as CTypes
54
+ import Ide.Plugin.Cabal.Orphans ()
55
+ import qualified Ide.Plugin.CabalProject.Completion.Completions as Completions
56
+ import Ide.Plugin.CabalProject.Diagnostics as Diagnostics
57
+ import Ide.Plugin.CabalProject.Parse as Parse
58
+ import Ide.Plugin.CabalProject.Types as Types
53
59
import Ide.Plugin.Error
54
60
import Ide.Types
55
- import qualified Language.LSP.Protocol.Lens as JL
56
- import qualified Language.LSP.Protocol.Message as LSP
61
+ import qualified Language.LSP.Protocol.Lens as JL
62
+ import qualified Language.LSP.Protocol.Message as LSP
57
63
import Language.LSP.Protocol.Types
58
- import qualified Language.LSP.VFS as VFS
59
- import System.FilePath (takeFileName )
64
+ import qualified Language.LSP.VFS as VFS
65
+ import System.FilePath (takeFileName )
60
66
import Text.Regex.TDFA
61
67
68
+
62
69
data Log
63
70
= LogModificationTime NormalizedFilePath FileVersion
64
71
| LogShake Shake. Log
@@ -67,6 +74,8 @@ data Log
67
74
| LogDocSaved Uri
68
75
| LogDocClosed Uri
69
76
| LogFOI (HashMap NormalizedFilePath FileOfInterestStatus )
77
+ | LogCompletionContext CTypes. Context Position
78
+ | LogCompletions CTypes. Log
70
79
deriving (Show )
71
80
72
81
instance Pretty Log where
@@ -91,7 +100,9 @@ descriptor recorder plId =
91
100
{ pluginRules = cabalRules recorder plId
92
101
, pluginHandlers =
93
102
mconcat
94
- []
103
+ [
104
+ mkPluginHandler LSP. SMethod_TextDocumentCompletion $ completion recorder
105
+ ]
95
106
, pluginNotificationHandlers =
96
107
mconcat
97
108
[ mkPluginNotificationHandler LSP. SMethod_TextDocumentDidOpen $
@@ -294,3 +305,49 @@ deleteFileOfInterest recorder state f = do
294
305
return [toKey IsFileOfInterest f]
295
306
where
296
307
log' = logWith recorder
308
+
309
+ -- ----------------------------------------------------------------
310
+ -- Completion
311
+ -- ----------------------------------------------------------------
312
+
313
+ completion :: Recorder (WithPriority Log ) -> PluginMethodHandler IdeState 'LSP.Method_TextDocumentCompletion
314
+ completion recorder ide _ complParams = do
315
+ let TextDocumentIdentifier uri = complParams ^. JL. textDocument
316
+ position = complParams ^. JL. position
317
+ mContents <- liftIO $ runAction " cabal-project-plugin.getUriContents" ide $ getUriContents $ toNormalizedUri uri
318
+ case (,) <$> mContents <*> uriToFilePath' uri of
319
+ Just (cnts, path) -> do
320
+ -- We decide on `useWithStale` here, since `useWithStaleFast` often leads to the wrong completions being suggested.
321
+ -- In case it fails, we still will get some completion results instead of an error.
322
+ mFields <- liftIO $ runAction " cabal-project-plugin.fields" ide $ useWithStale ParseCabalProjectFields $ toNormalizedFilePath path
323
+ case mFields of
324
+ Nothing ->
325
+ pure . InR $ InR Null
326
+ Just (fields, _) -> do
327
+ let lspPrefInfo = Ghcide. getCompletionPrefixFromRope position cnts
328
+ cabalPrefInfo = Completions. getCabalPrefixInfo path lspPrefInfo
329
+ let res = computeCompletionsAt recorder ide cabalPrefInfo path fields
330
+ liftIO $ fmap InL res
331
+ Nothing -> pure . InR $ InR Null
332
+
333
+ computeCompletionsAt :: Recorder (WithPriority Log ) -> IdeState -> CTypes. CabalPrefixInfo -> FilePath -> [Syntax. Field Syntax. Position ] -> IO [CompletionItem ]
334
+ computeCompletionsAt recorder ide prefInfo fp fields = do
335
+ runMaybeT (context fields) >>= \ case
336
+ Nothing -> pure []
337
+ Just ctx -> do
338
+ logWith recorder Debug $ LogCompletionContext ctx pos
339
+ let completer = Completions. contextToCompleter ctx
340
+ let completerData = CompleterTypes. CompleterData
341
+ {
342
+ cabalPrefixInfo = prefInfo
343
+ , stanzaName =
344
+ case fst ctx of
345
+ CTypes. Stanza _ name -> name
346
+ _ -> Nothing
347
+ }
348
+ completions <- completer completerRecorder completerData
349
+ pure completions
350
+ where
351
+ pos = CTypes. completionCursorPosition prefInfo
352
+ context fields = Completions. getContext completerRecorder prefInfo fields
353
+ completerRecorder = cmapWithPrio LogCompletions recorder
0 commit comments