1
+ {-# LANGUAGE DeriveGeneric #-}
2
+ {-# LANGUAGE DuplicateRecordFields #-}
3
+ {-# LANGUAGE FlexibleContexts #-}
4
+ {-# LANGUAGE FlexibleInstances #-}
1
5
{-# LANGUAGE OverloadedStrings #-}
2
- {-# LANGUAGE RecordWildCards #-}
6
+ {-# LANGUAGE RecordWildCards #-}
7
+ {-# LANGUAGE TupleSections #-}
8
+ {-# LANGUAGE TypeFamilies #-}
3
9
4
10
module Ide.Plugin.Example
5
11
(
6
12
plugin
7
13
) where
8
- import Development.IDE.Core.Rules
9
- import Development.IDE.Core.Service
10
- import Development.IDE.LSP.Server
11
- import Development.IDE.Types.Location
12
- import Development.IDE.Types.Logger
13
- import Development.Shake
14
- import qualified Language.Haskell.LSP.Core as LSP
15
14
16
- import Development.IDE.Plugin
17
- import Development.IDE.Core.Service
18
- import Development.IDE.Types.Location
15
+ import Control.Concurrent.Extra
16
+ import Control.DeepSeq
17
+ import Control.Exception
18
+ import Control.Monad (join )
19
+ import Control.Monad.Trans.Maybe
20
+ import Data.Aeson.Types (toJSON , fromJSON , Value (.. ), Result (.. ))
21
+ import Data.Binary
22
+ import qualified Data.ByteString.UTF8 as BS
23
+ import Data.Char
24
+ import Data.Functor
25
+ import qualified Data.HashMap.Strict as Map
26
+ import qualified Data.HashSet as HSet
27
+ import Data.Hashable
28
+ import Data.List.Extra
29
+ import Data.Maybe
30
+ import qualified Data.Rope.UTF16 as Rope
31
+ import qualified Data.Set as Set
32
+ import qualified Data.Text as T
33
+ import Data.Tuple.Extra
34
+ import Data.Typeable
35
+ import Development.IDE.Core.OfInterest
19
36
import Development.IDE.Core.PositionMapping
20
37
import Development.IDE.Core.RuleTypes
38
+ import Development.IDE.Core.Rules
39
+ import Development.IDE.Core.Service
21
40
import Development.IDE.Core.Shake
41
+ import Development.IDE.GHC.Error
22
42
import Development.IDE.GHC.Util
23
- import Development.IDE.LSP.Server
24
43
import Development.IDE.Import.DependencyInformation
25
-
26
- import Language.Haskell.LSP.Messages
27
- import Language.Haskell.LSP.Types
28
-
29
- import qualified Data.Text as T
44
+ import Development.IDE.LSP.Server
45
+ import Development.IDE.Plugin
46
+ import Development.IDE.Types.Diagnostics as D
47
+ import Development.IDE.Types.Location
48
+ import Development.IDE.Types.Logger
49
+ import Development.IDE.Types.Options
50
+ import Development.Shake hiding ( Diagnostic )
51
+ import GHC
52
+ import GHC.Generics
53
+ import qualified Language.Haskell.LSP.Core as LSP
54
+ import Language.Haskell.LSP.Messages
55
+ import Language.Haskell.LSP.Types
56
+ import Language.Haskell.LSP.VFS
57
+ import Outputable (ppr , showSDocUnsafe )
58
+ import SrcLoc
59
+ import Text.Regex.TDFA ((=~) , (=~~) )
60
+ import Text.Regex.TDFA.Text ()
30
61
31
62
-- ---------------------------------------------------------------------
32
63
33
64
plugin :: Plugin
34
- plugin = Plugin mempty handlersExample
65
+ plugin = Plugin exampleRules handlersExample <> codeActionPlugin codeAction
35
66
36
67
hover :: IdeState -> TextDocumentPositionParams -> IO (Maybe Hover )
37
68
hover = request " Hover" blah Nothing foundHover
@@ -46,6 +77,75 @@ handlersExample = PartialHandlers $ \WithMessage{..} x ->
46
77
47
78
48
79
-- ---------------------------------------------------------------------
80
+
81
+ data Example = Example
82
+ deriving (Eq , Show , Typeable , Generic )
83
+ instance Hashable Example
84
+ instance NFData Example
85
+ instance Binary Example
86
+
87
+ type instance RuleResult Example = ()
88
+
89
+ exampleRules = do
90
+ define $ \ Example file -> do
91
+ getParsedModule file
92
+ let diag = mkDiag file " example" DsError (Range (Position 0 0 ) (Position 1 0 )) " example diagnostic, hello world"
93
+ return ([diag], Just () )
94
+
95
+ action $ do
96
+ files <- getFilesOfInterest
97
+ void $ uses Example $ Set. toList files
98
+
99
+ mkDiag :: NormalizedFilePath
100
+ -> DiagnosticSource
101
+ -> DiagnosticSeverity
102
+ -> Range
103
+ -> T. Text
104
+ -> FileDiagnostic
105
+ mkDiag file diagSource sev loc msg = (file, D. ShowDiag ,)
106
+ Diagnostic
107
+ { _range = loc
108
+ , _severity = Just sev
109
+ , _source = Just diagSource
110
+ , _message = msg
111
+ , _code = Nothing
112
+ , _relatedInformation = Nothing
113
+ }
114
+
115
+ -- ---------------------------------------------------------------------
116
+
117
+ -- | Generate code actions.
118
+ codeAction
119
+ :: LSP. LspFuncs ()
120
+ -> IdeState
121
+ -> TextDocumentIdentifier
122
+ -> Range
123
+ -> CodeActionContext
124
+ -> IO [CAResult ]
125
+ codeAction lsp state (TextDocumentIdentifier uri) _range CodeActionContext {_diagnostics= List xs} = do
126
+ contents <- LSP. getVirtualFileFunc lsp $ toNormalizedUri uri
127
+ let text = Rope. toText . (_text :: VirtualFile -> Rope. Rope ) <$> contents
128
+ (ideOptions, parsedModule) <- runAction state $
129
+ (,) <$> getIdeOptions
130
+ <*> (getParsedModule . toNormalizedFilePath) `traverse` uriToFilePath uri
131
+ pure
132
+ [ CACodeAction $ CodeAction title (Just CodeActionQuickFix ) (Just $ List [x]) (Just edit) Nothing
133
+ | x <- xs, (title, tedit) <- suggestAction ideOptions ( join parsedModule ) text x
134
+ , let edit = WorkspaceEdit (Just $ Map. singleton uri $ List tedit) Nothing
135
+ ]
136
+
137
+ suggestAction :: IdeOptions -> Maybe ParsedModule -> Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
138
+ suggestAction ideOptions parsedModule text diag = concat
139
+ [ suggestAddTodo diag ]
140
+
141
+ suggestAddTodo :: Diagnostic -> [(T. Text , [TextEdit ])]
142
+ suggestAddTodo Diagnostic {_range= _range@ Range {.. },.. }
143
+ = [(" Add TODO Item" ,
144
+ -- [TextEdit _range "-- TODO added by Example Plugin\n"]) ]
145
+ [TextEdit (Range (Position 0 0 ) (Position 1 0 )) " -- TODO added by Example Plugin\n " ]) ]
146
+
147
+ -- ---------------------------------------------------------------------
148
+
49
149
foundHover :: (Maybe Range , [T. Text ]) -> Maybe Hover
50
150
foundHover (mbRange, contents) =
51
151
Just $ Hover (HoverContents $ MarkupContent MkMarkdown $ T. intercalate sectionSeparator contents) mbRange
0 commit comments