@@ -9,31 +9,45 @@ module Ide.Plugin.CabalProject where
99
1010import Control.Concurrent.Strict
1111import Control.DeepSeq
12+ import Control.Lens ((^.) )
1213import Control.Monad.Extra
1314import 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
1517import 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
1922import 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
2838import 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
3346import 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
3549import Language.LSP.Protocol.Types
36- import qualified Language.LSP.VFS as VFS
50+ import qualified Language.LSP.VFS as VFS
3751
3852data Log
3953 = LogModificationTime NormalizedFilePath FileVersion
@@ -43,6 +57,8 @@ data Log
4357 | LogDocSaved Uri
4458 | LogDocClosed Uri
4559 | LogFOI (HashMap NormalizedFilePath FileOfInterestStatus )
60+ | LogCompletionContext CTypes. Context Position
61+ | LogCompletions CTypes. Log
4662 deriving (Show )
4763
4864instance Pretty Log where
@@ -60,11 +76,22 @@ instance Pretty Log where
6076 " Closed text document:" <+> pretty (getUri uri)
6177 LogFOI files ->
6278 " 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
6385
6486descriptor :: Recorder (WithPriority Log ) -> PluginId -> PluginDescriptor IdeState
6587descriptor recorder plId =
6688 (defaultCabalProjectPluginDescriptor plId " Provides a variety of IDE features in cabal.project files" )
6789 { pluginRules = cabalProjectRules recorder plId
90+ , pluginHandlers =
91+ mconcat
92+ [
93+ mkPluginHandler LSP. SMethod_TextDocumentCompletion $ completion recorder
94+ ]
6895 , pluginNotificationHandlers =
6996 mconcat
7097 [ mkPluginNotificationHandler LSP. SMethod_TextDocumentDidOpen $
@@ -179,8 +206,8 @@ cabalProjectRules recorder plId = do
179206 where
180207 log' = logWith recorder
181208
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
184211actions to produce diagnostics for cabal.project files.
185212
186213It is paramount that this kick-function can be run quickly, since it is a blocking
@@ -189,6 +216,7 @@ function invocation.
189216kick :: Action ()
190217kick = do
191218 files <- HashMap. keys <$> getCabalProjectFilesOfInterestUntracked
219+ -- let keys = map Types.ParseCabalProjectFile files
192220 Shake. runWithSignal (Proxy @ " kick/start/cabal-project" ) (Proxy @ " kick/done/cabal-project" ) files Types. ParseCabalProjectFile
193221
194222
@@ -266,3 +294,49 @@ deleteFileOfInterest recorder state f = do
266294 return [toKey IsFileOfInterest f]
267295 where
268296 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