@@ -8,50 +8,62 @@ module Ide.Plugin.Cabal (descriptor, haskellInteractionDescriptor, Log (..)) whe
8
8
9
9
import Control.Concurrent.Strict
10
10
import Control.DeepSeq
11
- import Control.Lens ((^.) )
11
+ import Control.Lens ((^.) )
12
12
import Control.Monad.Extra
13
13
import Control.Monad.IO.Class
14
14
import Control.Monad.Trans.Class
15
- import Control.Monad.Trans.Maybe (runMaybeT )
16
- import qualified Data.ByteString as BS
15
+ import Control.Monad.Trans.Maybe (runMaybeT )
16
+ import qualified Data.ByteString as BS
17
17
import Data.Hashable
18
- import Data.HashMap.Strict (HashMap )
19
- import qualified Data.HashMap.Strict as HashMap
20
- import qualified Data.List.NonEmpty as NE
21
- import qualified Data.Maybe as Maybe
22
- import qualified Data.Text as T
23
- import qualified Data.Text.Encoding as Encoding
18
+ import Data.HashMap.Strict (HashMap )
19
+ import qualified Data.HashMap.Strict as HashMap
20
+ import qualified Data.List.NonEmpty as NE
21
+ import qualified Data.Maybe as Maybe
22
+ import qualified Data.Text as T
23
+ import qualified Data.Text.Encoding as Encoding
24
24
import Data.Typeable
25
- import Development.IDE as D
26
- import Development.IDE.Core.Shake (restartShakeSession )
27
- import qualified Development.IDE.Core.Shake as Shake
28
- import Development.IDE.Graph (Key , alwaysRerun )
29
- import qualified Development.IDE.Plugin.Completions.Logic as Ghcide
30
- import Development.IDE.Types.Shake (toKey )
31
- import qualified Distribution.Fields as Syntax
32
- import qualified Distribution.Parsec.Position as Syntax
25
+ import Development.IDE as D
26
+ import Development.IDE.Core.PluginUtils
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 Development.IDE.LSP.HoverDefinition (foundHover )
32
+ import qualified Development.IDE.Plugin.Completions.Logic as Ghcide
33
+ import Development.IDE.Types.Shake (toKey )
34
+ import qualified Distribution.Fields as Syntax
35
+ import Distribution.Package (Dependency )
36
+ import Distribution.PackageDescription (allBuildDepends ,
37
+ depPkgName ,
38
+ unPackageName )
39
+ import Distribution.PackageDescription.Configuration (flattenPackageDescription )
40
+ import qualified Distribution.Parsec.Position as Syntax
33
41
import GHC.Generics
34
- import qualified Ide.Plugin.Cabal.Completion.Completer.Types as CompleterTypes
35
- import qualified Ide.Plugin.Cabal.Completion.Completions as Completions
36
- import Ide.Plugin.Cabal.Completion.Types (ParseCabalCommonSections (ParseCabalCommonSections ),
37
- ParseCabalFields (.. ),
38
- ParseCabalFile (.. ))
39
- import qualified Ide.Plugin.Cabal.Completion.Types as Types
40
- import Ide.Plugin.Cabal.Definition (gotoDefinition )
41
- import qualified Ide.Plugin.Cabal.Diagnostics as Diagnostics
42
- import qualified Ide.Plugin.Cabal.FieldSuggest as FieldSuggest
43
- import qualified Ide.Plugin.Cabal.LicenseSuggest as LicenseSuggest
44
- import Ide.Plugin.Cabal.Orphans ()
42
+ import Ide.Plugin.Cabal.Completion.CabalFields as CabalFields
43
+ import qualified Ide.Plugin.Cabal.Completion.Completer.Types as CompleterTypes
44
+ import qualified Ide.Plugin.Cabal.Completion.Completions as Completions
45
+ import Ide.Plugin.Cabal.Completion.Types (ParseCabalCommonSections (ParseCabalCommonSections ),
46
+ ParseCabalFields (.. ),
47
+ ParseCabalFile (.. ))
48
+ import qualified Ide.Plugin.Cabal.Completion.Types as Types
49
+ import Ide.Plugin.Cabal.Definition (gotoDefinition )
50
+ import qualified Ide.Plugin.Cabal.Diagnostics as Diagnostics
51
+ import qualified Ide.Plugin.Cabal.FieldSuggest as FieldSuggest
52
+ import qualified Ide.Plugin.Cabal.LicenseSuggest as LicenseSuggest
53
+ import Ide.Plugin.Cabal.Orphans ()
45
54
import Ide.Plugin.Cabal.Outline
46
- import qualified Ide.Plugin.Cabal.Parse as Parse
55
+ import qualified Ide.Plugin.Cabal.Parse as Parse
56
+ import Ide.Plugin.Error
47
57
import Ide.Types
48
- import qualified Language.LSP.Protocol.Lens as JL
49
- import qualified Language.LSP.Protocol.Message as LSP
58
+ import qualified Language.LSP.Protocol.Lens as JL
59
+ import qualified Language.LSP.Protocol.Message as LSP
50
60
import Language.LSP.Protocol.Types
51
- import qualified Language.LSP.VFS as VFS
61
+ import qualified Language.LSP.VFS as VFS
62
+ import Text.Regex.TDFA
52
63
53
- import qualified Data.Text ()
54
- import qualified Ide.Plugin.Cabal.CabalAdd as CabalAdd
64
+
65
+ import qualified Data.Text ()
66
+ import qualified Ide.Plugin.Cabal.CabalAdd as CabalAdd
55
67
56
68
data Log
57
69
= LogModificationTime NormalizedFilePath FileVersion
@@ -118,6 +130,7 @@ descriptor recorder plId =
118
130
, mkPluginHandler LSP. SMethod_TextDocumentDocumentSymbol moduleOutline
119
131
, mkPluginHandler LSP. SMethod_TextDocumentCodeAction $ fieldSuggestCodeAction recorder
120
132
, mkPluginHandler LSP. SMethod_TextDocumentDefinition gotoDefinition
133
+ , mkPluginHandler LSP. SMethod_TextDocumentHover hover
121
134
]
122
135
, pluginNotificationHandlers =
123
136
mconcat
@@ -302,7 +315,6 @@ fieldSuggestCodeAction recorder ide _ (CodeActionParams _ _ (TextDocumentIdentif
302
315
let completionTexts = fmap (^. JL. label) completions
303
316
pure $ FieldSuggest. fieldErrorAction uri fieldName completionTexts _range
304
317
305
-
306
318
cabalAddCodeAction :: PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
307
319
cabalAddCodeAction state plId (CodeActionParams _ _ (TextDocumentIdentifier uri) _ CodeActionContext {_diagnostics= diags}) = do
308
320
maxCompls <- fmap maxCompletions . liftIO $ runAction " cabal.cabal-add" state getClientConfigAction
@@ -326,6 +338,55 @@ cabalAddCodeAction state plId (CodeActionParams _ _ (TextDocumentIdentifier uri)
326
338
cabalFilePath gpd
327
339
pure $ InL $ fmap InR actions
328
340
341
+ -- | Handler for hover messages.
342
+ --
343
+ -- Provides a Handler for displaying message on hover.
344
+ -- If found that the filtered hover message is a dependency,
345
+ -- adds a Documentation link.
346
+ hover :: PluginMethodHandler IdeState LSP. Method_TextDocumentHover
347
+ hover ide _ msgParam = do
348
+ nfp <- getNormalizedFilePathE uri
349
+ cabalFields <- runActionE " cabal.cabal-hover" ide $ useE ParseCabalFields nfp
350
+ case CabalFields. findTextWord cursor cabalFields of
351
+ Nothing ->
352
+ pure $ InR Null
353
+ Just cursorText -> do
354
+ gpd <- runActionE " cabal.GPD" ide $ useE ParseCabalFile nfp
355
+ let depsNames = map dependencyName $ allBuildDepends $ flattenPackageDescription gpd
356
+ case filterVersion cursorText of
357
+ Nothing -> pure $ InR Null
358
+ Just txt ->
359
+ if txt `elem` depsNames
360
+ then pure $ foundHover (Nothing , [txt <> " \n " , documentationText txt])
361
+ else pure $ InR Null
362
+ where
363
+ cursor = Types. lspPositionToCabalPosition (msgParam ^. JL. position)
364
+ uri = msgParam ^. JL. textDocument . JL. uri
365
+
366
+ dependencyName :: Dependency -> T. Text
367
+ dependencyName dep = T. pack $ unPackageName $ depPkgName dep
368
+
369
+ -- | Removes version requirements like
370
+ -- `==1.0.0.0`, `>= 2.1.1` that could be included in
371
+ -- hover message. Assumes that the dependency consists
372
+ -- of alphanums with dashes in between. Ends with an alphanum.
373
+ --
374
+ -- Examples:
375
+ -- >>> filterVersion "imp-deps>=2.1.1"
376
+ -- "imp-deps"
377
+ filterVersion :: T. Text -> Maybe T. Text
378
+ filterVersion msg = getMatch (msg =~ regex)
379
+ where
380
+ regex :: T. Text
381
+ regex = " ([a-zA-Z0-9-]*[a-zA-Z0-9])"
382
+
383
+ getMatch :: (T. Text , T. Text , T. Text , [T. Text ]) -> Maybe T. Text
384
+ getMatch (_, _, _, [dependency]) = Just dependency
385
+ getMatch (_, _, _, _) = Nothing -- impossible case
386
+
387
+ documentationText :: T. Text -> T. Text
388
+ documentationText package = " [Documentation](https://hackage.haskell.org/package/" <> package <> " )"
389
+
329
390
330
391
-- ----------------------------------------------------------------
331
392
-- Cabal file of Interest rules and global variable
0 commit comments