4
4
{-# LANGUAGE OverloadedStrings #-}
5
5
{-# LANGUAGE TypeFamilies #-}
6
6
7
- module Ide.Plugin.Cabal (descriptor , Log (.. )) where
7
+ module Ide.Plugin.Cabal (descriptor , haskellInteractionDescriptor , Log (.. )) where
8
8
9
9
import Control.Concurrent.Strict
10
10
import Control.DeepSeq
@@ -54,6 +54,35 @@ import Ide.Plugin.Cabal.Orphans ()
54
54
import Ide.Plugin.Cabal.Outline
55
55
import qualified Ide.Plugin.Cabal.Parse as Parse
56
56
import Ide.Plugin.Error
57
+ import Data.HashMap.Strict (HashMap )
58
+ import qualified Data.HashMap.Strict as HashMap
59
+ import qualified Data.List.NonEmpty as NE
60
+ import qualified Data.Maybe as Maybe
61
+ import qualified Data.Text as T
62
+ import qualified Data.Text.Encoding as Encoding
63
+ import Data.Typeable
64
+ import Development.IDE as D
65
+ import Development.IDE.Core.Shake (restartShakeSession )
66
+ import qualified Development.IDE.Core.Shake as Shake
67
+ import Development.IDE.Graph (Key , alwaysRerun )
68
+ import qualified Development.IDE.Plugin.Completions.Logic as Ghcide
69
+ import Development.IDE.Types.Shake (toKey )
70
+ import qualified Distribution.Fields as Syntax
71
+ import qualified Distribution.Parsec.Position as Syntax
72
+ import GHC.Generics
73
+ import qualified Ide.Plugin.Cabal.Completion.Completer.Types as CompleterTypes
74
+ import qualified Ide.Plugin.Cabal.Completion.Completions as Completions
75
+ import Ide.Plugin.Cabal.Completion.Types (ParseCabalCommonSections (ParseCabalCommonSections ),
76
+ ParseCabalFields (.. ),
77
+ ParseCabalFile (.. ))
78
+ import qualified Ide.Plugin.Cabal.Completion.Types as Types
79
+ import Ide.Plugin.Cabal.Definition (gotoDefinition )
80
+ import qualified Ide.Plugin.Cabal.Diagnostics as Diagnostics
81
+ import qualified Ide.Plugin.Cabal.FieldSuggest as FieldSuggest
82
+ import qualified Ide.Plugin.Cabal.LicenseSuggest as LicenseSuggest
83
+ import Ide.Plugin.Cabal.Orphans ()
84
+ import Ide.Plugin.Cabal.Outline
85
+ import qualified Ide.Plugin.Cabal.Parse as Parse
57
86
import Ide.Types
58
87
import qualified Language.LSP.Protocol.Lens as JL
59
88
import qualified Language.LSP.Protocol.Message as LSP
@@ -62,6 +91,9 @@ import qualified Language.LSP.VFS as VFS
62
91
import Text.Regex.TDFA
63
92
64
93
94
+ import qualified Data.Text ()
95
+ import qualified Ide.Plugin.Cabal.CabalAdd as CabalAdd
96
+
65
97
data Log
66
98
= LogModificationTime NormalizedFilePath FileVersion
67
99
| LogShake Shake. Log
@@ -72,6 +104,7 @@ data Log
72
104
| LogFOI (HashMap NormalizedFilePath FileOfInterestStatus )
73
105
| LogCompletionContext Types. Context Position
74
106
| LogCompletions Types. Log
107
+ | LogCabalAdd CabalAdd. Log
75
108
deriving (Show )
76
109
77
110
instance Pretty Log where
@@ -95,6 +128,25 @@ instance Pretty Log where
95
128
<+> " for cursor position:"
96
129
<+> pretty position
97
130
LogCompletions logs -> pretty logs
131
+ LogCabalAdd logs -> pretty logs
132
+
133
+ -- | Some actions with cabal files originate from haskell files.
134
+ -- This descriptor allows to hook into the diagnostics of haskell source files, and
135
+ -- allows us to provide code actions and commands that interact with `.cabal` files.
136
+ haskellInteractionDescriptor :: Recorder (WithPriority Log ) -> PluginId -> PluginDescriptor IdeState
137
+ haskellInteractionDescriptor recorder plId =
138
+ (defaultPluginDescriptor plId " Provides the cabal-add code action in haskell files" )
139
+ { pluginHandlers =
140
+ mconcat
141
+ [ mkPluginHandler LSP. SMethod_TextDocumentCodeAction cabalAddCodeAction
142
+ ]
143
+ , pluginCommands = [PluginCommand CabalAdd. cabalAddCommand " add a dependency to a cabal file" (CabalAdd. command cabalAddRecorder)]
144
+ , pluginRules = pure ()
145
+ , pluginNotificationHandlers = mempty
146
+ }
147
+ where
148
+ cabalAddRecorder = cmapWithPrio LogCabalAdd recorder
149
+
98
150
99
151
descriptor :: Recorder (WithPriority Log ) -> PluginId -> PluginDescriptor IdeState
100
152
descriptor recorder plId =
@@ -319,6 +371,32 @@ gotoDefinition ideState _ msgParam = do
319
371
isSectionArgName name (Syntax. Section _ sectionArgName _) = name == CabalFields. onelineSectionArgs sectionArgName
320
372
isSectionArgName _ _ = False
321
373
374
+
375
+ cabalAddCodeAction :: PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
376
+ cabalAddCodeAction state plId (CodeActionParams _ _ (TextDocumentIdentifier uri) _ CodeActionContext {_diagnostics= diags}) = do
377
+ maxCompls <- fmap maxCompletions . liftIO $ runAction " cabal.cabal-add" state getClientConfigAction
378
+ let suggestions = take maxCompls $ concatMap CabalAdd. hiddenPackageSuggestion diags
379
+ case suggestions of
380
+ [] -> pure $ InL []
381
+ _ ->
382
+ case uriToFilePath uri of
383
+ Nothing -> pure $ InL []
384
+ Just haskellFilePath -> do
385
+ mbCabalFile <- liftIO $ CabalAdd. findResponsibleCabalFile haskellFilePath
386
+ case mbCabalFile of
387
+ Nothing -> pure $ InL []
388
+ Just cabalFilePath -> do
389
+ verTxtDocId <- lift $ pluginGetVersionedTextDoc $ TextDocumentIdentifier (filePathToUri cabalFilePath)
390
+ mbGPD <- liftIO $ runAction " cabal.cabal-add" state $ useWithStale ParseCabalFile $ toNormalizedFilePath cabalFilePath
391
+ case mbGPD of
392
+ Nothing -> pure $ InL []
393
+ Just (gpd, _) -> do
394
+ actions <- liftIO $ CabalAdd. addDependencySuggestCodeAction plId verTxtDocId
395
+ suggestions
396
+ haskellFilePath cabalFilePath
397
+ gpd
398
+ pure $ InL $ fmap InR actions
399
+
322
400
-- | Handler for hover messages.
323
401
--
324
402
-- Provides a Handler for displaying message on hover.
@@ -361,6 +439,7 @@ hover ide _ msgParam = do
361
439
documentationText :: T. Text -> T. Text
362
440
documentationText package = " [Documentation](https://hackage.haskell.org/package/" <> package <> " )"
363
441
442
+
364
443
-- ----------------------------------------------------------------
365
444
-- Cabal file of Interest rules and global variable
366
445
-- ----------------------------------------------------------------
0 commit comments